Pular para o conteúdo

Despachar a mensagem do ato

POST
/notices/{id}/dispatch
curl --request POST \
--url https://api.emmaos.com.br/notices/019928f4-3c21-7a4b-8f0e-6c2d5b1a9f77/dispatch \
--header 'Authorization: Bearer <token>'

Envia ao destinatário a mensagem do ato e registra o fato: o ato passa de PENDING a SENT e ganha um evento MESSAGE_SENT na linha do tempo. Sempre limitado ao tribunal do token: um id que exista em outro tribunal responde 404, não 403.

Despachar é ato explícito, e não efeito do cadastro. O ato nasce PENDING em POST /notices e só sai daí por aqui. A ordem interna é reserva → claim → envio → registro: o id da mensagem é cunhado e gravado antes do envio, com o status ainda PENDING; só uma execução por vez consegue o claim do envio (as concorrentes recebem 409 DISPATCH_IN_PROGRESS); e o evento MESSAGE_SENT é gravado depois que o provedor aceitou, mesmo que um relatório de entrega já tenha avançado o status (nesse caso o evento entra e o status fica onde está; a resposta traz o status real). Uma falha no meio deixa um id reservado sem evento (que a repetição reivindica), e nunca um despacho sem rastro.

Não é idempotente por header, e sim por estado. Repetir sobre um ato que já saiu de PENDING responde 409: o status não retrocede nem se repete. Re-envio deliberado seria outra mensagem, com outro id, e ainda não existe; é pergunta aberta na frente projeto.

Qual plataforma envia depende do stage. Os stages autorizados a usar a Infobip (hoje, só o dev-andre) enviam a primeira mensagem de verdade pelo WhatsApp. Num stage que tenha lista de destinatários, só para essa lista (422, RECIPIENT_NOT_ALLOWED, para qualquer outro número); o dev-andre não tem lista desde a FIN-158, como a produção. Os outros stages de desenvolvimento e o staging usam um gateway falso, que não envia nada; ele existe para o rito ser exercitável de ponta a ponta sem depender do provedor. Produção não sobe sem a plataforma real.

id
required
string format: uuid

UUIDv7 do ato.

Example
019928f4-3c21-7a4b-8f0e-6c2d5b1a9f77

Despachado, com o status novo do ato

Media typeapplication/json

O resultado do despacho. Dois campos, e só: o id do ato e o status novo. O id da mensagem no provedor NÃO sai aqui: é controle interno da reserva, não tem leitor declarado, e publicá-lo prenderia o formato do fornecedor no contrato (ver projecao-publica-do-ledger.md).

object
id
required

UUIDv7 do ato despachado.

string format: uuid
status
required

Progresso operacional do ato.

string
Allowed values: PENDING SENT VIEWED CONFIRMED IDENTITY_STARTED COMPLETED FAILED TIMED_OUT CANCELLED RECIPIENT_MISMATCH
Example
{
"id": "019928f4-3c21-7a4b-8f0e-6c2d5b1a9f77",
"status": "SENT"
}

Requisição inválida

Media typeapplication/json
object
error
required

Código estável do erro. É por ele que se programa.

string
Allowed values: INVALID_REQUEST INVALID_JSON NOT_FOUND NOT_IMPLEMENTED TIMELINE_TOO_LONG DATABASE_RESUMING INTERNAL_ERROR SEALED_DOCUMENT_MISSING DISPATCH_IN_PROGRESS IDEMPOTENCY_KEY_CONFLICT SEALED_DOCUMENT_TOO_LARGE SOURCE_DOCUMENT_TOO_MANY_PAGES SOURCE_DOCUMENT_UNREADABLE STORAGE_LIMIT_REACHED TENANT_NOT_CONFIGURED
field

Campo que causou a recusa, quando aplicável.

string
message

Texto em português para diagnóstico. Não programe contra ele: pode mudar sem aviso.

string
missing

Só com TENANT_NOT_CONFIGURED: o que falta na configuração do tribunal.

Array<string>
Allowed values: displayName publicPhone whatsappSender
Examples

Campo obrigatório ausente ou malformado

{
"error": "INVALID_REQUEST",
"field": "noticeType",
"message": "noticeType é obrigatório"
}

Sem header Authorization. Recusado pelo API Gateway antes de chegar à aplicação.

Token inválido, expirado, ou de um emissor que não é tribunal conhecido neste stage. Recusado pelo authorizer.

Não existe neste tribunal

Media typeapplication/json
object
error
required

Código estável do erro. É por ele que se programa.

string
Allowed values: INVALID_REQUEST INVALID_JSON NOT_FOUND NOT_IMPLEMENTED TIMELINE_TOO_LONG DATABASE_RESUMING INTERNAL_ERROR SEALED_DOCUMENT_MISSING DISPATCH_IN_PROGRESS IDEMPOTENCY_KEY_CONFLICT SEALED_DOCUMENT_TOO_LARGE SOURCE_DOCUMENT_TOO_MANY_PAGES SOURCE_DOCUMENT_UNREADABLE STORAGE_LIMIT_REACHED TENANT_NOT_CONFIGURED
field

Campo que causou a recusa, quando aplicável.

string
message

Texto em português para diagnóstico. Não programe contra ele: pode mudar sem aviso.

string
missing

Só com TENANT_NOT_CONFIGURED: o que falta na configuração do tribunal.

Array<string>
Allowed values: displayName publicPhone whatsappSender
Example
{
"error": "NOT_FOUND"
}

O pedido é válido, e o conflito é com o estado do ato ou do tribunal. Quatro códigos:

  • INVALID_STATUS_TRANSITION: o ato já saiu de PENDING. Repetir dá o mesmo resultado.
  • DISPATCH_IN_PROGRESS: outra execução está despachando este ato agora (duas abas, repetição depois de timeout). Esta chamada não enviou nada. Repetir em instantes resolve: ou a primeira mensagem já está registrada (200, com o status real), ou a outra execução soltou o claim e esta envia. Vem com Retry-After.
  • SEALED_DOCUMENT_MISSING: o ato não tem documento selado, e o destinatário nunca recebe o documento-fonte. Só acontece com atos criados antes de o selado existir. Nada foi enviado. Repetir dá o mesmo resultado.
  • TENANT_NOT_CONFIGURED: o tribunal não tem configurado o nome do órgão, o telefone público ou, no stage com WhatsApp real, o número de envio. missing diz quais faltam. Nada foi enviado, e o ato continua PENDING. Repetir dá o mesmo resultado até a plataforma configurar o tribunal.
Media typeapplication/json
object
error
required

Código estável do erro. É por ele que se programa.

string
Allowed values: INVALID_REQUEST INVALID_JSON NOT_FOUND NOT_IMPLEMENTED TIMELINE_TOO_LONG DATABASE_RESUMING INTERNAL_ERROR SEALED_DOCUMENT_MISSING DISPATCH_IN_PROGRESS IDEMPOTENCY_KEY_CONFLICT SEALED_DOCUMENT_TOO_LARGE SOURCE_DOCUMENT_TOO_MANY_PAGES SOURCE_DOCUMENT_UNREADABLE STORAGE_LIMIT_REACHED TENANT_NOT_CONFIGURED
field

Campo que causou a recusa, quando aplicável.

string
message

Texto em português para diagnóstico. Não programe contra ele: pode mudar sem aviso.

string
missing

Só com TENANT_NOT_CONFIGURED: o que falta na configuração do tribunal.

Array<string>
Allowed values: displayName publicPhone whatsappSender
Examples
{
"error": "INVALID_STATUS_TRANSITION",
"message": "Ato 019928f4-3c21-7a4b-8f0e-6c2d5b1a9f77 está em SENT; o evento MESSAGE_SENT afirma SENT, que não é avanço. O status não retrocede."
}

O despacho foi recusado e repetir dá o mesmo resultado. Dois códigos, duas causas:

  • RECIPIENT_NOT_ALLOWED: o destinatário não está autorizado neste stage. Recusa nossa, não do provedor.
  • DISPATCH_REJECTED: a plataforma de mensagens recusou de forma permanente (por exemplo, número inválido, ou a mensagem aceita no HTTP mas com status REJECTED no corpo). Nada foi registrado como enviado. Não repita: revise o telefone do destinatário no cadastro. Falha transitória (5xx, prazo, limite de taxa) continua sendo 502 DISPATCH_FAILED, esta sim repetível.
Media typeapplication/json
object
error
required

Código estável do erro. É por ele que se programa.

string
Allowed values: INVALID_REQUEST INVALID_JSON NOT_FOUND NOT_IMPLEMENTED TIMELINE_TOO_LONG DATABASE_RESUMING INTERNAL_ERROR SEALED_DOCUMENT_MISSING DISPATCH_IN_PROGRESS IDEMPOTENCY_KEY_CONFLICT SEALED_DOCUMENT_TOO_LARGE SOURCE_DOCUMENT_TOO_MANY_PAGES SOURCE_DOCUMENT_UNREADABLE STORAGE_LIMIT_REACHED TENANT_NOT_CONFIGURED
field

Campo que causou a recusa, quando aplicável.

string
message

Texto em português para diagnóstico. Não programe contra ele: pode mudar sem aviso.

string
missing

Só com TENANT_NOT_CONFIGURED: o que falta na configuração do tribunal.

Array<string>
Allowed values: displayName publicPhone whatsappSender
Examples
{
"error": "RECIPIENT_NOT_ALLOWED",
"message": "Destinatário *********4321 não está autorizado neste stage. Nada foi enviado."
}

Erro interno

Media typeapplication/json
object
error
required

Código estável do erro. É por ele que se programa.

string
Allowed values: INVALID_REQUEST INVALID_JSON NOT_FOUND NOT_IMPLEMENTED TIMELINE_TOO_LONG DATABASE_RESUMING INTERNAL_ERROR SEALED_DOCUMENT_MISSING DISPATCH_IN_PROGRESS IDEMPOTENCY_KEY_CONFLICT SEALED_DOCUMENT_TOO_LARGE SOURCE_DOCUMENT_TOO_MANY_PAGES SOURCE_DOCUMENT_UNREADABLE STORAGE_LIMIT_REACHED TENANT_NOT_CONFIGURED
field

Campo que causou a recusa, quando aplicável.

string
message

Texto em português para diagnóstico. Não programe contra ele: pode mudar sem aviso.

string
missing

Só com TENANT_NOT_CONFIGURED: o que falta na configuração do tribunal.

Array<string>
Allowed values: displayName publicPhone whatsappSender
Example
{
"error": "INTERNAL_ERROR"
}

Falha transitória da plataforma de mensagens (5xx, prazo, limite de taxa), ou não se sabe se ela aceitou (por exemplo, a resposta dela se perdeu por tempo esgotado). O ato ficou com o id reservado e sem evento; repita: a repetição reaproveita a mesma reserva e reenvia com o mesmo messageId. O backend não garante que o cidadão receba uma mensagem só: se a plataforma tinha aceitado e o registro se perdeu sem que nenhum relatório de entrega chegasse, a repetição reenvia, e a deduplicação pelo messageId do lado da plataforma ainda não foi verificada (FIN-107, P7). Se um relatório de entrega já chegou, o claim reconhece o envio e a repetição não manda nada.

Media typeapplication/json
object
error
required

Código estável do erro. É por ele que se programa.

string
Allowed values: INVALID_REQUEST INVALID_JSON NOT_FOUND NOT_IMPLEMENTED TIMELINE_TOO_LONG DATABASE_RESUMING INTERNAL_ERROR SEALED_DOCUMENT_MISSING DISPATCH_IN_PROGRESS IDEMPOTENCY_KEY_CONFLICT SEALED_DOCUMENT_TOO_LARGE SOURCE_DOCUMENT_TOO_MANY_PAGES SOURCE_DOCUMENT_UNREADABLE STORAGE_LIMIT_REACHED TENANT_NOT_CONFIGURED
field

Campo que causou a recusa, quando aplicável.

string
message

Texto em português para diagnóstico. Não programe contra ele: pode mudar sem aviso.

string
missing

Só com TENANT_NOT_CONFIGURED: o que falta na configuração do tribunal.

Array<string>
Allowed values: displayName publicPhone whatsappSender
Example
{
"error": "DISPATCH_FAILED",
"message": "A plataforma de mensagens não aceitou o despacho. Tente novamente."
}

O banco do tribunal estava pausado e não retomou dentro do orçamento da requisição. Repita.

Media typeapplication/json
object
error
required

Código estável do erro. É por ele que se programa.

string
Allowed values: INVALID_REQUEST INVALID_JSON NOT_FOUND NOT_IMPLEMENTED TIMELINE_TOO_LONG DATABASE_RESUMING INTERNAL_ERROR SEALED_DOCUMENT_MISSING DISPATCH_IN_PROGRESS IDEMPOTENCY_KEY_CONFLICT SEALED_DOCUMENT_TOO_LARGE SOURCE_DOCUMENT_TOO_MANY_PAGES SOURCE_DOCUMENT_UNREADABLE STORAGE_LIMIT_REACHED TENANT_NOT_CONFIGURED
field

Campo que causou a recusa, quando aplicável.

string
message

Texto em português para diagnóstico. Não programe contra ele: pode mudar sem aviso.

string
missing

Só com TENANT_NOT_CONFIGURED: o que falta na configuração do tribunal.

Array<string>
Allowed values: displayName publicPhone whatsappSender
Example
{
"error": "DATABASE_RESUMING",
"message": "Banco de dados está retomando. Tente novamente."
}
Retry-After
integer

Segundos a esperar antes de repetir.