Facturiadocs
MCP / AI

Two-step confirmations

When the MCP client's approval is not enough, which tools can be put behind a second lock and how a pending action gets completed.

The default: direct execution

When an agent calls emit_document, it emits.

Facturia does not interpose an approval step of its own, because the MCP client's tool-approval UI — the thing that shows you "Claude wants to call emit_document, allow?" — is the human in the loop. Adding a second Facturia-side confirmation on top of that, for every write, would be redundant on the vast majority of calls and would defeat half the point of an agent interface.

Where that is not enough

There are two distinct failure modes, and the client's approval only solves one:

The agent tries to do something the person did not ask for. The client's own approval step solves it — that is exactly what it exists for.

The person reflexively approves everything their chat client asks them, and something with real financial consequence — filing a tax return, an irreversible claim — goes out because someone clicked "allow" on autopilot and not because they deliberately reviewed that figure. The client's approval does not solve this: it is the same click in both cases.

require_confirmation attacks the second.

Turning it on

It is a per-empresa flag in automation_settings, next to auto_emit_46 and friends. It ships off, like every automation.

curl -X PATCH https://api.facturia.cl/v1/empresas/emp_01K2R6.../automation_settings \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Content-Type: application/json" \
  -d '{"require_confirmation": true}'

It is a REST field, not an MCP-only setting: REST callers and the dashboard see and control the same flag. The REST API described in the rest of these docs does not change behavior because you turned it on.

Which tools it covers

emit_document, file_f29, emit_tipo46 and reclaim_received_document — the "emit / file / claim" set.

Every other write (accept_received_document, for instance) stays direct even with the flag on: accepting already matches the legal default that silence would produce anyway, so putting a lock on it protects nothing.

What it looks like

// → file_f29
{ "empresa": "77928532-4", "period": "2026-07", "confirm_total_payable": 1373000 }

// ← pending_confirmation (la empresa tiene require_confirmation: true)
{
  "status": "pending_confirmation",
  "confirmation_id": "conf_01K2SD3P7Q",
  "confirmation_url": "https://app.facturia.cl/aprobar/conf_01K2SD3P7Q?token=mcpapv_9lQpV3fA…",
  "action": "file_f29",
  "summary": "File F29 for período 2026-07 — CLP 1.373.000 payable, due 2026-08-20.",
  "expires_at": "2026-08-14T14:45:00Z"
}

Nothing was filed. The agent hands you the summary and the link, and stops there.

Who completes it: you, on a page

You open confirmation_url, see which empresa, which action and with what figure, and press Confirm or Reject. No sign-in needed: the link carries a single-use token, valid for those 15 minutes and for that action and no other. Only then is the F29 (Chile's monthly VAT return) filed — with exactly the data that was summarized when the agent proposed it, not with whatever the world looks like at that moment.

The agent cannot complete it, and that is the point

There is no MCP tool that closes a pending action. There was one — confirm_action — and it was withdrawn: if the agent can complete it by saying "the user told me yes", then the record of your approval is the agent's word for it, which is exactly what this flag exists to refuse. If you tell the agent you already approved, have it verify by reading (get_document_status, or file_f29 with a filing_id).

Confirming twice replays the original result instead of executing twice — the same guarantee Idempotency-Key gives a REST caller. Rejecting twice is not an error either: you wanted it not to happen, and it is not happening.

An approval expires after 15 minutes unclaimed. Once expired, the page says so and the agent has to propose the action again to mint a new one.

Sandbox: this is where you rehearse it

The two-step works the same in sandbox: turn require_confirmation on for a test empresa and the emission is parked, the link is minted, and the /aprobar page opens exactly as it does live — marked as a test, so nobody mistakes the rehearsal for the real thing.

This could not be rehearsed before

Until September 2026 the sandbox ignored the flag: the API accepted it, reported it as on, and emitted directly anyway. Which meant the most consequential bridge in the product — the one where an agent hands a decision to a person — could only be seen for the first time with real documents. Now it can be exercised at zero risk.

The sixty-second demo is unchanged: the flag defaults to off, so a freshly created sandbox empresa still issues in a single call.

The states that are not errors

Two behaviors of this layer worth being clear about in the client:

  • A rate limit and a pending confirmation are not failures. The equivalent of a 429 and the pending_confirmation responses are ordinary tool results with a clear status field, not thrown errors. An agent should read them and decide what to say next, not treat them as something broken.
  • Every tool description declares its own likely failure modes. An agent choosing between emit_document and draft_document, or deciding whether it is safe to retry after a sii_error, should never have to consult external documentation mid-conversation. That is why retriable travels with every rejection reason.

On this page