Appearance
API Quick-Start
Get a tenant provisioned, a token issued, and your first loyalty transaction recorded in under 10 minutes.
Prerequisites
- A Stackd super-admin account (for tenant provisioning) or an existing tenant account (skip to step 2).
- An HTTP client — examples below use
curl. The Bruno collection covers the same requests with saved environments.
Step 1 — Provision a tenant (super-admin only)
If you are integrating on behalf of an existing tenant, skip to step 2.
bash
curl -X POST https://yourloyalty.app/api/v1/platform/tenants \
-H "Authorization: Bearer <super_admin_token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Coffee",
"slug": "acme-coffee",
"email": "owner@acmecoffee.co.za",
"plan_id": 1,
"billing_cycle": "monthly",
"payment_method": "payfast"
}'The response includes the tenant object. Note the id — you will need it to issue a scoped token.
Platform endpoints require a token with no tenant scope (platform:*). Issue one from the super-admin Filament panel under Platform → API Tokens.
Step 2 — Issue a tenant API token
Tokens are issued from the TenantAdmin panel under Settings → API Tokens, or via the API if you have an existing tenant token with tokens:write scope.
From the admin UI:
- Log in as a tenant owner or manager.
- Go to Settings → API Tokens → New Token.
- Enter a descriptive name (e.g. POS Integration).
- Select the scopes your integration needs (start with the minimum required — see the scopes table).
- Copy the token immediately — it is only shown once.
Store the token in an environment variable or secret manager. Never commit it to source control.
bash
export STACKD_TOKEN="<your_token_here>"Step 3 — Create or look up a customer
Before recording a transaction you need a customer ULID. Use POST /api/v1/customers to upsert — if a customer with this phone or email already exists, their record is returned.
bash
curl -X POST https://yourloyalty.app/api/v1/customers \
-H "Authorization: Bearer $STACKD_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jane",
"last_name": "Smith",
"phone": "0821234567"
}'Response:
json
{
"data": {
"ulid": "01HWZK8X4VXXXX",
"first_name": "Jane",
"last_name": "Smith",
"phone": "0821234567",
"email": null,
"points_balance": 0,
"created_at": "2026-05-20T08:00:00Z"
}
}Save data.ulid — this is the customer's permanent identifier in all subsequent requests.
Step 4 — Record a points transaction
Note: The public API records pre-authorised transactions directly. The PIN-based QR flow (used by the Operator POS) is a separate path for walk-in customers — it is not available through the API.
bash
curl -X POST https://yourloyalty.app/api/v1/transactions \
-H "Authorization: Bearer $STACKD_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customer_ulid": "01HWZK8X4VXXXX",
"transaction_type_id": 2,
"spend_amount_cents": 4750
}'Response:
json
{
"data": {
"ulid": "01HWZK9AYYYYYYY",
"rule_type": "points",
"spend_amount_cents": 4750,
"points_awarded": 20,
"stamps_awarded": null,
"status": "completed",
"voided_at": null,
"customer_ulid": "01HWZK8X4VXXXX",
"created_at": "2026-05-20T08:01:00Z"
}
}The customer now has 20 additional points. points_awarded reflects the current transaction only.
Step 5 — Check the customer's updated balance
bash
curl https://yourloyalty.app/api/v1/customers/01HWZK8X4VXXXX \
-H "Authorization: Bearer $STACKD_TOKEN"data.points_balance is the current total across all ledger entries.
What's next
| Task | Endpoint |
|---|---|
| Redeem points for a reward | POST /api/v1/redemptions |
| Mark a redemption as fulfilled | POST /api/v1/redemptions/{ulid}/fulfil |
| Void an incorrect transaction | POST /api/v1/transactions/{ulid}/void |
| Send a coupon to a segment | Admin UI → Coupons (not available via API) |
| Pull a transaction summary report | GET /api/v1/reports/summary?from=2026-05-01&to=2026-05-31 |
Full request/response schemas are in the OpenAPI spec. Import the Bruno collection to explore every endpoint interactively.