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 409 CD-NOT-PROVISIONED-ASK-ADMIN: stop and ask; 202 with status_url means 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 (tag data).
  • 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.

We use essential cookies to keep you signed in. See our Privacy Policy for details.