Skip to content

Guides

Connect your customers’ workspaces, for agent platforms

For companies that build agent products. Register your product once, let each customer connect their Immiscible workspace with OAuth, then add an agent for them and ask before it acts. Their people approve in Slack or Teams, and every decision comes back signed.

If you build an agent product, your customers will want to know who approved what your agent did, and they will want that answer in the same place as every other agent they run. This guide connects your product to each customer’s Immiscible workspace. You ask before acting; the customer’s rules decide; their people approve when it matters; you get a signed receipt.

#How it fits together

  1. You register your product once, in your own Immiscible workspace.
  2. A customer presses Connect in your product. An owner or admin of their workspace reads what you ask for and allows it.
  3. You get a workspace token for that customer. You add an agent for them, with a rule from the same list their console uses.
  4. Before the agent acts, you ask. The answer is allow with a receipt, deny with reasons, or approval_required while a person at the customer decides.

#Register your product

In your own workspace, as an owner or admin: Settings, Connections, then For agent builders, Register a product. Give the name your customers will see on the consent page and your redirect URI. Redirect URIs must be https, or http on localhost while you build.

You get a client id (ipc_...) and a client secret (ips_...). The secret is shown once and only a hash of it is kept, so store it on your server. The same is available as an API for the console:

HTTP
POST /api/w/{your workspace id}/platform-apps
{ "name": "Acme Agents", "redirectUris": ["https://app.acme.example/immiscible/callback"], "homepage": "https://acme.example" }

Retiring the product (Retire, or DELETE /api/w/{id}/platform-apps/{clientId}) ends every customer connection it holds.

#The Connect button

Send the customer’s browser to the consent page with PKCE (S256 only) and the scopes you need:

HTTP
GET https://immiscible.fly.dev/oauth/platform/authorize
  ?response_type=code
  &client_id=ipc_...
  &redirect_uri=https://app.acme.example/immiscible/callback
  &scope=agents:read agents:write actions:write actions:read
  &code_challenge=BASE64URL(SHA256(verifier))
  &code_challenge_method=S256
  &state=...
ScopeWhat the customer reads on the consent pageWhat it opens
agents:readSee the agents it added to this workspaceGET /v1/platform/agents, GET /v1/platform/purposes
agents:writeAdd agents, each with a rule from the same list as Add an agentPOST /v1/platform/agents
actions:writeAsk for decisions as those agents, and report what happenedPOST /v1/actions/authorize, /settle, /callback
actions:readRead those decisions and their reasonsGET /v1/actions/{id}, /explain

Leave scope out to ask for all four. Only owners and admins can allow a connection, and they choose one workspace. The answer comes back to your redirect URI with code, your state and iss. If they decline, it carries error=access_denied. An unknown client or a redirect URI you did not register is never redirected to: the person sees an error page and nothing is shared.

#Swap the code for a token

HTTP
POST https://immiscible.fly.dev/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=...&code_verifier=...&redirect_uri=...&client_id=ipc_...&client_secret=ips_...

HTTP Basic with the client id and secret works too. The answer:

JSON
{
  "access_token": "ipt_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "ipr_...",
  "scope": "agents:read agents:write actions:write actions:read",
  "workspace": { "id": "ws_...", "name": "Northwind" }
}

The access token lasts an hour. Refresh with grant_type=refresh_token: each refresh token works once and comes back new. A code used twice, or a refresh token used after it was rotated, ends the whole connection, because it means someone else holds a copy.

#Add an agent for the customer

HTTP
POST https://immiscible.fly.dev/v1/platform/agents
Authorization: Bearer ipt_...

{ "name": "Invoice agent", "purpose": "pays_invoices", "vendor": "openai" }

GET /v1/platform/purposes lists the purposes, each with its rule in one sentence, as the customer’s workspace has set them. The agent acts for the person who allowed the connection. Sending the same name again returns the same agent ("reused": true). In a workspace with more than one owner, a rule for that person’s own agent waits for another owner to confirm; the answer says so under pending, and the agent can act once they do.

#Ask before acting

With the JS SDK, pass the workspace token and the agent:

JavaScript
import { Immiscible } from '@immiscible/sdk';

const immiscible = new Immiscible({ apiKey: workspaceToken, agentId: 'agt_...' });
const d = await immiscible.authorize({
  type: 'payment',
  summary: 'Pay Northwind invoice 2231',
  payment: { amount: 1840000, currency: 'GBP', merchant: { name: 'Northwind', domain: 'northwind.example' } },
  provenance: [{ source: 'email', detail: 'Supplier email, 3 Oct' }],
});
if (d.decision === 'approval_required') await immiscible.waitForDecision(d.id);

Over HTTP it is the ordinary decision API with one extra header:

HTTP
POST https://immiscible.fly.dev/v1/actions/authorize
Authorization: Bearer ipt_...
immiscible-agent: agt_...

The token can act only as agents your product added to that workspace; any other agent id answers 403 agent_not_yours. Every decision is recorded against the agent, exactly as it would be with an agent key, so the customer’s rules, approvals, freeze switch and receipts all apply. To be called when a person decides instead of polling, use POST /v1/actions/{id}/callback. Report the outcome with POST /v1/actions/{id}/settle.

#When a connection ends

A request with an ended connection answers 401 with invalid_token and a reason you can show the customer. A connection ends when:

  • an owner or admin disconnects it under Settings, Connections, Agent platforms;
  • you revoke a token at POST /oauth/revoke with your client credentials;
  • you retire your product;
  • the person who allowed it leaves the workspace, or loses the role to manage agents.

The agents you added stay in the customer’s workspace with their rules and records. Connecting again picks them up.

Each connection and disconnection is written to the workspace’s signed record.

#Without the Connect button

If you are not ready to build it, the customer can add the agent themselves, with npx immiscible init or under Agents in the console, and give you the agent key (ask_...). Every call above then works with that key and no immiscible-agent header. It is one key per agent, and the customer revokes it under Agents.

#What is not built yet

  • An approval card to embed inside your product. Approvals reach people in Slack, Microsoft Teams, email, the phone app and the console.
  • White-labelling for a platform. A self-hosted deployment carries its own name and colours from brand.config.json; the hosted service is Immiscible.
  • Published platform pricing. We never take a share of what agents spend. Write to us if you are building this in.