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.