> ## Documentation Index
> Fetch the complete documentation index at: https://docs.velan.app/llms.txt
> Use this file to discover all available pages before exploring further.

# API de pagamentos (links de cobrança)

> Crie pelo seu sistema um link de pagamento de uma oferta ou de um plano de assinatura, receba a URL na hora e acompanhe o pagamento pela API e pelos webhooks.

A API de pagamentos deixa o **seu sistema** (ERP, CRM, bot de atendimento, área do cliente) gerar uma cobrança na Velan e receber o **link de pagamento** para enviar ao comprador. O link abre o checkout da Velan da oferta ou do plano, com os dados do comprador já preenchidos. Quando ele paga, você fica sabendo pela API e pelos webhooks, com a sua referência.

**Endereço da API:** `https://open.velan.app/v1`

<Note>
  A API de pagamentos é separada do painel: usa um endereço próprio, chaves próprias e não lê dados do dash. Uma chave de pagamentos não funciona em `api.velan.app`, e uma chave de leitura (MCP) não funciona aqui.
</Note>

## Como funciona

<Steps>
  <Step title="Crie uma chave de pagamentos">
    No painel, vá em **Configurações → Chaves de API** e clique em **Criar chave de pagamento**. Copie a chave (`vk_live_…`): ela aparece uma única vez. Veja o passo a passo em [Gerar a chave de API](#gerar-a-chave-de-api).
  </Step>

  <Step title="Crie o link pela API">
    Seu sistema chama `POST /v1/payment-links` com o código da oferta ou do plano, os dados do comprador e a sua referência (`external_reference`).
  </Step>

  <Step title="Envie o link ao comprador">
    A resposta traz a `url` e a `short_url` (curta, boa para WhatsApp e SMS). O comprador abre o checkout com nome, e-mail, telefone e CPF preenchidos.
  </Step>

  <Step title="Receba a confirmação">
    O webhook `order.paid` chega com `external_reference` e `payment_link`. Você também pode consultar `GET /v1/payment-links/{id}`.
  </Step>
</Steps>

## Gerar a chave de API

A chave identifica a sua empresa em todas as chamadas. Só o **dono da empresa** cria e revoga chaves.

<Steps>
  <Step title="Abra Chaves de API">
    No [painel da Velan](https://dash.velan.app/dashboard/settings/api-keys), vá em **Configurações → Chaves de API**. No bloco **Chaves de pagamento**, clique em **Criar chave de pagamento**, ou em **Chave de teste** para o ambiente de teste.
  </Step>

  <Step title="Dê um nome">
    Use um nome que diga onde a chave será usada (por exemplo, "ERP da empresa"). Marque **Chave de teste** se quiser integrar sem cobrar ninguém.

    <Frame>
      <img src="https://mintcdn.com/velanapp/3Z2PWstu4Xoo1r7v/images/api-pagamentos-nova-chave.png?fit=max&auto=format&n=3Z2PWstu4Xoo1r7v&q=85&s=190a21d7b28153278b3bb8bb5db99a1b" alt="Modal Nova chave de pagamento com o campo Nome e a opção Chave de teste" width="1024" height="700" data-path="images/api-pagamentos-nova-chave.png" />
    </Frame>
  </Step>

  <Step title="Copie e guarde">
    Clique em **Criar chave** e copie o valor (`vk_live_…` ou `vk_test_…`). **Ele aparece uma única vez**: depois o painel mostra só o início mascarado. Se perder, revogue e crie outra.
  </Step>

  <Step title="Teste a chave">
    Liste as suas ofertas. Resposta `200` significa que a chave está funcionando:

    ```bash theme={null}
    curl https://open.velan.app/v1/offers \
      -H "Authorization: Bearer $VELAN_KEY"
    ```
  </Step>
</Steps>

**Boas práticas**

* Guarde a chave em variável de ambiente ou cofre de segredos do seu servidor. **Nunca** coloque a chave em site, app ou qualquer código que roda no navegador ou no celular do cliente.
* Use uma chave por sistema (ERP, CRM, bot). Assim você revoga uma sem derrubar as outras.
* A chave não expira. Para trocar sem parar a integração: crie a nova, atualize o seu sistema e só então revogue a antiga.
* Cada empresa pode ter até 10 chaves ativas, somando as de leitura e as de pagamentos.
* A coluna **Último uso** no painel mostra se a chave está sendo usada. Revogar é imediato.

## Ambiente de teste

Para integrar sem cobrar ninguém, crie uma **chave de teste**: em **Configurações → Chaves de API**, no bloco **Chaves de pagamento**, clique em **Chave de teste**. Ela começa com `vk_test_` e usa o mesmo endereço `https://open.velan.app/v1`.

* Links criados com chave de teste têm `"livemode": false` e abrem a **página de simulação** (`https://pay.velan.app/teste/{id}`), não o checkout. Nada é cobrado.
* Na página, os botões **Simular pagamento aprovado**, **recusado** ou **expirado** disparam os seus webhooks como se fosse um pedido real, com `"test": true` e `"sandbox": true` no corpo, e com o seu `external_reference`.
* Para testes automatizados, use `POST /v1/payment-links/{id}/simulate` com `{"outcome": "paid" | "failed" | "expired"}`.
* A simulação não cria pedido, não entra no financeiro e só é entregue a integrações do tipo webhook (Webhook, Make, SNS, Supabase). Área de membros e nota fiscal nunca recebem eventos de teste.
* Chave de teste só enxerga links de teste, e chave de produção só links de produção. Um link de teste nunca é aceito no checkout real.

```bash theme={null}
curl https://open.velan.app/v1/payment-links/jW20RTHT6hN2-x3AQoaSRg/simulate \
  -H "Authorization: Bearer $VELAN_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"outcome": "paid"}'
```

<Tip>
  Todos os endpoints estão na [Referência da API de pagamentos](/api-reference/links-de-pagamento/criar-link-de-pagamento), com o botão **Try it** para testar direto na documentação. A especificação OpenAPI 3.1 fica em `https://open.velan.app/v1/openapi.json`, pronta para importar no Postman ou no Insomnia.
</Tip>

## Autenticação

Envie a chave no cabeçalho de toda requisição:

```bash theme={null}
Authorization: Bearer vk_live_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

A chave pertence a uma empresa e só enxerga as ofertas, os planos e os links dessa empresa. Chaves `vk_live_…` são de produção e `vk_test_…` são do [ambiente de teste](#ambiente-de-teste). Revogue a chave no painel a qualquer momento: o bloqueio é imediato.

## Criar um link de pagamento

`POST /v1/payment-links`

```bash theme={null}
curl https://open.velan.app/v1/payment-links \
  -H "Authorization: Bearer $VELAN_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: fatura-2026-0042" \
  -d '{
    "offer_code": "8255qmu6",
    "customer": {
      "name": "Maria Souza",
      "email": "maria@exemplo.com",
      "phone": "5511999998888",
      "document": "52998224725"
    },
    "external_reference": "fatura-2026-0042",
    "metadata": { "crm_id": "8812" },
    "expires_at": "2026-10-20T23:59:59-03:00"
  }'
```

| Campo | Tipo | Descrição |
| - | - | - |
| `offer_code` | string | Código da oferta. Envie este **ou** `plan_code`. |
| `plan_code` | string | Código do plano de assinatura. O comprador assina pelo checkout do plano. |
| `customer` | objeto | Opcional. `name`, `email`, `phone` (DDI + DDD + número) e `document` (CPF ou CNPJ). Preenchem o checkout. |
| `external_reference` | string | Opcional, até 128 caracteres. Sua referência (fatura, pedido do ERP). Volta na API e nos webhooks. |
| `metadata` | objeto | Opcional. Até 20 pares chave/valor de texto. Volta na API e nos webhooks. |
| `coupon_code` | string | Opcional. Cupom da empresa aplicado no checkout. |
| `single_use` | booleano | Padrão `true`: depois de pago, o link não aceita novo pagamento. |
| `expires_at` | data ISO 8601 | Opcional. Padrão: 7 dias. Máximo: 90 dias. |

**Resposta `201`:**

```json theme={null}
{
  "object": "payment_link",
  "id": "jW20RTHT6hN2-x3AQoaSRg",
  "status": "active",
  "type": "offer",
  "offer_code": "8255qmu6",
  "plan_code": null,
  "url": "https://pay.velan.app/8255qmu6?plink=jW20RTHT6hN2-x3AQoaSRg",
  "short_url": "https://pay.velan.app/r/fcgbvr3b",
  "customer": {
    "name": "Maria Souza",
    "email": "maria@exemplo.com",
    "phone": "+5511999998888",
    "document": "52998224725"
  },
  "external_reference": "fatura-2026-0042",
  "metadata": { "crm_id": "8812" },
  "coupon_code": null,
  "single_use": true,
  "expires_at": "2026-10-21T02:59:59Z",
  "paid_at": null,
  "canceled_at": null,
  "created_at": "2026-10-13T17:36:23Z",
  "order": null
}
```

<Info>
  Os dados do comprador **não vão na URL**. O link carrega só um token, e o checkout busca os dados na Velan. Assim e-mail e CPF não ficam em histórico de navegador, logs ou ferramentas de análise.
</Info>

### Idempotência

Envie o cabeçalho `Idempotency-Key` (até 64 caracteres) para repetir uma requisição com segurança. Se a mesma chave chegar de novo, a API devolve o **mesmo link** com status `200`, sem criar outro. Use algo único do seu lado, como o número da fatura.

## Consultar um link

`GET /v1/payment-links/{id}`

Devolve o link com o `status` atual e, quando existe, o pedido gerado:

```json theme={null}
{
  "id": "jW20RTHT6hN2-x3AQoaSRg",
  "status": "paid",
  "paid_at": "2026-10-13T18:02:11Z",
  "order": {
    "code": "6f0c0a0e-3b7e-4b8e-9b1e-1d2c3a4b5c6d",
    "status": "paid",
    "amount": "1000.00",
    "payment_method": "pix",
    "paid_at": "2026-10-13T18:02:11Z"
  }
}
```

| Status | Significado |
| - | - |
| `active` | Pode ser pago. |
| `paid` | O pedido do link foi pago. |
| `expired` | Passou do `expires_at` sem pagamento. |
| `canceled` | Cancelado por você. |

## Listar links

`GET /v1/payment-links?external_reference=fatura-2026-0042&status=active&limit=20`

| Parâmetro | Descrição |
| - | - |
| `external_reference` | Filtra pela sua referência. |
| `status` | `active`, `paid`, `expired` ou `canceled`. |
| `limit` | De 1 a 100 (padrão 20). |
| `starting_after` | `id` do último link da página anterior, para paginar. |

A resposta é `{"object": "list", "data": [...], "has_more": false}`, do mais recente para o mais antigo.

## Cancelar um link

`POST /v1/payment-links/{id}/cancel`

O comprador passa a ver o aviso de link cancelado e não consegue pagar. Um link já pago não pode ser cancelado (`409`): use o reembolso do pedido no painel.

## Ofertas e planos

Para descobrir os códigos sem abrir o painel:

* `GET /v1/offers`: ofertas da empresa com `code`, `name`, `status`, `price` e produto.
* `GET /v1/plans`: planos com `code`, `name`, `status`, `price` e `billing_cycle`.

Só ofertas e planos com `status` `active` aceitam link de pagamento.

## Webhooks

Os eventos de pedido (`order.paid`, `order.created`, `order.failed`, `order.expired` e os demais) de um pedido feito por link trazem a sua referência em dois lugares:

```json theme={null}
{
  "data": {
    "purchase": {
      "tracking": { "external_reference": "fatura-2026-0042" },
      "payment_link": {
        "code": "jW20RTHT6hN2-x3AQoaSRg",
        "external_reference": "fatura-2026-0042",
        "metadata": { "crm_id": "8812" }
      }
    }
  }
}
```

Em pedidos que não vieram de link, `external_reference` e `payment_link` chegam como `null`. Veja como configurar em [Webhook](/integrations/webhook).

<Note>
  Para assinaturas, o link cobre a **primeira compra** do plano. As renovações seguem a régua normal da assinatura e chegam como `order.paid` da mesma assinatura, sem `external_reference`. Guarde o código da assinatura do primeiro pedido para relacionar as renovações.
</Note>

## Erros

Todo erro segue o mesmo formato:

```json theme={null}
{
  "error": {
    "type": "validation_error",
    "message": "Oferta não encontrada nesta empresa.",
    "fields": { "offer_code": ["Oferta não encontrada nesta empresa."] }
  }
}
```

| HTTP | `type` | Quando |
| - | - | - |
| 400 | `validation_error` | Campo inválido ou ausente. `fields` diz qual. |
| 401 | `authentication_error` | Chave ausente, inválida ou revogada. |
| 403 | `permission_error` | Chave sem acesso à API de pagamentos. |
| 404 | `not_found` | Link não encontrado nesta empresa. |
| 409 | `conflict` | Ação impossível no estado atual, como cancelar um link pago. |
| 429 | `rate_limited` | Limite de requisições atingido. `retry_after_seconds` diz quanto esperar. |

## Limites

| Operação | Limite por chave |
| - | - |
| Todas as requisições | 300 por minuto |
| Criar e cancelar links | 60 por minuto |
| Validade de um link | até 90 dias |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.