01 · Comece aqui
Início rápido
Para enviar uma cobrança, monte o JSON, gere a assinatura com seu segredo e faça um POST para a URL da fonte.
1. Obtenha a URL
Copie o endereço e o segredo no módulo Cobrança.
2. Monte o JSON
Use o schema 1.0 e comece com o modo incremental.
3. Assine e envie
Calcule o HMAC sobre os mesmos bytes enviados.
POST https://api.itstime.pro/api/integrations/collections/v1/sources/{PUBLIC_SOURCE_ID}/events02 · Preparação
Credenciais
No módulo Cobrança, crie uma fonte do tipo Webhook genérico. Nos detalhes técnicos você encontrará o identificador usado na URL. O segredo aparece uma única vez ao criar ou rotacionar a credencial.
03 · Segurança
Autenticação HMAC
A assinatura prova que o corpo veio do seu sistema. Primeiro gere o timestamp Unix atual. Depois concatene o timestamp, um ponto e o corpo JSON bruto.
assinatura = HMAC-SHA256(SEU_SEGREDO, timestamp + "." + corpo_json_bruto)
X-Collection-Signature: sha256=HASH_HEXADECIMAL| Cabeçalho | Valor |
|---|---|
Content-Type | application/json |
Idempotency-Key | Identificador único, até 255 caracteres |
X-Collection-Timestamp | Unix timestamp em segundos ou milissegundos |
X-Collection-Signature | sha256= seguido do hash hexadecimal |
04 · Dados
Payload completo
Use este exemplo como ponto de partida. A conexão é identificada pela URL; não envie IDs internos da Its Time.
{
"schemaVersion": "1.0",
"ingestionId": "cobranca-20260910-0001",
"mode": "incremental",
"occurredAt": "2026-09-10T14:30:00-03:00",
"records": [
{
"schemaVersion": "1.0",
"source": {
"externalEventId": "evento-98765",
"sourceUpdatedAt": "2026-09-10T14:30:00-03:00"
},
"customer": {
"externalId": "cliente-123",
"name": "Maria da Silva",
"phone": "5511999999999",
"document": "12345678900",
"email": "maria@exemplo.com"
},
"creditor": {
"externalId": "loja-01",
"name": "Ótica Exemplo",
"document": "12345678000190"
},
"receivable": {
"externalId": "titulo-456",
"description": "Parcela 2 de 3",
"originalAmount": "250.00",
"remainingAmount": "250.00",
"currency": "BRL",
"dueDate": "2026-09-05",
"status": "open"
},
"payment": {
"method": "pix",
"pixKey": "financeiro@exemplo.com",
"paymentUrl": "https://pagamentos.exemplo.com/titulo-456",
"expiresAt": "2026-09-15T23:59:59-03:00"
},
"metadata": {
"numeroParcela": 2,
"filial": "São Paulo"
}
}
]
}05 · Referência
Campos e formatos
Envelope
| Campo | Obrigatório | Formato |
|---|---|---|
schemaVersion | Sim | Sempre 1.0 |
ingestionId | Sim | ID único do evento ou lote |
mode | Sim | incremental ou snapshot |
occurredAt | Sim | ISO 8601 com fuso horário |
scope | Não | Escopo de um snapshot |
records | Sim | Lista de até 500 cobranças |
Registro de cobrança
| Grupo | Obrigatórios | Opcionais |
|---|---|---|
source | sourceUpdatedAt | externalEventId |
customer | externalId, name, phone | document, email |
creditor | externalId | name, document |
receivable | externalId, remainingAmount, currency, dueDate, status | description, originalAmount |
payment | Nenhum | method, pixKey, paymentUrl, expiresAt |
metadata | Nenhum | Objeto livre dentro dos limites |
Valores aceitos
| Campo | Valores |
|---|---|
status | open, settled, cancelled, suspended, unknown |
payment.method | pix, boleto, card, cash, bank_transfer, store_credit, other |
currency | ISO 4217, por exemplo BRL |
dueDate | YYYY-MM-DD |
valores | String decimal não negativa, por exemplo 250.00 |
06 · Exemplo
cURL e Bash
Requer Bash e OpenSSL. Troque o identificador da fonte e o segredo antes de executar.
WEBHOOK_URL="https://api.itstime.pro/api/integrations/collections/v1/sources/{PUBLIC_SOURCE_ID}/events"
WEBHOOK_SECRET="SEU_SEGREDO"
TIMESTAMP=$(date +%s)
IDEMPOTENCY_KEY="cobranca-$(date +%s)"
BODY='{"schemaVersion":"1.0","ingestionId":"cobranca-20260910-0001","mode":"incremental","occurredAt":"2026-09-10T14:30:00-03:00","records":[{"schemaVersion":"1.0","source":{"externalEventId":"evento-98765","sourceUpdatedAt":"2026-09-10T14:30:00-03:00"},"customer":{"externalId":"cliente-123","name":"Maria da Silva","phone":"5511999999999"},"creditor":{"externalId":"loja-01","name":"Ótica Exemplo"},"receivable":{"externalId":"titulo-456","remainingAmount":"250.00","currency":"BRL","dueDate":"2026-09-05","status":"open"}}]}'
SIGNATURE=$(printf '%s.%s' "$TIMESTAMP" "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -hex | sed 's/^.* //')
curl --request POST "$WEBHOOK_URL" --header "Content-Type: application/json" --header "Idempotency-Key: $IDEMPOTENCY_KEY" --header "X-Collection-Timestamp: $TIMESTAMP" --header "X-Collection-Signature: sha256=$SIGNATURE" --data-raw "$BODY"07 · Exemplo
Python
Instale requests com pip install requests. A serialização acontece uma vez e os mesmos bytes são assinados e enviados.
import hashlib
import hmac
import json
import time
import uuid
import requests
WEBHOOK_URL = "https://api.itstime.pro/api/integrations/collections/v1/sources/{PUBLIC_SOURCE_ID}/events"
WEBHOOK_SECRET = "SEU_SEGREDO"
payload = {
"schemaVersion": "1.0",
"ingestionId": f"cobranca-{uuid.uuid4()}",
"mode": "incremental",
"occurredAt": "2026-09-10T14:30:00-03:00",
"records": [{
"schemaVersion": "1.0",
"source": {
"externalEventId": "evento-98765",
"sourceUpdatedAt": "2026-09-10T14:30:00-03:00"
},
"customer": {
"externalId": "cliente-123",
"name": "Maria da Silva",
"phone": "5511999999999"
},
"creditor": {"externalId": "loja-01", "name": "Ótica Exemplo"},
"receivable": {
"externalId": "titulo-456",
"remainingAmount": "250.00",
"currency": "BRL",
"dueDate": "2026-09-05",
"status": "open"
}
}]
}
body = json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
timestamp = str(int(time.time()))
signature = hmac.new(
WEBHOOK_SECRET.encode("utf-8"),
timestamp.encode("utf-8") + b"." + body,
hashlib.sha256
).hexdigest()
response = requests.post(
WEBHOOK_URL,
data=body,
headers={
"Content-Type": "application/json",
"Idempotency-Key": payload["ingestionId"],
"X-Collection-Timestamp": timestamp,
"X-Collection-Signature": f"sha256={signature}"
},
timeout=30
)
print(response.status_code)
print(response.json())
response.raise_for_status()08 · Exemplo
Java 11+
O exemplo usa somente APIs incluídas no Java 11. Em produção, prefira uma biblioteca JSON para montar o corpo.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class ItsTimeWebhook {
private static final String URL = "https://api.itstime.pro/api/integrations/collections/v1/sources/{PUBLIC_SOURCE_ID}/events";
private static final String SECRET = "SEU_SEGREDO";
public static void main(String[] args) throws Exception {
String id = "cobranca-" + UUID.randomUUID();
String body = "{"schemaVersion":"1.0","ingestionId":"" + id
+ "","mode":"incremental","occurredAt":"2026-09-10T14:30:00-03:00","
+ ""records":[{"schemaVersion":"1.0","source":{"
+ ""sourceUpdatedAt":"2026-09-10T14:30:00-03:00"},"customer":{"
+ ""externalId":"cliente-123","name":"Maria da Silva","
+ ""phone":"5511999999999"},"creditor":{"externalId":"loja-01"},"
+ ""receivable":{"externalId":"titulo-456","remainingAmount":"250.00","
+ ""currency":"BRL","dueDate":"2026-09-05","status":"open"}}]}";
String timestamp = Long.toString(Instant.now().getEpochSecond());
String signature = hmacSha256(timestamp + "." + body, SECRET);
HttpRequest request = HttpRequest.newBuilder(URI.create(URL))
.header("Content-Type", "application/json")
.header("Idempotency-Key", id)
.header("X-Collection-Timestamp", timestamp)
.header("X-Collection-Signature", "sha256=" + signature)
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
private static String hmacSha256(String content, String secret) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] digest = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder(digest.length * 2);
for (byte value : digest) hex.append(String.format("%02x", value & 0xff));
return hex.toString();
}
}09 · Exemplo
Postman
- Crie as variáveis de ambiente
webhook_urlewebhook_secret. - Crie um POST para
{{webhook_url}}e cole o payload em Body → raw → JSON. - Adicione o script abaixo em Pre-request Script.
- Cadastre os quatro cabeçalhos da tabela.
const CryptoJS = pm.require("npm:crypto-js@4.2.0");
const timestamp = Math.floor(Date.now() / 1000).toString();
const body = pm.request.body.raw;
const secret = pm.environment.get("webhook_secret");
if (!secret) throw new Error("Configure a variável webhook_secret.");
const hash = CryptoJS.HmacSHA256(timestamp + "." + body, secret)
.toString(CryptoJS.enc.Hex);
pm.variables.set("collection_timestamp", timestamp);
pm.variables.set("collection_signature", "sha256=" + hash);
pm.variables.set("idempotency_key", "postman-" + Date.now());| Cabeçalho | Valor no Postman |
|---|---|
Content-Type | application/json |
Idempotency-Key | {{idempotency_key}} |
X-Collection-Timestamp | {{collection_timestamp}} |
X-Collection-Signature | {{collection_signature}} |
10 · Resultado
Resposta de sucesso
Uma requisição aceita retorna HTTP 202. O processamento já foi aplicado quando o status for succeeded.
{
"accepted": true,
"duplicate": false,
"ingestionId": "8d7c1398-ec65-4b3c-8827-b17c66371a3e",
"status": "succeeded",
"receivedCount": 1,
"createdCount": 1,
"updatedCount": 0,
"unchangedCount": 0,
"notPresentCount": 0,
"affectedCasesCount": 1
}Se duplicate for true, o mesmo lote já havia sido recebido e não foi aplicado novamente.
11 · Operação
Erros e retentativas
| HTTP | O que significa | O que fazer |
|---|---|---|
400 | JSON inválido ou Idempotency-Key ausente | Corrija a requisição |
401 | Fonte, timestamp ou assinatura inválidos | Revise URL, relógio, segredo e corpo |
409 | A chave idempotente foi usada com outro corpo | Use uma nova chave ou restaure o corpo original |
413 | Corpo acima de 1 MB | Divida o lote |
422 | Campo ausente ou formato inválido | Corrija os campos informados |
429 | Limite de requisições excedido | Aguarde o Retry-After |
503 | Fonte pausada ou indisponível | Tente novamente mais tarde |
Quando repetir
Se não receber uma resposta, repita com a mesma Idempotency-Key e o mesmo corpo. Gere apenas um novo timestamp e uma nova assinatura. Para falhas temporárias, use espera progressiva. Não repita erros 400, 401, 409 ou 422 sem corrigir a causa.
12 · Produção
Limites e segurança
- 500 registros por requisição
- 1 MB por corpo JSON
- 120 requisições por minuto por fonte e IP
- Timestamp dentro de 5 minutos
- Idempotency-Key de até 255 caracteres
- Metadata de até 16 KiB por registro
- Links de pagamento somente HTTPS
- Segredo anterior válido por 24 horas após rotação
13 · Finalização
Checklist da integração
- Fonte Webhook genérico criada no módulo Cobrança.
- URL e segredo guardados no servidor.
- Timestamp gerado no momento do envio.
- Corpo assinado antes da requisição.
- Chave idempotente única definida.
- Datas e valores enviados no formato correto.
- Respostas 202 e duplicate tratadas.
- Retentativas habilitadas apenas para falhas temporárias.
- Pagamento informado como settled com saldo 0.00.