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.
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.
| Job | Built yourself | With Anlyon | Still yours with Anlyon |
|---|---|---|---|
| Shared limit | A 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 approval | A 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 custody | A 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 responses | Operation 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 record | A 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.
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.
