Tudo o que entra na Vozea pode sair para o seu sistema: uma API REST com chave, e webhooks assinados a cada depoimento recebido, aprovado ou publicado. Make, n8n e Zapier funcionam sem código.
Começar em dois minutos
Crie uma chave em Configurações › API (a chave aparece uma vez; guarde).
Chame a API com o cabeçalho Authorization: Bearer vz_…
Para receber eventos, cadastre a URL do seu sistema em Configurações › Webhooks e confira a assinatura.
Toda requisição leva a chave da empresa no cabeçalho Authorization, como Bearer. Cada chave pertence a uma empresa; revogar em Configurações vale na hora. Até 5 chaves ativas por conta.
Erros e limites
Erro é sempre JSON com código e mensagem, e details por campo quando é validação. Limite de 120 requisições por minuto por chave: acima disso, 429 com Retry-After.
unauthorized 401 · chave ausente, inválida ou revogada
not_found 404 · o id não existe nesta empresa
validation_error 422 · campo inválido (veja details)
tagsquery · string — Comma-separated; every tag must match.
sincequery · string — created_at ≥ since
untilquery · string — created_at ≤ until
limitquery · integer
cursorquery · string — The `next_cursor` of the previous page.
Respostas
200 A page of testimonials.
401 Missing, invalid or revoked API key.
422 Invalid query parameter (see details).
429 Rate limit exceeded (120 requests per minute per key). Retry-After is set.
dataobrigatórioarray de object
idobrigatóriostring
authorobrigatórioobject
namestring
metastring
photo_urlstring | null
textobrigatóriostring
ratingobrigatóriointeger | null
kindobrigatóriostring: text | video
statusobrigatóriostring: new | approved | archived
originobrigatórioobject
kindstring: collected | added | imported — collected = the collection link/QR/e-mail; added = typed in the app or created by the API; imported = brought from a platform (Google, iFood…), a spreadsheet or the browser extension.
channelobrigatóriostring — Where the customer said it: vozea, whatsapp, instagram, google, email, presencial, outro.
tagsobrigatórioarray de string
capturedobrigatórioobject — Provenance when captured by the browser extension.
urlstring | null
titlestring | null
atstring | null
authored_atstring | null
created_atobrigatóriostring
approved_atstring | null
urlsobrigatórioobject
appstring
public_pagestring
sourcestring — The review on its source platform (Google etc.) when imported; null when collected by Vozea.
next_cursorobrigatóriostring | null — Pass as ?cursor= to get the next page; null on the last page.
post/testimonials— Create a testimonial
A ready testimonial you already have (from your CRM, a form, a survey). It enters as origin `added`; emits `testimonial.received` (and `approved`/`published` when approve is true and the plan allows).
Corpo
author_nameobrigatóriostring
textobrigatóriostring
ratinginteger
channelstring: whatsapp | instagram | google | email | in_person | other
author_metastring — Who the author is ("customer since 2024").
tagsarray de string
authored_atstring
approveboolean — Publish right away. Ignored (stays new) when the plan's cap of live testimonials is reached.
429 Rate limit exceeded (120 requests per minute per key). Retry-After is set.
dataobrigatórioobject
idobrigatóriostring
authorobrigatórioobject
namestring
metastring
photo_urlstring | null
textobrigatóriostring
ratingobrigatóriointeger | null
kindobrigatóriostring: text | video
statusobrigatóriostring: new | approved | archived
originobrigatórioobject
kindstring: collected | added | imported — collected = the collection link/QR/e-mail; added = typed in the app or created by the API; imported = brought from a platform (Google, iFood…), a spreadsheet or the browser extension.
channelobrigatóriostring — Where the customer said it: vozea, whatsapp, instagram, google, email, presencial, outro.
tagsobrigatórioarray de string
capturedobrigatórioobject — Provenance when captured by the browser extension.
urlstring | null
titlestring | null
atstring | null
authored_atstring | null
created_atobrigatóriostring
approved_atstring | null
urlsobrigatórioobject
appstring
public_pagestring
sourcestring — The review on its source platform (Google etc.) when imported; null when collected by Vozea.
approvedobrigatórioboolean — false when approve was requested but the plan cap was reached.
get/testimonials/{id}— Get a testimonial
Parâmetros
idpath · string · obrigatório
Respostas
200 The testimonial.
401 Missing, invalid or revoked API key.
404 No such testimonial in this company.
429 Rate limit exceeded (120 requests per minute per key). Retry-After is set.
dataobject
idobrigatóriostring
authorobrigatórioobject
namestring
metastring
photo_urlstring | null
textobrigatóriostring
ratingobrigatóriointeger | null
kindobrigatóriostring: text | video
statusobrigatóriostring: new | approved | archived
originobrigatórioobject
kindstring: collected | added | imported — collected = the collection link/QR/e-mail; added = typed in the app or created by the API; imported = brought from a platform (Google, iFood…), a spreadsheet or the browser extension.
channelobrigatóriostring — Where the customer said it: vozea, whatsapp, instagram, google, email, presencial, outro.
tagsobrigatórioarray de string
capturedobrigatórioobject — Provenance when captured by the browser extension.
urlstring | null
titlestring | null
atstring | null
authored_atstring | null
created_atobrigatóriostring
approved_atstring | null
urlsobrigatórioobject
appstring
public_pagestring
sourcestring — The review on its source platform (Google etc.) when imported; null when collected by Vozea.
post/requests— Request a testimonial from a customer
Same rules as a sale or the spreadsheet: channel by plan (e-mail goes out automatically on paid plans; WhatsApp appears in the day's list), monthly quota, one open request per contact.
Corpo
nameobrigatóriostring
emailstring
phonestring — Brazilian number with area code; WhatsApp.
productstring
purchased_atstring
channelstring: auto | email | whatsapp — auto = e-mail when the plan sends e-mail automatically, else WhatsApp.
422 Invalid field, or `details.reason` = no_channel | duplicate | over_quota | too_old (purchased_at older than 30 days: the API only asks for recent sales — past customers go through "Enviar para uma lista").
429 Rate limit exceeded (120 requests per minute per key). Retry-After is set.
dataobrigatórioobject
idobrigatóriostring
channelobrigatóriostring: email | whatsapp
sends_atobrigatóriostring — When the request goes out (e-mail) or appears in the day's WhatsApp list. Already moved into the account's business hours (default Mon–Fri, 08:00–18:00 America/Sao_Paulo); outside them it waits for the next opening.
get/requests/{id}— Get a request
The state of a request: use it to confirm an integration works without opening Vozea (right after the 201, `state` is `scheduled`).
Parâmetros
idpath · string · obrigatório — The request id (iv_…) returned when it was created.
Respostas
200 The request.
401 Missing or invalid key.
404 No such request in this company.
dataobrigatórioobject
idobrigatóriostring
stateobrigatóriostring: scheduled | sent | answered | canceled — scheduled = waiting for its send date; sent = the customer was asked (reminders may follow); answered = the customer sent a testimonial; canceled = stopped (by you, by the API or by the schedule).
channelobrigatóriostring: email | whatsapp
productstring
sends_atobrigatóriostring
sent_atstring
answeredobrigatórioboolean
testimonial_idstring — The testimonial the customer sent, once answered.
created_atobrigatóriostring
delete/requests/{id}— Cancel a pending request
Cancels a request that has not been answered yet (scheduled or sent): the customer is not asked and gets no reminders. Call it on refund, chargeback or order cancellation — never ask for a testimonial from someone who got their money back. `id` is the one returned by POST /requests; keep it with the sale.
Parâmetros
idpath · string · obrigatório — The request id (iv_…) returned when it was created.
Respostas
200 Canceled — or already canceled (`already_canceled` = true): calling again is safe.
401 Missing, invalid or revoked API key.
404 No such request in this company.
409 Not cancelable: `details.reason` = answered (the customer already sent a testimonial).
429 Rate limit exceeded (120 requests per minute per key). Retry-After is set.
dataobrigatórioobject
idobrigatóriostring
canceledobrigatórioboolean
already_canceledobrigatórioboolean — true when it was already canceled before this call: calling again is safe and changes nothing.
get/pages— List public pages
Respostas
200 Pages.
401 Missing, invalid or revoked API key.
429 Rate limit exceeded (120 requests per minute per key). Retry-After is set.
dataarray de object
idobrigatóriostring
nameobrigatóriostring
slugobrigatóriostring
urlobrigatóriostring
publishedobrigatórioboolean
get/widgets— List widgets
Respostas
200 Widgets with the embed tag.
401 Missing, invalid or revoked API key.
429 Rate limit exceeded (120 requests per minute per key). Retry-After is set.
dataarray de object
idobrigatóriostring
nameobrigatóriostring
designobrigatóriostring
embedobrigatóriostring — The <script> tag to paste into a site.
Eventos
Três eventos, com o mesmo objeto de depoimento da API. O payload tem version desde o primeiro dia: mudar um campo é subir a versão, nunca renomear em silêncio. Importações em lote (sync do Google, planilha) só chegam ao seu webhook se o destino marcar “incluir importados”.
testimonial.received — Depoimento recebido: Entrou na conta, antes de aprovação. Sent when a testimonial enters the account, before approval (link, e-mail, extension, spreadsheet, platform sync, API). Deliveries are retried with backoff (6 attempts); reply 2xx within 10 s.
testimonial.approved — Depoimento aprovado: Foi aprovado no Tratar (ou sozinho, pela aprovação automática). Sent when a testimonial is approved (in Tratar, in bulk, by auto-approval, or by the API). Deliveries are retried with backoff (6 attempts); reply 2xx within 10 s.
testimonial.published — Depoimento publicado: Entrou numa página pública ou num widget. Sent when an approved testimonial becomes part of a published public page or a widget (one event per target). Deliveries are retried with backoff (6 attempts); reply 2xx within 10 s.
statusobrigatóriostring: new | approved | archived
originobrigatórioobject
kindstring: collected | added | imported — collected = the collection link/QR/e-mail; added = typed in the app or created by the API; imported = brought from a platform (Google, iFood…), a spreadsheet or the browser extension.
Cada entrega é um POST com o evento em JSON e quatro cabeçalhos. Confira a assinatura com o segredo do destino (HMAC-SHA256 sobre timestamp.corpo) e recuse timestamps fora de 5 minutos — é o que barra replay. Responda 2xx em até 10 segundos; senão a Vozea tenta de novo em 1 min, 5 min, 30 min, 2 h e 12 h, e desliga o destino depois de dez falhas seguidas (você é avisado). Uma importação grande não vira rajada: no máximo 60 entregas por minuto por destino.
Cabeçalhos
X-Vozea-Signature — `v1=<hex HMAC-SHA256(secret, "<timestamp>.<body>")>`. Verify with the endpoint's secret and compare in constant time.
X-Vozea-Timestamp — Unix time in seconds when the delivery was signed. Reject if it differs from your clock by more than 300 s (replay protection).
X-Vozea-Event — The event type.
X-Vozea-Delivery — The delivery id (unique per attempt; a retry keeps the id).
Verificar em Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyVozea({ secret, header, timestamp, body }) {
const m = /^v1=([a-f0-9]{64})$/.exec(header ?? "");
if (!m) return false;
const ts = Number(timestamp);
if (!Number.isInteger(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false;
const expected = createHmac("sha256", secret).update(`${ts}.${body}`).digest("hex");
const a = Buffer.from(expected, "hex"), b = Buffer.from(m[1], "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
// Express: use o corpo cru (express.raw), nunca o JSON já parseado.
app.post("/vozea", express.raw({ type: "application/json" }), (req, res) => {
const ok = verifyVozea({ secret: process.env.VOZEA_WEBHOOK_SECRET, header: req.get("x-vozea-signature"), timestamp: req.get("x-vozea-timestamp"), body: req.body.toString() });
if (!ok) return res.status(401).end();
const event = JSON.parse(req.body);
res.status(200).end();
});
Make, n8n e Zapier
n8n
No n8n, um nó Webhook (POST) recebe os eventos: cole a URL dele em Configurações › Webhooks.
Para ler ou criar, um nó HTTP Request com Header Auth: Authorization = Bearer vz_…
Importe o openapi.json no nó HTTP Request para ter os endpoints prontos.
Make
No Make, um módulo Webhooks › Custom webhook recebe os eventos.
Para a API, HTTP › Make a request com o cabeçalho Authorization.
O corpo dos eventos já vem em JSON: mapeie os campos direto.
Zapier
O app da Vozea no diretório do Zapier vem depois; até lá, Webhooks by Zapier funciona com a mesma URL e a mesma API.
OpenAPI
O spec completo, em OpenAPI 3.1, para importar em qualquer ferramenta: /api/v1/openapi.json