SCSuper ConnectDocumentationOpen the admin panel →

SDK reference

Two dependency-free packages. Both use fetch and work in Node.js 18+, Bun, Deno, Cloudflare Workers and browsers.

@super-connect/client

For your backend. Every method returns the parsed JSON body and throws SuperConnectError (with status, optional code and optional details) on a non-2xx response.

import { createClient } from "@super-connect/client";

const sc = createClient({ apiKey: process.env.SUPER_CONNECT_API_KEY });

baseUrl defaults to https://super-connect.dogar.biz; the client adds /api/v1 itself. Pass it only when talking to a self-hosted or local instance.

Call Route
sc.providers.list() GET /api/v1/providers
sc.providers.save(id, provider) PUT /api/v1/providers/:id
sc.providers.delete(id) DELETE /api/v1/providers/:id
sc.providers.actions(id) GET /api/v1/providers/:id/actions
sc.integrations.list() GET /api/v1/integrations
sc.integrations.save(id, input) PUT /api/v1/integrations/:id
sc.integrations.delete(id) DELETE /api/v1/integrations/:id
sc.integrations.listWebhooks(id, filters) GET /api/v1/integrations/:id/webhooks
sc.integrations.getWebhook(id, inboundId) GET /api/v1/integrations/:id/webhooks/:inboundId
sc.users.create({ id, name, metadata }) POST /api/v1/users
sc.users.get(id, options) GET /api/v1/users/:id
sc.users.list(filters) GET /api/v1/users
sc.users.update(id, patch) PATCH /api/v1/users/:id
sc.users.delete(id) DELETE /api/v1/users/:id
sc.connectSessions.create(input) POST /api/v1/connect/sessions
sc.connect(integrationId, userId | input) POST /api/v1/connect/:integrationId
sc.connections.list(filters) GET /api/v1/connections
sc.connections.get(id, userId) GET /api/v1/connections/:id
sc.connections.setMetadata(id, metadata, userId) PATCH /api/v1/connections/:id
sc.connections.delete(id, userId) DELETE /api/v1/connections/:id
sc.connections.reconnect(id) POST /api/v1/connections/:id/reconnect
sc.connections.runAction(id, actionId, input, userId) POST /api/v1/connections/:id/actions/:actionId
sc.connections.invocations(id, userId) GET /api/v1/connections/:id/invocations
sc.invocations.summary() GET /api/v1/invocations/summary
sc.proxy(id, path, init, userId) ANY /proxy/:id/*

userId becomes the X-User-Id header. Pass it on every connection call from a backend. It is the id you gave users.create; create the user before minting a session for them, or the call throws with code: "user_not_found".

await sc.users.create({ id: user.id, name: user.name });
const session = await sc.connectSessions.create({ userId: user.id });

users.get(id, { include: "connections" }) and users.list({ include: "connections" }) add connectionCount to each user; without the option the field is absent. users.list pages return total, the organization's user count independent of paging.

setMetadata merges routing keys into a connection; a null value deletes a key. It's how GitHub webhooks find a connection, see Receive provider events.

await sc.connections.setMetadata(
  connectionId,
  { "repository.612345678": "1", "organization.9876543": null },
  userId,
);

listWebhooks pages the requests an integration's receive URL has taken, newest first, filtered by outcome, since, limit and cursor. It returns { data, nextCursor } without bodies; getWebhook returns one row with its headers, its raw body and the forward events it produced.

proxy returns the raw Response so streamed bodies survive. path is relative to the provider's API base.

Omit apiKey in a browser and the client sends the admin panel's session cookie instead. That is how the admin panel itself uses the package.

code is set on a 402 (billing refusals). A 402 is terminal until the organization subscribes, so do not retry it.

try {
  await sc.connectSessions.create({ userId });
} catch (error) {
  if (error instanceof SuperConnectError && error.status === 402) {
    // error.code: "organization_unpaid" | "free_tier_connection_limit"
    return showUpgradePrompt(error.message);
  }
  throw error;
}

Types are exported: ApiProvider, ApiIntegration, ApiConnection, ConnectionHealth, ConnectionPage, EndUser, EndUserInput, EndUserPatch, EndUserFilters, EndUserOptions, EndUserPage, ConnectSession, ActionOutcome, ApiInvocation, InvocationSummary, InboundWebhook, InboundWebhookPage, InboundWebhookDetail, InboundWebhookFilters, InboundForward, InboundOutcome and WebhookSetup.

@super-connect/connect

For your frontend.

openConnectUI

import { SuperConnect } from "@super-connect/connect";

const handle = new SuperConnect().openConnectUI({
  sessionToken, // optional; or call handle.setSessionToken later
  onEvent: (event) => {},
});

Renders a modal with an iframe served by Super Connect. Escape and a backdrop click dismiss it.

Event When
{ type: "connect", connectionId, integrationId } Consent completed
{ type: "error", message, code? } The session is invalid, or the provider refused
{ type: "close" } The user dismissed the modal

On a 402 the modal shows the end user generic copy and the event carries the Worker's message plus code (organization_unpaid or free_tier_connection_limit), so you can show your own upgrade prompt.

handle.setSessionToken(token) can be called before or after the iframe is ready. handle.close() dismisses without emitting close.

createSessionClient

For building your own picker instead of the hosted one.

import { createSessionClient } from "@super-connect/connect";

const session = createSessionClient({ sessionToken });
const view = await session.getSession(); // user, mode, allowed integrations
const result = await session.authorize("github"); // { kind: "handoff", url }
const stored = await session.authorize("retool", { apiKey }); // { kind: "connected" }

authorize returns a handoff to open in a popup for OAuth integrations, or a completed connection when the integration takes an API key (see authMode and apiKey on each integration in the session view). Both methods throw SessionError (with status and optional code) on a non-2xx response.

Open url in a popup; the callback page posts its result to window.opener. Most providers refuse to be framed, so do not load it in an iframe.