Skip to main content
Melhoria

Mudanca de contrato: um pedido pode chegar sem endereco de entrega

As lojas em breve poderao registrar vendas feitas fora do WhatsApp: na loja fisica ou por telefone. Quando o produto foi entregue em maos, o pedido nao tem endereco, e GET /v1/orders e GET /v1/orders/{order_id} o devolvem com address: null. Se a sua integracao le address.full_address, verifique antes que address nao e nulo.Um pedido tambem pode chegar sem linhas (items: []): foi registrado apenas a partir de um comprovante de pagamento ou de uma marcacao da loja, e ganha suas linhas depois com o mesmo id. Se voce sincroniza pedidos, leia-o de novo antes de considera-lo final.Dois valores novos que ja podem aparecer:
  • payment_terms: paid_pending_review — o cliente enviou o comprovante e a loja ainda nao o revisou. Trate como nao pago ate que mude.
  • status de produto nas respostas: unpublished. Criar ou editar um produto continua aceitando apenas published.
Nova funcionalidade

Um envio que a conta nao pode pagar agora responde 402

Os envios de WhatsApp sao pagos a partir de um saldo pre-pago. Ate agora, uma conta sem saldo podia continuar enviando pela API e a divida aparecia depois, sem ninguem ter sido avisado. A partir de hoje, POST /v1/template-messages responde 402 e nao envia nada quando a conta nao pode pagar a mensagem.O type diz qual dos tres casos e:
  • insufficient_funds — a conta ficou sem saldo para mensagens de saida.
  • wallet_sends_paused — os envios foram cortados quando o saldo acabou.
  • wallet_currency_mismatch — o saldo esta numa moeda que esta API nao consegue cotar. Tentar de novo nao muda nada; fale com a gente.
Os dois primeiros se resolvem recarregando na pagina de pagamentos do WhatsApp, e o envio volta sozinho. Nao precisa de um event_id novo: o que recebeu o 402 continua livre, entao voce pode reenviar o mesmo evento depois de recarregar e nada e duplicado.Tentar de novo sem recarregar devolve o mesmo 402. Trate como as outras recusas de conta: registre o type e nao fique tentando em loop.
Isto nao muda nada enquanto sua conta nao estiver incluida na cobranca por saldo. Ate la a API responde exatamente como antes.
Correção

Templates com variáveis por nome já podem ser enviados

Um template criado com marcadores nomeados — Hola {{name}}, …, que é o que o editor de campanhas escreve ao vincular uma variável a um campo do contato — aparecia em GET /v1/templates com required_variables: [], como se não tivesse variáveis, e qualquer envio era aceito com 202 para o provedor rejeitar depois. Agora:
  • O catálogo publica as chaves reais. required_variables lista posições (["1", "2"]) ou nomes (["name"]) conforme o template foi criado, e um novo campo sendable marca o template que mistura os dois estilos, que não pode ser enviado por esta via.
  • variables aceita as mesmas chaves. {"name": "Ronald"} sai como parâmetro nomeado.
  • Um envio que o provedor rejeitaria é rejeitado antes, com 422. Uma chave faltando, uma sobrando ou uma posição num template nomeado não produzem mais um 202 vazio; a mensagem nomeia as chaves que o template espera.
Se a sua integração já envia posições em templates posicionais, nada muda. Se enviava chaves a mais, agora recebe um 422 que antes se perdia no provedor.
Nova funcionalidade

O carrinho abandonado agora se ve e se desliga pelo painel

Saber se os envios de um comercio estavam saindo obrigava a consultar event_id por event_id, e desliga-los nao tinha outra via alem de parar de chamar a API do ecommerce. As duas coisas agora se resolvem no painel de carrinho abandonado:
  • Historico de envios. Lista os envios do comercio, do mais recente ao mais antigo, com o estado de cada um, dentro da janela de retencao de 30 dias. Nao e mais preciso saber de antemao qual event_id procurar.
  • Interruptor de ligar e desligar. O envio pode ser desligado e ligado de novo sem tocar na integracao. Desligar nao apaga nada: o historico continua la.
Com o envio desligado, POST /v1/template-messages responde 200 com "status": "discarded" e "status_detail": "disabled". Nao e um erro: a requisicao funcionou e o envio esta desligado de proposito, igual ao que acontece numa conversa em pausa. O estado fica guardado sob o seu event_id, entao GET /v1/template-messages/{event_id} conta a mesma historia.O seu ecommerce nao precisa de nenhuma mudanca. Se voce ja aceita qualquer 2xx e ramifica pelo status, um envio desligado entra por onde stale e paused ja entram. Esse catalogo de motivos pode crescer: registre o valor e nao ramifique de forma exaustiva por ele.
Melhoria

Enviar nao espera mais 10 segundos

POST /v1/template-messages mantinha o request aberto por 10 segundos aguardando uma confirmacao de entrega que nunca chegava por esse caminho: esgotava a espera inteira e respondia in_progress. Esses dez segundos nao compravam nenhuma informacao. Agora a resposta chega assim que o servico de envio pega a mensagem. Tres coisas mudam para voce:
  • Um envio aceito responde 202, nao 200. Se a sua integracao trata so o 200 como sucesso, ela passaria a ler envios bons como falhas. Aceite qualquer 2xx. O 200 fica para as respostas que nao produzem um envio novo: um event_id repetido e um envio descartado (stale, paused).
  • O estado de um envio novo passa a ser accepted, nao in_progress. Significam a mesma coisa — “a caminho, ainda nao sabemos como terminou” — e o desfecho continua sendo consultado em GET /v1/template-messages/{event_id}, que e o unico caminho: nenhum webhook reporta o resultado de um envio. in_progress continua sendo um valor valido do contrato: envios feitos antes desta mudanca ainda o devolvem enquanto estiverem dentro da janela de retencao de 30 dias.
  • A modalidade de confirmacao rapida por conta acaba, porque agora e o comportamento de todos. Nao ha nada a solicitar nem a configurar.
O timeout recomendado do seu cliente cai de 45 para 35 segundos: a chamada ao servico de envio mantem o seu teto de 30 segundos, e esse e agora o unico trecho que pode demorar.
Correcao

O estado de um envio agora diz como ele terminou

GET /v1/template-messages/{event_id} respondia in_progress para sempre. sent, delivered e read estavam no contrato mas nunca chegavam, e failed so aparecia por uma recusa de conta. Uma mensagem recusada pela Meta parecia igual a uma entregue. Agora o estado e resolvido contra a mensagem real. Tres coisas mudam para voce:
  • Os quatro estados ja chegam. Se a sua integracao assumia que in_progress era o fim do caminho, voce passara a ver sent, delivered, read e failed. Ramifique pelo status.
  • status_detail em um failed nao e uma lista fechada e pode estar ausente. Alem de account_not_eligible e no_sender_number, agora pode trazer send_failed quando o envio nao chegou a se completar, send_cancelled quando foi cancelado antes de ser tentado, ou provider_error_<codigo> com o codigo da propria Meta quando o provedor recusou a entrega com um — por exemplo provider_error_131049, o limite de frequencia de modelos de marketing. Esse catalogo muda sem nos avisar: nao o trate de forma exaustiva, registre o valor e caia para uma falha generica se nao reconhece-lo ou se vier vazio.
  • updated_at e quando o registro do envio mudou pela ultima vez, nao quando voce consultou. Consultar ja nao o move, mas ele pode se mover sem que o estado mude, entao nao serve como marca d’agua: reconcilie pelo event_id.
Os envios feitos antes desta mudanca tambem passam a responder corretamente, desde que ainda estejam dentro da janela de retencao.
Correcao

GET /v1/templates le o catalogo de onde os envios realmente saem

A lista vinha de uma copia que o trilho de envio desta API nao escreve. Podia mostrar modelos que o envio depois recusava, e esconder os que voce podia mandar. Agora os dois leem o mesmo catalogo. Tres coisas mudam para voce:
  • language nao pode mais vir vazio. Antes diziamos que, se o modelo nao tivesse idioma registrado, qualquer codigo passava na validacao. Esse caso acabou: o catalogo registra o idioma sempre, entao um idioma que nao bate e 422, sem excecao. Se voce dependia dessa saida, mande o codigo que a lista devolve.
  • category agora chega em minusculas — marketing, nao MARKETING. Ajuste se voce compara a string como esta.
  • Sua lista pode mudar de tamanho, ate ficar vazia. Os modelos vivem no catalogo do trilho de envio, preenchido quando sua conta sincroniza com o WhatsApp. Uma lista vazia significa que hoje todo envio e recusado — escreva para nos e sincronizamos.
Melhoria

Uma recusa de envio agora diz o que aconteceu

POST /v1/template-messages respondia 502 para situações bem diferentes: uma conta não habilitada, uma conversa em pausa, um event_id reciclado. De fora todas pareciam idênticas, e a documentação pedia para repetir mesmo quando nenhuma retentativa podia mudar o resultado. Agora cada uma tem a sua própria resposta:
  • Conta não habilitada para enviar → 422 com "type": "account_not_eligible" em detail[0]. Não é transitório: fale conosco para habilitá-la.
  • Sem número remetente conectado → 422 com "type": "no_sender_number". Conecte o número de WhatsApp da sua conta e repita.
  • Conversa em pausa → 200 com "status": "discarded" e "status_detail": "paused". A requisição funcionou; o envio está desligado de propósito, porque um agente humano assumiu a conversa ou há um corte de faturamento.
  • event_id já atrelado a outra mensagem, ou fora da janela de idempotência → 409 com "type": "idempotency_conflict". Mande o evento com um event_id novo: repetir o mesmo devolve o mesmo 409.
O 502 fica só para uma falha real do serviço interno de envio, essa sim vale repetir com backoff limitado.Com isso, failed passa a ser alcançável: as duas recusas de conta ficam guardadas sob o seu event_id, então GET /v1/template-messages/{event_id} conta a mesma história que o envio. Uma recusa de entrega ainda não produz esse estado. E um efeito da idempotência que vale ter em mente: reenviar o mesmo event_id depois de uma recusa de conta responde 200 com "status": "failed" — é o estado guardado, não um envio novo.
Correcao

O envio outbound agora e roteado por ambiente

POST /v1/template-messages devolve 502 para qualquer envio que passe na validacao: a chamada interna vai para um servico que nao atende essa rota. Esta versao da a escrita o proprio endereco, configuravel por ambiente. O envio comeca a funcionar quando esse endereco e apontado, nao com esta publicacao.Uma coisa nao vai mudar quando ligar: o template enviado nao aparecera em GET /v1/conversations/{id}/messages. O envio e essa lista sao servidos por armazenamentos diferentes, entao reconcilie pelo seu proprio event_id, nao pela lista.A conexao com o banco tambem nao fica mais retida durante a espera do envio. Se voce fazia rajadas de envios, podia ver lentidao ou erros em outros endpoints da API durante essas rajadas; isso esta corrigido aqui.
Nova funcionalidade

Lista de templates aprovados

Novo endpoint GET /v1/templates: devolve os templates que a sua conta pode enviar agora mesmo, com as variáveis que cada um espera. É o que faltava para montar um POST /v1/template-messages sem adivinhar os valores.Cada template traz:
  • name — o valor que vai em template.name.
  • language — o valor que vai em template.language. Pode vir vazio: o template não tem idioma registrado, mas o envio ainda exige um código, então não repasse o null — o request responde 422. Só nesse caso qualquer código passa a validação; se o template registra idioma e o seu não corresponde, também é 422. O código viaja tal qual para o WhatsApp, então errar o idioma faz o envio falhar lá — e hoje isso não se vê daqui: o estado fica em in_progress.
  • required_variables — as chaves que precisam estar em variables. "1" preenche {{1}}. Uma lista vazia significa que o template não leva variáveis.
  • category e display_name — este último é null a menos que difira do name, para reconhecê-lo do seu lado. A chave sempre vem: teste o valor, não a presença.
Só aparecem os templates aprovados, porque são exatamente os que um envio aceita: listar um rascunho lhe daria um template que responde 422. E se a lista voltar vazia, a sua conta ainda não tem nenhum template aprovado — cada envio vai responder 422 até que tenha.Escopo necessário: outbound:read.
Correcao

Correcao do contrato de Mensagens de Template

A pagina de carrinho abandonado documentava POST /v1/cart-abandonments, um contrato que foi substituido durante o desenho e que nunca existiu. Ele foi substituido pelo contrato real, na mesma URL.Se voce comecou a construir contra a versao anterior, estas sao as mudancas que afetam voce:
  • O caminho e POST /v1/template-messages, e o estado e consultado em GET /v1/template-messages/{event_id}.
  • O template viaja no request, como template: {name, language}. A versao anterior dizia o contrario: que ficava fixado do nosso lado.
  • template_variables agora se chama variables.
  • first_name, last_name e second_last_name nao existem. Este endpoint nao cria nem atualiza o contato, e hoje nenhuma chamada publica faz isso: a API de Contatos e somente leitura.
  • occurred_at e opcional. Se voce omitir, o aviso nunca e considerado vencido.
  • Novo campo opcional metadata.use_case para atribuir o envio a um caso de uso.
  • Escopos necessarios: outbound:send e outbound:read.
Sua conta precisa estar habilitada para enviar: enquanto nao estiver, cada chamada responde 502. E os estados sent, delivered, read e failed ainda nao sao alcancaveis — eles estao no contrato para que a sua integracao ja os contemple.
Nova funcionalidade

Conversas, contatos, perfil da empresa e contagem de pedidos

Quatro novos grupos de endpoints somente leitura:
  • Conversas: Lista as conversas do comercio (GET /v1/conversations, com filtro sale_status) e as mensagens de uma conversa (GET /v1/conversations/{conversation_id}/messages). Ler mensagens pela API nunca as marca como lidas.
  • Contatos: Lista os contatos do comercio com suas tags, status de opt-in e metadata (GET /v1/contacts).
  • Empresa: Perfil publico da empresa (GET /v1/company).
  • Contagem de pedidos: Total de pedidos e detalhamento por status (GET /v1/orders/counts).
Novos scopes: conversations:read, contacts:read, company:read (a contagem usa orders:read).
MelhoriaCorrecao

Comprovantes de pagamento, resumo da conversa e correcao de midia

  • Comprovantes de pagamento: Novo campo payment_receipt_urls nas respostas de pedidos. Contem a lista de URLs das imagens de comprovantes de pagamento enviados pelo cliente (Yape, Plin, transferencia bancaria, etc.). Se houver varios comprovantes, todos sao incluidos.
  • Resumo da conversa: Novo campo summary_conversation nas respostas de pedidos. Contem um resumo gerado por IA com os detalhes do pedido e preferencias do cliente, evitando ter que entrar na conversa.
  • Correcao de midia de variante: Criar uma imagem de variante sem o campo title nao retorna mais erro. O campo e opcional conforme documentado.
Nova funcionalidade

Webhooks, fusos horarios e SKU

  • Webhooks: Receba eventos de pedidos em tempo real (order.created, order.status_changed). CRUD completo em /v1/webhooks. Assinatura HMAC-SHA256 em cada entrega.
  • Fusos horarios: Todas as datas (order_date, created_at) agora sao retornadas no fuso horario local do comerciante. Header X-Timezone incluido em cada resposta.
  • SKU: Novo campo sku em variantes de produtos e itens de pedidos.
  • Variant ID: Novo campo variant_id em itens de pedidos.
Nova funcionalidade

Gestao completa de produtos

  • Produtos: Criar, atualizar e excluir produtos (POST, PATCH, DELETE /v1/products)
  • Variantes: CRUD completo de variantes por produto
  • Opcoes e valores: Gestao de opcoes e seus valores por produto
  • Midia: Upload, atualizacao e exclusao de midia por variante
  • Categorias: Criar, atualizar, excluir categorias e atribui-las a produtos
  • Catalogos: Listagem de catalogos disponiveis (GET /v1/catalogs)
Lancamento

Lancamento da Business API

Primeira versao publica da YaVendio Business API.
  • Pedidos: Listagem e detalhe de pedidos (GET /v1/orders, GET /v1/orders/{order_id})
  • Produtos: Consulta de produtos (GET /v1/products, GET /v1/products/{product_id})
  • Autenticacao: API Keys com header X-API-Key
  • Rate Limiting: Controle de taxa de requisicoes por API Key
  • Paginacao: Paginacao baseada em cursor para todas as listagens