Mint a data credential
Before a tool connects to a cell over the PostgreSQL wire or Arrow Flight SQL, it needs a per-database credential, a real identity that resolves through a directory entity to real grants, so the login carries exactly the permissions that identity has and no more. This guide mints one over the public admin API, for both a workspace and a clean room.
A data credential is distinct from an API key or OAuth token: those authenticate a program to the HTTP API; a data credential authenticates a SQL/Flight client to a database on a cell. You use the HTTP API (with an API key or OAuth token) to mint the data credential.
What you need
- A caller authenticated to the public API (see Programmatic authentication).
- The
catalog.credentials.mintpermission to issue, rotate, or revoke; orcatalog.credentials.readto list credentials and read connection endpoints. These are deliberately narrow, a credential-minting client does not need broad data-admin authority. - Membership in the target workspace or clean room, and a
database_idthat belongs to that scope. The API confines every call to the caller's own workspace or clean room: a database in another one is refused.
All paths below are under the admin base /api/public/v1/admin.
Mint a credential for a workspace
curl -sS -X POST \
https://<node>/api/public/v1/admin/workspaces/<workspace_id>/databases/<database_id>/credentials \
-H "Authorization: Bearer <api-key-or-oauth-token>" \
-H "Content-Type: application/json" \
-d '{
"permission_set": "read",
"label": "powerbi-analysts"
}'
Request fields:
| Field | Required | Meaning |
|---|---|---|
permission_set | no (default read) | The permission profile the credential carries. |
entity_id | no | The directory entity the credential acts as; defaults to the acting caller. |
label | no | A human label for later identification. |
expires_at | no | An expiry for the credential. |
The 201 response carries the one-time secret and the connection details:
{
"tenant_kind": "workspace",
"tenant_id": "<workspace_id>",
"credential": { "credential_id": "…", "secret": "…" },
"connection": {
"database_id": "<database_id>",
"username": "…",
"available": true,
"protocols": ["postgres", "flight-sql"],
"external_endpoints": [
{ "proto": "postgres", "host": "<node>", "port": 9950 }
],
"secure": true
}
}
The credential.secret is returned only in this mint response and is never
retrievable again. Store it in your secrets manager immediately. Listing
credentials (below) returns metadata only, never the secret.
The connection.username and the one-time secret are what a BI tool or SQL client
presents to the SQL or Flight surface. See
Connect a BI or reporting tool.
List, rotate, and revoke
# List (metadata only, no secrets)
curl -sS https://<node>/api/public/v1/admin/workspaces/<workspace_id>/databases/<database_id>/credentials \
-H "Authorization: Bearer <token>"
# Rotate: issues a new secret; the existing one is honored for a grace window
curl -sS -X POST \
https://<node>/api/public/v1/admin/workspaces/<workspace_id>/databases/<database_id>/credentials/<credential_id>/rotate \
-H "Authorization: Bearer <token>"
# Revoke
curl -sS -X DELETE \
https://<node>/api/public/v1/admin/workspaces/<workspace_id>/databases/<database_id>/credentials/<credential_id> \
-H "Authorization: Bearer <token>"
To read the reachable connection endpoints for a database without minting:
curl -sS https://<node>/api/public/v1/admin/workspaces/<workspace_id>/databases/<database_id>/endpoints \
-H "Authorization: Bearer <token>"
Clean rooms
A clean room mints credentials through the same shapes, under
/api/public/v1/admin/cleanrooms/<cleanroom_id>/…:
| Operation | Method + path |
|---|---|
| Issue | POST …/cleanrooms/{cleanroom_id}/databases/{database_id}/credentials |
| List | GET …/cleanrooms/{cleanroom_id}/databases/{database_id}/credentials |
| Rotate | POST …/cleanrooms/{cleanroom_id}/databases/{database_id}/credentials/{credential_id}/rotate |
| Revoke | DELETE …/cleanrooms/{cleanroom_id}/databases/{database_id}/credentials/{credential_id} |
| Endpoints | GET …/cleanrooms/{cleanroom_id}/databases/{database_id}/endpoints |
The same scope confinement applies: the caller must be a clean-room member and the
database_id must belong to that clean room.
Where the credential is valid
Figure: a minted data credential is presented to the cell's data surfaces. The SQL wire and Arrow Flight SQL are Live; the Iceberg REST catalog serves catalog reads only (Partial). Delta Sharing is served, but it does not take a minted data credential (amber, dotted): its recipients carry their own bearer token, issued on the sharing admin surface.
Table equivalent
| Surface | What the credential can do | Status |
|---|---|---|
PostgreSQL wire (:9950) | Full governed reads/writes for BI and SQL tools | Live |
Arrow Flight SQL (:59950) | High-throughput columnar pulls | Live |
| Iceberg REST catalog | Catalog reads (list/load); writes are in-memory, non-persistent | Partial |
| External Delta Sharing protocol | Nothing. Delta Sharing recipients are created on the sharing admin surface and present their own bearer recipient token, with grants made per share | Different credential: see connect a BI tool |
A data credential replicates across the org (mint once, use anywhere in the node), and the endpoint returned is always the node's external gateway. The mint API is a thin front door over the cell's data plane, it adds no store of its own, and it never returns a stored secret after the mint.
Verify it worked
| # | Check | How |
|---|---|---|
| 1 | The credential authenticates | A connection succeeds and resolves to a directory entity, not to an anonymous session |
| 2 | It carries the right authority | A query returns exactly what that entity is entitled to, with row filters and column masks applied |
| 3 | It is attributable | Activity shows against the entity, so the credential is not a shared identity in disguise |
| 4 | It is revocable | Revoking it ends access on the next use rather than at expiry |
Step 2 is the point of the whole exercise. A credential that authenticates but carries more authority than intended is worse than one that does not work, because nothing looks wrong.
When it does not work
| Symptom | Most likely cause | What to do |
|---|---|---|
| Authentication is refused | The credential is revoked, expired, or for another entity | Credentials resolve to an entity. Check which one you minted it for |
| It connects but returns nothing | Policy filtered everything, or no catalog is bound | An empty result under policy is not an error. Check the entity's grants first |
| A table is missing entirely | Enumeration is need-to-know filtered | The surface will not advertise a table you could not query |
| It works from one place and not another | The surface is not enabled, or the posture blocks it | Not every access method is on in every deployment |
| Rotation broke a running client | The old credential was revoked before the client picked up the new one | Rotate by issuing first and revoking after the switch |
Related topics
- Connect a BI or reporting tool, present the credential to the SQL and Flight surfaces.
- Programmatic authentication, the API key or OAuth token used to call the mint API.
- The entity model, the identity a data credential resolves to.