Skip to content

Credentials & OAuth

Connector instances need credentials before they can communicate with external services. Chainabit supports multiple credential types and handles OAuth token exchange and refresh automatically.

Credentials are encrypted at rest and never returned in API responses. Use the status endpoint to check whether credentials are stored.

Endpoints

MethodPathDescriptionAuth
POST/connectors/instances/:id/credentialsStore credentialsJWT
GET/connectors/instances/:id/credentials/statusGet credential statusJWT
DELETE/connectors/instances/:id/credentialsDelete credentialsJWT
POST/connectors/instances/:id/oauth/initiateInitiate OAuth flowJWT
GET/connectors/oauth/:connectorKey/callbackOAuth callback (automated)

Credential Types

credTypeUse caseRequired fields in credentials
oauth2OAuth 2.0 tokens (managed automatically)access_token, optionally refresh_token
api_keyAPI key authenticationapiKey
basic_authUsername + passwordusername, password
bearer_tokenStatic bearer tokentoken
connection_stringDatabase connection stringconnectionString
customAny other credential shapeAny key-value pairs

POST /connectors/instances/:id/credentials

Store or replace authentication credentials for a connector instance. Credentials are encrypted before storage and are not retrievable in plain text.

After storing credentials, test the connection to confirm they are valid.

Request

Path Parameters

ParameterTypeDescription
idstringInstance ID

Request Body

FieldTypeRequiredDescription
credTypestringYesCredential type: oauth2, api_key, basic_auth, bearer_token, connection_string, or custom
credentialsobjectYesKey-value credential pairs (see Credential Types above)
scopesstring[]NoOAuth scopes granted (if applicable)

Response

Response Example

json
{
  "data": {
    "id": "cred_xyz456",
    "instanceId": "inst_abc123",
    "credType": "api_key",
    "rotatedAt": "2026-03-23T10:00:00Z"
  }
}

Code Examples

bash
curl -X POST https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/credentials \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "credType": "api_key",
    "credentials": { "apiKey": "sk-your-api-key" }
  }'
bash
curl -X POST https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/credentials \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "credType": "connection_string",
    "credentials": { "connectionString": "postgresql://user:pass@host:5432/db" }
  }'
bash
curl -X POST https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/credentials \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "credType": "bearer_token",
    "credentials": { "token": "eyJ..." }
  }'
javascript
const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const INSTANCE_ID = process.env.INSTANCE_ID;

const response = await fetch(
  `${BASE_URL}/connectors/instances/${INSTANCE_ID}/credentials`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      credType: "api_key",
      credentials: { apiKey: "sk-your-api-key" },
    }),
  },
);
const { data } = await response.json();
python
import requests, os

BASE_URL = os.environ["BASE_URL"]
TOKEN = os.environ["TOKEN"]
INSTANCE_ID = os.environ["INSTANCE_ID"]

response = requests.post(
    f"{BASE_URL}/connectors/instances/{INSTANCE_ID}/credentials",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "credType": "api_key",
        "credentials": {"apiKey": "sk-your-api-key"},
    },
)
data = response.json()["data"]

GET /connectors/instances/:id/credentials/status

Check whether credentials are stored for an instance and inspect metadata such as expiry and scopes. The actual credential values are never returned.

Request

Path Parameters

ParameterTypeDescription
idstringInstance ID

Response

Response Example

json
{
  "data": {
    "configured": true,
    "credType": "oauth2",
    "expiresAt": "2026-04-23T10:00:00Z",
    "scopes": ["channels:read", "chat:write"]
  }
}

Response Fields

FieldTypeDescription
configuredbooleantrue if credentials are stored
credTypestring | nullThe stored credential type
expiresAtstring | nullISO 8601 expiry timestamp for OAuth tokens; null for non-expiring credentials
scopesstring[] | nullGranted OAuth scopes; null for non-OAuth credentials

Code Examples

bash
curl https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/credentials/status \
  -H "Authorization: Bearer $TOKEN"
javascript
const response = await fetch(
  `${BASE_URL}/connectors/instances/${INSTANCE_ID}/credentials/status`,
  {
    headers: { Authorization: `Bearer ${TOKEN}` },
  },
);
const { data } = await response.json();
python
response = requests.get(
    f"{BASE_URL}/connectors/instances/{INSTANCE_ID}/credentials/status",
    headers={"Authorization": f"Bearer {TOKEN}"},
)
data = response.json()["data"]

DELETE /connectors/instances/:id/credentials

Remove stored credentials for a connector instance. After deletion, the instance status changes to pending_auth.

Request

Path Parameters

ParameterTypeDescription
idstringInstance ID

Response

Response Example

json
{
  "data": {
    "id": "cred_xyz456",
    "instanceId": "inst_abc123",
    "credType": "oauth2",
    "rotatedAt": "2026-03-23T10:00:00Z"
  }
}

Code Examples

bash
curl -X DELETE https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/credentials \
  -H "Authorization: Bearer $TOKEN"
javascript
const response = await fetch(
  `${BASE_URL}/connectors/instances/${INSTANCE_ID}/credentials`,
  {
    method: "DELETE",
    headers: { Authorization: `Bearer ${TOKEN}` },
  },
);
const { data } = await response.json();
python
response = requests.delete(
    f"{BASE_URL}/connectors/instances/{INSTANCE_ID}/credentials",
    headers={"Authorization": f"Bearer {TOKEN}"},
)
data = response.json()["data"]

POST /connectors/instances/:id/oauth/initiate

Start an OAuth 2.0 authorization flow for a connector instance. Returns a redirect URL to send the user to on the external service.

After the user approves access, the external service redirects back to Chainabit's callback URL. The callback endpoint is public, but it is authorized by a single-use state value with a short TTL. Chainabit exchanges the authorization code for tokens, stores them encrypted, and marks the instance as active.

Request

Path Parameters

ParameterTypeDescription
idstringInstance ID

Response

Response Example

json
{
  "data": {
    "redirectUrl": "https://slack.com/oauth/v2/authorize?client_id=...&scope=...&state=abc123",
    "state": "abc123"
  }
}

Response Fields

FieldTypeDescription
redirectUrlstringThe authorization URL to open in a browser
statestringCSRF state token (used internally to validate the callback)

Open redirectUrl in the user's browser. Do not call the callback endpoint directly - it is invoked automatically by the external service.

Code Examples

bash
curl -X POST https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/oauth/initiate \
  -H "Authorization: Bearer $TOKEN"
javascript
const response = await fetch(
  `${BASE_URL}/connectors/instances/${INSTANCE_ID}/oauth/initiate`,
  {
    method: "POST",
    headers: { Authorization: `Bearer ${TOKEN}` },
  },
);
const { data } = await response.json();
// Redirect the user to data.redirectUrl
window.location.href = data.redirectUrl;
python
response = requests.post(
    f"{BASE_URL}/connectors/instances/{INSTANCE_ID}/oauth/initiate",
    headers={"Authorization": f"Bearer {TOKEN}"},
)
data = response.json()["data"]
print("Open this URL in your browser:", data["redirectUrl"])

GET /connectors/oauth/:connectorKey/callback

This endpoint is called automatically by the external OAuth service after the user approves access. You do not call this endpoint directly.

Request

GET /connectors/oauth/:connectorKey/callback?code=...&state=...
ParamLocationDescription
connectorKeyPathThe connector being authorized, e.g. slack, notion
codeQueryAuthorization code issued by the external OAuth provider
stateQueryShort-lived, single-use flow token generated by POST /connectors/instances/:id/oauth/initiate

Response

Chainabit validates the state parameter, enforces the single-use flow record, exchanges the authorization code for access and refresh tokens, stores them encrypted, and responds with a 302 redirect back to the web app's connector settings page (success or error state reflected in the redirect query string).

Security note: the callback endpoint is intentionally public. Do not add a JWT guard to it, because OAuth providers must be able to reach it. The protection mechanism is the state token plus the server-side flow state record.

Code Examples

Not applicable — this endpoint is invoked by the OAuth provider's redirect, not called directly by API clients.


Token Refresh

OAuth access tokens are refreshed automatically in the background. Chainabit runs a scheduled job every 5 minutes to detect tokens that are about to expire and refresh them using the stored refresh token.

If the refresh fails (e.g., the user revoked access), the instance status changes to error. Test the connection or re-initiate the OAuth flow to recover.

Built with purpose.