STACK
MENU
DOCS / API REFERENCE / CREDENTIALS

Credentials API

The Credentials API returns a connected service's credential to an authorized backend. STACK stores customer credentials with AWS KMS envelope encryption and decrypts them in memory when an authorized retrieval or proxy request needs them. A direct retrieval writes a credential.retrieve entry to the operator's audit chain.

Credentials are sensitive secrets (API keys, OAuth tokens, passwords). They are returned in plaintext over TLS. Ensure your application handles them securely - do not log them, store them in plaintext, or expose them to untrusted contexts.

These endpoints accept an operator API key or a user OAuth access token. Operator keys are account-wide. A member's OAuth token remains subject to that member's allowed_connections restrictions.

Get Credential by Provider

GET /v1/credentials/:provider

Retrieve the decrypted credential for a connected service, identified by its provider key (e.g., slack, openai,custom_internal_crm). If you have multiple connections to the same provider, use the /by-connection/:connectionId endpoint instead.

Request Example

bash
curl https://api.getstack.run/v1/credentials/openai \
  -H "Authorization: Bearer sk_live_op_abc123"

Response - 200 OK (Single-field credential)

For services connected with a single API key or token, the response includes a credential string field:

json
{
  "provider": "openai",
  "credential": "YOUR_PROVIDER_API_KEY"
}

Response - 200 OK (Multi-field credential)

For services connected with multiple credential fields (e.g., custom services with username + password, or host + port + key), the response includes a credentials object:

json
{
  "provider": "custom_sftp_server",
  "credentials": {
    "host": "sftp.example.com",
    "username": "stack-agent",
    "password": "s3cur3p4ss",
    "port": "22"
  }
}

Response - 200 OK (OAuth credential)

For OAuth-connected services, the credential is the access token granted during the OAuth flow:

json
{
  "provider": "slack",
  "credential": "xoxb-1234567890-1234567890123-abc123def456ghi789jkl012"
}

Error Responses

  • 401 Unauthorized - Missing or invalid bearer token.
  • 403 Forbidden - The member does not have access to this provider.
  • 404 Not Found - No connection exists for this provider under your account.
  • 409 Conflict - Multiple connections exist for this provider. Use /by-connection/:connectionId instead.

Get Credential by Connection ID

GET /v1/credentials/by-connection/:connectionId

Retrieve a decrypted credential by connection ID. This is the recommended approach when an operator has multiple connections to the same provider (e.g., two separate Slack workspaces or multiple AWS accounts).

Request Example

bash
curl https://api.getstack.run/v1/credentials/by-connection/conn_8mR4xK9wN2 \
  -H "Authorization: Bearer sk_live_op_abc123"

Response - 200 OK

json
{
  "provider": "slack",
  "connection_id": "conn_8mR4xK9wN2",
  "credential": "xoxb-1234567890-1234567890123-abc123def456ghi789jkl012"
}

Error Responses

  • 401 Unauthorized - Missing or invalid bearer token.
  • 403 Forbidden - The member does not have access to this connection.
  • 404 Not Found - Connection ID does not exist or belongs to a different operator.

Member Restrictions

A member can have an allowed_connections list that restricts which service credentials their session can access. Requests outside that list return403 Forbidden.

Example: Restricted member session

bash
# The signed-in member is limited to two connection IDs
curl https://api.getstack.run/v1/credentials/openai \
  -H "Authorization: Bearer $STACK_OAUTH_TOKEN"

Response - 403 Forbidden

json
{
  "error": {
    "code": "FORBIDDEN",
    "message": "You do not have access to this service connection"
  }
}

Operators and admins can manage member access restrictions via the PATCH /v1/team/members/:id endpoint or through the dashboard team settings.

Security Model

The Credentials API implements multiple layers of security to protect stored secrets:

Encryption at Rest

  • All credentials are encrypted using AWS KMS envelope encryption before storage.
  • A unique data encryption key (DEK) is generated per credential.
  • The DEK is encrypted with a KMS master key and stored alongside the ciphertext.
  • Plaintext DEKs are never persisted - they exist only in memory during encrypt/decrypt operations.

Encryption in Transit

  • All API communication is over TLS 1.2+.
  • Direct retrieval returns the decrypted credential in the response body.
  • Customer credentials are not cached in decrypted form on the server.

Audit Trail

Every successful direct retrieval records the action, outcome, operator, request trace, duration and hash-chain links. When the request carries an agent or Passport context, the same row includes those identifiers.

json
{
  "entry_id": "aud_abc123",
  "operator_id": "op_abc123",
  "agent_id": "system",
  "passport_jti": "none",
  "layer": "vault",
  "action": "credential.retrieve",
  "outcome": "success",
  "prev_entry_hash": "...",
  "entry_hash": "..."
}

The audit log is hash-chained - each entry includes a hash of the previous entry, making tampering detectable. Audit entries cannot be updated or deleted.

Common Usage Patterns

Trusted Backend: Retrieve and Use

Use direct retrieval only in a trusted backend that is allowed to handle the upstream secret. An agent that should not see the credential must use the proxy instead.

javascript
// Trusted backend retrieves the credential on demand
const response = await fetch('https://api.getstack.run/v1/credentials/openai', {
  headers: { 'Authorization': 'Bearer ' + process.env.STACK_API_KEY }
});
const { credential } = await response.json();

// Use it immediately
const completion = await fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${credential}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    model: 'gpt-4',
    messages: [{ role: 'user', content: 'Hello' }]
  })
});

// Do not log or persist the credential.

MCP credential use

The public MCP surface does not return decrypted credentials or accept custom secrets. Connect providers in the Console or provider browser flow. Use the STACK proxy when an MCP client needs to call a connected service.

Direct credential retrieval remains an API capability for explicitly configured runtimes. Proxy-only agents cannot use it.

stack | Docs