Skip to Content
API ReferenceConnections

Connections

A connection records that one site may use one service — a database, a storage bucket or an auth app — at one level. It drives the variables the site’s containers receive, its managed platform key and, for a database, the network policy. See the Connections guide for the concept.

All endpoints require Authorization: Bearer TOKEN. :id is a project id or slug; :siteId is a site uuid of that project.

These routes take an account token (gh_…) or a dashboard session. A project API key (gsk_…) answers 403: a site’s managed key is minted from its own connections, so a key able to change them could widen what it may do.

Kinds and Levels

Every kind takes two levels, listed here weakest first.

kindlevelGrants
databaseread-onlyThe connection variables and the network path, with a credential that may only read
databaseconnectThe connection variables and the network path
bucketreadThe bucket’s S3 credentials, with a key that may only GET and LIST
bucketread-writeThe bucket’s S3 credentials
auth_appclientThe app id, both endpoints and the server key
auth_appadminThe above, plus auth on the site’s managed GHAYMA_API_KEY, narrowed to that app

Omit level on a write and the kind’s default is used — connect for a database, read-write for a bucket, client for an auth_app. A level the kind does not accept is a 400. Reads need the project read role; every mutation needs write.

Connecting a site to a service also creates a credential for that connection alone: a database role or MongoDB user named c_ followed by eight characters, or an S3 access key scoped to that one bucket. The site’s DATABASE_URL, MONGODB_URI and STORAGE_ACCESS_KEY/STORAGE_SECRET_KEY carry it instead of the service’s own credential, and disconnecting deletes that credential and nothing else. Standalone MongoDB is the exception — it still hands out the shared user. See Each app gets its own database credential and Each app gets its own storage key.

The level does not change env_names or the network path; it changes only what the credential may do. See Read-only connections.

Connection Object

Every endpoint returns this shape:

{ "site_id": "b0a1…", "site_slug": "admin", "kind": "database", "resource_id": "9f3c…", "resource_name": "conn-db", "level": "connect", "env_names": ["DATABASE_URL", "DATABASE_URL_CONN_DB"], "created_at": "2026-09-08T10:00:00Z" }

resource_name is the database’s or bucket’s name, or the auth app’s app_id.

env_names

env_names holds the exact variable names the platform injects into that site for that connection — names only, never values. Read the values with ghayma env pull.

Connectionenv_names
PostgreSQL database conn-db, the site’s only databaseDATABASE_URL, DATABASE_URL_CONN_DB
Bucket conn-filesthe five STORAGE_* names, and the same five as STORAGE_CONN_FILES_*
Auth app azeghjESPACETECH_AUTH_APP_ID, ESPACETECH_AUTH_URL, ESPACETECH_AUTH_PUBLIC_URL, GHAYMA_AUTH_APP_ID, ESPACETECH_AUTH_SERVER_KEY, and the four ESPACETECH_AUTH_* names again suffixed _AZEGHJ

The suffix is the service’s name upper-cased, with every character that is not AZ or 09 replaced by _. The first service of each kind the site was connected to also holds the plain, unsuffixed names; every service holds the suffixed ones. The full derivation is in Automatically injected variables.

List Project Connections

GET /api/v1/projects/:id/connections
QueryDescription
site_idOnly this site’s connections
kindOnly database, bucket or auth_app
resource_idOnly connections to this resource — with kind, this is “which apps use this service”

Response: 200

{ "connections": [ { "site_id": "b0a1…", "site_slug": "main", "kind": "database", "resource_id": "9f3c…", "resource_name": "conn-db", "level": "connect", "env_names": ["DATABASE_URL", "DATABASE_URL_CONN_DB"], "created_at": "2026-09-08T10:00:00Z" } ] }

A project with no connections returns { "connections": [] }.

List a Site’s Connections

GET /api/v1/projects/:id/sites/:siteId/connections

Returns what the site is connected to and what else it could be connected to, so a UI needs one call.

Response: 200

{ "connections": [ { "site_id": "b0a1…", "site_slug": "admin", "kind": "auth_app", "resource_id": "7d21…", "resource_name": "main-auth", "level": "client", "env_names": ["ESPACETECH_AUTH_APP_ID", "ESPACETECH_AUTH_URL", "…"], "created_at": "2026-09-08T10:00:00Z" } ], "available": [ { "kind": "bucket", "resource_id": "4e88…", "resource_name": "conn-files", "levels": ["read", "read-write"], "env_names": [ "STORAGE_ENDPOINT", "STORAGE_ACCESS_KEY", "STORAGE_SECRET_KEY", "STORAGE_BUCKET", "STORAGE_REGION", "STORAGE_CONN_FILES_ENDPOINT", "STORAGE_CONN_FILES_ACCESS_KEY", "STORAGE_CONN_FILES_SECRET_KEY", "STORAGE_CONN_FILES_BUCKET", "STORAGE_CONN_FILES_REGION" ] } ] }

available lists the project’s services this site does not hold, each with the levels it accepts, weakest first — ["read-only", "connect"] for a database, ["read", "read-write"] for a bucket, ["client", "admin"] for an auth app. A deleted database and a disabled auth app never appear. An available entry carries env_names too — there it is what connecting the service would add.

Replace a Site’s Connections

PUT /api/v1/projects/:id/sites/:siteId/connections

Body — the full desired set (an empty array disconnects everything):

{ "connections": [ { "kind": "database", "resource_id": "9f3c…", "level": "connect" }, { "kind": "auth_app", "resource_id": "7d21…", "level": "admin" } ] }

Response: 200 — the site’s connections after the write, in the connection object shape. Only what actually moved is re-derived, so saving an unchanged set restarts nothing.

Connect a Service

POST /api/v1/projects/:id/sites/:siteId/connections

Connects one service, or changes the level of a connection that already exists.

Body:

{ "kind": "auth_app", "resource_id": "7d21…", "level": "admin" }

Response: 201 — the stored connection object.

Disconnect a Service

DELETE /api/v1/projects/:id/sites/:siteId/connections/:kind/:resourceId

Response: 200

{ "removed": true }

removed is false when there was no such connection. Disconnecting twice is not an error, so a retry after a dropped response is safe.

Rotate a Connection’s Credential

POST /api/v1/projects/:id/sites/:siteId/connections/:kind/:resourceId/rotate

Gives that one connection’s credential a new secret. Nothing else moves: not the level, not the env_names, not another site’s credential, and never the service’s own shared credential. kind is database or bucket — an auth_app has no per-connection credential.

Response: 204 — no body. The new secret is never returned; it reaches the app through the live patch this fires and through the next deploy’s derivation. See Rotating Credentials.

Beyond the errors below, this route answers 409 when the connection is served by the service’s shared credential (a standalone MongoDB, or a connection made while the database was stopped) — retrying cannot help, so rotate that credential on the service itself — and 503 when the engine could not be reached, in which case nothing changed and the same call works later.

Errors

CodeWhen
400Unknown kind, a level that kind does not accept, or a resource that belongs to another project
403The caller’s project role is too low — or the caller is a project API key
404No such project, :siteId is not a site of it, :siteId is not a uuid, or — on a rotate — the service is not connected to that site
409Rotate only: the connection is served by the service’s shared credential, so it has none of its own to rotate
503POST, PUT and rotate: the service could not prepare or replace the site’s own credential — a database that was unreachable at that moment, for example. Retrying later works

The per-service routes GET/PUT /databases/:id/sites and GET/PUT /storage/:id/sites keep working. They read and write these same connections, one service at a time.