Integrations
An integration is a third-party service Catentio can talk to (Discord, GitHub, Anthropic, OpenRouter, and the rest of the built-in catalogue, plus operator-declared custom ones). Each integration is keyed by a slug (e.g. discord), and a single integration can hold several distinct credentials keyed by kind, plus free-form "secrets" catalog entries. list() is the only method in this SDK that actually reaches a working route.
For the runtime resolution model, see API → Integrations.
Auth.
/v1/integrations/*requires the control plane'sx-catentio-sessionheader, which this SDK never sends. NeitherapiKeymode nor Bearer-session mode authenticates against any route in this namespace. See SDKs → Auth: what actually works.
Namespace
catentio.integrations — three methods:
catentio.integrations.list()
catentio.integrations.configure(kind, input)
catentio.integrations.delete(kind)
There's no get, even though the control plane has one (GET /v1/integrations/{slug}) — it's just not wrapped here.
Methods
integrations.list
Signature. catentio.integrations.list(token?): Promise<{ data: Integration[]; meta: { total: number; configured: number; by_category: Array<{ category: string; count: number }> } }>
Returns the whole integration catalogue (built-in + custom), each with a rolled-up configured/health status — not per-credential detail. The SDK does not unwrap this response; you get the full {data, meta} object back.
import { CatentioSaasClient } from '@forjio/catentio-saas-node';
const catentio = new CatentioSaasClient({ session });
const { data: integrations } = await catentio.integrations.list();
for (const i of integrations) {
console.log(i.slug, i.status, `${i.credentials_set}/${i.credentials_required}`);
}
Real row shape:
interface Integration {
slug: string; // 'discord', 'github', 'anthropic', ...
name: string;
category: string;
description: string;
is_configured: boolean;
status: 'ok' | 'unconfigured' | 'invalid' | 'expired' | 'rate_limited' | 'unchecked';
credentials_required: number;
credentials_set: number;
credentials_total: number;
config_count: number;
extras: string[];
}
There is no kind (as a top-level field), label, connectedAt, or metadata field on this row — those don't exist. (kind is real, but it's a property of an individual credential inside an integration, not of the integration row itself — and this SDK doesn't expose the detail endpoint that shows individual credentials.)
integrations.configure — targets a route that does not exist
Signature. catentio.integrations.configure(kind, input, token?): Promise<Integration>
This call fails. It sends
PUT /v1/integrations/{kind}. That route doesn't exist — the real route isPUT /v1/integrations/{slug}/credentials/{kind}, which takes two path segments (which integration, which credential kind), not one. This SDK method only accepts one identifier, so it cannot construct the real URL at all.The real route is also OTP-gated: without
x-catentio-otp-challenge+x-catentio-otp-codeheaders, it returns403with{"otp_required": true, "challenge_id", "scope", "expires_at"}in the body. Neither header is something this SDK sends. If you need to set a credential today, call the real two-segment route directly and handle the OTP challenge/response flow yourself.
integrations.delete — targets a route that does not exist
Signature. catentio.integrations.delete(kind, token?): Promise<void>
This call fails. It sends
DELETE /v1/integrations/{kind}. The real route isDELETE /v1/integrations/{slug}/credentials/{kind}(two path segments, plus optionalscopeandcustomer_idquery params), and it is OTP-gated the same wayconfigureis — see above.
Common patterns
Health-check connected integrations
The one thing this namespace can actually do is read the rollup:
const { data: integrations } = await catentio.integrations.list();
const broken = integrations.filter(i => i.status !== 'ok');
if (broken.length) {
alertOps(`Catentio integrations need attention: ${broken.map(i => i.slug).join(', ')}`);
}
Inspect what's connected before depending on it
const { data: integrations } = await catentio.integrations.list();
const github = integrations.find(i => i.slug === 'github');
if (!github?.is_configured) {
throw new Error('Configure the github integration first (via the REST API, not this SDK).');
}
Errors
The real error body is FastAPI's {"detail": ...}. Because the wire response never has an error key, the SDK doesn't parse out a structured code/message — you get CatentioError with a generic HTTP_ERROR code and the HTTP reason phrase as the message.
| Status | Cause |
|---|---|
| 401 | Missing/invalid session — every route here requires x-catentio-session, which this SDK never sends. |
| 403 | {"otp_required": true, ...} — the real credential-set/clear routes require an OTP challenge/response this SDK never provides. |
| 404 | Always, for configure() and delete() (see the warnings above). |
Next
- Tools — the consumers of integration credentials.
- API → Integrations — HTTP-level reference, including the real two-segment credential routes and the OTP gate.