{"openapi":"3.0.3","info":{"title":"BITS Loyalty API","version":"1.0.0","description":"Headless loyalty API. Machine clients authenticate with `Authorization: Bearer <api key>` (create keys in Admin → Integrations). Staff sessions ride Cloudflare Access."},"servers":[{"url":"/api/v1"}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key from Admin → Integrations"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}}},"Balance":{"type":"object","properties":{"available":{"type":"number"},"pending":{"type":"number"},"spent":{"type":"number"},"expired":{"type":"number"},"earnedTotal":{"type":"number"}}},"TransactionItem":{"type":"object","required":["sku","name","quantity","grossValue"],"properties":{"sku":{"type":"string"},"name":{"type":"string"},"quantity":{"type":"number"},"grossValue":{"type":"number","description":"line total in tenant currency"},"category":{"type":"string"},"maker":{"type":"string"},"labels":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}}}}}}},"security":[{"apiKey":[]}],"paths":{"/customers":{"get":{"summary":"List/search customers","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"string"}},{"name":"perPage","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Register a customer","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"firstName: string*; lastName: string*; email: string; phone: string; loyaltyCardNumber: string; birthDate: date; externalId: string; activate: boolean"}}}},"responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers/{id}":{"get":{"summary":"Customer profile with balance","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update customer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers/{id}/status":{"post":{"summary":"Change status (NEW/ACTIVE/BLOCKED/DELETED)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"status: string*"}}}},"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers/{id}/level":{"post":{"summary":"Manually assign level (null removes + recalculates)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"levelId: uuid|null"}}}},"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers/{id}/rewards":{"get":{"summary":"Available campaigns + owned coupons for a customer (member dashboard view)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/transactions":{"get":{"summary":"List transactions","parameters":[{"name":"customerId","in":"query","required":false,"schema":{"type":"string"}},{"name":"documentNumber","in":"query","required":false,"schema":{"type":"string"}},{"name":"unmatched","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Register a transaction (auto-matches customer by phone/card/email; awards points)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"documentNumber: string*; documentType: SELL|RETURN; purchasedAt: date*; customerData: {email?,phone?,loyaltyCardNumber?}; items: TransactionItem[]*; deliveryCost: number; revisedDocumentNumber: string (returns)"}}}},"responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/transactions/simulate":{"post":{"summary":"Preview points for a hypothetical transaction","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"items: TransactionItem[]*; customerId: uuid"}}}},"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/transactions/{id}/assign-customer":{"post":{"summary":"Manually assign a customer to an unmatched transaction","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"customerId: uuid*"}}}},"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/points/transfers":{"get":{"summary":"List ledger entries","parameters":[{"name":"customerId","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Manual add/spend points","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"customerId: uuid*; type: add|spend*; amount: number*; comment: string"}}}},"responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/points/transfers/{id}/cancel":{"post":{"summary":"Cancel an earned-points entry","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/points/p2p":{"post":{"summary":"Transfer points customer→customer (feature-flagged)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"fromCustomerId: uuid*; toCustomerId: uuid*; amount: number*"}}}},"responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events/{eventName}":{"post":{"summary":"Report a custom earning event for a customer","parameters":[{"name":"eventName","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"customerId: uuid*"}}}},"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/earning-rules":{"get":{"summary":"List earning rules","responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create earning rule","responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/earning-rules/{id}":{"get":{"summary":"Get rule","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update rule","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete rule","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/levels":{"get":{"summary":"List levels","responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create level","responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/levels/{id}":{"get":{"summary":"Get level","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update level","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/campaigns":{"get":{"summary":"List reward campaigns","responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create campaign (coupons: string[] seeds the pool)","responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/campaigns/{id}":{"get":{"summary":"Campaign with coupon pool","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update campaign / coupon pool","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/campaigns/{id}/buy":{"post":{"summary":"Redeem a reward for a customer (spends points, assigns coupon)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"customerId: uuid*; quantity: int (default 1); transactionValue: number (percentage campaigns)"}}}},"responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/coupons/{id}":{"patch":{"summary":"Mark bought coupon used/unused; update delivery status","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"used: boolean; deliveryStatus: ORDERED|CANCELED|SHIPPED|DELIVERED"}}}},"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/cashback":{"post":{"summary":"Simulate or redeem cashback (points → money value)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"customerId: uuid*; points: number*; simulate: boolean"}}}},"responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook-endpoints":{"get":{"summary":"List webhook endpoints","responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Add webhook endpoint","responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/audit-log":{"get":{"summary":"Query audit log","parameters":[{"name":"entityType","in":"query","required":false,"schema":{"type":"string"}},{"name":"entityId","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/invitations":{"get":{"summary":"List referral invitations","parameters":[{"name":"referrerId","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create invitation (returns token for the invite link; registration passes it as referralToken)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"referrerId: uuid*; email: string; phone: string"}}}},"responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/earn/qrcode":{"post":{"summary":"QR-code earning: award points for a scanned code (feature-flagged)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"customerId: uuid*; code: string*"}}}},"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/segments":{"get":{"summary":"List segments","responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create segment (parts: criteria ANDed, parts ORed; evaluates immediately)","responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/segments/{id}":{"get":{"summary":"Get segment","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update segment","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete segment","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/segments/{id}/evaluate":{"post":{"summary":"Re-evaluate membership now","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/analytics":{"get":{"summary":"Aggregate KPIs (customers, transactions, points, rewards)","responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers/import":{"post":{"summary":"CSV import of customers (text/csv or multipart file)","responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/transactions/import":{"post":{"summary":"CSV import of transactions (line-item rows grouped by documentNumber)","responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/export/{entity}":{"get":{"summary":"CSV export: coupons | segment?id= | level?id=","parameters":[{"name":"entity","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pos":{"get":{"summary":"List POS locations","responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create POS location","responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pos/{id}":{"patch":{"summary":"Update POS location","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/earn/geolocation":{"post":{"summary":"Geolocation earning: award points when within a rule radius (feature-flagged)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"customerId: uuid*; latitude: number*; longitude: number*"}}}},"responses":{"200":{"description":"OK"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/uploads":{"post":{"summary":"Upload an image/PDF to object storage (multipart: file + kind); returns { url } for photoUrl/logoUrl fields","responses":{"201":{"description":"Created"},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}