# 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.

Source: https://immiscible.fly.dev/docs/quickstart

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](https://immiscible.fly.dev/signup), or [run your own](https://immiscible.fly.dev/docs/guides/deploy-and-backups.md)), Node 22.13 or later, and `curl`. Not an engineer? [For finance teams](https://immiscible.fly.dev/docs/finance.md) is the same start without any code.

## 1. Create the agent

In your agent's project:

```bash
npx immiscible init --name "Invoice agent" --purpose pays_invoices
```

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:

```text
✓ 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)
```

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:

```text
! 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.
```

`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](https://immiscible.fly.dev/docs/cli.md). Then load the two variables into your shell:

```bash
set -a; . ./.env; set +a
```

> **Note**
> A new agent starts at your workspace's starting tier. That is **intern** unless an owner has chosen **junior** in the console, and an intern has a person sign off its payments until it has a record of good decisions. Expect an intern's first calls to ask; that is the system working. See [autonomy tiers](https://immiscible.fly.dev/docs/concepts/autonomy-tiers.md).

## 2. Ask before paying

Before the agent pays, it says what it wants to do and what influenced it:

curl:

```bash
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"
  }'
```

Node:

```ts
// 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',
});
```

Python:

```python
# 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",
)
```

The agent has never paid this supplier, so a person decides:

```json
{
  "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"
}
```

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](https://immiscible.fly.dev/docs/sdks.md).

## 3. Approve it

Open `approval.url`. You also get it by email, and in [Slack or Teams](https://immiscible.fly.dev/docs/guides/approvals-in-chat.md) if they are connected. Choose **Approve**. The agent polls until a person answers:

```bash
curl "$IMMISCIBLE_URL/v1/actions/act_b4811df1" \
  -H "authorization: Bearer $IMMISCIBLE_AGENT_KEY"
```

```json
{
  "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..."
}
```

The receipt's `hum` claim is `true`: a person approved this specific payment. Anyone can [verify it](https://immiscible.fly.dev/docs/concepts/receipts.md) without an account.

## 4. Report what happened

After paying, the agent settles the action, so the ledger holds the outcome and not only the permission:

```bash
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 }'
```

Settling above the authorised amount is recorded as an incident and alerts the owner.

## 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:

```json
{
  "decision": "approval_required",
  "reasons": [
    "£6,200.00 is above the £5,000.00 that Pays invoices lets the agent spend without asking."
  ]
}
```

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:

```json
{
  "decision": "deny",
  "status": "denied",
  "reasons": ["acme-suppl1es.example looks like acme-supplies.example but is not it."]
}
```

A refusal is a `200` with a decision, not an HTTP error: see [decisions](https://immiscible.fly.dev/docs/concepts/decisions.md). 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.

## 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](https://immiscible.fly.dev/docs/guides/kill-switch.md) before you need it.

## Next

- [Govern a coding agent](https://immiscible.fly.dev/docs/guides/mcp-proxy.md): Claude Code and Cursor behind the MCP proxy and the hook.
- [Decisions](https://immiscible.fly.dev/docs/concepts/decisions.md): every signal, and the order they combine in.
- [The SDKs](https://immiscible.fly.dev/docs/sdks.md): `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**:

1. **What does the agent do?** Choose **Pays invoices**, and give it a name a person will recognise on their phone.
2. **Who approves?** The person asked when the agent needs one. Choose **Add the agent**.
3. **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.
