NewIntroducing Integrations

Anlyon vs building it yourself

You can build shared limits, exact approvals, credential custody and outcome receipts for your agents yourself. The approve button takes an afternoon. The work is in the failure windows behind it: two agents reserving the same last $100, a retry that refunds twice, a timeout nobody can classify, and an approval that no longer matches what runs.

The short answer

Build it yourself when one agent runs in one process, the person approving is the developer at the keyboard, and the provider you call already offers idempotency and a way to look up what happened. A framework approval switch and a database row are enough there.

Use Anlyon when several agents, sessions or keys must draw from one allowance, when someone other than the requester approves, when the agent must not hold the provider key, or when you need a record of what the provider did, not what your code believed.

Either way, the sections below are the specification. Use them as a checklist even if you never use Anlyon.

Five jobs, and where each one breaks

Each job in the order it bites: what the first version looks like, what breaks it, what has to hold, and what Anlyon does, with its limits.

How hard is a shared limit across agents?

The first version

Read how much is left, compare it with the amount, call the provider, then add the amount to a counter.

What breaks it

Check, then act
Two agents read $100 left at the same moment. Both see room for an $80 refund and both dispatch. The counter ends at $160 against a $100 limit, and every single-agent test passed.
An allowance per session
A limit held in the agent's memory, or a counter keyed to one API key, starts again with every new session or key. Adding an agent adds an allowance.
Releasing on a timeout
The call times out, so the code releases the reservation. The refund did land. It no longer counts, and the next one goes through.

What has to hold

An atomic reservation before dispatch, in one store every agent shares, settled from the outcome and still held while the outcome is unknown.

What Anlyon does

Every agent, session and key in the environment draws from the same limit, reserved before dispatch. A new session or key does not get a new allowance. A limit counts money or a unit you declare, such as emails, and an unknown outcome keeps its allowance held. The limit is shared inside one environment, not across environments, and it covers governed actions routed through Anlyon. A call your own code makes directly is not bounded by it.

limit.ts
// The version that passes every single-agent test.
const { remaining } = await db.one('select remaining from limits where id = $1', [limitId]);
if (amount > remaining) throw new Error('over the limit');
await stripe.refunds.create({ payment_intent: pi, amount }); // two agents both get here
await db.none('update limits set remaining = remaining - $1 where id = $2', [amount, limitId]);

// The shape that holds: reserve atomically before dispatch, settle after.
const claim = await db.oneOrNone(
  'update limits set reserved = reserved + $1 where id = $2 and consumed + reserved + $1 <= amount returning id',
  [amount, limitId],
);
if (!claim) throw new Error('refused before dispatch');
// Dispatch. On success, move the amount from reserved to consumed.
// On a lost response, keep it reserved until you know what happened.

What does it take to bind an approval to the exact request?

The first version

Store a pending row, show the reviewer what the model wrote, flip a boolean, then run the tool.

What breaks it

The reviewer approves a summary
The model writes "Refund $12.50 for order 4411". The arguments say 125000 cents. The approver reads the sentence, not the request.
The request changes after review
The agent retries with different arguments under the same approval id, or someone edits the tool definition while the approval waits.
The requester approves itself
The agent's key can call your approve endpoint, because one service account does both.
The callback arrives twice
A double-clicked button or a retried webhook marks the approval twice, and each one dispatches.

What has to hold

Persist the canonical request, show the reviewer that request, recompute a digest at dispatch and refuse on any difference, keep the permission to decide apart from the permission to ask, and make approve-then-dispatch happen once.

What Anlyon does

The approval is bound to a SHA-256 digest of the request, target, versions, preconditions and expiry. Anlyon recomputes it before dispatch and refuses on any difference. A changed request sent with a reviewed preview is refused with PREVIEW_MISMATCH. The credential that requested an approval can never be the one that decides it. The approval binds the exact request, not atomic provider state: Stripe has no compare-and-set, so the refundable amount a reviewer sees is as of review. Any workspace member can decide an approval today. There are no approver groups.

Where should the provider's API key live?

The first version

Put the Stripe key in an environment variable the agent's process reads, and trust the tool code to call only what it should.

What breaks it

The process holds the key
Whatever the agent's process can read, a prompt injection or a buggy tool can reach. A gate inside that process is skipped by any code path that calls the provider directly.
An input picks the host
The URL is built from model input, so the key meant for one API is sent to another.
Rotation is per deployment
Every service that copied the key has to be found and redeployed when it changes.

What has to hold

A separate service that holds the key, accepts named operations with validated input, binds each secret to its destination host, and never returns the value.

What Anlyon does

An action references {{secret:NAME}} and Anlyon resolves it at dispatch. The secret is bound to a destination and checked again at dispatch. An action that carries a secret has a fixed host, so an input cannot choose where the credential is sent. The vault is write-only: no API returns a secret's value. This covers credentials in the vault, not a key your own process already holds.

What should happen when the provider never answers?

The first version

Wrap the call in a try block, retry on a timeout with backoff, and log success or failure.

What breaks it

A timeout is not a failure
The request may have landed. Retrying a refund on a timeout is how one refund becomes two.
Your idempotency layer races
Reading a cached result and then running the handler lets two concurrent requests with the same key both run. We shipped that bug ourselves, and the write-up is linked below.
Keys expire
Stripe's API reference says API v1 idempotency keys can be removed once they are at least 24 hours old, and a key reused after that makes a new request. A retry the next morning is a second refund.

Checked 2026-10-18 against Stripe idempotent requests.

Some APIs have neither idempotency nor a lookup
Then "did it happen?" needs a read-back you design for each provider, and an operator to decide when the read-back finds nothing.

What has to hold

An operation identity saved before dispatch, a third state beside success and failure, a reconciler that reads the provider's own record and never writes, and an operator path for what it cannot settle.

What Anlyon does

When a provider's response is lost, Anlyon records the outcome as unknown, never failed, does not send the request again, and settles it from the provider's own record. Not finding the operation proves nothing: it stays unknown, with its allowance held, until a replay or an operator resolves it. Reconciliation stops after 12 attempts. A declared action has no provider idempotency and no replay of a lost write.

What does the audit record need to say?

The first version

Log the tool name, the arguments and whether the call returned 200.

What breaks it

Logs record what your code believed
They do not say who approved which exact request under which policy version, or what the provider's own record said afterwards. When a customer asks whether the refund happened and who allowed it, you join three systems by timestamp.
Later evidence overwrites earlier evidence
A status column updated in place loses the timeout that came before the success, which is the part an incident review needs.

What has to hold

One record per operation: the reviewed request, the decider, the policy version, each dispatch, the provider's evidence, and an append-only trail of what was learned later.

What Anlyon does

Every decision records the decider, the timestamp, the note and the governing policy version. Every action returns a receipt with a grade. Confirmed means a read-back matched. Acknowledged means the provider accepted the request and nothing read it back. Unknown means no usable response, and Anlyon does not send it again. It stays unknown until a read-back or an operator settles it. Failed, refused and denied mean it did not run. Evidence on an effect is append-only. A receipt is a database record with provider evidence. It is not signed and not tamper-evident.

The same five jobs, side by side

What you build and run, what Anlyon does instead, and what stays your job either way.

Building agent limits, approvals, credential custody and receipts yourself, compared with Anlyon
JobBuilt yourselfWith AnlyonStill yours with Anlyon
Shared limitA ledger with an atomic reservation, settlement on each outcome, and a hold for unknown outcomes.One limit per environment, in money or a declared unit, reserved before dispatch.Choosing the amounts. Routing every write through Anlyon, because a direct call is not bounded.
Exact approvalA stored canonical request, a digest checked at dispatch, decide permissions, and one dispatch per approval.A SHA-256 digest of the request, target, versions, preconditions and expiry, recomputed before dispatch.Deciding who may approve. Any workspace member can decide today, and there are no approver groups.
Credential custodyA service that holds keys, fixes hosts, and serves named operations only.A write-only vault. Secrets resolved at dispatch and bound to their host.Removing other copies of the key from the agent's environment.
Lost responsesOperation keys, a third state, a reconciler for each provider, and an operator queue.Recorded unknown, never sent again, settled from the provider's own record where a lookup exists.Resolving what a read-back cannot settle, with evidence.
Audit recordA record per operation joining the request, decision, dispatch and provider evidence.A graded receipt with append-only evidence, and decisions with their policy version.Copying it into your own store for as long as you must keep it. Traces export as OTLP JSON for each run.

The same refund, through Anlyon

The key goes into the vault once. The agent holds an Anlyon key that can only invoke. The limit, the approval and the receipt sit on the action, so a second agent or a new session meets the same ones.

refund.ts
import { Client } from '@anlyonhq/sdk';

// Once, from a deploy script, with an operator key.
const ops = new Client({ apiKey: process.env.ANLYON_OPERATOR_KEY! });

await ops.secrets.put('STRIPE_KEY', {
  value: process.env.STRIPE_KEY!,
  allowedHosts: ['api.stripe.com'],
});

await ops.actions.create({
  name: 'refund',
  requiresApproval: true,
  adapter: {
    type: 'stripe.refund',
    config: { account: 'acct_1Nxyz', mode: 'live', currency: 'usd' }, // the key must match the mode
    credentialSecret: 'STRIPE_KEY',
  },
});

// One allowance for every agent, session and key in this environment.
await ops.impactLimits.create({
  name: 'Refunds per day', dimension: 'money', currency: 'usd', period: 'day', amount: 50_000,
});

// In the agent, with a key that can only invoke.
const agent = new Client({ apiKey: process.env.ANLYON_AGENT_KEY! });

const { data } = await agent.actions.invoke(
  'refund',
  { paymentIntent: 'pi_3Pabc', amount: 1250, currency: 'usd' },
  { operationKey: 'order-4411-refund' },
);

// pending_approval until an approver decides this exact request.
console.log(data!.status, data!.effectId);

When building it yourself is the right call

One agent, one process, one reviewer
If the person approving is the developer watching the run, your framework's approval switch already does the job. The human-in-the-loop comparison covers which frameworks have one and what each one pauses.
The action is cheap to undo
Labels, drafts and writes to your own database rarely need a shared limit or a provider read-back. Spend the effort on the calls that move money or reach a customer.
One provider you know deeply
A provider with idempotency, a lookup by your own id and conditional writes makes a small dedicated service practical. Write down the invariants and test the crash windows: a restart after dispatch, a duplicate callback, an expired key.
Your agent acts inside each end user's account
Anlyon connects a workspace's own accounts. It has no sign-in flow for each of your end users. If every customer connects their own account, you need your own OAuth or a product built for it.
No hosted service may sit in the call path
Anlyon is a hosted API. There is no self-hosting and no SSO, and it is not SOC 2 certified. If any of those is a requirement today, build it, and use this page as the specification.

Framework approval switches, compared: Human-in-the-loop approval tools for AI agents in 2026.

Where your own service can do better

A network hop on every governed call
Anlyon makes the call, so a governed action goes through Anlyon's API before it reaches the provider. A service in your own network is closer.
Your semantics, not ours
A limit period is a day, a month or a total, inside one environment. A rolling window, a limit across environments or a limit per end customer is yours to build.
Read-back is built in for three adapters
Stripe refunds, GitHub file updates and Resend email sends carry the provider lookup built in. Anything else is an HTTPS API you declare, and a declared action has no provider idempotency, no atomic stale-state protection and no replay of a lost write. On a declared action, confirmed means your own verify rule matched.
Approvals are decided in the dashboard or through the API
Slack, email, Discord and webhooks tell an approver something is waiting. The decision itself happens in the dashboard or through the API. There is no approve-by-reply.
A much smaller company
Anlyon is built and run by one person. Your own service is staffed by your own team.

You can do both

Route the calls that move money or reach a customer through Anlyon actions, and keep the rest in your own code. Anlyon governs the actions routed through its hosted executor. A tool your own code calls directly is not routed through Anlyon and is not governed by it.

For a call you cannot move yet, the SDK wrapper approvals.gate() waits for an approval decision before calling your function. Your process still runs your function with your credential, so it buys the decision and the record, not credential isolation, and it cannot attest that the function ran.

More in Build an action approval service yourself? and the governed actions docs.

Questions

Should I build my own human-in-the-loop approval system for AI agents?
Build it when one agent runs in one process and the reviewer is the developer: a framework approval switch and a database row are enough. Once several agents share an allowance, someone other than the requester approves, or the agent must not hold the key, you are building a ledger, a request digest, a vault and a reconciler. That is what Anlyon runs for the actions routed through it.
What is the hardest part of building spending limits for AI agents?
Two parts. The reservation has to be atomic and shared, or two agents spend the same last $100. And a timeout has to keep its reservation held as unknown, because the request may have landed. Releasing it on a timeout lets the next call through on top of one that already happened.
Why not just retry an agent's API call when it times out?
Because the first request may have reached the provider. Retrying a refund on a timeout is how one refund becomes two. Anlyon records a lost response as unknown, does not send the request again, and settles it from the provider's own record. What the lookup cannot find stays unknown until a replay or an operator resolves it.
Can I use Anlyon for some actions and my own code for others?
Yes. Anlyon governs the actions routed through its hosted executor. A tool your own code calls directly is not routed through Anlyon and is not governed by it, so put the calls that move money or reach customers behind actions first. Early Beta Access is free, with no credit card.

Free tier, no credit card. One command if you use Claude or Cursor.

$ claude mcp add anlyon -- npx -y @anlyonhq/mcp-server