vamobotDocs

Envio em massa

Importe até 10.000 contatos e envie até 1.000 mensagens numa requisição, acompanhando o resultado item por item.

Os endpoints em massa respondem na hora com 202 e fazem o trabalho em segundo plano, em lotes de 100. Um item com problema nunca interrompe os outros: cada um tem o seu resultado.

EndpointMáximo por requisiçãoComo acompanhar
POST /bulk/contacts10.000 contatosAviso na lista de Contatos do Vamo e webhook de resumo
POST /bulk/messages1.000 mensagensGET /bulk/jobs/:id e webhook de resumo

POST /bulk/contacts

Importa contatos para a loja. Acesso: chave secreta com Leitura e escrita.

CampoTipoObrigatórioDescrição
storeIdstring (UUID)SimA sua loja
contactsarraySimDe 1 a 10.000 contatos

Cada contato:

CampoObrigatórioDescrição
nameSimNome
phoneSimTelefone (veja as regras abaixo)
statusNãoactive, lead ou unresolved. Padrão: lead
tagsNãoLista de etiquetas
email, document, cityNãoE-mail, CPF/CNPJ (ou documento) e cidade
propertiesNãoValores dos seus campos personalizados, pela chave do campo
waIdNãoPadrão: o telefone sem o +
curl -X POST https://vamo.ai/api/v1/bulk/contacts \
  -H "Authorization: Bearer sk_live_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "storeId": "7b1e2c3d-4a5f-4b6c-8d7e-9f0a1b2c3d4e",
    "contacts": [
      { "name": "Mariana Costa", "phone": "+55 11 98765-4321", "tags": ["black-friday"] },
      { "name": "Rafael Souza", "phone": "(21) 99887-7665", "city": "Rio de Janeiro" },
      { "name": "Ana Lima", "phone": "11 9123", "email": "ana@email.com" }
    ]
  }'

Resposta 202

{
  "data": {
    "taskId": "run_cm3x9k2a0001",
    "importJobId": "e7f8a9b0-c1d2-4e3f-8a4b-5c6d7e8f9a0b",
    "status": "processing",
    "totalContacts": 3
  }
}

Regras da importação

  • Telefone. Com + (ou 00) e código do país, o número é mantido. Sem código do país, ele vira um número do país em que a sua organização vende: em reais, (21) 99887-7665 vira +5521998877665; em guaranis, 0981 123456 vira +595981123456. Uma organização em dólar precisa enviar o código do país. Um número que não dá para entender é recusado (no exemplo, o de Ana).
  • Repetidos são pulados. Um número que a loja já tem, ou que aparece duas vezes na lista, não cria outro contato e conta como pulado. A importação não atualiza contatos existentes.
  • Campo opcional inválido (e-mail ou documento inválido, campo personalizado que não existe) é deixado de fora; o contato é importado com os outros dados.

Acompanhar

O andamento e o resultado aparecem no aviso no topo da lista de Contatos, no Vamo. Não há rota da API para consultar uma importação de contatos.

Ao terminar, o Vamo dispara um webhook contact.created de resumo (não um por contato):

{
  "event": "contact.created",
  "data": { "bulk": true, "created": 2, "skipped": 0, "failed": 1 },
  "timestamp": "2026-10-31T15:00:12.000Z"
}

POST /bulk/messages

Envia mensagens livres para vários contatos. Acesso: chave secreta com Leitura e escrita.

CampoTipoObrigatórioDescrição
messagesarraySimDe 1 a 1.000 mensagens
storeIdstring (UUID)NãoA loja em que o envio fica registrado (404 se não for sua)

Cada mensagem tem os mesmos campos de POST /messages/send: contactId e content (obrigatórios), mediaUrl e mediaType.

curl -X POST https://vamo.ai/api/v1/bulk/messages \
  -H "Authorization: Bearer sk_live_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      { "contactId": "2c9d8e7f-6a5b-4c3d-9e2f-1a0b9c8d7e6f", "content": "Mariana, sua capinha chegou na loja!" },
      { "contactId": "3d0e9f8a-7b6c-4d5e-8f1a-2b3c4d5e6f70", "content": "Rafael, seu fone foi enviado hoje." }
    ]
  }'

Resposta 202

{
  "data": {
    "jobId": "c5d6e7f8-a9b0-4c1d-9e2f-d6e7f8a9b0c1",
    "taskId": "run_cm3x9k7b0002",
    "status": "processing",
    "totalMessages": 2
  }
}

Só para quem está na janela de 24 horas

Cada mensagem segue as regras de POST /messages/send: ela só sai para um contato que escreveu nas últimas 24 horas. As outras voltam como erro whatsapp_window_closed. A API não envia modelos em massa: para avisar muitos clientes fora da janela, use as Campanhas do Vamo.

Como o envio roda

  • As mensagens saem na ordem da lista, 100 por vez.
  • Se o WhatsApp pedir para esperar, o Vamo espera o tempo indicado (60 segundos, se a Meta não disser) e tenta aquela mensagem mais uma vez.
  • Um lote que falha não é repetido, para nenhum cliente receber a mesma mensagem duas vezes. O job fica failed e processed mostra até onde chegou: as mensagens depois desse ponto não saíram.
  • Cada mensagem enviada aparece na conversa do contato, com origem api.

GET /bulk/jobs/:id

O andamento de um envio de mensagens. Acesso: chave secreta (leitura). O job de outra organização responde 404, como um que não existe.

curl https://vamo.ai/api/v1/bulk/jobs/c5d6e7f8-a9b0-4c1d-9e2f-d6e7f8a9b0c1 \
  -H "Authorization: Bearer sk_live_SUA_CHAVE"

Resposta 200

{
  "data": {
    "id": "c5d6e7f8-a9b0-4c1d-9e2f-d6e7f8a9b0c1",
    "status": "running",
    "total": 250,
    "processed": 100,
    "sentCount": 99,
    "failedCount": 1,
    "errors": [
      { "index": 7, "contactId": "3d0e9f8a-7b6c-4d5e-8f1a-2b3c4d5e6f70", "code": "whatsapp_window_closed", "message": "The 24-hour customer service window is closed. Send an approved template instead." }
    ],
    "createdAt": "2026-10-31T15:02:11.000Z",
    "finishedAt": null
  }
}
CampoDescrição
statusrunning, depois completed; failed se o job parou antes do fim
processedQuantas mensagens já foram tratadas (enviadas ou com erro)
sentCount, failedCountEnviadas e não enviadas
errorsUma entrada por mensagem não enviada: a posição dela na lista (index, começando em 0), o contato, o code e o motivo. messageId aparece quando a tentativa ficou gravada na conversa
finishedAtQuando terminou; null enquanto roda

Os códigos são os de Erros, mais not_found (o contato não existe na sua organização) e internal_error.

Webhook de resumo

Ao terminar, o Vamo dispara um message.created com o resultado de cada item (as mensagens do envio em massa não geram um message.created cada):

{
  "event": "message.created",
  "data": {
    "bulk": true,
    "jobId": "c5d6e7f8-a9b0-4c1d-9e2f-d6e7f8a9b0c1",
    "sent": 1,
    "failed": 1,
    "results": [
      { "index": 0, "messageId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", "conversationId": "5e4d3c2b-1a09-4f8e-8d7c-6b5a49382716" }
    ],
    "errors": [
      { "index": 1, "contactId": "3d0e9f8a-7b6c-4d5e-8f1a-2b3c4d5e6f70", "code": "whatsapp_window_closed", "error": "The 24-hour customer service window is closed. Send an approved template instead." }
    ]
  },
  "timestamp": "2026-10-31T15:04:40.000Z"
}

Repare que no webhook o motivo vem em error, e em GET /bulk/jobs/:id em message.

Erros

Os erros vêm no formato de sempre: { "error": { "code": "…", "message": "…" } }.

StatuscodeQuando
400validation_errorFalta storeId (contatos) ou a lista; lista vazia (contacts array must not be empty); lista acima do limite (Maximum 10,000 contacts per bulk import / Maximum 1,000 messages per bulk send); um item inválido (message diz qual, por exemplo messages.3.contactId: Invalid uuid). Nada é enfileirado
404not_foundStore not found / Job not found: a loja ou o job não é da sua organização
429rate_limitedLimite de requisições da chave (veja Limites de requisição)

As duas rotas aceitam Idempotency-Key: repetir um envio em massa com a mesma chave devolve o mesmo jobId, sem enfileirar outro (veja Idempotência).

Nesta página