# SDKs

> Zero-dependency clients for JavaScript and Python. One call asks the gate, waits for a person if it must, runs your code only on allow, and settles the outcome.

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

Everything the SDKs do is plain HTTP, documented in the [API reference](https://immiscible.fly.dev/docs/api.md), so any language can call Immiscible directly. The SDKs exist to make the safe path the short one: `guard()` wraps the whole lifecycle so an agent cannot forget to wait or to settle.

| | JavaScript and TypeScript | Python |
|---|---|---|
| Package | `@immiscible/sdk`, source in [`packages/immiscible-js`](https://github.com/efr7-7/immiscible/tree/main/packages/immiscible-js) | `immiscible`, source in [`packages/immiscible-py`](https://github.com/efr7-7/immiscible/tree/main/packages/immiscible-py) |
| Runtime | Node 18 and later, Deno, Bun, browsers and edge runtimes; ESM and CommonJS | Python 3.9 and later, standard library only |
| Dependencies | none | none, including the Ed25519 receipt verifier |
| Types | bundled `.d.ts` | type hints |

## The lifecycle in one call

Node:

```ts
import { Immiscible, ImmiscibleDeniedError } from '@immiscible/sdk';

const gate = new Immiscible(); // reads IMMISCIBLE_AGENT_KEY and IMMISCIBLE_URL

try {
  await gate.pay(
    { amount: 4200, currency: 'GBP', merchant: 'grocer.example', provenance: [{ source: 'user' }] },
    (decision) => checkout(cart, decision.receipt), // runs only on allow
  );
} catch (err) {
  if (err instanceof ImmiscibleDeniedError) console.log(err.reasons, err.signals);
  else throw err;
}
```

Python:

```python
from immiscible import Immiscible, ImmiscibleDeniedError

gate = Immiscible()  # IMMISCIBLE_AGENT_KEY and IMMISCIBLE_URL from the environment

action = Immiscible.payment_action(4200, "GBP", "grocer.example", provenance=[{"source": "user"}])
try:
    with gate.guard(action) as decision:  # waits for a person if it must
        checkout(cart, receipt=decision.receipt)
except ImmiscibleDeniedError as e:
    print(e.reasons, e.signals)
```

That one call:

1. asks the gate whether this agent may pay (with an idempotency key, so a retry never authorises twice);
2. if a person must decide, waits, polling politely, and gives up with the approval link if nobody answers in time;
3. runs your code **only** on `allow`;
4. settles the action `completed`, or `failed` if your code threw.

If Immiscible cannot be reached, the call fails and your code never runs.

## Where it connects, and what it throws

Both SDKs talk to the hosted service, `https://immiscible.fly.dev`, unless you set `IMMISCIBLE_URL` (or pass `baseUrl` / `base_url`) to your own server; the CLI uses the same default.

HTTP failures arrive as a class you can catch by name, each still an `ImmiscibleError` carrying the server's `x-request-id`:

| Class | When | Carries |
|---|---|---|
| `ImmiscibleAuthenticationError` | 401: the key is missing, unknown or revoked | |
| `ImmiscibleInvalidRequestError` | 400 or 422 | `errors`, one entry per field |
| `ImmiscibleRateLimitError` | 429 | `retryAfter` (`retry_after`), in seconds |
| `ImmiscibleIdempotencyConflictError` | 409: the same idempotency key with a different body | |
| `ImmiscibleConnectionError` | the server could not be reached or did not answer in time | |

## Verifying receipts

Both SDKs include an offline receipt verifier that checks the Ed25519 signature against a JWKS you fetched and kept, with no network call. It is how a merchant or a downstream service checks that an action was allowed. See [verifying offline](https://immiscible.fly.dev/docs/security/verifying-offline.md#receipts) for what it checks.

## Without a server

Every example in the JavaScript package runs against a built-in fake, so you can write and test an integration before connecting anything:

```bash
cd packages/immiscible-js
node examples/shopping-agent.mjs --demo
node examples/merchant-checkout.mjs --demo
```

The Claude Code hook is its own package, `@immiscible/claude-code-hook`, in [`packages/immiscible-claude-code`](https://github.com/efr7-7/immiscible/tree/main/packages/immiscible-claude-code).
