Docs
BlogHomeStart building

Connected Accounts (totalum-sdk/connections)


import … from 'totalum-sdk/connections' — or the same namespace on totalum from 'totalum-sdk'. Install, configuration and errors: Totalum SDK.

Let your app's users connect their own accounts of about 1,500 apps — Google Calendar, Gmail, Google Drive, Outlook, Slack, HubSpot, Notion, GitHub, Stripe, Airtable and more — and act on them from your server: create a calendar event, send an e-mail from their Gmail, read their files, or receive their events. They sign in on a hosted page, you never handle OAuth apps or tokens, and each user only ever sees their own connections. userId is always your signed-in user's id, taken from the session on the server. Actions are billed in credits; listing is free.

ts
const url = await totalum.connections.connectUrl({ userId: user.id, toolkit: 'googlecalendar', returnUrl });
Method or typeWhat it does
totalumConnectionsCreates the connections namespace (the same object as totalum.connections; scope connections; server-side code only: it needs the project key).
connections.connectUrlThe URL to send the user to so they connect toolkit (e.g.
connections.listThe user's connected accounts (newest first), optionally of one app: show them on a settings page with a "Disconnect" button.
connections.isConnectedWhether the user has an active connection of an app: show "Connect" or the feature that needs it.
connections.disconnectDisconnects one of the user's connected accounts (the provider's access is revoked).
connections.executeRuns action (e.g.
connections.actionsThe actions an app offers (slug, name, description), optionally filtered by words — to find the slug to pass to execute().
connections.actionOne action with its inputSchema: the JSON Schema of what execute() takes as input.
connections.toolkitsSearches the catalogue of apps (search matches names and descriptions, category e.g.
connections.toolkitOne app, exactly: how it connects and, for credentials, the fields the user types on the hosted page.
connections.triggersThe events (triggers) an app can send, such as GMAIL_NEW_GMAIL_MESSAGE — to find the slug to pass to subscribe().
connections.triggerOne trigger with configSchema (what subscribe's config takes) and payloadSchema (the event's shape).
connections.subscribeWatches trigger on the user's active connection of its toolkit: each event is POSTed to deliverTo (a path of your own app, default /api/webhooks/connections) on the preview (dev users) or the published site (live users), signed with your project's webhook secret — read it with verifyEvent.
connections.subscriptionsThe user's event subscriptions.
connections.unsubscribeStops one of the user's subscriptions: no more events are delivered.
connections.verifyEventVerifies one delivered event (the raw body text and the request headers of your deliverTo route) with TOTALUM_WEBHOOK_SECRET and returns it typed: data.userId is your user, data.payload the event.
ConnectionOne of a user's connected accounts.
ConnectionActionAn action an app offers.
ConnectionEventOne event from a user's connected account, delivered to your deliverTo route and returned by connections.verifyEvent().
ConnectionStatusThe state of a connected account.
ConnectionSubscriptionA subscription to a trigger on a user's connected account.
ConnectionToolkitAn app of the catalogue.
ConnectionToolkitDetailOne app, from connections.toolkit(slug): the catalogue fields plus the credential fields.
ConnectionTriggerAn event an app can send.

#totalumConnections

ts
totalumConnections(options?: TotalumClientOptions): TotalumConnections

Creates the connections namespace (the same object as totalum.connections; scope connections; server-side code only: it needs the project key). Your app's users connect their own accounts of ~1,500 apps — Google Calendar, Gmail, Google Drive, Outlook, OneDrive, Slack, Dropbox, Notion, HubSpot, GitHub, Stripe, Airtable, … — on a hosted page (they sign in at the provider, or type their own API key there), with no OAuth app of yours; then your server runs actions on them (GOOGLECALENDAR_CREATE_EVENT, GMAIL_SEND_EMAIL, …) and receives their events (subscribe). The tokens never reach your app. userId is always your signed-in user's id (the one in your database), taken from the session on the server — never from the request: each user, project and environment (dev/live) only ever sees its own connections.

Parameters

NameTypeRequiredDescription
optionsTotalumClientOptionsnoThe project key, API URL and environment; each falls back to its environment variable.
options.keystringnoProject key tlm_pk_…; default TOTALUM_PROJECT_KEY. Server-side only.
options.apiUrlstringnoThe Totalum API's URL: default TOTALUM_SDK_API_URL, else https://sdk-api.totalum.app.
options.env'dev' | 'live'noSent as x-totalum-env on every call (the database it targets, and dev vs live usage); default TOTALUM_ENV, else live.

Returns TotalumConnections — The connections namespace: the methods below.

Example

ts
import { totalumConnections } from 'totalum-sdk/connections';

const connections = totalumConnections();
const list = await connections.list({ userId: user.id });

The namespace's type is exported as TotalumConnections (import type { TotalumConnections } from 'totalum-sdk/connections').

#connections.connectUrl

ts
connections.connectUrl(input: { userId, toolkit, returnUrl }): Promise<string>

The URL to send the user to so they connect toolkit (e.g. 'googlecalendar'); valid about 10 minutes, so make it when the user clicks "Connect" and redirect at once. They sign in at the provider (or type their own key on the hosted page) and come back to returnUrl with ?connection=success|failed&toolkit=<toolkit> added. A new connection through Totalum's sign-in is billed once in credits when it becomes active.

Parameters

NameTypeRequiredDescription
input{ userId, toolkit, returnUrl }yesYour user, the app to connect and where to come back.
input.userIdstringyesYour signed-in user's id (from your session, never from the request). Allowed: at least 1 character, at most 200 characters.
input.toolkitstringyesThe app to connect, by slug (googlecalendar, gmail, slack); find slugs with toolkits({ search }).
input.returnUrlstringyesWhere the user comes back after signing in; https:// (or http://localhost). ?connection=success|failed&toolkit=… is added. Allowed: at most 2000 characters, a URL.

Returns Promise<string> — The hosted sign-in URL to redirect the user to.

Errors — besides the errors every call can throw:

CodeHTTPWhen
VALIDATION_ERROR400returnUrl is not https:// (only localhost may be http).
RECORD_NOT_FOUND404No app with this slug: look it up with toolkits({ search }).
TOOLKIT_NOT_AVAILABLE400This app cannot be connected.
TOOLKIT_NEEDS_OWN_APP400This app only signs in through an OAuth app registered by the developer, which is not available.

Example

ts
// src/app/api/connect/google-calendar/route.ts
const user = await requireUser(); // your own session
const url = await totalum.connections.connectUrl({
  userId: user.id,
  toolkit: 'googlecalendar',
  returnUrl: 'https://acme.totalum.app/settings/integrations',
});
return Response.redirect(url, 302);

Result:

json
"https://connect.<sign-in host>/link/lk_Iem_dXdIv3-i"

#connections.list

ts
connections.list(input: { userId, toolkit? }): Promise<Connection[]>

The user's connected accounts (newest first), optionally of one app: show them on a settings page with a "Disconnect" button. status is active when usable. Free.

Parameters

NameTypeRequiredDescription
input{ userId, toolkit? }yesYour user, and optionally one app.
input.userIdstringyesYour signed-in user's id, from the session.
input.toolkitstringnoOnly connections of this app (googlecalendar).

Returns Promise<Connection[]> — The connections.

Each item:

FieldTypeAlways presentDescription
idstringyesThe connection id: pass it to disconnect(), or as connectionId.
toolkitstringyesThe app (googlecalendar).
statusConnectionStatusyesWhether it can be used: see ConnectionStatus.
createdAtstringyesWhen the user connected it.

Errors — besides the errors every call can throw:

CodeHTTPWhen
VALIDATION_ERROR400userId is empty or over 200 characters.

Example

ts
const list = await totalum.connections.list({ userId: 'user_42' });

Result:

json
[
  { "id": "ca_8Qw2nZ4kLp", "toolkit": "googlecalendar", "status": "active", "createdAt": "2026-10-02T09:14:00.000Z" },
  { "id": "ca_3Hy7tR1mXc", "toolkit": "gmail", "status": "expired", "createdAt": "2026-08-21T16:40:00.000Z" }
]

#connections.isConnected

ts
connections.isConnected(input: { userId, toolkit }): Promise<boolean>

Whether the user has an active connection of an app: show "Connect" or the feature that needs it. Free.

Parameters

NameTypeRequiredDescription
input{ userId, toolkit }yesYour user and the app.
input.userIdstringyesYour signed-in user's id, from the session.
input.toolkitstringyesThe app's slug (googlecalendar).

Returns Promise<boolean> — true when the user has an active connection of toolkit.

Errors — besides the errors every call can throw:

CodeHTTPWhen
VALIDATION_ERROR400userId is empty or over 200 characters.

Example

ts
const connected = await totalum.connections.isConnected({ userId: 'user_42', toolkit: 'googlecalendar' });

Result:

json
false

#connections.disconnect

ts
connections.disconnect(input: { userId, id }): Promise<{ deleted }>

Disconnects one of the user's connected accounts (the provider's access is revoked). Free.

Parameters

NameTypeRequiredDescription
input{ userId, id }yesYour user and the connection.
input.userIdstringyesYour signed-in user's id, from the session.
input.idstringyesThe connection's id, from list().

Returns Promise<{ deleted }> — { deleted: true }.

FieldTypeAlways presentDescription
deletedtrueyesAlways true.

Errors — besides the errors every call can throw:

CodeHTTPWhen
RECORD_NOT_FOUND404The user has no connection with this id.

Example

ts
await totalum.connections.disconnect({ userId: 'user_42', id: 'ca_8Qw2nZ4kLp' });

Result:

json
{ "deleted": true }

#connections.execute

ts
connections.execute(input: { userId, action, input?, connectionId? }): Promise<{ data, usage }>

Runs action (e.g. GOOGLECALENDAR_CREATE_EVENT) with the user's active connection of its toolkit and returns the provider's answer in data. Read the action's inputSchema with action(slug) to build input. Billed in credits per action.

Parameters

NameTypeRequiredDescription
input{ userId, action, input?, connectionId? }yesYour user, the action and its input.
input.userIdstringyesYour signed-in user's id. Allowed: at least 1 character, at most 200 characters.
input.actionstringyesThe action's slug (GOOGLECALENDAR_CREATE_EVENT); find it with actions({ toolkit, search }).
input.inputRecord<string, unknown>noThe action's arguments, as its inputSchema describes (read it with action(slug)).
input.connectionIdstringnoWhich of the user's connections to use, when they have several of the same app. Default: their newest active one. Allowed: at most 64 characters.

Returns Promise<{ data, usage }> — The provider's answer and what the action cost.

FieldTypeAlways presentDescription
dataunknownyesThe provider's answer, as the action's documentation describes (the created event, the sent message, …).
usage{ credits }yesWhat the call cost.
usage.creditsnumberyesCredits charged for this call.

Errors — besides the errors every call can throw:

CodeHTTPWhen
CONNECTION_REQUIRED409The user has not connected this app yet: send them to connectUrl(); details.action names the action.
CONNECTION_ACTION_FAILED422The provider refused the action (a bad input, a missing permission); the message says why.
RECORD_NOT_FOUND404No action with this slug.
PROVIDER_TIMEOUT504The provider did not answer in time.

Example

ts
const { data } = await totalum.connections.execute({
  userId: 'user_42',
  action: 'GOOGLECALENDAR_CREATE_EVENT',
  input: {
    summary: 'Demo with Acme',
    start_datetime: '2026-10-14T10:00:00',
    timezone: 'Europe/Madrid',
    event_duration_minutes: 30,
    attendees: ['ana@example.com'],
  },
});

Result:

ts
{
  "data": { "id": "5r1q8m2v6k0c4n9b", "status": "confirmed", "htmlLink": "https://www.google.com/calendar/event?eid=NXIxcThtMnY2azBj…", … },
  "usage": { "credits": 0.0028 }
}

#connections.actions

ts
connections.actions(input: { toolkit, search? }): Promise<ConnectionAction[]>

The actions an app offers (slug, name, description), optionally filtered by words — to find the slug to pass to execute(). Free.

Parameters

NameTypeRequiredDescription
input{ toolkit, search? }yesThe app and an optional search.
input.toolkitstringyesThe app's slug (googlecalendar).
input.searchstringnoOnly actions matching these words (create event).

Returns Promise<ConnectionAction[]> — The matching actions, without their input schemas (read one with action(slug)).

Each item:

FieldTypeAlways presentDescription
slugstringyesThe action's id, to pass to execute().
toolkitstringyesThe app it belongs to.
namestringyesA short human name.
descriptionstringyesWhat it does and how to call it.
inputSchemaRecord<string, unknown>noThe JSON Schema of execute()'s input (only from action(slug)).

Errors — besides the errors every call can throw:

CodeHTTPWhen
RECORD_NOT_FOUND404No app with this slug.

Example

ts
const actions = await totalum.connections.actions({ toolkit: 'googlecalendar', search: 'create event' });

Result:

json
[
  { "slug": "GOOGLECALENDAR_CREATE_EVENT", "toolkit": "googlecalendar", "name": "Create Event", "description": "Creates a timed event using start_datetime and either end_datetime or duration fields, or an all-day event…" },
  { "slug": "GOOGLECALENDAR_QUICK_ADD", "toolkit": "googlecalendar", "name": "Quick Add Event", "description": "Creates an event from a text string such as 'Lunch with Ana tomorrow at 1pm'…" }
]

#connections.action

ts
connections.action(slug: string): Promise<ConnectionAction>

One action with its inputSchema: the JSON Schema of what execute() takes as input. Free.

Parameters

NameTypeRequiredDescription
slugstringyesThe action's slug (GOOGLECALENDAR_CREATE_EVENT).

Returns Promise<ConnectionAction> — The action and its input schema.

FieldTypeAlways presentDescription
slugstringyesThe action's id, to pass to execute().
toolkitstringyesThe app it belongs to.
namestringyesA short human name.
descriptionstringyesWhat it does and how to call it.
inputSchemaRecord<string, unknown>noThe JSON Schema of execute()'s input (only from action(slug)).

Errors — besides the errors every call can throw:

CodeHTTPWhen
RECORD_NOT_FOUND404No action with this slug.

Example

ts
const action = await totalum.connections.action('GOOGLECALENDAR_CREATE_EVENT');

Result:

ts
{
  "slug": "GOOGLECALENDAR_CREATE_EVENT",
  "toolkit": "googlecalendar",
  "name": "Create Event",
  "description": "Creates a timed event using start_datetime and either end_datetime or duration fields…",
  "inputSchema": {
    "type": "object",
    "properties": {
      "summary": { "type": "string", "description": "Summary (title) of the event." },
      "start_datetime": { "type": "string", "description": "Event start time…" },
      …
    }
  }
}

#connections.toolkits

ts
connections.toolkits(input?: { search?, category?, cursor?, limit? }): Promise<{ toolkits, nextCursor, total }>

Searches the catalogue of apps (search matches names and descriptions, category e.g. crm); connect says how a user connects it: oauth (signs in at the provider), credentials (types their own key on the hosted page), none (no connection: run its actions directly), unavailable (cannot be connected). Pages with cursor = the previous nextCursor. Free.

Parameters

NameTypeRequiredDescription
input{ search?, category?, cursor?, limit? }noThe search, category and paging; all optional.
input.searchstringnoWords to match in app names and descriptions (calendar).
input.categorystringnoOnly apps of this category (crm, email, calendar).
input.cursorstringnoThe previous page's nextCursor.
input.limitnumbernoApps per page.

Returns Promise<{ toolkits, nextCursor, total }> — One page of apps, the cursor of the next and the total.

FieldTypeAlways presentDescription
toolkitsConnectionToolkit[]yesThe apps of this page.
nextCursorstring | nullyesPass it as cursor for the next page; null on the last.
totalnumberyesHow many apps match in total.

Errors — besides the errors every call can throw:

CodeHTTPWhen
PROVIDER_UNAVAILABLE503The catalogue is not available right now; retry later.

Example

ts
const page = await totalum.connections.toolkits({ search: 'calendar', limit: 2 });

Result:

json
{
  "toolkits": [
    { "slug": "googlecalendar", "name": "Google Calendar", "description": "Google Calendar is a time management tool…", "categories": ["scheduling-&-booking"], "authSchemes": ["OAUTH2"], "connect": "oauth", "actionsCount": 47, "triggersCount": 7 },
    { "slug": "outlook", "name": "Outlook", "description": "Outlook is Microsoft's email and calendaring platform…", "categories": ["email", "calendar"], "authSchemes": ["OAUTH2", "S2S_OAUTH2"], "connect": "oauth", "actionsCount": 297, "triggersCount": 5 }
  ],
  "nextCursor": "Mi0y",
  "total": 18
}

#connections.toolkit

ts
connections.toolkit(slug: string): Promise<ConnectionToolkitDetail>

One app, exactly: how it connects and, for credentials, the fields the user types on the hosted page. Free.

Parameters

NameTypeRequiredDescription
slugstringyesThe app's slug (googlecalendar).

Returns Promise<ConnectionToolkitDetail> — The app's details.

FieldTypeAlways presentDescription
slugstringyesThe app's id (googlecalendar).
namestringyesIts name.
descriptionstringyesWhat it is.
categoriesstring[]yesIts categories (email, calendar, crm).
authSchemesstring[]yesHow the provider authenticates (OAUTH2, API_KEY).
connect'none' | 'credentials' | 'oauth' | 'unavailable'yesHow your user connects it: oauth (signs in at the provider), credentials (types their own key on the hosted page), none (no connection needed: run its actions directly), unavailable (cannot be connected).
actionsCountnumberyesHow many actions it offers.
triggersCountnumberyesHow many event triggers it offers.
credentialFields({ name, label, description })[]yesFor connect: "credentials": the fields the user types on the hosted page.
credentialFields.namestringyesThe field's id.
credentialFields.labelstringyesIts label.
credentialFields.descriptionstringyesWhat to type there.

Errors — besides the errors every call can throw:

CodeHTTPWhen
RECORD_NOT_FOUND404No app with this slug.

Example

ts
const app = await totalum.connections.toolkit('googlecalendar');

Result:

json
{
  "slug": "googlecalendar",
  "name": "Google Calendar",
  "description": "Google Calendar is a time management tool providing scheduling features…",
  "categories": ["scheduling-&-booking"],
  "authSchemes": ["OAUTH2"],
  "connect": "oauth",
  "actionsCount": 47,
  "triggersCount": 7,
  "credentialFields": []
}

#connections.triggers

ts
connections.triggers(input: { toolkit }): Promise<ConnectionTrigger[]>

The events (triggers) an app can send, such as GMAIL_NEW_GMAIL_MESSAGE — to find the slug to pass to subscribe(). Free.

Parameters

NameTypeRequiredDescription
input{ toolkit }yesThe app.
input.toolkitstringyesThe app's slug (gmail).

Returns Promise<ConnectionTrigger[]> — The app's triggers, without their schemas (read one with trigger(slug)).

Each item:

FieldTypeAlways presentDescription
slugstringyesThe trigger's id, to pass to subscribe().
toolkitstringyesThe app it belongs to.
namestringyesA short human name.
descriptionstringyesWhen it fires.
typestringyespoll (checked every few minutes) or webhook (pushed by the provider at once).
instructionsstringnoSetup notes from the provider.
configSchemaRecord<string, unknown>noThe JSON Schema of subscribe()'s config (only from trigger(slug)).
payloadSchemaRecord<string, unknown>noThe JSON Schema of each event's payload (only from trigger(slug)).

Errors — besides the errors every call can throw:

CodeHTTPWhen
RECORD_NOT_FOUND404No app with this slug.

Example

ts
const triggers = await totalum.connections.triggers({ toolkit: 'gmail' });

Result:

json
[
  { "slug": "GMAIL_EMAIL_SENT_TRIGGER", "toolkit": "gmail", "name": "Email Sent", "description": "Triggers when a Gmail message is sent by the authenticated user…", "type": "poll" },
  { "slug": "GMAIL_NEW_GMAIL_MESSAGE", "toolkit": "gmail", "name": "New Gmail Message Received Trigger", "description": "Triggers when a new message is received in Gmail.", "type": "poll" }
]

#connections.trigger

ts
connections.trigger(slug: string): Promise<ConnectionTrigger>

One trigger with configSchema (what subscribe's config takes) and payloadSchema (the event's shape). Free.

Parameters

NameTypeRequiredDescription
slugstringyesThe trigger's slug (GMAIL_NEW_GMAIL_MESSAGE).

Returns Promise<ConnectionTrigger> — The trigger and its schemas.

FieldTypeAlways presentDescription
slugstringyesThe trigger's id, to pass to subscribe().
toolkitstringyesThe app it belongs to.
namestringyesA short human name.
descriptionstringyesWhen it fires.
typestringyespoll (checked every few minutes) or webhook (pushed by the provider at once).
instructionsstringnoSetup notes from the provider.
configSchemaRecord<string, unknown>noThe JSON Schema of subscribe()'s config (only from trigger(slug)).
payloadSchemaRecord<string, unknown>noThe JSON Schema of each event's payload (only from trigger(slug)).

Errors — besides the errors every call can throw:

CodeHTTPWhen
RECORD_NOT_FOUND404No trigger with this slug.

Example

ts
const trigger = await totalum.connections.trigger('GMAIL_NEW_GMAIL_MESSAGE');

Result:

ts
{
  "slug": "GMAIL_NEW_GMAIL_MESSAGE",
  "toolkit": "gmail",
  "name": "New Gmail Message Received Trigger",
  "description": "Triggers when a new message is received in Gmail.",
  "type": "poll",
  "instructions": "Provide the user ID (usually 'me')…",
  "configSchema": {
    "properties": {
      "interval": { "type": "number", "default": 2, "description": "Periodic Interval to Check for Updates & Send a Trigger in Minutes" },
      "labelIds": { "type": "string", "default": "INBOX", "description": "Filter messages by a single label ID…" }
    }
  },
  "payloadSchema": { "properties": { "message_id": { "type": "string" }, "sender": { "type": "string" }, "subject": { "type": "string" }, … } }
}

#connections.subscribe

ts
connections.subscribe(input: { userId, trigger, config?, connectionId?, deliverTo? }): Promise<ConnectionSubscription>

Watches trigger on the user's active connection of its toolkit: each event is POSTed to deliverTo (a path of your own app, default /api/webhooks/connections) on the preview (dev users) or the published site (live users), signed with your project's webhook secret — read it with verifyEvent. Each delivered event is billed in credits.

Parameters

NameTypeRequiredDescription
input{ userId, trigger, config?, connectionId?, deliverTo? }yesYour user, the trigger, its configuration and where to deliver.
input.userIdstringyesYour signed-in user's id. Allowed: at least 1 character, at most 200 characters.
input.triggerstringyesThe trigger's slug (GMAIL_NEW_GMAIL_MESSAGE); find it with triggers({ toolkit }).
input.configRecord<string, unknown>noThe trigger's settings, as its configSchema describes (read it with trigger(slug)).
input.connectionIdstringnoWhich of the user's connections to watch, when they have several of the same app. Allowed: at most 64 characters.
input.deliverTostringnoThe path of your app the events are POSTed to. Default /api/webhooks/connections.

Returns Promise<ConnectionSubscription> — The subscription; keep its id to unsubscribe() later.

FieldTypeAlways presentDescription
idstringyesThe subscription id, to pass to unsubscribe().
triggerstringyesThe trigger watched.
connectionIdstringyesThe connected account watched.
configRecord<string, unknown>yesThe trigger's settings.
enabledbooleanyesWhether events are being delivered.

Errors — besides the errors every call can throw:

CodeHTTPWhen
CONNECTION_REQUIRED409The user has not connected this app yet: send them to connectUrl().
RECORD_NOT_FOUND404No trigger with this slug.
VALIDATION_ERROR400config does not match the trigger's configSchema, or deliverTo is not a path.

Example

ts
const sub = await totalum.connections.subscribe({
  userId: 'user_42',
  trigger: 'GMAIL_NEW_GMAIL_MESSAGE',
  config: { labelIds: 'INBOX', interval: 1 },
});

Result:

json
{ "id": "ti_Zp4Lm8Qw2nXc", "trigger": "GMAIL_NEW_GMAIL_MESSAGE", "connectionId": "ca_3Hy7tR1mXc", "config": { "labelIds": "INBOX", "interval": 1 }, "enabled": true }

#connections.subscriptions

ts
connections.subscriptions(input: { userId }): Promise<ConnectionSubscription[]>

The user's event subscriptions. Free.

Parameters

NameTypeRequiredDescription
input{ userId }yesYour user.
input.userIdstringyesYour signed-in user's id, from the session.

Returns Promise<ConnectionSubscription[]> — The subscriptions.

Each item:

FieldTypeAlways presentDescription
idstringyesThe subscription id, to pass to unsubscribe().
triggerstringyesThe trigger watched.
connectionIdstringyesThe connected account watched.
configRecord<string, unknown>yesThe trigger's settings.
enabledbooleanyesWhether events are being delivered.

Errors — besides the errors every call can throw:

CodeHTTPWhen
VALIDATION_ERROR400userId is empty or over 200 characters.

Example

ts
const subs = await totalum.connections.subscriptions({ userId: 'user_42' });

Result:

json
[
  { "id": "ti_Zp4Lm8Qw2nXc", "trigger": "GMAIL_NEW_GMAIL_MESSAGE", "connectionId": "ca_3Hy7tR1mXc", "config": { "labelIds": "INBOX", "interval": 1 }, "enabled": true }
]

#connections.unsubscribe

ts
connections.unsubscribe(input: { userId, id }): Promise<{ deleted }>

Stops one of the user's subscriptions: no more events are delivered. Free.

Parameters

NameTypeRequiredDescription
input{ userId, id }yesYour user and the subscription.
input.userIdstringyesYour signed-in user's id, from the session.
input.idstringyesThe subscription's id, from subscribe() or subscriptions().

Returns Promise<{ deleted }> — { deleted: true }.

FieldTypeAlways presentDescription
deletedtrueyesAlways true.

Errors — besides the errors every call can throw:

CodeHTTPWhen
RECORD_NOT_FOUND404The user has no subscription with this id.

Example

ts
await totalum.connections.unsubscribe({ userId: 'user_42', id: 'ti_Zp4Lm8Qw2nXc' });

Result:

json
{ "deleted": true }

#connections.verifyEvent

ts
connections.verifyEvent(rawBody: string, headers: Headers | Record<string, string>, secret?: string): Promise<ConnectionEvent>

Verifies one delivered event (the raw body text and the request headers of your deliverTo route) with TOTALUM_WEBHOOK_SECRET and returns it typed: data.userId is your user, data.payload the event. Answer 2xx quickly; a failed delivery is retried.

Parameters

NameTypeRequiredDescription
rawBodystringyesThe request body as text, untouched (await request.text()).
headersHeaders | Record<string, string>yesThe request's headers.
secretstringnoThe signing secret. Default: TOTALUM_WEBHOOK_SECRET.

Returns Promise<ConnectionEvent> — The verified event.

FieldTypeAlways presentDescription
idstringyesThe event id; handle each id once (a failed delivery is retried).
createdAtstringyesWhen it was delivered.
projectIdstringyesYour project id.
env'dev' | 'live'yesdev for the preview's users, live for the published site's.
type'connections.event'yesAlways connections.event.
data{ subscriptionId, trigger, userId, connectionId, payload }yesThe event.
data.subscriptionIdstringyesThe subscription that produced it.
data.triggerstringyesThe trigger slug (GMAIL_NEW_GMAIL_MESSAGE).
data.userIdstringyesYour user's id, as given to subscribe().
data.connectionIdstringyesThe connected account it came from.
data.payloadRecord<string, unknown>yesThe event itself, as the trigger's payloadSchema describes (a new e-mail's sender, subject, …).
livemodebooleannoNot set for connection events.

Errors — besides the errors every call can throw:

CodeHTTPWhen
WEBHOOK_SIGNATURE_INVALID401Not signed by Totalum (or the body was changed): answer 401.
WEBHOOK_TIMESTAMP_EXPIRED401A replayed delivery: answer 401.
WEBHOOK_SECRET_MISSING—No secret is set or passed.

Example

ts
// src/app/api/webhooks/connections/route.ts
export async function POST(request: Request) {
  const event = await totalum.connections.verifyEvent(await request.text(), request.headers);
  if (event.data.trigger === 'GMAIL_NEW_GMAIL_MESSAGE') await saveInboxItem(event.data.userId, event.data.payload);
  return new Response('ok');
}

Result:

ts
{
  "id": "evt_01M4P2A9K3J7H5G1F8D6S4A2Q0",
  "type": "connections.event",
  "createdAt": "2026-10-11T18:20:31.000Z",
  "projectId": "acme-crm",
  "env": "live",
  "data": {
    "subscriptionId": "ti_Zp4Lm8Qw2nXc",
    "trigger": "GMAIL_NEW_GMAIL_MESSAGE",
    "userId": "user_42",
    "connectionId": "ca_3Hy7tR1mXc",
    "payload": { "message_id": "19a2c4e6f8b0d1e3", "sender": "Ana <ana@example.com>", "subject": "Quote for October", … }
  }
}

#Connection

One of a user's connected accounts.

FieldTypeAlways presentDescription
idstringyesThe connection id: pass it to disconnect(), or as connectionId.
toolkitstringyesThe app (googlecalendar).
statusConnectionStatusyesWhether it can be used: see ConnectionStatus.
createdAtstringyesWhen the user connected it.

#ConnectionAction

An action an app offers.

FieldTypeAlways presentDescription
slugstringyesThe action's id, to pass to execute().
toolkitstringyesThe app it belongs to.
namestringyesA short human name.
descriptionstringyesWhat it does and how to call it.
inputSchemaRecord<string, unknown>noThe JSON Schema of execute()'s input (only from action(slug)).

#ConnectionEvent

One event from a user's connected account, delivered to your deliverTo route and returned by connections.verifyEvent().

FieldTypeAlways presentDescription
idstringyesThe event id; handle each id once (a failed delivery is retried).
createdAtstringyesWhen it was delivered.
projectIdstringyesYour project id.
env'dev' | 'live'yesdev for the preview's users, live for the published site's.
type'connections.event'yesAlways connections.event.
data{ subscriptionId, trigger, userId, connectionId, payload }yesThe event.
data.subscriptionIdstringyesThe subscription that produced it.
data.triggerstringyesThe trigger slug (GMAIL_NEW_GMAIL_MESSAGE).
data.userIdstringyesYour user's id, as given to subscribe().
data.connectionIdstringyesThe connected account it came from.
data.payloadRecord<string, unknown>yesThe event itself, as the trigger's payloadSchema describes (a new e-mail's sender, subject, …).
livemodebooleannoNot set for connection events.

#ConnectionStatus

The state of a connected account.

ValueMeaning
'expired'The provider's access expired or was revoked: ask the user to connect again.
'active'Connected and usable.
'failed'The connection attempt failed: ask the user to connect again.
'disabled'Turned off: ask the user to connect again.
'pending'The user started connecting but has not finished.

#ConnectionSubscription

A subscription to a trigger on a user's connected account.

FieldTypeAlways presentDescription
idstringyesThe subscription id, to pass to unsubscribe().
triggerstringyesThe trigger watched.
connectionIdstringyesThe connected account watched.
configRecord<string, unknown>yesThe trigger's settings.
enabledbooleanyesWhether events are being delivered.

#ConnectionToolkit

An app of the catalogue.

FieldTypeAlways presentDescription
slugstringyesThe app's id (googlecalendar).
namestringyesIts name.
descriptionstringyesWhat it is.
categoriesstring[]yesIts categories (email, calendar, crm).
authSchemesstring[]yesHow the provider authenticates (OAUTH2, API_KEY).
connect'none' | 'credentials' | 'oauth' | 'unavailable'yesHow your user connects it: oauth (signs in at the provider), credentials (types their own key on the hosted page), none (no connection needed: run its actions directly), unavailable (cannot be connected).
actionsCountnumberyesHow many actions it offers.
triggersCountnumberyesHow many event triggers it offers.

#ConnectionToolkitDetail

One app, from connections.toolkit(slug): the catalogue fields plus the credential fields.

FieldTypeAlways presentDescription
slugstringyesThe app's id (googlecalendar).
namestringyesIts name.
descriptionstringyesWhat it is.
categoriesstring[]yesIts categories (email, calendar, crm).
authSchemesstring[]yesHow the provider authenticates (OAUTH2, API_KEY).
connect'none' | 'credentials' | 'oauth' | 'unavailable'yesHow your user connects it: oauth (signs in at the provider), credentials (types their own key on the hosted page), none (no connection needed: run its actions directly), unavailable (cannot be connected).
actionsCountnumberyesHow many actions it offers.
triggersCountnumberyesHow many event triggers it offers.
credentialFields({ name, label, description })[]yesFor connect: "credentials": the fields the user types on the hosted page.
credentialFields.namestringyesThe field's id.
credentialFields.labelstringyesIts label.
credentialFields.descriptionstringyesWhat to type there.

#ConnectionTrigger

An event an app can send.

FieldTypeAlways presentDescription
slugstringyesThe trigger's id, to pass to subscribe().
toolkitstringyesThe app it belongs to.
namestringyesA short human name.
descriptionstringyesWhen it fires.
typestringyespoll (checked every few minutes) or webhook (pushed by the provider at once).
instructionsstringnoSetup notes from the provider.
configSchemaRecord<string, unknown>noThe JSON Schema of subscribe()'s config (only from trigger(slug)).
payloadSchemaRecord<string, unknown>noThe JSON Schema of each event's payload (only from trigger(slug)).