Developer API · v1

One contract. Two implementations.

The iOS app and both consoles are built against this exact contract: a file-backed sandbox server you can run from the repo today, and a Postgres/RLS production path that behaves identically.

Base URL http://127.0.0.1:8787SANDBOXAuth Authorization: Bearer <token>Errors { "error": string } with 4xx/5xxMoney integer cents USD

Run it yourself: node backend/server.mjs from the platform repo serves this API and both consoles at 127.0.0.1:8787. Production DNS is pending; the contract is final.

Endpoints

Sessions

Bearer-token auth; roles are owner, manager, viewer, admin.
POST /api/session{email, password} → {token, role, name, merchantID} · 401 on invalid credentials
GET /api/meCurrent identity + merchant record

Merchant

Own resources only — cross-merchant access 404s by design.
GET/PUT /api/storefrontProfile, storefront, hours, menu
GET/POST /api/offersOffers; creation always lands as a draft
POST /api/offers/:id/publishRequires {confirmReviewed: true} — 422 otherwise; draft→published only
POST /api/offers/:id/retirepublished → retired
GET/POST /api/campaignsObjectives → eligibility rules
GET /api/orders · PATCH /api/orders/:idaccept | reject | fulfill | refund — legal transitions only, 409 on violation
GET /api/analyticsOrders, revenue, commission, per-offer conversion
GET /api/payoutsCommission statements (sandbox-labeled until a payment provider exists)
POST /api/assistant/draft{goal} → labeled AI draft; drafts are never auto-published
GET /api/auditOwn audit trail

Public (traveler surfaces)

Only verified merchants' live offers ever appear.
GET /api/public/offerspublished ∧ started ∧ unexpired ∧ under cap ∧ merchant verified ∧ not suspended
GET /api/public/storefront/:merchantIDVerified merchant storefront
POST /api/public/ordersIdempotent on idempotencyKey — resubmission returns the original order
GET /api/public/orders/:idOrder + full status history

Admin

Role admin: verification lifecycle, moderation, flags, health, global audit.
POST /api/admin/merchants/:id/(verify|suspend|reinstate)Merchant lifecycle
POST /api/admin/offers/:id/(suspend|reinstate)Platform moderation — suspension removes offers from the public feed immediately
GET /api/admin/health · /flags · /auditOps surface

Quick start

Public surface — cURL
# Live offers near the traveler (public, no session in sandbox)
curl "$BASE_URL/api/public/offers"

# Submit an order — idempotent, safe to retry
curl -X POST "$BASE_URL/api/public/orders" \
  -H "content-type: application/json" \
  -d '{
    "offerID": "offer_tacoloco_tuesday",
    "items": [{ "name": "Street tacos", "priceCents": 1450, "qty": 1 }],
    "idempotencyKey": "d290f1ee-6c54-4b01-90e6-d701748f0851"
  }'
Merchant session — JavaScript
const res = await fetch(`${BASE_URL}/api/session`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ email, password }),
});
const { token } = await res.json();

// Publish an offer — the contract forces explicit review
await fetch(`${BASE_URL}/api/offers/${offerId}/publish`, {
  method: "POST",
  headers: {
    authorization: `Bearer ${token}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({ confirmReviewed: true }),
});

Contract invariants

Guarantees the test suite enforces.

These aren’t docs promises — each one is covered by the platform’s API contract tests.

  1. Draft offers never appear publicly; publishing requires explicit merchant review confirmation.
  2. Order transitions follow the state machine — no skipping merchant acceptance.
  3. Merchants cannot read or mutate other merchants' resources (404-scoped).
  4. Admin suspension removes a merchant's offers from the public feed immediately.
  5. Idempotency keys deduplicate order submissions.
  6. AI assistant output is labeled fixture/live and only ever produces drafts.
  7. All money is integer cents USD. Sensitive actions append to the audit log.
Build with usRequest API access

Questions about integrating a POS or booking system? Start with the Partner Program.