Vensy Developers
Automações comerciais

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_WEBHOOK

Envie 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.

Nesta página