Skip to main content

Integrate Zelty (POS auto-mint and burn for Loyalty)

Wire a Zelty restaurant to a Keyban Loyalty application so paid orders automatically credit loyalty points to the customer, and so cashiers can redeem points back from the POS at the next visit. The mechanics rely on a single Zelty webhook (order.ended) inbound to Keyban, plus a public burn endpoint Keyban exposes for the POS to call.

How the integration runs

Auto-mint after a paid order

  1. Customerpays at the counter
  2. Zelty POScloses the order
  3. Zelty backendemits webhooks
  4. Keybanloyalty + indexer
  1. 1pays for the order
  2. 2order.status = 255 (closed)
  3. 3POST /loyalty/zelty/webhooks/order.ended
  4. 4verify HMAC SHA-256 signature
  5. 5match restaurant + Zelty customer → mint
When the cashier closes a Zelty order with status 255, Zelty fires its order.ended webhook. Keyban verifies the HMAC SHA-256 signature, looks up the matching restaurant + Keyban account, and queues an on-chain mint that the indexer materialises into a loyalty order.

The mint only happens when all of the following hold; otherwise the webhook is logged and skipped:

  • Order status equals 255 (Zelty's « closed » terminal state).
  • The order carries a customer (the cashier scanned the loyalty card or registered the customer at checkout).
  • The HMAC signature on the webhook header matches Keyban's shared secret (Keyban rejects the call otherwise).
  • The Zelty restaurant ID is bound to a Keyban application.

Burn from the POS at redemption time

  1. Customerredeems points
  2. Zelty POSinitiates the burn
  3. Keybanloyalty + chain
  4. Ledgersettles the burn
  1. 1asks to redeem N points
  2. 2POST /loyalty/zelty/burn/:restaurantId/:customerId
  3. 3verify order belongs to the customer
  4. 4burn N points
  5. 5updated balance
At redemption, the POS calls Keyban's public burn endpoint with the Zelty order ID and the points to burn. Keyban verifies the order against Zelty, decrements the optimistic balance, then burns the points on-chain.

The burn endpoint is public (no API key required) but every call re-checks the order against Zelty before touching the balance — a POS cannot burn points on someone else's account.

One-time setup

Prerequisites

  • Access to your Zelty back-office: https://bo.zelty.fr.
  • Admin rights on the Keyban Admin Console with zelty/settings: ['edit'] permission.
  • A Loyalty application already provisioned (network and reward tiers in place).

Step 1 — Generate a Zelty API key

Option A — via the marketplace

If Keyban appears in your Zelty marketplace:

  1. Open https://bo.zelty.fr/marketplace.
  2. Find Keyban and open its integration page.
  3. Generate or retrieve your API key.

Option B — via Zelty's manual form

If Keyban is not yet on your marketplace:

  1. Submit the request at https://zelty.fr/forms/api.
  2. Per Zelty's documented turn-around, keys are issued Tuesday and Thursday end of day.

Step 2 — Create the transaction method and capture its ID

Keyban needs a Zelty transaction method it can attribute its on-chain settlements to. The display name does not matter — Keyban identifies the method by its Zelty ID, not its name.

  1. Open https://bo.zelty.fr/transaction-methods.
  2. Go to Configurations → Payment Methods, click Add.
  3. Pick a name your cashiers will recognise (« Keyban » is a sensible default but anything works) and a special type (None is fine).
  4. Save the method, then open it again — the Transaction method ID shows up in the URL or the detail panel. Copy it; you'll paste it in Step 3.
  5. Optional: define the method at the brand level if you want it shared across multiple restaurants, or at the restaurant level for a single location.

Step 3 — Wire the integration in the Keyban Admin Console

  1. Sign in to the Keyban Admin Console and open Organization → Integrations → Zelty.
  2. Paste the API key from Step 1.
  3. Paste the Transaction method ID from Step 2.
  4. Save.

On save, Keyban automatically registers an order.ended webhook against your Zelty account (target: {keyban-host}/loyalty/zelty/webhooks/order.ended) and stores the shared HMAC secret. From there, every closed order at the bound restaurant flows through the Auto-mint sequence above.

To bind a specific restaurant to a Loyalty application, open the application in the Admin Console and pick the Zelty restaurant from the Point of sale picker. The restaurant ↔ application link is exclusive — see Constraints below.

Constraints to know about

  • One restaurant ↔ one application. A given Zelty restaurant ID can be bound to a single Keyban application, and a given Keyban application accepts at most one Zelty restaurant. Switching the binding is possible from the Admin Console, but every prior accrual on the old application stays where it was minted.
  • The customer must exist in Zelty. Auto-mint relies on the order.customer field. A walk-in checkout without a registered customer logs « no customer associated with order… skipping » and no points are minted.
  • Only status === 255 triggers a mint. Partial closures, cancels, and refunds carry different statuses and are deliberately ignored.
  • Webhook authentication is HMAC SHA-256, not an API key. Keyban rejects any call whose header signature does not match the secret installed in Step 3.

Troubleshooting

SymptomFirst check
« Webhook received but nothing minted »Order status (must be 255) and order.customer (must be set)
« 401/403 on /loyalty/zelty/webhooks/... »HMAC mismatch — re-save the integration in the Admin Console to rotate the shared secret
« API key looked good but Step 3 fails »Marketplace key vs. manual-form key delay — wait for the next Tuesday/Thursday batch if the form route was used
« Burn returns 400 from the POS »The order ID, the customer ID and the restaurant ID must all match — Keyban re-verifies the triplet against Zelty before debiting

Next steps