/api/*, PostgREST /rest/v1/*, GoTrue /auth/v1/*, Storage /storage/v1/*, and Realtime. They were built independently (most are upstream Supabase services Powabase ships) and have slightly different conventions. This page documents the shared patterns and where they diverge.
For specific endpoints, see the relevant /api-reference/* pages.
Header conventions
Two headers on every authenticated request:apikey header is required by Kong’s key-auth plugin for routing. The Authorization header is what the downstream service (PostgREST, GoTrue, etc.) actually verifies for role assignment. They can be different values: apikey: Anon + Authorization: Bearer <user-access-token> is the standard browser-side pattern after sign-in.
For Realtime WebSocket connections, both pieces come in as query parameters: ?apikey=<KEY>&vsn=1.0.0. WebSocket APIs in browsers don’t let you set headers on the upgrade request.
PUT vs PATCH for updates
The agentic/api/* surface is inconsistent here:
| Resource | Update method |
|---|---|
/api/agents/{id} | PATCH |
/api/tools/{id} | PUT |
/api/orchestrations/{id} | PUT |
/api/sources/{id} | PATCH |
/api/workflows/{id} | PATCH |
Prefer: resolution=merge-duplicates (which is really an upsert).
GoTrue uses PUT on /user and /admin/users/{id} — both partial.
Storage uses PUT to overwrite an object at the same path.
If you’re writing client code, match what the docs say for each endpoint. Don’t try to derive PUT-vs-PATCH from first principles.
Path versioning
The BaaS services have versioned paths:/auth/v1/, /rest/v1/, /storage/v1/, /realtime/v1/. The agentic /api/* surface is not versioned in the path.
This isn’t a hard product commitment — it’s just where we are today. If the agentic surface needs versioning later, the most likely move is to introduce /api/v2/* alongside the existing /api/*. If you want maximum future-proofing, build a tiny indirection layer in your client code that constructs the URL from a constant.
Resource naming
Most resources use plural-noun URLs:/api/agents/{id}(not/api/agent/{id})/api/knowledge-bases/{id}(with a hyphen)/api/ai-provider-keys(also hyphenated)
/api/ai-provider-keys/platform_supported(underscore — this is the one most people get wrong)/api/config/kb-defaults(hyphen)
Error envelopes
The agentic/api/* surface uses {"error": "<message>"} as the standard error envelope. Some endpoints add code or error_code for machine-readable identifiers:
code, message, details, hint:
code is the Postgres SQLSTATE (5-character class).
GoTrue uses yet another shape — {"error": "code", "error_description": "..."} for OAuth-style errors and {"msg": "..."} for some validation errors:
{"statusCode", "error", "message"}:
response.status first, then try .error || .message || .error_description || .msg in that order.
Pagination
PostgREST uses HTTPRange headers for offset pagination and the Content-Range response header for the total count:
/api/* surface uses query string pagination with explicit limit and offset params:
limit=50 with a cap of 200.
For listing endpoints that return many items (KB sources, source page texts), prefer the agentic endpoints’ explicit pagination. The PostgREST Range approach is fine but easier to forget.
Response shapes for lists
Inconsistency worth knowing about. List endpoints across the agentic surface use a wrapped shape:{ items: [...], total, ... }. The audit-flagged-and-corrected wrong examples in PR A (#8) were spots where this inconsistency tripped the docs themselves.
When in doubt, log the response and read its shape. The reference pages should show the actual shape — file an issue if you find one that doesn’t.
Idempotency
The agentic/api/* surface does not honor an Idempotency-Key header (despite the convention being common). Idempotency is computed internally for billing charges (see Billing model) but the API itself doesn’t deduplicate user-supplied retry headers.
In practice: retrying a POST /api/agents/{id}/run/stream from a timeout creates a second run. Build your retry logic to handle that — usually by waiting longer between retries, or by checking session state before retrying.
PostgREST doesn’t honor Idempotency-Key either. For inserts that need idempotency, use unique constraints + upserts (Prefer: resolution=merge-duplicates).
Common headers worth knowing
A short list of headers that change behavior across multiple endpoints:| Header | Where | Effect |
|---|---|---|
Prefer: return=representation | PostgREST writes | Return inserted/updated rows in response body |
Prefer: count=exact | PostgREST reads | Include total count in Content-Range |
Prefer: resolution=merge-duplicates | PostgREST POST | Upsert semantics |
Accept-Profile: <schema> | PostgREST reads | Target a non-public schema |
Content-Profile: <schema> | PostgREST writes | Same, for writes |
Accept: application/vnd.pgrst.object+json | PostgREST reads | Return single object, not array; fail if not exactly one row |
x-upsert: true | Storage upload | Overwrite existing object |
Range: 0-19 | PostgREST reads | Offset-based slicing |
Authentication conventions across services
| Service | Auth shape |
|---|---|
Agentic /api/* | apikey + Authorization: Bearer |
PostgREST /rest/v1/* | apikey + Authorization: Bearer |
GoTrue /auth/v1/* | apikey + Authorization: Bearer (Anon Key OR user access token) |
Storage /storage/v1/* | apikey + Authorization: Bearer |
Realtime /realtime/v1/* (WS) | ?apikey=<KEY> query parameter |
Realtime /realtime/v1/api (REST) | apikey + Authorization: Bearer headers |
/api/webhooks/{id} | Authorization: Bearer <webhook_secret> or ?token=<secret> — no apikey |
apikey, only the webhook secret.
Next steps
Auth & Connection
Where the headers all come from and how to assemble them.
Glossary
The terminology that goes alongside the conventions.
Common pitfalls
The errors that come from getting the conventions wrong.
PostgREST advanced
The
Prefer and Accept header patterns that change PostgREST behavior.