# API keys API
Source: https://docs.tryardent.com/api/api-keys
Create, list, and revoke organization API keys
API keys let automation call Ardent without a human browser login. Create the narrowest key that can do the job, store it in a secret manager, and revoke it when it is no longer needed.
## Endpoints
| Method | Path | Description |
| -------- | ----------------------------------------------------------------- | ---------------------------------------------- |
| `GET` | [`/v1/orgs/{org_id}/api-keys`](/api/api-keys/list) | List API keys. Secret values are not returned |
| `POST` | [`/v1/orgs/{org_id}/api-keys`](/api/api-keys/create) | Create an API key. The secret is returned once |
| `DELETE` | [`/v1/orgs/{org_id}/api-keys/{api_key_id}`](/api/api-keys/delete) | Revoke an API key |
# Create an API key
Source: https://docs.tryardent.com/api/api-keys/create
openapi.public.json POST /v1/orgs/{org_id}/api-keys
Create an API key with a role and optional scopes
The secret token is shown only once, in the create response. Copy it directly into your secret manager.
The key's role determines what it can do; `scopes` optionally restricts it to a subset of the role's permissions. Scopes are permission keys like `connectors.read` — each role's `permissions` array from [`GET /v1/orgs/{org_id}/roles`](/api/orgs) lists the valid values.
# Revoke an API key
Source: https://docs.tryardent.com/api/api-keys/delete
openapi.public.json DELETE /v1/orgs/{org_id}/api-keys/{api_key_id}
Revoke an API key immediately
Revocation is immediate — the next request with that token fails authentication.
# List API keys
Source: https://docs.tryardent.com/api/api-keys/list
openapi.public.json GET /v1/orgs/{org_id}/api-keys
List an organization's active API keys
List responses include each key's name, key prefix, scopes, creation time, expiration, and last-used time — never the secret token.
# Authentication
Source: https://docs.tryardent.com/api/authentication
Authenticate API requests with bearer tokens and manage access safely
The API uses bearer tokens. Create them in **Ardent dashboard > Settings > API keys** (or via the [API keys endpoints](/api/api-keys)) and store them in a secret manager. Keys start with `sk-ard_live_` (or `sk-ard_test_` in test environments) and go in an `Authorization: Bearer` header on every request.
```bash theme={null}
curl -H "Authorization: Bearer $ARDENT_TOKEN" https://api.tryardent.com/v1/projects
```
## Token handling
* Store tokens in your CI secret manager or platform vault.
* Do not commit tokens to source control.
* Rotate tokens when someone leaves the team or a token is exposed.
* Prefer the least-privileged role that can perform the job.
## API key roles
| Role ID | Intended use |
| ----------------- | ---------------------------------------------------------------------- |
| `role_org_viewer` | Read-only automation and reporting |
| `role_org_member` | Create and modify day-to-day resources such as branches and connectors |
| `role_org_admin` | Administrative automation, including team and API-key management |
| `role_org_owner` | Full organization control |
List the roles available in your organization with [`GET /v1/orgs/{org_id}/roles`](/api/orgs).
## CLI and API tokens
The CLI can also use API tokens through `ARDENT_TOKEN`:
```bash theme={null}
export ARDENT_TOKEN="sk-ard_..."
ardent branch create pr-123 --print-url
```
For local interactive work, `ardent login` is friendlier. For CI and scripts, `ARDENT_TOKEN` is more explicit.
## Common auth errors
| Status | Meaning | What to do |
| ------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `401 Unauthorized` | The token is missing, expired, revoked, or malformed | Check the token and create a new one if needed |
| `403 Forbidden` | The token is valid but does not have access to that organization or action | Use a token with the right role, or ask an organization admin to update access |
| `404 Not Found` | The resource does not exist or is outside the token's accessible scope | Check the ID and organization context |
## Example: list projects
```bash theme={null}
curl -H "Authorization: Bearer $ARDENT_TOKEN" https://api.tryardent.com/v1/projects
```
# Branches API
Source: https://docs.tryardent.com/api/branches
Create and list isolated database branches
Branches are isolated Postgres databases created from a connector. Use them for CI runs, local development, migration testing, agent work, and short-lived review environments.
## Endpoints
| Method | Path | Description |
| ------ | --------------------------------------------------- | ----------------------------- |
| `POST` | [`/v1/branch/create`](/api/branches/create) | Create a branch |
| `GET` | [`/v1/branches/{connector_id}`](/api/branches/list) | List branches for a connector |
Branch deletion is CLI-only today: [`ardent branch delete`](/cli/branches). Switching (`ardent branch switch`) is a CLI concept — the API has no notion of a current branch.
Branch connections go through Ardent's routing layer, where TLS ends. Configure SQL clients and IDEs accordingly:
* Set SSL mode to `require`. This encrypts the connection; without a custom root certificate it does not validate a CA chain.
* Leave root certificate, CA bundle, and `sslrootcert` fields blank. A source database CA bundle can't verify the routing host and causes TLS errors.
* Keep `channel_binding=disable`. Clients that force channel binding reject valid branch connections.
## Naming guidance
Use names that are unique, readable, and safe to clean up automatically:
* `pr-123-456789`
* `agent-fix-login-20260601`
* `migration-add-index-smoke-test`
Avoid embedding secrets, customer names, or production incident details in branch names.
# Create a branch
Source: https://docs.tryardent.com/api/branches/create
openapi.public.json POST /v1/branch/create
Start an async branch create and get an operation handle
Branch creation is asynchronous: the create call returns an operation handle, and polling that operation gives you the branch's connection details.
All three body fields are required. For Postgres branches, `service_type` is `postgres` — not the connector type `postgresql`.
The response is `202 Accepted` with an operation handle:
```json theme={null}
{
"operation_id": "op_123",
"status": "pending",
"type": "branch_create",
"resource_id": "br_123"
}
```
Poll the operation until it finishes. On `completed`, its `result` holds the branch details:
```bash theme={null}
curl -H "Authorization: Bearer $ARDENT_TOKEN" "https://api.tryardent.com/v1/operations/op_123?wait=10"
```
```json theme={null}
{
"id": "op_123",
"status": "completed",
"result": {
"branch_id": "br_123",
"name": "pr-123",
"connector_id": "conn_123",
"service_type": "postgres",
"status": "active",
"branch_url": "postgresql://user:***@pr-123-postgres.routing.tryardent.com:5432/db?sslmode=require&channel_binding=disable",
"pooled_branch_url": null,
"pooled_branch_prisma_url": null,
"created_at": "2026-06-01T12:00:00Z",
"read_ready_at": "2026-06-01T12:00:05Z",
"write_ready_at": "2026-06-01T12:00:05Z",
"masked_ready_at": null
}
}
```
See [Operations](/api/operations) for the polling model and the `?wait=` parameter.
Treat `branch_url` as sensitive, and use it exactly as returned — including `sslmode=require` and `channel_binding=disable`. A `completed` operation can still return a null `result` or a null `branch_url`, so confirm `result.branch_url` is present and non-empty before using it. If it's missing, fail closed: never fall back to the main connection string or any other configured URL.
Retries are safe. Send an `X-Idempotency-Key` header with the create request; re-sending the same connector, service type, and name resumes the original request instead of creating a duplicate.
Create can fail with: `400` (a required field is missing), `409` (this create is already in flight, or the idempotency key was reused for a different request), `422` (the connector's engine isn't ready to branch), or `503` (Ardent could not start the work — retry).
# List branches
Source: https://docs.tryardent.com/api/branches/list
openapi.public.json GET /v1/branches/{connector_id}
List a connector's branches with their connection URLs
Returns a JSON array of branch rows. Each row includes the branch's metadata (`id`, `name`, `status`, `created_at`, and more) plus `branch_url` and the pooled URL fields when available.
Use this to populate internal dashboards or cleanup tools. For shell scripts that only need a URL, the CLI is simpler:
```bash theme={null}
DATABASE_URL="$(ardent branch create pr-123 --print-url)"
```
# Connectors API
Source: https://docs.tryardent.com/api/connectors
Preflight, create, inspect, and update Postgres connectors
A connector links Ardent to one Postgres source server. The safest API flow is the same as the CLI flow: preflight first, fix anything preflight reports, then create the connector.
## Public lifecycle
| Step | Endpoint | Notes |
| ------------------------ | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| Preflight source | [`POST /v1/connectors/preflight`](/api/connectors/preflight) | Checks source readiness without creating a connector |
| Create connector | [`POST /v1/connectors`](/api/connectors/create) | Stores connector configuration and starts setup |
| Discover schema | [`POST /v1/connectors/{connector_id}/discover`](/api/connectors/discover) | Starts async discovery when needed |
| Select databases/schemas | [`POST /v1/connectors/{connector_id}/selection`](/api/connectors/selection) | Sets selected paths |
| Submit replica decisions | [`PUT /v1/connectors/{connector_id}/replica-identity-decisions`](/api/connectors/replica-identity-decisions) | Resolves table identity choices |
| Start setup | [`POST /v1/connectors/{connector_id}/engine-setup`](/api/connectors/engine-setup) | Starts async setup |
| Inspect connector | [`GET /v1/connectors/{connector_id}`](/api/connectors/get) | Returns status and readiness information |
| List connectors | [`GET /v1/connectors?org_id={org_id}`](/api/connectors/list) | Lists connectors; `org_id` is required |
| Update connector | [`PUT /v1/connectors/{connector_id}`](/api/connectors/update) | Updates supported settings |
| Delete connector | [`DELETE /v1/connectors/{connector_id}`](/api/connectors/delete) | Starts an async delete; returns an operation |
| Lock deletion | [`POST /v1/connectors/{connector_id}/deletion-lock`](/api/connectors/lock) | Blocks deletes until unlocked |
| Unlock deletion | [`DELETE /v1/connectors/{connector_id}/deletion-lock`](/api/connectors/unlock) | Removes the deletion lock |
| List quarantines | [`GET /v1/connectors/{connector_id}/quarantine`](/api/connectors/quarantine-list) | Lists paused replication deployments |
| Release quarantine | [`POST /v1/connectors/{connector_id}/quarantine/{quarantine_id}/release`](/api/connectors/quarantine-release) | Resumes a paused deployment |
Discovery, setup, and deletion can return an operation ID — poll it with the [Operations API](/api/operations).
# Create a connector
Source: https://docs.tryardent.com/api/connectors/create
openapi.public.json POST /v1/connectors
Connect a Postgres source to Ardent
If preflight hasn't passed for this source, create fails with `422` and the response body is the full preflight report — fix the failures it lists and retry.
Include the environment and private connection IDs that your Ardent team has set up:
```json theme={null}
{
"use_environment": true,
"environment_id": "env_123",
"private_link_id": "epl_123"
}
```
`environment_id` and `private_link_id` are only valid together with `use_environment: true`. Add `"allow_high_rtt_placement": true` to accept a far-away placement despite higher latency.
# Delete a connector
Source: https://docs.tryardent.com/api/connectors/delete
openapi.public.json DELETE /v1/connectors/{connector_id}
Start an async connector delete
Deletion is asynchronous: the call returns `202 Accepted` with an operation handle. Poll it like any other [operation](/api/operations).
By default, Ardent waits for in-progress replication to finish before deleting. Add `?force=true` to skip that wait and abandon un-replicated changes.
Two things block a delete: a deletion lock (`409` — unlock it first) and, without `force`, replication that hasn't finished yet.
# Discover schema
Source: https://docs.tryardent.com/api/connectors/discover
openapi.public.json POST /v1/connectors/{connector_id}/discover
Start async schema discovery on a connector
Starts async discovery of the source's schemas and tables. Returns an operation handle — poll it like any other [operation](/api/operations). If discovery is already running, the call joins it instead of starting another.
# Start setup
Source: https://docs.tryardent.com/api/connectors/engine-setup
openapi.public.json POST /v1/connectors/{connector_id}/engine-setup
Start async connector setup
Starts async setup and returns an operation handle — poll it like any other [operation](/api/operations). If setup is already running, the call joins it. If the engine is already set up, the call returns `200` with the connector and a message instead of starting anything.
# Get a connector
Source: https://docs.tryardent.com/api/connectors/get
openapi.public.json GET /v1/connectors/{connector_id}
Inspect a connector's status and readiness
Returns status and readiness information, plus `can_update` and `can_delete` permission flags for the calling token.
# List connectors
Source: https://docs.tryardent.com/api/connectors/list
openapi.public.json GET /v1/connectors
List an organization's connectors
`org_id` is required.
# Lock deletion
Source: https://docs.tryardent.com/api/connectors/lock
openapi.public.json POST /v1/connectors/{connector_id}/deletion-lock
Protect a connector from deletion
While locked, [delete](/api/connectors/delete) refuses with `409` until the lock is removed. Use it to protect production sources from accidental cleanup.
# Preflight a source
Source: https://docs.tryardent.com/api/connectors/preflight
openapi.public.json POST /v1/connectors/preflight
Check a database before connecting it — creates nothing
Preflight checks reachability, writer status, logical replication settings, slot capacity, plugin availability, permissions, and whether the source is already connected — without creating anything.
A failed check is still a `200` — read `preflight_pass` in the response. When grants are the problem, `grant_script` holds ready-to-run SQL that fixes them.
# List quarantines
Source: https://docs.tryardent.com/api/connectors/quarantine-list
openapi.public.json GET /v1/connectors/{connector_id}/quarantine
List a connector's paused replication deployments
If part of a connector's replication keeps crashing, Ardent pauses (quarantines) it to protect your source. Each entry shows what was paused, why, and when.
# Release a quarantine
Source: https://docs.tryardent.com/api/connectors/quarantine-release
openapi.public.json POST /v1/connectors/{connector_id}/quarantine/{quarantine_id}/release
Resume a paused replication deployment
Fix the underlying cause before releasing, or it will likely quarantine again.
# Submit replica identity decisions
Source: https://docs.tryardent.com/api/connectors/replica-identity-decisions
openapi.public.json PUT /v1/connectors/{connector_id}/replica-identity-decisions
Resolve tables that need an identity decision
If Ardent reports tables that need a decision, submit a table-to-decision map:
```json theme={null}
{
"decisions": {
"public.audit_events": "exclude",
"public.invoice_line_items": "replica_identity_full"
}
}
```
Decision values:
| Value | Meaning |
| ----------------------- | ------------------------------------------------------------------------ |
| `exclude` | Do not replicate this table yet |
| `add_pk` | You will add a primary key or valid unique key on source before retrying |
| `replica_identity_full` | Opt into `REPLICA IDENTITY FULL` for this table |
The map is a full replace: tables you omit revert to `exclude`.
# Select databases and schemas
Source: https://docs.tryardent.com/api/connectors/selection
openapi.public.json POST /v1/connectors/{connector_id}/selection
Set which discovered paths the connector replicates
Sets the selected paths from the discovered schema. Requires a completed [discovery](/api/connectors/discover) first — without one, the call fails with `400`.
# Unlock deletion
Source: https://docs.tryardent.com/api/connectors/unlock
openapi.public.json DELETE /v1/connectors/{connector_id}/deletion-lock
Remove a connector's deletion lock
# Update a connector
Source: https://docs.tryardent.com/api/connectors/update
openapi.public.json PUT /v1/connectors/{connector_id}
Change a connector's supported settings
Updates supported settings — for example, which source extensions to leave out of new branches.
# Errors
Source: https://docs.tryardent.com/api/errors
Understand API error responses and safe retry behavior
Ardent uses standard HTTP status codes. Error responses include a `detail` field with a human-readable message.
```json theme={null}
{
"detail": "Project not found"
}
```
Validation errors may include structured field details from the request validator.
## Status codes
| Status | Meaning | Retry? |
| -------------------------- | ---------------------------------------------------- | ------------------------------------------ |
| `400 Bad Request` | Required data is missing or invalid | No. Fix the request first |
| `401 Unauthorized` | Missing or invalid token | No. Refresh or replace the token |
| `403 Forbidden` | Token is valid but not allowed to perform the action | No. Update access or use a different token |
| `404 Not Found` | Resource was not found in the accessible scope | No. Check the resource ID |
| `409 Conflict` | The request conflicts with current resource state | Usually no. Fetch current state and decide |
| `422 Unprocessable Entity` | Request body failed validation | No. Fix the request body |
| `429 Too Many Requests` | Too many requests in a short period | Yes, with backoff |
| `500`/`502`/`503` | Server or dependency error | Yes, with bounded retry and alerting |
## Long-running operation failures
For async work, the HTTP request may succeed while the operation later fails. Always [poll the operation](/api/operations) and inspect `status` and `error`:
```json theme={null}
{
"id": "op_123",
"status": "failed",
"stage": "connector_discovery",
"error": "Preflight must pass before setup can continue"
}
```
Treat `failed` as terminal. Fix the cause and start a new operation or retry the supported CLI/API action.
## Automation guidance
Never substitute a production database URL as a fallback when a branch URL is missing — fail loudly instead.
* Fail loudly when required IDs, operation IDs, or branch URLs are missing.
* Retry only temporary failures, and cap retries.
* Log request IDs or timestamps in your own systems so support can help correlate issues.
# Operations API
Source: https://docs.tryardent.com/api/operations
openapi.public.json GET /v1/operations/{operation_id}
Poll long-running Ardent work to completion
Some Ardent work takes longer than a single request: connector discovery, connector setup, resets, and environment operations. Those endpoints return an operation ID that you can poll.
Add `?wait=10` to long-poll: the server holds the request up to that many seconds (max 10) and answers early if the operation finishes. This cuts most polling loops to a single line.
Add `?wait=10` to long-poll: the server holds the request up to that many seconds (max 10) and answers early if the operation finishes. This cuts most polling loops to a single line.
Representative response:
```json theme={null}
{
"id": "op_123",
"org_id": "org_123",
"type": "connector_engine_setup",
"resource_id": "conn_123",
"status": "running",
"stage": "initializing",
"stage_label": "Initializing",
"progress": 35,
"result": null,
"error": null,
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-01T12:02:00Z"
}
```
`stage` is a machine token; `stage_label` is the human-readable version for progress displays.
## Status values
| Status | Meaning |
| ----------- | ------------------------------- |
| `pending` | The operation is queued |
| `running` | Work is in progress |
| `completed` | Work finished successfully |
| `failed` | Work reached a terminal failure |
## Results
When an operation completes, `result` holds the outcome. For `branch_create` operations, `result` contains the full branch details, including `branch_url` — that's where automation picks up the connection string. See [Branches](/api/branches) for the shape.
## Polling loop
```bash theme={null}
while true; do
json="$(curl -s -H "Authorization: Bearer $ARDENT_TOKEN" "https://api.tryardent.com/v1/operations/$OPERATION_ID?wait=10")"
status="$(node -e 'console.log(JSON.parse(process.argv[1]).status)' "$json")"
case "$status" in
completed) echo "$json"; break ;;
failed) echo "$json" >&2; exit 1 ;;
pending|running) : ;; # no sleep needed: ?wait=10 makes the server hold each poll
*) echo "Unknown operation status: $status" >&2; exit 1 ;;
esac
done
```
Poll with a bounded timeout in production automation. If an operation fails, read `error`, fix the cause, and start a new supported action rather than assuming the old operation will recover.
# Organizations API
Source: https://docs.tryardent.com/api/orgs
Manage organizations, members, invites, and roles
Organization APIs are for team administration: listing members, sending invites, updating roles, and managing organization metadata.
## Organization endpoints
| Method | Path | Description |
| -------- | ------------------- | ----------------------------------------------- |
| `GET` | `/v1/orgs` | List organizations — dashboard session only |
| `POST` | `/v1/orgs` | Create an organization — dashboard session only |
| `GET` | `/v1/orgs/{org_id}` | Get organization details |
| `PATCH` | `/v1/orgs/{org_id}` | Update organization metadata |
| `DELETE` | `/v1/orgs/{org_id}` | Delete an organization |
`GET /v1/orgs` and `POST /v1/orgs` accept only a logged-in dashboard session, not API tokens — an `sk-ard_` token gets `401`. API tokens belong to one organization, so they don't need to list or create orgs. Every other endpoint on this page accepts API tokens.
## Members and invites
| Method | Path | Description |
| -------- | -------------------------------------------- | -------------------- |
| `GET` | `/v1/orgs/{org_id}/members` | List members |
| `PATCH` | `/v1/orgs/{org_id}/members/{target_user_id}` | Update a member role |
| `DELETE` | `/v1/orgs/{org_id}/members/{target_user_id}` | Remove a member |
| `POST` | `/v1/orgs/{org_id}/invite` | Invite a user |
| `GET` | `/v1/orgs/{org_id}/invites` | List pending invites |
| `DELETE` | `/v1/orgs/{org_id}/invites/{invite_id}` | Revoke an invite |
| `GET` | `/v1/orgs/{org_id}/roles` | List available roles |
## Invite a teammate
```bash theme={null}
curl -H "Authorization: Bearer $ARDENT_TOKEN" -X POST https://api.tryardent.com/v1/orgs/org_123/invite -H 'Content-Type: application/json' -d '{"email":"teammate@example.com","role_key":"member"}'
```
## Update a role
```bash theme={null}
curl -H "Authorization: Bearer $ARDENT_TOKEN" -X PATCH https://api.tryardent.com/v1/orgs/org_123/members/user_123 -H 'Content-Type: application/json' -d '{"role_key":"admin"}'
```
## Role guidance
* Use `viewer` for read-only users and reporting automation.
* Use `member` for developers who create branches and manage day-to-day resources.
* Use `admin` for teammates who manage access and shared resources.
* Reserve `owner` for organization-level control.
An organization's last owner can't be demoted or removed — promote someone else first.
# API overview
Source: https://docs.tryardent.com/api/overview
Build on Ardent with the REST API
The Ardent API lets you create projects, connect Postgres sources, create branches, poll long-running operations, and manage organization access from your own automation.
Use the CLI for interactive setup and day-to-day terminal work. Use the API when you are building CI systems, internal developer platforms, custom dashboards, or agent workflows.
## Base URL
```text theme={null}
https://api.tryardent.com/v1
```
All examples use JSON over HTTPS and assume your token is stored in `ARDENT_TOKEN`.
## Authentication
Pass an API token with each request:
```bash theme={null}
curl -H "Authorization: Bearer $ARDENT_TOKEN" https://api.tryardent.com/v1/projects
```
Create and revoke tokens in **Ardent dashboard > Settings > API keys**, or via the [API keys endpoints](/api/api-keys). See [Authentication](/api/authentication) for token handling, roles, and troubleshooting.
## Resource model
```text theme={null}
Organization
└─ Project
└─ Connector
└─ Branch
```
* **Organization**: your team and billing/security boundary.
* **Project**: a workspace for related connectors and branches.
* **Connector**: one Postgres source server that Ardent can replicate and branch.
* **Branch**: an isolated database created from a connector.
## A minimal API flow
1. List or create a project.
2. Preflight a Postgres source.
3. Create a connector after preflight passes.
4. Create a branch from the connector — this returns an operation ID.
5. Poll the operation; when it completes, `result.branch_url` is the connection string for your app, test runner, or agent.
```bash theme={null}
curl -H "Authorization: Bearer $ARDENT_TOKEN" -X POST https://api.tryardent.com/v1/branch/create -H 'Content-Type: application/json' -d '{"connector_id":"conn_123","service_type":"postgres","name":"pr-123"}'
```
For Postgres branches, `service_type` is `postgres` — not the connector type `postgresql`. The create call returns `202 Accepted` with an `operation_id` — see [Branches](/api/branches) for the full flow.
## Long-running work
Some API calls return immediately with an operation ID while Ardent continues the work in the background. Connector discovery and setup are the most common examples.
```bash theme={null}
curl -H "Authorization: Bearer $ARDENT_TOKEN" https://api.tryardent.com/v1/operations/op_123
```
See [Operations](/api/operations) for the full polling model.
## Stability notes
Read the fields you need and ignore unknown ones — the API may add fields over time. Treat missing required fields, failed operations, and empty branch URLs as hard failures in automation. See [Errors](/api/errors) for status codes and retry guidance.
# Projects API
Source: https://docs.tryardent.com/api/projects
Create, list, update, and delete Ardent projects
Projects organize related connectors and branches inside an organization. Most teams create one project per application or environment family.
## Endpoints
| Method | Path | Description |
| -------- | --------------------------------------------------- | ---------------------------------- |
| `POST` | [`/v1/projects`](/api/projects/create) | Create a project |
| `GET` | [`/v1/projects`](/api/projects/list) | List projects visible to the token |
| `GET` | [`/v1/projects/{project_id}`](/api/projects/get) | Get one project |
| `PATCH` | [`/v1/projects/{project_id}`](/api/projects/update) | Rename a project |
| `DELETE` | [`/v1/projects/{project_id}`](/api/projects/delete) | Delete a project |
Deleting a project takes everything under it with it. For routine cleanup, delete branches or unused connectors instead.
# Create a project
Source: https://docs.tryardent.com/api/projects/create
openapi.public.json POST /v1/projects
Create a project in an organization
`org_id` is optional when the token has exactly one clear organization. Pass it explicitly in automation.
# Delete a project
Source: https://docs.tryardent.com/api/projects/delete
openapi.public.json DELETE /v1/projects/{project_id}
Delete a project and everything under it
Deleting a project takes everything under it with it. For routine cleanup, delete branches or unused connectors instead.
# Get a project
Source: https://docs.tryardent.com/api/projects/get
openapi.public.json GET /v1/projects/{project_id}
Fetch one project by ID
# List projects
Source: https://docs.tryardent.com/api/projects/list
openapi.public.json GET /v1/projects
List projects visible to the token
# Rename a project
Source: https://docs.tryardent.com/api/projects/update
openapi.public.json PATCH /v1/projects/{project_id}
Update a project's mutable fields
Only provided fields are changed. Today the only mutable field is `name`.
# Architecture
Source: https://docs.tryardent.com/architecture
How Ardent's split-plane architecture keeps your data safe and your branches fast
Ardent separates the **control plane** from the **data plane**:
* **Control plane:** Operated by Ardent. Handles branch coordination, replication health monitoring, connection routing, auto-scaling, and policy enforcement. Spans across all deployments.
* **Data plane:** Where your data lives. Includes Ardent's replication pipeline, your read replica, and your branches. Can be deployed on your cloud or ours.
Only metadata (schema structure, replication status, branch state) flows from the data plane to the control plane. Your actual data never leaves the data plane.
## How it works
```mermaid actions={false} theme={null}
%%{init: {'themeCSS': ".cluster > rect { min-width: 42rem; }"}}%%
flowchart TB
subgraph CP["Control plane"]
direction TB
ORCH["Branch coordination"]
PRXY["Branch proxy"]
ROUTE["Connection routing"]
HEALTH["Health and WAL monitoring"]
POL["Policies and auth"]
end
subgraph DP["Data plane"]
direction LR
PROD[("Production DB")] -->|logical replication| AR["Ardent Replicator + Kafka"]
AR --> REP[("Read replica")]
REP --> B1[("Branch 1")]
REP --> B2[("Branch 2")]
REP --> B3[("Branch 3")]
end
CP <-->|metadata only| DP
```
### Control plane
Always operated by Ardent. Coordination, policies, routing, and visibility across every branch and connection.
* **Branch lifecycle:** Create, suspend, delete, and route traffic to the right branch. Connection URLs route through Ardent's proxy. Every session is tied to an account, policies apply per branch, and raw URLs never leak.
* **Replication coordination:** Ardent watches replication health and scales the Kafka layer that backs it, so you never have to size or operate brokers yourself.
* **Connector setup:** When you connect a database, Ardent inspects and validates it, and gives you connector-level settings: the default database name for new branches, and [any SQL to run on each new branch](/cli/settings).
* **Auth, usage, and billing:** Org access, API keys, and usage tracking. Per-user and per-branch visibility Postgres does not give you out of the box.
### Data plane
Where your data physically lives and where branches are served from. Runs on Ardent's infrastructure (fully managed) or in your own cloud account (managed, your cloud).
* **Ardent Replicator**
* Takes logical replication from production through a Kafka-backed pipeline into the read replica.
* Handles DDL in flight using **custom event triggers** that detect DDL on the primary and replay it in order on the replica — schema and data changes stream together with no gaps, duplicates, or silent drift, even across failures and retries.
* Manages WAL and slot pressure so replication does not destabilize the primary.
* Quarantines, resumes, and recovers broken streams without manual replay scripts.
* **Read replica:** Stays in sync with production; branches are always created from the replica, never from production directly, so branch load never hits your primary.
* **Branches (compute and storage):** Each branch is a full Postgres instance cloned from the replica with **compute that auto-scales with load**. Branches share storage from the same replica through copy-on-write, so scaling to many branches is efficient for both compute and storage.
* **Isolation:** Your data stays inside this boundary. Only metadata crosses to the control plane. This means in **managed, your cloud** mode you can maintain residency of all your data without losing Ardent's ability to manage the service.
You connect your database and open branches. The control plane coordinates; the data plane runs replication, the replica, and branches.
***
## Deployment options
Ardent manages everything from our control plane. The data plane is the only thing that moves.
| | **Fully managed** | **Managed, your cloud** |
| ------------------ | --------------------- | ----------------------- |
| **Control plane** | Ardent infrastructure | Ardent infrastructure |
| **Data plane** | Ardent infrastructure | Your cloud account |
| **Data residency** | Ardent's cloud | Your cloud |
**Fully managed.** We host the entire data plane. Connect your database and we handle the Ardent Replicator, Kafka pipeline, read replica, and branch compute.
**Managed, your cloud.** Same managed experience, but the data plane deploys into your own cloud account. Your data never leaves your infrastructure. The control plane still manages everything via API; all replication and branch compute runs inside your network.
# Authentication
Source: https://docs.tryardent.com/cli/auth
Sign in from your terminal, or use a token in CI
Two ways to sign in:
* **`ardent login`** opens your browser. Use this on your own machine.
* **`ARDENT_TOKEN`** is an environment variable. Use this in CI and scripts.
## ardent login
```bash theme={null}
ardent login
```
Your browser opens to authenticate. Back in the terminal, Ardent selects your project and connector for you if you only have one of each — so you can go straight to `ardent branch create`.
```text theme={null}
✓ Logged in as username@tryardent.com
Auto-selected project 'my-app'
Auto-selected connector 'my-app-db'
Ready to branch:
ardent branch create
```
```text theme={null}
✓ Logged in as username@tryardent.com
3 projects found.
Next step: select a project: ardent project switch
List your projects: ardent project list
```
## Tokens for CI
Set `ARDENT_TOKEN` and run commands normally — no login step:
```bash theme={null}
export ARDENT_TOKEN="sk-ard_..."
ardent branch create pr-123 --print-url
```
When `ARDENT_TOKEN` is set, it always wins — even if the machine has a stored login. There's nothing to clean up first.
In GitHub Actions:
```yaml theme={null}
env:
ARDENT_TOKEN: ${{ secrets.ARDENT_TOKEN }}
steps:
- run: npm install -g ardent-cli
- run: ardent project switch my-project
- run: ardent connector switch prod-db
- run: echo "DATABASE_URL=$(ardent branch create pr-${{ github.run_id }} --print-url)" >> "$GITHUB_ENV"
```
Create and revoke tokens in the dashboard under **Settings > API keys**. Keep them in your secret manager, and rotate any token that leaks.
For a one-off login with a token instead of the browser, `ardent login --token ` stores it like a normal login.
## ardent status
Shows whether you're signed in, and as whom:
```bash theme={null}
ardent status
```
```text theme={null}
✓ Authenticated
Account: Username
Email: username@tryardent.com
Org: Ardent
```
If you're authenticated only through `ARDENT_TOKEN`, status shows a masked token instead of profile details. That's normal in CI.
## ardent logout
```bash theme={null}
ardent logout
```
Deletes the CLI's local state file — stored login, API URL, selections, and cache. It does **not** revoke API tokens; do that in the dashboard.
# Branches
Source: https://docs.tryardent.com/cli/branches
Create isolated database branches and connect to them
A branch is an isolated Postgres database created from your connected source. Run migrations, tests, data investigations, and agents against it — your source database is never touched.
## ardent branch create
Create a branch from the current connector. It becomes your current branch.
```bash theme={null}
ardent branch create my-feature
```
```text theme={null}
✓ Branch 'my-feature' created and checked out in 4.2s
postgresql://user:***@my-feature-postgres.routing.tryardent.com:5432/db?sslmode=require&channel_binding=disable
```
### Options
| Option | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `--print-url` | Print only the connection URL. Best for scripts |
| `--format json` | Print a stable JSON object. See [Configuration & automation](/cli/configuration) |
| `--url-type ` | Which URL to print: `direct` (default), `pooled`, or `prisma` |
| `-s, --service ` | Branch service type. Default `postgres` — not the same as the connector type `postgresql`. Leave it alone unless Ardent support says otherwise |
`--print-url` and `--format` can't be combined. `--url-type` can't be combined with `--format json` — the JSON already includes every URL.
### Use in scripts
```bash theme={null}
DATABASE_URL="$(ardent branch create pr-123 --print-url)"
export DATABASE_URL
```
If `--print-url` prints nothing, stop the script. A missing URL means setup failed — don't fall back to another database.
If creation takes too long, the CLI stops waiting after 65 minutes — but the work usually finishes server-side.
Re-run the exact same command. For the same connector, branch name, and service type, a re-run picks up the original request instead of starting a second one. Changing the name or service type starts a new request.
You can also run `ardent branch list` — if the branch appears there, it finished. Use it.
The error includes an operation ID you can give Ardent support:
```text theme={null}
✗ Branch creation did not complete within 65 minutes. It may still be running server-side — do NOT assume it failed. Re-run the same command to resume, or check `ardent branch list`. If you contact Ardent support, reference operation id .
```
## Connecting to a branch
Use the URL exactly as Ardent returns it, including everything after the `?`:
```bash theme={null}
psql "$(ardent branch info --print-url)"
```
Two settings matter in database GUIs and IDEs:
* **SSL mode:** `require`. Leave root certificate / CA fields **blank** — your source database's certificate won't work here and causes TLS errors.
* **Channel binding:** `disable`, if your client has the setting.
Branch connections go through Ardent's routing layer, where TLS ends. Your connection is encrypted, but it isn't signed by your source database's certificate authority — so a client configured to verify against your source's CA bundle will reject it. Clearing the certificate fields keeps the connection encrypted without that check.
The same routing layer is why clients that force channel binding can reject an otherwise valid connection. `channel_binding=disable` in the URL handles it; set the IDE option too if there is one.
## ardent branch list
List branches for the current connector:
```bash theme={null}
ardent branch list
```
Your current branch is green and marked `*`. `●` means active; `○` means not. Each branch shows its type, readiness, age, and idle time.
Branches:
\* ● my-feature
postgres · ready · created 2026-06-01 · idle 2m (active)
○ staging
postgres · ready · created 2026-05-20 · idle 3d (suspended)
If you're offline, the CLI shows cached data and labels it: `⚠ Offline - showing cached data from ...`.
## ardent branch info
Show details and connection URLs for a branch. With no name, it shows the current branch.
```bash theme={null}
ardent branch info my-feature
```
```text theme={null}
Branch: my-feature (current)
Type: postgres
Status: active
Ready: read + write
Created: 6/1/2026, 12:00:00 PM
Direct URL: postgresql://user:***@my-feature-postgres.routing.tryardent.com:5432/db?sslmode=require&channel_binding=disable
```
The same script flags work here:
```bash theme={null}
ardent branch info --print-url
ardent branch info my-feature --format json
```
## ardent branch switch
Change which branch is current for future CLI commands:
```bash theme={null}
ardent branch switch staging
```
Switching doesn't touch your source database or your app's config. Copy the printed URL into your app when you want to use the branch.
## ardent branch delete
```bash theme={null}
ardent branch delete my-feature
```
The CLI queues the delete, waits for it to finish, then prints `✓ Branch deleted`.
If the CLI stops waiting (after 10 minutes), the delete usually still finishes server-side.
Re-running `ardent branch delete ` is safe — it checks on the existing delete instead of starting a second one. Run `ardent branch list` online to see the final state; the local cache only drops the branch once the server confirms it's gone.
The error includes an operation ID you can give Ardent support:
```text theme={null}
✗ Branch delete did not complete within 10 minutes. It may still be running server-side. If you contact Ardent support, reference operation id .
```
Branches suspend on their own when idle, so cleanup isn't urgent on your laptop. CI should still delete its branches — use `if: always()` (or your CI's equivalent) so every run cleans up.
# Configuration & automation
Source: https://docs.tryardent.com/cli/configuration
Environment variables, exit codes, and JSON output for scripts, CI, and agents
Everything on this page is optional. The defaults work for everyday use — come here when you're wiring the CLI into a script, CI job, or agent.
## Which credentials the CLI uses
In order:
1. The `ARDENT_TOKEN` environment variable, if set.
2. Otherwise, the login stored on this machine from `ardent login`.
The environment variable always wins. In CI, just set `ARDENT_TOKEN` — you don't need to log out first or clean up old logins.
The API endpoint works the same way: `ARDENT_API_URL` wins if set, then any stored value, then the default `https://api.tryardent.com`.
## Where the CLI keeps its state
One file: `~/.ardent/config.json`. It holds your token, API URL, current selections, and a cache for offline use.
`ardent logout` deletes this file. It does **not** revoke API tokens — do that in the dashboard under **Settings > API keys**.
## Exit codes
| Code | Meaning |
| ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0` | Success |
| `1` | Something failed while running — network, API error, timeout |
| `2` | The command itself was wrong — unknown flag, bad value. Also used by `connector preflight` when checks fail and `connector list --fail-on-warnings` when there are warnings |
## JSON output
`branch create` and `branch info` accept `--format json`:
```bash theme={null}
ardent branch create pr-123 --format json
```
```json theme={null}
{
"schema_version": 3,
"id": "br_123",
"name": "pr-123",
"connector_id": "conn_123",
"service_type": "postgres",
"status": "active",
"branch_url": "postgresql://user:***@pr-123-postgres.routing.tryardent.com:5432/db?sslmode=require&channel_binding=disable",
"pooled_branch_url": "postgresql://user:***@pr-123-pooler.routing.tryardent.com:5432/db?sslmode=require&channel_binding=disable",
"pooled_branch_prisma_url": "postgresql://user:***@pr-123-pooler.routing.tryardent.com:5432/db?sslmode=require&channel_binding=disable",
"created_at": "2026-06-01T12:00:00Z",
"read_ready_at": "2026-06-01T12:00:05Z",
"write_ready_at": "2026-06-01T12:00:05Z",
"masked_ready_at": null,
"last_branch_activity": null,
"current": true,
"warning": null
}
```
Three rules keep your scripts stable:
* Read the fields you need; ignore the rest. New fields may be added.
* Fields that don't apply are `null`, never missing.
* Errors are JSON too, with a stable `code`:
```json theme={null}
{
"schema_version": 1,
"error": {
"code": "timeout",
"message": "Branch creation did not complete within 65 minutes. ..."
}
}
```
| Code | When |
| ----------------------------------------- | ------------------------------------------------------------------- |
| `offline` | No network and no usable cache |
| `not_authenticated` | No token found |
| `not_found` | The named branch doesn't exist |
| `no_current_project` / `no_projects` | Nothing selected, or nothing to select |
| `no_connectors` / `connector_unavailable` | No connector selected or it isn't usable |
| `no_current_branch` | No branch named and none selected |
| `invalid_name` / `invalid_arguments` | The name or arguments failed validation |
| `branch_url_missing` | Branch created but the server returned no URL |
| `create_succeeded_but_details_missing` | Branch created but its details couldn't be fetched |
| `timeout` | The operation is still running server-side; the CLI stopped waiting |
| `api_error` | Any other API failure |
Flag mistakes — an unknown `--format` value, two flags that can't be combined — print a plain-text error and exit `2`. They are never wrapped in JSON, because they happen before JSON mode starts.
`--print-url` is the simpler option when you only want the URL: it prints one line and nothing else. You can't combine it with `--format`.
## Timeouts and polling
Branch creates and deletes run on Ardent's servers; the CLI waits and checks progress. By default it waits up to **65 minutes** for a create and **10 minutes** for a delete. If the CLI stops waiting, the work usually finishes server-side anyway — the [Branches page](/cli/branches) covers what to do.
Most CI jobs never need to change this. If yours does:
| Variable | Default | What it does |
| --------------------------------------------- | ------------------ | ----------------------------------------------------------- |
| `ARDENT_BRANCH_CREATE_MAX_WAIT_MS` | `3900000` (65 min) | How long `branch create` waits before giving up |
| `ARDENT_BRANCH_CREATE_POLL_INTERVAL_MS` | adaptive | Fixed time between progress checks during create |
| `ARDENT_BRANCH_CREATE_OPERATION_WAIT_SECONDS` | `5` (max `10`) | How long each progress check is willing to wait server-side |
| `ARDENT_BRANCH_DELETE_MAX_WAIT_MS` | `600000` (10 min) | How long `branch delete` waits before giving up |
| `ARDENT_BRANCH_DELETE_POLL_INTERVAL_MS` | `2000` (2 s) | Fixed time between progress checks during delete |
Always use positive whole numbers. Zero, negative, and non-numeric values are either rejected or fall back to defaults — and a zero or negative interval can hammer the API in a tight loop.
# Connectors
Source: https://docs.tryardent.com/cli/connectors
Check, connect, monitor, and manage your Postgres sources
A connector links Ardent to one Postgres server. Once it's ready, every branch you create comes from that source.
Currently supported connector type: `postgresql`.
## ardent connector preflight
Check a database **before** connecting it. Preflight creates nothing and stores no credentials:
```bash theme={null}
ardent connector preflight postgresql 'postgresql://user:***@host:5432/mydb'
```
**It answers one question: can Ardent safely branch this database?** It checks:
* **Connection** — the host is reachable and the credentials work
* **Permissions** — the role can read the tables you want to replicate
* **Replication settings** — `wal_level`, free replication slots, WAL senders, and the `wal2json` plugin
When something is missing, it tells you the exact fix — and prints a ready-to-run SQL grant script when grants are the problem.
```text theme={null}
Running preflight against db.example.com:5432…
Connection checks:
✓ tcp reachable
✓ auth
✓ postgres metadata
✓ is writer
✓ wal level (actual: logical)
✓ replication slots (2/10 used)
✓ wal senders (max: 10)
✓ wal2json
✗ can read tables
Missing SELECT on public.orders
→ Run the grant script below, then re-run preflight.
Detected source provider: vanilla
--- Grant script ---
-- psql artifact generated by Ardent
-- ...
✗ Preflight checks did NOT pass. Fix the failures above before creating.
```
| Option | Description |
| ---------------------------- | ------------------------------------------------------------------ |
| `--schemas ` | Only check these schemas, e.g. `public,billing` |
| `--byoc` | Run the check through your customer-owned Ardent environment |
| `--environment-id ` | Which environment to use (needs `--byoc`) |
| `--private-link-id ` | Which private connection to use (needs `--byoc`) |
| `--allow-high-rtt-placement` | Allow a far-away placement despite higher latency (needs `--byoc`) |
Preflight exits `0` when everything passes and `2` when it doesn't, so you can use it in scripts.
## ardent connector create
When preflight passes, connect the database:
```bash theme={null}
ardent connector create postgresql 'postgresql://user:***@host:5432/mydb' --name prod-db
```
**Wrap the URL in single quotes.** Passwords often contain characters your shell would otherwise interpret.
| Option | Description |
| ------------------------------------- | ---------------------------------------------------------------------------------- |
| `-n, --name ` | Connector name. Leave it off and Ardent derives one from your project and database |
| `--byoc [environment-id]` | Create in a customer-owned Ardent environment |
| `--environment-id ` | Which environment (needs `--byoc`) |
| `--private-link-id ` | Which private connection. Auto-picked when the environment has exactly one |
| `--allow-high-rtt-placement` | Allow a far-away placement despite higher latency (needs `--byoc`) |
| `--replica-identity-decisions ` | JSON file of table decisions — see below |
| `--accept-replica-identity-defaults` | Accept the default decision for every table, without prompting |
| `--write-replica-plan ` | Write the recommended decisions to a file and stop, so you can review before setup |
The last three are different ways to answer replica identity decisions — use at most one. With none, the CLI walks you through the choices interactively.
```text theme={null}
✓ Connector created and ready
ID: conn_abc123
Ready to branch:
ardent branch create
```
First-time setup copies your data, so it can take minutes to hours depending on database size, write rate, and network speed. After that, branches are created in seconds regardless of size.
### Replica identity decisions
Some tables have no primary key or valid unique index, so Postgres can't tell which row an UPDATE or DELETE touched. When Ardent finds tables like this, the CLI asks what to do with each one. In scripts (no terminal), it exits with code `1` instead of guessing — pass one of the three flags above. You can also answer ahead of time: `--replica-identity-decisions` takes a JSON file mapping each table to a choice.
Most databases have keys on every table and never hit this.
A valid unique index means unique, NOT NULL on every column, non-partial, and non-deferrable. Tables without a primary key or an index like that need a decision:
```json theme={null}
{
"public.audit_events": "exclude",
"public.invoice_line_items": "replica_identity_full",
"public.legacy_orders": "add_pk"
}
```
| Value | Meaning |
| ----------------------- | ---------------------------------------------------------------- |
| `exclude` | Don't replicate this table yet |
| `add_pk` | You'll add a primary key or unique key on the source, then retry |
| `replica_identity_full` | Use `REPLICA IDENTITY FULL` for this table |
The file must cover every listed table. With `--accept-replica-identity-defaults`, undecided tables are excluded.
`REPLICA IDENTITY FULL` makes UPDATE and DELETE write more to your database's WAL. Choose it deliberately. Ardent never changes replica identity on your source — when a change is needed, it prints the SQL for you to run.
## ardent connector list
```bash theme={null}
ardent connector list
```
Your active connector is green and marked `*`. Status icons:
| Icon | Meaning |
| ---------- | -------------------------------------------------------------------- |
| `●` green | Ready to branch |
| `●` yellow | Working, but degraded — some source objects are left out of branches |
| `◐` cyan | Setup still in progress |
| `○` red | Broken — failed or unreachable |
Warnings appear under the connector, with a `⚠` count next to the name. A `[delete locked]` marker means deletion is locked (see below).
In CI, add `--fail-on-warnings` to exit with code `2` when any connector has warnings.
## ardent connector status
Show one connector's health and setup progress:
```bash theme={null}
ardent connector status prod-db
```
```text theme={null}
Connector: prod-db
Service: postgresql
Status: Ready
```
`validating` means source checks or setup are running right now.
## ardent connector switch
Change the active connector without changing projects:
```bash theme={null}
ardent connector switch staging-db
```
## ardent connector retry-setup
Finish or repair setup without deleting the connector. Your credentials and configuration are kept.
```bash theme={null}
ardent connector retry-setup prod-db
```
What happens depends on the connector's state:
* **Checks are stale or failed** — the CLI re-checks the source first, then runs setup.
* **Already healthy** — nothing to do; the CLI says so and exits.
* **Setup already running** — safe to run anyway; it joins the running setup instead of starting another. If that setup still needs replica identity decisions, the CLI exits with code `1` — decisions can't change mid-setup. Wait for it to finish, then retry.
If the original setup needed replica identity decisions, pass the same file or flag again:
```bash theme={null}
ardent connector retry-setup prod-db --replica-identity-decisions ./decisions.json
ardent connector retry-setup prod-db --accept-replica-identity-defaults
```
## ardent connector update
Change connector configuration. One setting today: which source extensions to leave out of new branches.
```bash theme={null}
ardent connector update prod-db --drop-extensions pgmq,supabase_vault
```
Pass an empty string to clear the list:
```bash theme={null}
ardent connector update prod-db --drop-extensions ''
```
## ardent connector delete
```bash theme={null}
ardent connector delete staging-db
```
By default, Ardent waits for in-progress replication to finish so no changes are lost. `--force` skips the wait and abandons those changes.
Two things can stop a delete:
* **Deletion is locked** — someone ran `connector lock` on it. Unlock first.
* **A branch has un-replicated changes** — wait, or re-run with `--force`.
## Locks and quarantines
You'll rarely need these two, but you should know they exist.
```bash theme={null}
ardent connector lock prod-db --reason "production source"
ardent connector unlock prod-db
```
While locked, `connector delete` refuses and tells you to unlock first. Locked connectors show `[delete locked]` in `connector list`.
If part of a connector's replication keeps crashing, Ardent pauses (quarantines) it to protect your source. Quarantines don't show in `connector status` — list them directly:
```bash theme={null}
ardent connector quarantine list prod-db
```
Each entry shows what was paused, why, when, and the exact release command:
```bash theme={null}
ardent connector quarantine release prod-db
```
Fix the underlying cause before releasing, or it will likely quarantine again.
# Team
Source: https://docs.tryardent.com/cli/org
Invite teammates and manage your organization
Roles, from most to least access: `owner`, `admin`, `member`, `viewer`.
### ardent invite
Invite someone to your organization. The role is optional — it defaults to `member`:
```bash theme={null}
ardent invite alice@company.com
ardent invite alice@company.com admin
```
```
✓ Invite sent to alice@company.com
```
### ardent invite list
Pending invites:
```bash theme={null}
ardent invite list
```
```
Pending invites:
alice@company.com
Role: member | Sent: 4/29/2026
```
### ardent invite delete
Revoke a pending invite:
```bash theme={null}
ardent invite delete alice@company.com
```
### ardent org members
Everyone in your organization:
```bash theme={null}
ardent org members
```
```
Organization members:
vikram@company.com (Vikram)
Role: admin
alice@company.com
Role: member
```
### ardent org set-role
```bash theme={null}
ardent org set-role alice@company.com admin
```
Only admins and owners can change roles.
### ardent org remove
```bash theme={null}
ardent org remove alice@company.com
```
Only admins and owners can remove members.
# CLI reference
Source: https://docs.tryardent.com/cli/overview
Install the Ardent CLI and learn the handful of commands you'll use every day
The Ardent CLI connects your Postgres database to Ardent, creates isolated branches of it, and gives you their connection URLs.
Use the CLI at a terminal. Use the [API](/api/overview) when you're building your own integration.
## Install
```bash theme={null}
npm install -g ardent-cli
```
Needs Node.js 18 or newer. Check it worked:
```bash theme={null}
ardent --version
```
Every command explains itself with `--help`:
```bash theme={null}
ardent branch create --help
```
## The commands
| Group | What it manages |
| ------------------------------------------------- | ----------------------------------------- |
| `ardent login`, `ardent logout`, `ardent status` | [Your session](/cli/auth) |
| [`ardent project ...`](/cli/projects) | Projects — the top-level grouping |
| [`ardent connector ...`](/cli/connectors) | Source databases Ardent can branch |
| [`ardent branch ...`](/cli/branches) | Isolated database branches and their URLs |
| [`ardent settings ...`](/cli/settings) | Defaults for new branches |
| [`ardent invite ...`, `ardent org ...`](/cli/org) | Your team |
## How the CLI knows what you mean
The CLI remembers three things: your current **project**, **connector**, and **branch**. Commands act on the current one unless you say otherwise.
After login, Ardent picks your project and connector for you if you only have one of each. If you have more, pick them yourself:
```bash theme={null}
ardent project switch my-project
ardent connector switch prod-db
ardent branch switch my-feature
```
In CI, always switch project and connector explicitly. Scripts shouldn't depend on someone's saved defaults.
## Output for scripts
Two flags cover most automation:
```bash theme={null}
ardent branch create pr-123 --print-url # prints only the connection URL
ardent branch info my-feature --format json # prints a stable JSON object
```
The JSON format, error codes, and exit codes are documented on [Configuration & automation](/cli/configuration).
## Environment variables
| Variable | What it does |
| ---------------- | -------------------------------------------------------------------------------------------------------- |
| `ARDENT_TOKEN` | Authenticate without `ardent login`. When set, it always wins — even if this machine has a stored login. |
| `ARDENT_API_URL` | Point at a different API endpoint. When set, it always wins. Default: `https://api.tryardent.com`. |
That's all most people need. Timeout and polling knobs for CI live on [Configuration & automation](/cli/configuration).
For GitHub Actions, store your token as a secret named `ARDENT_TOKEN` and run commands normally:
```yaml theme={null}
env:
ARDENT_TOKEN: ${{ secrets.ARDENT_TOKEN }}
steps:
- run: npm install -g ardent-cli
- run: ardent project switch my-project
- run: ardent connector switch prod-db
- run: echo "DATABASE_URL=$(ardent branch create pr-${{ github.run_id }} --print-url)" >> "$GITHUB_ENV"
```
## A complete first branch
```bash theme={null}
ardent login
ardent connector preflight postgresql 'postgresql://user:***@host:5432/mydb'
ardent connector create postgresql 'postgresql://user:***@host:5432/mydb' --name prod-db
ardent branch create my-feature --print-url
```
Preflight comes first on purpose. It checks that your database is ready — without creating anything or storing credentials.
## Troubleshooting
| Symptom | What to try |
| ------------------------------------ | ----------------------------------------------------------------------------------------------- |
| `Not authenticated` | Run `ardent login`, or set `ARDENT_TOKEN` |
| Wrong project or connector selected | `ardent project list`, then `ardent project switch ` and `ardent connector switch ` |
| A script needs only the database URL | `ardent branch create --print-url` |
| Preflight fails | Fix the check it names, then run preflight again |
| A branch URL is missing in CI | Treat it as a failure and stop the job. Re-run `ardent branch info --print-url` to check |
# Projects
Source: https://docs.tryardent.com/cli/projects
Projects group your connectors and branches
Projects are the top-level grouping for connectors and branches. Most people start with one; the project commands matter once you manage several environments or teams.
### ardent project create
```bash theme={null}
ardent project create
```
The new project becomes your current project.
```
✓ Project 'staging' created
```
### ardent project list
```bash theme={null}
ardent project list
```
Your current project is green and marked `*`. Offline, the CLI shows cached data and labels it: `⚠ Offline - showing cached data from …`.
Projects:
\* my-app
staging
internal-tools
### ardent project switch
```bash theme={null}
ardent project switch staging
```
Your connector and branch selections belong to the old project, so they're cleared. If the new project has exactly one connector, Ardent selects it for you.
```
Switched to project 'staging'
Auto-selected connector 'staging-db'
```
### ardent project delete
```bash theme={null}
ardent project delete staging
```
This runs immediately — there is no confirmation prompt. Check the name before you press enter.
```
Deleting project...
✓ Project deleted
Name: staging
```
# Settings
Source: https://docs.tryardent.com/cli/settings
Defaults for new branches, branch setup SQL, and local Supabase linking
Settings control what each **new branch** looks like, per connector: the database name in its URL, and SQL that runs right after it's created.
| Key | What it sets |
| ------------ | ------------------------------------------------------------ |
| `default_db` | The database name used in branch connection URLs |
| `branch_sql` | SQL that runs once on every new branch, right after creation |
## branch\_sql: shape every new branch
`branch_sql` is how teams make every branch come up ready — without each developer keeping their own setup script.
Common uses:
* Redact or anonymize sensitive values on every branch.
* Add fixtures or baseline rows.
* Apply DDL your app expects in test environments.
For anonymization patterns, see [Security](/security#branch-anonymization).
`branch_sql` runs on new branches, never on your source database. Keep it safe to run more than once where you can, so branch creation stays predictable.
## ardent settings list
```bash theme={null}
ardent settings list
```
```text theme={null}
Connector: prod-db
default_db myapp
branch_sql (not set) SQL to run on every new branch after creation
```
## ardent settings set
```bash theme={null}
ardent settings set default_db myapp
```
For `branch_sql`, pass SQL inline or point at a file with `@`:
```bash theme={null}
ardent settings set branch_sql @./branch-init.sql
```
Give each script a name with `--hook`, and control order and context:
```bash theme={null}
ardent settings set branch_sql @./anonymize.sql --hook anonymize --order 1
ardent settings set branch_sql @./fixtures.sql --hook fixtures --order 2
```
| Flag | What it does |
| ------------------------------ | ---------------------------------------------------- |
| `--hook ` | Names this script, so several can exist side by side |
| `--order ` | Run order across scripts (lower runs first) |
| `--database ` | Run against this database instead of the default |
| `--run-as ` | Run as this role |
| `--no-database`, `--no-run-as` | Clear a previously set override |
Remove a named script with `ardent settings remove branch_sql --hook `.
## ardent settings remove
```bash theme={null}
ardent settings remove branch_sql
```
## Local Supabase linking
If you run Supabase locally, Ardent can point its services — auth, REST, realtime, storage — at an Ardent branch instead of the local Postgres container. Useful when you want the full local stack running against isolated, real data.
### ardent settings supabase status
Is a local Supabase project linked right now?
```bash theme={null}
ardent settings supabase status
```
If you have several local Supabase projects, name one with `--project ` (the Supabase CLI project id).
### ardent settings supabase link
Link local Supabase to the current branch:
```bash theme={null}
ardent settings supabase link
```
Or to a specific branch:
```bash theme={null}
ardent settings supabase link my-feature --project my-app
```
The CLI checks that local Supabase is running before it changes anything. Use `--skip-health-check` only if you've stopped the services yourself.
### ardent settings supabase unlink
Put local Supabase back on its own local database:
```bash theme={null}
ardent settings supabase unlink
```
`--project ` and `--skip-health-check` work here too.
# PlanetScale
Source: https://docs.tryardent.com/connectors/planetscale
The PlanetScale-specific parts of connecting to Ardent
Ardent works with PlanetScale Postgres. This page covers only what's specific to PlanetScale — the full walkthrough is the [Quickstart](/quickstart).
With the [ardent-cli skill](/workflows/ai-agents#let-the-agent-drive-set-it-up-once) installed, a short ask is enough — the skill fetches this page and runs the checks:
```
Check if my PlanetScale Postgres database is ready to connect to Ardent.
My connection string: [paste here]
```
Without the skill, paste the full directions:
```
Check if my PlanetScale Postgres database is ready to connect to Ardent.
My connection string: [paste here]
Use Ardent preflight first:
ardent connector preflight postgresql ''
Verify:
1. The host is reachable and credentials work.
2. Logical replication is enabled in the PlanetScale dashboard.
3. The URL points to the writable primary: SELECT pg_is_in_recovery(); must return false.
4. The user has replication privileges.
5. Tables selected for replication have a primary key, valid unique NOT NULL index, or REPLICA IDENTITY FULL.
After preflight passes, run:
ardent connector create postgresql ''
```
Two things matter here:
In your PlanetScale dashboard, go to **Settings > Integrations** and enable **Logical Replication**. This is what lets Ardent replicate your data for branching.
Go to **Connect > Connect with: Postgres** and copy the connection string. It looks like:
```text theme={null}
postgresql://user:[YOUR-PASSWORD]@aws.connect.psdb.cloud/mydb?sslmode=require
```
Wrap it in single quotes in your shell — the `?sslmode=…` part must not be interpreted by the shell.
From here, continue with the [Quickstart](/quickstart): run preflight, fix anything it reports, then create the connector.
# AWS RDS
Source: https://docs.tryardent.com/connectors/rds
The RDS-specific parts of connecting to Ardent
Ardent works with AWS RDS for PostgreSQL. This page covers only what's specific to RDS — the full walkthrough is the [Quickstart](/quickstart).
With the [ardent-cli skill](/workflows/ai-agents#let-the-agent-drive-set-it-up-once) installed, a short ask is enough — the skill fetches this page and runs the checks:
```
Check if my AWS RDS Postgres database is ready to connect to Ardent.
My connection string: [paste here]
```
Without the skill, paste the full directions:
```
Check if my AWS RDS Postgres database is ready to connect to Ardent.
My connection string: [paste here]
Use Ardent preflight first:
ardent connector preflight postgresql '' --schemas public
Verify:
1. The endpoint is reachable from Ardent and the security group allows inbound Postgres traffic.
2. The URL points to the writable primary: SELECT pg_is_in_recovery(); must return false.
3. The DB parameter group has rds.logical_replication = 1 and the instance has been rebooted after the change.
4. The connecting role has GRANT rds_replication.
5. The Ardent preflight Grant script has been run for the schemas I want to replicate.
6. Tables selected for replication have a primary key, valid unique NOT NULL index, or REPLICA IDENTITY FULL.
After preflight passes, run:
ardent connector create postgresql ''
```
RDS needs three things before preflight can pass:
Create or modify a parameter group for your RDS Postgres major version and set:
```text theme={null}
rds.logical_replication = 1
```
Apply the parameter group to your instance.
This takes effect only after a reboot. Plan a maintenance window.
Keep enough replication slots and WAL sender capacity for Ardent plus any existing replicas or CDC tools. Don't lower values that other replication consumers rely on.
In the RDS console, open **Databases > your instance > Connectivity & security** and confirm the endpoint and port. Allow Ardent's outbound IP to reach the instance on the Postgres port, usually `5432`. Ask your Ardent contact for the current outbound IP if you don't have it.
If your instance is private-only, use a BYOC environment and PrivateLink instead of allowing public inbound connections.
Connect as a role with `rds_superuser` privileges and grant RDS's replication role to the user Ardent will connect as:
```sql theme={null}
GRANT rds_replication TO your_user;
```
Per-database and per-schema grants may also be needed — preflight generates the exact SQL for those.
Your connection string looks like:
```text theme={null}
postgresql://user:[YOUR-PASSWORD]@your-instance.region.rds.amazonaws.com:5432/mydb
```
From here, continue with the [Quickstart](/quickstart): run preflight, run the grant script if preflight prints one, then create the connector.
Two RDS-specific notes:
* If preflight says the source is a **read replica**, use the writer endpoint instead.
* **WAL retention:** replication slots retain WAL while a consumer is behind. A very low retention limit can invalidate the slot under heavy writes or long transactions, forcing a connector rebuild. Leave enough headroom and watch long-running transactions.
# Self-hosted Postgres
Source: https://docs.tryardent.com/connectors/self-hosted
Prepare any self-managed Postgres 13+ for Ardent
Ardent works with self-managed Postgres 13 or newer. This page prepares your server; the connect-and-branch walkthrough is the [Quickstart](/quickstart).
Setup is a loop: run preflight, apply the SQL it prints, run preflight again until it passes.
```bash theme={null}
ardent connector preflight postgresql 'postgresql://user:[YOUR-PASSWORD]@host:5432/mydb' --schemas public
```
Preflight is fast and non-destructive — it stores no credentials and creates nothing. It prints a pass/fail checklist and a **Grant script** with the exact SQL for your database and schemas.
With the [ardent-cli skill](/workflows/ai-agents#let-the-agent-drive-set-it-up-once) installed, a short ask is enough — the skill fetches this page and runs the checks:
```
Check whether my self-hosted Postgres database is ready to connect to Ardent.
My connection string: [paste here]
```
Without the skill, paste the full directions:
```
Check whether my self-hosted Postgres database is ready to connect to Ardent.
My connection string: [paste here]
Use Ardent preflight first:
ardent connector preflight postgresql '' --schemas public
Then verify these source requirements and tell me exactly what to fix:
1. The host is reachable from Ardent over TLS on the Postgres port.
2. pg_hba.conf allows both normal and replication connections from Ardent's outbound IP.
3. wal_level is logical, max_wal_senders >= 10, max_replication_slots >= 10 (or higher if already higher, since these are minimums Ardent expects), and at least one replication slot is free.
4. wal2json is available. If you create a temporary slot to test it, immediately drop it; if the drop fails, stop and run SELECT pg_drop_replication_slot('ardent_probe'); before doing anything else.
5. The connection is to a writable primary, not a read replica: SELECT pg_is_in_recovery(); must return false.
6. The Ardent role has the grants from the preflight Grant script.
7. Tables selected for replication have a primary key, valid unique NOT NULL index, or REPLICA IDENTITY FULL.
After preflight passes, run:
ardent connector create postgresql ''
```
Ardent connects from a static outbound IP for your environment. If your network restricts inbound traffic, allow that IP on the Postgres port, usually `5432`. Ask your Ardent contact for the current outbound IP if you don't have it.
Make sure `ssl = on` and `pg_hba.conf` accepts **both** regular SQL sessions and logical replication sessions from Ardent. Postgres treats them as different connection types, so both lines matter:
```conf theme={null}
# regular connections: discovery, grants, and bookkeeping
hostssl all ardent_replication /32 scram-sha-256
# replication connections: logical WAL streaming
hostssl replication ardent_replication /32 scram-sha-256
```
After editing `pg_hba.conf`, reload Postgres — a reload is enough, no restart needed:
```sql theme={null}
SELECT pg_reload_conf();
```
Or from the shell: `pg_ctl reload -D ` (or `sudo systemctl reload postgresql` on systemd installs).
Hostnames must resolve to an IPv4 address. IPv6-only hosts are not supported today.
Check the current values first:
```sql theme={null}
SHOW wal_level;
SHOW max_wal_senders;
SHOW max_replication_slots;
```
Ardent needs `wal_level = logical`, at least one free replication slot, and enough WAL sender capacity for Ardent plus anything else already replicating from your database. If your existing values are higher than Ardent's minimums, keep the higher values.
`ALTER SYSTEM SET` only accepts a literal value, not an expression — so `ALTER SYSTEM SET max_wal_senders = 10` on a server that already has 20 (say, for existing standbys) would *lower* it on the next restart and could start refusing those standbys. This block raises each setting only when it's below Ardent's minimum:
```sql theme={null}
DO $$
DECLARE
v_wal_level text := current_setting('wal_level');
v_senders int := current_setting('max_wal_senders')::int;
v_slots int := current_setting('max_replication_slots')::int;
BEGIN
IF v_wal_level <> 'logical' THEN
EXECUTE 'ALTER SYSTEM SET wal_level = ''logical''';
END IF;
IF v_senders < 10 THEN
EXECUTE format('ALTER SYSTEM SET max_wal_senders = %s', 10);
END IF;
IF v_slots < 10 THEN
EXECUTE format('ALTER SYSTEM SET max_replication_slots = %s', 10);
END IF;
END
$$;
```
Prefer doing it by hand? Run the `SHOW` queries above and only change the settings whose values are below 10.
These changes need a Postgres restart. Plan a maintenance window before continuing.
Be careful with `max_slot_wal_keep_size`. A finite value protects your disk, but if a logical slot falls behind past that limit, Postgres can invalidate the slot — which forces a connector rebuild, not a graceful catch-up. For high-write databases, leave enough headroom and monitor retained WAL.
Long idle transactions hold back WAL cleanup. Consider `idle_in_transaction_session_timeout` on roles that might leave transactions open, for example `ALTER ROLE app_user SET idle_in_transaction_session_timeout = '5min';`.
Ardent reads changes through the `wal2json` logical-decoding plugin. Install the package matching your Postgres major version:
* Debian and Ubuntu: `postgresql--wal2json`
* RHEL family: `wal2json_`
Restart Postgres if the plugin wasn't already loaded.
To test it manually, create and immediately drop a temporary slot:
```sql theme={null}
SELECT pg_create_logical_replication_slot('ardent_probe', 'wal2json');
SELECT pg_drop_replication_slot('ardent_probe');
```
If your session is interrupted after the create statement, run the drop manually before continuing — an unconsumed logical slot retains WAL indefinitely and can fill the disk. Verify cleanup:
```sql theme={null}
SELECT slot_name
FROM pg_replication_slots
WHERE slot_name = 'ardent_probe';
```
No rows should come back.
Today, vanilla self-hosted Postgres requires the Ardent role to create event triggers, which is restricted to `SUPERUSER`. That's a broad permission — a Postgres superuser can read and modify anything on the cluster. Use a dedicated role, restrict network access to Ardent's outbound IP, rotate the password deliberately, and prefer a dedicated source cluster or database if your security model requires stricter isolation.
```sql theme={null}
CREATE ROLE ardent_replication WITH LOGIN PASSWORD '';
ALTER ROLE ardent_replication WITH REPLICATION SUPERUSER;
```
The grants below are still worth running. They don't limit a superuser — they document the intended read footprint and make the setup ready for the future narrower Ardent role.
For each database you want to replicate:
```sql theme={null}
GRANT CONNECT ON DATABASE "" TO ardent_replication;
GRANT CREATE ON DATABASE "" TO ardent_replication;
```
`GRANT CREATE ON DATABASE` lets Ardent create its own `pgstream` bookkeeping schema and publication. It does not grant write access to your application schemas.
For each schema you want replicated, run inside the matching database:
```sql theme={null}
GRANT USAGE ON SCHEMA "" TO ardent_replication;
GRANT SELECT ON ALL TABLES IN SCHEMA "" TO ardent_replication;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA "" TO ardent_replication;
ALTER DEFAULT PRIVILEGES IN SCHEMA ""
GRANT SELECT ON TABLES TO ardent_replication;
ALTER DEFAULT PRIVILEGES IN SCHEMA ""
GRANT SELECT ON SEQUENCES TO ardent_replication;
```
The `ALTER DEFAULT PRIVILEGES` lines keep future tables and sequences visible to replication. They only apply to objects created by the role that ran the `ALTER` — if your application creates tables as another role, repeat them with `FOR ROLE`:
```sql theme={null}
ALTER DEFAULT PRIVILEGES FOR ROLE "app_writer" IN SCHEMA ""
GRANT SELECT ON TABLES TO ardent_replication;
ALTER DEFAULT PRIVILEGES FOR ROLE "app_writer" IN SCHEMA ""
GRANT SELECT ON SEQUENCES TO ardent_replication;
```
Find table-owner roles in a schema with:
```sql theme={null}
SELECT DISTINCT tableowner
FROM pg_tables
WHERE schemaname = '';
```
You usually don't need to assemble this by hand — preflight prints the generated grant script for your schemas.
```bash theme={null}
ardent connector preflight postgresql 'postgresql://ardent_replication:[YOUR-PASSWORD]@your-host:5432/mydb' --schemas public
```
Common failures:
* `is writer` fails — the URL points at a read replica. Use the writable primary.
* `wal level` fails — enable logical replication and restart Postgres.
* `wal2json` unverified or failed — install the plugin, or ask Ardent to confirm provider support.
* `can read tables` fails — run the generated grant script.
* `duplicate source` fails — this database is already connected. Run `ardent connector list`.
When preflight passes, continue with the [Quickstart](/quickstart): create the connector and your first branch. One heads-up for that step: tables without a primary key or valid unique index need a [replica identity decision](/cli/connectors#replica-identity-decisions). The CLI walks you through it, and Ardent never runs DDL on your source.
# Supabase
Source: https://docs.tryardent.com/connectors/supabase
The Supabase-specific parts of connecting to Ardent
Ardent works with Supabase Postgres. This page covers only what's specific to Supabase — the full walkthrough is the [Quickstart](/quickstart).
With the [ardent-cli skill](/workflows/ai-agents#let-the-agent-drive-set-it-up-once) installed, a short ask is enough — the skill fetches this page and runs the checks:
```
Check if my Supabase database is ready to connect to Ardent.
My connection string: [paste here]
```
Without the skill, paste the full directions:
```
Check if my Supabase database is ready to connect to Ardent.
My connection string: [paste here]
Use Ardent preflight first:
ardent connector preflight postgresql '' --schemas public
Verify:
1. This is the Direct connection string, not the pooler string.
2. IPv4 is enabled for the Supabase project.
3. The URL points to the writable primary: SELECT pg_is_in_recovery(); must return false.
4. Logical replication and wal2json are available.
5. The Ardent preflight Grant script has been run for the schemas I want to replicate.
6. Tables selected for replication have a primary key, valid unique NOT NULL index, or REPLICA IDENTITY FULL.
After preflight passes, run:
ardent connector create postgresql ''
```
Three things matter here:
In your Supabase dashboard:
1. Click **Connect** in the top right.
2. Select the **Direct** tab.
3. Choose **Direct connection**.
4. Set Type to **URI**.
The pooler connection string — host ending in `pooler.supabase.com`, usually port `6543` — will not work. Ardent replicates your data over a direct, long-lived Postgres connection; poolers only forward regular queries and can't carry replication.
Scroll down in the Connect dialog to the **IPv4 compatible** indicator.
* **Checkmark** — you're good.
* **No checkmark** — click **IPv4 settings** and enable the dedicated IPv4 add-on. Ardent needs a direct IPv4 route to your database.
It looks like:
```text theme={null}
postgresql://postgres:[YOUR-PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres
```
Wrap it in single quotes when you use it in a shell.
From here, continue with the [Quickstart](/quickstart): run preflight, run the grant script if preflight prints one, then create the connector.
One Supabase-specific preflight failure to know: if preflight says the source is a **read replica**, you've copied a replica URL. Use the writable primary instead — branching needs a source that supports logical replication.
Running Supabase locally too? Ardent can point your local Supabase services at a branch — see [Local Supabase linking](/cli/settings#local-supabase-linking).
# Contact
Source: https://docs.tryardent.com/contact
Get in touch with the Ardent team
Questions or setup help? Reach out.
[vikram@tryardent.com](mailto:vikram@tryardent.com)
# FAQ
Source: https://docs.tryardent.com/faq
Common questions answered
Postgres, including [Supabase](/connectors/supabase), [AWS RDS](/connectors/rds), [PlanetScale](/connectors/planetscale), and [self-hosted](/connectors/self-hosted). More databases coming soon.
Branch creation is under 6 seconds, regardless of source database size. First-time connector setup is separate and runs once; how long it takes scales with your data size and write rate.
No. Ardent uses logical replication to sync your data. Your application tables are never written to or modified. Ardent does install its own replication slot and DDL-tracking metadata on the source so schema changes replicate correctly; your data stays untouched.
They auto-suspend after 5 minutes of inactivity. When you reconnect, they spin back up instantly.
Yes. Create a branch and pass the connection URL to Claude Code, Cursor, or any agent. They can read, write, and run migrations without touching production.
Each branch gets its own connection string with no access to your source database. A leaked branch URL — or one handed to an LLM — can't reach production or your real credentials.
No. A branch is a point-in-time snapshot taken from a continuously synced replica. After creation it's isolated: your changes don't affect production, and production changes don't flow in.
As many as you need. Branches are efficient: you only pay storage for data you change on a branch.
Yes, just like git. Everyone on your team can see, switch to, and collaborate on branches. Use [`ardent invite`](/cli/org) to add people to your org.
Most of it. Branches include schema, data, grants, RLS, functions, triggers, and supported extensions. Two things need an explicit choice during connector setup: [replica identity](/cli/connectors#replica-identity-decisions) for tables without a primary key, and some provider-specific extensions. Both are decided before you branch.
Yes — it's just a lot. You'd need logical replication (WAL config, replication slots, schema drift, failure recovery), a branching engine, a connection proxy that routes to the right branch, auto-suspend and resume, credential isolation, and a CLI on top. Ardent is all of that in one `npm install`.
Yes. See our [Security](/security) page for full details.
## Still have questions?
Reach out at [vikram@tryardent.com](mailto:vikram@tryardent.com).
# Ardent: Git for your data infrastructure
Source: https://docs.tryardent.com/index
Ardent creates instant, isolated copies of your production Postgres database — in seconds, right from your terminal.
Test migrations, run schema changes, and let coding agents like Claude Code and Cursor actually verify their database work — all against real data, without touching production.
No more shared staging databases. No more hand-maintained fixtures. An instant database for every task.
Full database copies in under 6 seconds.
Test against production data, not stale fixtures.
Supabase, RDS, PlanetScale, or self-hosted.
## Get started
Branch your Postgres database in a few commands.
With the [ardent-cli skill](/workflows/ai-agents#let-the-agent-drive-set-it-up-once) installed, a short ask is enough — the skill fetches the current docs and works through setup:
```
Set up Ardent for database branching on this project.
My connection string: [paste here]
```
Without the skill, paste the full directions:
```
Set up Ardent for database branching. Act like a teammate at my keyboard:
run the commands yourself, stop only when you need me to do something
only I can do (browser sign-up, paste a credential, run source-database SQL).
Before each step, give me one short line on what's happening and why.
Run these in order:
1. Install the CLI: npm install -g ardent-cli
2. ardent login → opens a browser to authenticate. Tell me to finish
sign-up or login and ping you when done.
3. Tell me to export my Postgres connection string in this shell:
export ARDENT_DB_URL='postgres://...'
Then run:
ardent connector preflight postgresql "$ARDENT_DB_URL"
If preflight prints a Grant script, stop and ask me to run it on the
source database, then re-run preflight until it passes.
4. After preflight passes, run:
ardent connector create postgresql "$ARDENT_DB_URL"
Setup runs server-side and may take minutes or longer depending on
database size. If it's interrupted, resume with:
ardent connector retry-setup
5. ardent branch create my-feature → an isolated copy of my database
I can use however I want without touching prod.
6. ardent branch info → hand me the connection URL. I connect to it
like any Postgres database.
If a command errors, fix it. Docs: https://docs.tryardent.com/quickstart
```
## How it works
Ardent replicates your database into a [read replica](/architecture), then creates isolated **branches** from that replica in seconds. Each branch is a full Postgres database with your real data — like branches in git.
```
Production DB → Replication → Read replica → Many branches (each in seconds)
```
The workflow mirrors git:
* **[`ardent branch create`](/cli/branches)** — spin up an isolated database copy
* **`ardent branch switch`** — move between branches
* **`ardent branch info`** — see the connection URL and status
* **`ardent branch delete`** — clean up when you're done
Branches suspend after 5 minutes of inactivity and wake instantly on reconnect. You pay storage only for the changes you make on a branch — so create as many as you need and throw them away freely.
## Next steps
Supabase, RDS, PlanetScale, and self-hosted Postgres.
Commands, flags, and automation output.
Common questions answered.
# Quickstart
Source: https://docs.tryardent.com/quickstart
Branch your Postgres database in a few minutes
**You'll need:**
* Node.js 18 or newer
* Access to your Postgres database (Supabase, RDS, PlanetScale, or self-hosted)
```bash theme={null}
npm install -g ardent-cli
```
```bash theme={null}
ardent login
```
Your browser opens — sign in or create your account there, then come back to the terminal.
```text theme={null}
✓ Logged in as you@company.com
No projects found.
Create one with: ardent project create
```
On a fresh account, create a project first: `ardent project create my-app`. If you already have exactly one project and connector, Ardent selects them for you.
Save it in your shell so the next two commands can reuse it:
```bash theme={null}
export ARDENT_DB_URL='postgresql://user:password@host:5432/mydb'
```
Keep the single quotes — passwords often contain characters your shell would eat.
Where to find it, and the one thing that usually blocks the next step:
In your Supabase dashboard, open **Connect** and copy the **direct connection** string — not the pooler one. The pooler can't do replication, and preflight will fail on it. Details: [Supabase setup](/connectors/supabase).
Logical replication is usually off by default. Set `rds.logical_replication = 1` in your parameter group and reboot before running preflight. Details: [RDS setup](/connectors/rds).
Use your Postgres connection string from the PlanetScale dashboard. Details: [PlanetScale setup](/connectors/planetscale).
Make sure `wal_level = logical` in your Postgres config (needs a restart if you change it). Details: [Self-hosted setup](/connectors/self-hosted).
```bash theme={null}
ardent connector preflight postgresql "$ARDENT_DB_URL"
```
Preflight checks whether Ardent can branch this database. It creates nothing and stores no credentials — it just tells you what, if anything, to fix.
If a check fails:
* **Missing grants** — preflight prints the SQL. Run it on your database, then run preflight again.
* **`wal level` fails** — logical replication is off. See your provider tab above.
* **"Already connected"** — this database has a connector. Run `ardent connector list`.
When preflight passes:
```bash theme={null}
ardent connector create postgresql "$ARDENT_DB_URL"
```
This copies your data into Ardent once. Small databases take minutes; large ones take longer. Setup runs on Ardent's servers — if your terminal disconnects or you interrupt the command, nothing is lost. Run `ardent connector retry-setup ` and it picks up where it left off.
The new connector becomes your active connector automatically.
```bash theme={null}
ardent branch create my-feature
```
That's an isolated copy of your database, created in seconds. Prove it — connect and query:
```bash theme={null}
psql "$(ardent branch info --print-url)" -c 'select count(*) from ;'
```
Real data, real Postgres, and nothing you do here touches your source database.
## What's next
Install the ardent-cli skill and let Claude Code or Cursor test migrations and schema changes on a branch.
Create and clean up branches automatically in CI.
Point your local stack at branches instead of a shared staging database.
Branches suspend on their own when idle, so there's no cleanup pressure — create one per task and move on.
# Security
Source: https://docs.tryardent.com/security
How Ardent keeps your data safe
Ardent does not put your production data at risk.
## Minimal production footprint
Ardent uses logical replication to sync your table data and does not write to your application tables. During connector setup, Ardent creates one dedicated schema for its own metadata and installs DDL event triggers there so schema changes replicate correctly.
Replication has minimal performance impact on production. Normal application reads and writes continue against your database as usual.
## Encryption and secrets
Ardent encrypts source database credentials, API keys, and connection secrets at rest. Credentials are read only at runtime, when Ardent needs to connect to your database.
With **[BYOC (bring your own cloud)](/architecture#deployment-options)**, secrets live in your own infrastructure (for example AWS SSM Parameter Store). Ardent reads them directly from your secret store at runtime, and your credentials never leave your network.
Every branch gets its own generated connection string. If a branch URL is ever compromised, it has zero access to your original database. This makes it safe to hand branch URLs to AI agents like Claude Code or Cursor without leaking your source database credentials.
Branches are controlled through Ardent's control plane, so you can delete or remove them at any time.
## Branch connection TLS
Branch connection URLs include `?sslmode=require&channel_binding=disable`. Use the URL exactly as Ardent returns it.
Set SQL clients and IDEs to SSL mode `require`, and leave any root certificate, CA bundle, or `sslrootcert` field blank — including your source database's certificate bundle. A source CA can't verify an Ardent branch URL, and some clients treat `require` plus a configured certificate as full verification, which causes TLS errors like a secure channel mismatch. Without a custom certificate, `sslmode=require` keeps the connection encrypted.
Keep `channel_binding=disable` in the URL. TLS ends at Ardent's routing layer, so clients that force channel binding can reject an otherwise valid branch connection. If your client exposes channel binding, set it to `disable` or `off`.
## Branch anonymization
Branches default to a full copy of your production data. When you need realistic schema and relationships but not real PII, you can run SQL on **every new branch** from this connector after it is created.
Set it with the CLI:
```bash theme={null}
ardent settings set branch_sql @./scripts/anonymize-branch.sql
```
Or paste SQL inline (large scripts are easier as a file with `@path`).
Your script might `UPDATE` columns to hashed or synthetic values, redact free-text fields, or trim tables that should never leave production. The script runs once per branch. If Ardent re-runs it for the same branch, successful runs are not repeated.
This is the same **`branch_sql`** setting documented in [CLI settings](/cli/settings). There is no separate anonymization toggle — you write the SQL, so you control exactly what changes.
Each branch is a fully independent copy. Changes on one branch don't affect production or any other branch. When you're done, delete it or let it auto-suspend. Nothing persists unless you want it to.
## Data residency
In **fully managed** mode, your data lives on Ardent's cloud infrastructure. In **[managed, your cloud](/architecture#deployment-options)** mode, the entire data plane (read replica, branches, and secrets) lives in your own cloud account. Your data never leaves your network. Ardent still manages everything via the control plane.
For teams with data residency or compliance requirements, the **managed, your cloud** deployment keeps all data and credentials inside your infrastructure while Ardent manages the service.
Contact [vikram@tryardent.com](mailto:vikram@tryardent.com) to run the data plane in your cloud.
# AI agents
Source: https://docs.tryardent.com/workflows/ai-agents
Give agents a real database to work against: isolated, disposable, cloned from production
Agents are only as good as the data they can work with. Mocked data misses edge cases. Shared staging gets corrupted. Production is off-limits.
Ardent gives every agent run its own isolated copy of your real database: spun up in under 6 seconds, handed off as a connection URL, thrown away when the task is done.
There are two ways to run it: you create the branch and hand the agent a URL, or the agent manages branches itself.
## Drive it yourself: hand the agent a URL
Your script or CI harness runs the CLI; the agent only ever sees a branch URL. The agent needs no Ardent token and can't touch anything but its branch.
```bash theme={null}
# Create a branch for the agent task and capture its URL
AGENT_DATABASE_URL="$(ardent branch create agent-task --print-url)"
if [ -z "$AGENT_DATABASE_URL" ]; then
echo "branch creation did not return a database URL" >&2
exit 1
fi
# Hand the returned URL to your agent
# → $AGENT_DATABASE_URL
# Agent writes migrations, runs queries, tests against real data
# When it's done, clean up
ardent branch delete agent-task
```
Nothing reads `AGENT_DATABASE_URL` automatically — pass the URL in the agent's prompt, or export it as `DATABASE_URL` if your app reads that.
## Let the agent drive: set it up once
The agent runs the Ardent CLI itself — creating, using, and deleting its own branches:
For a single task, paste this into Claude Code or Cursor:
```
I'm working on [describe task]. Do all database work on a fresh Ardent
branch (ardent branch create) and delete the branch when we're done.
Never touch the main connection string.
```
If your agents touch the database every day, install this as a skill instead of pasting per task. Install into your project (or into `~/.claude/skills/` to use it everywhere) and Claude Code loads it automatically whenever a task involves Ardent:
```bash theme={null}
mkdir -p .claude/skills/ardent-cli
tmp_skill="$(mktemp)"
if curl -fsS https://docs.tryardent.com/skills/ardent-cli-skill.txt -o "$tmp_skill"; then
mv "$tmp_skill" .claude/skills/ardent-cli/SKILL.md
else
rm -f "$tmp_skill"
exit 1
fi
```
The skill keeps itself current: each time it runs, it fetches the latest version of itself from these docs and follows that copy. If the fetch fails (offline, blocked network), it falls back to the directions already installed.
To pin the installed copy and skip the self-update, set `SKIP_ARDENT_SKILL_SYNC=1` in the shell that launches your agent (for Claude Code: export it before running `claude`, or set it in `.claude/settings.json` under `env`). A Python venv does not carry it.
Skills load when the task looks relevant — so a request that never mentions the database might skip it. To guarantee it's used, add this to your project's `CLAUDE.md`, which Claude Code reads every session:
```
Before touching the database in this repo — Postgres in any form, including
Supabase, RDS, PlanetScale, and self-hosted — for migrations, schema changes,
seeds, or queries against real data, use the ardent-cli skill and work on an
Ardent branch. Never use the main connection string unless the user
explicitly asks to change the real database.
```
This is what it installs:
```markdown theme={null}
---
name: ardent-cli
description: Use whenever a task touches a Postgres database in any
form — Supabase, RDS, PlanetScale, self-hosted — in a project that
uses Ardent: migrations, schema changes, queries against real data,
destructive tests, or any ardent CLI usage. Works on an isolated
branch and fetches the current command and API reference from
docs.tryardent.com before acting.
---
# Stay in sync
Before anything else, run exactly this command (the pin check is built
into it — do not fetch any other way):
[ "$SKIP_ARDENT_SKILL_SYNC" = "1" ] || curl -sf https://docs.tryardent.com/skills/ardent-cli-skill.txt
- If it prints a skill, that copy is authoritative: follow it instead
of this file, and save it over this SKILL.md so the local copy stays
current.
- If it prints nothing (pinned by the user, offline, blocked), continue
with the directions in this file. Do not retry or fetch any other way.
# Before doing anything
Never rely on memorized Ardent commands or arguments — fetch the
current reference for the task first:
1. Discover pages: curl -s https://docs.tryardent.com/llms.txt
2. Fetch the relevant page as markdown, for example:
curl -s https://docs.tryardent.com/cli/branches.md
3. For API request/response shapes, read the OpenAPI spec:
curl -s https://docs.tryardent.com/openapi.public.json
4. Cross-check arguments against the installed binary:
ardent --help
If the docs and --help disagree, trust --help for arguments.
# Rules
- Work on a branch URL by default. Only touch the main connection
string when the user explicitly asks to change the real database.
- Branch URLs contain credentials. Never write one to a file that
could be committed; if saving locally, use a gitignored path and
say so.
- Wrap connection strings in single quotes in shells.
- Branch lifecycle: create -> use the returned URL -> delete when done.
- Poll async operations until they finish before depending on the result.
```
The skill deliberately contains no command arguments. It tells the agent to fetch the current reference from these docs before acting, so when the CLI or API changes, the docs update and every installed copy of the skill stays correct without being touched.
Real data, isolated per run, no production access.
# CI / CD
Source: https://docs.tryardent.com/workflows/ci-cd
A fresh database for every PR. Isolated, production-like, torn down after merge.
Every PR runs against its own fresh database branch cloned from your connected source — no shared staging that drifts, no test runs corrupting each other, no production credentials in CI.
## Prerequisites
You need:
* An Ardent account with one ready connector. If you do not have one yet, follow the [Quickstart](/quickstart).
* An API token stored as a CI secret named `ARDENT_TOKEN`.
* The project and connector names you want CI to use.
Find project and connector names locally:
```bash theme={null}
ardent project list
ardent connector list
```
## GitHub Actions workflow
Copy this to `.github/workflows/ardent-branch.yml`, replacing `my-project`, `my-connector`, and `npm test` for your repo.
```yaml theme={null}
name: Test with Ardent branch
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
test:
runs-on: ubuntu-latest
env:
ARDENT_TOKEN: ${{ secrets.ARDENT_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: npm ci
- name: Install Ardent CLI
run: npm install -g ardent-cli
- name: Select project and connector
run: |
ardent project switch my-project
ardent connector switch my-connector
- name: Create database branch
run: |
BRANCH_NAME="pr-${{ github.event.pull_request.number }}-${{ github.run_id }}"
DATABASE_URL="$(ardent branch create "$BRANCH_NAME" --print-url)"
if [ -z "$DATABASE_URL" ]; then
echo "Ardent did not return a database URL" >&2
exit 1
fi
echo "DATABASE_URL=$DATABASE_URL" >> "$GITHUB_ENV"
echo "ARDENT_BRANCH=$BRANCH_NAME" >> "$GITHUB_ENV"
- name: Run tests
run: npm test
- name: Delete database branch
if: always() && env.ARDENT_BRANCH != ''
run: ardent branch delete "$ARDENT_BRANCH"
```
## Why this shape works
* `ARDENT_TOKEN` authenticates the CLI with no login step or stored session.
* `ardent project switch` and `ardent connector switch` make the workflow predictable.
* `ardent branch create --print-url` prints only the URL, so there's no human output to parse.
* The branch name includes both PR number and run ID so re-runs and concurrent jobs do not collide.
* Cleanup runs with `if: always()` so failed tests still delete the branch.
## Other CI providers
The steps are the same on any CI provider:
```bash theme={null}
npm install -g ardent-cli
ardent project switch my-project
ardent connector switch my-connector
export DATABASE_URL="$(ardent branch create "$CI_BRANCH_NAME" --print-url)"
# run tests
ardent branch delete "$CI_BRANCH_NAME"
```
Store `ARDENT_TOKEN` in your CI provider's secret manager. Do not commit tokens to the repository.
## JSON variant
If your CI step wants more than the URL, use JSON output:
```bash theme={null}
BRANCH_JSON="$(ardent branch create "$BRANCH_NAME" --format json)"
DATABASE_URL="$(node -e 'console.log(JSON.parse(process.argv[1]).branch_url)' "$BRANCH_JSON")"
```
## Compared to shared staging
* **Full data, not schema-only:** branches carry your real rows, not empty tables.
* **Isolation:** each PR gets its own database.
* **Fast branch creation:** once a connector is ready, branch creation is independent of source database size.
* **Clean teardown:** branches can be deleted after each run and auto-suspend when idle.
Running destructive test suites rather than PR checks? See [Regression testing](/workflows/regression-testing).
# Local development
Source: https://docs.tryardent.com/workflows/local-dev
Branch before you touch the database. Every time.
A branch takes 6 seconds, and on it you can run any migration, drop any table, try any index. If it breaks, delete the branch and start over. Your production data is never at risk.
## How it works
```bash theme={null}
# Starting a new feature
ardent branch create my-feature
# Work, run migrations, break things freely
BRANCH_DATABASE_URL="$(ardent branch info --print-url)"
if [ -z "$BRANCH_DATABASE_URL" ]; then
echo "branch info did not return a database URL" >&2
exit 1
fi
psql "$BRANCH_DATABASE_URL"
# Done, or it all went wrong. Either way:
ardent branch delete my-feature
```
## Working across multiple tasks
```bash theme={null}
ardent branch list
# * ● my-feature
# ○ auth-refactor
# ○ index-experiment
ardent branch switch auth-refactor
```
Branches auto-suspend after 5 minutes of inactivity. Leave them running and they won't cost you anything while idle.
# Regression testing
Source: https://docs.tryardent.com/workflows/regression-testing
Run destructive tests against real production data without touching production
Unit tests with mocked data miss the bugs that only appear with real data at real scale. Ardent lets you run your full test suite, including destructive operations, against an exact copy of production.
## How it works
[`ardent branch create`](/cli/branches) prints a Postgres URL for the new branch. If you need it again, run `ardent branch info regression-run` (or `ardent branch info` when that branch is already checked out).
The examples below assume your tests read `DATABASE_URL` (Django, SQLAlchemy, Prisma, and most pytest setups do). If yours uses a different variable or a `.env` file, substitute that.
```bash theme={null}
# Branch from production and export the returned URL
DATABASE_URL="$(ardent branch create regression-run --print-url)"
if [ -z "$DATABASE_URL" ]; then
echo "branch creation did not return a database URL" >&2
exit 1
fi
export DATABASE_URL
npm test
# or: pytest (if your tests read DATABASE_URL; some stacks use a different env var)
# Tear it down: production was never touched
ardent branch delete regression-run
```
**One-liner:** `DATABASE_URL='…' npm test` sets the variable for just that command. `export` is easier when you'll run more than one.
**In CI:** create the branch in the job, assign the URL to `DATABASE_URL` (or wire it into your framework), run your test command, then delete the branch. See [CI / CD](/workflows/ci-cd) for a GitHub Actions–shaped example.
## What this unlocks
* **Migration safety:** run `ALTER TABLE`, `DROP COLUMN`, schema changes against real data before you ship
* **Destructive queries:** `DELETE`, `TRUNCATE`, bulk updates; see how much damage they would do — safely
* **Index validation:** try a new index and see if it actually speeds up your real queries
* **Data-dependent bugs:** catch edge cases that only exist in production data, not fixtures
## Hand it to an AI agent
With the [ardent-cli skill](/workflows/ai-agents#let-the-agent-drive-set-it-up-once) installed, a short ask is enough:
```
Run a regression test on [describe change] against a real copy of our database.
```
Without the skill, paste the full directions:
```
Run a regression test on [describe change] against a real copy of our database.
Create an Ardent branch (ardent branch create), run the tests against it,
report what broke, then delete the branch.
```