---
name: egbe-os-db
description: Query/export attached PostgreSQL data; update or delete individual rows by primary key.
metadata:
  egbe-when: "Use for inspecting, exporting or correcting stored product data."
  egbe-visible: "true"
  egbe-title: "Database access"
  egbe-description: "Inspect and correct your app's PostgreSQL data without redeploying your app."
  egbe-category: "Development & hosting"
---

<!-- egbe-catalog-updates:v1 -->
For work in a workspace with an installed, executable Egbe catalog, run
`python3 "<skills-directory>/egbe-os-quickstart/scripts/sync-catalog.py" --check` using the
actual installation path before starting work with this skill. `<skills-directory>` is the
project folder that contains this skill's directory, such as `.agents/skills`; find it with
file tools or `ls`, not inline interpreter code. Follow its result
with `<skills-directory>/egbe-os-quickstart/references/skill-updates.md`, then reread this
skill if updated. The check is read-only; the reference covers applying updates.
For public guidance without an installed catalog or command execution, skip
this local check and read the current published instructions directly. Reading
does not require a project or installation. For project execution that needs
connection or setup, use `GET /quickstart` on `OS_URL` to choose the appropriate
workspace or no-installation path before setup. The quickstart handles its own check.

Scheduled and other unattended runs have no one to approve inline code, so
`python -c`, `node -e`, heredoc scripts and code-execution tools may be blocked
there. Run bundled scripts as `python3 <script> ...` or `node <script> ...`, and
read files with file tools or plain shell commands.

<!-- egbe-connection-identity:v1 -->
Public instruction and playbook reads do not require credentials or connection
identity. The following applies to authenticated project requests, not reading
or applying public guidance without a connection.

For a BYOA workspace, retain its connection identity on **every authenticated
Egbe request**, including the examples and referenced workflows below. Load
`OS_URL` and `OS_API_KEY` from the project's Git-ignored `.env.local`. Set
`OS_AGENT_NAME` to your actual human-readable agent name, for example
`Claude Code` or `Codex`, not a slug such as `claude-code` or `CLAUDE_CODE`.
This is the name the owner sees in Dashboard. Never infer it from curl or reuse
another agent's identity. If your name is unknown, leave it unset. Quickstart saves the per-agent UUID in
`OS_CONNECTION_ID_<AGENT_SLUG>` (uppercase name with separators changed to
underscores, e.g. `CLAUDE_CODE`) and the optional owner-provided nickname in
`OS_WORKSPACE_NAME` in that file.

Many agent runtimes, including Claude Code, start a fresh shell for every
command, so shell functions and exported variables from an earlier command are
gone. Make each command that calls Egbe self-contained: load `OS_URL` and
`OS_API_KEY` in that same command, and run each authenticated curl example
through the project-local helper with your agent name set inline:

```bash
OS_AGENT_NAME="<your agent name>" python3 "<skills-directory>/egbe-os-quickstart/scripts/egbe-curl.py" --egbe-request /me/limits -s
```

`--egbe-request` loads the saved URL and API key from `.env.local` without
executing it, constructs the full URL, and adds the bearer token before calling
curl. Replace `/me/limits` with the API path from an example and keep its other
curl options; omit the example's URL and Authorization header. This mode rejects
additional destinations and redirects. Without this mode, the helper takes the
same arguments as `curl` and you must load credentials before shell expansion.
Do not edit global shell
configuration. Only in a shell that persists between your commands may you
instead export `OS_AGENT_NAME` once and define
`curl() { python3 "<skills-directory>/egbe-os-quickstart/scripts/egbe-curl.py" "$@"; }`.
Likewise prefix `OS_AGENT_NAME="<your agent name>"` on each command that runs a
bundled Python or JavaScript client.

The helper adds the saved identity headers only for the configured Egbe URL;
other destinations receive no extra headers. For an Egbe request it exits 22 on
an HTTP status of 400 or above and writes a status and request ID line to stderr
after every response; `<skills-directory>/egbe-os-quickstart/references/connection.md`
describes both. It rejects mixed destinations and redirects when attaching
identity. If the helper or this agent's UUID is missing, follow `GET /quickstart`
first. Before catalog installation, download and inspect
`GET /skills/quickstart/resources/scripts/egbe-curl.py` into the workspace's temporary
setup directory and use that path. After installation, switch to the installed helper.
For other Python, JavaScript or MCP clients, explicitly configure the same
`X-Egbe-Connection-Id`, `X-Egbe-Agent-Name`, and optional `X-Egbe-Workspace-Name`
headers on Egbe requests. A client that cannot attach identity headers remains
usable as an unidentified connection; do not claim its later activity is identified.
Deployed-product requests use their separate product credentials and no agent identity.


# Work with your database

Use the founder's existing `OS_API_KEY` and configured `OS_URL` (normally
`https://os.hz.egbe.dev`). No additional key or scope is needed. Requests use
`Authorization: Bearer $OS_API_KEY` and return the usual `{code,message,data}` envelope.

The attached PostgreSQL/Supabase schema is shared by your account's apps. OS
selects it from your key. If no database exists, provision it through
`POST /deploy/<slug>/db` as described by `/deploy/skill`.

## Read and inspect

`POST /db/query` accepts one read SQL query and a `params` array. Use `$1`, `$2`,
etc. for values. SELECT, joins, aggregates and read-only CTEs work. Supply SQL
without a trailing comment after its final semicolon.

```bash
OS_AGENT_NAME="<your agent name>" python3 "<skills-directory>/egbe-os-quickstart/scripts/egbe-curl.py" --egbe-request "/db/query" -s  \
 -H 'Content-Type: application/json' \
  -d '{"sql":"SELECT id, email FROM leads WHERE email = $1 ORDER BY id","params":["test@example.com"]}'
```

Results contain `rows`, `row_count`, `truncated` and `limits`. Default limits:
1,000 rows, 1 MiB of response data, 5 seconds per statement and 1 second waiting
for a lock. A request body is limited to 64 KiB and 100 SQL parameters. Too many
concurrent requests return 429. An oversized single row returns 413; select
fewer columns or shorter values. Limits may be configured by the platform.

Large integers and fractional numbers are returned as strings to preserve
precision. Send large IDs and exact decimals as strings in parameters/changes
as well. Alias same-named columns when joining tables.

For exports, query ordered pages using a unique key: `WHERE id > $1 ORDER BY id
LIMIT 500`, then pass the last ID to the next call. Composite keys need matching
ordering/continuation on all key columns. A `truncated: true` response is
incomplete: reduce the page size/selected fields and repeat that page. Separate
requests do not share a snapshot; concurrent changes may affect an export.

The repository normally describes the schema. If needed, inspect
`information_schema.columns` or PostgreSQL catalogs using `/db/query`; there is
no separate schema-discovery endpoint.

## Correct one record

Read the target record first. Supply every primary-key column in `key`.
Tables without a primary key are not supported for these operations.

```bash
OS_AGENT_NAME="<your agent name>" python3 "<skills-directory>/egbe-os-quickstart/scripts/egbe-curl.py" --egbe-request "/db/rows/update" -s  \
 -H 'Content-Type: application/json' \
  -d '{"table":"leads","key":{"id":"42"},"values":{"name":"Corrected name"}}'

OS_AGENT_NAME="<your agent name>" python3 "<skills-directory>/egbe-os-quickstart/scripts/egbe-curl.py" --egbe-request "/db/rows/delete" -s  \
 -H 'Content-Type: application/json' \
  -d '{"table":"leads","key":{"id":"42"}}'
```

Changes accept literal values, not SQL expressions. Primary-key/generated
columns cannot be changed. For JSON/JSONB columns, strings remain strings;
send objects and arrays as JSON values. A request value of `null` sets SQL NULL.
Each request commits one direct row change;
database triggers and foreign-key cascades still apply. Missing rows return
404; constraint violations return 409 and roll back. Change activity records
the operation/table/outcome without storing row contents.

If the response says `database_write_outcome_unknown`, read the record before
retrying. A disconnected request can lose the acknowledgement of a committed
change. There is no automatic retry or undo history.

## CLI

Use the same key with the existing CLI environment variable:

```bash
EGBE_API_KEY="$OS_API_KEY" EGBE_OS_URL="$OS_URL" egbe db skill
EGBE_API_KEY="$OS_API_KEY" EGBE_OS_URL="$OS_URL" egbe --json db query \
  --body '{"sql":"SELECT id, email FROM leads ORDER BY id LIMIT 20","params":[]}'
EGBE_API_KEY="$OS_API_KEY" EGBE_OS_URL="$OS_URL" egbe db update \
  --body '{"table":"leads","key":{"id":"42"},"values":{"name":"Corrected name"}}'
EGBE_API_KEY="$OS_API_KEY" EGBE_OS_URL="$OS_URL" egbe db delete \
  --body '{"table":"leads","key":{"id":"42"}}'
```

Generic database inspection and cleanup use this capability. Product admin
features that enforce business workflows can remain in the application.
Migrations stay in the product's existing deployment workflow; this API does
not execute arbitrary write SQL, INSERT, bulk changes or DDL.

## Convex

These SQL endpoints operate on attached PostgreSQL, not Convex. For a Convex
product, obtain its existing backend details through `GET /deploy/<slug>/convex`,
configure `CONVEX_SELF_HOSTED_URL` and `CONVEX_SELF_HOSTED_ADMIN_KEY`, and use the
project's installed Convex CLI: `npx convex data`, `npx convex export`, or
`npx convex run <function> <args>`. Function execution uses deployed queries,
mutations or actions. Check the installed CLI/backend versions before relying
on newer options such as inline queries.
