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
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.
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.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.
ISO 3166-1 alpha-2 country code. One of
BR, CO, EC, CL, AR, VE. Determines the validation rules for the document object.Document state. One of:
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 authorized or cancelled, the document field is required. When it is rejected, denied, or error, the error field is required instead.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.ISO 8601 UTC timestamp of when the event happened at the fiscal authority (not when the provider sent the callback).
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.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 differenteventType(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.
The authorized / cancelled fiscal document. Required when
eventType is authorized or cancelled. Shape varies per country — see Document by country.Error context for negative outcomes. Required when
eventType is rejected, denied, or error.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.
| Field | Type | Required | Notes |
|---|---|---|---|
docType | "invoice" / "cancellation" | ✓ | Document class — invoice for issuance, cancellation for a cancellation |
docSubtype | string | ✓ | Country/provider subtype (e.g. nfce, nfe, factura, boleta, factura_a) |
pdfUrl | string (URL) | null | Download link for the document PDF (DANFE / graphical representation) | |
xmlUrl | string (URL) | null | Download link for the canonical XML from the fiscal authority | |
emittedAt | string (ISO 8601) | null | UTC timestamp when the authority authorized/emitted the document | |
cancelledAt | string (ISO 8601) | null | UTC timestamp of the cancellation — relevant when eventType is cancelled | |
totalAmount | number | null | Document gross total | |
taxAmount | number | null | Total tax amount | |
currencyCode | string | null | ISO 4217 currency code — exactly 3 letters (e.g. BRL, COP, USD) |
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 (
The example below includes the optional base fields (
docType, docSubtype, pdfUrl, xmlUrl, emittedAt, etc.), BR requires these specific identifiers:| Field | Type | Required | Notes |
|---|---|---|---|
chaveAcesso | string | ✓ | Exactly 44 digits — SEFAZ access key |
protocolo | string | ✓ | SEFAZ authorization protocol |
numero | int / string | ✓ | Document number |
serie | int / string | null | Document series (encoded in chave) | |
modelo | 55 / 65 | null | 55 = NF-e, 65 = NFC-e | |
cnpjEmitente | string | null | Emitter CNPJ |
pdfUrl, xmlUrl, emittedAt, totalAmount, taxAmount, currencyCode) — all may be omitted, but send them if your provider has them: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.
Always
true when the request was accepted and enqueued.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.Echo of the
eventId you sent (the Fire emission’s event.id). Use it to correlate this acknowledgement with your request.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.Current queue status of the record —
queued → processing → processed (and retry / failed / dead / ignored).ISO 8601 UTC timestamp of when Fire first received this event. Stable across retries — useful as a trace anchor.
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:
Queue lifecycle:
queued → processing → processed (done) · failed / dead (gave up after retries) · retry (waiting for the next attempt) · ignored.Processing attempts so far.
On success, the worker outcome — e.g.
{ "kind": "updated", "documentId": "…", "receiptId": "…" }.{ "message": "…" } when the last attempt failed; null otherwise.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). An event is unique to its order: the same (orderId, eventId) resent with the same eventType is a benign replay; the same (orderId, eventId) with a different eventType is an attempt to ride one action on another’s event, which Fire rejects.
This is the full behavior matrix — every combination you can send:
| Scenario | Result |
|---|---|
New (orderId, eventId) | 202 · duplicate: false — enqueued |
Same (orderId, eventId, eventType) resent (any providerEventId) | 202 · duplicate: true — existing record returned, not re-enqueued |
Same (orderId, eventId) but different eventType (e.g. cancelled echoing the authorized’s eventId) | 409 — rejected. Use the event that belongs to this action |
eventId not emitted by Fire for this orderId | 400 — wrong/invented eventId |
| Order/event not under your API key’s account + vendor | 403 |
| Malformed payload (Zod), missing required fields | 400 |
| Missing / invalid API key | 401 |
| Auth store momentarily unreachable | 503 — transient, retry |
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.

