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 hands it to a workflow or an agent.

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 run or an agent turn. 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 output

Connect a provider

Open Connections under Integrations and click Register connector. 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.

What ships in the catalog

Several hundred providers come with the platform, each one served by its Activepieces piece. The piece declares its operations, its authentication and its events, so you only add the credential. A box lists these seven by default:

Connector Authentication Starts a workflow when
Gmail OAuth Any new message, sent mail included
Outlook OAuth, or client credentials A message arrives in the Inbox
Google Drive OAuth A file or folder is created in a folder
GitHub OAuth, GitHub App or token A push, a new commit, an issue or pull request changes
Linear API key An issue is created, changed or removed, a comment is added
Supabase Project URL and API key A row is inserted, through a webhook
Stripe Secret API key A charge, refund, payment, invoice, dispute or subscription event

Trigger events lists what each of their triggers delivers.

Outlook is one connector for both ways of signing in to Microsoft: a person's mailbox through OAuth, or an app reading a mailbox you name through client credentials. The mailbox belongs to the connection, so a trigger link on it watches that mailbox's Inbox.

Anything else is a Custom integration: you point it at the provider's OpenAPI document and it gets the same treatment, operations included. The list grows, so the dialog in Studio is the current answer, not this table.

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 from its catalog entry, or live from the OpenAPI document of a custom integration. Your code names those operation ids.

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:

deep-agents/support/index.ts
import { z } from 'zod';
import { defineDeepAgent, connectorTool } from '@stackbone/sdk/deep';

export default defineDeepAgent({
  name: 'support',
  model: 'openai/gpt-4o-mini',
  tools: [
    connectorTool({
      connector: 'stripe',
      operation: 'retrieve_charge',
      description: 'Look up a Stripe charge by its id (ch_...).',
      schema: z.object({ charge_id: z.string() }),
    }),
  ],
});

The agent's own instruction ("check the charge before you answer") is a prompt in the catalogue. You write it in Studio, not in this file. See Prompts.

From a workflow step, call the operation directly:

workflows/notify.workflow.ts
import { callConnector } from '@stackbone/sdk/workflow';

async function notify(to: string, text: string) {
  'use step';
  await callConnector('gmail', 'send_email', {
    receiver: [to],
    subject: 'Your order',
    body_type: 'plain_text',
    body: text,
    draft: false,
  });
}

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 a workflow or an agent. You build it in Triggers under Integrations in Studio: pick the trigger and the destination, map the event onto the destination's input, then turn it on. From then on the box polls the provider, 30 seconds apart by default, and hands each new event to the destination.

Triggers walks through the screen: the dialog, the mapping editor, Test, and the key that ties a reply to the run or the conversation it belongs to.

What counts as a new event

A poll asks the provider what changed since last time, so what starts a run is whatever the provider's own dates say moved. Google Drive's New File fires for a file created after the last poll, which is a file uploaded into the folder. A file moved in from elsewhere, or one already there and only edited, starts nothing. New or Updated File also fires when a file the link has never delivered is edited: a document that was already sitting there when you armed the link, for instance.

Either fires once per file, though. Once a file id has been delivered, later edits to it start no further run.

The event carries both of Drive's dates, so the workflow can tell which of the two happened. Read the gap between them, not just their order:

What you see What happened
modifiedTime before createdTime Someone uploaded a document written earlier. Drive keeps the source file's own date.
The two within a few seconds The file was created in the folder. A workflow that files through this connector lands here: one upload carries the name, the parent and the bytes.
modifiedTime clearly later The file was edited where it already sat.

Delivery is at-least-once per item, keyed on the provider's own id — for Drive, the file id. Three consequences worth knowing before you design the workflow:

  • A document dropped in the folder starts one run, however old the document is. Drive keeps the source file's own modified date on upload, so a contract last edited in March arrives stamped March. New File asks on the arrival date, which is what makes it fire anyway.
  • Editing that same file again does not start a second run. The delivery is already claimed under that file id. A re-uploaded recording does start one, because a re-upload is a new file in Drive with a new id — not a new date on the old one.
  • A file your own workflow writes into the folder it watches is an arrival like any other, so it fires the trigger and the run writes again. Watch one folder and write to a different one, or filter your own output out by name or mime type.

Under New or Updated File, a Google Doc in a folder people work in is chatty for the same reason: its modified date moves on every collaborator's keystroke. That costs listings, not runs, since every one of those polls dedups to the same file id.

Resume a run instead of starting a new one

By default every event starts a new run. When a workflow emailed someone and paused for their reply, you want that reply to wake that run. Wire the event field that identifies the conversation, such as Gmail's threadId, to the resume key on the link's Correlation card (Triggers shows where), 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 dev or 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 it still traces back to that run. See stackbone.connection.

Read more

BUILT WITH ❤️ FROM CANADA AND SPAIN