Skip to content

Authentication Guide

The Stackd API uses Laravel Sanctum personal access tokens. Every request to a tenant-scoped endpoint must carry a valid token in the Authorization header.


Issuing a Token

Tokens are issued from within the TenantAdmin panel or via the API itself.

  1. Log in to your TenantAdmin at https://{slug}.yourloyalty.app/admin
  2. Navigate to Settings → API Tokens
  3. Click New Token, give it a descriptive name, and select the scopes you need
  4. Copy the token immediately — it is shown once only

Via API

If you are provisioning tokens programmatically (e.g. in a CI pipeline or integration bootstrap script), use the token endpoint. This requires an active web session, not a token — so it is suited to server-to-server provisioning flows where the calling service has already authenticated via the web login.

http
POST /api/v1/tokens
Content-Type: application/json
Authorization: Bearer <existing-token-with-any-scope>

{
  "name": "My Integration",
  "scopes": ["customers:read", "transactions:write"]
}

Response (201):

json
{
  "data": {
    "id": 7,
    "name": "My Integration",
    "token": "8|aBcDeFgHiJkLmNoPqRsTuVwXyZ...",
    "abilities": ["customers:read", "transactions:write"],
    "created_at": "2026-05-20T09:00:00+02:00"
  }
}

Store the token value. It will not be returned again.


Using a Token

Pass the token as a Bearer token in every API request:

http
GET /api/v1/customers
Authorization: Bearer 8|aBcDeFgHiJkLmNoPqRsTuVwXyZ...

Scopes

Each token has one or more scopes (called "abilities" in Sanctum). A request to a scope-protected endpoint with a token that lacks the required scope returns 403 Forbidden.

ScopeEndpoints covered
customers:readGET /customers, GET /customers/{ulid}, GET /customers/{ulid}/transactions
customers:writePOST /customers, PATCH /customers/{ulid}
transactions:writeGET /transactions/{ulid}, POST /transactions/{ulid}/void
rewards:readGET /rewards, POST /redemptions, GET /redemptions/{ulid}, POST /redemptions/{ulid}/fulfil
coupons:readGET /coupons, POST /coupons/{ulid}/redeem, GET /coupons/{ulid}/redemptions
reports:readGET /reports/summary, GET /reports/customers/top, GET /reports/coupons

The GET /api/v1/tokens, POST /api/v1/tokens, and DELETE /api/v1/tokens/{id} endpoints require only a valid authenticated session — no specific scope.

Platform endpoints (/api/v1/platform/*) require the caller to be authenticated as a super-admin user. They do not use token scopes.

Principle of least privilege: issue tokens with only the scopes your integration actually needs. A read-only reporting integration should carry only reports:read.


Rate Limits

Authentication stateLimit
Authenticated (valid token)60 requests / minute per user
Unauthenticated10 requests / minute per IP

When you exceed the limit, the API returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait.

http
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/problem+json

{
  "type": "https://httpstatuses.com/429",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Too Many Attempts."
}

Revoking a Token

http
DELETE /api/v1/tokens/{id}
Authorization: Bearer <any-valid-token>

Use GET /api/v1/tokens to list your tokens and find the id.


Security Recommendations

  • Never commit tokens to source control. Use environment variables or a secrets manager.
  • Rotate tokens periodically. Issue a new token, update your integration, then revoke the old token.
  • Use narrow scopes. A compromised customers:read token cannot void transactions.
  • Use HTTPS only. All requests must use TLS. Plain HTTP is rejected.

Stackd Loyalty Platform