Files
sentryagent-idp/docs/developers/guides/manage-credentials.md
SentryAgent.ai Developer 61ea975c79 docs: bedroom developer documentation — complete docs/developers/ set
Adds the full bedroom-developer-docs OpenSpec change implementation:

- docs/developers/README.md — index page
- docs/developers/quick-start.md — bootstrap to working token in 7 steps
- docs/developers/concepts.md — AgentIdP, AGNTCY, lifecycle, OAuth 2.0, free tier
- docs/developers/guides/README.md — guide index
- docs/developers/guides/register-an-agent.md — all fields, validation, common errors
- docs/developers/guides/manage-credentials.md — generate, list, rotate, revoke
- docs/developers/guides/issue-and-revoke-tokens.md — OAuth 2.0 flow, introspect, revoke
- docs/developers/guides/query-audit-logs.md — filters, pagination, 90-day retention
- docs/developers/api-reference.md — all 14 endpoints, all error codes, curl examples

Also commits deferred OpenSpec housekeeping from previous session:
- Archives phase-1-mvp-implementation change to openspec/changes/archive/
- Adds bedroom-developer-docs change artifacts (30/30 tasks complete)
- Syncs 4 delta specs to openspec/specs/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:13:03 +00:00

5.2 KiB

Manage Credentials

A credential is a client_id + client_secret pair that your agent uses to get access tokens. This guide covers all four credential operations.

All credential endpoints are under /api/v1/agents/{agentId}/credentials and require a Bearer token with agents:write scope.


Generate credentials

POST /api/v1/agents/{agentId}/credentials

Creates a new credential for the agent. The clientSecret is returned once only.

curl -s -X POST "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}' | jq .

To set an expiry date (optional):

curl -s -X POST "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "expiresAt": "2027-03-28T00:00:00.000Z" }' | jq .

Response (201 Created):

{
  "credentialId": "c9d8e7f6-a5b4-3210-fedc-ba9876543210",
  "clientId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "clientSecret": "sk_live_7f3a2b1c9d8e4f0a6b5c3d2e1f0a9b8c",
  "status": "active",
  "createdAt": "2026-03-28T09:00:00.000Z",
  "expiresAt": "2027-03-28T00:00:00.000Z",
  "revokedAt": null
}

Save the clientSecret immediately. It is shown once. The server stores a bcrypt hash and cannot recover the plaintext. If you lose it, rotate the credential to get a new one.

An agent can hold multiple active credentials at the same time. This supports zero-downtime rotation: generate a new credential, update all consumers to use it, then revoke the old one.

Restrictions:

  • The agent must be in active status. Suspended and decommissioned agents cannot generate credentials.

List credentials

GET /api/v1/agents/{agentId}/credentials

Returns all credentials for the agent (both active and revoked). The clientSecret is never returned in list responses.

curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials" \
  -H "Authorization: Bearer $TOKEN" | jq .

Response:

{
  "data": [
    {
      "credentialId": "c9d8e7f6-a5b4-3210-fedc-ba9876543210",
      "clientId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "status": "active",
      "createdAt": "2026-03-28T09:00:00.000Z",
      "expiresAt": "2027-03-28T00:00:00.000Z",
      "revokedAt": null
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 20
}

Pagination

curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials?page=1&limit=50" \
  -H "Authorization: Bearer $TOKEN" | jq .

Filter by status

# Active credentials only
curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials?status=active" \
  -H "Authorization: Bearer $TOKEN" | jq .

# Revoked credentials only
curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials?status=revoked" \
  -H "Authorization: Bearer $TOKEN" | jq .

Rotate a credential

POST /api/v1/agents/{agentId}/credentials/{credentialId}/rotate

Rotation immediately invalidates the current clientSecret and generates a new one — the credentialId stays the same. Use this for periodic secret rotation or emergency rotation if a secret is compromised.

curl -s -X POST \
  "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials/$CREDENTIAL_ID/rotate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}' | jq .

Response (200 OK):

{
  "credentialId": "c9d8e7f6-a5b4-3210-fedc-ba9876543210",
  "clientId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "clientSecret": "sk_live_9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d",
  "status": "active",
  "createdAt": "2026-03-28T09:00:00.000Z",
  "expiresAt": null,
  "revokedAt": null
}

What changes after rotation:

  • The clientSecret is a new value — the old secret is immediately invalid
  • The credentialId is the same — no changes needed to references by ID
  • Any tokens issued using the old secret remain valid until they expire naturally (tokens are not revoked by credential rotation)

What cannot be rotated: A revoked credential cannot be rotated. Generate a new credential instead.


Revoke a credential

DELETE /api/v1/agents/{agentId}/credentials/{credentialId}

Permanently revokes a credential. The credential can no longer be used to obtain new tokens.

curl -s -X DELETE \
  "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials/$CREDENTIAL_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -o /dev/null -w "%{http_code}\n"

Successful response: 204 No Content (empty body).

Effects of revocation:

  • The credential status is set to revoked
  • The credential cannot be used to call POST /token
  • Any tokens that were issued using this credential remain valid until they expire — to immediately invalidate tokens, revoke them explicitly using POST /token/revoke
  • The credential record is retained for audit purposes
  • Revocation is irreversible — a revoked credential cannot be re-activated

Revocation vs decommission:

  • Revoking a credential affects that credential only; the agent stays active
  • Decommissioning an agent (DELETE /api/v1/agents/{agentId}) revokes all credentials simultaneously and permanently retires the agent