<!-- section: Data API · status: building · source: docs/data-api/04-connection-details.md -->

> Fetch the connection strings from your deployment pipeline instead of pasting them.


# Connection details

## GET /v1/data/_connection

**Secret key.** What comes back is a password.

```bash Request
curl "$BASE/v1/data/_connection" -H "authorization: Bearer $SK"
```

```json Response
{
  "connectionString": "postgresql://booking_owner:…@ep-…-pooler.c-5.eu-central-1.aws.neon.tech/booking?sslmode=require&channel_binding=require",
  "directConnectionString": "postgresql://booking_owner:…@ep-….c-5.eu-central-1.aws.neon.tech/booking?sslmode=require&channel_binding=require",
  "database": "booking",
  "role": "booking_owner",
  "region": "aws-eu-central-1",
  "regionName": "Frankfurt",
  "rotatedAt": null
}
```

| Field | |
|---|---|
| `connectionString` | Pooled. What your application should use |
| `directConnectionString` | Unpooled. Migrations and long-lived connections |
| `database`, `role` | What the strings connect as |
| `region` | Where it physically is |
| `rotatedAt` | When the password was last replaced, or `null` |

Requires the `data:read` scope.

A key only carries the services your project had when the key was issued, so a
key older than this service — or older than the day you turned it on — does not
have it, and this call answers `403`. Issue a new key in the studio with
**Database** ticked; rotating the old one deliberately keeps its original grant,
because rotation replaces a credential rather than widening one.

## Why this exists

So a deploy can fetch the string rather than a person copying it into a secret
store. The copying step is where credentials end up in chat messages, and it is
the step this removes.

```bash In a deploy script
DATABASE_URL=$(curl -fsS "$BASE/v1/data/_connection" \
  -H "authorization: Bearer $SK" | jq -r .connectionString)
```

## It answers to servers only

A publishable key is refused, and there are **no CORS headers on this route at
all**. A browser cannot call it even if a secret key ends up in a bundle, and
that protection does not depend on anybody reading an error message.

```json 403
{ "error": "This is a publishable key. A connection string is a credential — use a secret key (sk_…) from a server." }
```

## There is no POST

Creating a database is a studio action. See
[create a database](/docs/data-api/create) for why.

## Errors

| Status | Means |
|---|---|
| `401` | Missing or invalid key |
| `403` | Publishable key, or a key without `data:read` |
| `429` | Rate limited; see [limits](/docs/data-api/limits) |
| `503` | No database ready — never created, still provisioning, or suspended |

**One answer covers all three `503` cases.** Your move is the same for each —
look at the studio — and distinguishing them would tell an unauthenticated
caller about our internal state for no benefit to a legitimate one.
