Install
npm install neltava
Zero dependencies; Node 18+, Bun, Deno and edge runtimes. The client reads NELTAVA_AGENT_KEY and NELTAVA_API_URL from the environment when you don’t pass them. Use the agent’s own key — it can only ask for decisions as that agent.
Authorize a spend
One call, right before the agent pays:
import { Neltava } from "neltava";
const neltava = new Neltava(); // NELTAVA_AGENT_KEY
const d = await neltava.authorize({
task: "Q4 pipeline: enterprise developer leads",
action: {
merchant: "Platform Weekly",
amount: 90.00,
currency: "USD",
description: "Sponsored slot reaching platform engineers at enterprise companies",
},
});
if (d.proceed) {
// pay d.amount — in Enforce, through your payment adapter with d.capability
}| Parameter | |
|---|---|
action.merchant | Who would be paid. Required. |
action.amount / action.amountMinor | Major units (49.99) or exact minor units (4999). Required. |
action.currency | ISO-4217. Required. |
action.description | What, and why it serves the task. |
action.type | purchase (default), subscription, top_up… |
action.payeeId | The payee’s id at your rail (domain:clearbit.com, stripe:acct_…). |
action.category | A declared category — recorded as a claim, never trusted to allow. |
task | What the agent is working on in this run. |
limitable | May Neltava cap the amount instead of sending it to a person? Then over-limit spends come back as LIMIT. |
minAmountMinor | With limitable: below this, a capped spend isn’t useful — send it to a person instead. |
agent | Only with a non-agent key: the agent’s slug. |
idempotencyKey | Retries with the same key return the same decision. Generated if omitted. |
The decision
| Field | |
|---|---|
proceed | Go ahead: effective ALLOW or LIMIT. In Shadow Mode, always true. |
needsReview | A person must decide (effective REVIEW, Enforce only). |
amount / amountMinor | What may be spent — the capped amount on LIMIT; null when it may not. |
decision / effectiveDecision | Neltava’s verdict / what applies now. |
reasonCode, reasonCodes | Stable codes to branch on — all of them. |
explanation | A sentence for people. Don’t parse it. |
mode | SHADOW or ENFORCE. |
decisionId | The record in your console; use it to report the outcome. |
capability | Enforce: the single-use token for this payment. Null in Shadow Mode. |
source | neltava, or fallback when onUnavailable decided. |
Attach the steps
Purpose checks are sharper with the agent’s own account of how it got here. A run collects steps (kinds: search, evaluate, select, tool_call, purchase, other) and sends them with the request. They are evidence, never instructions.
const run = neltava.run("Find enterprise developer leads");
run
.step("Compared three data vendors", "evaluate")
.step("Picked Clearbit", "select", "vendor_search");
const d = await run.authorize({
action: { merchant: "Clearbit", amount: 49, currency: "USD", description: "Enrichment credits" },
});Report outcomes
After paying — or failing to — report what happened, so budgets and the report reflect real spend:
await neltava.reportOutcome(d.decisionId!, {
type: "SPEND_EXECUTED", // SPEND_FAILED · SPEND_CANCELLED · REFUNDED · PARTIALLY_REFUNDED
amount: 49,
currency: "USD",
externalReference: charge.id, // makes a retried report idempotent
});An agent’s report is recorded as CLIENT_REPORTED. It never releases reserved budget; a payment adapter’s PAYMENT_RAIL report does. See outcomes and budget.
When Neltava can’t be reached
Requests retry network errors, timeouts, 429 and 5xx with the same idempotency key, so a retry never creates a second decision. After that, onUnavailable decides:
| onUnavailable | |
|---|---|
"throw" (default) | Throws NeltavaUnavailableError — you decide. |
"review" | Treated as needing a person; does not proceed. |
"deny" | Does not proceed. |
"allow" | Proceeds only while Neltava has confirmed, in this process, that the agent is in Shadow Mode. In Enforce, when the mode isn’t known yet, or on a rate limit, it acts as "review". There is no fail-open in Enforce. |
A response that doesn’t look like a decision is treated as unavailable — it never lets money move. Fallback decisions carry source: "fallback" and reasonCode: "NELTAVA_UNAVAILABLE".
Errors
import { Neltava, NeltavaError, NeltavaUnavailableError } from "neltava";
try {
const d = await neltava.authorize({ action });
} catch (err) {
if (err instanceof NeltavaUnavailableError) {
// network, timeout, 5xx or 429 after retries (err.rateLimited on 429)
} else if (err instanceof NeltavaError) {
// the API refused the request: err.status, err.code, err.requestId
}
}Error codes are listed in the HTTP API reference.
Options
| new Neltava({…}) | Default |
|---|---|
apiKey | NELTAVA_AGENT_KEY (or NELTAVA_API_KEY) |
baseUrl | NELTAVA_API_URL or https://api.neltava.com |
timeoutMs | 10000 |
maxRetries | 2 |
onUnavailable | "throw" |
fetch | the global fetch |
amount follows the currency’s minor unit (USD 49.99, JPY 5000, KWD 1.250); pass amountMinor for exact integers.
More methods
neltava.authority(slug)
The agent’s current authority and its version — for an agent that plans within its limits.
neltava.agent(slug)
The agent’s record: status, mode, current authority version.
neltava.consumeCapability(…)
For your payment service, with a Payment adapter key — never the agent’s: spends the capability for the exact payment you’re about to make, or throws (capability_used, payee_mismatch, amount_exceeds_capability…). See Enforce at the payment point.