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 + connection management permission
GET/connectors/instances/:id/credentials/statusGet credential statusJWT
DELETE/connectors/instances/:id/credentialsDelete credentialsJWT + connection management permission
POST/connectors/instances/:id/oauth/initiateInitiate OAuth flowJWT + connection management permission
GET/connectors/oauth/:connectorKey/callbackOAuth callback (automated)—

Storing or deleting credentials and starting OAuth require ownership of a Personal connection, or owner/admin permission in the owning Business workspace for a Team connection. Live workspace access and plan requirements also apply. Allowing a Personal connection in another workspace does not transfer or copy its credentials. See Who can manage an instance.


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.