Appendix A: Complete API Reference

Version: 4.0.0 Last Updated: February 25, 2026 Base URL: https://api.pos-platform.com/api/v1


A.1 Overview

This appendix contains the complete API reference for the POS Platform, organized by domain. All endpoints require authentication unless marked as public.

Authentication

All authenticated requests must include a Bearer token:

Authorization: Bearer <jwt_token>

Role Hierarchy

RoleLevelCapabilities
SuperAdmin5Full system access
Admin4Tenant-wide administration
Manager3Location management, overrides
Cashier2POS operations
Viewer1Read-only access

Common Response Codes

CodeMeaning
200Success
201Created
204No Content
400Bad Request
401Unauthorized
403Forbidden
404Not Found
409Conflict
422Validation Error
429Rate Limited
500Server Error

A.2 Domain 1: Authentication

POST /auth/login

Description: Authenticate user and receive JWT token

Authentication: None (public)

Request Body:

{
  "email": "user@example.com",
  "password": "securePassword123",
  "tenantId": "tenant_nexus"
}

Response: 200 OK

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
  "expiresAt": "2025-12-29T16:00:00Z",
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "firstName": "John",
    "lastName": "Doe",
    "role": "cashier",
    "locationId": "loc_gm",
    "permissions": ["sales.create", "sales.void", "inventory.view"]
  }
}

Errors: 401 Invalid credentials, 423 Account locked


POST /auth/refresh

Description: Refresh an expired access token

Authentication: None (requires valid refresh token)

Request Body:

{
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl..."
}

Response: 200 OK

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "bmV3IHJlZnJlc2ggdG9r...",
  "expiresAt": "2025-12-29T18:00:00Z"
}

Errors: 401 Invalid or expired refresh token


POST /auth/logout

Description: Invalidate current session

Authentication: Bearer token (Any role)

Request Body: None

Response: 204 No Content


POST /auth/password/change

Description: Change current user’s password

Authentication: Bearer token (Any role)

Request Body:

{
  "currentPassword": "oldPassword123",
  "newPassword": "newSecurePassword456"
}

Response: 204 No Content

Errors: 400 Password requirements not met, 401 Current password incorrect


POST /auth/password/reset

Description: Request password reset email

Authentication: None (public)

Request Body:

{
  "email": "user@example.com",
  "tenantId": "tenant_nexus"
}

Response: 202 Accepted

{
  "message": "If the email exists, a reset link has been sent"
}

A.3 Domain 2: Tenants

GET /tenants

Description: List all tenants (SuperAdmin only)

Authentication: Bearer token (SuperAdmin)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | status | string | Filter by status (active, suspended, trial) | | page | int | Page number (default: 1) | | limit | int | Items per page (default: 20, max: 100) |

Response: 200 OK

{
  "data": [
    {
      "id": "tenant_nexus",
      "name": "Nexus Clothing",
      "subdomain": "nexus",
      "status": "active",
      "plan": "enterprise",
      "createdAt": "2025-01-01T00:00:00Z",
      "locationCount": 5,
      "userCount": 25
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 45,
    "pages": 3
  }
}

POST /tenants

Description: Create a new tenant

Authentication: Bearer token (SuperAdmin)

Request Body:

{
  "name": "New Retail Store",
  "subdomain": "newretail",
  "plan": "professional",
  "adminUser": {
    "email": "admin@newretail.com",
    "firstName": "Jane",
    "lastName": "Smith",
    "password": "initialPassword123"
  },
  "settings": {
    "timezone": "America/New_York",
    "currency": "USD",
    "taxRate": 6.0
  }
}

Response: 201 Created

{
  "id": "tenant_newretail",
  "name": "New Retail Store",
  "subdomain": "newretail",
  "status": "trial",
  "trialEndsAt": "2025-01-28T00:00:00Z",
  "adminUserId": "usr_admin123"
}

Errors: 409 Subdomain already exists, 422 Validation error


GET /tenants/

Description: Get tenant details

Authentication: Bearer token (SuperAdmin or tenant Admin)

Response: 200 OK

{
  "id": "tenant_nexus",
  "name": "Nexus Clothing",
  "subdomain": "nexus",
  "status": "active",
  "plan": "enterprise",
  "settings": {
    "timezone": "America/New_York",
    "currency": "USD",
    "taxRate": 6.0,
    "loyaltyEnabled": true,
    "rfidEnabled": true
  },
  "usage": {
    "locations": 5,
    "users": 25,
    "monthlyTransactions": 12500,
    "storageUsedMB": 2048
  },
  "createdAt": "2025-01-01T00:00:00Z",
  "updatedAt": "2025-12-29T10:00:00Z"
}

PATCH /tenants/

Description: Update tenant settings

Authentication: Bearer token (SuperAdmin or tenant Admin)

Request Body:

{
  "name": "Nexus Clothing Inc.",
  "settings": {
    "taxRate": 6.5
  }
}

Response: 200 OK (returns updated tenant)


POST /tenants/{tenantId}/suspend

Description: Suspend a tenant account

Authentication: Bearer token (SuperAdmin)

Request Body:

{
  "reason": "Payment overdue",
  "suspendAt": "2025-12-30T00:00:00Z"
}

Response: 200 OK


POST /tenants/{tenantId}/activate

Description: Reactivate a suspended tenant

Authentication: Bearer token (SuperAdmin)

Response: 200 OK


A.4 Domain 3: Locations

GET /locations

Description: List all locations for current tenant

Authentication: Bearer token (Viewer+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | status | string | Filter by status (active, inactive) | | type | string | Filter by type (store, warehouse, popup) |

Response: 200 OK

{
  "data": [
    {
      "id": "loc_gm",
      "code": "GM",
      "name": "Greenbrier Mall",
      "type": "store",
      "status": "active",
      "address": {
        "street": "1401 Greenbrier Pkwy",
        "city": "Chesapeake",
        "state": "VA",
        "zip": "23320"
      },
      "phone": "757-555-0100",
      "timezone": "America/New_York",
      "shopifyLocationId": "19718045760"
    }
  ]
}

POST /locations

Description: Create a new location

Authentication: Bearer token (Admin)

Request Body:

{
  "code": "NL",
  "name": "New Location",
  "type": "store",
  "address": {
    "street": "123 Main St",
    "city": "Norfolk",
    "state": "VA",
    "zip": "23510"
  },
  "phone": "757-555-0200",
  "timezone": "America/New_York",
  "settings": {
    "fulfillmentPriority": 5,
    "canShipOnline": true
  }
}

Response: 201 Created


GET /locations/

Description: Get location details

Authentication: Bearer token (Viewer+)

Response: 200 OK

{
  "id": "loc_gm",
  "code": "GM",
  "name": "Greenbrier Mall",
  "type": "store",
  "status": "active",
  "address": {
    "street": "1401 Greenbrier Pkwy",
    "city": "Chesapeake",
    "state": "VA",
    "zip": "23320"
  },
  "phone": "757-555-0100",
  "timezone": "America/New_York",
  "settings": {
    "fulfillmentPriority": 1,
    "canShipOnline": true,
    "showInventoryOnWeb": true
  },
  "registers": [
    {
      "id": "reg_01",
      "name": "Register 1",
      "status": "active"
    }
  ],
  "operatingHours": {
    "monday": { "open": "10:00", "close": "21:00" },
    "tuesday": { "open": "10:00", "close": "21:00" },
    "wednesday": { "open": "10:00", "close": "21:00" },
    "thursday": { "open": "10:00", "close": "21:00" },
    "friday": { "open": "10:00", "close": "21:00" },
    "saturday": { "open": "10:00", "close": "21:00" },
    "sunday": { "open": "12:00", "close": "18:00" }
  }
}

PATCH /locations/

Description: Update location details

Authentication: Bearer token (Admin)

Request Body:

{
  "name": "Greenbrier Mall Store",
  "settings": {
    "fulfillmentPriority": 2
  }
}

Response: 200 OK


A.5 Domain 4: Users & Employees

GET /users

Description: List all users for current tenant

Authentication: Bearer token (Admin)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | role | string | Filter by role | | locationId | string | Filter by location | | status | string | active, inactive, locked |

Response: 200 OK

{
  "data": [
    {
      "id": "usr_abc123",
      "email": "john.doe@example.com",
      "firstName": "John",
      "lastName": "Doe",
      "role": "cashier",
      "locationId": "loc_gm",
      "status": "active",
      "lastLoginAt": "2025-12-29T08:00:00Z"
    }
  ]
}

POST /users

Description: Create a new user

Authentication: Bearer token (Admin)

Request Body:

{
  "email": "newuser@example.com",
  "firstName": "Jane",
  "lastName": "Smith",
  "role": "cashier",
  "locationId": "loc_gm",
  "pin": "1234",
  "permissions": ["sales.create", "sales.void"]
}

Response: 201 Created


GET /users/

Description: Get user details

Authentication: Bearer token (Admin or self)

Response: 200 OK


PATCH /users/

Description: Update user details

Authentication: Bearer token (Admin)

Request Body:

{
  "role": "manager",
  "permissions": ["sales.create", "sales.void", "inventory.adjust"]
}

Response: 200 OK


DELETE /users/

Description: Deactivate user (soft delete)

Authentication: Bearer token (Admin)

Response: 204 No Content


POST /users/{userId}/reset-pin

Description: Reset user’s POS PIN

Authentication: Bearer token (Admin)

Request Body:

{
  "newPin": "5678"
}

Response: 204 No Content


GET /employees/{employeeId}/timeclock

Description: Get employee time clock entries

Authentication: Bearer token (Manager+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | startDate | date | Start of date range | | endDate | date | End of date range |

Response: 200 OK

{
  "data": [
    {
      "id": "tc_001",
      "employeeId": "usr_abc123",
      "clockIn": "2025-12-29T08:00:00Z",
      "clockOut": "2025-12-29T17:00:00Z",
      "hoursWorked": 9.0,
      "breaks": [
        {
          "start": "2025-12-29T12:00:00Z",
          "end": "2025-12-29T12:30:00Z",
          "type": "lunch"
        }
      ]
    }
  ]
}

POST /employees/{employeeId}/clock-in

Description: Clock in employee

Authentication: Bearer token (Cashier+ or self)

Request Body:

{
  "locationId": "loc_gm",
  "registerId": "reg_01"
}

Response: 201 Created

{
  "id": "tc_002",
  "employeeId": "usr_abc123",
  "clockIn": "2025-12-29T08:00:00Z",
  "locationId": "loc_gm"
}

POST /employees/{employeeId}/clock-out

Description: Clock out employee

Authentication: Bearer token (Cashier+ or self)

Response: 200 OK

{
  "id": "tc_002",
  "clockOut": "2025-12-29T17:00:00Z",
  "hoursWorked": 9.0
}

A.6 Domain 5: Products & Catalog

GET /products

Description: List products in catalog

Authentication: Bearer token (Viewer+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | search | string | Search by name, SKU, barcode | | categoryId | string | Filter by category | | vendorId | string | Filter by vendor | | status | string | active, discontinued, draft | | page | int | Page number | | limit | int | Items per page |

Response: 200 OK

{
  "data": [
    {
      "id": "prod_abc123",
      "name": "Classic V-Neck Tee",
      "sku": "NXP0323",
      "barcode": "657381512532",
      "categoryId": "cat_shirts",
      "vendorId": "vendor_abc",
      "status": "active",
      "basePrice": 29.99,
      "cost": 12.50,
      "variants": [
        {
          "id": "var_001",
          "sku": "NXP0323-S-BLK",
          "options": { "size": "S", "color": "Black" },
          "price": 29.99,
          "barcode": "657381512533"
        }
      ],
      "images": [
        {
          "url": "https://cdn.example.com/images/nxp0323.jpg",
          "alt": "Classic V-Neck Tee",
          "position": 1
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 5000
  }
}

POST /products

Description: Create a new product

Authentication: Bearer token (Admin)

Request Body:

{
  "name": "New Product",
  "sku": "NXP9999",
  "categoryId": "cat_shirts",
  "vendorId": "vendor_abc",
  "basePrice": 39.99,
  "cost": 15.00,
  "description": "Product description here",
  "variants": [
    {
      "sku": "NXP9999-S-BLK",
      "options": { "size": "S", "color": "Black" },
      "price": 39.99,
      "barcode": "657381599999"
    }
  ]
}

Response: 201 Created


GET /products/

Description: Get product details

Authentication: Bearer token (Viewer+)

Response: 200 OK


PATCH /products/

Description: Update product

Authentication: Bearer token (Admin)

Request Body:

{
  "basePrice": 34.99,
  "status": "active"
}

Response: 200 OK


DELETE /products/

Description: Discontinue product (soft delete)

Authentication: Bearer token (Admin)

Response: 204 No Content


GET /products/{productId}/variants

Description: List all variants for a product

Authentication: Bearer token (Viewer+)

Response: 200 OK


POST /products/{productId}/variants

Description: Add variant to product

Authentication: Bearer token (Admin)

Request Body:

{
  "sku": "NXP0323-XL-BLK",
  "options": { "size": "XL", "color": "Black" },
  "price": 29.99,
  "barcode": "657381512599"
}

Response: 201 Created


GET /categories

Description: List product categories

Authentication: Bearer token (Viewer+)

Response: 200 OK

{
  "data": [
    {
      "id": "cat_shirts",
      "name": "Shirts",
      "parentId": null,
      "children": [
        {
          "id": "cat_tees",
          "name": "T-Shirts",
          "parentId": "cat_shirts"
        },
        {
          "id": "cat_polos",
          "name": "Polos",
          "parentId": "cat_shirts"
        }
      ]
    }
  ]
}

GET /vendors

Description: List vendors

Authentication: Bearer token (Viewer+)

Response: 200 OK


A.7 Domain 6: Inventory

GET /inventory

Description: Get inventory levels across locations

Authentication: Bearer token (Viewer+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | locationId | string | Filter by location | | variantId | string | Filter by variant | | sku | string | Filter by SKU | | belowReorder | boolean | Show only items below reorder point |

Response: 200 OK

{
  "data": [
    {
      "variantId": "var_001",
      "sku": "NXP0323-S-BLK",
      "productName": "Classic V-Neck Tee - S Black",
      "levels": [
        {
          "locationId": "loc_gm",
          "locationName": "Greenbrier Mall",
          "onHand": 15,
          "available": 13,
          "reserved": 2,
          "reorderPoint": 5,
          "reorderQty": 20
        },
        {
          "locationId": "loc_hm",
          "locationName": "Peninsula Town Center",
          "onHand": 8,
          "available": 8,
          "reserved": 0,
          "reorderPoint": 5,
          "reorderQty": 20
        }
      ],
      "totalOnHand": 23,
      "totalAvailable": 21
    }
  ]
}

GET /inventory/locations/

Description: Get inventory for specific location

Authentication: Bearer token (Viewer+)

Response: 200 OK


POST /inventory/adjustments

Description: Create inventory adjustment

Authentication: Bearer token (Manager+)

Request Body:

{
  "locationId": "loc_gm",
  "adjustmentType": "cycle_count",
  "items": [
    {
      "variantId": "var_001",
      "systemQty": 15,
      "countedQty": 13,
      "reason": "shrinkage"
    }
  ],
  "notes": "Quarterly cycle count - Section A"
}

Response: 201 Created

{
  "id": "adj_001",
  "status": "completed",
  "items": [
    {
      "variantId": "var_001",
      "variance": -2,
      "previousOnHand": 15,
      "newOnHand": 13,
      "costImpact": -25.00
    }
  ],
  "totalVariance": -2,
  "totalCostImpact": -25.00
}

GET /inventory/adjustments

Description: List inventory adjustments

Authentication: Bearer token (Manager+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | locationId | string | Filter by location | | type | string | cycle_count, shrinkage, damage, correction | | startDate | date | Start date | | endDate | date | End date |

Response: 200 OK


POST /inventory/transfers

Description: Create inventory transfer request

Authentication: Bearer token (Manager+)

Request Body:

{
  "fromLocationId": "loc_hq",
  "toLocationId": "loc_gm",
  "priority": "normal",
  "reason": "low_stock",
  "items": [
    {
      "variantId": "var_001",
      "quantity": 10
    }
  ],
  "notes": "Restocking for weekend sale"
}

Response: 201 Created

{
  "id": "xfer_001",
  "status": "pending",
  "fromLocationId": "loc_hq",
  "toLocationId": "loc_gm",
  "items": [
    {
      "variantId": "var_001",
      "quantityRequested": 10
    }
  ],
  "expectedShipDate": "2025-12-30",
  "expectedArrivalDate": "2025-12-31"
}

GET /inventory/transfers/

Description: Get transfer details

Authentication: Bearer token (Viewer+)

Response: 200 OK


POST /inventory/transfers/{transferId}/ship

Description: Mark transfer as shipped

Authentication: Bearer token (Manager+)

Request Body:

{
  "items": [
    {
      "variantId": "var_001",
      "quantityShipped": 10
    }
  ],
  "trackingNumber": "1Z999AA10123456784",
  "carrier": "UPS"
}

Response: 200 OK


POST /inventory/transfers/{transferId}/receive

Description: Receive transfer at destination

Authentication: Bearer token (Manager+)

Request Body:

{
  "items": [
    {
      "variantId": "var_001",
      "quantityReceived": 10,
      "quantityDamaged": 0
    }
  ],
  "notes": null
}

Response: 200 OK


A.8 Domain 7: Sales & Orders

POST /sales

Description: Create a new sale transaction

Authentication: Bearer token (Cashier+)

Request Body:

{
  "locationId": "loc_gm",
  "registerId": "reg_01",
  "customerId": "cust_john_doe",
  "lineItems": [
    {
      "variantId": "var_001",
      "quantity": 2,
      "unitPrice": 29.99,
      "discountAmount": 0,
      "discountReason": null
    }
  ],
  "discounts": [
    {
      "type": "percentage",
      "value": 10,
      "code": "SAVE10",
      "appliesTo": "order"
    }
  ],
  "payments": [
    {
      "method": "card",
      "amount": 53.98,
      "reference": "tok_visa_4242"
    }
  ]
}

Response: 201 Created

{
  "id": "ord_xyz789",
  "orderNumber": "ORD-2025-00001",
  "receiptNumber": "GM-2025-001234",
  "status": "completed",
  "lineItems": [
    {
      "id": "li_001",
      "variantId": "var_001",
      "sku": "NXP0323-S-BLK",
      "name": "Classic V-Neck Tee - S Black",
      "quantity": 2,
      "unitPrice": 29.99,
      "lineTotal": 59.98
    }
  ],
  "subtotal": 59.98,
  "discountTotal": 6.00,
  "taxAmount": 3.24,
  "total": 57.22,
  "payments": [
    {
      "id": "pay_001",
      "method": "card",
      "amount": 57.22,
      "status": "completed",
      "authCode": "AUTH123456",
      "lastFour": "4242"
    }
  ],
  "customerId": "cust_john_doe",
  "loyaltyPointsEarned": 57,
  "createdAt": "2025-12-29T14:30:00Z",
  "createdBy": "usr_cashier1"
}

Errors: 400 Bad Request, 422 Validation Error, 402 Payment Failed


GET /sales/

Description: Get sale details

Authentication: Bearer token (Cashier+)

Response: 200 OK


GET /sales

Description: List sales with filters

Authentication: Bearer token (Cashier+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | locationId | string | Filter by location | | registerId | string | Filter by register | | startDate | datetime | Start of date range | | endDate | datetime | End of date range | | customerId | string | Filter by customer | | status | string | completed, voided, refunded | | minAmount | decimal | Minimum total | | maxAmount | decimal | Maximum total |

Response: 200 OK


POST /sales/{saleId}/void

Description: Void a sale (requires manager)

Authentication: Bearer token (Manager+)

Request Body:

{
  "reason": "customer_changed_mind",
  "managerPin": "1234"
}

Response: 200 OK

{
  "id": "ord_xyz789",
  "status": "voided",
  "voidedAt": "2025-12-29T14:35:00Z",
  "voidedBy": "usr_manager1",
  "voidReason": "customer_changed_mind",
  "refundAmount": 57.22
}

POST /returns

Description: Process a return

Authentication: Bearer token (Cashier+)

Request Body:

{
  "originalOrderId": "ord_xyz789",
  "originalReceiptNumber": "GM-2025-001234",
  "locationId": "loc_gm",
  "items": [
    {
      "originalLineItemId": "li_001",
      "variantId": "var_001",
      "quantityReturned": 1,
      "reason": "wrong_size",
      "condition": "resaleable"
    }
  ],
  "refundMethod": "original_payment"
}

Response: 201 Created

{
  "id": "ret_abc123",
  "returnReceiptNumber": "RET-GM-2025-0001",
  "originalOrderId": "ord_xyz789",
  "items": [
    {
      "variantId": "var_001",
      "quantityReturned": 1,
      "refundAmount": 28.61,
      "inventoryRestocked": true
    }
  ],
  "totalRefund": 28.61,
  "refundTransactionId": "refund_001",
  "loyaltyPointsDeducted": 29,
  "createdAt": "2025-12-29T15:00:00Z"
}

GET /returns/

Description: Get return details

Authentication: Bearer token (Cashier+)

Response: 200 OK


A.9 Domain 8: Customers & Loyalty

GET /customers

Description: List customers

Authentication: Bearer token (Cashier+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | search | string | Search by name, email, phone | | tier | string | Filter by loyalty tier | | tag | string | Filter by tag | | hasEmail | boolean | Has email address | | page | int | Page number | | limit | int | Items per page |

Response: 200 OK

{
  "data": [
    {
      "id": "cust_john_doe",
      "customerNumber": "CUST-2025-00001",
      "firstName": "John",
      "lastName": "Doe",
      "email": "john.doe@example.com",
      "phone": "555-0100",
      "loyalty": {
        "tier": "gold",
        "pointsBalance": 1250,
        "lifetimeSpend": 2500.00
      },
      "tags": ["vip", "birthday_month"],
      "createdAt": "2025-01-15T00:00:00Z"
    }
  ]
}

POST /customers

Description: Create a new customer

Authentication: Bearer token (Cashier+)

Request Body:

{
  "firstName": "Jane",
  "lastName": "Smith",
  "email": "jane.smith@example.com",
  "phone": "555-0200",
  "address": {
    "street": "123 Main St",
    "city": "Chesapeake",
    "state": "VA",
    "zip": "23320"
  },
  "marketingOptIn": true,
  "smsOptIn": false,
  "enrollInLoyalty": true
}

Response: 201 Created


GET /customers/

Description: Get customer details

Authentication: Bearer token (Cashier+)

Response: 200 OK

{
  "id": "cust_john_doe",
  "customerNumber": "CUST-2025-00001",
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "phone": "555-0100",
  "address": {
    "street": "456 Oak Ave",
    "city": "Virginia Beach",
    "state": "VA",
    "zip": "23451"
  },
  "loyalty": {
    "programId": "loyalty_standard",
    "tier": "gold",
    "pointsBalance": 1250,
    "pointsToNextTier": 750,
    "lifetimeSpend": 2500.00,
    "lifetimePoints": 3000
  },
  "preferences": {
    "marketingOptIn": true,
    "smsOptIn": true,
    "preferredContactMethod": "email"
  },
  "tags": ["vip", "birthday_month"],
  "purchaseHistory": {
    "totalOrders": 25,
    "totalSpend": 2500.00,
    "averageOrderValue": 100.00,
    "lastPurchase": "2025-12-28T14:00:00Z"
  },
  "createdAt": "2025-01-15T00:00:00Z",
  "updatedAt": "2025-12-28T14:00:00Z"
}

PATCH /customers/

Description: Update customer details

Authentication: Bearer token (Cashier+)

Request Body:

{
  "phone": "555-0300",
  "preferences": {
    "smsOptIn": true
  }
}

Response: 200 OK


GET /customers/{customerId}/orders

Description: Get customer’s order history

Authentication: Bearer token (Cashier+)

Response: 200 OK


POST /customers/{customerId}/loyalty/redeem

Description: Redeem loyalty points

Authentication: Bearer token (Cashier+)

Request Body:

{
  "points": 500,
  "orderId": "ord_xyz790"
}

Response: 200 OK

{
  "pointsRedeemed": 500,
  "discountAmount": 5.00,
  "previousBalance": 1250,
  "newBalance": 750
}

POST /customers/merge

Description: Merge duplicate customer records

Authentication: Bearer token (Admin)

Request Body:

{
  "survivingCustomerId": "cust_john_doe",
  "mergeCustomerIds": ["cust_john_d", "cust_jdoe"],
  "conflictResolutions": {
    "email": "cust_john_doe"
  }
}

Response: 200 OK


A.10 Domain 9: Payments

POST /payments/process

Description: Process a payment

Authentication: Bearer token (Cashier+)

Request Body:

{
  "orderId": "ord_xyz789",
  "method": "card",
  "amount": 57.22,
  "token": "tok_visa_4242",
  "terminalId": "term_verifone_01"
}

Response: 200 OK

{
  "id": "pay_001",
  "status": "approved",
  "amount": 57.22,
  "authorizationCode": "AUTH123456",
  "transactionId": "txn_gateway_abc",
  "cardBrand": "visa",
  "lastFour": "4242",
  "entryMethod": "chip",
  "batchId": "batch_2025-12-29"
}

Errors: 402 Payment Declined


POST /payments/refund

Description: Process a refund

Authentication: Bearer token (Manager+)

Request Body:

{
  "originalPaymentId": "pay_001",
  "amount": 28.61,
  "reason": "return"
}

Response: 200 OK


GET /payments/batch/

Description: Get payment batch details

Authentication: Bearer token (Manager+)

Response: 200 OK


POST /payments/batch/{batchId}/settle

Description: Settle payment batch

Authentication: Bearer token (Manager+)

Response: 200 OK


A.11 Domain 10: Gift Cards

POST /giftcards

Description: Create/sell a gift card

Authentication: Bearer token (Cashier+)

Request Body:

{
  "amount": 50.00,
  "purchasedBy": "cust_john_doe",
  "recipientEmail": "jane@example.com",
  "recipientName": "Jane",
  "message": "Happy Birthday!",
  "type": "digital"
}

Response: 201 Created

{
  "id": "gc_001",
  "cardNumber": "6012XXXXXXXXXXXX1234",
  "balance": 50.00,
  "status": "active",
  "expiresAt": null
}

GET /giftcards/{cardNumber}/balance

Description: Check gift card balance

Authentication: Bearer token (Cashier+)

Response: 200 OK

{
  "cardNumber": "6012XXXXXXXXXXXX1234",
  "balance": 50.00,
  "status": "active",
  "expiresAt": null
}

POST /giftcards/{cardNumber}/redeem

Description: Redeem gift card for payment

Authentication: Bearer token (Cashier+)

Request Body:

{
  "orderId": "ord_xyz790",
  "amount": 35.00
}

Response: 200 OK


A.12 Domain 11: Cash Management

POST /shifts/open

Description: Open a new shift

Authentication: Bearer token (Manager+)

Request Body:

{
  "registerId": "reg_01",
  "openingFloat": 267.50,
  "floatBreakdown": {
    "bills_20": 5,
    "bills_10": 5,
    "bills_5": 10,
    "bills_1": 50,
    "quarters": 40,
    "dimes": 50,
    "nickels": 40,
    "pennies": 50
  }
}

Response: 201 Created

{
  "id": "shift_001",
  "registerId": "reg_01",
  "openedAt": "2025-12-29T08:00:00Z",
  "openedBy": "usr_manager1",
  "openingFloat": 267.50,
  "status": "active"
}

POST /shifts/{shiftId}/close

Description: Close shift and reconcile

Authentication: Bearer token (Manager+)

Request Body:

{
  "closingCount": {
    "bills_100": 2,
    "bills_50": 3,
    "bills_20": 15,
    "bills_10": 10,
    "bills_5": 20,
    "bills_1": 75,
    "quarters": 80,
    "dimes": 100,
    "nickels": 80,
    "pennies": 100
  }
}

Response: 200 OK

{
  "id": "shift_001",
  "closedAt": "2025-12-29T17:00:00Z",
  "expectedCash": 725.50,
  "actualCash": 723.00,
  "variance": -2.50,
  "varianceSeverity": "notable",
  "summary": {
    "cashSales": 458.00,
    "cardSales": 1250.00,
    "returns": 45.00,
    "paidOuts": 25.00,
    "tillDrops": 200.00
  }
}

POST /shifts/{shiftId}/till-drop

Description: Record till drop to safe

Authentication: Bearer token (Cashier+)

Request Body:

{
  "amount": 200.00,
  "breakdown": {
    "bills_100": 2
  }
}

Response: 201 Created


POST /shifts/{shiftId}/paid-out

Description: Record paid out (petty cash)

Authentication: Bearer token (Manager+)

Request Body:

{
  "amount": 25.00,
  "category": "office_supplies",
  "description": "Printer paper",
  "receiptAttached": true
}

Response: 201 Created


GET /shifts/

Description: Get shift details

Authentication: Bearer token (Manager+)

Response: 200 OK


A.13 Domain 12: RFID (Optional Module — Counting Only)

Scope: RFID endpoints support inventory counting operations only. Receiving is handled by the barcode Scanner in the POS Client. See BRD Section 5.16.6 for the Scanner vs RFID distinction.

POST /rfid/tags/print

Description: Queue RFID tags for printing

Authentication: Bearer token (Manager+)

Request Body:

{
  "printerId": "printer_zebra_01",
  "items": [
    {
      "variantId": "var_001",
      "quantity": 50
    }
  ],
  "templateId": "tmpl_standard"
}

Response: 202 Accepted

{
  "jobId": "print_job_001",
  "status": "queued",
  "totalTags": 50
}

GET /rfid/tags/print/

Description: Get print job status

Authentication: Bearer token (Manager+)

Response: 200 OK


POST /rfid/scans/sessions

Description: Create a new RFID counting session

Authentication: Bearer token (Cashier+)

Request Body:

{
  "locationId": "loc_gm",
  "sectionId": "section_a_mens_tops",
  "sessionType": "cycle_count",
  "notes": "Pre-inventory count for Q4 audit"
}

Session Types: full_inventory, cycle_count, spot_check, find_item

Response: 201 Created

{
  "sessionId": "scan_001",
  "status": "active",
  "startedAt": "2025-12-29T10:00:00Z",
  "expectedCount": 505,
  "sectionsAvailable": ["section_a_mens_tops", "section_b_mens_bottoms"]
}

POST /rfid/scans/sessions/{sessionId}/join

Description: Join an existing session as an additional operator (multi-operator counting)

Authentication: Bearer token (Cashier+)

Request Body:

{
  "operatorId": "user_002",
  "deviceId": "device_mc3390r_02",
  "assignedSection": "section_b_mens_bottoms"
}

Response: 200 OK

{
  "sessionId": "scan_001",
  "operatorCount": 3,
  "yourSection": "section_b_mens_bottoms",
  "sessionStartedAt": "2025-12-29T10:00:00Z"
}

Business Rules:

  • Maximum 10 operators per session
  • One active session per operator
  • Section assignment is advisory (not hardware-enforced)

POST /rfid/scans/sessions/{sessionId}/chunks

Description: Upload scan events in chunks (≤5,000 events per chunk). Idempotent — duplicate (session_id, epc) pairs are deduplicated server-side using UPSERT with highest RSSI kept.

Authentication: Bearer token (Cashier+)

Request Body:

{
  "chunkIndex": 0,
  "totalChunks": 10,
  "operatorId": "user_001",
  "deviceId": "device_mc3390r_01",
  "events": [
    {
      "epc": "E28011606000020752345678",
      "rssi": -45,
      "readCount": 3,
      "firstSeenAt": "2025-12-29T10:05:00Z",
      "lastSeenAt": "2025-12-29T10:05:12Z"
    }
  ]
}

Response: 200 OK

{
  "eventsAccepted": 4892,
  "eventsDeduplicated": 108,
  "chunksReceived": 1,
  "chunksExpected": 10
}

Chunk Size: Maximum 5,000 events per request. For a 100,000-tag session, this requires 20 chunks.


GET /rfid/scans/sessions/{sessionId}/upload-status

Description: Check upload progress. Used by mobile app to resume after network failure — identifies which chunks are missing so only those need retrying.

Authentication: Bearer token (Cashier+)

Response: 200 OK

{
  "sessionId": "scan_001",
  "status": "incomplete",
  "chunksReceived": [0, 1, 2, 4, 5],
  "chunksMissing": [3, 6, 7, 8, 9],
  "totalEvents": 14892,
  "uniqueEpcs": 14540
}

POST /rfid/scans/sessions/{sessionId}/complete

Description: Complete scan session and trigger variance calculation

Authentication: Bearer token (Cashier+)

Request Body:

{
  "endedAt": "2025-12-29T10:30:00Z",
  "notes": "Section A complete, 2 unknown tags flagged"
}

Response: 200 OK

{
  "sessionId": "scan_001",
  "status": "completed",
  "summary": {
    "totalTagsScanned": 47000,
    "uniqueEpcs": 46540,
    "expectedCount": 505,
    "variance": 7,
    "variancePercentage": 1.39,
    "reviewRequired": false
  },
  "completedAt": "2025-12-29T10:30:00Z"
}

Variance Thresholds (configurable per tenant):

VarianceAction
0%Auto-approve
1-2%Review recommended
3-5%Manager review required
> 5%Recount required

A.14 Domain 13: Sync & Offline

POST /sync/push

Description: Push offline changes to server

Authentication: Bearer token (Cashier+)

Request Body:

{
  "deviceId": "dev_pos_01",
  "lastSyncTimestamp": "2025-12-29T10:00:00Z",
  "events": [
    {
      "localSequence": 1,
      "eventType": "OrderCompleted",
      "timestamp": "2025-12-29T10:30:00Z",
      "payload": { }
    }
  ],
  "inventoryDeltas": [
    {
      "variantId": "var_001",
      "locationId": "loc_gm",
      "lastSyncQty": 15,
      "delta": -2
    }
  ]
}

Response: 200 OK

{
  "success": true,
  "syncedEvents": 5,
  "conflicts": [
    {
      "type": "inventory",
      "variantId": "var_001",
      "resolution": "delta_merged",
      "serverValue": 12,
      "localDelta": -2,
      "resolvedValue": 10
    }
  ],
  "serverTimestamp": "2025-12-29T12:00:00Z"
}

GET /sync/pull

Description: Pull updates from server

Authentication: Bearer token (Cashier+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | since | datetime | Last sync timestamp | | types | string[] | Event types to pull |

Response: 200 OK


GET /sync/status

Description: Get sync status for device

Authentication: Bearer token (Cashier+)

Response: 200 OK

{
  "deviceId": "dev_pos_01",
  "lastSync": "2025-12-29T12:00:00Z",
  "pendingPush": 0,
  "pendingPull": 15,
  "status": "synced"
}

A.15 Domain 14: Reports

GET /reports/sales/daily

Description: Daily sales summary

Authentication: Bearer token (Manager+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | date | date | Report date | | locationId | string | Filter by location |

Response: 200 OK

{
  "date": "2025-12-29",
  "summary": {
    "grossSales": 5250.00,
    "discounts": 250.00,
    "returns": 150.00,
    "netSales": 4850.00,
    "tax": 291.00,
    "transactionCount": 85,
    "averageTicket": 57.06,
    "unitsPerTransaction": 2.3
  },
  "byPaymentMethod": {
    "cash": 1250.00,
    "card": 3500.00,
    "giftCard": 100.00
  },
  "byCategory": [
    { "category": "Shirts", "sales": 2500.00, "units": 75 },
    { "category": "Pants", "sales": 1500.00, "units": 30 }
  ],
  "topItems": [
    { "sku": "NXP0323", "name": "Classic V-Neck", "units": 25, "sales": 749.75 }
  ]
}

GET /reports/inventory/valuation

Description: Inventory valuation report

Authentication: Bearer token (Manager+)

Query Parameters: | Parameter | Type | Description | |———–|——|———––| | locationId | string | Filter by location | | asOfDate | date | Valuation date |

Response: 200 OK


GET /reports/employees/timeclock

Description: Employee time clock report

Authentication: Bearer token (Manager+)

Response: 200 OK


GET /reports/customers/loyalty

Description: Loyalty program report

Authentication: Bearer token (Manager+)

Response: 200 OK


A.16 Webhooks

Configuring Webhooks

Description: Register webhook endpoints

Authentication: Bearer token (Admin)

Request Body:

{
  "url": "https://your-server.com/webhooks",
  "events": [
    "order.completed",
    "order.refunded",
    "inventory.low_stock",
    "customer.created"
  ],
  "secret": "whsec_your_secret_key"
}

Webhook Events

EventDescription
order.completedSale completed
order.voidedSale voided
order.refundedReturn processed
inventory.low_stockBelow reorder point
inventory.adjustedManual adjustment
customer.createdNew customer
customer.updatedCustomer modified
sync.conflictOffline conflict detected

Webhook Payload Format

{
  "id": "evt_webhook_001",
  "type": "order.completed",
  "timestamp": "2025-12-29T14:30:00Z",
  "tenantId": "tenant_nexus",
  "data": {
    "orderId": "ord_xyz789",
    "orderNumber": "ORD-2025-00001",
    "total": 57.22
  }
}

A.17 Rate Limits

Endpoint TypeRate Limit
Authentication10 requests/minute
Read operations1000 requests/minute
Write operations100 requests/minute
Bulk operations10 requests/minute
Webhooks1000 events/minute

A.18 Additional Endpoint References

Note (v5.0.0): The following endpoint groups are defined in Chapter 05 (Architecture Components) and are not fully duplicated here. Refer to the source chapter for complete request/response schemas.

  • Tax Jurisdictions (/api/v1/tax-jurisdictions, /api/v1/tax-rates): Compound tax configuration with 3-level (State/County/City) support. See Chapter 05 Section 1.17 for full specification.
  • RFID Configuration & Counting (/api/v1/rfid/*): Tag templates, tag mappings, counting sessions, chunked sync upload. See Chapter 05 Section 5.16 for the complete RFID API specification. Also see Domain 12 (Section A.13) above for endpoint details already documented.
  • Integration Sync (/api/v1/integrations/*): Shopify, Amazon SP-API, Google Merchant channel sync endpoints. See Chapter 13 (Integrations) for full specification.

A.19 API Versioning

The API uses URL versioning:

  • Current version: v1
  • URL format: /api/v1/{resource}
  • Deprecated versions are supported for 12 months
  • Version header: X-API-Version: 2025-12-29

This API reference covers 75+ endpoints across 14 domains. For additional details, see the OpenAPI specification at /api/v1/docs.


Document Information

AttributeValue
Version5.0.0
Created2025-12-29
Updated2026-02-25
AuthorClaude Code
StatusActive
SectionAppendix A

This appendix is part of the POS Blueprint Book. All content is self-contained.