> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omegas.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Handoff SDKs

> The Python and TypeScript clients, their honest installation state, and the patterns they make easy.

Both SDKs are MIT, deliberately thin, and dependency-free. Both assert
byte-identical serialization against every fixture in `spec/fixtures/` and
reproduce the signature vectors in `spec/signing.md`, including the negative
cases. Both prove, by killing a real subprocess mid-poll, that a client can die
at any point without losing an answer.

<Note>
  **Installation state, checked while writing this page** — neither SDK is on a
  public registry yet. `pip install handoff-human` and
  `npm install @handoffproto/sdk` do not resolve today; both installs below work
  from a checkout of
  [github.com/OmegaAgent/handoff](https://github.com/OmegaAgent/handoff). When
  the registry releases land, the package names will be the ones shown here.
</Note>

## Python: `handoff-human`

Standard library only. Version 0.2.0 in the repository.

```sh theme={null}
git clone https://github.com/OmegaAgent/handoff
pip install ./handoff/sdk/python
```

```python theme={null}
import handoff

handoff.configure(base_url="https://handoff.example.com/v1", api_key=...)

address = handoff.ask("Which shipping address should I use?")

outcome = handoff.approve("Refund $2,400 to Acme Corp?", mode="gated")
if outcome and outcome.redeem("stripe:refund:ch_1B").first_redemption:
    stripe.refund("ch_1B")
```

The durable-wait pattern (`raise_request`, `resume`, `receive`) is covered in
the [quickstart](/handoff/quickstart#4-survive-your-own-crash). Two details
that matter in production:

* `receive()` acks when its block completes. If the block raises, nothing is
  acked and the signal stays queued. Acking first and applying second would
  turn at-least-once delivery into at-most-once application, which is the
  exact bug the protocol exists to make impossible.
* To record that a decision arrived and could not be acted on, call
  `received.unable("the refund API was down")` inside the block. That is not
  an error; it is a fact worth keeping.

Callback verification is standard library too:

```python theme={null}
from handoff import verify_callback

result = verify_callback(request.headers, request.body_bytes, active_secrets=[...])
# result.delivery_id is your deduplication key
```

Pass the raw bytes as received; passing a `str` is refused rather than
silently encoded. Receipt-chain verification (`verify_receipt_chain`,
`verify_chain`) needs nothing outside the standard library; only the optional
detached Ed25519 layer needs the `cryptography` package.

If you are coming from the 0.1.x hackathon package: the module is now
`handoff`, and `import human` still works in 0.2.x with a deprecation warning.

## TypeScript: `@handoffproto/sdk`

Zero runtime dependencies, and no Node built-ins: hashing, HMAC and randomness
go through WebCrypto, so the same source runs on Node, Deno, Bun, and Workers.
The package ships TypeScript source, consumed directly by any runtime that
strips types (Node 22.18 or newer, Deno, Bun) or by any bundler.

```ts theme={null}
import * as handoff from "@handoffproto/sdk";

handoff.configure({ baseUrl: "https://handoff.example.com/v1", apiKey });

const address = await handoff.ask("Which shipping address should I use?");

const outcome = await handoff.approve("Refund $2,400 to Acme Corp?", { mode: "gated" });
if (outcome.approved && (await outcome.redeem("stripe:refund:ch_1B")).firstRedemption) {
  await stripe.refunds.create({ charge: "ch_1B" });
}
```

The durable wait mirrors Python:

```ts theme={null}
const waiter = await handoff.resume("run:0198f2a1");
await waiter.receive(async (received) => {
  await apply(received.values);   // the ack is sent after this resolves
});
```

Because hashing rides WebCrypto, `verifyCallback`, `verifyChain`, and `digest`
all return promises.

## Shared error model

Every error carries a stable `code` and raises or throws a class that mirrors
it: `AlreadyAnswered` (carrying the receipt id), `RequesterMayNotAnswer`,
`InsufficientAuthority`, `AuthorizationSpent`, `AnswerValidationFailed` (with
per-field detail), and the rest of the spec's §13. A code the SDK version does
not recognize surfaces as `HandoffProtocolError` with the code intact. Never
branch on `.message`; it is written for people and may change at any time.
