Skip to main content

Create Credentials

Securely store credentials for API access.

POST /api/credentials

Authorization

  • Customer (verified): Creates credentials for themselves (owner inferred from the session).
  • Analyst / Admin: Must use the on-behalf-of fields — an analyst cannot create credentials without on_behalf_of_customer_id (and a on_behalf_of_reason).

The target connector must be in production status. A customer may hold only one active credential per connector (409 otherwise).

Request Body

FieldTypeRequiredDescription
connector_idstring (uuid)YesConnector these credentials are for
credential_typestringYesCredential type (see below)
credential_dataobjectYesCredential values; discriminated by a type field
expires_atstring (ISO 8601)NoExpiry timestamp
on_behalf_of_customer_idstring (uuid)NoAnalyst-only: target customer
on_behalf_of_reasonstring (10–500)NoAnalyst-only: required with on_behalf_of_customer_id
note

There is no top-level customer_id field. Customers own the credentials they create; analysts target a customer via on_behalf_of_customer_id.

Credential Types

oauth2, api_key, bearer, username_password, certificate, query_params, token_exchange.

Credential Data by Type

credential_data is a discriminated union keyed on type, with camelCase fields:

OAuth 2.0

{
"credential_type": "oauth2",
"credential_data": {
"type": "oauth2",
"accessToken": "xoxp-123456789...",
"refreshToken": "xoxr-987654321...",
"tokenType": "Bearer",
"expiresIn": 3600,
"scope": "channels:read"
}
}

API Key

{
"credential_type": "api_key",
"credential_data": {
"type": "api_key",
"apiKey": "sk-1234567890abcdef",
"apiKeyHeader": "X-API-Key"
}
}

Bearer Token

{
"credential_type": "bearer",
"credential_data": {
"type": "bearer",
"token": "ghp_xxxxxxxxxxxx"
}
}

Username/Password

{
"credential_type": "username_password",
"credential_data": {
"type": "username_password",
"username": "api_user",
"password": "secure_password"
}
}

Certificate

{
"credential_type": "certificate",
"credential_data": {
"type": "certificate",
"certificate": "-----BEGIN CERTIFICATE-----\n...",
"privateKey": "-----BEGIN PRIVATE KEY-----\n...",
"passphrase": "optional"
}
}

Query Params (URL-based auth, e.g. Trello)

{
"credential_type": "query_params",
"credential_data": {
"type": "query_params",
"params": [
{ "key": "key", "value": "..." },
{ "key": "token", "value": "..." }
]
}
}

Token exchange (token_exchange) credentials carry a full token-endpoint config object — see the connector test-credentials flow.

Response

Returns 201 Created:

{
"id": "cred_abc123",
"connector_id": "conn_slack001",
"credential_type": "oauth2",
"validation_status": "not_tested",
"created_at": "2024-01-15T10:30:00Z"
}

Security

Credentials are stored securely:

  1. The raw secret is written to Doppler (the analyst never sees it on on-behalf-of uploads)
  2. Only a secret reference is stored on the database record
  3. Every create/upload is written to the credential access log
caution

Credential values are never returned by the API after creation.

Example Request

curl -X POST "https://api.traces.io/api/credentials" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"connector_id": "conn_slack001",
"credential_type": "oauth2",
"credential_data": {
"type": "oauth2",
"accessToken": "xoxp-123456789...",
"refreshToken": "xoxr-987654321..."
}
}'

Error Responses

400 Bad Request

Invalid credential format:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "OAuth2 credentials require access_token"
}
}

403 Forbidden

Analyst attempting to create without on-behalf-of:

{
"error": {
"code": "FORBIDDEN",
"message": "Analysts must specify on_behalf_of_customer_id to create credentials"
}
}

409 Conflict

An active credential already exists for this connector:

{
"error": {
"code": "CONFLICT",
"message": "Credential already exists for this connector. Revoke the existing credential first."
}
}