Indoor / Table Service¶
API Spec
The implementable contract (endpoints, fields, errors, and examples) is in the Indoor API Spec — English only.
The Indoor capability standardizes on-premise consumption — table, tab, and counter — covering both waiter-mediated service and full self-service via totem, QR Code, or tablet. It covers the full dining session: grouping orders into an account, registering payments (including partial ones), issuing fiscal documents, and closing the account — kept in sync between the restaurant management system and the ordering application.
This page is a reading guide: concepts, roles, account vs order, flows, and checklists. Field-level contract lives in the API Spec (note above).
Repeated account lifecycle call
If the operation was already applied (e.g. pre-close while account is already IN_PAYMENT, close while already CLOSED), the host returns 202 — not 409 merely because it was duplicated. See Conventions and Indoor API Spec.
What it is for¶
In traditional delivery, an order is born and ends as an isolated unit: created, prepared, delivered, closed. On the floor the reality is different. An account opens — at a table, on a tab, or at a self-service counter — and receives several orders over time, possibly from different channels (waiter tablet, customer QR Code, totem). Items are cancelled or transferred to another account, the total is split, paid in parts, and only then is the account closed with fiscal issuance.
Without a standard, every POS–floor-app integration had to bilaterally negotiate how to represent that accumulation: where the total lives, how to cancel an item without cancelling the whole order, when to issue the invoice, how to handle partial payment. Indoor removes that negotiation by defining the account as the central entity and a fixed set of operations and events on it.
What changes from V1 to V2¶
Breaking - read before migrating
Indoor in V2 turns a common set of bilateral integrations into a normative contract. If your company already operates dine-in with proprietary flows, these are the highest-impact migration points.
| Topic | V1 / legacy integrations | V2 |
|---|---|---|
| Capability model | Usually proprietary or POS-coupled | Explicit Indoor capability with normative contract |
| Item entry | Often described as local account opening | Always via Orders: CREATED event + GET /orders/{id} with fulfillment.orderType: INDOOR |
| Central entity | Order, account, or ticket varied by integrator | Account is the operational entity for grouping, payment, close, and fiscal |
| Account events | Format and transport were proprietary | ACCOUNT_*, PAYMENT_*, and FISCAL_* events are standardized |
| Event delivery | Polling, webhook, or proprietary integrations | Webhook-only |
| Repeated mutation | Repeated pre-close / close often treated as error |
202 if target state was already applied |
| Payment timing | Frequently coupled to final checkout only | Payment may happen in IN_USE or IN_PAYMENT |
There is no POST /orders to create an order in the protocol - including Indoor. The order is born in Orders and the account is born from that order.
Prerequisite: Orders protocol¶
Indoor is an Orders extension, not a standalone capability
Both parties MUST implement the Orders protocol before Indoor. The dining account is opened from an order: when the Software Service processes an order with fulfillment.orderType: INDOOR (CREATED event + GET /orders/{id} — there is no POST /orders) for an operational key with no open account, it creates the account. Later INDOOR orders on the same key only accumulate items.
Order lifecycle and order-event details live only in Orders — they are not redefined here. Implementations without active Orders MUST NOT use this capability.
- Guide: Orders
- Contract: Orders API Spec
The order is the channel that brings items in; the account is the operational aggregator of those orders for payment, close, and fiscal integration.
Roles¶
| Role | Responsibility |
|---|---|
| Software Service | Restaurant management system. Hosts and implements all endpoints in this spec and emits account lifecycle events. |
| Ordering Application | Ordering surface (totem, waiter tablet, customer app, front desk). Consumes endpoints and receives events via webhook to stay in sync. |
In every operation of this capability the Software Service is the server and the Ordering Application is the client.
Account vs events¶
This is the most important distinction to avoid mixing Indoor with Orders.
| Concept | What it is | Source of truth |
|---|---|---|
| INDOOR order | Item entry channel | Orders |
| Account | Operational session aggregator | GET /accounts / GET /accounts/{accountId} |
| Account event | Immutable notified fact about the account | accountEvent webhook |
Rules:
- The order still follows the Orders lifecycle; Indoor does not redefine
order.status. - The account is the source of truth for grouping, payments, lock, and close.
202 Acceptedon account mutations does not mean final outcome; business confirmation closes via event and/or GET reconciliation.- Account events are notifications, not commands.
- Deduplicate by
eventIdand do not assume strict delivery ordering.
Discovery¶
Participants that expose Indoor MUST declare capabilities.indoor in the well-known manifest (GET /.well-known/opendelivery), per the Discovery API Spec. Indoor remains an Orders extension at the domain level, but its public Discovery declaration is published as its own object.
Typical Indoor declaration fields in Discovery (normative detail in Discovery API Spec):
| Field | Meaning |
|---|---|
invoiceIssuer |
Who issues the fiscal document (pos / app / platform) |
invoiceIssueMoment |
When it is issued (account_closing, item_addition, payment) |
usesAccountId |
Whether the participant tracks sessions with accountId |
"capabilities": {
"indoor": {
"version": "1.0.0",
"supported": true,
"endpoint": "https://api.example.com/od/v2",
"invoiceIssuer": "pos",
"invoiceIssueMoment": "account_closing",
"usesAccountId": true
}
}
General guide: Discovery. Contract: Discovery API Spec.
Map: goal → API Spec operation¶
Use this table to go from business flow to HTTP contract. All links open the Indoor API Spec reference.
| Goal | Operation | Spec |
|---|---|---|
| Open account / add items | CREATED event + GET /orders/{id} with fulfillment.orderType: INDOOR |
Orders (prerequisite) |
| Query account (table/tab) | GET /accounts?operationMode&identifier |
getAccount |
| Query account by ID | GET /accounts/{accountId} |
getAccountById |
| Pre-close (lock) | POST /accounts/pre-close |
preCloseAccount |
| Unlock | POST /accounts/unlock |
unlockAccount |
| Close account | POST /accounts/close |
closeAccount |
| Cancel item | POST /accounts/items/{itemId}/cancel |
cancelAccountItem |
| Transfer items/account | POST /accounts/transfers |
transferAccountContent |
| Post payment | POST /accounts/payments |
createPayment |
| List payments | GET /accounts/payments |
listAccountPayments |
| Request fiscal | POST /accounts/fiscal |
requestFiscalDocument |
| List fiscal documents | GET /accounts/fiscal |
listAccountFiscalDocuments |
| Receive events | Webhook accountEvent |
receiveAccountEvent |
Key concepts¶
The account (Account)¶
The account aggregates all orders, items, payments, and fiscal documents of a consumption session. Each account is located by an operational key — the pair operationMode + identifier:
operationMode |
Meaning | Example identifier |
|---|---|---|
TABLE |
Table service | "5" (table 5) |
TAB |
Tab service | "A-102" |
COUNTER |
Counter consumption | "3" (position 3) |
Besides the operational key, the account may have an internal POS accountId. Primary lookup uses the operational key (GET /accounts?operationMode=TABLE&identifier=5); when accountId is already known, use GET /accounts/{accountId}. The account always carries lastEvent with the type of the last emitted event (e.g. ACCOUNT_ITEM_ADDED) — useful for sync and debugging when webhook delivery is uncertain (e.g. after reconnection).
Origin channels (originChannel)¶
operationMode defines how the account is grouped — not where the order entered. That is originChannel, present on each order, and this is where Indoor makes clear it does not assume waiter-mediated service only:
originChannel.type |
Description |
|---|---|
TOTEM |
Self-service on a physical totem in the venue. |
QR_CODE |
Customer scans a QR Code on the table/tab and orders on their phone. |
CUSTOMER_TABLET |
Tablet handed to the customer to order directly. |
WAITER_TABLET |
Tablet used by the waiter/attendant to place the order. |
FRONT_DESK |
Order placed at the front desk. |
POS |
Order originated directly on the POS. |
APP |
Venue or third-party ordering app. |
WHATSAPP |
Order received via WhatsApp. |
OTHER |
Any other unlisted channel. |
operationMode and originChannel are independent: the same account may accumulate orders from different channels during the session. For example, the customer opens the account via QR Code (originChannel: QR_CODE) and later the waiter posts an extra item on a tablet (originChannel: WAITER_TABLET) — both land on the same account because they share the operational key. The channel is order metadata only; it does not define account identity.
fulfillment.indoor object on the order¶
In Orders payloads, Indoor data lives at order.fulfillment.indoor.
V2 canonical values (technical identifiers) are always in English:
operationMode:TABLE,TAB,COUNTERconsumptionType:DINE_IN,TAKEAWAYoriginChannel.type:TOTEM,QR_CODE,CUSTOMER_TABLET,WAITER_TABLET,FRONT_DESK,POS,APP,WHATSAPP,OTHER
Common mapping from legacy labels: MESA -> TABLE, COMANDA -> TAB, BALCAO -> COUNTER, CONSUMIR_NO_LOCAL -> DINE_IN, LEVAR -> TAKEAWAY.
| Field | Type | Required | Description / enum |
|---|---|---|---|
operationMode |
string |
Yes | Account operation mode: TABLE, TAB, COUNTER |
identifier |
string |
Yes | Operational identifier (table/tab/counter) |
account.id |
string |
No | Account id when already known |
originChannel.type |
string |
Yes | Order origin channel |
originChannel.id |
string |
No | Specific device/channel/origin id |
originChannel.notes |
string |
No | Free-text note about the channel |
consumptionType |
string |
Yes | Consumption intent: DINE_IN, TAKEAWAY |
fulfillment.type |
string |
Conditional | On-premise handoff mode: DISPATCH or CALL |
fulfillment.call.notifyOriginator |
boolean |
Conditional | Notify originator in call flow |
fulfillment.call.type |
string |
Conditional | Call type: TICKET_NUMBER, PAGER, CALL_NAME, WHATSAPP, OTHER |
fulfillment.call.label |
string |
Conditional | Visible call label (ticket/name/pager etc.) |
fulfillment.dispatch.id |
string |
Conditional | Dispatch point id |
fulfillment.dispatch.label |
string |
Conditional | Dispatch point display label |
fulfillment.dispatch.sector |
string |
Conditional | Sector/operational area |
fulfillment.dispatch.seat.id |
string |
Conditional | Seat/position identifier |
fulfillment.dispatch.seat.label |
string |
Conditional | Seat/position display label |
service.waiterCode |
string |
No | Waiter/attendant code |
service.waiterName |
string |
No | Waiter/attendant name |
service.peopleCount |
integer |
No | Number of people in session |
notifications.whatsAppId |
string |
No | WhatsApp identifier for notifications |
fiscal.shouldIssueDocument |
boolean |
Conditional | Whether a fiscal document should be issued |
fiscal.documentStatus |
string |
No | Known fiscal status: NOT_ISSUED, ISSUED, PENDING |
fiscal.printDocument |
boolean |
No | Whether document printing is requested |
fiscal.paymentNSURequiredForNFCE |
boolean |
No | Whether payment NSU is required for NFC-e |
How the account is born¶
The account is not created by any endpoint in this spec. It is created automatically in the Software Service when Orders processes an order with fulfillment.orderType: INDOOR (entry via CREATED event and snapshot on GET /orders/{id} — no POST /orders) for an operational key that has no open account — whether that order comes from a waiter, a totem, or a QR Code. From then on, new INDOOR orders for the same key accumulate items on the existing account, regardless of each order’s origin channel.
Account status¶
stateDiagram-v2
direction LR
[*] --> IN_USE: 1st INDOOR order
IN_USE --> IN_PAYMENT: pre-close
IN_PAYMENT --> IN_USE: unlock
IN_PAYMENT --> CLOSED: close
CLOSED --> [*]
| Status | Meaning |
|---|---|
IN_USE |
Account open, accepting new items. Payments may also be posted here. |
IN_PAYMENT |
Account pre-closed/locked — no new items, but still accepts payments, awaiting close. |
CLOSED |
Account permanently closed. No further operations are accepted. |
Payments and closing¶
This is the area that most often confuses Indoor integrations, so it is worth highlighting carefully:
Payment does not depend on pre-close. POST /accounts/payments may be called at any time — while the account is IN_USE or already IN_PAYMENT. You do not need to wait for pre-close to register a partial payment: many venues take payments throughout the session (e.g. the customer pays a round of drinks mid-meal).
Pre-close is a lock, not a payment trigger. POST /accounts/pre-close blocks new items, signalling the account is ready for final checkout. That is when the last payment is expected — remaining balance if earlier payments exist, or the full amount if none yet.
Close is final. POST /accounts/close should only be called when paid total covers the account amount. After close, no further operations are accepted — including new payments.
| Operation | IN_USE |
IN_PAYMENT |
|---|---|---|
| Add items (via Orders) | ✅ | ❌ |
| Cancel item | ✅ | ❌ |
| Transfer items | ✅ | ❌ |
Post payment (POST /accounts/payments) |
✅ | ✅ |
Pre-close (pre-close) |
✅ | — |
Unlock (unlock) |
— | ✅ |
Close (close) |
❌ | ✅ |
Payment is deliberately the only operation valid in both states — it allows charging the customer without locking the rest of account operations.
Events¶
On every relevant transition, the Software Service MUST notify the Ordering Application via webhook. There is no polling for Indoor events: delivery is webhook-only, and the Ordering Application must implement an endpoint compatible with the accountEvent contract in the spec.
Account event matrix¶
| Event | Trigger | Obligation | Account status | Notes |
|---|---|---|---|---|
ACCOUNT_OPENED |
Account created (INDOOR order) | MUST | IN_USE |
Dining session opened |
ACCOUNT_ITEM_ADDED |
New INDOOR order adds items | MUST | IN_USE |
Order is the item channel |
ACCOUNT_ITEM_REMOVED |
Items transferred to another account | MAY | IN_USE |
Transfer between tables/tabs |
ACCOUNT_ITEM_CANCELLED |
Item cancelled | MUST | IN_USE |
Item cancel, not account cancel |
PAYMENT_CREATED |
Payment posted | MUST | — | Valid in IN_USE and IN_PAYMENT |
PAYMENT_UPDATED |
Internal adjustment of an existing payment | MAY | — | Reconciliation; omit if no post-create update |
ACCOUNT_PRE_CLOSED |
Account locked for payment | MUST | IN_PAYMENT |
Lock — no new items |
ACCOUNT_UNLOCKED |
Lock reversed | MAY | IN_USE |
Reopens for new items |
FISCAL_ISSUED |
Fiscal document issued | MUST | — | If fiscal requested; async; GET as fallback |
FISCAL_ERROR |
Fiscal issuance failed | MUST | — | If fiscal requested and failed; account may still close |
ACCOUNT_CLOSED |
Account closed permanently | MUST | CLOSED |
Irreversible |
Indoor orders vs account
The order lifecycle for INDOOR (CREATED → CONFIRMED → …) is in the Orders matrix — INDOOR profile. The matrix above is only the account.
Webhook payloads and JSON examples
Examples for each EventType and the EventNotification contract are in the API Spec, operation receiveAccountEvent — not duplicated here to avoid drift.
Out of MVP (V2.1+)¶
The Indoor committee discussed and left out of this release candidate:
| Topic | Status |
|---|---|
| Table reservations and waitlists | Post-V2 (possible V2.1) |
| “Call waiter” endpoint | Evaluated; not normative in this RC |
Structured transfer history (TransferHistoryEntry) |
Under discussion; use reason on TransferRequest |
Account event polling (GET /events) |
Commented in the spec; delivery is webhook-only |
| Automatic lock / pre-close timeout by origin | Under operational evaluation |
Do not expect these behaviours from a V2.0.0-rc-compliant implementation.
Flows¶
The flows below show the call sequence between Ordering Application and Software Service, and the events emitted at each step.
Happy path¶
Full dining session: open via INDOOR order, add items, pre-close, payment, fiscal issuance, and close.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
Note over OA,SS: Open — order enters via Orders (no POST /orders)
OA->>SS: CREATED event type=INDOOR (webhook or polling)
SS->>OA: GET /orders/{orderId}
OA-->>SS: Order type=INDOOR
SS-)OA: event: ACCOUNT_OPENED — status: IN_USE
Note over OA,SS: Add items — new INDOOR order
OA->>SS: CREATED event (new orderId)
SS->>OA: GET /orders/{orderId}
SS-)OA: event: ACCOUNT_ITEM_ADDED
opt Early payment (optional, any time in IN_USE)
OA->>SS: POST /accounts/{id}/payments
SS-->>OA: 200 OK
SS-)OA: event: PAYMENT_CREATED
end
Note over OA,SS: Pre-close — lock for checkout
OA->>SS: POST /accounts/{id}/pre-close
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_PRE_CLOSED — status: IN_PAYMENT
Note over OA,SS: Final settlement
OA->>SS: POST /accounts/{id}/payments
SS-->>OA: 200 OK
SS-)OA: event: PAYMENT_CREATED
Note over OA,SS: Fiscal issuance
OA->>SS: POST /accounts/{id}/fiscal
SS-->>OA: 202 Accepted
SS-)OA: event: FISCAL_ISSUED (async)
Note over OA,SS: Close
OA->>SS: POST /accounts/{id}/close
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_CLOSED — status: CLOSED
Unlock¶
The Ordering Application may reverse a pre-close, returning the account to IN_USE to keep adding items.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
OA->>SS: POST /accounts/{id}/pre-close
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_PRE_CLOSED — status: IN_PAYMENT
Note over OA,SS: Operator decides to add more items
OA->>SS: POST /accounts/{id}/unlock
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_UNLOCKED — status: IN_USE
OA->>SS: CREATED event (new INDOOR order)
SS->>OA: GET /orders/{orderId}
SS-)OA: event: ACCOUNT_ITEM_ADDED
Partial payments¶
An account may receive multiple payments over its lifetime, not only after pre-close — including interleaved with new items, since payment and items do not compete for the same lock. The example below shows a partial payment while still IN_USE (e.g. customer pays a round of drinks mid-session), then more items, then pre-close and final settlement.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
Note over OA,SS: Partial payment — e.g. card, account still in use
OA->>SS: POST /accounts/{id}/payments
SS-->>OA: 200 OK
SS-)OA: event: PAYMENT_CREATED (partial amount)
Note over OA,SS: Account stays open — new items added normally
OA->>SS: CREATED event (new INDOOR order)
SS->>OA: GET /orders/{orderId}
SS-)OA: event: ACCOUNT_ITEM_ADDED
OA->>SS: POST /accounts/{id}/pre-close
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_PRE_CLOSED — status: IN_PAYMENT
Note over OA,SS: Final settlement — e.g. cash (remaining balance)
OA->>SS: POST /accounts/{id}/payments
SS-->>OA: 200 OK
SS-)OA: event: PAYMENT_CREATED (balance settled)
OA->>SS: POST /accounts/{id}/close
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_CLOSED — status: CLOSED
Fiscal issuance¶
Issuance is asynchronous — the Software Service returns 202 Accepted and emits the event when the document is available. The account may be closed even without successful issuance.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
OA->>SS: POST /accounts/{id}/fiscal
SS-->>OA: 202 Accepted
alt Successful issuance
SS-)OA: event: FISCAL_ISSUED
OA->>SS: GET /accounts/{id}/fiscal
SS-->>OA: 200 OK (document available)
else Issuance failure
SS-)OA: event: FISCAL_ERROR
Note over OA: Operator records the failure
end
Note over OA,SS: Account may close regardless of fiscal outcome
OA->>SS: POST /accounts/{id}/close
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_CLOSED — status: CLOSED
Item cancellation¶
Cancels a specific item on an IN_USE account. The account stays open; other items are unaffected.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
Note over OA,SS: Item already on the account via INDOOR order
OA->>SS: POST /accounts/items/{itemId}/cancel
SS-->>OA: 200 OK
SS-)OA: event: ACCOUNT_ITEM_CANCELLED
Note over OA,SS: Account remains IN_USE — other items unchanged
Transfer between accounts¶
Moves items from one account to another — e.g. table change, groups splitting, or a counter order reassigned to a tab. Both accounts must be IN_USE.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
Note over OA,SS: Account A (source) and Account B (target) are IN_USE
OA->>SS: POST /accounts/transfers
Note over OA,SS: body: targetAccountId + item list
SS-->>OA: 200 OK
SS-)OA: event: ACCOUNT_ITEM_REMOVED — Account A (source)
SS-)OA: event: ACCOUNT_ITEM_ADDED — Account B (target)
Note over OA,SS: Both accounts remain IN_USE
Implementing the Software Service¶
If you host the endpoints and manage accounts, pay attention to:
Open the account on the first INDOOR order. When Orders processes a fulfillment.orderType: INDOOR order (CREATED event + GET — no POST /orders) for an operational key with no active account, create the account and emit ACCOUNT_OPENED. Later orders for the same key accumulate items (ACCOUNT_ITEM_ADDED) instead of opening a new account.
Emit an event for every transition. Every relevant state change must produce the corresponding event, delivered via webhook (accountEvent). The Ordering Application relies exclusively on these events to sync — a transition without an event is an invisible transition. There is no polling fallback: if the webhook fails, use the account’s lastEvent field to reconcile state.
Treat pre-close as a real lock — but only for items. An IN_PAYMENT account must not accept new items. If the operator needs to add something, require an explicit unlock (ACCOUNT_UNLOCKED, back to IN_USE). Payment is not part of that lock: POST /accounts/{id}/payments must remain accepted in both IN_USE and IN_PAYMENT at any time — do not require pre-close before posting a payment.
Validate payment before close, not before pay. Accumulate PAYMENT_CREATED over the whole account life and only accept POST /accounts/{id}/close when paid total covers the account amount — unless your own business rules (courtesy, discount). After close, reject any further operation, including new payments: close is the only truly irreversible milestone.
Make fiscal issuance asynchronous. Respond 202 Accepted immediately and emit FISCAL_ISSUED or FISCAL_ERROR when the result is available. Do not block account close waiting on SEFAZ.
Keep totals consistent. Cancellations, transfers, and new orders must reflect immediately in totals, because the Ordering Application shows that value to the customer.
Implementing the Ordering Application¶
If you consume the endpoints and display the account to operator or customer, pay attention to:
Locate the account by operational key. Use GET /accounts?operationMode=...&identifier=... as the primary access path. Only use GET /accounts/{accountId} when you already have an accountId from a previous response.
Receive events via webhook. Indoor has no polling: implement the webhook endpoint (per the accountEvent contract in the spec) to receive ACCOUNT_ITEM_ADDED, PAYMENT_CREATED, FISCAL_ISSUED, etc. in real time and update the UI. Use account lastEvent to reconcile if you suspect a missed delivery.
Do not wait for pre-close to post a payment. POST /accounts/{id}/payments works with the account IN_USE or IN_PAYMENT — use it whenever the customer wants to pay, even mid-session. Reserve pre-close for when the account must stop accepting new items.
Do not assume immediate close after payment. Payment and close are distinct steps. Post payments with POST /accounts/{id}/payments and only then call POST /accounts/{id}/close — typically after pre-close, when the remaining balance (or full total if no prior payments) is settled.
Treat fiscal issuance as asynchronous. After 202 Accepted, wait for FISCAL_ISSUED (or FISCAL_ERROR) — do not expect the document in the request response. Call GET /accounts/{id}/fiscal when the event arrives.
Reflect cancellations and transfers immediately. On ACCOUNT_ITEM_CANCELLED or ACCOUNT_ITEM_REMOVED/ACCOUNT_ITEM_ADDED, update the displayed account — the customer must not see items that already left the account.
Checklist — Software Service
- Account created and
ACCOUNT_OPENEDemitted on first INDOOR order. - Every state transition emits the corresponding event.
IN_PAYMENTaccount rejects new items untilunlock.- Payment (
POST /accounts/payments) accepted inIN_USEandIN_PAYMENT, without requiring pre-close. - Paid total validated before accepting close.
- After
close, every further operation is rejected — including new payments. - Fiscal issuance responds
202and later emitsFISCAL_ISSUED/FISCAL_ERROR. totalsreflect cancellations and transfers immediately.
Checklist — Ordering Application
- Account located by operational key (
operationMode+identifier). - Webhook endpoint implemented and registered for events (no polling).
- Payments may be posted at any time (
IN_USEorIN_PAYMENT), not only after pre-close. - Payment and close treated as separate steps.
- Fiscal issuance treated as asynchronous (wait for the event).
- Cancellations and transfers reflected in the UI in real time.