Voren API v1

Integre pagamentos, produtos e webhooks em minutos.

REST previsível, autenticação HTTP Basic, chaves com escopos, idempotência e webhooks assinados HMAC-SHA256.

Base URL: https://appvoren.com.br/api/public/v1

Autenticação

Crie uma chave em /voren-api, copie e cole no sistema que vai se conectar. É só isso — não existe nenhuma outra configuração do lado da Voren. Envie a chave no header Authorization: Bearer.

curl https://appvoren.com.br/api/public/v1/ping \
  -H "Authorization: Bearer voren_live_xxx"

Chaves antigas no formato client_id + client_secret continuam funcionando com HTTP Basic, sem prazo de desativação.

OpenAPI, Postman e geração automática de cliente

A API é descrita em um arquivo legível por máquina. Qualquer linguagem, ERP, CRM, ferramenta no-code ou gerador de SDK consegue criar o cliente sozinho a partir dele — sem ninguém precisar ler esta página.

  • Especificação OpenAPI 3.1: https://appvoren.com.br/api/public/v1/openapi.json
  • Coleção Postman/Insomnia: https://appvoren.com.br/api/public/v1/postman.json (importe, preencha voren_api_key e teste)
# Cliente pronto em qualquer linguagem, a partir da especificação:
npx @openapitools/openapi-generator-cli generate \
  -i https://appvoren.com.br/api/public/v1/openapi.json \
  -g typescript-fetch -o ./voren-sdk

Validar a chave e o status da API

Na tela onde o seu usuário cola a chave da Voren, chame GET /v1/me antes de salvar: a resposta confirma se a chave é válida, em qual ambiente ela está, quais permissões possui e qual o limite de requisições por minuto. GET /v1/health não exige chave e serve para monitoramento.

curl https://appvoren.com.br/api/public/v1/me \
  -H "Authorization: Bearer voren_live_xxx"

# {"success":true,"data":{"valid":true,"environment":"live",
#   "scopes":["sales.write","sales.read","events.read"],
#   "rate_limit_per_min":300,"account":{"id":"...","name":"..."}}}

curl https://appvoren.com.br/api/public/v1/health
# {"status":"ok","api_version":"v1","components":{"api":"ok","database":"ok"}}

Toda resposta traz X-Request-Id (use no suporte) e os cabeçalhos X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset.

Eventos (polling)

A Voren mantém um histórico de eventos por conta. O seu sistema lê esse histórico quando quiser, usando um cursor incremental — nada precisa ser configurado aqui e nenhuma URL precisa ser exposta na internet.

# Primeira chamada (sem cursor)
curl "https://appvoren.com.br/api/public/v1/events?limit=50" \
  -H "Authorization: Bearer voren_live_xxx"

# Resposta
{
  "success": true,
  "data": [
    { "id":"...", "type":"payment.paid", "resource_type":"payment",
      "resource_id":"...", "created_at":"...", "data":{ } }
  ],
  "has_more": true,
  "next_cursor": "MS5hZTZjODAwZmYyNzIzMDY3"
}

# Próximas chamadas: reenvie o next_cursor recebido
curl "https://appvoren.com.br/api/public/v1/events?cursor=MS5hZTZjODAwZmYyNzIzMDY3&limit=50" \
  -H "Authorization: Bearer voren_live_xxx"

# Espera curta por eventos novos (até ~20s) em vez de repetir a chamada
curl "https://appvoren.com.br/api/public/v1/events?wait=true" \
  -H "Authorization: Bearer voren_live_xxx"

# Filtrar tipos
curl "https://appvoren.com.br/api/public/v1/events?types=payment.paid,payment.refunded" \
  -H "Authorization: Bearer voren_live_xxx"

Guarde sempre o último next_cursor: repetir a chamada com ele nunca perde nem duplica evento. O cursor é opaco e válido apenas para a sua conta. Tipos disponíveis: payment.created, payment.paid, payment.failed, payment.refunded, payment.canceled, payment.chargeback, order.shipped, order.delivered, subscription.created, subscription.charged, subscription.updated, subscription.canceled. O histórico mantém 90 dias.

Clientes e saldo

curl "https://appvoren.com.br/api/public/v1/customers?limit=50" -H "Authorization: Bearer voren_live_xxx"
curl "https://appvoren.com.br/api/public/v1/balance" -H "Authorization: Bearer voren_live_xxx"

/customers exige o escopo customers.read; /balance e /events exigem sales.read.

Quickstart em 3 passos

  1. Ative seu recebedor Voren e crie uma chave em /voren-api.
  2. Opcional: crie um produto via API. Se os produtos já existem no seu sistema, pule esta etapa e cobre por valor.
  3. Gere uma cobrança e receba o resultado por webhook.
# 1) Pix sem produto cadastrado na Voren (catálogo no seu sistema)
curl -X POST https://appvoren.com.br/api/public/v1/charges \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001" \
  -d '{
    "amount_cents":9700,
    "item_name":"Curso de Trading - Plano Anual",
    "external_reference":"pedido-1001",
    "payment_method":"pix",
    "customer":{"name":"Ana Silva","email":"ana@example.com","document":"12345678909","phone":"11999998888"}
  }'

# 2) Pix usando um produto do catálogo Voren
curl -X POST https://appvoren.com.br/api/public/v1/charges \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1002" \
  -d '{
    "product_id":"<uuid>",
    "payment_method":"pix",
    "customer":{"name":"Ana Silva","email":"ana@example.com","document":"12345678909","phone":"11999998888"}
  }'

Obrigatório em Pix e Boleto: customer.phone com DDD (ex.: 11999998888). Sem telefone válido o provedor recusa a cobrança. No cartão o telefone é opcional.

Cartão de crédito: envie o endereço de cobrança em customer.address(line_1, zip_code, city, state, opcional line_2 e country). O antifraude só é aplicado quando o endereço é enviado — sem ele a taxa de aprovação cai bastante.

# Cartão com endereço de cobrança
curl -X POST https://appvoren.com.br/api/public/v1/charges \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_cents":9700,
    "item_name":"Curso de Trading",
    "payment_method":"credit_card",
    "card_token":"token_xxx",
    "installments":1,
    "customer":{
      "name":"Ana Silva","email":"ana@example.com","document":"12345678909","phone":"11999998888",
      "address":{"line_1":"Rua das Flores, 123, Centro","zip_code":"01001000","city":"Sao Paulo","state":"SP","country":"BR"}
    }
  }'

Escopos e ambientes

Cada chave tem escopos e ambiente (live ou test). Chame apenas o mínimo necessário.

  • products.read, products.write
  • sales.read, sales.write (inclui cobranças e estornos)
  • subscriptions.read, subscriptions.write, customers.read
  • events.read, balance.read, shipments.write, tokens.write
  • webhooks.manage

Aceitamos aliases comuns de outras plataformas: payments.write/charges.write/refunds.write valem como sales.write, e payments.read/charges.read valem como sales.read. Uma chave com sales.write também pode ler suas vendas e seu catálogo.

Compatibilidade garantida: chaves antigas com sales.read continuam lendo eventos e saldo sem precisar ser recriadas.

Tokens de cartão

POST/v1/tokensscope: sales.write

Para montar o checkout dentro do seu próprio sistema, troque os dados do cartão por um card_token de uso único e envie esse token em POST /v1/charges. A Voren não armazena o número do cartão.

curl -X POST https://appvoren.com.br/api/public/v1/tokens \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "card":{
      "number":"4000000000000010",
      "holder_name":"ANA SILVA",
      "exp_month":12,
      "exp_year":2030,
      "cvv":"123"
    }
  }'

# resposta
# { "object":"card_token", "id":"token_xxx", "card":{ "last_four_digits":"0010", "brand":"visa" } }

Idempotência

Envie um header Idempotency-Key em POST. Requisições repetidas com a mesma chave retornam o mesmo recurso e o header Idempotency-Replayed: true.

Rate limits

300 requisições por minuto por chave. Ao exceder, respondemos 429 com header Retry-After em segundos.

Formato de erro

Todos os erros usam JSON estável:

{
  "error": "invalid_request",
  "message": "Descrição legível do problema.",
  "request_id": "req_ab12cd34...",
  "details": [{ "path": "customer.email", "message": "Invalid email" }]
}

Códigos comuns: unauthorized, invalid_credentials, insufficient_scope, invalid_request, not_found, invalid_state, rate_limited, provider_error, internal_error.

Products

GET/v1/productsscope: products.read
POST/v1/productsscope: products.write
GET/v1/products/{id}scope: products.read
PATCH/v1/products/{id}scope: products.write
curl -X POST https://appvoren.com.br/api/public/v1/products \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name":"Mentoria Voren",
    "description":"3 meses de acompanhamento",
    "price_cents":149700,
    "status":"active",
    "delivery_methods":["email"],
    "support_email":"suporte@example.com"
  }'

Charges (cobranças)

POST/v1/chargesscope: sales.write
GET/v1/chargesscope: sales.read
GET/v1/charges/{id}scope: sales.read

Existem dois modos, e você usa exatamente um deles por cobrança:

  • Catálogo Voren — envie product_id. Preço, oferta, cupom e order bumps vêm do produto cadastrado.
  • Item externo — envie amount_cents + item_name. Não é preciso criar nenhum produto na Voren; o catálogo continua no seu sistema. Use external_reference para amarrar a cobrança ao seu pedido (também dá para filtrar por ele em GET /v1/charges?external_reference=...).

No modo item externo, offer_id, coupon_code e bump_ids não se aplicam (dependem do catálogo Voren). A divisão (split), as taxas e a liquidação funcionam igual nos dois modos, e o valor final é sempre validado no servidor. Métodos: pix, boleto, credit_card.

# Item externo (sem produto na Voren)
curl -X POST https://appvoren.com.br/api/public/v1/charges \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cli-2025-0001" \
  -d '{
    "amount_cents":149700,
    "item_name":"Mentoria - Turma 12",
    "external_reference":"pedido-8842",
    "payment_method":"pix",
    "customer":{"name":"Ana","email":"ana@x.com","document":"12345678909","phone":"11999998888"},
    "metadata":{"origin":"checkout-proprio"}
  }'

# Catálogo Voren
curl -X POST https://appvoren.com.br/api/public/v1/charges \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cli-2025-0002" \
  -d '{
    "product_id":"<uuid>",
    "offer_id":"<uuid>",
    "payment_method":"credit_card",
    "card_token":"card_token_xxx",
    "installments":3,
    "customer":{"name":"Ana","email":"ana@x.com","document":"12345678909","phone":"11999998888"},
    "coupon_code":"BEMVINDO10",
    "ref_code":"abc123",
    "metadata":{"origin":"landing-a"}
  }'

Parcelamento no cartão: os juros são de responsabilidade do cliente final e somados automaticamente ao valor cobrado. A resposta traz amount_cents (valor do item),interest_cents (juros do parcelamento) e charged_amount_cents (total efetivamente cobrado no cartão). O repasse ao produtor é sempre calculado sobre amount_cents.

Refunds (estornos)

POST/v1/refundsscope: sales.write

Suporta estorno total ou parcial. Envie sale_id (ou charge_id) e opcionalmente amount_cents para parcial.

# Estorno total
curl -X POST https://appvoren.com.br/api/public/v1/refunds \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: refund-2025-0001" \
  -d '{"sale_id":"<uuid>","reason":"solicitação do cliente"}'

# Estorno parcial (R$ 30,00)
curl -X POST https://appvoren.com.br/api/public/v1/refunds \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"sale_id":"<uuid>","amount_cents":3000}'

Sales

GET/v1/salesscope: sales.read
GET/v1/sales/{id}scope: sales.read

Filtros: status, from, to, limit (máx 100), cursor.

Subscriptions (assinaturas recorrentes)

POST/v1/subscriptionsscope: subscriptions.write
GET/v1/subscriptionsscope: subscriptions.read
GET/v1/subscriptions/{id}scope: subscriptions.read
PATCH/v1/subscriptions/{id}scope: subscriptions.write
DELETE/v1/subscriptions/{id}scope: subscriptions.write

Assinaturas são cobradas sempre em cartão de crédito: gere o card_token em POST /v1/tokens antes de criar. Funciona em dois modos, igual a Charges: com product_id (item do catálogo Voren) ou avulso com amount_cents + item_name, sem nada cadastrado na Voren.customer.phone com DDD é obrigatório.

# Assinatura mensal avulsa (R$ 49,90/mês)
curl -X POST https://appvoren.com.br/api/public/v1/subscriptions \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sub-2026-0001" \
  -d '{
    "amount_cents": 4990,
    "item_name": "Plano Pro",
    "external_reference": "plan_pro_mensal",
    "interval": "month",
    "interval_count": 1,
    "card_token": "<token de /v1/tokens>",
    "customer": {
      "name": "Maria Silva",
      "email": "maria@exemplo.com",
      "document": "12345678909",
      "phone": "11999998888"
    }
  }'

# Consultar
curl https://appvoren.com.br/api/public/v1/subscriptions/<id> -u "$KEY_ID:$KEY_SECRET"

# Cancelar (idempotente)
curl -X DELETE https://appvoren.com.br/api/public/v1/subscriptions/<id> -u "$KEY_ID:$KEY_SECRET"

# Alterar o valor de uma assinatura ativa (recria o split automaticamente)
curl -X PATCH https://appvoren.com.br/api/public/v1/subscriptions/<id> \
  -u "$KEY_ID:$KEY_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"price_cents": 6990}'

PATCH /v1/subscriptions/{id} altera o valor da recorrência usando o cartão já salvo do cliente: a assinatura é recriada no gateway com o split e as taxas vigentes e a resposta traz recreated e previous_subscription_id. Nada é perdido — o registro antigo continua consultável com o vínculo para o novo, e o histórico de cobranças permanece intacto. Se o cartão salvo não estiver disponível, a resposta traz manual_required: true e nossa equipe é alertada automaticamente.

Cobranças seguintes são geradas automaticamente e confirmadas pela Voren em tempo real — acompanhe pelos webhooks subscription.charged e subscription.canceled. Filtros da listagem: status, external_reference, limit (máx 100), cursor.

Webhooks

GET/v1/webhooksscope: webhooks.manage
POST/v1/webhooksscope: webhooks.manage
PATCH/v1/webhooks/{id}scope: webhooks.manage
DELETE/v1/webhooks/{id}scope: webhooks.manage
POST/v1/webhooks/{id}/testscope: webhooks.manage

Eventos: sale.paid, sale.failed, sale.refunded, sale.chargeback, sale.canceled, subscription.created, subscription.charged, subscription.canceled, subscription.updated, test.ping. Use * para assinar todos.

Verificar assinatura de webhook

Cada entrega inclui o header X-Voren-Signature: t=<timestamp>,v1=<sha256_hex>. Calcule HMAC-SHA256 sobre ${timestamp}.${raw_body} com seu secret e compare em tempo constante. Rejeite entregas com timestamp com mais de 5 minutos.

// Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

function verifyVorenSignature(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
  const timestamp = parts.t;
  const v1 = parts.v1;
  if (!timestamp || !v1) return false;
  if (Math.abs(Date.now()/1000 - Number(timestamp)) > 300) return false;
  const expected = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  const a = Buffer.from(v1, "hex");
  const b = Buffer.from(expected, "hex");
  return a.length === b.length && timingSafeEqual(a, b);
}
Precisa de ajuda? Escreva para api@appvoren.com.br.