Data API
Every app answers an HTTP API on its own host, the same one its pages use. A backend, a script or a mobile app can read and write your tables, call your server actions and run your named queries, and the same access policies decide what each caller gets. You write no routes: the API comes from db/ and server/.
Where it is
APP=https://dev-teamday.apps.appbricx.com # the dev site; production: https://teamday.apps.appbricx.com or your domainThe host decides the environment: the dev site reads and writes the dev database, the production host and custom domains production's. Your agent can call the same API without keys with the call_app tool.
Keys and sessions
| Header | When | What |
|---|---|---|
Authorization: Bearer <key> | Always | A project key: the browser key (dpk_c_…, every page carries it; needs an Origin of the app), or a server key (dpk_s_…, made in the console under the project's Settings, API keys). |
X-Appbricx-Data-Api: 1 | On /__appbricx/api, queries and actions | Marks a data-API call. |
X-Appbricx-App-Session: <token> | To act as a signed-in user | The token POST /__appbricx/auth/login answers (or the session cookie). Without it, the call is a signed-out visitor. |
A server key needs no Origin and may act as a user with X-Appbricx-App-User and X-Appbricx-App-Role. Treat it like a database password and keep it on servers.
KEY=$(curl -s "$APP/" | grep -o 'dpk_c_[A-Za-z0-9_-]*' | head -1)
H=(-H "Authorization: Bearer $KEY" -H "Origin: $APP" -H 'X-Appbricx-Data-Api: 1')
SESSION=$(curl -sS -X POST "$APP/__appbricx/auth/login" -H "Authorization: Bearer $KEY" \
-H 'content-type: application/json' -d '{"email": "hr@demo.teamday.app", "password": "…"}' | jq -r .token)
U=(-H "X-Appbricx-App-Session: $SESSION")Tables
| Route | Does |
|---|---|
GET /__appbricx/api/v1/<table> | List: where, filter, sort, limit, offset or cursor, embed |
GET /__appbricx/api/v1/<table>/<id> | One row |
POST /__appbricx/api/v1/<table> | Create |
PATCH /__appbricx/api/v1/<table>/<id> | Update |
DELETE /__appbricx/api/v1/<table>/<id> | Delete (to the trash on a ctx.soft_delete table) |
PATCH …/<id>/transition, POST …/<id>/restore | Move a lifecycle state; bring a row back from the trash |
GET …/<table>/aggregate, GET …/<table>/search?q=, GET /__appbricx/api/v1/search | count, sum, avg, min, max by group or time bucket; full-text search |
GET /__appbricx/api/v1/openapi.json | The app's OpenAPI document |
# Published experiences under ₹2,000 a head, signed out (the public policy decides)
curl -sS -G "$APP/__appbricx/api/v1/experience" "${H[@]}" \
--data-urlencode 'where={"status":"published"}' \
--data-urlencode 'filter=[{"field":"price","op":"lte","value":2000}]' \
--data-urlencode 'sort={"field":"title","dir":"asc"}'
# My bookings with the experience's title, as the signed-in booker
curl -sS -G "$APP/__appbricx/api/v1/booking" "${H[@]}" "${U[@]}" --data-urlencode 'embed=experience(title)'
# Cancel one
curl -sS -X PATCH "$APP/__appbricx/api/v1/booking/$BOOKING/transition" "${H[@]}" "${U[@]}" \
-H 'content-type: application/json' -d '{"field": "status", "to": "cancelled"}'Answers are {"ok": true, "rows": [...], "rowCount": n, "fields": [...]}; with a cursor they add nextCursor and hasMore. Columns marked @hide <role> are left out for that role; a table marked @api off isn't served.
Actions, queries and batches
# A server action (server/bookings.ts confirmBooking), as an ops user
curl -sS -X POST "$APP/__appbricx/actions/confirmBooking" "${H[@]}" "${U[@]}" \
-H 'content-type: application/json' -H "Idempotency-Key: confirm-$BOOKING" \
-d "{\"booking\": \"$BOOKING\"}"
# → {"ok": true, "result": {"ref": "BK-ACME1", "status": "confirmed"}, "runId": "…"}
# A named query (db/queries/experience-bookings.sql)
curl -sS -X POST "$APP/__appbricx/queries/experience-bookings" "${H[@]}" "${U[@]}" \
-H 'content-type: application/json' -d "{\"experience\": \"$EXPERIENCE\"}"
# Up to 20 calls in one round trip, each with its own status
curl -sS -X POST "$APP/__appbricx/batch" "${H[@]}" "${U[@]}" -H 'content-type: application/json' \
-d '[{"method": "GET", "path": "/__appbricx/api/v1/booking?limit=5"}, {"method": "GET", "path": "/__appbricx/api/v1/experience?limit=5"}]'An action's input is checked against its schema before it runs; the same Idempotency-Key twice answers the first result without running again.
Also on the app host
- Sign-in:
/__appbricx/auth/*(sign-up, login, sign-in links, social sign-in, two-factor, sessions). - Files:
POST /__appbricx/files/sign, then upload; download withGET /__appbricx/files/<id>. - Webhooks:
POST /__appbricx/hooks/<name>delivers to awebhook(name)workflow. - Realtime and topics:
/__appbricx/realtime/*,/__appbricx/topics/<name>/subscribe(server-sent events). - AI:
POST /__appbricx/ai/chaton the project's own key.
Limits and errors
- 600 requests a minute per project (1,200 with a server key), a quarter of that per IP and per user:
429 RATE_LIMITEDwithRetry-After. 403 TABLE_FORBIDDEN: the role may not use the table;400 RLS_VIOLATION: a policy refused the row;422 RULE_REFUSED: your trigger's message;409 CONFLICT: a unique value is taken.- Errors answer
{"ok": false, "error": {"code": "…", "message": "…"}}. Ask your agent to runexplainon a refused request to see which policy decided.