Voltar ao siteITS TIME / DEV

Documentação · API v1

Integração de cobrança via webhook

Envie títulos financeiros do seu sistema para a Its Time. Este guia mostra o processo completo, da assinatura à primeira resposta.

POSTJSONHMAC-SHA256

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.

Endpoint
POST https://api.itstime.pro/api/integrations/collections/v1/sources/{PUBLIC_SOURCE_ID}/events

02 · 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.

Regra da assinatura
assinatura = HMAC-SHA256(SEU_SEGREDO, timestamp + "." + corpo_json_bruto)

X-Collection-Signature: sha256=HASH_HEXADECIMAL
CabeçalhoValor
Content-Typeapplication/json
Idempotency-KeyIdentificador único, até 255 caracteres
X-Collection-TimestampUnix timestamp em segundos ou milissegundos
X-Collection-Signaturesha256= 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.

JSON
{
  "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

CampoObrigatórioFormato
schemaVersionSimSempre 1.0
ingestionIdSimID único do evento ou lote
modeSimincremental ou snapshot
occurredAtSimISO 8601 com fuso horário
scopeNãoEscopo de um snapshot
recordsSimLista de até 500 cobranças

Registro de cobrança

GrupoObrigatóriosOpcionais
sourcesourceUpdatedAtexternalEventId
customerexternalId, name, phonedocument, email
creditorexternalIdname, document
receivableexternalId, remainingAmount, currency, dueDate, statusdescription, originalAmount
paymentNenhummethod, pixKey, paymentUrl, expiresAt
metadataNenhumObjeto livre dentro dos limites

Valores aceitos

CampoValores
statusopen, settled, cancelled, suspended, unknown
payment.methodpix, boleto, card, cash, bank_transfer, store_credit, other
currencyISO 4217, por exemplo BRL
dueDateYYYY-MM-DD
valoresString 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.

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

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

Java
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

  1. Crie as variáveis de ambiente webhook_url e webhook_secret.
  2. Crie um POST para {{webhook_url}} e cole o payload em Body → raw → JSON.
  3. Adicione o script abaixo em Pre-request Script.
  4. Cadastre os quatro cabeçalhos da tabela.
Pre-request Script
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çalhoValor no Postman
Content-Typeapplication/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.

202 Accepted
{
  "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

HTTPO que significaO que fazer
400JSON inválido ou Idempotency-Key ausenteCorrija a requisição
401Fonte, timestamp ou assinatura inválidosRevise URL, relógio, segredo e corpo
409A chave idempotente foi usada com outro corpoUse uma nova chave ou restaure o corpo original
413Corpo acima de 1 MBDivida o lote
422Campo ausente ou formato inválidoCorrija os campos informados
429Limite de requisições excedidoAguarde o Retry-After
503Fonte pausada ou indisponívelTente 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.