Custom data: an organization's own PostgreSQL tables
Status: the org entitlement customData.enabled defaults ON, but the feature needs a database
cluster (CUSTOM_DATA_CLUSTER_URL + CUSTOM_DATA_ROLE_SECRET). Until a deployment has one, every data route answers a
signed-in member of an entitled org 501 not_configured (anyone else gets the 404 an unknown route gets), Settings > Data shows "not configured yet", and the data MCP tools are not registered. The
/orgs/{orgId}/data... paths are in the served OpenAPI document on every deployment (the document is
org-less, so it cannot say whether YOUR org has data). Never tell a person custom data works until data_status succeeds.
Who can use it
- Key family
data, explicit only: a key that names no family never reaches it, and a runner key can never name it. - A key with a persona gets no data tools (its data access stays REST, through a binding a person sets).
- What you may do is your data role (view, edit, manage), derived from the key owner's capped org role and the data roles and grants a person set. Roles, bindings, grants and assigning a database to a project are session only.
- Provisioning is a person's job (
POST /v1/orgs/{orgId}/data/provision, data admin, session). A key on an org that is not provisioned gets 409CD-NOT-PROVISIONED-ASK-ADMIN: stop and ask; 202 withstatus_urlmeans it is being set up.
Signed-in bootstrap
A person with data:admin or data:create creates a database with
POST /v1/orgs/{orgId}/data/databases, body { "key": "outreach", "name": "Outreach", "description": "Optional" }.
This provisions the org if needed and returns {id,key}. If it answers 202, poll status_url, then retry creation.
The URL points to the member data overview for a creator, or the detailed status view for a data admin.
Keys are immutable, 1–48 lower-case letters, digits or underscores starting with a letter; public, goodqa,
information_schema and pg_* are reserved. Retry/concurrent calls reuse one database without overwriting metadata.
A data:create caller can retry their own database; a data admin can retry any live database in their org.
A new database has projects scope and no assignments. Assign explicitly with
PUT /v1/orgs/{orgId}/data/databases/{db}/projects/{projectId}, body { "access": "write" } (or "read").
This needs data:admin, or data:create plus project:manage on that project for a database you created.
Both routes require a signed-in session; API keys and user tokens cannot create or assign databases.
After assignment, use describe, table creation and row operations with the existing data permissions.
Workflow
data_status (GET /v1/orgs/{orgId}/data) -> describe (compact DDL, schema_digest) -> query / find_rows /
insert_rows / update_rows. Errors are {error: {code, message, fix, position?}} with codes CD-PG-<SQLSTATE>,
CD-DENY-<rule>, CD-WARN-FULLTABLE, CD-LIMIT-*, CD-FORBIDDEN, CD-NOT-FOUND: read fix and retry with it.
Main routes: GET /v1/orgs/{orgId}/data/status, POST .../data/databases/{db}/query, .../explain,
.../tables, .../tables/{table}/rows, .../tables/{table}/import, .../tables/{table}/export, .../describe.
Reference
- API reference:
/orgs/{orgId}/data...in/v1/openapi.json(tagdata). - MCP tools:
data_status,describe,query,explain,create_table,alter_table,find_rows,insert_rows,update_rows,delete_rows,import_rows,export_rows, see the MCP tool reference. - Agent skill: custom data skill.
- Runbook: "Configuration prerequisites" in feature switches; design notes in design/custom-data and the not-configured convention.