Skip to main content

Connections in Headless

A headless integration declares its connections in its configuration, instead of on a config page. Your init function, server functions, and flows can then use them, and your app lets customers choose or create them.

Declare connections​

Add connections to the connections property of a configuration scope. Each key names the connection within your integration:

src/configuration.ts
import {
configuration,
customerActivatedConnection,
} from "@prismatic-io/spectral";
import { z } from "zod";

export const integrationConfiguration = configuration({
instance: {
schema: z.object({
channel: z.object({ id: z.string(), name: z.string() }),
}),
version: "slack-alerts-v1",
connections: {
slack: customerActivatedConnection({ stableKey: "slack-oauth" }),
},
},
});

The instance scope accepts these kinds of connections:

FunctionConnection type
customerActivatedConnection({ stableKey })A customer-activated connection your customer creates.
organizationActivatedConnection({ stableKey })An org-activated connection your organization supplies.
connectionConfigVar({ ... })An integration-specific connection with its own inputs.

The userLevel scope accepts userActivatedConnection({ stableKey }) for a user-level connection, or connectionConfigVar().

Use connections in init and server functions​

init and server functions receive only the connections they declare. List them with their scope, such as instance.slack, and read them from context.connections:

src/configuration.ts
import { serverFunction, util } from "@prismatic-io/spectral";

export const listChannels = serverFunction({
label: "List channels",
inputSchema: z.object({ search: z.string().optional() }),
outputSchema: z.array(z.object({ id: z.string(), name: z.string() })),
connections: ["instance.slack"],
perform: async (context, { search }) => {
const token = util.types.toString(
context.connections.instance.slack.token?.access_token,
);
return searchSlackChannels(token, search);
},
});

init declares connections the same way, in init.connections. Spectral checks the declared keys against your configuration at compile time, so a typo in a connection key is a type error.

Use connections in flows​

Flows read the instance's connections from context.connections, by scope:

src/flows.ts
onExecution: async (context) => {
const slack = context.connections?.instance?.slack;
// ...
},

Let customers choose connections in your app​

When your app loads a configuration with Solis, the configuration lists the connections that init and each server function need. Each requirement offers the customer's existing connections that can fill it. Your organization supplies org-activated connections, so they're never offered to customers.

Your app keeps the customer's choices as a list of { key, id } selections, using each requirement's key exactly as the SDK gives it, and passes them when it calls init or a server function:

// A requirement from configuration.data.connections, and the option the customer chose
const [requirement] = configuration.data.connections.data.init;
await configuration.actions.init.execute({
connections: [{ key: requirement.key, id: selectedConnectionId }],
});

A requirement backed by a customer-activated connection can also create a new connection in one click. For OAuth 2.0 connections, this opens the provider's consent screen.

See Building configuration UIs with Solis React for the details.