Indoor / Salão¶
A capability Indoor padroniza as operações de consumo no local — mesa, comanda e balcão — cobrindo tanto atendimento mediado por garçom quanto autoatendimento completo via totem, QR Code ou tablet. Ela cobre o ciclo completo de uma sessão de salão: agrupar os pedidos numa conta, registrar pagamentos (inclusive parciais), emitir documento fiscal e fechar a conta — tudo sincronizado entre o sistema de gestão do restaurante e a aplicação de pedido.
Esta página explica o que é a capability, os conceitos que você precisa dominar, os fluxos de interação e como implementar cada lado. As regras normativas e a referência completa de campos estão na spec da API Indoor.
Para que serve¶
No delivery tradicional, um pedido nasce e morre como uma unidade isolada: é criado, preparado, entregue, encerrado. No salão a realidade é outra. Uma conta abre — numa mesa, numa comanda ou num balcão de autoatendimento — e recebe vários pedidos ao longo do tempo, possivelmente vindos de canais diferentes (garçom com tablet, cliente pelo QR Code, totem). Itens são cancelados ou transferidos para outra conta, o total é dividido, pago em partes, e só então a conta é fechada com emissão fiscal.
Sem um padrão, cada integração entre PDV e aplicação de salão precisava negociar bilateralmente como representar esse acúmulo: onde fica o total, como cancelar um item sem cancelar o pedido inteiro, quando emitir a nota, como tratar pagamento parcial. O Indoor elimina essa negociação ao definir a conta como entidade central e um conjunto fixo de operações e eventos sobre ela.
Pré-requisito: protocolo de Orders¶
Indoor é uma extensão de Orders, não uma capability autônoma
Esta capability pressupõe que ambas as partes já implementaram o protocolo de Orders do Open Delivery. Um pedido indoor (orderType: INDOOR) trafega pelo mesmo ciclo de vida e pelos mesmos mecanismos de evento de qualquer outro pedido. O Indoor adiciona, sobre esse fluxo base, a gestão da conta de salão.
Implementações que não possuem o protocolo de Orders ativo não podem usar esta capability.
O pedido é o canal de entrada de itens; a conta é o agrupador operacional desses pedidos para fins de pagamento, fechamento e integração fiscal.
Os dois lados da integração¶
| Papel | Responsabilidade |
|---|---|
| Software Service | Sistema de gestão do restaurante. Hospeda e implementa todos os endpoints desta spec e emite os eventos do ciclo de vida da conta. |
| Ordering Application | Aplicação de pedido (totem, tablet do garçom, app do cliente, frente de caixa). Consome os endpoints e recebe os eventos via webhook para manter-se sincronizada. |
Em todas as operações desta capability o Software Service é o servidor e a Ordering Application é o cliente.
Conceitos-chave¶
A conta (Account)¶
A conta é o agrupador de todos os pedidos, itens, pagamentos e documentos fiscais de uma sessão de consumo. Cada conta é localizada por uma chave operacional — a combinação operationMode + identifier:
operationMode |
Significado | Exemplo de identifier |
|---|---|---|
TABLE |
Atendimento por mesa | "5" (mesa 5) |
TAB |
Atendimento por comanda | "A-102" |
COUNTER |
Consumo no balcão | "3" (posição 3) |
Além da chave operacional, a conta pode ter um accountId interno do PDV. A consulta primária é feita pela chave operacional (GET /accounts?operationMode=TABLE&identifier=5); quando o accountId já é conhecido, há também GET /accounts/{accountId}. A conta sempre traz um campo lastEvent com o tipo do último evento emitido (ex.: ACCOUNT_ITEM_ADDED) — útil para sincronização e depuração quando a entrega do webhook é incerta (ex.: após reconexão).
Canais de origem (originChannel)¶
O operationMode define como a conta é agrupada — mas não diz por onde o pedido entrou. Essa informação é do originChannel, presente em cada pedido, e é aqui que fica explícito que o Indoor não pressupõe atendimento mediado por garçom:
originChannel.type |
Descrição |
|---|---|
TOTEM |
Autoatendimento em totem físico no estabelecimento. |
QR_CODE |
Cliente escaneia um QR Code na mesa/comanda e pede pelo próprio celular. |
CUSTOMER_TABLET |
Tablet entregue ao cliente para pedir diretamente, sem intermediário. |
WAITER_TABLET |
Tablet usado pelo garçom/atendente para lançar o pedido. |
FRONT_DESK |
Pedido lançado na frente de caixa. |
POS |
Pedido originado diretamente no sistema de ponto de venda. |
APP |
App de pedido do estabelecimento ou de terceiros. |
WHATSAPP |
Pedido recebido via WhatsApp. |
OTHER |
Qualquer outro canal não listado. |
operationMode e originChannel são independentes: uma mesma conta pode acumular pedidos de canais diferentes ao longo da sessão. Por exemplo, o cliente abre a conta pelo QR Code (originChannel: QR_CODE) e, depois, o garçom lança um item adicional pelo tablet (originChannel: WAITER_TABLET) — ambos os pedidos caem na mesma conta porque compartilham a chave operacional (operationMode + identifier). O canal é só um metadado do pedido, não afeta a identidade da conta.
Como a conta nasce¶
A conta não é criada por nenhum endpoint desta spec. Ela nasce automaticamente no Software Service quando o protocolo de Orders processa um pedido com orderType: INDOOR para uma chave operacional que ainda não tem conta aberta — seja esse pedido originado por um garçom, um totem ou um QR Code. A partir daí, novos pedidos INDOOR para a mesma chave acumulam itens na conta existente, independente do canal de origem de cada um.
Status da conta¶
stateDiagram-v2
direction LR
[*] --> IN_USE : 1º pedido INDOOR
IN_USE --> IN_PAYMENT : pré-fechamento
IN_PAYMENT --> IN_USE : desbloqueio
IN_PAYMENT --> CLOSED : fechamento
CLOSED --> [*]
| Status | Significado |
|---|---|
IN_USE |
Conta aberta, aceitando novos itens. Pagamentos também podem ser registrados aqui. |
IN_PAYMENT |
Conta pré-fechada/bloqueada — não aceita novos itens, mas continua aceitando pagamentos, aguardando o fechamento. |
CLOSED |
Conta fechada definitivamente. Nenhuma operação adicional é aceita. |
Pagamentos e fechamento¶
Este é o ponto que mais gera confusão em integrações Indoor, então vale destacar com cuidado:
Pagamento não depende de pré-fechamento. POST /accounts/payments pode ser chamado a qualquer momento — com a conta IN_USE ou já IN_PAYMENT. Não é preciso esperar o pré-fechamento para registrar um pagamento parcial: muitos estabelecimentos recebem pagamentos ao longo da sessão (ex.: o cliente paga uma rodada de bebidas no meio do consumo).
Pré-fechamento é um lock, não um gatilho de pagamento. POST /accounts/pre-close bloqueia novos itens, sinalizando que a conta está pronta para o checkout final. É o momento em que se espera o último pagamento — o saldo restante, se já houve pagamentos anteriores, ou o valor total, se nenhum pagamento foi feito até então.
Fechamento é definitivo. POST /accounts/close só deve ser chamado quando o total pago cobrir o valor da conta. Após o fechamento, nenhuma operação adicional é aceita — incluindo novos pagamentos.
| Operação | IN_USE |
IN_PAYMENT |
|---|---|---|
| Adicionar itens (via Orders) | ✅ | ❌ |
| Cancelar item | ✅ | ❌ |
| Transferir itens | ✅ | ❌ |
Registrar pagamento (POST /accounts/payments) |
✅ | ✅ |
Pré-fechar (pre-close) |
✅ | — |
Desbloquear (unlock) |
— | ✅ |
Fechar (close) |
❌ | ✅ |
Pagamento é, propositalmente, a única operação válida nos dois estados — é o que permite cobrar o cliente sem travar o resto da operação da conta.
Eventos¶
A cada transição relevante, o Software Service DEVE notificar a Ordering Application via webhook. Não há polling para eventos Indoor: a entrega é exclusivamente via webhook, e a Ordering Application deve implementar um endpoint compatível com o contrato accountEvent da spec para recebê-los.
| Evento | Gatilho | Status após |
|---|---|---|
ACCOUNT_OPENED |
Conta criada (pedido INDOOR) | IN_USE |
ACCOUNT_ITEM_ADDED |
Novo pedido INDOOR adiciona itens | IN_USE |
ACCOUNT_ITEM_REMOVED |
Itens transferidos para outra conta | IN_USE |
ACCOUNT_ITEM_CANCELLED |
Item cancelado | IN_USE |
ACCOUNT_PRE_CLOSED |
Conta bloqueada para pagamento | IN_PAYMENT |
ACCOUNT_UNLOCKED |
Bloqueio revertido | IN_USE |
ACCOUNT_CLOSED |
Conta fechada definitivamente | CLOSED |
PAYMENT_CREATED |
Pagamento lançado | — |
FISCAL_ISSUED |
Documento fiscal emitido com sucesso | — |
FISCAL_ERROR |
Falha na emissão do documento fiscal | — |
Fluxos¶
Os fluxos abaixo mostram a sequência de chamadas entre a Ordering Application e o Software Service, e os eventos emitidos em cada passo.
Fluxo normal¶
Ciclo completo de uma sessão de salão: abertura via pedido INDOOR, adição de itens, pré-fechamento, pagamento, emissão fiscal e fechamento.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
Note over OA,SS: Abertura
OA->>SS: POST /orders (orderType: INDOOR)
SS-->>OA: 201 Created
SS-)OA: event: ACCOUNT_OPENED — status: IN_USE
Note over OA,SS: Adição de itens
OA->>SS: POST /orders (novo pedido INDOOR)
SS-->>OA: 201 Created
SS-)OA: event: ACCOUNT_ITEM_ADDED
opt Pagamento antecipado (opcional, a qualquer momento em IN_USE)
OA->>SS: POST /accounts/{id}/payments
SS-->>OA: 200 OK
SS-)OA: event: PAYMENT_CREATED
end
Note over OA,SS: Pré-fechamento — lock para 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: Quitação final
OA->>SS: POST /accounts/{id}/payments
SS-->>OA: 200 OK
SS-)OA: event: PAYMENT_CREATED
Note over OA,SS: Emissão fiscal
OA->>SS: POST /accounts/{id}/fiscal
SS-->>OA: 202 Accepted
SS-)OA: event: FISCAL_ISSUED (assíncrono)
Note over OA,SS: Fechamento
OA->>SS: POST /accounts/{id}/close
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_CLOSED — status: CLOSED
Desbloqueio (unlock)¶
A Ordering Application pode reverter um pré-fechamento, devolvendo a conta ao estado IN_USE para continuar adicionando itens.
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: Operador decide adicionar mais itens
OA->>SS: POST /accounts/{id}/unlock
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_UNLOCKED — status: IN_USE
OA->>SS: POST /orders (novo pedido INDOOR)
SS-->>OA: 201 Created
SS-)OA: event: ACCOUNT_ITEM_ADDED
Pagamentos parciais¶
Uma conta pode receber múltiplos pagamentos ao longo de toda a sua vida útil, não apenas após o pré-fechamento — inclusive intercalados com a inclusão de novos itens, já que pagamento e itens não competem pelo mesmo lock. O exemplo abaixo mostra um pagamento parcial registrado ainda em IN_USE (ex.: o cliente paga uma rodada de bebidas no meio do consumo), seguido da inclusão de mais itens, e só então o pré-fechamento e a quitação final.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
Note over OA,SS: Pagamento parcial — ex.: cartão de crédito, conta ainda em uso
OA->>SS: POST /accounts/{id}/payments
SS-->>OA: 200 OK
SS-)OA: event: PAYMENT_CREATED (valor parcial)
Note over OA,SS: Conta continua aberta — novos itens são adicionados normalmente
OA->>SS: POST /orders (novo pedido INDOOR)
SS-->>OA: 201 Created
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: Quitação final — ex.: dinheiro (saldo restante)
OA->>SS: POST /accounts/{id}/payments
SS-->>OA: 200 OK
SS-)OA: event: PAYMENT_CREATED (saldo quitado)
OA->>SS: POST /accounts/{id}/close
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_CLOSED — status: CLOSED
Emissão fiscal¶
A emissão é assíncrona — o Software Service retorna 202 Accepted e emite o evento quando o documento fica disponível. A conta pode ser fechada mesmo sem emissão bem-sucedida.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
OA->>SS: POST /accounts/{id}/fiscal
SS-->>OA: 202 Accepted
alt Emissão bem-sucedida
SS-)OA: event: FISCAL_ISSUED
OA->>SS: GET /accounts/{id}/fiscal
SS-->>OA: 200 OK (documento disponível)
else Falha na emissão
SS-)OA: event: FISCAL_ERROR
Note over OA: Operador registra falha
end
Note over OA,SS: Conta pode ser fechada independente do resultado fiscal
OA->>SS: POST /accounts/{id}/close
SS-->>OA: 202 Accepted
SS-)OA: event: ACCOUNT_CLOSED — status: CLOSED
Cancelamento de item¶
Cancela um item específico dentro de uma conta IN_USE. A conta permanece aberta e os demais itens não são afetados.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
Note over OA,SS: Item já adicionado à conta via pedido INDOOR
OA->>SS: POST /accounts/items/{itemId}/cancel
SS-->>OA: 200 OK
SS-)OA: event: ACCOUNT_ITEM_CANCELLED
Note over OA,SS: Conta permanece IN_USE — demais itens inalterados
Transferência entre contas¶
Move itens de uma conta para outra — ex.: mesa muda de lugar, grupos se separam, ou um pedido de balcão é remanejado para uma comanda. Ambas as contas devem estar IN_USE.
sequenceDiagram
participant OA as Ordering Application
participant SS as Software Service
Note over OA,SS: Conta A (origem) e Conta B (destino) estão IN_USE
OA->>SS: POST /accounts/transfers
Note over OA,SS: body: targetAccountId + lista de itens
SS-->>OA: 200 OK
SS-)OA: event: ACCOUNT_ITEM_REMOVED — Conta A (origem)
SS-)OA: event: ACCOUNT_ITEM_ADDED — Conta B (destino)
Note over OA,SS: Ambas as contas permanecem IN_USE
Implementando o Software Service¶
Se você hospeda os endpoints e gerencia as contas, atente para:
Abra a conta na recepção do primeiro pedido INDOOR. Quando o protocolo de Orders processar um pedido orderType: INDOOR para uma chave operacional sem conta ativa, crie a conta e emita ACCOUNT_OPENED. Pedidos seguintes para a mesma chave acumulam itens (ACCOUNT_ITEM_ADDED) em vez de abrir uma conta nova.
Emita um evento para cada transição. Toda mudança de estado relevante precisa gerar o evento correspondente, notificado via webhook (accountEvent). A Ordering Application depende exclusivamente desses eventos para se sincronizar — uma transição sem evento é uma transição invisível. Não há fallback de polling: se o webhook falhar, use o campo lastEvent da conta para reconciliar o estado.
Trate o pré-fechamento como um bloqueio real — mas só para itens. Uma conta IN_PAYMENT não deve aceitar novos itens. Se o operador precisar adicionar algo, exija o unlock explícito (ACCOUNT_UNLOCKED, volta a IN_USE). Pagamento não entra nesse bloqueio: POST /accounts/{id}/payments deve continuar sendo aceito tanto em IN_USE quanto em IN_PAYMENT, a qualquer momento — não exija pré-fechamento como condição para registrar um pagamento.
Valide o pagamento antes de fechar, não antes de pagar. Acumule os PAYMENT_CREATED ao longo de toda a vida da conta e só aceite POST /accounts/{id}/close quando o total quitado cobrir o valor da conta — salvo regras de negócio próprias (cortesia, desconto). Após o fechamento, rejeite qualquer operação adicional, incluindo novos pagamentos: close é o único marco realmente irreversível do ciclo de vida.
Faça a emissão fiscal assíncrona. Responda 202 Accepted imediatamente e emita FISCAL_ISSUED ou FISCAL_ERROR quando o resultado estiver disponível. Não bloqueie o fechamento da conta esperando a SEFAZ.
Mantenha os totais consistentes. Cancelamentos, transferências e novos pedidos precisam refletir imediatamente em totals, já que a Ordering Application exibe esse valor ao cliente.
Implementando a Ordering Application¶
Se você consome os endpoints e exibe a conta ao operador ou cliente, atente para:
Localize a conta pela chave operacional. Use GET /accounts?operationMode=...&identifier=... como forma primária de acesso. Só use GET /accounts/{accountId} quando já tiver o accountId retornado anteriormente.
Receba eventos via webhook. O Indoor não tem polling: implemente o endpoint de webhook (conforme o contrato accountEvent da spec) para receber ACCOUNT_ITEM_ADDED, PAYMENT_CREATED, FISCAL_ISSUED etc. em tempo real e atualizar a tela. Use o lastEvent da conta para reconciliar o estado caso suspeite de entrega perdida.
Não espere o pré-fechamento para lançar um pagamento. POST /accounts/{id}/payments funciona com a conta IN_USE ou IN_PAYMENT — use-o sempre que o cliente quiser pagar, mesmo no meio do consumo. Reserve o pre-close para o momento em que a conta deve parar de aceitar novos itens.
Não assuma fechamento imediato após o pagamento. Pagamento e fechamento são passos distintos. Lance os pagamentos com POST /accounts/{id}/payments e só então chame POST /accounts/{id}/close — tipicamente após o pre-close, quando o saldo restante (ou total, se nenhum pagamento anterior existiu) é quitado.
Trate a emissão fiscal como assíncrona. Após 202 Accepted, aguarde o evento FISCAL_ISSUED (ou FISCAL_ERROR) — não espere o documento na resposta da requisição. Consulte GET /accounts/{id}/fiscal quando o evento chegar.
Reflita cancelamentos e transferências na hora. Ao receber ACCOUNT_ITEM_CANCELLED ou ACCOUNT_ITEM_REMOVED/ACCOUNT_ITEM_ADDED, atualize a conta exibida — o cliente não pode ver itens que já saíram da conta.
Checklist — Software Service
- Conta criada e
ACCOUNT_OPENEDemitido na recepção do 1º pedido INDOOR. - Todas as transições de estado emitem o evento correspondente.
- Conta
IN_PAYMENTrejeita novos itens até ounlock. - Pagamento (
POST /accounts/payments) é aceito emIN_USEeIN_PAYMENT, sem depender de pré-fechamento. - Total quitado é validado antes de aceitar o fechamento.
- Após
close, toda operação adicional é rejeitada — incluindo novos pagamentos. - Emissão fiscal responde
202e emiteFISCAL_ISSUED/FISCAL_ERRORdepois. totalsrefletem cancelamentos e transferências imediatamente.
Checklist — Ordering Application
- Conta localizada pela chave operacional (
operationMode+identifier). - Endpoint de webhook implementado e registrado para receber eventos (sem polling).
- Pagamentos podem ser lançados a qualquer momento (
IN_USEouIN_PAYMENT), não só após o pré-fechamento. - Pagamento e fechamento tratados como passos separados.
- Emissão fiscal tratada como assíncrona (aguarda o evento).
- Cancelamentos e transferências refletidos na interface em tempo real.
Referência completa de campos e regras normativas: API Indoor →