Skip to main content

API overview

The GrinGo API is a single Hono app running on Cloudflare Workers. Every route is versionless — there is no /v1 prefix. Auth is a session JWT signed by us, issued after a native OAuth exchange with Apple or Google.

Base URLs​

EnvironmentURL
Local devhttp://localhost:8787
ProductionSet by Cloudflare Workers deploy — TBD DNS

Auth​

Most routes require:

Authorization: Bearer <session JWT>

The session JWT is obtained by POST /auth/session — exchange an Apple / Google ID token for a session. See architecture/auth for the full model. The token is HS256-signed with SESSION_SECRET, valid for 30 days by default.

Public endpoints (no session required): GET /health, POST /auth/session, POST /webhooks/infinia/:secret.

Content type​

  • Requests: application/json (Zod-validated via @hono/zod-validator).
  • Responses: application/json.

Errors​

  • 4xx — validation, auth, business rules. Body: { "error": "<user-facing message>" }. Zod validation errors return 400 with zod-validator's default shape.
  • 5xx — internal / upstream. Body: { "error": "<message>" }.
  • Infinia upstream failures surface as 502 with a generic message; details are logged server-side.

Rate limiting​

Not implemented at the API layer for MVP (see roadmap Phase 1.5). Cloudflare's default DDoS protection applies.

Idempotency​

Where relevant (payment creation, withdraws), the API derives Infinia idempotency handles deterministically from the internal payment id — a retried API call returns the original resource instead of creating a duplicate. See payment-providers → Idempotency.

Route index​

Full request/response shapes in endpoints. Webhook contract in webhooks.

What changed from the previous architecture​

If you're familiar with the old API surface, the material differences:

  • Auth is now POST /auth/session { provider, idToken } returning our own session JWT — not a Privy access token verified against Privy's JWKS.
  • No POST /virtual-accounts endpoint — the user's home-currency account is auto-provisioned at KYC completion. The funding instructions are surfaced via GET /me.
  • No POST /transactions/:id/confirm — there's no on-chain leg to confirm. PIX and withdraws advance via Infinia webhooks alone.
  • POST /pix/quote is a new step — locks the FX rate for 60s so the review screen shows a firm price.
  • Bank details columns for sensitive fields (accountNumber, sortCode, iban, bic) are stored encrypted; the API returns masked values (****1234) except during in-app edit with an explicit reveal.
  • POST /webhooks/infinia/:secret replaces POST /webhooks/bridge — the :secret path segment is the first-line defence; verification is behind a swappable strategy.
  • New endpoints for data subject requests (GDPR).