POS API

A server-to-server API so a cash register / POS can award loyalty points for a sale and look up a customer's balance. Give your POS provider this page plus an API key (Dashboard → Settings → Integrations → POS API).

Authentication

Send the API key as a Bearer token. The full key is shown once at creation; only a hash is stored and keys can be revoked anytime. Every request is scoped to the key's organization.

Authorization: Bearer lp_live_xxxxxxxxxxxxxxxx

Base URL: https://loyalpanther.app/api/v1

Errors return a JSON body and an HTTP status code. Basic rate limiting applies (HTTP 429 when exceeded).

{ "error": { "code": "unauthorized", "message": "Invalid or missing API key." } }

POST/api/v1/transactions

Award points for a purchase. Points = amount × earn-rate × any active bonus × the customer's tier multiplier (same as the in-store scan). Idempotent via external_ref: retrying with the same value never awards points twice.

curl -X POST https://loyalpanther.app/api/v1/transactions \
  -H "Authorization: Bearer lp_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": { "type": "loyalty_code", "value": "F55EA00D" },
    "amount_cents": 2500,
    "external_ref": "order-12345"
  }'

identifier.type is loyalty_code or email. amount_cents is the total in cents. Response:

{
  "customer": { "loyalty_code": "F55EA00D", "name": "Sophie", "points_balance": 1265 },
  "points_added": 25,
  "duplicate": false,
  "bonus_applied": "Happy hour"
}

Errors: 400 invalid request · 401 bad key · 404 customer not found · 422 amount too low · 429 rate limited.

GET/api/v1/customers/:code

Look up a customer's balance by loyalty code.

curl https://loyalpanther.app/api/v1/customers/F55EA00D \
  -H "Authorization: Bearer lp_live_…"

{
  "customer": {
    "loyalty_code": "F55EA00D",
    "name": "Sophie",
    "points_balance": 1265,
    "lifetime_points": 4300
  }
}

GET/api/v1/rewards

List the business's active rewards (id, name, description, points cost, icon).

{
  "rewards": [
    { "id": "…", "name": "Free coffee", "description": null, "points_cost": 50, "icon": "☕" }
  ]
}

POST/api/v1/redemptions

Redeem a reward for a customer. Validates balance, tier and the reward offer atomically.

curl -X POST https://loyalpanther.app/api/v1/redemptions \
  -H "Authorization: Bearer lp_live_…" -H "Content-Type: application/json" \
  -d '{
    "identifier": { "type": "loyalty_code", "value": "F55EA00D" },
    "reward_id": "…"
  }'

{ "customer": { "loyalty_code": "F55EA00D", "name": "Sophie", "points_balance": 1215 },
  "reward_id": "…" }

Errors: 404 customer/reward not found · 422 insufficient_points · 422 tier_locked.

POST/api/v1/customers

Create (register) a customer. If the email already exists, the existing customer is returned with existing: true.

curl -X POST https://loyalpanther.app/api/v1/customers \
  -H "Authorization: Bearer lp_live_…" -H "Content-Type: application/json" \
  -d '{ "name": "Sophie", "email": "sophie@example.com", "birthday": "1990-05-01" }'

{ "customer": { "loyalty_code": "A1B2C3D4", "name": "Sophie", "email": "sophie@example.com", "points_balance": 0 },
  "existing": false }

GET/api/v1/customers?email=…

Look up a customer by email or loyalty_code query parameter.

POST/api/v1/orders

Award points for an order with line items. Points = base (order total × earn-rate × bonus × tier) plus any matching product earn rules (bulk / category / specific product, configured in the dashboard). Idempotent via external_ref.

curl -X POST https://loyalpanther.app/api/v1/orders \
  -H "Authorization: Bearer lp_live_…" -H "Content-Type: application/json" \
  -d '{
    "identifier": { "type": "loyalty_code", "value": "F55EA00D" },
    "external_ref": "order-12345",
    "line_items": [
      { "name": "Cappuccino", "category": "drinks", "quantity": 2, "unit_price_cents": 350 },
      { "name": "Cake", "category": "food", "quantity": 1, "unit_price_cents": 450 }
    ]
  }'

{ "customer": { "loyalty_code": "F55EA00D", "name": "Sophie", "points_balance": 1300 },
  "points_added": 26, "base_points": 11, "product_points": 15,
  "rules_applied": ["2 cappuccinos = 15 pts"], "duplicate": false }

name and category are matched case-insensitively against your earn rules. If amount_centsis omitted, it's summed from the line items.