Skip to content
Vai integrar a API REST da Zarv com ajuda de IA? Copie a instrução que aponta o modelo para esta documentação.
Ver a instrução
Você vai integrar a API REST da Zarv. Use a documentação oficial como fonte da verdade.

- Página desta integração: https://developers.zarv.com/api/id-v3/webhook
- Autenticação Bearer JWT e hosts: https://developers.zarv.com/setup
- Índice das APIs: https://developers.zarv.com/api
- Índice da documentação em markdown: https://developers.zarv.com/llms.txt
- Documentação completa em um arquivo: https://developers.zarv.com/llms-full.txt

Os specs OpenAPI publicados são a fonte da verdade para rotas e schemas — use o spec da API que você escolher antes de escrever qualquer request.

Antes de escrever qualquer código, leia a página desta integração. Siga exatamente os nomes de campos, endpoints, callbacks e formatos que estiverem documentados — não invente parâmetros nem endpoints, e não deduza comportamento a partir de outras APIs que você conhece. Se algo de que você precisa não estiver na documentação, diga que não está em vez de supor.

Minha tarefa: [ex.: escolher a API certa e fazer a primeira chamada autenticada]

Webhook de Enriquecimento ​

Visão Geral ​

Quando um enriquecimento não termina dentro da espera síncrona de 25 segundos da criação (a resposta é 202), a Zarv pode avisar o seu sistema quando ele chegar a COMPLETED ou FAILED, com um POST assinado no endpoint que você indicar. É a alternativa ao polling em GET /api/v3/enrichments/{id}.

O callback só é enviado para um enriquecimento cuja criação respondeu 202. Se a criação respondeu 200, o resultado já veio na própria resposta e nenhum callback é enviado.

Configuração ​

Inclua um objeto callback no corpo de POST /api/v3/enrichments:

json
{
  "document": "111.444.777-35",
  "profileId": "8f14e45f-ceea-4e7a-9c1a-2b3c4d5e6f70",
  "callback": {
    "url": "https://sua-empresa.com/webhooks/zarv",
    "headers": {
      "Authorization": "Bearer <token-do-seu-endpoint>",
      "X-Tenant-Id": "my-tenant"
    }
  }
}
ParâmetroTipoObrigatórioDescrição
urlstringSimOnde entregar o callback. Só https, até 2048 caracteres. Endereço IP literal, localhost, *.internal e *.cluster.local são recusados (400 invalid_callback).
headersobject (string→string)NãoAté 20 headers enviados junto do callback, por exemplo para autenticar no seu endpoint.

O callback é sempre um POST com Content-Type: application/json. Content-Type, X-Zarv-Timestamp e X-Zarv-Signature são reservados e não podem ser definidos em headers.

Headers de credencial ficam cifrados

Headers que são credenciais — Authorization, Proxy-Authorization, Cookie, X-Api-Key/Api-Key/ApiKey e qualquer nome que contenha token, secret, password, passwd, apikey, auth, signature, credential ou session — são guardados cifrados e só são abertos no momento do envio. Nenhuma resposta da API devolve a configuração do callback.

Um replay idempotente (mesma Idempotency-Key e mesmo corpo) de um enriquecimento ainda em andamento rearma o callback enviado junto.

Requisição do Webhook ​

http
POST /webhooks/zarv HTTP/1.1
Host: sua-empresa.com
Content-Type: application/json
X-Zarv-Timestamp: 1790600404
X-Zarv-Signature: sha256=5d1f0c3e9a7b2c4d6e8f0a1b3c5d7e9f1a2b4c6d8e0f2a4b6c8d0e2f4a6b8c0d
Authorization: Bearer <token-do-seu-endpoint>

{"type":"enrichment.completed","id":"enr_0199a0b2-7c1e-7d4a-9f0e-3b1c2d4e5f60","status":"COMPLETED","outcome":"ok","createdAt":"2026-09-28T13:00:00Z","completedAt":"2026-09-28T13:00:41Z"}

Headers ​

HeaderDescrição
X-Zarv-TimestampMomento do envio, em segundos Unix
X-Zarv-Signaturesha256= seguido do HMAC-SHA256 em hexadecimal (veja abaixo)
os seus headersOs headers configurados em callback.headers

Campos do Payload ​

CampoTipoDescrição
typestringSempre enrichment.completed
idstringO id do enriquecimento
statusstringCOMPLETED ou FAILED
outcomestring | nullok ou subject_not_found quando COMPLETED; null quando FAILED
createdAtstringCriação do enriquecimento (RFC 3339, UTC)
completedAtstringQuando chegou ao status terminal (RFC 3339, UTC)

O payload não traz o documento nem os dados: busque-os com as suas credenciais em GET /api/v3/enrichments/{id}?include=data. Também não traz metadata; use o id para relacionar o callback ao seu pedido.

Verificando a Assinatura ​

Todo callback é assinado com um segredo de assinatura. Consulte-o com GET /api/v3/callback-secret e guarde-o no seu cofre de segredos:

bash
curl "https://services.zarv.com/api/v3/callback-secret" \
  -H "Authorization: Bearer SEU_TOKEN_DE_ACESSO"
# {"secret":"<64 caracteres hexadecimais>"}

Para validar um callback:

  1. Leia X-Zarv-Timestamp e o corpo bruto da requisição, exatamente como chegou (antes de qualquer parse de JSON).
  2. Calcule HMAC-SHA256(chave, "{X-Zarv-Timestamp}.{corpo}"), em que a chave são os 32 bytes do segredo decodificado de hexadecimal (não o texto hexadecimal).
  3. Compare sha256=<hex do resultado> com X-Zarv-Signature usando comparação em tempo constante (crypto.timingSafeEqual, hmac.Equal, hmac.compare_digest) — nunca ==.
  4. Rejeite o callback se o timestamp estiver a mais de 300 segundos do seu relógio, para que uma assinatura antiga não possa ser reaproveitada.
  5. Deduplique pelo id do corpo: a entrega é at-least-once, e o mesmo enriquecimento pode chegar mais de uma vez.
js
import crypto from "node:crypto";
import express from "express";

const secret = Buffer.from(process.env.ZARV_CALLBACK_SECRET, "hex");
const app = express();

app.post(
  "/webhooks/zarv",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const timestamp = req.get("X-Zarv-Timestamp") ?? "";
    const signature = req.get("X-Zarv-Signature") ?? "";

    const expected =
      "sha256=" +
      crypto
        .createHmac("sha256", secret)
        .update(`${timestamp}.`)
        .update(req.body) // corpo bruto (Buffer)
        .digest("hex");

    const valid =
      signature.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
    const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
    if (!valid || !fresh) return res.status(401).end();

    const event = JSON.parse(req.body);
    res.status(200).end(); // confirme logo; processe em segundo plano
    processOnce(event.id, event); // deduplique pelo id
  },
);
go
package webhook

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"io"
	"math"
	"net/http"
	"strconv"
	"time"
)

func Verify(secretHex string, r *http.Request) ([]byte, bool) {
	secret, err := hex.DecodeString(secretHex)
	if err != nil {
		return nil, false
	}
	body, err := io.ReadAll(r.Body) // corpo bruto
	if err != nil {
		return nil, false
	}
	ts := r.Header.Get("X-Zarv-Timestamp")
	sec, err := strconv.ParseInt(ts, 10, 64)
	if err != nil || math.Abs(float64(time.Now().Unix()-sec)) > 300 {
		return nil, false
	}
	mac := hmac.New(sha256.New, secret)
	mac.Write([]byte(ts + "."))
	mac.Write(body)
	expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
	return body, hmac.Equal([]byte(expected), []byte(r.Header.Get("X-Zarv-Signature")))
}
py
import hashlib
import hmac
import time

SECRET = bytes.fromhex(ZARV_CALLBACK_SECRET)

def verify(headers, raw_body: bytes) -> bool:
    timestamp = headers.get("X-Zarv-Timestamp", "")
    signature = headers.get("X-Zarv-Signature", "")
    try:
        if abs(time.time() - int(timestamp)) > 300:
            return False
    except ValueError:
        return False
    digest = hmac.new(SECRET, f"{timestamp}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={digest}", signature)

Entrega e Novas Tentativas ​

  • Responda com qualquer 2xx para confirmar o recebimento. A Zarv espera no máximo 10 segundos pela resposta: confirme logo e processe em segundo plano.
  • Falha de conexão, timeout e respostas 408, 425, 429 ou 5xx são reenviadas automaticamente, com intervalo crescente.
  • Respostas 3xx e as demais 4xx são tratadas como definitivas e não são reenviadas. Redirecionamentos não são seguidos: use a URL final.
  • Se o seu endpoint ficar fora do ar e o callback não chegar, o resultado continua disponível em GET /api/v3/enrichments/{id}. Para pedir um novo envio, fale com o time da Zarv.

Melhores Práticas ​

  1. Valide a assinatura e o timestamp antes de qualquer processamento.
  2. Deduplique pelo id — o mesmo callback pode chegar mais de uma vez.
  3. Responda 2xx rapidamente e processe de forma assíncrona.
  4. Busque os dados com GET /api/v3/enrichments/{id}?include=data depois do callback, com as suas credenciais.
  5. Use HTTPS com certificado válido e, se quiser, autentique o seu endpoint com callback.headers.
  6. Mantenha o polling como plano B: se um callback não chegar, consulte o enriquecimento pelo id retornado no 202.