Skip to content

Emissão em Lote

Cria e acompanha cobranças em lote de forma assíncrona — até 500 itens por requisição.

HABILITAÇÃO NECESSÁRIA

A emissão em lote é liberada por empresa via feature-flag. Se receber 403, entre em contato com o suporte DiviPay para habilitar o recurso na sua conta.

Conceitos

Assíncrono

O POST /api/charge/batch devolve 202 Accepted imediatamente. As cobranças são geradas em background. Acompanhe o progresso por polling (GET .../{id} e GET .../{id}/items) e/ou pelo webhook de conclusão enviado à callbackUrl.

Idempotência

Use o header Idempotency-Key (por lote) e o campo externalId (por item). É seguro reenviar a mesma requisição em caso de timeout ou erro de rede — o lote não será duplicado e cada item é processado apenas uma vez.

Sucesso Parcial

Cada item é processado de forma independente. O lote pode terminar com status PARTIALLY_COMPLETED. Consulte os itens com falha via GET .../{id}/items?status=FAILED e reprocesse com POST .../{id}/retry.


POST /api/charge/batch

Cria um novo lote de cobranças.

Endpoint

POST https://api.divipay.com.br/api/charge/batch

Headers

HeaderValorObrigatório
AuthorizationBearerSim
Content-Typeapplication/jsonSim
Idempotency-Keystring único por loteSim

Body Parameters

CampoTipoDescriçãoObrigatório
itemsBatchChargeItem[]Lista de cobranças (1 a 500 itens)Sim
callbackUrlstringURL para receber o webhook de conclusãoNão

Objeto BatchChargeItem

CampoTipoDescriçãoObrigatório
externalIdstringSua referência por item — único no lote, usado para idempotênciaSim
amountnumberValor em reais (> 0)Sim
descriptionstringDescrição da cobrançaSim
namestringNome do pagadorNão
documentstringCPF ou CNPJ (somente números)Não
emailstringE-mail do pagadorNão
phonestringTelefone com DDD (somente números)Não
dueDatestringData de vencimento (ISO, ex: "2026-07-10"). Padrão: +7 diasNão
maxInstallmentsnumberNúmero máximo de parcelasNão

VALIDAÇÃO ATÔMICA

A validação do envelope é atômica: se items estiver vazio, ultrapassar 500 itens, algum item não tiver externalId, amount ou description, ou houver externalId duplicado no mesmo lote, a API retorna 400 e nenhuma cobrança é criada.

Exemplo de Requisição

bash
curl -X POST https://api.divipay.com.br/api/charge/batch \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: lote-nov-2026-001" \
  -d '{
    "callbackUrl": "https://seusite.com/webhook/lote",
    "items": [
      {
        "externalId": "ERP-1001",
        "amount": 150.00,
        "description": "Mensalidade Novembro/2026",
        "name": "João Silva",
        "document": "12345678901",
        "email": "joao@email.com",
        "dueDate": "2026-07-10"
      },
      {
        "externalId": "ERP-1002",
        "amount": 320.50,
        "description": "Mensalidade Novembro/2026",
        "name": "Maria Oliveira",
        "document": "98765432100",
        "email": "maria@email.com",
        "dueDate": "2026-07-10"
      },
      {
        "externalId": "ERP-1003",
        "amount": 89.90,
        "description": "Renovação de Plano",
        "name": "Carlos Souza",
        "dueDate": "2026-07-15"
      }
    ]
  }'
javascript
const response = await fetch('https://api.divipay.com.br/api/charge/batch', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer SEU_TOKEN',
    'Content-Type': 'application/json',
    'Idempotency-Key': 'lote-nov-2026-001'
  },
  body: JSON.stringify({
    callbackUrl: 'https://seusite.com/webhook/lote',
    items: [
      {
        externalId: 'ERP-1001',
        amount: 150.00,
        description: 'Mensalidade Novembro/2026',
        name: 'João Silva',
        document: '12345678901',
        email: 'joao@email.com',
        dueDate: '2026-07-10'
      },
      {
        externalId: 'ERP-1002',
        amount: 320.50,
        description: 'Mensalidade Novembro/2026',
        name: 'Maria Oliveira',
        document: '98765432100',
        email: 'maria@email.com',
        dueDate: '2026-07-10'
      },
      {
        externalId: 'ERP-1003',
        amount: 89.90,
        description: 'Renovação de Plano',
        name: 'Carlos Souza',
        dueDate: '2026-07-15'
      }
    ]
  })
});

const data = await response.json();
python
import requests

response = requests.post(
    'https://api.divipay.com.br/api/charge/batch',
    headers={
        'Authorization': 'Bearer SEU_TOKEN',
        'Content-Type': 'application/json',
        'Idempotency-Key': 'lote-nov-2026-001'
    },
    json={
        'callbackUrl': 'https://seusite.com/webhook/lote',
        'items': [
            {
                'externalId': 'ERP-1001',
                'amount': 150.00,
                'description': 'Mensalidade Novembro/2026',
                'name': 'João Silva',
                'document': '12345678901',
                'email': 'joao@email.com',
                'dueDate': '2026-07-10'
            },
            {
                'externalId': 'ERP-1002',
                'amount': 320.50,
                'description': 'Mensalidade Novembro/2026',
                'name': 'Maria Oliveira',
                'document': '98765432100',
                'email': 'maria@email.com',
                'dueDate': '2026-07-10'
            },
            {
                'externalId': 'ERP-1003',
                'amount': 89.90,
                'description': 'Renovação de Plano',
                'name': 'Carlos Souza',
                'dueDate': '2026-07-15'
            }
        ]
    }
)

data = response.json()

Resposta de Sucesso

Status: 202 Accepted

json
{
  "batchId": "8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11",
  "status": "PENDING",
  "totalItems": 3
}

POST /api/charge/batch/validate

Valida o envelope do lote sem criar nenhuma cobrança (dry-run).

Endpoint

POST https://api.divipay.com.br/api/charge/batch/validate

Headers

HeaderValorObrigatório
AuthorizationBearerSim
Content-Typeapplication/jsonSim

Body Parameters

Mesmo body do POST /api/charge/batch (campos items e callbackUrl).

Exemplo de Requisição

bash
curl -X POST https://api.divipay.com.br/api/charge/batch/validate \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "externalId": "ERP-1001",
        "amount": 150.00,
        "description": "Mensalidade Novembro/2026"
      },
      {
        "externalId": "ERP-1001",
        "amount": 320.50,
        "description": "externalId duplicado — vai falhar"
      }
    ]
  }'
javascript
const response = await fetch('https://api.divipay.com.br/api/charge/batch/validate', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer SEU_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    items: [
      { externalId: 'ERP-1001', amount: 150.00, description: 'Mensalidade Novembro/2026' },
      { externalId: 'ERP-1001', amount: 320.50, description: 'externalId duplicado — vai falhar' }
    ]
  })
});

const result = await response.json();
python
import requests

response = requests.post(
    'https://api.divipay.com.br/api/charge/batch/validate',
    headers={
        'Authorization': 'Bearer SEU_TOKEN',
        'Content-Type': 'application/json'
    },
    json={
        'items': [
            {'externalId': 'ERP-1001', 'amount': 150.00, 'description': 'Mensalidade Novembro/2026'},
            {'externalId': 'ERP-1001', 'amount': 320.50, 'description': 'externalId duplicado — vai falhar'}
        ]
    }
)

result = response.json()

Resposta

Status: 200 OK

json
{
  "valid": false,
  "items": [
    {
      "index": 0,
      "externalId": "ERP-1001",
      "ok": true,
      "errors": []
    },
    {
      "index": 1,
      "externalId": "ERP-1001",
      "ok": false,
      "errors": ["externalId duplicado no lote"]
    }
  ]
}

GET /api/charge/batch/

Consulta o status e os contadores de um lote.

Endpoint

GET https://api.divipay.com.br/api/charge/batch/{id}

Headers

HeaderValorObrigatório
AuthorizationBearerSim

Path Parameters

ParâmetroTipoDescrição
idstringID do lote retornado no POST

Status do Lote

StatusDescrição
PENDINGLote criado, aguardando processamento
PROCESSINGCobranças sendo geradas em background
COMPLETEDTodos os itens processados com sucesso
PARTIALLY_COMPLETEDProcessamento concluído, mas alguns itens falharam
FAILEDTodos os itens falharam

Exemplo de Requisição

bash
curl -X GET https://api.divipay.com.br/api/charge/batch/8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11 \
  -H "Authorization: Bearer SEU_TOKEN"
javascript
const response = await fetch(
  'https://api.divipay.com.br/api/charge/batch/8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11',
  {
    headers: { 'Authorization': 'Bearer SEU_TOKEN' }
  }
);

const batch = await response.json();
python
import requests

response = requests.get(
    'https://api.divipay.com.br/api/charge/batch/8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11',
    headers={'Authorization': 'Bearer SEU_TOKEN'}
)

batch = response.json()

Resposta de Sucesso

Status: 200 OK

json
{
  "batchId": "8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11",
  "status": "PARTIALLY_COMPLETED",
  "totalItems": 3,
  "processedItems": 3,
  "succeededItems": 2,
  "failedItems": 1,
  "createdAt": "2026-06-17T10:00:00.000Z",
  "startedAt": "2026-06-17T10:00:05.000Z",
  "finishedAt": "2026-06-17T10:00:48.000Z"
}

GET /api/charge/batch/{id}/items

Lista os resultados item a item, com suporte a filtro por status e paginação.

Endpoint

GET https://api.divipay.com.br/api/charge/batch/{id}/items

Headers

HeaderValorObrigatório
AuthorizationBearerSim

Query Parameters

ParâmetroTipoDescriçãoPadrão
statusstringFiltra por status do item
pagenumberPágina atual (0-based)0
limitnumberItens por página (máx. 500)100

Status do Item

StatusDescrição
PENDINGAguardando processamento
PROCESSINGEm processamento
SUCCEEDEDCobrança criada com sucesso
FAILEDFalhou ao criar a cobrança
SKIPPED_DUPLICATEIgnorado por idempotência (externalId já processado)

Exemplo de Requisição

bash
curl -X GET "https://api.divipay.com.br/api/charge/batch/8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11/items?status=FAILED&page=0&limit=100" \
  -H "Authorization: Bearer SEU_TOKEN"
javascript
const params = new URLSearchParams({ status: 'FAILED', page: 0, limit: 100 });

const response = await fetch(
  `https://api.divipay.com.br/api/charge/batch/8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11/items?${params}`,
  {
    headers: { 'Authorization': 'Bearer SEU_TOKEN' }
  }
);

const result = await response.json();
python
import requests

response = requests.get(
    'https://api.divipay.com.br/api/charge/batch/8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11/items',
    headers={'Authorization': 'Bearer SEU_TOKEN'},
    params={'status': 'FAILED', 'page': 0, 'limit': 100}
)

result = response.json()

Resposta de Sucesso

Status: 200 OK

json
{
  "registers": [
    {
      "index": 0,
      "externalId": "ERP-1001",
      "status": "SUCCEEDED",
      "chargeId": "a1b2c3d4-1234-5678-abcd-ef0123456789"
    },
    {
      "index": 1,
      "externalId": "ERP-1002",
      "status": "SUCCEEDED",
      "chargeId": "b2c3d4e5-2345-6789-bcde-f01234567890"
    },
    {
      "index": 2,
      "externalId": "ERP-1003",
      "status": "FAILED",
      "chargeId": null,
      "error": {
        "code": "INVALID_DOCUMENT",
        "message": "CPF/CNPJ inválido"
      }
    }
  ],
  "total": 3,
  "pages": 1
}

ACESSANDO A COBRANÇA

Para cada item com status: "SUCCEEDED", use o chargeId em GET /api/charge/{chargeId} para obter o QR Code Pix, link de pagamento ou boleto.


POST /api/charge/batch/{id}/retry

Re-enfileira apenas os itens com status FAILED para nova tentativa.

Endpoint

POST https://api.divipay.com.br/api/charge/batch/{id}/retry

Headers

HeaderValorObrigatório
AuthorizationBearerSim

Exemplo de Requisição

bash
curl -X POST https://api.divipay.com.br/api/charge/batch/8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11/retry \
  -H "Authorization: Bearer SEU_TOKEN"
javascript
const response = await fetch(
  'https://api.divipay.com.br/api/charge/batch/8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11/retry',
  {
    method: 'POST',
    headers: { 'Authorization': 'Bearer SEU_TOKEN' }
  }
);

const result = await response.json();
python
import requests

response = requests.post(
    'https://api.divipay.com.br/api/charge/batch/8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11/retry',
    headers={'Authorization': 'Bearer SEU_TOKEN'}
)

result = response.json()

Resposta de Sucesso

Status: 200 OK

json
{
  "batchId": "8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11",
  "requeued": 1
}

Webhook de Conclusão

Quando o processamento do lote termina, a DiviPay envia um POST para a callbackUrl informada na criação (ou o webhook padrão configurado na conta):

json
{
  "type": "charge_batch.completed",
  "batchId": "8f7c6a2d-4a1b-4de8-9f2d-1e84f7b80c11",
  "status": "PARTIALLY_COMPLETED",
  "totalItems": 100,
  "succeededItems": 98,
  "failedItems": 2
}

WEBHOOKS INDIVIDUAIS

As cobranças geradas continuam disparando seus webhooks normais (ex.: charge.paid / APPROVED) quando o pagador efetuar o pagamento. Consulte Configurar Webhooks para mais detalhes.


Exemplo Completo

Cria o lote, faz polling até concluir e baixa os itens com falha.

javascript
const BASE_URL = 'https://api.divipay.com.br';
const TOKEN = 'SEU_TOKEN';

async function apiRequest(method, path, body) {
  const res = await fetch(`${BASE_URL}${path}`, {
    method,
    headers: {
      'Authorization': `Bearer ${TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: body ? JSON.stringify(body) : undefined
  });

  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw Object.assign(new Error(`HTTP ${res.status}`), { response: err });
  }

  return res.json();
}

async function createBatch(items, callbackUrl) {
  // Idempotency-Key gerada uma vez e armazenada antes de enviar
  const idempotencyKey = `lote-${Date.now()}-${Math.random().toString(36).slice(2)}`;

  const res = await fetch(`${BASE_URL}/api/charge/batch`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${TOKEN}`,
      'Content-Type': 'application/json',
      'Idempotency-Key': idempotencyKey
    },
    body: JSON.stringify({ items, callbackUrl })
  });

  if (!res.ok) throw new Error(`Erro ao criar lote: HTTP ${res.status}`);
  return res.json(); // { batchId, status, totalItems }
}

async function pollBatch(batchId, { intervalMs = 5000, timeoutMs = 300000 } = {}) {
  const deadline = Date.now() + timeoutMs;
  const terminalStatuses = new Set(['COMPLETED', 'PARTIALLY_COMPLETED', 'FAILED']);

  while (Date.now() < deadline) {
    const batch = await apiRequest('GET', `/api/charge/batch/${batchId}`);
    console.log(`[${new Date().toISOString()}] Status: ${batch.status} — ${batch.processedItems}/${batch.totalItems} processados`);

    if (terminalStatuses.has(batch.status)) return batch;

    await new Promise(resolve => setTimeout(resolve, intervalMs));
  }

  throw new Error(`Timeout: lote ${batchId} não concluiu em ${timeoutMs / 1000}s`);
}

async function getFailedItems(batchId) {
  const failed = [];
  let page = 0;

  while (true) {
    const result = await apiRequest(
      'GET',
      `/api/charge/batch/${batchId}/items?status=FAILED&page=${page}&limit=500`
    );

    failed.push(...result.registers);

    if (result.registers.length === 0 || failed.length >= result.total) break;
    page++;
  }

  return failed;
}

// --- Uso ---
async function main() {
  const items = [
    { externalId: 'ERP-1001', amount: 150.00, description: 'Mensalidade Julho/2026', name: 'João Silva', document: '12345678901', email: 'joao@email.com', dueDate: '2026-07-10' },
    { externalId: 'ERP-1002', amount: 320.50, description: 'Mensalidade Julho/2026', name: 'Maria Oliveira', document: '98765432100', email: 'maria@email.com', dueDate: '2026-07-10' },
    { externalId: 'ERP-1003', amount: 89.90,  description: 'Renovação de Plano',     name: 'Carlos Souza',   dueDate: '2026-07-15' }
  ];

  // 1. Criar o lote
  const { batchId, totalItems } = await createBatch(items, 'https://seusite.com/webhook/lote');
  console.log(`Lote criado: ${batchId} (${totalItems} itens)`);

  // 2. Aguardar conclusão via polling
  const batch = await pollBatch(batchId);
  console.log(`Lote finalizado com status: ${batch.status}`);
  console.log(`Sucesso: ${batch.succeededItems} | Falha: ${batch.failedItems}`);

  // 3. Tratar falhas
  if (batch.failedItems > 0) {
    const failedItems = await getFailedItems(batchId);

    console.log('\nItens com falha:');
    for (const item of failedItems) {
      console.log(`  [${item.externalId}] ${item.error?.code}: ${item.error?.message}`);
    }

    // Opção A: retry automático
    const retry = await apiRequest('POST', `/api/charge/batch/${batchId}/retry`);
    console.log(`\nRe-enfileirados: ${retry.requeued} itens`);
  }
}

main().catch(console.error);

Respostas de Erro

400 Bad Request

Envelope inválido. Nenhuma cobrança é criada.

json
{
  "statusCode": 400,
  "message": "Validation failed",
  "errors": [
    "items must contain at least 1 element",
    "items[2].externalId must be unique within the batch",
    "items[4].amount must be a positive number"
  ]
}

403 Forbidden

Token inválido/expirado ou feature de lote não habilitada para a empresa.

json
{
  "statusCode": 403,
  "message": "Forbidden"
}

TIP

Se o token estiver correto mas o erro persistir, entre em contato com o suporte DiviPay para habilitar a emissão em lote na sua conta.

404 Not Found

Lote não encontrado.

json
{
  "statusCode": 404,
  "message": "Batch not found",
  "error": "Not Found"
}

Próximos Passos

Documentação da API DiviPay