Data API
List, read, create, update and delete rows of the app's tables, plus aggregates, full-text search, lifecycle transitions, external tables, named queries and raw SQL for trusted backends.
On this page
- Authentication
- Endpoints
- GET /queries
- POST /queries/*
- GET /api/v1/search
- GET /api/v1/openapi.json
- GET /api/v1/:table
- GET /api/v1/:table/aggregate
- GET /api/v1/:table/:id
- POST /api/v1/:table
- PATCH /api/v1/:table/:id
- DELETE /api/v1/:table/:id
- GET /api/v1/ext/:entity
- GET /api/v1/ext/:entity/aggregate
- GET /api/v1/ext/:entity/:id
- POST /api/v1/ext/:entity
- PATCH /api/v1/ext/:entity/:id
- DELETE /api/v1/ext/:entity/:id
- PATCH /api/v1/:entity/:id/transition
- POST /data/query
- POST /data/exec
- POST /data/schema
- POST /data/migrate
Base URL: your app's origin ($APP_ORIGIN): the published host, a custom domain, or the platform origin for the builder preview. Errors answer { ok: false, error: { code, message } } unless noted. Add x-appbricx-env: dev or prod to pick the database explicitly; otherwise the host decides (preview means dev, a published host means prod).
Authentication
- App key + data marker
- A project API key (dpk_c_… in browsers, bound to the app's origins; dpk_s_… on servers) or the builder preview token, plus the data-API marker header. With an app session (the header, or the appbricx_app_session cookie) the call acts as that user under row-level security; without one it acts as a guest. A server key may assert x-appbricx-app-user, x-appbricx-app-role and x-appbricx-app-org.
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user) - App key + signed-in user
- A project key or preview token and a signed-in app session (header or cookie). Signed-out callers get 401.
Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION - Server key
- A server-tier project API key (dpk_s_…, created in the editor under Backend, API). Browser keys and end-user sessions are refused.
Authorization: Bearer $SERVER_KEYx-appbricx-data-api: 1
Endpoints
List named queries
GET/__appbricx/queries
The app's named queries with their descriptions and declared parameters.
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Response
{
"ok": true,
"queries": [
{
"name": "reports/monthly",
"description": "Revenue per day",
"params": {
"month": {
"type": "string",
"required": true
}
},
"allow": [
"end_user",
"workflow"
]
}
]
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
curl "$APP_ORIGIN/__appbricx/queries" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/queries`, {
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:51
Run a named query
POST/__appbricx/queries/*
Run a named query (a .sql file kept on the server) with parameters. The path after /queries/ is the query name; folders are allowed (reports/monthly, or URL-encoded).
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Path parameters
| Name | Type | Description |
|---|---|---|
*required | string | The query name. |
Body
| Name | Type | Description |
|---|---|---|
paramsoptional | object | Declared parameters. A flat body is read as the parameter map too. |
Response
{
"ok": true,
"rows": [
{
"day": "2026-10-01",
"revenue": 812
}
],
"rowCount": 1,
"fields": [
{
"name": "day",
"type": "1082"
}
],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 400 | PARAMS_INVALID | Invalid query name, or a missing / mistyped parameter. |
| 404 | (query error) | "Unknown query". |
An admin app session plus x-appbricx-admin: 1 elevates SELECT queries so dashboards can read across users. Mutations stay row-scoped.
curl -X POST "$APP_ORIGIN/__appbricx/queries/reports/monthly" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION" \
-H "content-type: application/json" \
-d '{"params":{"month":"2026-10"}}'const res = await fetch(`${APP_ORIGIN}/__appbricx/queries/reports/monthly`, {
method: "POST",
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
"content-type": "application/json",
},
body: JSON.stringify({
"params": {
"month": "2026-10"
}
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:64
Full-text search
GET/__appbricx/api/v1/search
Ranked full-text search over the generated search vectors of the tables you name, under the caller's row-level security.
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Query parameters
| Name | Type | Description |
|---|---|---|
qrequired | string | Search text (web-search syntax: quotes, OR, -word). |
tablesrequired | string | Comma-separated table names (up to 20). Tables the role may not read are skipped. |
limitoptional | number | Rows per table (default 5, max 25). |
Response
{
"ok": true,
"results": [
{
"table": "tickets",
"rows": [
{
"id": "8c1d…",
"title": "Printer jam"
}
]
},
{
"table": "assets",
"rows": []
}
]
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 400 | MISSING_QUERY | q is empty. |
| 400 | MISSING_TABLES | tables is empty. |
curl "$APP_ORIGIN/__appbricx/api/v1/search?q=printer&tables=tickets%2Cassets&limit=5" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/search?q=printer&tables=tickets%2Cassets&limit=5`, {
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:142
OpenAPI document
GET/__appbricx/api/v1/openapi.json
The app's OpenAPI 3.1 document: every table, its schema and the role matrix. No data. Import it into Postman or a client generator.
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Response
{
"openapi": "3.1.0",
"info": {
"title": "Tablefor API",
"version": "1"
},
"paths": {
"/__appbricx/api/v1/bookings": {
"get": {
"summary": "List bookings"
}
}
}
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 404 | NO_APP | "This project has no generated app yet." |
curl "$APP_ORIGIN/__appbricx/api/v1/openapi.json" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/openapi.json`, {
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:171
List rows
GET/__appbricx/api/v1/:table
A page of rows the caller may read, filtered and sorted in SQL.
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Path parameters
| Name | Type | Description |
|---|---|---|
:tablerequired | string | The table name (an entity of the contract). |
Query parameters
| Name | Type | Description |
|---|---|---|
limitoptional | number | Page size (default 50, max 500). |
offsetoptional | number | Rows to skip. |
cursoroptional | string | Opaque cursor from a previous page. When present, offset is ignored and the answer carries nextCursor and hasMore. |
whereoptional | JSON object | Equality filters, e.g. {"status":"open"}. |
filteroptional | JSON array | Rich clauses [{ field, op, value }]. op: eq, neq, in, not_in, lt, lte, gt, gte, contains, not_contains, starts_with, ends_with, is_null, is_not_null, between, today, yesterday, this_week, this_month, this_year, last_n_days, next_n_days, range. |
sortoptional | JSON object | {"field":"created_at","dir":"desc"} (dir defaults to asc). |
Response
With cursor: { ok, rows, rowCount, nextCursor, hasMore }.
{
"ok": true,
"rows": [
{
"id": "8c1d…",
"title": "Printer jam",
"status": "open",
"created_at": "2026-10-12T09:14:03Z"
}
],
"rowCount": 1,
"fields": [
{
"name": "id",
"type": "2950"
},
{
"name": "title",
"type": "25"
}
],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | TABLE_FORBIDDEN | The table isn't exposed, or the caller's role may not use this method on it. |
| 400 | PARAMS_INVALID | where, filter or sort isn't valid JSON. |
| 400 | (SQL error code) | A filter names an unknown column or a value of the wrong type. |
curl "$APP_ORIGIN/__appbricx/api/v1/tickets?limit=20&where=%7B%22status%22%3A%22open%22%7D" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/tickets?limit=20&where=%7B%22status%22%3A%22open%22%7D`, {
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:180
Aggregate
GET/__appbricx/api/v1/:table/aggregate
count, sum, avg, min or max over the matching rows, optionally per group or per day/week/month bucket, computed in SQL under the caller's row-level security.
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Path parameters
| Name | Type | Description |
|---|---|---|
:tablerequired | string | The table name (an entity of the contract). |
Query parameters
| Name | Type | Description |
|---|---|---|
metricrequired | "count" | "sum" | "avg" | "min" | "max" | What to compute. |
columnoptional | string | The column (required unless metric=count). |
filteroptional | JSON array | Clauses as on list, including op "range". |
rangeoptional | string | today, this_week, last_7_days, this_month, last_30_days, this_quarter, this_year or all. |
dateFieldoptional | string | The date column range applies to (default created_at). |
tzoptional | IANA zone | Timezone for range and bucket (default UTC). |
compareoptional | "1" | With range: also the previous period (both to date). |
groupByoptional | string | One value per distinct value of this column. |
bucketoptional | "day" | "week" | "month" | With groupBy on a date column. |
Response
With groupBy: { ok, groups: [{ key, value }], truncated? } (keys ascending, null last).
{
"ok": true,
"value": 18420.5,
"previous": 15210,
"window": {
"from": "2026-10-01",
"to": "2026-10-12"
},
"previousWindow": {
"from": "2026-09-01",
"to": "2026-09-12"
}
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | TABLE_FORBIDDEN | The table isn't exposed, or the caller's role may not use this method on it. |
| 400 | PARAMS_INVALID | Unknown metric, or a column that doesn't exist on the table. |
curl "$APP_ORIGIN/__appbricx/api/v1/orders/aggregate?metric=sum&column=total&range=this_month&compare=1" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/orders/aggregate?metric=sum&column=total&range=this_month&compare=1`, {
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:284
Get a row
GET/__appbricx/api/v1/:table/:id
One row by id. rows is empty when it doesn't exist or row-level security hides it.
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Path parameters
| Name | Type | Description |
|---|---|---|
:tablerequired | string | The table name (an entity of the contract). |
:idrequired | string | The row id. |
Response
{
"ok": true,
"rows": [
{
"id": "8c1d…",
"title": "Printer jam",
"status": "open",
"created_at": "2026-10-12T09:14:03Z"
}
],
"rowCount": 1,
"fields": [
{
"name": "id",
"type": "2950"
},
{
"name": "title",
"type": "25"
}
],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | TABLE_FORBIDDEN | The table isn't exposed, or the caller's role may not use this method on it. |
curl "$APP_ORIGIN/__appbricx/api/v1/tickets/8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/tickets/8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10`, {
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:307
Create a row
POST/__appbricx/api/v1/:table
Insert a row. Owner columns are stamped from the caller's identity, before-write rules run, and a row-changed event fires (workflows on created).
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Path parameters
| Name | Type | Description |
|---|---|---|
:tablerequired | string | The table name (an entity of the contract). |
Body
| Name | Type | Description |
|---|---|---|
(columns)required | object | The row's own fields. Leave out id, owner columns and timestamps. |
Response
201 Created.
{
"ok": true,
"rows": [
{
"id": "8c1d…",
"title": "Printer jam",
"priority": "high",
"user_id": "u_42",
"created_at": "2026-10-12T09:14:03Z"
}
],
"rowCount": 1,
"fields": [
{
"name": "id",
"type": "2950"
},
{
"name": "title",
"type": "25"
}
],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | TABLE_FORBIDDEN | The table isn't exposed, or the caller's role may not use this method on it. |
| 403 | (stamp code) | The caller may not write rows owned by someone else. |
| 422 | RULE_REFUSED | A before-write rule refused the row; message says why. |
| 409 | CONFLICT | A unique constraint; the message is shown verbatim. |
curl -X POST "$APP_ORIGIN/__appbricx/api/v1/tickets" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION" \
-H "content-type: application/json" \
-d '{"title":"Printer jam","priority":"high"}'const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/tickets`, {
method: "POST",
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
"content-type": "application/json",
},
body: JSON.stringify({
"title": "Printer jam",
"priority": "high"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:330
Update a row
PATCH/__appbricx/api/v1/:table/:id
Patch a row. Before-write rules see the row as it would be. Lifecycle fields are refused here: use the transition endpoint.
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Path parameters
| Name | Type | Description |
|---|---|---|
:tablerequired | string | The table name (an entity of the contract). |
:idrequired | string | The row id. |
Body
| Name | Type | Description |
|---|---|---|
(columns)required | object | Fields to change. |
Response
{
"ok": true,
"rows": [
{
"id": "8c1d…",
"title": "Printer jam",
"status": "open",
"created_at": "2026-10-12T09:14:03Z"
}
],
"rowCount": 1,
"fields": [
{
"name": "id",
"type": "2950"
},
{
"name": "title",
"type": "25"
}
],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | TABLE_FORBIDDEN | The table isn't exposed, or the caller's role may not use this method on it. |
| 422 | RULE_REFUSED | A before-write rule refused the change. |
| 409 | E_LIFECYCLE_DIRECT_WRITE | The patch writes a lifecycle field. |
| 409 | CONFLICT | A unique constraint. |
curl -X PATCH "$APP_ORIGIN/__appbricx/api/v1/tickets/8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION" \
-H "content-type: application/json" \
-d '{"priority":"low"}'const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/tickets/8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10`, {
method: "PATCH",
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
"content-type": "application/json",
},
body: JSON.stringify({
"priority": "low"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:366
Delete a row
DELETE/__appbricx/api/v1/:table/:id
Delete a row the caller may delete.
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Path parameters
| Name | Type | Description |
|---|---|---|
:tablerequired | string | The table name (an entity of the contract). |
:idrequired | string | The row id. |
Response
{
"ok": true,
"rows": [],
"rowCount": 1,
"fields": [],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | TABLE_FORBIDDEN | The table isn't exposed, or the caller's role may not use this method on it. |
curl -X DELETE "$APP_ORIGIN/__appbricx/api/v1/tickets/8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/tickets/8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/queries-routes.ts:424
List external rows
GET/__appbricx/api/v1/ext/:entity
List rows of a table in an external Postgres database the builder connected. Same envelope and grammar as the tenant list, plus q for a case-insensitive search.
- Auth
- App key + signed-in user:
Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION
Path parameters
| Name | Type | Description |
|---|---|---|
:entityrequired | string | A binding recorded on the project's connection (lowercase). |
Query parameters
| Name | Type | Description |
|---|---|---|
limitoptional | number | Page size (default 50, max 500). |
offsetoptional | number | Rows to skip. |
cursoroptional | string | Opaque cursor from a previous page. When present, offset is ignored and the answer carries nextCursor and hasMore. |
whereoptional | JSON object | Equality filters, e.g. {"status":"open"}. |
filteroptional | JSON array | Rich clauses [{ field, op, value }]. op: eq, neq, in, not_in, lt, lte, gt, gte, contains, not_contains, starts_with, ends_with, is_null, is_not_null, between, today, yesterday, this_week, this_month, this_year, last_n_days, next_n_days, range. |
sortoptional | JSON object | {"field":"created_at","dir":"desc"} (dir defaults to asc). |
qoptional | string | ILIKE search over the binding's text columns. |
Response
{
"ok": true,
"rows": [
{
"id": "8c1d…",
"title": "Printer jam",
"status": "open",
"created_at": "2026-10-12T09:14:03Z"
}
],
"rowCount": 1,
"fields": [
{
"name": "id",
"type": "2950"
},
{
"name": "title",
"type": "25"
}
],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 401 | APP_SESSION_REQUIRED | "Sign in to see this data." (no app session and no server key). |
| 403 | TABLE_FORBIDDEN | The role may not read this entity. |
| 404 | EXTERNAL_ENTITY_UNKNOWN | Not a binding of this project. |
| 400 | PARAMS_INVALID | where, filter or sort isn't valid JSON. |
| 502 | EXTERNAL_DB_ERROR | The external database failed (also AUTH_FAILED, UNREACHABLE); 504 TIMEOUT when it is too slow. |
External databases have no row-level security that knows app users, so a signed-in, enabled app user (or a server key) is required and the access matrix applies per role.
curl "$APP_ORIGIN/__appbricx/api/v1/ext/customers?limit=20&q=acme" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/ext/customers?limit=20&q=acme`, {
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/external-routes.ts:103
Aggregate external rows
GET/__appbricx/api/v1/ext/:entity/aggregate
count, sum, avg, min or max over an external table's matching rows.
- Auth
- App key + signed-in user:
Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION
Path parameters
| Name | Type | Description |
|---|---|---|
:entityrequired | string | The binding. |
Query parameters
| Name | Type | Description |
|---|---|---|
metricrequired | string | count, sum, avg, min or max. |
columnoptional | string | Required unless count. |
filteroptional | JSON array | Clauses. |
Response
{
"ok": true,
"value": 1284
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 401 | APP_SESSION_REQUIRED | No app session and no server key. |
| 403 | TABLE_FORBIDDEN | Role may not read. |
curl "$APP_ORIGIN/__appbricx/api/v1/ext/customers/aggregate?metric=count" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/ext/customers/aggregate?metric=count`, {
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/external-routes.ts:115
Get an external row
GET/__appbricx/api/v1/ext/:entity/:id
One external row by primary key.
- Auth
- App key + signed-in user:
Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION
Path parameters
| Name | Type | Description |
|---|---|---|
:entityrequired | string | The binding. |
:idrequired | string | The primary key value. |
Response
{
"ok": true,
"rows": [
{
"id": "8c1d…",
"title": "Printer jam",
"status": "open",
"created_at": "2026-10-12T09:14:03Z"
}
],
"rowCount": 1,
"fields": [
{
"name": "id",
"type": "2950"
},
{
"name": "title",
"type": "25"
}
],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 401 | APP_SESSION_REQUIRED | No app session and no server key. |
| 403 | TABLE_FORBIDDEN | Role may not read. |
curl "$APP_ORIGIN/__appbricx/api/v1/ext/customers/1042" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/ext/customers/1042`, {
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/external-routes.ts:133
Create an external row
POST/__appbricx/api/v1/ext/:entity
Insert into an external table. Refused on read-only bindings before any SQL runs.
- Auth
- App key + signed-in user:
Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION
Path parameters
| Name | Type | Description |
|---|---|---|
:entityrequired | string | The binding. |
Body
| Name | Type | Description |
|---|---|---|
(columns)required | object | The row. |
Response
201 Created.
{
"ok": true,
"rows": [
{
"id": "8c1d…",
"title": "Printer jam",
"status": "open",
"created_at": "2026-10-12T09:14:03Z"
}
],
"rowCount": 1,
"fields": [
{
"name": "id",
"type": "2950"
},
{
"name": "title",
"type": "25"
}
],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 401 | APP_SESSION_REQUIRED | No app session and no server key. |
| 405 | READ_ONLY | The binding is read-only. |
| 403 | TABLE_FORBIDDEN | Role may not create. |
| 409 | CONFLICT | A unique value is taken. |
curl -X POST "$APP_ORIGIN/__appbricx/api/v1/ext/customers" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-app-session: $APP_SESSION" \
-H "content-type: application/json" \
-d '{"name":"Acme GmbH","country":"DE"}'const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/ext/customers`, {
method: "POST",
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-app-session": APP_SESSION,
"content-type": "application/json",
},
body: JSON.stringify({
"name": "Acme GmbH",
"country": "DE"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/external-routes.ts:141
Update an external row
PATCH/__appbricx/api/v1/ext/:entity/:id
Patch an external row.
- Auth
- App key + signed-in user:
Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION
Path parameters
| Name | Type | Description |
|---|---|---|
:entityrequired | string | The binding. |
:idrequired | string | The primary key value. |
Body
| Name | Type | Description |
|---|---|---|
(columns)required | object | Fields to change. |
Response
{
"ok": true,
"rows": [
{
"id": "8c1d…",
"title": "Printer jam",
"status": "open",
"created_at": "2026-10-12T09:14:03Z"
}
],
"rowCount": 1,
"fields": [
{
"name": "id",
"type": "2950"
},
{
"name": "title",
"type": "25"
}
],
"truncated": false
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 401 | APP_SESSION_REQUIRED | No app session and no server key. |
| 405 | READ_ONLY | The binding is read-only. |
curl -X PATCH "$APP_ORIGIN/__appbricx/api/v1/ext/customers/1042" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-app-session: $APP_SESSION" \
-H "content-type: application/json" \
-d '{"country":"AT"}'const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/ext/customers/1042`, {
method: "PATCH",
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-app-session": APP_SESSION,
"content-type": "application/json",
},
body: JSON.stringify({
"country": "AT"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/external-routes.ts:151
Delete an external row
DELETE/__appbricx/api/v1/ext/:entity/:id
Delete an external row.
- Auth
- App key + signed-in user:
Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION
Path parameters
| Name | Type | Description |
|---|---|---|
:entityrequired | string | The binding. |
:idrequired | string | The primary key value. |
Response
{
"ok": true,
"rows": [],
"rowCount": 1
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 401 | APP_SESSION_REQUIRED | No app session and no server key. |
| 405 | READ_ONLY | The binding is read-only. |
curl -X DELETE "$APP_ORIGIN/__appbricx/api/v1/ext/customers/1042" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-app-session: $APP_SESSION"const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/ext/customers/1042`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-app-session": APP_SESSION,
},
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/external-routes.ts:161
Transition a lifecycle field
PATCH/__appbricx/api/v1/:entity/:id/transition
Move a lifecycle field along the contract's transition graph, checking the role restrictions on that edge.
- Auth
- App key + data marker:
Authorization: Bearer $APP_KEYx-appbricx-data-api: 1x-appbricx-app-session: $APP_SESSION (optional: acts as that user)
Path parameters
| Name | Type | Description |
|---|---|---|
:entityrequired | string | The table. |
:idrequired | string | The row id. |
Body
| Name | Type | Description |
|---|---|---|
fieldrequired | string | The lifecycle field, e.g. status. |
torequired | string | The target state. |
Response
{
"ok": true,
"from": "open",
"to": "resolved"
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 400 | PARAMS_INVALID | The x-appbricx-data-api: 1 header is missing. |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | TABLE_FORBIDDEN | The table isn't exposed, or the caller's role may not use this method on it. |
| 400 | E_TRANSITION_INVALID | Body isn't { field, to }. |
| 400 | E_FIELD_NOT_LIFECYCLE | The field has no lifecycle. |
| 404 | E_ROW_NOT_FOUND | No such row (or hidden). |
| 404 | E_ENTITY_NOT_FOUND | Unknown entity. |
| 409 | E_TRANSITION_FORBIDDEN | No edge from the current state to `to`. |
| 403 | E_TRANSITION_ROLE | The role may not make this move. |
| 500 | E_TRANSITION_UPDATE_FAILED | The write failed. |
curl -X PATCH "$APP_ORIGIN/__appbricx/api/v1/tickets/8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10/transition" \
-H "Authorization: Bearer $APP_KEY" \
-H "x-appbricx-data-api: 1" \
-H "x-appbricx-app-session: $APP_SESSION" \
-H "content-type: application/json" \
-d '{"field":"status","to":"resolved"}'const res = await fetch(`${APP_ORIGIN}/__appbricx/api/v1/tickets/8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10/transition`, {
method: "PATCH",
headers: {
Authorization: `Bearer ${APP_KEY}`,
"x-appbricx-data-api": "1",
"x-appbricx-app-session": APP_SESSION,
"content-type": "application/json",
},
body: JSON.stringify({
"field": "status",
"to": "resolved"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/app-runtime/http/transition-routes.ts:38
Raw SQL: query
POST/__appbricx/data/query
Run one read statement with parameters on the tenant database. Browser apps use the CRUD API, named queries or search instead.
- Auth
- Server key:
Authorization: Bearer $SERVER_KEYx-appbricx-data-api: 1
Body
| Name | Type | Description |
|---|---|---|
sqlrequired | string | One statement with $1, $2… placeholders. |
paramsoptional | unknown[] | Placeholder values. |
Response
{
"ok": true,
"rows": [
{
"id": "8c1d…",
"title": "Printer jam"
}
],
"rowCount": 1,
"fields": [],
"truncated": false,
"elapsed_ms": 4
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 403 | ORIGIN_NOT_ALLOWED | A browser key used from an origin it isn't bound to (ORIGIN_REQUIRED when there is no Origin at all). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | RAW_SQL_FORBIDDEN | Not a server-tier key. |
| 403 | TOOL_NOT_ALLOWED | The key's allowed tools exclude data.query. |
| 400 | FORBIDDEN_STMT | The statement isn't a read. |
| 403 | ADMIN_REQUIRED | x-appbricx-admin without an admin session. |
| 423 | WORKSPACE_QUARANTINED | The workspace is under abuse review. |
| 503 | WORKER_UNAVAILABLE | The project's database couldn't be reached. |
x-appbricx-admin: 1 with an admin app session reads past row-level security.
curl -X POST "$APP_ORIGIN/__appbricx/data/query" \
-H "Authorization: Bearer $SERVER_KEY" \
-H "x-appbricx-data-api: 1" \
-H "content-type: application/json" \
-d '{"sql":"select id, title from tickets where status = $1 limit 10","params":["open"]}'const res = await fetch(`${APP_ORIGIN}/__appbricx/data/query`, {
method: "POST",
headers: {
Authorization: `Bearer ${SERVER_KEY}`,
"x-appbricx-data-api": "1",
"content-type": "application/json",
},
body: JSON.stringify({
"sql": "select id, title from tickets where status = $1 limit 10",
"params": [
"open"
]
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/routes/app-data.ts:530
Raw SQL: exec
POST/__appbricx/data/exec
Execute a write statement with parameters. Emits change events for mutations.
- Auth
- Server key:
Authorization: Bearer $SERVER_KEYx-appbricx-data-api: 1
Body
| Name | Type | Description |
|---|---|---|
sqlrequired | string | The statement. |
paramsoptional | unknown[] | Placeholder values. |
Response
{
"ok": true,
"rows": [],
"rowCount": 1,
"fields": [],
"truncated": false,
"elapsed_ms": 6
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | RAW_SQL_FORBIDDEN | Not a server-tier key. |
| 409 | QUOTA_EXCEEDED | The workspace's storage quota is used up. |
| 503 | WORKER_UNAVAILABLE | The database couldn't be reached. |
curl -X POST "$APP_ORIGIN/__appbricx/data/exec" \
-H "Authorization: Bearer $SERVER_KEY" \
-H "x-appbricx-data-api: 1" \
-H "content-type: application/json" \
-d '{"sql":"update tickets set status = 'closed' where id = $1","params":["8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10"]}'const res = await fetch(`${APP_ORIGIN}/__appbricx/data/exec`, {
method: "POST",
headers: {
Authorization: `Bearer ${SERVER_KEY}`,
"x-appbricx-data-api": "1",
"content-type": "application/json",
},
body: JSON.stringify({
"sql": "update tickets set status = 'closed' where id = $1",
"params": [
"8c1d2f9e-0d4b-4c39-9f63-2a1d5b7e8f10"
]
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/routes/app-data.ts:531
Raw SQL: schema
POST/__appbricx/data/schema
Introspect the tenant schema: tables, columns and keys.
- Auth
- Server key:
Authorization: Bearer $SERVER_KEYx-appbricx-data-api: 1
Response
{
"ok": true,
"tables": [
{
"name": "tickets",
"columns": [
{
"name": "id",
"type": "uuid",
"nullable": false
}
]
}
],
"elapsed_ms": 12
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | TIER_INSUFFICIENT | Not a server-tier key. |
curl -X POST "$APP_ORIGIN/__appbricx/data/schema" \
-H "Authorization: Bearer $SERVER_KEY" \
-H "x-appbricx-data-api: 1" \
-H "content-type: application/json" \
-d '{}'const res = await fetch(`${APP_ORIGIN}/__appbricx/data/schema`, {
method: "POST",
headers: {
Authorization: `Bearer ${SERVER_KEY}`,
"x-appbricx-data-api": "1",
"content-type": "application/json",
},
body: JSON.stringify({}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/routes/app-data.ts:532
Raw SQL: migrate
POST/__appbricx/data/migrate
Apply a DDL migration once, recorded by id.
- Auth
- Server key:
Authorization: Bearer $SERVER_KEYx-appbricx-data-api: 1
Body
| Name | Type | Description |
|---|---|---|
migration_idrequired | string | Unique id, ^[a-z0-9_-]{1,80}$. |
sqlrequired | string | The DDL. |
Response
{
"ok": true,
"applied": true,
"elapsed_ms": 31
}Errors
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key"). |
| 429 | RATE_LIMITED | The project's per-minute request budget is spent (600 for preview tokens and browser keys, 1200 for server keys by default). |
| 403 | TIER_INSUFFICIENT | DDL needs a server-tier key. |
| 400 | PARAMS_INVALID | migration_id missing or malformed. |
curl -X POST "$APP_ORIGIN/__appbricx/data/migrate" \
-H "Authorization: Bearer $SERVER_KEY" \
-H "x-appbricx-data-api: 1" \
-H "content-type: application/json" \
-d '{"migration_id":"add_ticket_sla","sql":"alter table tickets add column sla_hours int"}'const res = await fetch(`${APP_ORIGIN}/__appbricx/data/migrate`, {
method: "POST",
headers: {
Authorization: `Bearer ${SERVER_KEY}`,
"x-appbricx-data-api": "1",
"content-type": "application/json",
},
body: JSON.stringify({
"migration_id": "add_ticket_sla",
"sql": "alter table tickets add column sla_hours int"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);Source: services/api/src/routes/app-data.ts:533