Fiscal callback
API
Fiscal callback
Inbound endpoint your fiscal provider POSTs to when a fiscal document changes state. Multi-country (BR, CO, EC, CL, AR, VE). Updates the order’s fiscal state inside Fire and — for Brazil — triggers downstream order events.
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, updates the order’s fiscal state in
How it complements
The
Negative outcomes (
For non-authorized states, omit
A
fiscal_documents and orders.fiscal, and — for Brazil — dispatches downstream order.invoiced / order.reversed events to your Integration Flows.
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 the callback is processed, the next order.completed or order.cancelled event for the same orderId reflects the updated fiscal state in:
data.store.storeFiscalConfig— emitter context (CNPJ / NIT / RUC / RUT / CUIT / RIF…)data.payments.metadata.fiscal— aggregate fiscal totals (BR populated; other countriesnull)data.orderLines[].metadata.fiscal— per-line fiscal classification (BR populated; othersnull)
order.invoiced (or order.reversed) event is also emitted with the SEFAZ document references in data.fiscal.
Today, only
order.invoiced and order.reversed are wired as outbound flow events (Brazil). The endpoint validates and stores callbacks for all 6 countries (CO, EC, CL, AR, VE, BR), and the updated fiscal state shows up in the next order.completed / order.cancelled event regardless of country. The corresponding outbound flow events for CO/EC/CL/AR/VE are coming.Authentication
This endpoint requires an API key with thewebhooks: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 Settings → API Keys → Developer keys, with scope
webhooks:fiscal and a vendor binding for the account whose orders this key may update.string
Optional
Bearer <token>. Not currently required for this endpoint, but reserved for future provider-issued tokens.Request body
The body is a JSON object with these top-level fields. Thedocument shape is discriminated by countryCode — 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.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 Thefiscal_graphicstate below.authorized— the fiscal authority approved the documentcancelled— a previously authorized document was cancelledrejected— 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
eventType is fiscal_graphic, authorized or cancelled, the document field is required. When it is rejected, denied, or error, the error 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), 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
eventIdthat does not reference a Fire event for that order is rejected with400before the202. - Each action carries its own event. Echo the
event.idof the event that drove this action — theorder.invoicedemission for anauthorizedcallback, the cancellation/reversal event for acancelledcallback. Reusing one action’seventIdfor a different terminaleventType(e.g. acancelledthat echoes theauthorized’seventId) is rejected with409— a cancellation must reference its own event, not ride on the authorization’s. See Idempotency & scenarios.
object
The authorized / cancelled fiscal document. Required when
eventType is fiscal_graphic, authorized or cancelled. Shape varies per country — see Document by country.object
Error context for negative outcomes. Required when
eventType is rejected, denied, or error.object
Free-form bag for provider-specific extras (raw response, internal IDs, etc.). Not validated; passed through to the audit log.
Document by country
Thedocument field’s required shape depends on countryCode. All variants share the base fields below (common to all 6 countries) plus the country-specific identifiers required by that authority.
Common base fields
These apply to anycountryCode. docType and docSubtype are required; the rest are optional — send them whenever your fiscal provider has them available. Fire persists them in fiscal_documents / orders.fiscal and re-emits them in the outbound order.invoiced / order.reversed events (Brazil) and in the next order.completed / order.cancelled.
pdfUrl and xmlUrl are stored exactly as you send them — Fire does not download or re-host the file. If your provider signs these URLs with expiry, note that the stored link may expire; download and persist the artifact on your side if you need durable access.- Brazil (BR)
- Colombia (CO)
- Ecuador (EC)
- Chile (CL)
- Argentina (AR)
- Venezuela (VE)
Brazilian fiscal documents (NF-e / NFC-e). Use this country code when sending callbacks from any BR fiscal provider.In addition to the common base fields (
docType, docSubtype, pdfUrl, xmlUrl, emittedAt, etc.), BR requires these specific identifiers:The example below includes the optional base fields (
pdfUrl, xmlUrl, emittedAt, totalAmount, taxAmount, currencyCode) — all may be omitted, but send them if your provider has them:The fiscal_graphic state
fiscal_graphic reports that your provider is delivering the representación gráfica — the printable artifact (PDF / RIDE / DANFE) for the document. It is a state of its own, not an outcome:
- It is not an approval. Receiving
fiscal_graphicsays 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 — nofiscal_graphicis required first. Both flows are valid. - It can share the
eventIdwith its outcome. The graphic and the terminal result belong to the same action, so you may sendfiscal_graphicand thenauthorized/rejected/denied/cancelledreusing the sameeventId. That is not a conflict and never returns409— see Idempotency & scenarios. - It requires
documentwithpdfUrl. The PDF is the representation, so it is mandatory.xmlUrlstays optional here — the legal XML travels with the authorization. - It triggers no outbound flow event.
order.invoicedandorder.reversedremain tied toauthorizedandcancelledrespectively. The graphic only changes the document’s state.
fiscal_graphic (CO)
Negative outcomes (rejected, denied, error)
For non-authorized states, omit document and provide error. The countryCode still applies (validates routing); the document itself is not required because there is no authorized artifact.
Rejected
Response
On success the endpoint returns202 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.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, the202 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.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, eventType) — not on providerEventId (which you may regenerate freely). The same (orderId, eventId) resent with the same eventType is a benign replay; the same (orderId, eventId) with a different terminal eventType is an attempt to ride one action on another’s event, which Fire rejects.
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:
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 onfiscal_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.
What happens after the 202
The202 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:
fiscal_documentsrow is upserted with the new status and document references (chaveAcesso,protocolo,cufe,cae, etc., per country).orders.fiscalJSONB column is merged with the same data — making it visible in the nextorder.completed/order.cancelledevent for the sameorderId, regardless of country.- An outbound flow trigger is dispatched — for Brazil,
triggerType = 'order.invoiced'(foreventType=authorized) or'order.reversed'(foreventType=cancelled). Active Integration Flows for that trigger run and POST to your endpoint.
- Today, only
order.invoicedandorder.reversedare wired. They fire when an authorized/cancelled callback for a BR store is processed. - For CO/EC/CL/AR/VE, the equivalent outbound events are not yet wired — callbacks are validated and stored, and the fiscal state is reflected in the next
order.completed/order.cancelledevent for the same order.
Related
order.completed
See where the fiscal state lands inside the V4 order snapshot.
order.invoiced
Brazil-only outbound event triggered by this callback.
Inject order
The order injection endpoint that creates the order this callback updates.
Authentication
How API keys, scopes, and vendor binding work.

