Skip to main content
POST
Fiscal callback
This endpoint is inbound — your fiscal provider POSTs to it whenever a fiscal document is authorized, rejected, denied, cancelled, or errors out. It is the canonical, multi-country fiscal callback API that accepts payloads from BR, CO, EC, CL, AR, VE. Fire validates the payload, stores the authority’s verdict on the order, and dispatches order.invoiced / order.reversed to your Integration Flows — for every country. What you send here is what the event consumers read: see From the callback to the events.
This endpoint is asynchronous. Fire authenticates, runs the source-of-truth + tenant guard, deduplicates, pre-marks the order as processing, and enqueues the callback — then returns 202 Accepted with a webhookEventId (typically in under 100 ms). The fiscal_documents / orders.fiscal update happens a moment later in a background worker (usually within ~2 seconds). To check the processing outcome, poll GET /v1/webhooks/events/{webhookEventId}. Client-fixable problems (bad payload, wrong eventId, wrong tenant, action reusing another event’s id) are rejected synchronously with 4xx before the 202.

How it complements order.completed and order.cancelled

order.completed and order.cancelled carry an order’s business state. The fiscal callback carries the fiscal state (SEFAZ / DIAN / SRI / SII / AFIP / SENIAT authorization). Together they form the lifecycle below: After an authorized callback is processed, Fire emits order.invoiced; after a cancelled one, order.reversed. Both fire for every country — the country travels in the event’s fiscal block, not in its name. Later order events for the same orderId also reflect the new status in data.lastKnown.fiscal. For the whole fiscal picture — numbering, callback and events, and how they fit together — see the fiscal overview.

Authentication

This endpoint requires an API key with the webhooks:fiscal scope, vendor-scoped to the account that owns the order. Unscoped or account-only keys are rejected with 403 Forbidden.
string
required
Your Fire API key. Generate one from the dashboard at Developers → API Management, with scope webhooks:fiscal and a vendor binding for the account whose orders this key may update.
string
Legacy alias: the same Fire API key, as Bearer pk_live_.... Prefer x-api-key; if both are sent, x-api-key is used.

Request body

The body is a JSON object with these top-level fields. Everything canonical lives up here; document carries ONLY your country authority vocabulary, picked by country — see Document by country below.
string
required
ISO 3166-1 alpha-2 country code. One of BR, CO, EC, CL, AR, VE. Determines the validation rules for the document object.Formerly named countryCode.
string
required
Document state. One of:
  • fiscal_graphic — the provider is delivering the representación gráfica (the printable artifact) for the document. It is not an approval and not a terminal state — see The fiscal_graphic state below.
  • authorized — the fiscal authority approved the document
  • cancelled — a previously authorized document was cancelled
  • rejected — the document was rejected (validation, schema, signature)
  • denied — the authority denied the request (typically a permanent business rule failure)
  • error — a non-recoverable error occurred at the provider or authority
When eventType is fiscal_graphic, authorized or cancelled, the document field is required. When it is rejected, denied, or error, the failure field is required instead.
string
required
Your provider’s own id for this delivery. Stored for audit/forensics — it is not the idempotency key. Fire deduplicates on (orderId, eventId) — a different eventType on the same pair returns 409, except fiscal_graphic and retries after a failure (see Retries and rejected cancellations) — so you may safely send a fresh providerEventId on every retry without creating duplicates. Use the provider’s native event id if available; otherwise a UUID.
string
required
ISO 8601 UTC timestamp of when the event happened at the fiscal authority (not when the provider sent the callback).
string
required
UUID of the order in Fire. Matches data.orderId in order.completed and order.cancelled events. Fire uses this to find the existing fiscal document.
string
required
Correlation UUID and idempotency key (together with orderId). It must be the event.id of a V4 envelope Fire emitted for this order — echo it exactly; never invent it.
  • Source-of-truth check. An eventId that does not reference a Fire event for that order is rejected with 400 before the 202.
  • Each action carries its own event. Echo the event.id of the event that drove this action — the order.invoiced emission for an authorized callback, the cancellation/reversal event for a cancelled callback. Reusing one action’s eventId for a different terminal eventType (e.g. a cancelled that echoes the authorized’s eventId) is rejected with 409 — a cancellation must reference its own event, not ride on the authorization’s. See Idempotency & scenarios.
object
Your country authority vocabulary, and nothing else: claveAcceso, cufe, chaveAcesso, folio, cae… Required when eventType is authorized, cancelled or fiscal_graphic. See Document by country.The common fields —documentType, the number, the URLs, the amounts, the dates— do not live in here: they are at the root. Everything in document reaches the events as authority.countryData, with the same key names.

Canonical fields (root)

They mean the same thing in all six countries, so they live at the root of the payload rather than inside document. Same rule as the fiscal numbering response, so the same fact is never looked up at two different depths depending on which path it came in through.
string
SALE_INVOICE or CREDIT_NOTE — the same vocabulary as the fiscal numbering. Required when there is a document (authorized / cancelled), and it must match the event: authorized is a SALE_INVOICE, cancelled is a CREDIT_NOTE. Any other combination returns 400.Formerly docType: "invoice" | "cancellation". The old name is ignored, not translated: an authorized or cancelled payload that carries only docType receives 400 on documentType.
string
What your regime calls it: nfce, nfe, factura, boleta, factura_a. Open: we do not validate it against a list and it branches nothing on our side — it is referential. Required when there is a document.

Document types

Fire accepts exactly two values in documentType. There is no third type: any other value returns 400. What each regime calls its document goes in docSubtype, and that one is open: nfce, nfe (Brazil), factura (Ecuador, Colombia), boleta (Chile), factura_a (Argentina) and any other name your authority uses. Fire does not validate it against a list and never branches on it.
Do not confuse these with the Document types catalog: that endpoint lists the buyer identification documents (CEDULA, CPF, NIT…), not fiscal document types.
string
The document’s visible number, as the authority composes it (12388, 001-020-000000123, C012001). The pieces it is built from still travel in document with the country’s names. Required on authorized: Fire no longer deduces it.
string
UTC date/time (ISO 8601) the document was issued. Same name as in the fiscal numbering response. Required on authorized.
string
UTC date/time (ISO 8601) the authority approved the document — a different fact from issuedAt. They can be seconds apart (online authorization) or hours apart (contingency authorized when the connection returns). Optional; when absent, event consumers see authority.authorizedAt: null. On a cancellation use cancelledAt instead: a cancelled callback keeps the issuedAt and authorizedAt the document already had — they are the invoice’s — and only uses the ones you send if Fire had none.
string
UTC date/time the authority approved the cancellation. Send it on cancelled.
string
ONLINE, OFFLINE or BATCH. OFFLINE is contingency: the document is valid but was issued without reaching the authority, and some regimes require the receipt to say so (the Ecuadorian RIDE prints EMISION POR CONTINGENCIA).
string
Download link for the PDF (DANFE / RIDE). HTTPS, downloadable by direct GET, valid for at least 24 h — Fire archives it. Required for authorized and cancelled.
string
Download link for the authority’s legal XML. Same conditions as pdfUrl. Required for authorized and cancelled.
number
Gross total of the document, in real decimals (not scaled). Required on authorized.
number
Total tax of the document, in real decimals. Required on authorized.
string
ISO 4217 code — exactly 3 letters (BRL, COP, USD). Required on authorized.
string
The document’s id in your system. It is what the PDF and XML are fetched by and what a cancellation is requested with. Required on authorized.
object
What gets printed: a map of key → exact string to encode ({ "qr": "…" }, { "ted": "…" }). The key names the purpose; how it is drawn is the receipt template’s choice. It sits at the root, next to document, as in the fiscal numbering response. Required and non-empty on fiscal_graphic; optional otherwise.
object
Who issued it and with what version. It reaches the order and the events as authority.providerIdentity — the same name as in the fiscal numbering — with the three keys always present. If you do not send provider, or send it empty, providerIdentity is null.
object
Why there is NO document. Required when eventType is rejected, denied or error.Formerly named error, and it now carries the same shape as the fiscal numbering response.
object
Free-form bag for provider-specific extras (raw response, internal IDs, etc.). Not validated. It reaches the order and the events as authority.providerMetadata, as is and opaque — just like in the fiscal numbering. Absent or {} becomes null. Do not send null: omit it or send {} — null returns 400.Formerly named providerSpecific.
The root takes no extra keys. Only the fields above are read; any other root key is silently discarded — it is not rejected. orderCode is not part of the contract — the order is resolved by orderId.

What is required, per event

authorizedAt, cancelledAt, authorizationMode and provider are always optional. A required field sent as null counts as missing.

Document by country

The required shape of document depends on country. document carries ONLY that country authority identifiers — the fields common to all six live at the root of the payload, above.
document changed contentsUntil now document mixed two things: what means the same in every country —docType, pdfUrl, xmlUrl, the amounts, the dates— and each authority own vocabulary. That mix propagated to everything that read the callback.Now the canonical fields live at the root and document keeps only the regime’s. Same shape as the fiscal numbering response: the same fact, in the same place, regardless of which path it came in through.Other changes in the same batch: countryCode → country, docType → documentType (SALE_INVOICE / CREDIT_NOTE, no alias), error → failure (with scope and details), providerSpecific → metadata, and emittedAt → issuedAt. New at the root: documentNumber, authorizedAt and graphic.
Brazilian fiscal documents (NF-e / NFC-e). Use this country code when sending callbacks from any BR fiscal provider.In addition to the canonical root fields (documentType, docSubtype, documentNumber, pdfUrl, xmlUrl, issuedAt, etc.), BR requires these specific identifiers:The example below is a complete authorized payload: every root field it shows is required for that event, except provider and metadata, which are optional and reach the order as authority.providerIdentity and authority.providerMetadata — see What is required, per event.

The fiscal_graphic state

fiscal_graphic reports that your provider is delivering the representación gráfica — what the till prints for the document. It is a state of its own, not an outcome:
  • It is not an approval. Receiving fiscal_graphic says nothing about whether the authority approved the document. Fire stores the graphic and the document stays non-terminal.
  • It is optional. If your provider has no graphic for a document, send authorized (or any terminal state) directly — no fiscal_graphic is required first. Both flows are valid.
  • It can share the eventId with its outcome. The graphic and the terminal result belong to the same action, so you may send fiscal_graphic and then authorized / rejected / denied / cancelled reusing the same eventId. That is not a conflict and never returns 409 — see Idempotency & scenarios.
  • It requires document and a non-empty graphic. The graphic representation is what the till prints — the QR, the TED, the encoded access key — and it travels in the root graphic. The PDF and the XML are the files of the authorized document and arrive with authorized.
  • It triggers no outbound flow event. order.invoiced and order.reversed remain tied to authorized and cancelled respectively. The graphic only changes the document’s state.
fiscal_graphic (EC)

Negative outcomes (rejected, denied, error)

For non-authorized states, omit document (or send it as null) and provide failure. country still applies (it validates routing); the document itself is not required because there is no authorized artifact — and neither are documentType/docSubtype, since there is no document to type. A negative outcome stores no receipt data. If you send documentNumber, providerDocId, issuedAt, authorizedAt, amounts, pdfUrl, xmlUrl or graphic with rejected, denied or error, Fire ignores them: only documentType, docSubtype, the country’s document keys, failure, provider and metadata are kept. A document that already had receipt data keeps it.
Rejected

Retries and rejected cancellations

A failed issuance can be retried. If you reported rejected, denied or error and then retry with the authority, send the new result with the same eventId — the issuance’s. Fire accepts it and the flow continues normally: if the new result is authorized, order.invoiced is dispatched. If the retry fails again, the new reason replaces the previous one.
Cancellation rejected by the authority. When Fire asks you to void an authorized invoice (order.cancelled event), the order is left “cancelling”. If the authority does not accept the cancellation, send rejected, denied or error with the eventId of that cancellation and the reason in failure. Fire reads it as a failed cancellation: the invoice remains valid (the order goes back to authorized, with the reason in error) and no event is dispatched. If you later retry and the authority accepts it, send cancelled with the same eventId and order.reversed is dispatched.
A rejected on an authorized invoice that is not being cancelled still means what it always did: the authority rejected the invoice.

Response

On success the endpoint returns 202 Accepted — the event was authenticated, validated, deduplicated, and enqueued. A 202 does not mean the fiscal document was updated yet; that happens asynchronously in a background worker. Use the status endpoint to confirm the final outcome. The body carries two distinct ids so there is no ambiguity: eventId is the id you sent (echoed back), webhookEventId is Fire’s id for the queued record.
boolean
Always true when the request was accepted and enqueued.
boolean
true when this (orderId, eventId) was already ingested with the same eventType — the existing record is returned and nothing is re-enqueued. false for a fresh event. A different eventType on the same pair never reaches this field: it returns 409, except fiscal_graphic.
string
Echo of the eventId you sent (the Fire emission’s event.id). Use it to correlate this acknowledgement with your request.
string
Fire’s id for the queued record in webhook_events. Pass it to GET /v1/webhooks/events/{webhookEventId} to poll the processing outcome. On a duplicate it is the same id returned the first time.
string
Current queue status of the record — queued → processing → processed (and retry / failed / dead / ignored).
string
ISO 8601 UTC timestamp of when Fire first received this event. Stable across retries — useful as a trace anchor.
string
Human-readable summary of what happened.

Checking the outcome

Because processing is asynchronous, the 202 only confirms the event was queued. To see whether the fiscal document was actually updated, poll the companion status endpoint with the webhookEventId returned by the 202:
string
Queue lifecycle: queued → processing → processed (done) · failed / dead (gave up after retries) · retry (waiting for the next attempt) · ignored.
number
Processing attempts so far.
object | null
On success, the worker outcome — e.g. { "kind": "updated", "documentId": "…", "receiptId": "…" }.
object | null
{ "message": "…" } when the last attempt failed; null otherwise.
The eventType returned here is the one you sent for that queued record. If you sent fiscal_graphic and then the terminal state under the same eventId, each is its own queue record with its own webhookEventId — poll the one you care about.
A 404 is returned for unknown ids — or ids that belong to another tenant — with no existence leak. Authenticate with the same webhooks:fiscal key you used for the callback.

Idempotency & scenarios

Fire deduplicates callbacks on (orderId, eventId) — not on providerEventId (which you may regenerate freely). The same pair resent with the same eventType is a benign replay; the same pair with a different eventType returns 409 — an attempt to ride one action on another’s event — except fiscal_graphic, which may share the eventId with its outcome. fiscal_graphic is the exception. It is a step of the same action, not a competing outcome, so it may share the eventId with the terminal state that follows it — that combination is accepted, never a 409. This is the full behavior matrix — every combination you can send:
A cancellation needs its own event. You cannot cancel a document by resending the authorization’s eventId with eventType: "cancelled" — that returns 409. Fire is the source of truth: a cancellation must reference the cancellation/reversal event Fire emitted (a distinct eventId). Returning a 202 duplicate here would wrongly tell you the cancellation was accepted while Fire never cancelled — so Fire rejects it explicitly instead.
409 vs 202. A 409 is the only case where a well-formed, authenticated callback is refused at the idempotency layer — because accepting it would diverge state. A same-type replay is never an error: it returns 202 so your retries stay clean. A 503 is ours (transient infra), so it is safe and expected to retry.

Concurrency

Status transitions on fiscal_documents use optimistic locking. If two callbacks for the same document arrive concurrently, one succeeds and the other resolves to idempotent or regression depending on the order. The orders.fiscal JSONB merge is atomic. When the order already has a document, a callback is matched to it by eventId. A callback with a different valid eventId ends in notFound, not regression; resending the document’s own eventId with an earlier state is a same-pair replay. Neither changes the document.

What happens after the 202

From the callback to the events

How each field you send here reaches order.invoiced and order.reversed.

Fiscal overview

Numbering, callback and events in one picture.
The 202 only enqueues the event. A background worker then picks it up — within ~2 seconds via the instant wake-up, or on the next poll cycle as a fallback — and:
  1. The fiscal document is updated with the new status and the authority’s identifiers.
  2. The verdict is stored on the order, structured: canonical fields, amounts, countryData and graphic — and appended to the order’s fiscal history.
  3. An outbound event is dispatched, for every country: order.invoiced for eventType=authorized, order.reversed for eventType=cancelled. Active Integration Flows for that event run and POST to your endpoint. fiscal_graphic, rejected, denied and error update the state but emit no event.

From the callback to the events

Whoever consumes order.invoiced or order.reversed reads exactly what you sent here, reorganized into the event’s fiscal block. The full reference of that block is in order.invoiced. The root fields land in fiscal.authority (or cancellation.fiscal.authority) with the same names, except the three amounts, which are grouped in authority.amounts. document becomes authority.countryData, and provider and metadata become providerIdentity and providerMetadata (null when absent or empty). The field-by-field table is in From the callback to the events. document and countryData are the same block. Fire does not keep a list of each country’s keys: anything in document that is not a canonical field reaches countryData untouched. A new identifier your authority starts requiring travels to the events with no change on Fire’s side.

Why this endpoint keeps its /v1 path

The field renames above (docType → documentType, countryCode → country, error → failure, providerSpecific → metadata) break a sender that still uses the previous names: it receives 400 naming the field. The endpoint nonetheless kept its path. When the contract changed, this callback only carried test traffic in production, and Fire’s own test client moved in the same release, so a parallel /v2 would have kept two ways of saying the same thing for no sender. The event side is different — it has production consumers — and the three affected events each published a new documented version.

order.completed

See where the fiscal state lands inside the V4 order snapshot.

order.invoiced

Outbound event triggered by an authorized callback, for every country.

Inject order

The order injection endpoint that creates the order this callback updates.

Authentication

How API keys, scopes, and vendor binding work.