Get started
Quickstart in five minutes
One command creates an agent with a payment rule. Then watch Immiscible send one payment to a person, allow it once they approve, and refuse a lookalike supplier on its own.
To see it work first, with no account and nothing leaving your machine, run npx immiscible try: an action allowed, a payment held for you to approve, a lookalike supplier denied, and the signed receipt verified. Then:
You need an Immiscible workspace (sign up at https://immiscible.fly.dev/signup, or run your own), Node 22.13 or later, and curl. Not an engineer? For finance teams is the same start without any code.
#1. Create the agent
In your agent’s project.
It signs you in through the browser, creates the agent and its rule, writes IMMISCIBLE_URL and IMMISCIBLE_AGENT_KEY to .env (and .env to .gitignore), prints the code for the SDK it finds, and ends with a live test call.
In a workspace with more than one owner, a new rule waits for a second owner to confirm it, and the end reads like this instead.
init then exits with code 10: the agent and its key are ready, and the rule takes effect once the other owner confirms it in the console. Run npx immiscible init again afterwards and the test call is made afresh under the confirmed rule.
The rule’s limits come from your workspace’s templates, so yours may differ. Every option, and what to do in CI, is in the CLI. Then load the two variables into your shell.
npx immiscible init --name "Invoice agent" --purpose pays_invoices✓ Created Invoice agent in Quayside
Rule: Pays up to £10,000 at a time and £50,000 a month. Payments above £5,000 need a person. A supplier it has not paid before needs a person.
✓ Added IMMISCIBLE_URL and IMMISCIBLE_AGENT_KEY in .env
✓ Governed by Immiscible: Invoice agent (Quayside)! Waiting for another owner to confirm the rule for Invoice agent (Quayside). Until then everything it asks for is refused.
The connection works: the test payment reached Immiscible and was refused, as it should be while the rule waits.set -a; . ./.env; set +a#2. Ask before paying
Before the agent pays, it says what it wants to do and what influenced it.
The agent has never paid this supplier, so a person decides.
With the SDKs, guard() (or pay() with a function) asks, waits for the person, runs your code only on allow, and settles: see the SDKs.
curl -X POST "$IMMISCIBLE_URL/v1/actions/authorize" \
-H "authorization: Bearer $IMMISCIBLE_AGENT_KEY" \
-H "content-type: application/json" \
-d '{
"type": "payment",
"summary": "Pay Acme Supplies invoice 0931",
"payment": {
"amount": 125000,
"currency": "GBP",
"merchant": {
"name": "Acme Supplies",
"domain": "acme-supplies.example"
}
},
"provenance": [
{
"source": "user",
"detail": "invoice approved in the finance inbox"
}
],
"idempotencyKey": "inv-0931"
}'// npm install @immiscible/sdk
import { Immiscible } from '@immiscible/sdk';
// Reads IMMISCIBLE_URL and IMMISCIBLE_AGENT_KEY.
const immiscible = new Immiscible();
const decision = await immiscible.pay({
amount: 125000, // minor units: £1,250.00
currency: 'GBP',
merchant: {
name: 'Acme Supplies',
domain: 'acme-supplies.example',
},
summary: 'Pay Acme Supplies invoice 0931',
provenance: [
{
source: 'user',
detail: 'invoice approved in the finance inbox',
},
],
idempotencyKey: 'inv-0931',
});# pip install immiscible
from immiscible import Immiscible
# Reads IMMISCIBLE_URL and IMMISCIBLE_AGENT_KEY.
immiscible = Immiscible()
decision = immiscible.pay(
125000, # minor units: £1,250.00
"GBP",
{
"name": "Acme Supplies",
"domain": "acme-supplies.example",
},
summary="Pay Acme Supplies invoice 0931",
provenance=[
{
"source": "user",
"detail": "invoice approved in the finance inbox",
},
],
idempotency_key="inv-0931",
){
"id": "act_b4811df1",
"decision": "approval_required",
"status": "pending_approval",
"reasons": [
"This agent has not paid acme-supplies.example before. A person must approve new merchants."
],
"mandateId": "mdt_31b2a52d",
"risk": {
"score": 20,
"signals": [
{
"id": "new_merchant",
"severity": "medium",
"effect": "approval",
"detail": "This agent has not paid acme-supplies.example before. A person must approve new merchants."
}
]
},
"approval": {
"id": "apr_aee8d38b",
"url": "https://immiscible.fly.dev/app/approvals/apr_aee8d38b",
"expiresAt": "2026-10-06T13:13:32.770Z"
},
"expiresAt": "2026-10-06T13:13:32.770Z"
}#3. Approve it
Open approval.url. You also get it by email, and in Slack or Teams if they are connected. Choose Approve. The agent polls until a person answers.
The receipt’s hum claim is true: a person approved this specific payment. Anyone can verify it without an account.
curl "$IMMISCIBLE_URL/v1/actions/act_b4811df1" \
-H "authorization: Bearer $IMMISCIBLE_AGENT_KEY"{
"id": "act_b4811df1",
"decision": "allow",
"status": "allowed",
"reasons": [
"Approved by sam@quayside.example.",
"This agent has not paid acme-supplies.example before. A person must approve new merchants."
],
"receipt": "eyJhbGciOiJFZERTQSIs..."
}#4. Report what happened
After paying, the agent settles the action, so the ledger holds the outcome and not only the permission.
Settling above the authorised amount is recorded as an incident and alerts the owner.
curl -X POST "$IMMISCIBLE_URL/v1/actions/act_b4811df1/settle" \
-H "authorization: Bearer $IMMISCIBLE_AGENT_KEY" \
-H "content-type: application/json" \
-d '{ "status": "completed", "amount": 125000 }'#5. Watch it ask, and refuse
Send the same request with "amount": 620000 (£6,200) and the idempotency key inv-0933. It is above the £5,000 line, so a person decides.
Now set the domain to acme-suppl1es.example (a one for the i), the source to email, and the key to inv-0934. Nobody is bothered.
A refusal is a 200 with a decision, not an HTTP error: see decisions. If the agent is an intern, a second payment to Acme also asks (tier_intern); it earns the right to pay alone on its record.
{
"decision": "approval_required",
"reasons": [
"£6,200.00 is above the £5,000.00 that Pays invoices lets the agent spend without asking."
]
}{
"decision": "deny",
"status": "denied",
"reasons": ["acme-suppl1es.example looks like acme-supplies.example but is not it."]
}#6. Find the kill switch
Agents, the agent, then Stop. From that moment every request from that agent is refused: its action requests are denied, ones already waiting for approval are cancelled, and its model calls and tool calls through Immiscible get 403 agent_stopped before anything is sent. Read the kill switch before you need it.
#Next
- Govern a coding agent: Claude Code and Cursor behind the MCP proxy and the hook.
- Decisions: every signal, and the order they combine in.
- The SDKs:
guard()wraps authorise, wait, run and settle in one call.
#Without the CLI
If you would rather not run npx, install an SDK directly (npm install @immiscible/sdk or pip install immiscible; both have no runtime dependencies) and do step 1 in the console instead. Open Agents and choose Add an agent:
- What does the agent do? Choose Pays invoices, and give it a name a person will recognise on their phone.
- Who approves? The person asked when the agent needs one. Choose Add the agent.
- Give this to whoever set up the agent. Open the agent’s setup page and choose Collect the agent’s key. The key is shown once.
An agent key can ask for permission and nothing else: it holds no card number, no personal data, and it cannot approve its own requests, widen a rule or lift a freeze. Set IMMISCIBLE_URL and IMMISCIBLE_AGENT_KEY yourself, then carry on from step 2.