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 aon_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
| Field | Type | Required | Description |
|---|---|---|---|
connector_id | string (uuid) | Yes | Connector these credentials are for |
credential_type | string | Yes | Credential type (see below) |
credential_data | object | Yes | Credential values; discriminated by a type field |
expires_at | string (ISO 8601) | No | Expiry timestamp |
on_behalf_of_customer_id | string (uuid) | No | Analyst-only: target customer |
on_behalf_of_reason | string (10–500) | No | Analyst-only: required with on_behalf_of_customer_id |
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:
- The raw secret is written to Doppler (the analyst never sees it on on-behalf-of uploads)
- Only a secret reference is stored on the database record
- Every create/upload is written to the credential access log
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."
}
}