> ## Documentation Index
> Fetch the complete documentation index at: https://docs.powabase.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Database (PostgREST)

> Direct REST access to your project's public schema. PostgREST exposes every table and view as a REST endpoint with filtering, ordering, pagination, and embedded relations.

Each project has its own Postgres database. The `ai` schema is managed by Powabase (sources, knowledge bases, agents, sessions, and so on). The `public` schema is yours: create tables, define relationships, and add indexes, and PostgREST exposes them at /rest/v1/\{table}. PostgREST honours Row Level Security. The Anon (Publishable) Key respects RLS policies; the Service Role (Secret) Key bypasses them, so use the service role server-side only. Both keys, plus the Project URL, are in the Studio's Connect modal: click the Connect button in your project header, or append ?showConnect=true to any project URL.

## Common Patterns

Read with GET /rest/v1/\{table} and a select= query parameter. Insert with POST and a JSON body. Update with PATCH and a filter. Delete with DELETE and a filter. Embed related tables with select=*,other(*). Filter operators (eq, gt, lt, like, in, is, ...) follow PostgREST conventions. Always include both apikey and Authorization: Bearer headers, both set to the same key; sending only one returns 401.

## Reading rows

### GET /rest/v1/{table}

List rows from a table or view. Use select= to project columns, filter operators (eq, gt, like, in, ...) to filter, order= to sort, limit and offset for pagination. Combine select with embedded relations to fetch joined data in a single request.

<ParamField path="table" type="string" required>
  Table or view name in the public schema
</ParamField>

<ParamField query="select" type="string">
  Comma-separated columns. Use *,relation(*) to embed related rows. Default: \*
</ParamField>

<ParamField query="order" type="string">
  Order by column, e.g. created\_at.desc
</ParamField>

<ParamField query="limit" type="integer">
  Max rows to return
</ParamField>

<ParamField query="offset" type="integer">
  Skip the first N rows
</ParamField>

<CodeGroup>
  ```python Python theme={null}
  response = requests.get(f"{BASE_URL}/rest/v1/users?select=id,email&limit=20", headers=headers)
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(`${BASE_URL}/rest/v1/users?select=id,email&limit=20`, { headers });
  ```

  ```bash cURL theme={null}
  curl '{BASE_URL}/rest/v1/users?select=id,email&limit=20' -H "apikey: {API_KEY}" -H "Authorization: Bearer {API_KEY}"
  ```
</CodeGroup>

### GET /rest/v1/{table}?id=eq.{id}

Read a single row by primary key (or any unique column) using the eq filter. Add Accept: application/vnd.pgrst.object+json to receive a single object instead of an array.

<ParamField path="table" type="string" required>
  Table or view name
</ParamField>

<CodeGroup>
  ```python Python theme={null}
  response = requests.get(f"{BASE_URL}/rest/v1/users?id=eq.{user_id}", headers={**headers, "Accept": "application/vnd.pgrst.object+json"})
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(`${BASE_URL}/rest/v1/users?id=eq.${userId}`, { headers: { ...headers, Accept: "application/vnd.pgrst.object+json" } });
  ```

  ```bash cURL theme={null}
  curl '{BASE_URL}/rest/v1/users?id=eq.{user_id}' -H "apikey: {API_KEY}" -H "Authorization: Bearer {API_KEY}" -H "Accept: application/vnd.pgrst.object+json"
  ```
</CodeGroup>

## Writing rows

### POST /rest/v1/{table}

Insert one or many rows. Pass a single JSON object or an array. Add Prefer: return=representation to receive the inserted rows back. Use Prefer: resolution=merge-duplicates with on\_conflict= for upsert semantics.

<ParamField path="table" type="string" required>
  Target table
</ParamField>

<RequestExample>
  ```json Request theme={null}
  { "email": "ana@acme.io", "role": "admin" }
  ```
</RequestExample>

<CodeGroup>
  ```python Python theme={null}
  response = requests.post(f"{BASE_URL}/rest/v1/users", headers={**headers, "Prefer": "return=representation"}, json={"email": "ana@acme.io", "role": "admin"})
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(`${BASE_URL}/rest/v1/users`, { method: "POST", headers: { ...headers, Prefer: "return=representation" }, body: JSON.stringify({ email: "ana@acme.io", role: "admin" }) });
  ```

  ```bash cURL theme={null}
  curl -X POST '{BASE_URL}/rest/v1/users' -H "apikey: {API_KEY}" -H "Authorization: Bearer {API_KEY}" -H "Content-Type: application/json" -H "Prefer: return=representation" -d '{"email": "ana@acme.io", "role": "admin"}'
  ```
</CodeGroup>

### PATCH /rest/v1/{table}

Update rows that match the filter in the query string. Always include a filter; without one, PATCH updates every row in the table.

<ParamField path="table" type="string" required>
  Target table
</ParamField>

<RequestExample>
  ```json Request theme={null}
  { "role": "viewer" }
  ```
</RequestExample>

<CodeGroup>
  ```python Python theme={null}
  response = requests.patch(f"{BASE_URL}/rest/v1/users?id=eq.{user_id}", headers=headers, json={"role": "viewer"})
  ```

  ```typescript TypeScript theme={null}
  await fetch(`${BASE_URL}/rest/v1/users?id=eq.${userId}`, { method: "PATCH", headers, body: JSON.stringify({ role: "viewer" }) });
  ```

  ```bash cURL theme={null}
  curl -X PATCH '{BASE_URL}/rest/v1/users?id=eq.{user_id}' -H "apikey: {API_KEY}" -H "Authorization: Bearer {API_KEY}" -H "Content-Type: application/json" -d '{"role": "viewer"}'
  ```
</CodeGroup>

### DELETE /rest/v1/{table}

Delete rows that match the filter in the query string. Always include a filter; without one, DELETE removes every row.

<ParamField path="table" type="string" required>
  Target table
</ParamField>

<CodeGroup>
  ```python Python theme={null}
  response = requests.delete(f"{BASE_URL}/rest/v1/users?id=eq.{user_id}", headers=headers)
  ```

  ```typescript TypeScript theme={null}
  await fetch(`${BASE_URL}/rest/v1/users?id=eq.${userId}`, { method: "DELETE", headers });
  ```

  ```bash cURL theme={null}
  curl -X DELETE '{BASE_URL}/rest/v1/users?id=eq.{user_id}' -H "apikey: {API_KEY}" -H "Authorization: Bearer {API_KEY}"
  ```
</CodeGroup>

## Stored procedures

### POST /rest/v1/rpc/{function_name}

Call a Postgres function (stored procedure) defined in the public schema. Pass arguments as a JSON body. Functions returning a table are listable like a regular endpoint.

<ParamField path="function_name" type="string" required>
  Postgres function name
</ParamField>

<RequestExample>
  ```json Request theme={null}
  { "arg1": "value", "arg2": 42 }
  ```
</RequestExample>

<CodeGroup>
  ```python Python theme={null}
  response = requests.post(f"{BASE_URL}/rest/v1/rpc/get_user_stats", headers=headers, json={"user_id": "..."})
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(`${BASE_URL}/rest/v1/rpc/get_user_stats`, { method: "POST", headers, body: JSON.stringify({ user_id: "..." }) });
  ```

  ```bash cURL theme={null}
  curl -X POST '{BASE_URL}/rest/v1/rpc/get_user_stats' -H "apikey: {API_KEY}" -H "Authorization: Bearer {API_KEY}" -H "Content-Type: application/json" -d '{"user_id": "..."}'
  ```
</CodeGroup>

## Error Responses

| Status | Code              | Description                                                  |
| ------ | ----------------- | ------------------------------------------------------------ |
| 401    | `unauthorized`    | Missing or invalid apikey/Authorization headers              |
| 403    | `rls_denied`      | Row Level Security policy denied the operation for this user |
| 404    | `not_found`       | Table or view does not exist in the public schema            |
| 409    | `conflict`        | Insert violated a unique constraint or foreign key           |
| 422    | `invalid_request` | Malformed filter syntax or invalid column reference          |
