nara-agent pays a person from the agent's USDG in one call: it finds the runner, encrypts the recipient's details to them, locks the USDG with a single permit transaction, and waits. ESM, Node 18+ and modern browsers; it depends on viem and @noble/* only.
npm i https://usenara.cash/pkg/nara-agent-0.1.1.tgzcreateNaraAgent(options)#
| Option | Default | |
|---|---|---|
privateKey | required | The agent wallet's key. Kept in memory only: never logged or serialized. |
baseUrl | https://usenara.cash | Nara API. |
chain | "mainnet" | "mainnet" (Robinhood Chain 4663) or "local" (anvil, development). |
rpcUrl | the chain's public RPC | Must serve that chain. |
poll | { minMs: 2000, maxMs: 10000 } | Polling cadence. |
nara.payFiat(params)#
| Param | ||
|---|---|---|
rail | required | A rail id: paypal, zelle, revolut, venmo, sepa… |
to | required | The recipient as they gave it (email, phone, @handle, IBAN, or a fields object for US and UK banks). |
amountUsd | required | What the recipient receives, $1 to $1,000. Fees come on top. |
memo | The payment note the runner writes, ≤ 140 chars. Encrypted. | |
maxFeeBps | 300 | Highest runner fee you accept (max 500 = 5 %). |
deadlineMin | 60 | The runner's payment window once funded (15 to 2880). |
waitFor | "paid" | Resolve at "funded", "paid" or "released". |
autoRelease | false | Release as soon as the runner marks it paid (gives up the dispute window). |
onUpdate | (event) => void: created, assigned, secret_sent, funding, funded, paid, released, refunded… |
nara.startPayment(params) does the same but returns { job, done } as soon as the job exists, with the rest running in the background.
CashJob#
id, state, snapshot | The last state seen: costs, runner alias and track record, deadlines, every transaction (fund, paid, release…). |
status() | Fresh state from Nara. |
wait(state) | Until the job reaches that state. |
proof() | The runner's proof, decrypted with the agent key. |
release() | Pay the runner now (from funded or paid). Irreversible. |
dispute() | Within the 24 h window, from paid. |
finalize() / expire() | Anyone can: after the dispute window, or after the runner's deadline. |
cancel() | Before funding only. |
Also: nara.job(id), nara.jobs(), nara.resume(id) after a restart, nara.limits(), nara.rails(), nara.stats(), nara.balance() and nara.quote() (the worst-case cost before creating anything).
Errors#
| Code | Meaning | Locked? |
|---|---|---|
INSUFFICIENT_USDG | The wallet can't cover amount + max runner fee + Nara fee. | No |
INSUFFICIENT_GAS | No ETH on Robinhood Chain for the funding transaction. | No |
CAP_EXCEEDED | Above $1,000 per payment, $5,000 per agent per day, or 10 jobs in progress. | No |
NO_RUNNER | No runner accepted in time. The job is cancelled. | No |
RUNNER_TIMEOUT | The runner missed the deadline; the SDK refunded the escrow. | Refunded |
SERVER_MISMATCH | Nara answered something the SDK won't fund (a fee above your max, a signature that doesn't match). | No |
UNAUTHORIZED | Request signature refused: wrong key, or a clock more than 5 minutes off. |
The server can't redirect your money#
Before funding, the SDK checks that the job id is derived from your wallet and its salt, that the runner's per-job key signed its payout address and fee, that the fee is within your max, and that the escrow is Nara's, on your chain. Anything else, and it refuses to fund.