Platform API reference

Base URL: https://api.sadakio.com/platform/v1 Auth: Authorization: Bearer pk_YOUR_KEY — keys come from self-serve signup, scopes are read and write. Machine contract: platform/v1/openapi.yaml

Three rules hold on every endpoint:

Businesses

GET /businesses

Everything you have provisioned, newest first.

curl https://api.sadakio.com/platform/v1/businesses \
  -H "Authorization: Bearer pk_YOUR_KEY"
{"data": [{"id": 42, "name": "Kahve Dünyası", "slug": "kahve-dunyasi",
           "managed": true, "join_url": "https://app.sadakio.com/join/kahve-dunyasi",
           "created_at": "2026-09-22T10:00:00Z"}]}

POST /businesses

Creates a real, isolated tenant. program picks the loyalty preset, in managed mode the engine decides what an event earns.

curl -X POST https://api.sadakio.com/platform/v1/businesses \
  -H "Authorization: Bearer pk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Kahve Dünyası", "sector": "kafe", "program": "damga"}'

The response is 201 with the business object.

GET /businesses/{id}

One business, with its join_url. Your guests sign themselves up on that page.

GET /businesses/{id}/qr.png

The business's guest sign-up QR as a print-ready branded PNG. «A QR generator for your cafés» is literally this call.

curl https://api.sadakio.com/platform/v1/businesses/42/qr.png \
  -H "Authorization: Bearer pk_YOUR_KEY" -o cafe-qr.png

Guests

GET /businesses/{id}/guests

Paginated guest list. q filters by name, cursor and limit follow exactly the Business API contract, phones come back masked (KVKK).

{"data": [{"id": 184, "name": "Elif", "masked_phone": "+•••••••4579",
           "joined_at": "2026-09-16T08:31:00Z"}],
 "next_cursor": 184}

POST /businesses/{id}/guests

Registration by phone. A new guest answers 201, a second attempt with the same phone answers 200 with created: false: a retried signup form tells «already there» from «just created» without guessing.

DELETE /businesses/{id}/guests/{guest_id}

KVKK erasure. The partner is the data controller for their businesses: when your guest demands deletion you carry it out. The guest is anonymized, counters are zeroed, the audit ledger keeps only ids and numbers.

Counters and events

POST /businesses/{id}/guests/{gid}/adjust

Adds delta to a named counter. Raw and direct: stamps, points, tokens, whatever you name.

curl -X POST .../guests/184/adjust \
  -H "Authorization: Bearer pk_YOUR_KEY" \
  -H "Idempotency-Key: visit-2026-09-22-184" \
  -H "Content-Type: application/json" \
  -d '{"counter": "damga", "delta": 1}'
{"data": {"counter": "damga", "value": 5, "entry_id": 901, "replayed": false}}

POST /businesses/{id}/guests/{gid}/events

Managed mode: you say «a visit happened» and the engine decides what it earns from the business's own program. amount carries the ticket total, stamps the stamp count.

GET /businesses/{id}/guests/{gid}/counters

Every counter of the guest in one flat object: {"damga": 5, "puan": 120}. An unknown guest id answers 404, same as adjust and events.

Protections

Protection Value On breach
Rate 30 requests per second, 50,000 per day 429 with Retry-After
Magnitude per write 1,000 422 validation_failed
Daily total magnitude 100,000 429 budget_exceeded, resets at midnight UTC

Budgets protect the partner from their own bugs too: code stuck in a loop cannot mint unlimited balance. If a limit is tight for your account, ask [email protected] to raise it.