Tool catalog
Up to 90 tools the MCP server exposes, depending on the grant's scopes, grouped by domain, with their inputs, their annotations and which ones pass through confirmation.
This page is generated from the server's real tool registry: if a tool exists, it is here, with the same annotations your MCP client sees. Up to 90 tools, depending on the grant's scopes.
Conventions every one of them shares
- The first argument of every empresa-scoped tool is
empresa. Account-level tools reject it. - An argument with
?is optional. - Annotations are the protocol's own, read off the same
annotationsobject your client receives: "read-only" isreadOnlyHint, "irreversible" isdestructiveHint, "idempotent" isidempotentHint. Your client decides whether to ask you every time from those. - Idempotency is the server's job, not the agent's. A REST client sends its own
Idempotency-Key; an MCP agent should not have to think about retry safety. The server generates and manages the key underneath — anchored to thepreview_idwhen the call came fromdraft_document, or to a fresh UUID per call — so a retry after a dropped connection reuses the folio already assigned instead of burning a second one. - This page lists every tool. YOUR CONNECTION MAY SEE FEWER.
tools/listis filtered by the scopes the person granted: a tool appears only when everything in its Scopes column is in the grant. A connection holdingdocuments:read empresas:readis not offeredemit_document, because promising an invoice it cannot issue is worse than not offering it. Each tool also publishes its requirement in_meta.requiresScope, so a client can say WHY something is missing. Calling a tool the grant does not cover still answersinsufficient_scope, naming the scope and how to get it — hidden is not the same as absent. - Almost every tool needs
empresas:read, even the ones that look like they only write: naming a company means reading the list of companies this connection may reach, which is also where the sandbox boundary is checked. - Errors are REST errors. A tool's error content is the same
{ type, code, message, hint, retriable, param, doc_url, request_id, empresa }object from the error envelope, returned as MCP error content. - Money, dates and RUT follow exactly the API's own conventions: integers in CLP, civil dates
YYYY-MM-DDin America/Santiago, canonical RUT.
Issuing
| Tool | Description | Inputs | Annotations | Scopes | Confirmation? |
|---|---|---|---|---|---|
draft_document | Preview a document before anything is signed: resolves the receptor, prices items, computes totals, and previews (never reserves) the folio that would be used. No side effects — nothing is emitted, no folio is consumed, and the SII is not contacted. Call this first whenever the user described a document in prose, then pass the returned preview_id to emit_document. The preview reports how it identified the receptor (resolution_method: rut_exact, anonymous, or needs_clarification) — if it is needs_clarification, ask the user rather than guessing. folio_preview is the folio this document WOULD take. It is null on the SII free portal (emission_channel: sfg), where the portal assigns the folio during the emission itself — that is normal and not a shortage. A WARNING IS NOT A REFUSAL. warnings[] carry a stable code — BRANCH ON THAT, never on the English message — and the draft stays valid whatever they say: emit_document works. receptor_unresolved means a name was given where a RUT belongs, and param says which field to ask for. no_folios_available means the emission would fail LATER for want of folios, not that this draft is wrong: check get_folio_status for the exact stock and, if the tipo really is out, request_folios asks the SII for a range (#575 put it on this surface — check first, because replenishment below the company’s threshold is already automatic). Do not silently stop. It cannot come from a sandbox company, whose folios are virtual and unlimited, nor from the SII free portal. An unrecognised code is not a fault: relay it verbatim. Likely errors: receptor_incomplete — the counterparty has no giro/dirección/comuna on file and a factura cannot be filed without them. The refusal names exactly which, with the length each caps at; ASK THE USER, never invent one, then store the answer with update_counterparty and draft again. Answering once is enough: it goes into this company’s address book and every later document to that RUT resolves from it. Also empresa_not_allowed. | empresa?, tipo_dte, receptor?, items, issue_date?, references? | writes · reversible | empresas:read + documents:write | no |
emit_document | Emit for real. Sandbox: mocked, deterministic, settles in about a second. Live: real SII round-trip, a real folio, a real tax record. Pass either the preview_id from draft_document (strongly preferred — the numbers the user approved are the numbers that go out) or the full explicit fields. RETRY SAFETY: if you pass neither preview_id nor external_id, this tool drafts a preview itself first — the same call draft_document makes — purely so the emission has a stable identity to retry against. Without one, a dropped connection or a client that calls this tool twice for the same invoice has no way to be recognised as a retry, and on a live empresa that means a second real folio for one invoice. Prefer calling draft_document yourself (the user sees the numbers first) or passing your own external_id (an order id, an invoice number) — both give the same protection without the extra round trip this tool otherwise makes on your behalf. Do not hand-compute and pass totals: that field is an assertion, not a hint, and a disagreement with our computation returns totals_mismatch rather than being quietly accepted. If the company has require_confirmation on, this returns a pending_confirmation envelope instead of a document — that is a normal result, not an error. Give the user the summary and the confirmation_url and stop: they approve on that page themselves, and there is no tool that finishes it for them. Repeat calls with the same preview_id, or the same external_id, return the original document rather than emitting twice. So does a repeat of a keyless call whose fields are identical — within 24 hours, the same tipo_dte, receptor, items, issue_date and references means the same invoice. SO A BATCH OF GENUINELY IDENTICAL DOCUMENTS NEEDS TELLING APART, and this case is common rather than exotic: several cuotas of a payment plan, a monthly retainer billed twice, two identical charges to the same customer on the same day. Emitting those from identical fields would collapse them into ONE document and you would report six invoices where four exist. Two ways through, both fine: call draft_document once per document and emit each returned preview_id (each draft is its own identity — and the user sees the numbers first, which is the preferred flow anyway), or give each emission its own external_id. Do NOT tell them apart by editing the glosa: the wording on a document is what the customer reads, and changing it to defeat a retry check puts a bookkeeping artefact on a legal record. IF THE RECEPTOR IS INCOMPLETE, THIS ASKS. A factura’s receptor needs a giro, a dirección and a comuna: they are optional in the SII’s schema and mandatory to its business validation, so a document without them is accepted as an envelope and then REFUSED as a document — with the folio spent either way. Facturia resolves them from what it holds (this company’s address book, and its own earlier documents to the same RUT) and, when it cannot, refuses with receptor_incomplete BEFORE a folio is allocated, naming exactly which fields and how long each may be. Ask the user for those and send them back in receptor_details — never invent them. ANSWER IN receptor_details, NOT BY ENLARGING receptor. The two look interchangeable and are not: receptor is part of this emission’s retry identity, so adding fields to it makes the retry look like a second, different invoice, while receptor_details is deliberately outside that identity — the answering call and the call it answers are the same action. receptor_details is also STORED, in this company’s address book for that RUT, so the next document to them needs nobody asked anything. Likely errors: receptor_incomplete (see above — supply receptor_details, never a guessed address), totals_mismatch, no_folios_available (get_folio_status says which tipos are out), external_id_exists, empresa_not_emitting — the company has not finished onboarding, and it is almost always waiting on a PERSON: call get_pending_human_action and relay the handoff it returns verbatim. Do not improvise the steps and do not tell the user to "go to the dashboard". subscription_required means the company has no active subscription; error.details.checkout_url is the page the ACCOUNT HOLDER opens to fix it, and the error text hands you that link — pass it on verbatim rather than describing it. | empresa?, preview_id?, tipo_dte?, receptor?, items?, issue_date?, references?, external_id?, receptor_details? | writes · irreversible · idempotent | empresas:read + documents:write | sí |
get_document_status | Cheap, narration-sized status check — not the full document, so polling one does not drag its items and totals back into your context every time. Identify the document by document_id, by your own external_id, or by tipo_dte + folio. It does NOT carry the glosa or the line items; list_documents does, filtered by folio or external_id. Statuses run queued → sent → accepted, with rejected and failed as terminal alternatives. Every rejection reason carries retriable, which tells you whether calling again could help. Likely errors: document_not_found (no document matches that external_id or tipo_dte+folio pair for this company), resource_not_found (wrong document_id), empresa_required. | empresa?, document_id?, external_id?, tipo_dte?, folio? | read-only | empresas:read + documents:read | no |
get_document_file | A PDF or XML as a resource link — not embedded bytes. The link is signed, unguessable, needs no credential to open, and is valid for ten minutes; hand it to the user rather than trying to read the file yourself. PDF is for a human to read; XML is the legal artifact the receptor’s accounting system ingests. copy picks which PDF copy the link points at — original (default, no acuse-de-recibo box, no CEDIBLE legend) or cedible (both, the copy that makes the invoice assignable). Only meaningful when format is pdf. A cedible copy exists only for facturas — tipo 33 and 34 (Ley 19.983 gives the tercera copia to facturas, not to boletas or notas); asking for it on any other type is 422 cedible_not_available. A document that is still queued or sent has no rendered file yet, and this refuses with artifact_not_ready and retry_after_seconds RATHER than handing you a link that would open «este enlace no es válido, venció o fue revocado» in the person’s browser. Wait that long and call again, or check get_document_status. Likely errors: artifact_not_ready (retriable — the file is still being produced), cedible_not_available (wrong tipo for copy: cedible). | empresa?, document_id, format?, copy? | writes · reversible | empresas:read + documents:read | no |
list_documents | Search and browse documents this company has issued, newest first. Also the way to fetch one document in full — filter by folio or external_id — which is why there is no separate get_document tool. EACH DOCUMENT COMES BACK WITH ITS GLOSA — the line items, word for word — plus its references, its id, its external_id and, on a rejection, the reason. So this is the tool that answers «what did we bill them last time», «use the same wording as the invoice from Tuesday» or «what was that nota de crédito against»: read the wording off a previous document rather than asking the person to type it again. Use get_document_status instead when you only need to know whether something was accepted; this tool returns whole documents and is much heavier in context. Likely errors: invalid_limit (1–100), empresa_required. | empresa?, status?, tipo_dte?, folio?, receptor_rut?, external_id?, issue_date_gte?, issue_date_lte?, cursor?, limit? | read-only | empresas:read + documents:read | no |
get_folio_status | How many documents of each type this company can still issue — the check that answers "will my next invoice go through?" before you try. Every document consumes one folio from a range the SII grants, and running out is the no_folios_available error the emission tools warn about. available COUNTS EVERY RANGE THE COMPANY HOLDS, including the ones queued behind the one it is currently drawing from, because that is what the emission path draws from too. Do not compute a shortage yourself from next_folio and a range end. Each row reports status: ok, low (below this company’s own top-up threshold — replenishment is automatic and already in hand), exhausted (the next emission of that tipo WILL fail), or sfg_managed. sfg_managed means the company emits through the SII’s free portal, which assigns a folio at the moment of each emission — there is no stock, available is null rather than zero, and no_folios_available cannot happen to it. Never report a null as "none left". A tipo the company has never been granted folios for shows as exhausted with no ranges; that is not the same as running low, and the fix is a first CAF rather than a top-up. DO NOT RELAY low TO THE PERSON. Folios are stock this platform keeps for them; a threshold this platform chose, crossed on a tipo that can still emit, is not news, and repeating it as a warning turns routine housekeeping into something they think they have to act on. Say something only when exhausted actually blocks what they asked for — and then say what is already being done about it. request_folios asks the SII for more, and it is on this surface now (#575) — but read this first: replenishment below the threshold is AUTOMATIC, so a low tipo needs nothing said and nothing done. A timbraje registers a real range at the SII, so one request and then wait — never a loop. Likely errors: empresa_required, empresa_not_allowed. | empresa?, tipo_dte? | read-only | empresas:read | no |
send_document | Email an emitted document to the receptor, as PDF and/or signed XML. The XML is the artifact their accounting system ingests; the PDF is what a person reads. Always the ORIGINAL printed copy, never the cedible one — that is an instrument for assigning the debt, not the invoice you send someone to be paid. Omit to and it goes to the addresses stored on the document’s receptor. Pass to to send it somewhere else; do NOT invent an address, and if the document has none stored and the user gave none, no_recipient says exactly that — ask. A REPEAT IS SAFE: the same document to the same addresses with the same attachments is one email and one job — a retry after a timeout returns the ORIGINAL job rather than mailing twice. A DIFFERENT address is a different send and does mail again, so do not "re-send to make sure" with a changed recipient list. This is asynchronous: you get a job back and the mail is delivered afterwards. Call this tool again with job_id to see how it went — result.delivered is the fact, and in a sandbox company result.simulated is true and nothing reached a real mailbox. Never tell a user the mail arrived on the strength of the 202 alone. A document still queued refuses with artifact_not_ready because the signed XML does not exist yet; a document whose PDF has not rendered refuses the same way, and attach: ["xml"] works immediately in that case. Likely errors: no_recipient (nothing stored, nothing supplied — ask the user for an address), artifact_not_ready (retriable — the file is still being produced), too_many_recipients (at most 10 addresses), invalid_email, subscription_required (delivery is a paid capability; error.details.checkout_url is the ACCOUNT HOLDER’s page — pass the link on verbatim). | empresa?, document_id?, to?, attach?, job_id? | writes · reversible | empresas:read + documents:write | no |
emit_credit_note | Emit a nota de crédito (tipo 61) against a document that is already at the SII. There is no "delete an invoice" in Chile: code: 1 ANNULS the original, 2 corrects its text, 3 corrects its amounts. This creates a REAL second tax document with its own folio, and it cannot be undone. With code 1 or 2 the note mirrors the original’s amounts — you do not restate them, and you must not, because an annulment has to reverse exactly what was issued. With code 3 you pass items carrying the CORRECTED values, and the difference is what gets credited. reason is required and is limited to 90 characters: it is the SII’s own RazonRef field and it is printed on the document, so write what a tax auditor should read, not a note to yourself. Requires confirm_emit: true — say what you are about to annul (tipo, folio, total, receptor) and get a real answer first. If the company requires human approval for agent writes, this tool refuses instead of emitting: an annulment cannot be parked on the approval page, so the account holder does it from the panel. The document must have a folio (a queued one has not been issued yet) and must not already be annulled. Likely errors: document_already_annulled (a nota de crédito already annulled this one — there is nothing left to do), document_not_referenceable (no folio yet: wait for the original to leave queued), invalid_field (code must be 1, 2 or 3; reason is required and ≤ 90 chars; items is required with code 3), subscription_required. | empresa?, document_id, code, reason, items?, external_id?, confirm_emit? | writes · irreversible · idempotent | empresas:read + documents:write | no |
retry_document | Re-queue a document stuck in failed — the folio was exhausted when it was first sent, the certificate had expired, the SII was down. It REUSES the folio already assigned and never creates a second document, so this is the right answer to a failure and re-emitting is not. ONLY failed can be retried. A document that is queued or sent is still in flight (wait), and accepted or rejected is terminal — retrying either is refused with document_not_retryable, naming the status it actually has. Check get_document_status first if you are unsure. Fix the cause before retrying a document that failed for a reason retrying cannot change: get_document_status carries rejection_reasons[] with a retriable flag on each, and get_folio_status says whether the tipo has folios again. Retrying into the same wall spends nothing but achieves nothing. It comes back queued: the worker picks it up and the SII round trip happens afterwards, so do not report it as accepted. Likely errors: document_not_retryable (the document is not in failed — the message names the status it is in), resource_not_found (wrong document_id, or it belongs to another company), empresa_required. | empresa?, document_id | writes · reversible · idempotent | empresas:read + documents:write | no |
get_document_events | Everything that happened to one document, oldest first: when it was signed, when it was transmitted, what the SII answered and when, whether it was emailed, whether a nota de crédito annulled it. This is the tool for "what happened to this invoice?" — get_document_status answers "where is it now", which is a different question and much shorter. The timeline is the record of what Facturia PUBLISHED about the document, independent of whether any webhook delivery succeeded, so it is complete even for an account with no webhook endpoint at all. An empty timeline is not an error: a document emitted seconds ago may have no event yet. Say that rather than reporting the document as missing. Likely errors: resource_not_found (wrong document_id, or it belongs to another company), empresa_required, empresa_not_allowed. | empresa?, document_id | read-only | empresas:read + documents:read | no |
check_cesion | Ask the SII whether one factura can be assigned to a factoring company right now. A REPORT, never a computation and never the assignment itself: under Ley 19.983 a factura becomes assignable when the receptor gives acuse de recibo, or when eight días corridos pass with no reclamo, and one declared paid al contado never does — only the SII knows which applies. Use cede_document to actually assign it. The answer is asynchronous: this queues the question and the SII answers afterwards. Read the result from the document itself (list_documents with the folio, or get_document_file’s sibling reads) under cesion: cedible true/false, sii_status_code the SII’s raw code, checked_at when the SII last ANSWERED. All three are null until the first answer lands — null means "nobody has asked yet", never "not cedible". Facturas only: the registro answers about tipos 33, 34 and 43, because Ley 19.983 gives the cedible copy to facturas and not to boletas or notas. And only about a document it has recorded, so a queued or sent one is refused with cesion_status_not_available — wait until it is accepted. In a sandbox company the answer is simulated in-process against the simulated clock and no SII socket is opened; say so rather than reporting it as the SII’s word. Likely errors: cesion_not_applicable (wrong tipo — a boleta or a nota has no cesibilidad status at all), cesion_status_not_available (the document has not reached the SII yet; retriable once it is accepted), resource_not_found. | empresa?, document_id | read-only | empresas:read + documents:read | no |
cede_document | Assign the credit of one factura to a third party, registering it in the SII’s Registro Electrónico de Transferencia de Crédito. This ASSIGNS MONEY OWED to somebody else and IT CANNOT BE UNDONE. Check check_cesion first: the SII refuses a factura that is not cedible. THERE IS NO APPROVAL PAGE FOR THIS ONE. require_confirmation covers four actions — emitting a document, filing an F29, emitting a tipo 46 and reclaiming a supplier document — and a cesión is not among them, so a company that gated agent writes did NOT gate this. That is why confirm_irreversible: true is required and why you must name the cesionario, the amount and the factura to the user, in their own words, before calling. If they hesitate, do not call. Facturas only (tipos 33, 34, 43 — Ley 19.983). Every field of cesionario is required and none may be guessed: a wrong RUT assigns the credit to the wrong company. monto defaults to the document’s full total and must be between 1 and that total. A RECEIPT IS NOT A REGISTRATION. The furthest the upload itself reaches is submitted — the SII answered «Envío recibido OK» and issued a TrackID, and its validation happens hours later by email to the cedente. Check with job_id: while the job is running with registration: pending, say the AEC was RECEIVED and the anotación is pending. Only registration: confirmed means the SII recorded the cesión, and only that is worth settling money on. If a previous attempt already put a signed AEC on the wire, a second one is refused with cesion_attempt_may_have_registered — whether the credit was already assigned cannot be decided from here, and the refusal carries the last registry answer this system holds. Do not retry it; a person has to check the SII’s registro for that folio. Likely errors: cesion_tipo_not_cedible (only 33, 34 and 43 can be ceded), cesion_cedente_email_missing (the company has no contact_email, and the declaración jurada names it), cesion_attempt_may_have_registered (a previous attempt may already have assigned this credit — never retry), missing_field / invalid_field (the cesionario’s RUT fails the módulo-11 check, the dirección is under 5 characters, or monto is outside 1..total). | empresa?, document_id?, cesionario_rut?, cesionario_razon_social?, cesionario_direccion?, cesionario_email?, monto?, ultimo_vencimiento?, otras_condiciones?, email_deudor?, job_id?, confirm_irreversible? | writes · irreversible · idempotent | empresas:read + documents:write | no |
revoke_document_links | Revoke every live public link for one document — the signed, credential-free URLs get_document_file mints. Use it when a link went to the wrong person: the URLs stop resolving immediately, and anyone opening one afterwards sees «este enlace no es válido, venció o fue revocado». It does NOT unsend anything: a copy already downloaded stays downloaded, and an email that already carried the PDF still carries it. Say that rather than letting a user believe a document was recalled. The document itself is untouched — still emitted, still at the SII, still with its folio. get_document_file mints a fresh link whenever one is needed again. Likely errors: resource_not_found (wrong document_id, or it belongs to another company), empresa_required, empresa_not_allowed. | empresa?, document_id | writes · reversible | empresas:read + documents:write | no |
draft_document returns receptor_incomplete naming the exact field that is missing, never a silent
guess. emit_document can return totals_mismatch if the agent sent explicit totals that do not add up — the
tool's own description tells it never to compute totals by hand unless it is sure, precisely because that field
is an assertion, not a hint.
Reception
| Tool | Description | Inputs | Annotations | Scopes | Confirmation? |
|---|---|---|---|---|---|
list_received_documents | Browse the DTEs suppliers have sent this company, filterable by commercial-response state. Use status: pending to find the ones still inside their 8-day response window — those are the ones with a deadline attached. Chilean law gives the receiver 8 calendar days to reject a received factura (33/34/43); silence is acceptance and after day 8 it is irrevocable. Each row reports days_remaining. An empty list on a sandbox company is normal and does not mean anything is broken: reception is driven entirely from outside — a document appears when a supplier actually sends one. There is no tool here that fabricates one, and none is coming; seeding a sandbox supplier document is a REST-only test helper. Say that rather than looking for a tool to create one. Likely errors: invalid_limit (1–100), empresa_required. | empresa?, status?, tipo_dte?, issue_date_gte?, issue_date_lte?, cursor?, limit? | read-only | empresas:read + received:read | no |
get_received_document | Full detail on one supplier document, including the acuse deadline. THE DEADLINE MATTERS: Chilean law gives the receiver 8 calendar days from receipt to reject a 33/34/43. Doing nothing is legally meaningful — it is aceptación tácita, and after day 8 the document is irrevocably accepted and becomes freely transferable. The response reports days_remaining and available_actions; say the remaining days to the user whenever the state is still pending. Likely errors: resource_not_found (wrong received_document_id, or wrong company), empresa_required. | empresa?, received_document_id | read-only | empresas:read + received:read | no |
accept_received_document | Register explicit acceptance (the SII’s ACD event) of a supplier document’s content. This matches what would happen anyway if nobody acted — silence accepts at day 8 — so it creates an explicit record rather than changing the outcome. It executes directly even on companies that gate other agent writes, for exactly that reason. One shot: a second commercial response on the same document is refused. Likely errors: commercial_response_already_registered (already answered), commercial_response_window_closed (past day 8), commercial_response_not_applicable (only 33/34/43 carry a commercial response). | empresa?, received_document_id | writes · irreversible · idempotent | empresas:read + received:write | no |
reclaim_received_document | Register a reclamo against a supplier document — by content, partial missing goods, or total missing goods. IRREVERSIBLE: there is no un-reclamar. It tells the SII and the supplier that this company disputes the document, and it stops the invoice becoming freely transferable. Only do this when the user has said clearly which document and why. The window is 8 calendar days from receipt; after that the document is irrevocably accepted and this tool refuses. Companies with require_confirmation on get a pending_confirmation envelope instead of an immediate reclamo — relay the summary and the confirmation_url; the person decides on that page and no tool here completes it. Likely errors: commercial_response_window_closed (past day 8, with the deadline in the error), commercial_response_already_registered, commercial_response_not_applicable (only 33/34/43). | empresa?, received_document_id, reason, note? | writes · irreversible · idempotent | empresas:read + received:read + received:write | sí |
acknowledge_received_document | Register the acuse de recibo (SII acción ERM) for a supplier document — that the goods or service it describes were actually received. THIS IS NOT ACCEPTANCE OF THE DOCUMENT'S CONTENT. It is the OTHER commercial response Ley 19.983 art. 3 recognises, independent of accept_received_document's ACD: a document can carry an ERM, an ACD, both, or neither. Acknowledging receipt says the merchandise or service arrived — it says nothing about whether the invoice itself is correct, and does not stop a later reclamo on the same document. Like acceptance, this is NEVER gated behind a company's require_confirmation setting: it registers a fact about delivery rather than disputing the document, so it executes directly even on companies that gate a reclamo. One shot: a second acuse (or any other commercial response) already registered for this document is refused, and so is an attempt on a document whose 8-day window has no anchor yet — the clock starts at the SII's own recorded receipt timestamp, and until that has been read there is no window to be inside of. Likely errors: commercial_response_already_registered (already acknowledged, or a conflicting response already recorded), commercial_response_anchor_pending (we have not yet read this document's SII receipt timestamp), commercial_response_window_closed (past day 8 — the document is irrevocably accepted by silence already), commercial_response_not_applicable (only 33/34/43 carry a commercial response). | empresa?, received_document_id | writes · irreversible · idempotent | empresas:read + received:write | no |
Trap
These tools' own descriptions say "8 calendar days"
get_received_document and reclaim_received_document declare the 8-day window in their own description and
surface days_remaining prominently. An agent narrating "you have 2 days left to respond" only works if the
deadline is impossible to miss in the schema.
Common errors: commercial_response_window_closed (past day 8),
commercial_response_already_registered (one shot only), commercial_response_not_applicable (wrong type —
only 33/34/43 carry a commercial response).
Tax
| Tool | Description | Inputs | Annotations | Scopes | Confirmation? |
|---|---|---|---|---|---|
get_periodo_summary | The company’s IVA position for one month, ready to narrate: débito, crédito, retenido, remanente, PPM, total payable and the due date — plus a narrative paragraph written in correct Chilean tax Spanish. QUOTE THE NARRATIVE RATHER THAN COMPOSING YOUR OWN. Getting débito/crédito/remanente right in Spanish is easy to get subtly wrong and impossible for the user to catch, because a wrong version still reads fluently. Set compare_previous to answer "why do I owe more/less than last month". This is a read: nothing is filed and nothing changes. A period with no synced data yet comes back as zeros with sync describing the gap, not as an error — check sync before presenting the figures as current. Likely errors: invalid_period (period is not a real YYYY-MM month), empresa_required. | empresa?, period, compare_previous? | read-only | empresas:read + tax:read | no |
query_rcv | Read RCV rows — purchases, sales, or both — as the SII holds them. This is the ledger the F29 is computed from, so it is where to look when a number in a propuesta is surprising. Every response carries a sync block saying how fresh the data is; if status is degraded, say so rather than presenting the figures as current. estado_contable is the important filter: REGISTRO rows count toward the F29, PENDIENTE and NO_INCLUIR do not. An unsynced period returns an empty list with sync describing the gap, not an error. Likely errors: invalid_period (period is not a real YYYY-MM month), invalid_limit (1–100). | empresa?, direction?, period?, estado_contable?, counterparty_rut?, tipo_dte?, issue_date_gte?, issue_date_lte?, cursor?, limit? | read-only | empresas:read + tax:read | no |
sync_rcv | Ask Facturia to read the Registro de Compras y Ventas from the SII for one period — or for a range of periods, which is how the history the daily sync never read gets filled in. WHEN TO CALL IT: whenever query_rcv or get_periodo_summary answers with sync.last_synced_at: null for the period you asked about. That means the month was never fetched, and an empty list there is "unknown", not "no documents" — the daily sync covers the last three months, and a one-time backfill of the twelve before that runs only after a company’s first successful sync, so anything older (or anything asked about before that backfill ran) has never been read. Never tell a user a sale or purchase does not exist from an unsynced period; call this, wait, read again. HOW IT RUNS: each period becomes one job (type rcv_sync) the worker executes against the SII with the company’s certificate; one takes roughly 5–60 seconds. Pass job_id to check on one instead of starting another, or simply re-read query_rcv after a minute — sync.last_synced_at for that period moves when its job lands. A call for a period whose sync is still queued or running attaches to that job rather than starting a second one; a call after it finished starts a fresh read, whatever the daily sync did this morning. LIMITS: from/to may span at most ten months per call (the endpoint is metered at ten calls per minute per company). For a longer backfill call again with the next range; if the meter trips mid-range the answer lists exactly which periods were queued and which were not, with retry_after_seconds. Omitting every period argument syncs the PREVIOUS month, which is the one "sync my RCV" means on any day before the 20th. Sandbox companies never open a session against the SII: the job settles at once over whatever test data was seeded. Likely errors: invalid_period (not a real YYYY-MM month), invalid_range (to before from, or more than ten months), rate_limit_error (wait retry_after_seconds and continue with the periods not yet queued), empresa_not_emitting (the company has no certificate in custody yet — nothing can read the SII on its behalf; get_pending_human_action says what is missing). | empresa?, period?, from?, to?, job_id? | writes · reversible · idempotent | empresas:read + tax:write | no |
get_f29_propuesta | The computed F29 for one period, every código explained in plain language, with the RCV rows behind each figure. This is what would be filed — read it before recommending file_f29. PAY ATTENTION TO observations: any entry with severity error means the proposal is not safe to file, and you should tell the user what it says rather than proceeding. Warnings are worth mentioning too (three purchases sitting in PENDIENTE means real credit is being left behind). Also returns a link to the SII-format .txt upload file, valid ten minutes, in case the user prefers to submit it by hand. Likely errors: resource_not_found (no propuesta has been computed for that period yet — nothing to sync it from), invalid_period (period is not a real YYYY-MM month). | empresa?, period | writes · reversible | empresas:read + tax:read | no |
file_f29 | File the F29 for a period — or, if you pass filing_id, report on one already filed rather than creating a duplicate. FILING IS A LEGALLY BINDING DECLARATION with money attached. confirm_total_payable is mandatory: it must equal the propuesta’s figure, which forces the number to appear explicitly in this call and therefore in whatever you told the user before making it. If they disagree, it fails with f29_amount_confirmation_mismatch rather than filing something else. Read get_f29_propuesta first, every time. If it carries an observation with severity error, do not call this. Most companies gate this: expect a pending_confirmation envelope rather than a filing. Relay the summary and the confirmation_url and stop — the person approves on that page, and nothing here can complete it on their behalf. There is no un-filing — a filed F29 is corrected by a rectificatoria, which Facturia does not support. Likely errors: f29_amount_confirmation_mismatch (the RCV moved since you read the propuesta — re-read it), f29_already_filed, resource_not_found (no propuesta computed for that period — call get_f29_propuesta first). | empresa?, period, confirm_total_payable?, filing_id? | writes · irreversible · idempotent | empresas:read + tax:read + tax:write | sí |
list_tipo46_suggestions | Foreign digital-services purchases we detected that need a self-issued factura de compra (tipo 46). Since Ley 21.210 the Chilean buyer of foreign digital services IS the taxpayer: the provider charges no IVA, you self-issue a tipo 46 with IVA totalmente retenido, and you take that IVA as crédito fiscal. Almost nobody does it, so this is usually free money the user did not know about — each suggestion reports estimated_benefit.iva_credit. Read-only. Confirming one is emit_tipo46. Likely errors: empresa_required, invalid_limit. | empresa?, status?, period? | read-only | empresas:read + tax:read | no |
emit_tipo46 | Confirm a foreign-digital-services suggestion, which emits the tipo-46 factura de compra with IVA totalmente retenido and books the crédito fiscal. This issues a real tax document in the company’s name. Show the user the suggestion’s proposed_document and estimated_benefit first — particularly the exchange rate, which determines the peso base of the document and is the figure that would be questioned if the criterion is ever challenged. Companies with require_confirmation on get a pending_confirmation envelope instead. Repeat calls with the same suggestion_id return the original document rather than emitting twice. Likely errors: suggestion_already_resolved (already confirmed or dismissed), resource_not_found (no such suggestion), no_folios_available (tipo 46 needs its own CAF — get_folio_status shows whether one exists, and a company that has never been granted tipo-46 folios shows as exhausted with no ranges). | empresa?, suggestion_id | writes · irreversible · idempotent | empresas:read + tax:read + tax:write | sí |
characterize_purchases | Set the tipo de compra on RCV purchase entries — the decision that says whether each purchase’s IVA is recoverable crédito fiscal, and therefore what códigos 519/520 carry on the F29. Pass one entry for the single case; up to 500 in one call for "clear the whole month". WHEN TO CALL IT: when query_rcv shows purchases whose characterization is empty or wrong, or when get_f29_propuesta counts crédito the user says is not theirs to take (a supermercado charge, an activo fijo, an expense outside the giro). WHAT YOU MUST NOT CLAIM: in this build the SII’s own register DOES NOT MOVE. Live caracterización writes are fenced off, the answer says so on every action (live_writes_enabled: false, dry_run: true, and a note in Spanish), and each action settles as dry_run. Relay that: the instruction is recorded against the entry and the tipo de compra at the SII is unchanged. Never say "characterized at the SII". THE API HAS ITS OWN CONFIRMATION GATE. A company with caracterización gated answers a pending_confirmation envelope instead of queueing anything — that is a normal result, not an error. Relay its summary and confirmation_url and STOP. Only if the user then tells you to proceed, call again with the identical entries plus confirmation_token (the token is bound to the NUMBER of entries, so the second call must carry exactly the same count). no_recuperable is not a complete instruction on its own: it needs iva_no_recuperable_code (1, 2, 3, 4 or 9), because the SII does not accept a non-recoverable IVA without a reason. A bulk call is per entry: one bad id lands in failed[] with its own error and the other 499 still go through — read failed before reporting success. Changing a caracterización invalidates the F29 propuesta for that period, which is recomputed. Likely errors: invalid_tipo_compra (not one of the taxonomy’s values), iva_no_recuperable_code_required (you asked for no_recuperable without a reason code), characterization_already_pending (an instruction for that entry is still in flight — wait for it rather than sending a second), caracterizacion_automation_disabled (this company has not opted in; a person turns it on, you cannot). | empresa?, entries, confirmation_token? | writes · reversible | empresas:read + tax:write | no |
revert_characterization | Put a purchase’s tipo de compra back to what it was before the last settled caracterización. It queues a NEW action restoring previous_tipo_compra, linked to the one it undoes by reverts — it does not edit or delete the original, because a write to a tax register that happened, happened, and its row is the evidence. WHEN TO CALL IT: the user says a caracterización was wrong. Only an action that already SETTLED can be reverted; an instruction still in flight is superseded by calling characterize_purchases again, not by this. WHAT YOU MUST NOT CLAIM: as with characterize_purchases, in this build the SII’s register does not move — the answer carries live_writes_enabled: false and settles as dry_run. Say so rather than reporting an undo at the SII. It passes the identical gate: a gated company answers a pending_confirmation envelope, which is a normal result — relay its summary and confirmation_url and stop, and only send confirmation_token back after the user says to go ahead. If the value being restored is no_recuperable, the SII needs a reason: pass iva_no_recuperable_code unless an earlier action on the same document already recorded one. Likely errors: characterization_nothing_to_revert (no settled action on this purchase, or the last one recorded no previous value — set the tipo you want with characterize_purchases instead), iva_no_recuperable_code_required (the restored value is no_recuperable and no reason is on file), characterization_previous_not_revertible (the recorded previous value is not one this API can set — choose one explicitly). | empresa?, entry_id, iva_no_recuperable_code?, confirmation_token? | writes · reversible | empresas:read + tax:write | no |
set_estado_contable | Record YOUR bookkeeping estado for one RCV purchase — REGISTRO, PENDIENTE or NO_INCLUIR. It is written to estado_contable_override, beside the SII’s own estado_contable, and NEVER over it: the SII’s register is refreshed by every sync and stays authoritative, so a resync can always correct us. THE OVERRIDE IS IGNORED BY THE F29 unless this company has honor_estado_contable_override turned on. The answer says which value the propuesta will actually use, in estado_contable_effective, and estado_contable_override.honored says whether the F29 obeys you at all. Read those two before telling anyone their crédito changed — on a company that does not honour overrides, this call changes a label and nothing else. RECLAMADO is deliberately not settable here: an entry is RECLAMADO because a reclamo was registered against the document, and asserting it here would claim a commercial action that never happened. Use reclaim_received_document for that. This is NOT the same act as caracterización — that is characterize_purchases, which decides whether the IVA is recoverable. Every write is recorded with the key that made it and the SII value it did not replace. Likely errors: invalid_field (estado_contable is not REGISTRO, PENDIENTE or NO_INCLUIR), resource_not_found (no such purchase entry for this company), empresa_required. | empresa?, entry_id, estado_contable | writes · reversible · idempotent | empresas:read + tax:write | no |
list_f29_propuestas | List the periods that have an F29 propuesta and what state each one is in — ready, computing, stale or blocked — newest month first. This is how to answer "which months are done" and "is anything still outstanding" without probing twelve periods one by one and collecting eleven 404s. READ-ONLY: nothing is computed, nothing is filed, nothing changes. The rows deliberately omit the validation block and the filing id; for one month in full — every código explained, the observations, the upload file — call get_f29_propuesta with that period. A period MISSING from this list has never been computed, which is not the same as "nothing was owed": call recompute_f29_propuesta for it, or check that the RCV for that month was ever synced (sync_rcv). Likely errors: invalid_cursor (the cursor did not come from a previous page of this same list), invalid_limit (1–100), empresa_required. | empresa?, cursor?, limit? | read-only | empresas:read + tax:read | no |
recompute_f29_propuesta | Ask Facturia to rebuild one period’s F29 propuesta from the ledgers as they stand now, optionally storing overrides for individual códigos. Call it after a caracterización, after a sync_rcv that moved a month, or when the user says a figure is stale. It does NOT file anything and nothing reaches the SII. IT IS QUEUED, NOT DONE. The answer comes back with status: computing: the computation is a worker job, so say "recomputing", never "recomputed". get_f29_propuesta for the same period reflects it after a moment — read it again before quoting any number, and do not call file_f29 off a propuesta that is still computing. AN OVERRIDE IS A HUMAN OVERRULING A COMPUTED TAX FIGURE, so every one needs a reason in the user’s own words. It is stored, not applied here — the number in the propuesta and the number in the SII upload file come from the same arithmetic, which is why this layer refuses to do the sum itself. Do not invent a reason; if the user did not give one, ask. Likely errors: override_reason_required (an override arrived with no reason — unauditable, so it is refused), invalid_field (code and value must be integers), invalid_period (period is not a real YYYY-MM month). | empresa?, period, overrides? | writes · reversible · idempotent | empresas:read + tax:write | no |
list_f29_filings | Every F29 filing recorded for this company, newest first, with the period, the declared total and where each one got to. This is the read that answers "have we filed August yet" and "what happened to the one we sent" without filing anything: it is the list form of file_f29’s filing_id check. READ-ONLY. Nothing is filed, nothing is retried and nothing reaches the SII. MIND THE VOCABULARY when you narrate a row: submitted means the SII has the file and has not answered; accepted is filed; rejected means the SII refused it and it is NOT filed; dry_run is a rehearsal that never left the building and does not hold the period. A period with no row here has not been filed through Facturia — it says nothing about whether somebody filed it at the SII by hand, which we cannot see. Likely errors: invalid_period (a period_gte/period_lte filter that is not a real YYYY-MM month), invalid_cursor, empresa_required. | empresa?, period_gte?, period_lte?, status?, cursor?, limit? | read-only | empresas:read + tax:read | no |
list_expenses | The charges Facturia holds for this company — posted through add_expense, pulled from a bank feed, or parsed out of an uploaded cartola. These are the raw material the tipo-46 detector reads: a foreign-currency charge from a provider with no domicile in Chile is what becomes a factura de compra. READ-ONLY. Use it to answer "what did we import", "did that Anthropic charge land", and to see which charges already have a suggestion (suggestion_id) or a document (document_id) hanging off them. A charge with no suggestion is not necessarily ineligible: detection runs asynchronously after intake, and a CLP charge is never a tipo-46 candidate at all. list_tipo46_suggestions is the read that answers eligibility. Likely errors: invalid_period (period is not a real YYYY-MM month), invalid_limit (1–100), empresa_required. | empresa?, period?, status?, source?, cursor?, limit? | read-only | empresas:read + tax:read | no |
add_expense | Put one charge on file so the tipo-46 detector can see it — the way a foreign digital-services purchase (Anthropic, AWS, Google, Figma…) enters Facturia when there is no bank feed for the card it was paid with. It records a charge; it does NOT emit anything and nothing reaches the SII. WHAT MAKES IT A TIPO-46 CANDIDATE: the charge must be in a FOREIGN CURRENCY. A CLP charge is never one — a Chilean supplier issues their own document, and self-issuing a second would duplicate the purchase. The provider’s name is what the detector matches on, so send it as it appears on the statement; you do not send a RUT, because the SII’s generic foreign-provider RUT is what the document will carry. fx_rate IS REQUIRED whenever the currency is not CLP and Facturia will never invent one: the rate sets the peso base of a tax document, and guessing it would make us the author of the user’s tax position. Ask the user for the rate their card actually settled at (that is fx_source: card_rate) rather than looking one up. amount_original is the charge in its own currency; every CLP figure in this product is an integer with no decimals. The suggestion follows asynchronously — this call does not return one. Read list_tipo46_suggestions a moment later, then emit_tipo46 to confirm. Pass external_id if your source has a stable id for the charge: it is what stops the same charge being counted twice when a feed and a person both send it. Likely errors: fx_rate_required (a non-CLP charge with no rate), invalid_amount (amount_original must be positive), invalid_date (date must be YYYY-MM-DD), external_id_exists (this charge is already on file — it was NOT duplicated). | empresa?, provider_name, provider_country?, description?, amount_original, currency, date, fx_rate?, fx_source?, external_id? | writes · reversible | empresas:read + tax:write | no |
list_statements | The bank and card statements (cartolas) uploaded for this company, newest first, with how each import went: rows read, charges created, duplicates skipped, and the per-row errors the parser refused. This is where to look when a user says "I uploaded my cartola and nothing happened". READ-ONLY. Nothing is re-parsed and nothing is imported. A row with errors[] is the important one: the parser REJECTS a line whose amount, currency, date or decimal convention is ambiguous rather than guessing it, because a guessed row looks imported and nothing announces the mistake. Relay those reasons and line numbers to the user; they are the fix. Likely errors: invalid_cursor (the cursor did not come from a previous page of this list), invalid_limit (1–100), empresa_required. | empresa?, cursor?, limit? | read-only | empresas:read + tax:read | no |
upload_statement | Import a bank or card statement so its foreign-currency lines become charges the tipo-46 detector can read. A tool call cannot carry a file, so pass the cartola as base64 in file_base64; a cartola is a list of the user’s own charges, and there is no credential in it. IT IS QUEUED, NOT PARSED HERE. The answer is a job: say "importing", not "imported". list_statements for this company reports rows read, charges created, duplicates skipped and the per-row refusals once the job lands, and list_expenses shows what actually came in. RE-UPLOADING THE SAME FILE IS SAFE, and the answer tells you which happened: byte-identical bytes are recognised by their sha256 BEFORE anything is stored, and the ORIGINAL import job comes back — no second parse, no second copy, nothing double-counted. Overlapping (not identical) cartolas are safe too: rows are deduplicated on date, amount, currency and provider. THE PARSER REJECTS RATHER THAN GUESSES. A line whose amount, currency, date or decimal convention is ambiguous comes back as an error with its line number instead of a charge, because a guessed row looks imported and nothing would announce the mistake. Pass default_currency only when the file has no currency column, and it must be a FOREIGN currency — a CLP default would turn every row into a peso row, and a peso row never becomes a tipo 46. Likely errors: file_too_large (the limit is 8 MB), invalid_format (format is not a layout we know — leave it at auto unless the user told you the bank), default_currency_not_foreign (you passed CLP), file_empty (the base64 decoded to nothing — NOTHING was stored). | empresa?, file_base64, filename?, format?, default_currency? | writes · reversible · idempotent | empresas:read + tax:write | no |
dismiss_tipo46_suggestion | Reject a foreign-digital-services suggestion without emitting anything, with a reason the detector learns from. Use it when the user says a charge is not a foreign digital service, or that they already declared it. Nothing is emitted, nothing reaches the SII, and the charge itself stays on file. DISMISSING GIVES UP REAL MONEY, so say what it costs before you call it: the suggestion carries estimated_benefit.iva_credit, which is IVA the user could have taken as crédito fiscal. Read it back to them first — list_tipo46_suggestions has it — and dismiss only when they have said so. The other half of this pair is emit_tipo46, which confirms rather than rejects. The reason is stored on the row rather than logged, because a detector that is never told it was wrong stays wrong. other exists so the enum never blocks a dismissal you were told to make. Likely errors: suggestion_already_resolved (it was already confirmed or dismissed — nothing changed), resource_not_found (no such suggestion for this company), invalid_field (reason is not one of the four). | empresa?, suggestion_id, reason? | writes · reversible | empresas:read + tax:write | no |
list_bank_connections | The bank/card connections linked to this company, with each one’s status and when it last synced. A feed is what removes the assumption add_expense makes — that somebody already typed the charge in — by turning foreign-currency card movements into charges the tipo-46 detector reads. READ-ONLY. Nothing is linked, synced or disconnected, and no credential is ever returned: the bank credentials live with the provider, and Facturia holds only a link token. THE STATUS IS THE WHOLE POINT of this read. pending_link means a person still has to complete the link at link_url and nothing is arriving. requires_relink is the near-universal failure mode of bank feeds and means the feed went quiet — say so rather than reporting an empty month as "no charges". active is the only status that syncs. Likely errors: invalid_cursor (the cursor did not come from a previous page of this list), invalid_limit (1–100), empresa_required. | empresa?, cursor?, limit? | read-only | empresas:read + tax:read | no |
link_bank_connection | Create a bank or card connection for this company and get back the link_url a PERSON opens once to authorize it. From then on each sync turns foreign-currency movements into charges with the rate the issuer actually settled at, and the tipo-46 detector runs over them. NO CREDENTIAL PASSES THROUGH THIS CONVERSATION, and none may: the bank login happens at the provider, on the page this call mints, and Facturia stores a link token rather than a password. Never ask the user for a bank password here. IT IS NOT LINKED YET. A live connection comes back pending_link with a link_url: relay the link, and say that nothing arrives until somebody completes it. Do not report a feed as working until list_bank_connections shows it active. (A SANDBOX company has no bank to link, so its connection is born active with no link_url and nothing behind it.) A second call creates a SECOND connection rather than reusing the first, so read list_bank_connections before calling this — a company that already has a requires_relink feed needs that one re-linked, not a new one. Likely errors: invalid_provider (fintoc is the only provider), invalid_field (account_types or sync_frequency is not one of the accepted values), empresa_required. | empresa?, provider?, account_types?, sync_frequency? | writes · reversible | empresas:read + tax:write | no |
sync_bank_connection | Ask a linked bank/card feed to pull its movements immediately instead of waiting for the scheduled sync. Each foreign-currency movement becomes a charge, and the tipo-46 detector runs over the new ones. Nothing is emitted and nothing reaches the SII. IT IS QUEUED, NOT DONE. The answer is a job (type: connection_sync): say "syncing", not "synced". Pass job_id to check on one instead of starting another, or read list_expenses after a minute. A job that stays queued for a few minutes means the worker has not picked it up yet — that is "still running", not "failed". A connection that is not active is REFUSED rather than queued, on purpose: a sync job for an unlinked feed would run, do nothing, and report success. If you get connection_not_linked, read list_bank_connections — the fix is a person completing (or repeating) the link at its link_url, which you cannot do for them. In the SANDBOX the job comes back already succeeded with nothing imported: there is no bank to call, and a job left queued would never move. Likely errors: connection_not_linked (the feed is pending_link or requires_relink — a person must link it), resource_not_found (no such connection for this company), rate_limit_exceeded. | empresa?, connection_id?, job_id? | writes · reversible · idempotent | empresas:read + tax:write | no |
delete_bank_connection | Disconnect a bank or card feed: the link is revoked and no further movements are ingested. Charges the feed already created are KEPT — an expense that became a tipo 46 is part of the company’s F29, and its provenance does not stop mattering because the feed was turned off. ASK BEFORE YOU CALL IT. Reconnecting is not something you can do: link_bank_connection creates a new connection and a PERSON has to complete the bank link again, at their bank. From the moment this returns, foreign charges stop arriving on their own and nothing announces it — which is how a month quietly ends up with no crédito. If the feed merely went quiet, this is the wrong tool: a requires_relink connection needs re-linking, not disconnecting. Read list_bank_connections first. Likely errors: connection_already_disconnected (it was already off — nothing changed), resource_not_found (no such connection for this company), empresa_required. | empresa?, connection_id | writes · irreversible | empresas:read + tax:write | no |
confirm_total_payable is still required through the MCP layer. The REST API already refuses to file
without it, and that barrier is worth double here: it forces the figure to appear explicitly in the tool call,
which forces it to appear explicitly in what the agent tells the human before filing.
Account
| Tool | Description | Inputs | Annotations | Scopes | Confirmation? |
|---|---|---|---|---|---|
list_empresas | Which companies this connection can see — the disambiguation tool. Call it first whenever the user names a company loosely ("the consultancy", "my other one") or whenever you are unsure which company a request is about, and ask rather than guessing. Reports each company’s mode (test = sandbox, nothing it does reaches the SII), its status, and — the field that decides whether you can do anything at all — its onboarding_status. Anything other than emitting means it CANNOT issue documents yet and is almost always waiting on a person: call get_pending_human_action for that company and relay the handoff. Also reports emission_channel (how it emits: mercado = our engine, sfg = the SII free portal, which has a different setup entirely) and certification_track (what it is being certified for), plus capabilities when the engine supplies it — which document types it can issue RIGHT NOW, and what is blocking the rest. Prefer capabilities over your own inference: "it looks set up" is not the same fact as "the SII will accept a folio from it today". This is an account-level tool: it takes no empresa argument. | status? | read-only | empresas:read | no |
select_empresa | Remember one company as the default for the rest of this session, so you do not have to repeat its RUT on every call. Useful when this connection can reach several companies and the user has settled on one. An explicit empresa argument on any tool always overrides this, so the selection can never silently redirect a call that named its target. If the connection drops, the selection is lost and tools ask for the company again — nothing produces a wrong answer. Pass empresa: null to clear the selection. | empresa | writes · reversible · idempotent | empresas:read | no |
get_usage | How many documents this account has emitted in a billing period. Omit empresa for the whole account, or name one to see just that company. A document is billable when the SII accepts it — not when it is submitted. Rejected, failed and sandbox documents are never billed, so a sandbox-heavy month legitimately shows near-zero usage. Read-only; nothing here changes a plan or a bill. | empresa?, period | read-only | usage:read | no |
get_pending_confirmation | Read what happened to an approval you handed to a person. Call it with the confirmation_id from a pending_confirmation envelope — this is how you find out they came back, and it is the ONLY read that works for it: get_document_status needs an identifier the envelope does not carry. status is one of: pending (still waiting — the link is live and nothing has been done), confirmed (they approved it AND it executed; result is the document, filing or response the original tool would have returned), rejected (they said no — do not raise the same action again unless the user asks), expired (nobody decided in time; call the ORIGINAL tool again with the same arguments to raise a fresh approval), failed (they approved it and the execution was refused — error says why, and it will not change on a retry). Do NOT poll this in a loop. Call it when the user says they have done it, or when they ask you whether it went through. A person deciding on a page is a human-length wait, not a machine-length one. Likely errors: confirmation_not_found (no such approval for THIS connection — a confirmation belongs to the connection that raised it). | confirmation_id | read-only | none | no |
poll_events | The account’s event ledger, read forward from a checkpoint — the way to find out that something happened WITHOUT calling a read tool over and over hoping. Use it after handing a person a link ("did they sign it yet?"), after emitting ("did the SII accept it?"), or to catch up on everything since your last look. PASS after WITH THE next_cursor (or the last event id) YOU GOT LAST TIME. It pages FORWARD, oldest first, strictly newer than what you name, so you never re-read a row and never need to remember which ones you have seen. The first call may omit it. wait_seconds lets you wait for the next event instead of coming back empty: 0 (the default) looks once and answers immediately; up to 30 waits that long and returns as soon as something arrives. DO NOT CALL THIS IN A LOOP. Ask when the user asks, or once after they say they have done their part — a person deciding on a page is a human-length wait, and burning turns on an empty ledger helps nobody. This is an account-level tool: it takes no empresa argument. Filter with type if you only care about one kind. Likely errors: insufficient_scope (this connection was not granted webhooks:read — the user chooses that on the consent screen and an agent cannot widen its own grant), invalid_cursor. | after?, type?, limit?, wait_seconds? | read-only | webhooks:read | no |
Deliberately sparse. select_empresa is scoped so it cannot break the requirement that the server be able
to operate statelessly: an explicit empresa argument always wins, and losing the session loses a default, not
a wrong answer.
Onboarding
| Tool | Description | Inputs | Annotations | Scopes | Confirmation? |
|---|---|---|---|---|---|
create_empresa | Add a company (empresa) to this account. In test mode it is born emitting — no mandato, no certificate, no certification — which is the whole point of the sandbox. In LIVE mode it starts at the beginning of onboarding and a person has to do several things at the SII before it can issue anything; call get_pending_human_action right after to find out what. Ask the user for the RUT and the razón social rather than inferring them: a company created under the wrong RUT is a tenant whose documents would be issued in somebody else’s name. emission_channel decides HOW this company will emit — through Facturia’s own engine (mercado) or through the SII’s free portal (sfg) — and the two have different setup steps entirely. If the user has not said, ask; do not pick one. origen is REQUIRED for a live company and there is no default: it is the user’s own statement about how they emit TODAY, which nobody can read off the SII. ASK — do not infer it from emission_channel, which is a different question about the future. READ THE RESULT BEFORE PROMISING ANYTHING. It says whether the new company is inside THIS connection’s grant (reachable_by_this_connection): a grant scoped to a list of companies does not stretch to cover one created afterwards, and no agent can widen its own grant. When it is false, every other tool will answer empresa_not_allowed for this company and the fix is a human one, stated in the result. Likely errors: invalid_field (almost always origen on a live key — ask the user which of the three it is and call again), empresa_already_exists (this RUT is already on the account — list_empresas will show it), email_verification_required (a live company needs a confirmed e-mail address on the account), invalid_rut. | rut, legal_name, giro?, emission_channel?, origen?, contact_email?, doc_types?, resolucion?, resolucion_boletas? | writes · reversible | empresas:write | no |
get_onboarding_status | Where a company is in the SII onboarding process, and what it is blocked on. Call this when emission fails with empresa_not_emitting, or when the user asks "can I start invoicing yet". YOU CAN DRIVE THIS, not just report on it. When status is action_required, call get_pending_human_action for the handoff; mint the link with mint_onboarding_link (or request_mandato_signature for the mandato, upload_certificate for the certificate page, set_sfg_credential for the SFG clave); and when the person tells you they have done it, call recheck_onboarding_step so we go and look. If there is no company yet, create_empresa makes one. WHAT NO AGENT CAN DO, here or anywhere: the acts a person decides. The representante legal signs the mandato and the titular authorizes the go-live, each on a hosted page built for it — you carry the link to them and carry the result back, and you are never the record that they agreed. Facturia files the postulación itself, with the certificate it holds in custody, after the titular has approved it on a page. The declaración de cumplimiento starts the company's Información Electrónica de Compras y Ventas obligation FROM THE MONTH IT IS MADE, which is why choosing that month is a person's decision and not a progression. Never tell a user it has been scheduled or done. A step advances when WE READ THE FACT off the SII, never because somebody said so — including you. recheck_onboarding_step returns the status unchanged, and that is it working. | empresa? | read-only | empresas:read | no |
get_pending_human_action | The handoff for a company that is waiting on a PERSON. Call this whenever emission fails with empresa_not_emitting, whenever get_onboarding_status reports action_required, or whenever the user asks why they cannot invoice yet — and note that a company can be emitting AND still owe a decision, so read pending_action rather than deciding from status. TWO KINDS OF WAITING, ANSWERED DIFFERENTLY (rulings 2026-09-06). Most of these acts are OURS to perform: the certificate the empresa gave us belongs to its representante legal and does authenticate them at the SII, so Facturia files the postulación, runs the whole boleta habilitación and files the Declaración de Cumplimiento with it. What we need from the person is an AUTHORIZATION, not an errand — and when the handoff carries an approval, that is exactly what it is asking for: the act, who may take it (the titular, or the representante legal for cumplimiento_declaracion), and what approving causes. Send them to the page and let them decide; never approve it yourself and never say you did. The acts that DO stay the client’s stay so because the certificate cannot reach them, not because of a rule we chose: granting «Enviar Doctos.» in Mantención de usuarios and declaring the casilla de intercambio happen inside the usuario administrador’s own SII session, where a certificate we hold in custody has no standing. Say that — it is true, and it tells the user which of their people has to open it. Do not improvise a third: whatever pending_action names is the list, and it already accounts for what this deployment can and cannot reach. What comes back is a pending_action: a URL, ordered instructions in Spanish, what to have in hand first, exactly which person may do it, how we verify it happened, and the legal citation for why it cannot be delegated. RELAY THE SPANISH VERBATIM — it is written for the person who has to act, who is very often NOT the account holder (the account is the accountant’s; the certificate is a socio’s). Do not paraphrase the steps and do not invent any: if evidence is not captured, caveat says what we are unsure about, and that sentence must be relayed too. Read verification before promising anything. sii_probe means we will notice on our own. no_producer means we genuinely cannot see this one yet and a person has to tell us — never say it is being watched. What comes back also says how to GET the person a working link: action_url is a live one if there is one, and null if there is not — never send a user to a URL you built yourself. When it is null, link.mint names the call that mints one, and the tool for it is mint_onboarding_link (or request_mandato_signature for the mandato). You cannot mark any of this done. A step advances when we read the fact off the SII, never because someone said so — but when the user tells you they have finished, call recheck_onboarding_step, which asks us to go and look. It changes nothing by itself and says so. | empresa? | read-only | empresas:read | no |
request_mandato_signature | Mint the hosted page where the representante legal signs the mandato — the document that authorizes Facturia to act before the SII on this company’s behalf. Nothing is signed by calling this: it produces a URL to give to a person. THE URL COMES BACK ONCE and only its hash is stored, so relay it verbatim and do not summarise it away. It is SINGLE-USE: once the mandato is signed the link stops working, and calling this again revokes whatever link was outstanding. The signer is the representante legal of the company, who is very often NOT the account holder — the account may be the accountant’s. Say who has to open it. You cannot accept the mandato on anyone’s behalf and no tool here can: the acceptance is the person’s act, recorded against the exact text they were shown. Likely errors: mandato_already_signed (there is nothing left to sign — use get_mandato to see when and by whom), mandato_terms_draft (the terms are not published yet — a live company cannot accept them and this is ours to fix, not the user’s). | empresa? | writes · reversible · idempotent | empresas:read + mandato:write | no |
get_mandato | Read the mandato for a company: whether it has been signed, when, by whom, and against which version of the text. THIS IS HOW YOU FIND OUT THE HUMAN CAME BACK — call it after handing over a signing link instead of minting a second one, because minting revokes the outstanding link. status is none (nobody has been asked yet), pending (a signing link is outstanding), signed, or revoked. None of those is an error; none in particular is a state, not a 404. Read terms_current before trusting a signed mandato: false means the text was amended and the acceptance on file no longer binds, so the pipeline has already stopped trusting it and a fresh signature is needed. superseded_at says when that happened. acceptance_method says how it reached us — hosted (a person completed the page), dashboard (the titular in session) or api (an integrator attested it). Likely errors: none in normal operation — an absent mandato answers status: "none" rather than failing. empresa_not_allowed if this connection cannot reach the company. | empresa? | read-only | empresas:read + mandato:write | no |
mint_onboarding_link | Mint the hosted page for a step that a PERSON has to perform, and give the URL to whoever has to perform it. The step key comes from get_pending_human_action or get_onboarding_status — do not invent one; each company has its own checklist and it differs between the mercado and SFG channels. THE URL COMES BACK ONCE and only its hash is stored: relay it verbatim. It is reusable and lives about thirty days, and minting again REVOKES the previous link — so do not mint one to "check", use get_pending_human_action, which reports whether a live link already exists. These pages exist because the person who must act often has no account here: the account belongs to the accountant and the certificate to a socio. A link works without a login; a dashboard does not. The mandato is the exception — its link comes from request_mandato_signature. Likely errors: invalid_step (not a step on this company’s checklist, or not one a person performs), step_already_complete. | empresa?, step | writes · reversible · idempotent | empresas:read + onboarding:write | no |
recheck_onboarding_step | The resume half of the handoff: the person says they have done it, and this asks us to go and find out whether the SII agrees. Call it when a user tells you a step is finished. IT ACCEPTS A CLAIM AND PRODUCES NO FACT. It writes no status, moves no company, and returns the step exactly as it stood when you called — read BEFORE the check was queued. If you recheck an action_required step and read action_required back, that is the endpoint working: a step advances when we see the fact at the SII, never because somebody said so. Never report to the user that the step is now done. Read evidence_source in the answer and relay what it means. sii_probe: we will go and read the SII. own_record: the act lands in our own tables and we will see it. no_producer: we genuinely cannot observe this step yet — say so, do NOT say it is being watched, and tell them a person here has to confirm it. sandbox: a test company never opens a session against the SII, so there is nothing to re-read. Likely errors: invalid_step (not a step on this company’s checklist), step_already_complete. | empresa?, step | writes · reversible · idempotent | empresas:read + onboarding:write | no |
upload_certificate | Put the company’s digital certificate (.pfx / PKCS#12) into Facturia’s custody. It is what signs this company’s documents; without it nothing can be emitted. DO NOT ASK FOR THE PASSWORD. Call this with only empresa and it MINTS THE HOSTED PAGE where the person uploads the file themselves — a URL you hand over, no login, no transcript. That is the right answer almost every time: a certificate password typed into a chat window is stored in that conversation’s history wherever that history lives, and the certificate is the company’s legal signature. The person who has the .pfx is usually the representante legal or a socio, who has no account here anyway. Only pass file_base64 (the .pfx, base64-encoded) and password when the user has ALREADY volunteered both, unprompted. A tool call cannot carry a file, which is why they would have to be pasted. If this account already holds a certificate that the SII lists as an authorized user of this company’s RUT, pass reuse_from_empresa instead and no file or password is needed at all — prefer that whenever it applies. When the person has uploaded it on the page, call recheck_onboarding_step so we go and look. Likely errors: certificate_invalid_password, certificate_expired, certificate_rut_mismatch (the certificate does not belong to a person the SII authorizes for this RUT), certificate_not_authorized_user (for a reuse). | empresa?, file_base64?, password?, filename?, reuse_from_empresa? | writes · reversible | empresas:read + onboarding:write | no |
set_sfg_credential | For a company that emits through the SII’s free portal (emission_channel: sfg), Facturia needs the clave of the CERTIFICADO CENTRALIZADO the SII already holds for it — that is what authorizes the SII to sign each document server-side. Without it an SFG company cannot emit at all. DO NOT ASK FOR IT IN THIS CONVERSATION. Call this with only empresa and it MINTS THE HOSTED PAGE where the person types it themselves — no login, and the secret never reaches this transcript. That is the right answer unless the user has ALREADY volunteered the value unprompted. IT IS NOT THE CLAVE TRIBUTARIA. Facturia never asks for, holds or wants the taxpayer’s SII password. If a user offers you that instead, refuse it and say why: it is the key to their whole tax identity, and this is not it. The value is never readable back — GET reports only whether one is stored and since when. Likely errors: invalid_field (a synonym like clave or password is refused rather than coerced — the field is clave_centralizado), 422 on a mercado or sandbox company, which has no Portal MIPYME to sign anything. | empresa?, clave_centralizado? | writes · reversible | empresas:read + onboarding:write | no |
upload_certification_set_file | Put the .txt SET DE PRUEBAS the SII issued for this company into Facturia, so the certification round can be built from it. The SII issues one per taxpayer when it accepts the postulación and nothing downloads it for us — this is how the bytes get in, and certification cannot start without them. A tool call cannot carry a file, so pass it as base64 in file_base64. There is no secret in a set de pruebas — it is a list of test cases — so unlike a certificate password this is a reasonable thing to move through a tool call. THE BYTES MATTER. The file is ISO-8859-1 and its accents are part of the document: «Cajón» and «Pañuelo» are two of the items the SII has reported as reparos when they were lost. Base64 the file EXACTLY as downloaded; never open it in an editor and re-save it, and never retype its contents. This stores and parses the file. It does not run a round, does not contact the SII and does not spend a folio. Likely errors: set_file_unrecognized (those bytes are not a set de pruebas — a portal login page saved as .txt is the usual cause, and NOTHING is stored), 422 for a company whose lane has no certification. | empresa?, file_base64, filename? | writes · reversible | empresas:read + empresas:write | no |
request_go_live_authorization | Mint the page where the account holder authorizes the Declaración de Cumplimiento — the last SII step before a company can emit for real. This does NOT authorize it and no tool here can. WHY IT IS A HUMAN DECISION AND NOT A PROGRESSION: declaring puts the company into the electronic invoicing system immediately AND starts its Información Electrónica de Compras y Ventas obligation FROM THE MONTH OF THE DECLARACIÓN, whether or not it ever emits anything. Which month that is belongs to the customer and their accountant. The SII’s own page says the step may be postponed. So: relay the link, say plainly that declaring starts a monthly obligation from that month, and never tell a user it has been scheduled, requested or done. THE LINK IS SINGLE-USE and lives seven days: it records a decision with a tax consequence, so it stops working once the titular decides. Calling this again mints a fresh one and revokes whatever was outstanding. Likely errors: go_live_not_ready (the company has not reached the cumplimiento decision yet — error.details.blocked_on names what it is waiting on and error.details.next is the one call or link that moves it; relay that instead), go_live_not_applicable (an SFG company has no declaración to authorize), cumplimiento_already_filed (there is nothing left to authorize), subscription_required (the company has no active subscription — error.details.checkout_url is the page the ACCOUNT HOLDER opens to fix it; hand it over rather than describing it). | empresa? | writes · reversible · idempotent | empresas:read + onboarding:write | no |
get_account | The authenticated account and the credential in use: this key’s own scopes, mode and empresa allowlist, how many empresas it can reach (empresas_total) versus how many of those can actually emit right now (empresas_ready), and the account’s rate limits. Call this to learn what this connection can actually DO before promising a user anything — a grant missing tax:write, for instance, cannot file an F29 no matter how the rest of the conversation goes, and this is the one call that says so up front rather than after a refusal three turns later. Account-level: it takes no empresa argument, and a Facturia-Empresa header on it is refused. Likely errors: none beyond the ordinary authentication failures — a valid connection cannot make this call wrong. | none | read-only | none | no |
get_job | One endpoint for every asynchronous action this API starts: a folio request, sending a document by email, submitting a cesión, an RCV sync, a bank-statement import, or a bank-connection sync. Call it with the job_id a 202 response handed you. READ type FIRST — it decides the shape of result and how soon it is worth asking again: about 10s for document_send/statement_import, 15s for connection_sync, 30s for rcv_sync/cesion_submit, 60s for folio_request (the SII’s timbraje is a scripted portal walk end to end; asking sooner only earns a 429). Nothing on this surface can finish faster than about 10 seconds regardless of type — that is how often the worker claims new work. For rcv_sync, this narrates exactly what sync_rcv’s own job_id check-back does — call either one, interchangeably. A job created in the OTHER mode (sandbox vs. live) does not exist for this connection: that is resource_not_found, not a refusal that would confirm it exists. This is account-level and takes no empresa argument — a job belongs to the account, not to one company, and a Facturia-Empresa header on it is refused. Likely errors: resource_not_found (wrong job_id, or it belongs to the other mode), insufficient_scope (this connection can watch some job types and not others — the scope needed is whichever scope STARTS that kind of job, plus the read scope for what it acts on; a documents:read-only grant, for example, cannot watch an rcv_sync job). | job_id | read-only | empresas:read | no |
list_usage_records | One record per document the SII actually ACCEPTED — the raw ledger get_usage rolls up into a summary. A document is recorded the moment it is accepted, never on submission; rejected, failed and sandbox documents never appear here, and a correction is a compensating record with negative quantity, never an edit of the original. This is a report, not a meter: the plan is flat and unlimited, so nothing here is priced per document — read it to reconcile "how many did we actually get billed for" against your own records, never to predict a bill. Omit empresa for every empresa this connection can reach; name one to filter to it. Likely errors: invalid_period (period is not a real YYYY-MM month), invalid_limit (1–100). | empresa?, period?, metric?, cursor?, limit? | read-only | usage:read | no |
get_event | One event from the account’s own ledger — the exact object poll_events pages through and a webhook delivery would carry — for a caller reconciling by id (an id quoted in a support ticket, or a webhook payload received out of order) rather than by walking the ledger forward. Account-level: it takes no empresa argument. Likely errors: resource_not_found (no such event, or it belongs to the other mode), insufficient_scope (this connection was not granted webhooks:read — the user chooses that on the consent screen and an agent cannot widen its own grant). | event_id | read-only | webhooks:read | no |
get_mandato_terms | The exact text (and version) a representante legal is asked to sign when request_mandato_signature mints their link — quote it, or a faithful summary of it, rather than describing the mandato from memory. CHECK status FIRST. draft means the text has not finished attorney review and is not yet binding — never present a draft as terms someone is about to sign. annexes[] carries the documents the contract itself calls «parte integrante» (price, personal data, the automation matrix) and belongs alongside text for anyone actually deciding whether to sign. This needs no scope from the grant — /v1 serves it unauthenticated, the same as the error-code dictionary, because the text is a constant with no tenant data in it. Omit version for the text in force today. Pass one from available_versions to read a draft of what is coming next, or the exact version an old acceptance recorded (its receipt names it). Likely errors: invalid_field (an unknown version). | version? | read-only | none | no |
list_webhook_endpoints | Webhook endpoints configured for the account: URL, which event types each receives, which empresas it is restricted to, and status. Signing secrets are never included. THIS SURFACE ONLY READS. Creating, updating or deleting an endpoint, and reading or rotating its secret, stay on REST and the panel — an agent has no HTTPS endpoint of its own to point one at, and the founder keeps webhook writes off the MCP regardless. Account-level: it takes no empresa argument. Likely errors: insufficient_scope (this connection was not granted webhooks:read). | cursor?, limit? | read-only | webhooks:read | no |
list_webhook_deliveries | Delivery attempts for one webhook endpoint, newest first — response status, attempt count, and whether a retry is scheduled. Use it to answer "did our webhook actually fire for that event" rather than guessing from silence on the receiving end. THIS SURFACE ONLY READS: redelivering an event stays on REST and the panel. Likely errors: resource_not_found (no such webhook_endpoint_id), insufficient_scope (this connection was not granted webhooks:read). | webhook_endpoint_id, status?, event_id?, cursor?, limit? | read-only | webhooks:read | no |
get_certification | The set de pruebas round for a mercado-track empresa: which round is running, whether the SII set file is stored, what inputs the round still needs (slots[].supplied), any problems or notices the last build hit, every envío sent so far and its verdict. One read for the whole thing — there is no separate endpoint per envío. submittable is null until a build has been attempted at all, which is not the same as false. An entry in problems blocks the next round; an entry in notices is something the build decided on the empresa’s behalf and is worth mentioning, never a blocker. Likely errors: empresa_not_allowed, 422 for a company whose lane has no certification (an SFG-only empresa, or one with no certification_track). | empresa? | read-only | empresas:read | no |
set_certification_inputs | The set states several things by implication and never spells them out: who issued each purchase in the libro de compras, what the CodRef-2 correction says, which factura invoiced a guía the set marks “facturada”, the factor de proporcionalidad, the SII-issued folio de notificación, and — for a boleta round — the abbreviated unidades and ten everyday boletas. get_certification’s slots[] says which of these THIS empresa’s round still needs. THE MERGE IS PER TOP-LEVEL SLOT: omit a slot to leave it as it is, send null to CLEAR it, send a value to REPLACE it whole. Restating proveedores restates every one of them — that is both what a form submits and the only way to remove one. NOTHING IS WRITTEN WHEN ANY SLOT IS INVALID, not even the valid ones — a partial write would leave the empresa in a state nobody asked for. This writes facts. It does not run a round, does not contact the SII and does not spend a folio — the round runs on its own schedule; call get_certification afterwards to see it reflected. Likely errors: certification_input_invalid (every problem is listed in error.details.problems, not just the first). | empresa?, receptor?, giro_recep_emisor?, text_correction?, proveedores?, iva_no_rec_codes?, libro_treatments?, libro_guias?, factor_proporcionalidad?, libro_boleta_folio_notificacion?, boleta_unidades?, boleta_muestras?, permiso_usuario_rut? | writes · reversible · idempotent | empresas:read + empresas:write | no |
list_onboarding_evidence | Cláusula 4.4 of the mandato obliges Facturia to render account of every act performed on this empresa’s behalf: every probe, envelope, verdict, signature and authorization, newest first, with the date it happened and the date it will be purged (six years out — Código Tributario prescripción extraordinaria). Filter with step or kind to find one act. FOR THE WHOLE HISTORY AS A DOWNLOADABLE FILE: GET …/onboarding/evidence/export answers a raw CSV file with no public-link mechanism, so this server does not expose it as a tool — page through this list instead, or tell the user to ask a person with direct API access for the export. Likely errors: empresa_not_allowed, invalid_cursor, invalid_limit. | empresa?, step?, kind?, cursor?, limit? | read-only | empresas:read | no |
get_certificate | Metadata about the digital certificate (.pfx) in Facturia’s custody for this empresa — never the material itself, which this API never returns to anybody. Subject, issuer, expiry, status, and which RUTs the SII lists it as an authorized user of. null means no certificate has been uploaded yet — that is an answer, not an error; upload_certificate mints the page for whoever holds the file. Likely errors: empresa_not_allowed. | empresa? | read-only | empresas:read | no |
get_sfg_credential | Whether Facturia holds the SII «certificado centralizado» clave for an SFG-channel empresa, and since when — never the value, which this API cannot return to anybody, not even the person who set it. Only meaningful for a LIVE emission_channel: sfg empresa; a mercado or sandbox one has nothing this could apply to. Likely errors: not_applicable_for_channel (this empresa emits through mercado, which signs with its own certificate and never asks the SII to sign), not_applicable_in_test_mode. | empresa? | read-only | empresas:read | no |
delete_certificate | Permanently destroy the digital certificate (.pfx) Facturia holds for this empresa. EMISSION STOPS the moment this runs — the empresa is walked back to certificate_pending and nothing can be signed until a new one is uploaded. This is cláusula 7.3’s custody-ending right, and it is irreversible: the bytes are destroyed, not archived. REQUIRES confirm_delete: true. Without it, this tool refuses before making any call, precisely because there is no undo once it runs. Likely errors: none blocking — this always succeeds (204) whether or not a certificate existed, because "you hold nothing of mine" is the same outcome either way. | empresa?, confirm_delete? | writes · irreversible · idempotent | empresas:read + empresas:write | no |
delete_sfg_credential | Permanently destroy the SII «certificado centralizado» clave Facturia holds for this SFG-channel empresa. The next SFG emission will refuse for want of a signing clave until a new one is set — unlike deleting the certificate, this does NOT walk the empresa’s onboarding backwards, because the clave can be re-supplied in one call. Irreversible: the bytes are destroyed. REQUIRES confirm_delete: true. Without it, this tool refuses before making any call. Likely errors: not_applicable_for_channel, not_applicable_in_test_mode. | empresa?, confirm_delete? | writes · irreversible · idempotent | empresas:read + empresas:write | no |
revoke_onboarding_link | Revoke the hosted page a previous mint_onboarding_link call minted for one step, before it expires on its own. Use it if the wrong link went out, or the person it was for should no longer have it. Succeeds (204) whether or not a live link existed — "nobody can use this any more" is the same outcome either way. This does not touch the step’s status: a person can still be walked through it again with a fresh mint_onboarding_link call. The mandato has no link here: request_mandato_revocation_link is the mandato’s own revocation door, for the SIGNED mandato itself — a different thing from a hosted link. Likely errors: none blocking in normal use. | empresa?, step | writes · reversible | empresas:read + onboarding:write | no |
request_mandato_revocation_link | Mint the hosted page where the representante legal exercises cláusula 7.1: revoking the mandato at any time, without cause and without penalty. This tool only MINTS the page — the revocation itself is the representante legal’s own act, gated by their RUT as recorded on the mandato they signed, and this tool cannot perform it and cannot see whether they have. REVOKING STOPS FACTURIA FROM ACTING FOR THIS EMPRESA AT THE SII: every queued document is cancelled and the empresa is suspended the moment the page is completed. Say that plainly when you hand the link over — do not present it as a routine settings change. The link lives about a century, because the right it addresses never expires; it is returned EXACTLY ONCE, so relay it verbatim. Likely errors: mandato_not_signed (there is no mandato in force to revoke — get_mandato says the current state), mandato_revocation_link_unavailable (the mint failed; the mandato still stands, retry). | empresa? | writes · reversible · idempotent | empresas:read + mandato:write | no |
None of these mark a step done, and that is not an omission: a step advances when we read the fact from the
SII, never because someone said so. get_pending_human_action is the one to call when an issuance fails with
empresa_not_emitting — it returns the full handoff, written for the person who has to act, who very often is
not the account holder. Relay it as written: if a caveat comes back, that sentence goes too.
Sandbox
| Tool | Description | Inputs | Annotations | Scopes | Confirmation? |
|---|---|---|---|---|---|
sandbox_set_onboarding_step | REHEARSAL ONLY, and only for a SANDBOX company: force one onboarding step into a state, so you can practise the handoff before doing it with somebody’s real company. A sandbox empresa is born emitting with every step skipped, which means the most important thing this surface does — read what a person must do, mint the link, ask for the recheck — has no sandbox to be tried in without this. Set mandato to action_required and get_pending_human_action returns the real mandato handoff; set certificate to failed and see what a failed step reads like. completed and skipped work too. IT ASSERTS A STATE, IT DOES NOT RUN ANYTHING. Nothing is verified, nothing is sent to the SII (a sandbox company never talks to it at all), and no real onboarding works this way: outside the sandbox a step advances only when we read the fact off the SII. Do not let a rehearsal teach you to describe the real thing as something an agent can move. A LIVE COMPANY IS REFUSED, here and again by the API. There is no version of this that touches real data. Likely errors: sandbox_only (you named a live company), invalid_field (not a step on this company’s checklist, or not a status the step model has). | empresa?, step, status, tipo_dte? | writes · reversible | empresas:read + empresas:write | no |
sandbox_advance_clock | REHEARSAL ONLY, and only for a SANDBOX company: move its clock forward by whole days, so deadlines that are days away can be reached in a conversation. THE CASE THIS EXISTS FOR is the 8-day acuse window on a received document. A supplier document injected into the sandbox sits at day 8 forever without this, so «you have 2 days left to respond» — the thing the reception tools are for — could not be seen at all before emitting for real. The offset is ADDITIVE and cumulative: advancing 5 days twice lands 10 days out, and real time keeps passing underneath. It cannot be moved backwards; a company whose clock has gone too far is one to recreate rather than rewind. A LIVE COMPANY IS REFUSED, here and again by the API — a clock that could be moved on real data would be a way to walk a real deadline past its date. Likely errors: sandbox_only (you named a live company), invalid_field (days must be a whole number from 1 to 400). | empresa?, days | writes · reversible | empresas:read + empresas:write | no |
Both of these are REHEARSAL levers and touch test companies only: the API refuses them on a live empresa
and so do the tools, before calling it. They exist because a sandbox empresa is born emitting with every step
skipped, so the handoff — the most important thing this surface does — could not be tried without a live company
and the real SII. Forcing a step ASSERTS a state; it does not advance anything. Outside the sandbox a step moves
when we read the fact off the SII, never because somebody said so.
undefined
| Tool | Description | Inputs | Annotations | Scopes | Confirmation? |
|---|---|---|---|---|---|
request_folios | Request a new folio range for one document type — the timbraje, which is a REAL act at the SII against a finite resource, not a counter we increment. Every document consumes a folio; when a tipo runs out, no_folios_available is what the emission tools answer. CHECK get_folio_status FIRST. A tipo reporting ok needs nothing, and a tipo reporting low needs nothing either: replenishment below the company’s own threshold is AUTOMATIC (auto_request_folios), so asking again by hand while the automatic request is already in flight registers a SECOND range that nobody will use. Call this when the user explicitly asks for folios, or when exhausted is blocking emission and nothing has been requested. AN UNUSED RANGE IS NOT FREE: it must be annulled, and enough unused ranges block further timbraje for the company. So one call, then wait — never a loop. IT TAKES MINUTES, NOT SECONDS: the SII has no timbraje webservice, so this is a scripted walk through its portal. You get a job back; check on it by calling this tool again with job_id, and wait 60 seconds between checks (that is the Facturia-Poll-After the API publishes for this job type). On succeeded the job’s result names the range actually registered — say the from–to folios and the caf_id, and note that quantity there is what the SII GRANTED, which can be smaller than what was asked for. On failed read range_registered: if it is TRUE a range was already registered before the failure, so CALLING AGAIN WOULD ASSIGN ANOTHER ONE — do not retry, say what happened and let a person look. If it is false, the failure is safe to retry once the message says why. A sandbox company answers succeeded immediately over its virtual range (1–1.000.000) and nothing is asked of the SII; say that rather than reporting a timbraje that never happened. A company on the SII free portal (emission_channel: sfg) has no timbraje at all and is refused with not_applicable_for_channel — relay that refusal, it is the truth and not a bug. An insufficient_scope naming folios:write means this CONNECTION predates the scope: the user has to disconnect Facturia in their MCP client and connect it again, ticking «Pedirle folios al SII y administrar tus CAF». No panel screen widens an existing grant. Likely errors: not_applicable_for_channel (sfg company — there is nothing to request), invalid_tipo_dte, subscription_required (error.details.checkout_url is the page the ACCOUNT HOLDER opens — pass the link on verbatim). | empresa?, tipo_dte?, quantity?, job_id? | writes · irreversible | empresas:read + folios:write | no |
list_cafs | The folio authorizations (CAF) on file for this company: one row per range, with its tipo_dte, its first and last folio, whether it is still active, and whether it came from a timbraje we ran (source: sii_request) or from a file somebody uploaded (source: upload). Use get_folio_status instead when the question is "can I emit" — that one reports remaining stock per tipo. This one answers "which ranges exist", which is what you need before deactivating one or before deciding whether a range is already on file. A company on the SII free portal (emission_channel: sfg) holds NO CAF — not "none yet", none ever, because the SII assigns each folio at emission. An empty list from an sfg company is the truthful answer, not a gap. Likely errors: empresa_required, empresa_not_allowed. | empresa? | read-only | empresas:read | no |
upload_caf | Register a CAF file the company downloaded from the SII itself, so Facturia can draw folios from that range. The alternative to request_folios: this one registers a range the SII ALREADY granted, and asks the SII for nothing. Pass the file’s bytes as caf_xml_base64 (base64 of the raw <AUTORIZACION> XML), or the XML itself as caf_xml. READ THE FILE, DO NOT ASK A PERSON TO PASTE IT: a CAF carries <RSASK>, the private key that signs the timbre of every document drawn from the range, so it belongs in a file the user points you at — never in the conversation, never echoed back, never quoted in your answer. Nothing of the file is repeated in this tool’s result. The range is the identity: re-registering a file already on file returns the existing range rather than a second one, so a retry after a timeout is safe. A new range never lowers the folio counter — it waits until the range in use is spent, so uploading early does not abandon folios you paid for. Refused for a sandbox company (not_applicable_in_test_mode): sandbox folios come from a virtual range no CAF backs, so a real CAF stored against one could never be drawn from — and the file is live SII key material. Refused for an sfg company for the same reason list_cafs returns nothing there. An insufficient_scope naming folios:write means this CONNECTION predates the scope: the user disconnects Facturia in their MCP client and connects it again, ticking «Pedirle folios al SII y administrar tus CAF». A grant cannot be widened in place. Likely errors: caf_belongs_to_another_empresa (the CAF names a different RUT — every document drawn from it would be rejected AFTER the folio was spent), not_applicable_in_test_mode, not_applicable_for_channel (sfg company), invalid_field (not a CAF, or one that is not usable — the message says which). | empresa?, caf_xml_base64?, caf_xml? | writes · reversible · idempotent | empresas:read + folios:write | no |
deactivate_caf | Stop Facturia from drawing new folios from one CAF range. History is preserved — documents already emitted from it keep their folios, and the range is not deleted — but the range stops offering folios and THIS CANNOT BE UNDONE from here. Only do this when the user asked for it by name: a range with folios left that gets deactivated is authorized, paid for, and now unusable, and the folios in it still have to be annulled at the SII. Call list_cafs first and tell the user which range you are about to switch off, by its folio numbers. Requires confirm_deactivate: true in the arguments — a deliberate second act, because the first one is not reversible. An insufficient_scope naming folios:write means this CONNECTION predates the scope: the user disconnects Facturia in their MCP client and connects it again, ticking «Pedirle folios al SII y administrar tus CAF». Nothing in the panel widens an existing grant. Likely errors: resource_not_found (no CAF with that id belongs to this company — list_cafs has the real ids), empresa_not_allowed. | empresa?, caf_id, confirm_deactivate? | writes · irreversible · idempotent | empresas:read + folios:write | no |
undefined
| Tool | Description | Inputs | Annotations | Scopes | Confirmation? |
|---|---|---|---|---|---|
get_empresa | The empresa exactly as /v1 holds it: onboarding status, which SII environment it emits into, what it can actually emit RIGHT NOW, and any resolución it has claimed or proven. list_empresas shows the same row across every company on this connection; call this one when you need the full detail for a single empresa, or to re-read it after update_empresa. Read capabilities.tipos for the emission TRUTH — doc_types is only the declaration, and the two can legitimately disagree while certification is in progress. resolucion.verified (and resolucion_boletas.verified) is what actually matters when this empresa arrived claiming a resolución: false means the claim is still unproven, however confident it sounded when it was declared. Likely errors: empresa_not_allowed (this connection cannot reach the company), empresa_required (ambiguous and no default set). | empresa? | read-only | empresas:read | no |
update_empresa | Update an existing empresa’s stated facts: legal_name, giro, acteco, address, codigo_sucursal, contact_email, doc_types, certification_track, origen, emission_channel, resolucion, resolucion_boletas or status. Every field is optional — send only what changes; everything else is left exactly as it was. ambiente cannot be sent here: it is derived, never stated. THIS IS THE DOOR TO DECLARE AN EXISTING SII RESOLUCIÓN, for an empresa that already invoices electronically through another provider. resolucion.numero (NroResol) and resolucion.fecha (FchResol) are printed on the carátula of any invoice it has ALREADY issued — read them off a recent one rather than sending anyone to Mi SII. DECLARING SKIPS NOTHING BY ITSELF: the empresa still needs its certificate and mandato, and resolucion.verified on the response stays false until Facturia proves the claim by asking the SII to timbrar one declared document type. NEVER tell the user the resolución is confirmed from this call — call get_empresa afterwards and read verified. resolucion_boletas is the same claim for boleta electrónica: a date, no número, sent alongside doc_types containing 39 or 41. Send null for either to clear it. emission_channel IS A REAL ROUTING CHANGE, not a preference update: moving to sfg sets ambiente to produccion in the same write and switches which checklist, which folios and which driver govern this empresa from now on. Narrate the BEFORE and AFTER from the response, not just that the call succeeded. doc_types is the DECLARATION, never the emission truth — call get_empresa and read capabilities.tipos for what can actually be emitted today. Adding 39/41 re-opens certification for boleta on mercado; sending them on sfg (or moving to sfg while they are declared) is refused, because the free portal has no boleta form. Likely errors: invalid_field (a value the SII layer refuses, or doc_types sent empty), doc_type_not_supported_on_channel (39/41 on sfg), empresa_not_allowed. | empresa?, legal_name?, giro?, acteco?, address?, codigo_sucursal?, contact_email?, doc_types?, certification_track?, origen?, emission_channel?, resolucion?, resolucion_boletas?, status? | writes · reversible · idempotent | empresas:read + empresas:write | no |
get_automation_settings | Every automatic-write switch this empresa has: auto-emitting tipo 46 documents, auto-filing the F29, auto-requesting folios, auto-accepting received documents, and require_confirmation — whether a person has to approve an agent-initiated write on this empresa before it runs. Everything defaults OFF except auto_request_folios. Likely errors: empresa_not_allowed. | empresa? | read-only | empresas:read | no |
update_automation_settings | Turn an automation on or off, or tune its threshold. Send only the fields that change. AN AGENT MAY NOT TURN require_confirmation OFF THROUGH THIS TOOL. That flag exists so a PERSON, not an agent, is the record that this empresa’s money-moving writes may run unattended — sending require_confirmation: false is refused right here, before anything reaches Facturia, no matter who is asking or why. Turning it ON is fine and safe at any time. To turn it OFF, a person does it from the panel themselves. auto_accept_received needs auto_accept_received_after_days (1–7) the first time it is turned on, in the SAME call. auto_file_f29 files a binding declaration before day 20 with nobody reviewing it — check auto_file_f29_max_payable before assuming that is safe for this company. Likely errors: unknown_setting (not a field this object has), invalid_field, invalid_automation_settings (a combination the domain refuses, e.g. auto_accept_received with no day count). | empresa?, auto_emit_46?, auto_emit_46_max_amount?, auto_file_f29?, auto_file_f29_day?, auto_file_f29_max_payable?, auto_request_folios?, auto_request_folios_threshold?, auto_accept_received?, auto_accept_received_after_days?, require_confirmation?, rcv_sync_frequency?, auto_characterize_purchases?, honor_estado_contable_override? | writes · reversible · idempotent | empresas:read + empresas:write | no |
get_branding | The logo, footer text, and whether a factura defaults to cedible, that this empresa’s documents render with. default_pdf_format only ever reads carta — no thermal layout exists on this API. Likely errors: empresa_not_allowed. | empresa? | read-only | empresas:read | no |
update_branding | Set the logo URL, footer text, or whether a factura (tipo 33/34) defaults to cedible. logo_url MUST already be a URL this empresa hosts — this tool never accepts image bytes and a result here never inlines one; if the user only has a file, they need to host it somewhere first and give you the address. default_pdf_format accepts only the literal carta. Likely errors: unknown_setting, invalid_field (a default_pdf_format other than carta). | empresa?, logo_url?, default_pdf_format?, cedible_by_default?, footer_text? | writes · reversible · idempotent | empresas:read + empresas:write | no |
list_counterparties | The counterparties this empresa has emitted a factura, nota or guía to (tipos 33, 34, 56, 61 — a boleta’s receptor is a consumer and never appears here), each with whatever WhatsApp contacts are already on file. DERIVED from the documents already emitted, not a stored list: a counterparty this empresa stops invoicing simply stops appearing here, and one it has never invoiced can still carry a contact. Use it to find the rut for list_counterparty_contacts and set_counterparty_contact. NOT PAGINATED: the window is a bound on the last several hundred documents scanned, not a page of results — has_more is always false. Likely errors: empresa_not_allowed. | empresa? | read-only | empresas:read | no |
get_counterparty | The stored record for ONE counterparty RUT: razón social, giro and address, plus source (where each came from — the address book itself, a prior emission, or nothing yet) and when it was last updated. THIS IS WHAT A FACTURA’S <Receptor> BLOCK IS BUILT FROM. GiroRecep, DirRecep and CmnaRecep are optional in the SII’s schema and mandatory to its business validation: a document missing them is ACCEPTED as an envelope and then REFUSED as a document, with the folio spent either way. Which of them a given document actually needs depends on the tipo and the channel, and the emission is the authority on that — it names anything still missing as receptor_incomplete before a folio is allocated. Read this before promising a user their invoice will go out, and use update_counterparty to fill anything blank. list_counterparties is the DERIVED list of who has been invoiced; this is the STORED record of who they are, and a counterparty can have one without the other. Likely errors: invalid_rut, resource_not_found (nothing on file for that RUT yet — which is a normal state, not a fault), empresa_not_allowed. | empresa?, counterparty_rut | read-only | empresas:read | no |
update_counterparty | Write down what a counterparty’s razón social, giro and address ACTUALLY are, so every future document to that RUT carries them without anybody being asked again. Creates the record if there is none. Send only what you know; anything omitted is left exactly as it was. THIS IS THE ANSWER TO receptor_incomplete. A factura whose receptor has no giro/dirección/comuna is accepted by the SII as an envelope and then refused as a document — the folio is spent either way, and seven production folios were lost that way on 2026-09-08. Facturia refuses such a request before allocating a folio and names the missing fields; store them here (once) and the emission goes through. NEVER INVENT, GUESS OR INFER ANY OF THIS. Not from the company name, not from a web search, not from another counterparty with a similar name. A wrong dirección on a factura is a defect in a legal tax document. Everything you send must have come from the user or from a document they gave you. THE CAPS ARE REFUSALS, NOT TRUNCATIONS: giro 40 characters, dirección 70, comuna 20, ciudad 20, razón social 100. A longer value is refused right here, before anything is written — because a shortened name of trade changes what the document says. If a real giro is longer than 40, ask the user for their own shorter form of it. Likely errors: invalid_field (a value past its cap, or nothing to change), invalid_rut, empresa_not_allowed. | empresa?, counterparty_rut, legal_name?, giro?, address? | writes · reversible · idempotent | empresas:read + empresas:write | no |
list_counterparty_contacts | Every contact this empresa has recorded for one counterparty RUT — the address book, primary first. notifiable says whether a contact would actually be messaged (primary, opted in, not opted out); v1 notifies ONLY the primary. Likely errors: invalid_rut, empresa_not_allowed. | empresa?, counterparty_rut | read-only | empresas:read | no |
set_counterparty_contact | This is “who do I message about this invoice” for a counterparty: create a contact by omitting contact_id, or edit one by passing it. The FIRST contact for a counterparty becomes primary automatically unless you say otherwise; only the primary is ever notified. THE OPT-IN IS AN ATTESTATION, NOT A FORMALITY: pass opt_in: true and opt_in_method only when you have an actual basis for it — WhatsApp’s own policy is that merely holding a number is not consent. Omitting it stores the contact as an address-book entry that is never messaged, which is a legitimate and common state, not an error to fix. AN OPT-OUT IS ONE-WAY: pass opted_out: true only to RECORD one a person told you about through another channel. It can never be un-set through this tool, and a number that opted out can never be re-added under a new contact — that refusal is the point. Likely errors: invalid_field (creating without name or phone), invalid_phone (not E.164, e.g. +56912345678), optin_method_required (opt_in: true with no method), contact_opted_out (this number, or this contact, cannot be written to), resource_not_found (bad contact_id). | empresa?, counterparty_rut, contact_id?, name?, phone?, counterparty_name?, primary?, opt_in?, opt_in_method?, opt_in_attested_by?, opted_out? | writes · reversible | empresas:read + empresas:write | no |
delete_counterparty_contact | Delete a contact from a counterparty’s address book. A HARD DELETE with no undo — and it cannot be used to work around an opt-out: a contact that already opted out is refused (contact_opted_out), because the tombstone row is what keeps that number from being re-added later. Likely errors: contact_opted_out (this contact opted out and its record is kept on purpose). | empresa?, counterparty_rut, contact_id | writes · irreversible · idempotent | empresas:read + empresas:write | no |
How files come back
MCP gives a tool two ways to return a file: embedded as a base64 blob, or as a resource_link the client only
fetches if and when it needs the bytes. Facturia MCP always uses resource_link, never embedded blobs.
// → get_document_file
{ "empresa": "77928532-4", "document_id": "doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N", "format": "pdf" }
// ← contenido del resultado
[
{ "type": "text", "text": "Factura 33 folio 412, enlace válido 10 minutos." },
{
"type": "resource_link",
"uri": "https://ver.facturia.cl/l/3d9f2b8a1c?exp=1755184211",
"name": "factura-33-412.pdf",
"mimeType": "application/pdf"
}
]Three reasons:
- Context economy. A base64-embedded signed PDF can be hundreds of kilobytes of content injected into the model's context for a document nobody asked to have read aloud. A link costs one sentence.
- Client compatibility. The REST API's
pdf_url/xml_urlURLs require a key. Most MCP hosts have no way to attach Facturia's OAuth token when resolving a resource link — nor should they have to. - Safer than the REST default.
get_document_fileswitches on the mechanism REST already publishes as opt-in (signed public links, with an expiry, unauthenticated) and shortens its life to 10 minutes, scoped to a single document and never listable.
What is not on the agent surface
Requesting a CAF, managing webhooks, branding and RCV characterisation writes stay REST- and dashboard-only: they are one-off or rare actions with a UI far better than a chat turn, and dynamic client registration means the calling software is not verified the way a dashboard session is.
There are also no void (credit_note) or retry (retry) tools in this cut.
Besides tools, this server also publishes a prompt (onboard_empresa, the full onboarding walkthrough) and
resources that point at this documentation and at the site's llms.txt, so an agent can discover them from
inside the connection.
Connect an agent
mcp.facturia.cl — the same engine, a different front door. How to connect Claude, what happens during OAuth and why a sandbox grant is a hard limit.
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.