Workspace database
Every workspace can have a real managed Postgres behind it: your agent's durable memory, the table a pipeline writes into, the rows a dashboard view charts. Your agent reaches it through its own tools, and your backend reaches the same database over REST.
What you get
A Postgres database scoped to the workspace, provisioned the first time something asks for it. There is no create step in your code: the first call provisions it if the organization has a plan, a credit package or a running trial.
On the no-card trial, the first use of the database starts the trial's 7-day clock if it is not running yet: any read, write or schema change from an agent, the API, a data job, an agent testing a dashboard query or the Database page in the superagnt app. Checking its status does not, and neither does opening a dashboard view.
Provisioning takes a moment, and the API tells you so in band rather than failing. While the database is coming up, every /v1/db/* route answers HTTP 202 with the current status and an instruction to retry.
{
"success": true,
"data": { "status": "provisioning" },
"message": "Database is still provisioning. Retry shortly."
}If the organization has none of those, the answer is HTTP 402 instead, naming the fix: choose a plan. Nothing is provisioned and nothing is charged.
Two kinds of database
A serverless database is a Postgres database project of its own that sleeps when idle and wakes on the next call, reached only through our API. Every plan can have one. A dedicated database is a Postgres project of its own too. A workspace gets one or the other when its database is first created and keeps it. agnt_db_status reports which kind a workspace has in its backend field.
| Capability | Serverless | Dedicated |
|---|---|---|
| SQL, tables, rows, migrations and vector search (agnt_db_* tools and /v1/db routes) | Yes | Yes |
| File storage (/v1/db/storage/*) | Not available on this database | Yes |
| Advisors (security and performance checks) | Not available on this database | Yes |
| Migration history | Kept in the database itself; agnt_db_list_migrations reads it | Kept in the project's migration history |
| Extensions | pgvector, pgcrypto, pg_trgm and citext installed and callable unqualified; no others | The project's own set |
| New tables locked with row-level security | No; only our API connects | Yes, automatically |
| Supabase auth helpers: auth.uid(), auth.role(), auth.jwt() | No auth schema; keep user records in a table of your own | Callable in migrations (policies, column defaults, function bodies); nothing else in auth, auth.users included, and none of it outside migrations |
| Supabase platform schemas (storage, vault, cron, extensions and the rest) | Not applicable: grants and extension changes are refused outright | Off limits: nothing can be moved into, granted across or changed in them. CREATE EXTENSION ... SCHEMA extensions still works |
| Plan limits on tables, rows and storage | Yes (below) | No |
On a serverless database, calls that are not available answer that they are not available on this database rather than failing silently, so an agent can tell a missing capability from a broken one.
From an agent
Over MCP the database is the agnt_db_* family, served on the base connection with nothing to enable.
| Tool | What it does |
|---|---|
agnt_db_status | Is the database provisioned and reachable (or paused), which kind it is, and its tables, rows and storage against the plan's limits. |
agnt_db_list_tables | Tables with their columns, types and RLS status. |
agnt_db_list_extensions | Installed Postgres extensions. |
agnt_db_list_migrations | Migrations applied so far. |
agnt_db_apply_migration | Apply a named DDL migration. |
agnt_db_execute_sql | Run SQL directly. |
agnt_db_select | Read rows with filters and ordering. |
agnt_db_insert | Insert rows. |
agnt_db_upsert | Insert or update on conflict. |
agnt_db_update | Update matching rows. |
agnt_db_delete | Delete matching rows. |
agnt_db_load_csv | Bulk-load a CSV already held in workspace files into a table. |
deployed agents use their own path
/v1/db/*. It reaches the same database through its runtime path, which is what keeps its table allowlist, its read-only setting and its audit attribution in force. Both surfaces run the same underlying implementation, so the contract is identical; only the policy layer differs.From your backend
/v1/db/* takes the same Authorization: Bearer <key> as the rest of the API and is scoped to that key's workspace.
| Route | What it does |
|---|---|
POST /v1/db/sql | Run SQL. Destructive statements need confirm; more than one statement needs allowMultiStatement; reads are capped at 1000 rows. |
GET /v1/db/tables | List tables in the public schema. |
GET /v1/db/tables/:name | Columns, types, nullability, defaults and indexes for one table. |
POST /v1/db/tables | Create a table from a column list. |
PATCH /v1/db/tables/:name | Add or drop columns. Dropping needs confirm. |
DELETE /v1/db/tables/:name | Drop a table. Requires ?confirm=true. |
GET /v1/db/rows/:table | Read rows with repeatable filter params, order, limit and offset. |
POST /v1/db/rows/:table | Insert one row or a batch. |
PATCH /v1/db/rows/:table | Update rows. Requires at least one filter. |
DELETE /v1/db/rows/:table | Delete rows. Requires at least one filter. |
Row reads take filter more than once, and the conditions are combined. Paging is limit and offset, not a cursor.
curl "https://api.superagnt.com/v1/db/rows/leads?filter=status.eq.open&filter=score.gte.80&order=created_at.desc&limit=50&offset=0" \
-H "Authorization: Bearer $AGNTDATA_API_KEY"Vector search
POST /v1/db/vectors/:table/search takes a query vector and returns the nearest rows. column defaults to embedding, k is clamped between 1 and 200, and metric is cosine (the default), l2 or ip. Each row comes back with an extra distance column.
curl -X POST "https://api.superagnt.com/v1/db/vectors/documents/search" \
-H "Authorization: Bearer $AGNTDATA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"column": "embedding",
"query": [0.014, -0.221, 0.087],
"k": 10,
"metric": "cosine"
}'Object storage
A dedicated database carries buckets and objects under /v1/db/storage/*: list and create buckets, list objects by prefix with limit and offset, upload, download and delete. On a serverless database these routes answer HTTP 409: file storage isn't available on this database. Keep files in workspace files instead.
- 01
Upload sends raw bytes
POST /v1/db/storage/objects/:bucket?path=...takes the file as the raw request body, not a JSON field and not multipart. SetContent-Typeto the file's own type; it is stored and replayed on download. - 02
Download streams the object itself
GET /v1/db/storage/objects/:bucket/<path>returns the bytes with the object's own content type, not the usual JSON envelope. Do not try to parse it as JSON.
Everything else on the storage surface answers in the standard envelope.
Limits on a serverless database
A serverless database is capped in tables, rows and storage, summed across every database the organization has. The no-card trial runs on Growth limits, and an organization on a managed plan or a credit package gets Scale limits on a serverless database.
| Plan | Tables | Rows, all tables | Storage |
|---|---|---|---|
| Starter | 10 | 25,000 | 250 MB |
| Growth | 50 | 250,000 | 1 GB |
| Scale | 250 | 2,500,000 | 10 GB |
- 01
Tables are checked up front
A
CREATE TABLE(orCREATE TABLE AS,SELECT INTOor a materialized view) that would pass the table cap is refused before it runs, with an upgrade offer. Partitions and materialized views count as tables. If the organization is over the cap, after a plan change for example, creating or altering anything is refused until tables are dropped or the plan goes up. Dropping a table still works. - 02
Rows and storage are measured, not metered
Row counts and database size are measured as the database is used rather than on every insert, so a burst can pass a cap briefly. Storage is the database's size on disk, which includes a few megabytes of Postgres's own catalogs.
- 03
A notice at 80%
When any cap reaches 80%,
agnt_db_statusand the results of the otheragnt_db_*tools carry a notice the agent can relay before anything is refused. The dashboard's Database page shows the same notice beside the usage meters. - 04
Storage never turns the database read-only
Over the storage cap you get a notice, not a refusal. Some headroom is left past the cap; once it is used, a write that needs more space is refused with an offer for more storage, while reads and deletes keep working. Space freed by
DELETEis reused after cleanup, andTRUNCATEorDROP TABLEfrees it at once. - 05
Read-only for new data after 24 hours over
A database that stays over its table or row cap for 24 hours stops taking new rows. Inserts, upserts and migrations are refused and raw SQL runs read-only, except SQL that only removes data:
DELETE,TRUNCATE, andDROPof a table, view, materialized view, index or sequence still run (a request that mixes them with anything else runs read-only). Reads keep working, and so do row updates and deletes throughagnt_db_update,agnt_db_deleteand/v1/db/rows, so you can get back under on your own. A statement that only removes data but inserts rows on the way (through a trigger or a function) is rolled back. It lifts as soon as the database is back under the cap or the plan goes up. - 06
Synced vendor tables count too
Tables a vendor sync keeps (named after the vendor, such as
heyreach_conversations) count against these caps like any table you create. The Sync panel shows the tables and rows a sync would add before it is turned on, its event tables keep a rolling 90 days by default so they do not grow without bound, and the Database page breaks usage down by sync. Over the row cap a sync stops adding rows and keeps updating the ones it has.
when a trial or plan ends
- 01
Paused, not deleted
When the trial ends or a plan lapses, a serverless database is paused: nothing reaches it until a plan is active. Every call from an agent, the API or the dashboard answers with the offer to choose a plan (HTTP 402 with
error.upgradeover REST, arequires_upgraderesult over MCP), and the Database page says it is paused. - 02
Resumes within seconds of a plan
Once a plan is active the database resumes on its own, usually within seconds. Until it has, calls answer HTTP 503 with a
Retry-Afterheader, andagnt_db_statusreports it not ready with a hint to retry.
What it costs
calls are free
/v1/db/* routes deduct no data credits and write no usage rows. Query your own database as often as you like. What is priced is having a database at all.Every plan includes the database. On the builder plans each workspace gets its own, so the plan's workspace count is the database count. On the managed plans the database count comes with the plan.
| Managed plan | Price | Databases |
|---|---|---|
| Solo | $99/mo | 1 |
| Team | $199/mo | 3 |
| Business | $499/mo | 10 |
Safety rails
The rails exist because the caller is often a language model, and the cost of a mistyped statement is your data.
- Destructive SQL is refused unless the request says confirm. A DROP or a TRUNCATE has to be deliberate, not a side effect of a generated string.
- More than one statement in a single POST /v1/db/sql needs allowMultiStatement, so a stray semicolon cannot smuggle a second command in.
- Reads through POST /v1/db/sql are capped at 1000 rows, and the cap cannot be raised past that.
- PATCH and DELETE on rows both require at least one filter. An update with no WHERE clause is not expressible on this surface.
- DELETE on a table requires ?confirm=true on the URL.
- On a serverless database, statements that change roles, grants or server settings, add extensions, or reach outside the database are refused, and every statement runs under a time limit.
agents should use the runtime surface
/v1/db/* has the whole database. A deployed agent going through its runtime path is held to its own policy instead: only the tables on its allowlist, read-only when you set it that way, and every statement attributed to that agent in the audit trail. Give an agent the runtime surface and keep /v1/db/* for your own backend.Dashboards over this data are on dashboards.