Autenticação
A API utiliza chaves de acesso (API Keys) que você mesmo gera no painel, em API → API Keys. Envie sua chave no header de todas as requisições autenticadas.
● Como obter sua API Key
- Crie uma conta em zavvi.app e confirme seu e-mail (chaves de contas sem e-mail confirmado recebem 403)
- No painel, acesse API → API Keys em
https://www.zavvi.com.br/app/api/keys— o painel de API é liberado para assinantes do plano Escala - Clique em criar chave, informe um nome (obrigatório) e, se quiser, uma data de expiração (opcional, precisa ser posterior a hoje)
- A chave completa aparece uma única vez logo após a criação — copie e guarde. Na listagem só ficam visíveis os primeiros caracteres
- Utilize a chave no header
X-API-Keyde todas as requisições
● Exemplo de requisicao
curl -X GET https://www.zavvi.com.br/api/v1/surveys \ -H "X-API-Key: sua_chave_aqui" \ -H "Accept: application/json"
● Erros de autenticacao
{
"message": "API key required. Use header X-API-Key."
}
{
"message": "Invalid or expired API key."
}
Headers obrigatórios
| Header | Valor | Descrição | |
|---|---|---|---|
| X-API-Key | string | obrigatório | Sua chave de API (48 caracteres) |
| Accept | string | recomendado | application/json |
| Content-Type | string | POST/PUT | application/json |
Pesquisas
Gerencie pesquisas de satisfação. Todas as rotas requerem X-API-Key. Pesquisas são identificadas por UUID.
Retorna as pesquisas do usuário autenticado, das mais recentes para as mais antigas. O resultado é paginado: use per_page (padrão 15, máximo 100) e page na query string. A resposta traz também os blocos links e meta da paginação.
Resposta
{
"data": [
{
"id": "a1b2c3d4-e5f6-...",
"title": "Satisfacao no Atendimento",
"status": "published",
"url": "https://www.zavvi.com.br/s/a1b2c3d4-e5f6-...",
"questions_count": 8,
"responses_count": 342,
"created_at": "2026-01-15T10:30:00Z"
}
]
}
Retorna os detalhes de uma pesquisa específica. A lista de questões não vem neste endpoint — o objeto traz apenas os contadores questions_count e responses_count, além de identificação, textos e cores. Para obter as questões, use GET /surveys/{uuid}/questions.
Resposta
{
"data": {
"id": "a1b2c3d4-e5f6-...",
"title": "Satisfacao no Atendimento",
"status": "published",
"url": "https://www.zavvi.com.br/s/a1b2c3d4-e5f6-...",
"welcome_message": "Ola! Leva 1 minuto.",
"goodbye_text": "Obrigado pela participacao!",
"redirect_url": null,
"visibility": true,
"is_template": false,
"notify_new_responses": true,
"color": "#7c3aed",
"question_color": "#ffffff",
"answer_color": "#ffffff",
"button_color": "#7c3aed",
"button_text_color": "#ffffff",
"background_color": "#0b0b12",
"logo": null,
"questions_count": 8,
"responses_count": 342,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-20T09:00:00Z"
}
}
Cria uma nova pesquisa. Requer assinatura ativa e limite de pesquisas não atingido. Envie sempre welcome_message e goodbye_text junto com o título: as duas colunas não aceitam valor nulo no banco e a criação só com title falha.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| title | string | obrigatório | Título da pesquisa (max 255) |
| welcome_message | string | envie sempre | Mensagem de boas-vindas — a validação aceita omitir, mas o banco não tem valor padrão para esta coluna |
| goodbye_text | string | envie sempre | Mensagem de agradecimento — mesma observação de welcome_message |
| visibility | boolean | opcional | Pesquisa publicada (default: true — sem informar nada ela já nasce publicada) |
| redirect_url | string | opcional | URL válida para redirecionar ao final |
| notify_new_responses | boolean | opcional | Notificar por e-mail a cada nova resposta |
| color, question_color, answer_color, button_color, button_text_color, background_color | string | opcional | Cores da pesquisa (max 50 caracteres cada) |
Atualiza uma pesquisa existente. Apenas o dono pode atualizar.
Remove permanentemente uma pesquisa e todas as suas questões e respostas.
Cria uma copia da pesquisa com todas as questões. A copia inicia como rascunho com zero respostas.
Questoes
Gerencie questões dentro de uma pesquisa. Tipos aceitos pela API: multiple-choices, yes-no, dropdown, short-text, long-text, number, email, phone, date, rating, slider
nps, csat e ces não são tipos válidos na API — enviá-los retorna 422. Para uma pergunta de recomendação de 0 a 10, use o tipo rating: é sobre ele que o endpoint de analytics calcula o NPS.
Retorna todas as questões da pesquisa ordenadas por posição (sem paginação). O campo choices só é preenchido para os tipos multiple-choices, yes-no, dropdown e slider; nos demais tipos ele vem null.
Resposta
{
"data": [
{
"id": 1,
"survey_id": 7,
"type": "rating",
"text": "De 0 a 10, quanto voce recomendaria?",
"position": 1,
"visibility": true,
"is_required": true,
"attributes": { "scale": 10 },
"choices": null,
"image": null
}
]
}
Adiciona uma questão à pesquisa. Requer limite de questões não atingido.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| text | string | obrigatório | Texto da questão (max 1000) |
| type | string | obrigatório | Tipo (ver lista acima). Valor fora da lista retorna 422 |
| position | integer | opcional | Ordem de exibicao (default: última posição) |
| visibility | boolean | opcional | Questão visível na pesquisa (default: true) |
| is_required | boolean | opcional | Resposta obrigatoria (default: true) |
| attributes | object | opcional | Configuracoes especificas do tipo (choices, scale, etc.) |
| image | string | opcional | Caminho da imagem da questão |
Atualiza uma questão existente. A rota é rasa: use o ID numérico da questão direto em /api/v1/questions/{id}, sem o UUID da pesquisa. Os campos aceitos são os mesmos da criação, todos opcionais (text e type não podem ser enviados vazios).
Remove uma questão. Assim como no update, a rota é rasa e usa apenas o ID numérico da questão. Retorna 204 sem corpo.
Respostas
Colete e consulte respostas de pesquisas. A submissao e publica (nao requer autenticacao).
Retorna as respostas de uma pesquisa uma linha por questão respondida — não vêm agrupadas por respondente. Para reconstruir o questionário de cada pessoa, agrupe as linhas pelo campo attendee_id, que identifica o respondente. Cada linha já traz a questão correspondente embutida em question.
O resultado é paginado e ordenado da resposta mais recente para a mais antiga: use per_page (padrão 50) e page na query string.
Resposta
{
"data": [
{
"id": 981,
"survey_id": 7,
"question_id": 1,
"attendee_id": 4512,
"response": "9",
"question": {
"id": 1,
"type": "rating",
"text": "De 0 a 10, quanto voce recomendaria?"
},
"created_at": "2026-03-18T14:30:00Z",
"updated_at": "2026-03-18T14:30:00Z"
}
],
"links": { "..." },
"meta": { "..." }
}
Submete respostas para uma pesquisa. Rate limit: 10 requisições/minuto por IP. A pesquisa precisa estar publicada — caso contrário a API responde 403 com "This survey is not available.". Cada chamada cria um novo respondente com todas as respostas enviadas no corpo.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| responses | array | obrigatório | Lista de respostas (pelo menos 1 item). Não é um objeto/mapa |
| responses[].question_id | integer | obrigatório | ID numérico da questão (o mesmo id devolvido em GET /surveys/{uuid}/questions) |
| responses[].response | string | obrigatório | Valor respondido, sempre como string — inclusive notas numéricas ("9") |
Exemplo
{
"responses": [
{ "question_id": 1, "response": "9" },
{ "question_id": 2, "response": "Atendimento excelente!" },
{ "question_id": 3, "response": "Sim" }
]
}
Resposta
{
"data": [
{
"id": 981,
"survey_id": 7,
"question_id": 1,
"attendee_id": 4512,
"response": "9",
"created_at": "2026-03-18T14:30:00Z",
"updated_at": "2026-03-18T14:30:00Z"
}
]
}
responses como objeto ({"1": "9"}) retorna 422. IDs de questão que existem mas pertencem a outra pesquisa são ignorados em silêncio: não geram erro e não aparecem na resposta. IDs inexistentes reprovam a validação com 422.
Analytics & Planos
Dados analíticos das pesquisas e planos disponíveis.
Retorna os totais da pesquisa, a taxa de conclusão e um detalhamento por questão. A resposta não é embrulhada em data — os campos vêm na raiz do JSON. Não existe um bloco scores com nps/csat/ces: o único indicador calculado é o NPS por questão, e apenas para questões do tipo rating.
Campos da raiz
| Campo | Tipo | Descrição | |
|---|---|---|---|
| survey_id | string | UUID da pesquisa | |
| title | string | Título da pesquisa | |
| total_responses | integer | Total de respostas registradas na pesquisa | |
| total_attendees | integer | Total de respondentes | |
| total_questions | integer | Total de questões | |
| completion_rate | float | % de respondentes que responderam todas as questões visíveis. Vem null quando não há respondentes ou questões visíveis | |
| questions | array | Detalhamento por questão (ver abaixo) |
Campos de cada item de questions
| Campo | Tipo | Descrição | |
|---|---|---|---|
| question_id | integer | sempre | ID da questão |
| text | string | sempre | Texto da questão |
| type | string | sempre | Tipo da questão |
| response_count | integer | sempre | Quantas respostas a questão recebeu |
| average_rating | float | só rating | Média das notas numéricas maiores que zero |
| nps | integer | só rating | NPS da questão, arredondado: % de notas ≥ 9 menos % de notas ≤ 6, considerando escala de 1 a 10. Só aparece quando há pelo menos uma nota numérica |
| distribution | object | só escolha | Contagem por alternativa, da mais escolhida para a menos. Presente em multiple-choices, yes-no e dropdown |
Resposta
{
"survey_id": "a1b2c3d4-e5f6-...",
"title": "Satisfacao no Atendimento",
"total_responses": 342,
"total_attendees": 405,
"total_questions": 8,
"completion_rate": 84.5,
"questions": [
{
"question_id": 1,
"text": "De 0 a 10, quanto voce recomendaria?",
"type": "rating",
"response_count": 120,
"average_rating": 8.74,
"nps": 72
},
{
"question_id": 2,
"text": "Voce voltaria a comprar?",
"type": "yes-no",
"response_count": 118,
"distribution": { "Sim": 101, "Nao": 17 }
}
]
}
Retorna todos os planos disponíveis com preços e limites.
Resposta
{
"data": [
{
"id": 1,
"title": "Gratuito",
"price": 0,
"interval": "monthly",
"is_free": true,
"limits": {
"survey_count": 3,
"question_count_per_survey": 10,
"response_count_per_survey": 100
}
},
{
"id": 2,
"title": "Starter",
"price": 49.00,
"is_free": false,
"limits": { "..." }
}
]
}
Limites e Erros
Informações sobre rate limiting e códigos de erro.
Rate Limits
| Com API Key | 60 req/min por conta |
| Submissão pública | 10 req/min por IP |
| Sem API Key | 60 req/min por IP |
Códigos de Erro
Plano Escala
Endpoints exclusivos para assinantes do plano Escala (enterprise). Requerem X-API-Key de um usuário com plano Escala ativo.
Tags dinamicas permitem personalizar o texto da pesquisa para cada destinatario. Use placeholders no formato {{NOME_DA_TAG}} nos campos da pesquisa, e envie os valores correspondentes no campo context da API.
Como Funciona
No painel, crie uma Pesquisa API e use tags nos campos de boas-vindas e perguntas:
Pedido: {{NRO_PEDIDO}} 📝
No campo context, passe os valores das tags:
{
"channel": "email",
"recipient": {
"name": "Joao Silva",
"email": "joao@email.com"
},
"context": {
"NOME_CLIENTE": "Joao Silva",
"NRO_PEDIDO": "PED-2026-00142"
},
"subject": "{{NOME_CLIENTE}}, como foi seu pedido {{NRO_PEDIDO}}?"
}
As tags sao substituidas em todos os lugares:
Pedido: PED-2026-00142 📝
Onde as tags funcionam
| Campo | Descricao |
|---|---|
| welcome_message | Mensagem de boas-vindas exibida ao abrir a pesquisa |
| Texto das perguntas | O texto de cada pergunta — somente no modo de exibição conversacional. No modo clássico o texto da pergunta é exibido cru, sem substituição de tags |
| subject | Assunto do e-mail de convite (passado no envio via API) |
Regras
- Tags usam o formato
{{NOME_TAG}}— apenas letras, numeros e underscore entre as chaves (sem espacos, acentos ou hifens) - As tags sao livres: voce pode usar qualquer nome que quiser
- Se uma tag nao tiver valor no
context, ela permanece no texto exatamente como foi escrita — o destinatario ve{{NOME_TAG}}. Envie sempre todas as tags usadas na pesquisa, mesmo que com string vazia - A unica situacao em que as tags somem do texto e quando o acesso nao tem nenhum
contextassociado e a pesquisa e do tipo Pesquisa API: ai todas as tags sao apagadas de uma vez. Em pesquisas comuns sem context, elas continuam visiveis - Os valores do
contextprecisam ser strings de ate 500 caracteres — numeros e objetos aninhados sao recusados com 422 - As tags sao case-sensitive:
NOMEenomesao tags diferentes - Cada destinatario recebe um link unico com seus dados personalizados
Tags no Envio em Massa
No endpoint de envio em massa (POST /send/bulk), cada destinatario pode ter seu proprio context:
{
"channel": "email",
"subject": "{{NOME_CLIENTE}}, avalie seu pedido",
"recipients": [
{
"name": "Joao Silva",
"email": "joao@email.com",
"context": {
"NOME_CLIENTE": "Joao Silva",
"NRO_PEDIDO": "PED-001"
}
},
{
"name": "Maria Santos",
"email": "maria@email.com",
"context": {
"NOME_CLIENTE": "Maria Santos",
"NRO_PEDIDO": "PED-002"
}
}
]
}
Painel de Acompanhamento
Todos os envios via API sao rastreados e podem ser acompanhados no painel API do dashboard, incluindo:
- Status de cada envio (pendente, enviado, entregue, falha)
- Dados do
contextenviado para cada destinatario - Taxa de conclusao (quem abriu e respondeu a pesquisa)
- Possibilidade de reenvio para envios que falharam
Envia uma pesquisa para um destinatário por e-mail ou WhatsApp. Use o campo context para passar valores das tags dinamicas configuradas na pesquisa. O envio é síncrono.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| channel | string | obrigatório | email ou whatsapp |
| recipient.name | string | obrigatório | Nome do destinatário |
| recipient.email | string | se email | E-mail do destinatário |
| recipient.phone | string | se whatsapp | Telefone internacional (ex: +5511999999999) |
| context | object | opcional | Dados contextuais livres (chave-valor) para personalizar a mensagem. Cada valor precisa ser string de até 500 caracteres |
| subject | string | opcional | Assunto do e-mail (ignorado para WhatsApp) |
Exemplo
{
"channel": "email",
"recipient": {
"name": "Carlos Eduardo",
"email": "carlos@email.com"
},
"context": {
"NOME_CLIENTE": "Carlos Eduardo",
"NRO_PEDIDO": "PED-2026-00142"
},
"subject": "{{NOME_CLIENTE}}, como foi seu pedido {{NRO_PEDIDO}}?"
}
Resposta
{
"data": {
"id": 123,
"channel": "email",
"recipient": "carlos@email.com",
"status": "sent",
"sent_at": "2026-03-18T14:30:00Z",
"attendee_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}
Status
Envia pesquisas para múltiplos destinatários de uma vez. Os envios são enfileirados e processados em background. Máximo de 500 destinatários por requisição. Um canal por request.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| channel | string | obrigatório | email ou whatsapp |
| subject | string | opcional | Assunto do e-mail (aplicado a todos) |
| recipients | array | obrigatório | Lista de destinatários (max 500) |
| recipients[].name | string | obrigatório | Nome do destinatário |
| recipients[].email | string | se email | E-mail do destinatário |
| recipients[].phone | string | se whatsapp | Telefone internacional |
| recipients[].context | object | opcional | Dados contextuais por destinatário. Cada valor precisa ser string de até 500 caracteres |
Exemplo
{
"channel": "email",
"subject": "Como foi sua experiência?",
"recipients": [
{
"name": "Carlos Eduardo",
"email": "carlos@email.com",
"context": { "NOME_CLIENTE": "Carlos Eduardo", "NRO_PEDIDO": "PED-001" }
},
{
"name": "Maria Silva",
"email": "maria@email.com",
"context": { "NOME_CLIENTE": "Maria Silva", "NRO_PEDIDO": "PED-002" }
}
]
}
Resposta
{
"data": {
"batch_id": "batch_abc123xyz456",
"channel": "email",
"total": 2,
"status": "queued",
"message": "2 envios foram enfileirados para processamento."
}
}
Status
Retorna o QR Code da pesquisa em formato PNG (base64) ou SVG.
Query Params
| Param | Tipo | Descrição | |
|---|---|---|---|
| format | string | opcional | png (default) ou svg |
| size | integer | opcional | 100-1000 pixels (default: 300) |
Exemplo
curl -X GET https://www.zavvi.com.br/api/v1/surveys/{uuid}/qrcode?format=png&size=400 \ -H "X-API-Key: sua_chave_aqui" \ -H "Accept: application/json"
Resposta
{
"data": {
"survey_id": "a1b2c3d4-e5f6-...",
"survey_url": "https://www.zavvi.com.br/s/a1b2c3d4-e5f6-...",
"format": "png",
"size": 400,
"qrcode": "data:image/png;base64,iVBORw0KGgo..."
}
}