Conectar formulários e webhooks
URL exclusiva, Bearer ou HMAC no servidor, e formulários públicos protegidos por Turnstile.
Cada automação tem uma URL opaca:
POST https://vensy.com.br/api/v1/webhooks/automations/ID_DO_WEBHOOKEnvie um objeto JSON (application/json) ou campos simples (application/x-www-form-urlencoded), até 64 KiB. Arquivos devem ser representados por URLs, não enviados em multipart ou base64. Dados têm até 12 níveis, arrays de até 200 elementos e strings de até 16.384 caracteres.
O workspace é resolvido pelo webhook autenticado. Não envie tenantId, IDs de vendedor, canal ou pipeline para mudar o destino: esses vínculos são escolhidos pelo administrador no editor. O payload é dado, nunca autorização.
Servidor para servidor: Bearer
No editor, selecione Segredo Bearer. O segredo aparece apenas ao criar/rotacionar; guarde-o no backend do seu site ou cofre da integração. Nunca em HTML, JavaScript público, URL, analytics ou logs.
curl --request POST "$VENSY_AUTOMATION_URL" \
--header "Authorization: Bearer $VENSY_AUTOMATION_SECRET" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: formulario-envio-000042' \
--data '{
"nome": "Maria Exemplo",
"telefone": "+5511999999999",
"email": "maria@example.com",
"interesse": "Solicitar orçamento",
"whatsappOptIn": true,
"consentimentoRegistradoEm": "2026-08-31T15:00:00Z",
"origem": "Site"
}'O exemplo é fictício. whatsappOptIn só deve ser verdadeiro quando o seu formulário registrar uma escolha afirmativa do visitante. No bloco WhatsApp, escolha Tenho o opt-in registrado em um campo → Formulário / gatilho · whatsappOptIn. Não digite true como valor fixo.
Assinatura HMAC
Selecione HMAC para assinar o conteúdo exato no seu servidor:
import { createHmac } from "node:crypto";
const body = JSON.stringify(eventoDoFormulario);
const timestamp = String(Math.floor(Date.now() / 1000));
const signature = createHmac("sha256", process.env.VENSY_AUTOMATION_SECRET!)
.update(`${timestamp}.${body}`)
.digest("hex");
const response = await fetch(process.env.VENSY_AUTOMATION_URL!, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Idempotency-Key": idPersistenteDoEnvio,
"X-Vensy-Timestamp": timestamp,
"X-Vensy-Signature": `sha256=${signature}`,
},
body,
signal: AbortSignal.timeout(15_000),
});O timestamp usa segundos Unix e tolerância de 5 minutos. Assine os mesmos bytes enviados; reformatar o JSON depois de assinar invalida a assinatura. Em um retry, gere timestamp/assinatura novos e mantenha a mesma chave de evento e conteúdo. O servidor deve estar com o relógio sincronizado.
Formulário direto do navegador
Quando o site não tem backend, selecione Formulário público protegido e cadastre origens exatas, por exemplo https://www.sualoja.com.br. Não há wildcard; esquema e subdomínio importam.
A Vensy precisa ter o Turnstile configurado e autorizado para o hostname do site. O editor informa a site key pública. Renderize o widget com data-action="vensy_lead" e envie o token com o formulário. A chave secreta do Turnstile permanece apenas na Vensy.
<form id="lead-form">
<input name="nome" autocomplete="name" required aria-label="Nome" />
<input name="telefone" autocomplete="tel" required aria-label="WhatsApp" />
<label>
<input type="checkbox" name="whatsappOptIn" value="true" />
Aceito receber contato desta empresa pelo WhatsApp sobre minha solicitação.
</label>
<div class="cf-turnstile" data-sitekey="SITE_KEY_PUBLICA"
data-action="vensy_lead"></div>
<button type="submit">Enviar solicitação</button>
<p role="status" id="lead-status"></p>
</form>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>Quando a caixa não é marcada, o navegador não envia whatsappOptIn; a automação interpreta isso como ausência de consentimento e bloqueia o contato proativo. Não marque a caixa previamente e mantenha no seu sistema a evidência da escolha e sua data.
const form = document.querySelector("#lead-form");
const status = document.querySelector("#lead-status");
let submission = null;
form.addEventListener("submit", async (event) => {
event.preventDefault();
const fields = Object.fromEntries(new FormData(form));
const captcha = fields["cf-turnstile-response"];
delete fields["cf-turnstile-response"];
// Uma nova edição é um novo evento; retries do mesmo envio mantêm a chave.
const fingerprint = JSON.stringify(fields);
if (submission?.fingerprint !== fingerprint) {
submission = { fingerprint, eventId: crypto.randomUUID() };
}
const button = form.querySelector('button[type="submit"]');
button.disabled = true;
try {
const response = await fetch("URL_PUBLICA_DA_AUTOMACAO", {
method: "POST",
credentials: "omit",
headers: { "Content-Type": "application/json", "X-Turnstile-Token": captcha },
body: JSON.stringify({ ...fields, eventId: submission.eventId }),
});
if (!response.ok) throw new Error("Não foi possível confirmar a solicitação.");
status.textContent = "Recebemos sua solicitação.";
form.reset();
submission = null;
} catch {
status.textContent = "Falha ao confirmar. Tente novamente sem alterar os dados.";
} finally {
button.disabled = false;
window.turnstile?.reset(); // tokens são curtos e de uso único
}
});Não substitua Turnstile por uma chave Bearer no frontend. Origin/CORS sozinho não autentica um formulário. O servidor verifica o token, hostname e action; sem proteção válida não admite o evento. Quem usa o modo público aceita uma entrada pública protegida contra abuso, não uma prova de identidade do visitante.
Idempotência e resposta
Envie Idempotency-Key (ou X-Idempotency-Key) ou um campo eventId: chave de 8–128 caracteres, estável para o mesmo envio. Não use só o telefone; a mesma pessoa pode gerar novas oportunidades.
{ "runId": "ID_DA_EXECUCAO", "duplicate": false, "status": "QUEUED" }202 significa aceito e persistido, não mensagem enviada. Se a Vensy estiver instável no momento do envio, o evento fica guardado e a resposta é { "queued": true }, sem runId. A execução começa assim que o serviço volta, e não é preciso reenviar. Mesmo evento e dados devolvem duplicate: true e não consomem outra execução; mesma chave com outros dados retorna 409. A deduplicação de entrada acompanha a retenção de execuções (90 dias após conclusão); não recicle chaves antigas.
Em timeout/erro transitório, use backoff com jitter e a mesma chave. Respeite Retry-After em 429; não repita 400/401/403 sem corrigir a causa. O limite de entrada é 60 requisições/minuto por IP, compartilhado entre URLs de automação; cotas do workspace são verificadas separadamente.
Capturar amostra sem efeitos
Clique Aguardar evento de teste e envie X-Vensy-Test-Token com o token temporário mostrado. A captura aceita um evento por até 5 minutos, mesmo em rascunho, e responde 200 { "captured": true }. Ela não cria contato, negócio, execução nem mensagem. Gere outra captura para repetir e use dados fictícios. Tokens de captura não devem ser mantidos no site publicado.
Referências: verificação server-side do Turnstile.