Pular para o conteúdo principal

Caixa — visão geral

O caixa é uma conta operacional (operator_type=cashier), criada pela gestão e escopada a um workspace específico — ver Gestão → Operadores (PDV e caixa).

Autenticação

EndpointO que faz
POST /v1/auth/cashier/loginLogin com username/senha gerados pela gestão. Seta wallet_cashier_access_token, wallet_cashier_refresh_token, wallet_cashier_csrf_token.
POST /v1/auth/cashier/refreshRotaciona o refresh token.
POST /v1/auth/cashier/logoutRevoga a sessão e limpa os cookies wallet_cashier_*.

O JWT do caixa segue o mesmo formato do PDV — troque operator_type para "cashier". Veja PDV → visão geral para o exemplo completo das claims.

Localizar ou cadastrar um participante

Duas rotas globais (wallet-control-api), sem eventId porque identidade é global — o saldo/wallet fica na região:

EndpointO que faz
GET /v1/cashier/participants/by-phone/{phone}Consulta pura, nunca cria nada. 404 PARTICIPANT_NOT_FOUND se o telefone não tem cadastro ainda.
POST /v1/cashier/participantsCria-ou-localiza pelo telefone. Se for novo, cria o perfil sem auth_user_id (a pessoa ainda não instalou o app nem fez OTP — isso acontece depois, sozinho, na primeira vez que ela confirmar o OTP do mesmo telefone). Sempre devolve uma activationAuthorization: uma autorização assinada, de uso único, válida só pra esse participante e pro evento do caixa.
curl -X POST http://127.0.0.1:8787/v1/cashier/participants \
-H "Authorization: Bearer <accessToken do caixa>" \
-H "Content-Type: application/json" \
-d '{ "phone": "+5511999999999" }'

Consultar e ativar a wallet em nome do participante

Duas rotas regionais (wallet-api, /v1/events/{eventId}/...):

EndpointO que faz
GET /v1/events/{eventId}/cashier/wallets/{userId}Consulta pura: saldo e status da wallet desse participante nesse evento. 404 WALLET_NOT_FOUND se ele ainda não ativou.
POST /v1/events/{eventId}/cashier/wallets/activateCria a wallet, usando a activationAuthorization obtida no passo anterior. Idempotente. A autorização só pode ser usada uma vez — mesmo se a ativação falhar por outro motivo, ela é consumida; peça uma nova em caso de erro.
GET /v1/events/{eventId}/cashier/wallets/{userId}/statementExtrato paginado das movimentações dessa wallet (recargas hoje; vendas/estornos quando existirem).
curl -X POST http://127.0.0.1:8787/v1/events/{eventId}/cashier/wallets/activate \
-H "Authorization: Bearer <accessToken do caixa>" \
-H "Content-Type: application/json" \
-d '{ "userId": "<userId devolvido no passo anterior>", "activationAuthorization": "<token devolvido no passo anterior>" }'

Extrato

curl "http://127.0.0.1:8787/v1/events/{eventId}/cashier/wallets/{userId}/statement?limit=20&sortDirection=desc" \
-H "Authorization: Bearer <accessToken do caixa>"
Resposta
{
"items": [
{
"entryId": "018f26d7-...",
"transactionId": "018f26d7-...",
"type": "recharge",
"accountType": "WALLET_AVAILABLE",
"amountMinor": 5000,
"occurredAt": "2026-09-03T16:30:29.377Z",
"paymentMethod": "cash",
"note": null,
"createdByOperatorUsername": "CX-55130771",
"createdByOperatorDescription": "Entrada Principal"
}
],
"nextCursor": null
}

createdByOperatorUsername/createdByOperatorDescription identificam quem fez a movimentação (o caixa ou, no futuro, o PDV) — vêm gravados junto com o lançamento no momento em que ele acontece, então continuam mostrando quem era o operador na hora, mesmo que a conta seja renomeada depois. Ambos são null quando não há operador, ou quando a conta não tem descrição cadastrada.

Paginado por cursor (não por página numerada): quando nextCursor vier preenchido, chame de novo com ?cursor=<valor> pra pegar a próxima leva — mantendo os mesmos sortDirection/filtros da chamada original, senão a API rejeita com WALLET_STATEMENT_CURSOR_SORT_MISMATCH.

Filtros disponíveis, todos opcionais e combináveis:

ParâmetroO que faz
typeSó um tipo de movimento (hoje só recharge existe).
occurredAfter / occurredBeforeIntervalo de datas, ISO 8601, inclusivo dos dois lados.
sortDirectionasc ou desc (padrão: desc, mais recente primeiro).
limit1 a 100 (padrão 50).

amountMinor vem com sinal — positivo é crédito (dinheiro entrando), negativo é débito. paymentMethod/note só aparecem em lançamentos de recarga; ficam null pra outros tipos de movimento no futuro.

Recarga

EndpointO que faz
POST /v1/events/{eventId}/cashier/topupsRecarrega a wallet do participante. Sempre imediata — não existe confirmação externa de pagamento nesta fase, o caixa só declara como o dinheiro entrou.

paymentMethod é texto livre, não uma lista fechada — "cash", "credit_card", "pix", ou qualquer categoria que a organização usar. Exige o header Idempotency-Key: repetir a mesma chave devolve o mesmo resultado, sem recreditar — mesmo que as duas chamadas cheguem ao mesmo tempo, nunca executam a recarga em paralelo. Se uma segunda chamada com a mesma chave chegar enquanto a primeira ainda está em andamento, ela é rejeitada com 409 IDEMPOTENCY_KEY_IN_PROGRESS (em vez de esperar) — tente de novo em seguida, não é um erro definitivo.

curl -X POST http://127.0.0.1:8787/v1/events/{eventId}/cashier/topups \
-H "Authorization: Bearer <accessToken do caixa>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: <uuid gerado pelo cliente>" \
-d '{ "userId": "<userId do participante>", "amountMinor": 5000, "paymentMethod": "cash" }'

Credencial QR impressa com PIN

Para participante sem celular/app:

EndpointO que faz
POST /v1/events/{eventId}/cashier/wallet-credentials/printed-qrEmite uma credencial revogável ligada à wallet. O sistema gera um PIN aleatório e devolve no corpo da resposta uma única vez — não fica gravado em texto puro em lugar nenhum, só o hash. Reemitir revoga automaticamente a credencial anterior — nunca fica mais de uma ativa por wallet.

Não existe endpoint de reset de PIN isolado, mantendo o mesmo QR. QR e PIN são dois fatores independentes — o PDV lê o QR pra achar a credencial, só depois pede o PIN pra validar aquela credencial específica. Um vazando sozinho não serve pra nada; só os dois juntos são um problema. Por isso, diante de qualquer suspeita (PIN visto, cartão sumiu, o que for), a resposta é sempre reemitir tudo — nunca dá pra ter certeza que só um dos dois vazou. Se for só o participante esquecendo o PIN, sem suspeita nenhuma de vazamento, também vale reemitir (é o único jeito disponível hoje).

O QR impresso ainda não pode ser usado pra pagar — o PDV ainda não tem o lado de leitura/consumo dessa credencial implementado.

Minhas próprias operações

GET /v1/events/{eventId}/cashier/operations — é o "extrato" acima, só que ao contrário: em vez de todas as movimentações de uma wallet, é todas as operações que você mesmo fez, em quantas wallets diferentes tiver mexido. Sempre autoserviço — não existe parâmetro pra ver a atividade de outro caixa, é sempre a sua própria, identificada pelo seu próprio token.

curl "http://127.0.0.1:8787/v1/events/{eventId}/cashier/operations?limit=20" \
-H "Authorization: Bearer <accessToken do caixa>"
Resposta
{
"items": [
{
"transactionId": "018f26d7-...",
"type": "recharge",
"userId": "018f26d7-...",
"walletId": "018f26d7-...",
"amountMinor": 5000,
"occurredAt": "2026-09-03T16:30:29.377Z",
"paymentMethod": "cash",
"note": null,
"createdByOperatorUsername": "CX-55130771",
"createdByOperatorDescription": "Entrada Principal"
}
],
"nextCursor": null
}

Aqui createdByOperatorUsername/createdByOperatorDescription são sempre os seus mesmos — esse endpoint é autoserviço.

Mesmos filtros/paginação do extrato de wallet (type, occurredAfter, occurredBefore, sortDirection, limit, cursor), mais um a mais: userId, pra filtrar só as operações feitas num participante específico.

Diferença importante: occurredAfter aqui tem um teto — a API nunca mostra nada com mais de 48 horas, não importa o que você peça. Não é um valor padrão que dá pra contornar; é um limite de verdade, imposto pelo servidor. Existe porque não há conceito de turno de caixa ainda — sem um "início de turno" formal, essa janela fixa evita que a sessão do caixa consiga navegar um histórico enorme e desnecessário.

Esse endpoint é só de identificação — serve pra você mesmo conferir o que fez (ex.: bater com o caixa físico no fim do dia), não existe (ainda) nenhuma ação de estorno de recarga a partir daqui.

O que ainda não existe

  • Verificação/consumo da credencial QR impressa pelo PDV (a emissão e o reset de PIN já existem; falta o PDV conseguir usar isso num pagamento).
  • Turno de caixa (abrir com valor inicial em espécie, fechar com conciliação) — a auditoria da recarga hoje usa direto o operatorAccountId do JWT, sem turno/caixa físico provisionado.