# SDKs

The TypeScript SDK @enruta/sdk and the Python SDK enruta, both 0.3.0: the same API for agents, sellers and platforms, webhook signature verification and typed errors.

Status: Live · Updated: 2026-09-14
Source: https://enruta.ai/docs/sdks

Two SDKs cover the whole REST API: `@enruta/sdk` for TypeScript and JavaScript, and `enruta` for Python. Both are version 0.3.0, depend on nothing beyond the platform’s own `fetch` or standard library, and expose the same resources for agents, sellers and platforms.

**TypeScript**

```bash
npm i @enruta/sdk
```

```ts
import { Enruta } from "@enruta/sdk";

const enruta = new Enruta(process.env.ENRUTA_AGENT_KEY!);

const r = await enruta.paymentRequests.create({
  payee: { name: "Ream & Rule", domain: "reamandrule.com" },
  amount: { value: "24.98", currency: "USD" },
  purpose: "2 packs of ballpoint pens for the office",
  checkout: { protocol: "ucp", reference: "chk_01M2511XQ4N7B8T2R6H9K3W5MD" },
  intent: { text: "Order two packs of pens, keep it under $30" },
}, { idempotencyKey: "a3c9…" });

// allow → pay with r.mandate; clarify → ask the person r.decision.questions, then clarifications.answer
```

Runs wherever `fetch` exists: Node 18 or later, Deno, Bun, Cloudflare Workers, Vercel Edge.

**Python**

```bash
pip install enruta
```

```python
import os
from enruta import Enruta

enruta = Enruta(os.environ["ENRUTA_AGENT_KEY"])

# a platform key acting for one connected account
customer = Enruta(os.environ["ENRUTA_PLATFORM_KEY"], account="cust_48121", agent="agt_01M3D2…")
```

Python 3.10 or later, standard library only (`urllib`, `json`, `hmac`). The resources are the same as in TypeScript, in snake case: `payment_requests` (with `hand_off` and `pay_resource(request_id, url, method=None, headers=None, body=None)`), `clarifications`, `merchants`, `economics`, `recurrences`, `approval_delegations`, `storefronts`, `sellers`, `platform` and the rest.

## Resources

| Area | TypeScript | For |
|---|---|---|
| Paying | `paymentRequests` (with `handOff` and `payResource`), `clarifications.answer`, `merchants.identity` | Ask to pay, hand a checkout to a person, pay an x402 or MPP resource, answer questions, check a merchant first |
| Identity | `attestations.nonce`, `attestations.create`, `attestations.list`, `agents.identity` | Anchors and the assurance level |
| Money split | `economics.forPayment`, `sellers.orders.economics` | Who paid, received and earned on one payment |
| Repeated payments | `recurrences.*` | Create, list, pause, resume or end a recurrence |
| After the payment | `reversals.*`, `settlements.get`, `reconciliation.*`, `disputes.*`, `records.*`, `exports.create` | Refunds, settlement, the weekly report, dispute packets, records |
| Controls | `consents.*`, `agents.pause`, `agents.resume`, `approvalDelegations.*`, `org.settings`, `org.verification` | The user’s controls and the organization |
| Billing | `billing.*` | The plan, usage and invoices |
| Sellers | `sellers.orders.*`, `sellers.verify`, `sellers.adjustments.report`, `sellers.reversals.request`, `sellers.fulfillments.confirm`, `sellers.pspConnections.*`, `sellers.conformance.*`, `sellers.domains.*`, `storefronts.*` | Agent orders, refunds, PSP connections, storefronts |
| Platforms | `platform.accounts.*`, `platform.feeds.*`, `platform.brand`, `platform.usage` | Connected accounts, feeds, the verifier’s brand, usage |

## Acting for an account

With a platform key, `new Enruta(key, { platform: { account, agent } })` or `enruta.forAccount(account, { agent })` in TypeScript, and `Enruta(key, account=…, agent=…)` in Python, add `Enruta-Account` (and `Enruta-Agent`) to every call, so the same resources act for one of your connected accounts ([Platform accounts](https://enruta.ai/docs/platform-accounts)).

## Webhooks

Both SDKs verify a delivery’s `Enruta-Signature: t=<unix>,v1=<HMAC-SHA256>` header over the timestamp and the raw body with your endpoint’s secret, for webhooks and for [feeds](https://enruta.ai/docs/dispute-field-feeds) alike.

## Errors

An answer outside 2xx raises a typed error carrying the HTTP status, the error `code` (such as `invalid_request`, `forbidden`, `idempotency_conflict`, `rate_limited` or `account_not_connected`), the request id and, on a 429, the seconds to wait. A denied payment is not an error: it is a decision with `result: "deny"`, and it has a record.
