Integrations
Stackbone Connect is how your agents and workflows reach the outside world, in both directions. An operator connects a provider once in Studio, and the credential stays in the broker inside your box. Your code names the connector and an operation; the broker mints a short-lived token and makes the call, so no key ever enters your container. When something happens on the provider's side, a new email say, a trigger starts a durable workflow run with it.
| Direction | What happens | Where you set it up |
|---|---|---|
| Outbound | A tool or a workflow step calls a provider: send a message, read a thread. | Connections in Studio, then one line in your code. |
| Inbound | A provider event starts a workflow as a durable run. | Triggers in Studio. No code, unless the mapping needs it. |
your code ──names connector + operation──▶ the broker, inside your box
├─ holds the real credential
├─ mints a short-lived scoped token
└─ makes the call, returns the outputConnect a provider
Open Connections under Integrations and click Register integration. The catalog lists the providers Stackbone knows, and a Custom integration for any other service that speaks OpenAPI.
The catalog. Custom integration covers any OpenAPI service you set up by hand.
Pick one and the technical fields come pre-filled from the catalog. You add the credential: the API key, or the OAuth client id and secret. It is write-only: the broker encrypts it and never reads it back to the screen. For a custom integration you also paste the provider's OpenAPI document (JSON or YAML), its base URL, the auth mode, the scopes and where the token travels. Registering the same integration again rotates its credential in place.
A key-based provider is ready as soon as you register it. An OAuth one waits for an account.
Each row says what still needs doing. A provider that authenticates with a key is Ready on registration. An OAuth provider shows No account until you click Connect account and finish the provider's sign-in; that grant is the account the broker acts as. Open Operations on a row to see what the provider exposes, read live from its OpenAPI document. Those operation ids are what your code names.
What the Gmail integration exposes. The id under each path is what your code calls.
Registering, editing and removing an integration takes the owner or admin
role: a connection holds a customer credential, so it follows the same gate as
secrets.
Call it from your code
After you connect a provider, your code reaches it by name. There is no client to build and nothing to import from the provider.
| You are in | Use | Import from |
|---|---|---|
| An agent's tool list | connectorTool({ connector, operation }) |
@stackbone/sdk/deep |
| A tool body or a step | stackbone.connection(id).call(op, args) |
@stackbone/sdk |
| A workflow step, explicit | callConnector(id, op, args) |
@stackbone/sdk/workflow |
The shortest form gives an agent a provider operation as a tool. The model sees a tool named after the connector and the operation; the body runs through the broker:
import { z } from 'zod';
import { defineDeepAgent, connectorTool } from '@stackbone/sdk/deep';
export default defineDeepAgent({
name: 'support',
model: 'openai/gpt-4o-mini',
tools: [
connectorTool({
connector: 'telegram',
operation: 'send-message',
description: 'Send a Telegram message to a chat.',
schema: z.object({ chat_id: z.string(), text: z.string() }),
}),
],
});The agent's own instruction — "reply to customers on Telegram" — is a prompt in the catalogue you write in Studio, not a string in this file. See Prompts.
From a workflow step, call the operation directly:
import { callConnector } from '@stackbone/sdk/workflow';
async function notify(chatId: string, text: string) {
'use step';
await callConnector('telegram', 'send-message', { chat_id: chatId, text });
}While stackbone dev runs, it reads each connector's document and writes typed
operation maps into .stackbone/connect.d.ts, so
stackbone.connection('gmail').call(...) autocompletes its operations and
their arguments.
A call fails with a coded error rather than a token: for
example connector_installation_required when the provider was never
connected. The stackbone.connection page
covers every call form, the principal a call is made as, and the error codes.
Receive events
A trigger link binds one provider event to one workflow. Open Triggers under Integrations and click New link: pick the integration (one whose catalog entry fires a trigger), the trigger, the workflow to start, and the credential to poll with, the shared account by default. A new link starts switched off, so nothing runs until you have mapped the event.
Map the event onto the workflow's input. The event has its own fields
(from, subject, threadId); the workflow declares its own input schema.
The mapping editor puts them side by side. Click a field on the left, then the
field it should fill, or drag between their dots. One event field can feed
several inputs. Transforms (a regex, a template, a default, a date format,
JSON parse, a free expression) sit between the two when a value needs
shaping. Auto-map asks the workspace's model for a first draft, drawn
dotted until you accept each line. Code shows the same mapping as one
expression you can edit by hand.
Two lines drawn, one required field still to fill. The link stays off until it is complete.
The editor names any required input still unmapped. Test against the sample event runs the mapping over a representative event from the catalog and shows what the workflow would receive. Save mapping keeps the polling cursor, so events already processed are not replayed. Then Turn on.
From then on the box polls the provider on the trigger's own interval (30 seconds by default) and starts one durable run per new event. Those runs sit in Runs with the others, and View runs on the link filters to them. The polling itself is one of the box's recurring jobs, so its cadence and last result are on that screen too.
The Triggers list: each link with its state, its mapping and its last poll.
Resume a run instead of starting a new one
By default every event starts a new run. A workflow that emailed someone and
paused for their reply wants the reply to wake that run. Wire the event
field that identifies the conversation, the threadId in the mapping above,
to the correlation key, and have the paused workflow park its hook on that
same value (see waiting for a reply).
When an event arrives, a run parked on that key resumes with the state it built
up, and the delivery says resumed; with no run parked, a new run starts and
the delivery says started. If every delivery says started when you expected
replies to continue a conversation, the two sides are not landing on the same
value.
Try the path without a real account
Start the workflow the event would start, with your own input:
stackbone workflows start refund --input '{ "orderId": "A-1042", "amount": 89.9 }'How it stays safe
- The broker runs inside your box. It encrypts credentials there, and they never reach the control plane or your agent code.
- The runtime signs every call your code makes, and mints a token for that one call and that one connector.
- A connector call outside the runtime (
stackbone devor a deployed box) fails with an error saying so, because nothing outside it can sign a call. - The box refreshes the token an OAuth provider handed over, on its own timer, another of the recurring jobs, so a long-lived trigger keeps polling.
- A call made while the runtime is running your code carries the id of the run
that made it. You pass nothing, and a connector call traces back to the run
that issued it. See
stackbone.connection.
Read more
stackbone.connection: every way to call a connector from code, principals, and the error codes.- Human-in-the-loop: parking a workflow on a key so a reply resumes it.
- Recurring jobs: the polling and the token refresh, on the screen that lists every timer.
stackbone workflows start: start a workflow by hand with the input an event would carry.