Referência da API
Criar cupom
Cria um cupom de desconto. **V2:** o cupom só funciona nas Offers da whitelist (passe `offerIds` para já criar com a whitelist preenchida). Códigos são normalizados pra UPPERCASE — `verao2026` é salvo como `VERAO2026`. Cupom só tem efeito em Offers recorrentes (que geram Invoice). QuickLink e Checkout one-time **não aceitam cupom**. A chave de API precisa da permissão `isInvoiceAvailable`.
/v2/couponsdiscountValue Quando `discountType: PERCENTAGE`, `discountValue` está em **basis points** (1.000 = 10%, 10.000 = 100%). Quando `discountType: FIXED`, está em **centavos** (100 = R$ 1,00). `minPurchaseAmount` também é em centavos. Veja a seção 'Cupons (visão geral)' para a tabela completa.Autenticação
HTTP Basic Auth. Envie sua secret key no header:
Authorization: Basic {base64(secret:SUA_SECRET_KEY)}Parâmetros
codestringobrigatórioCódigo do cupom. 3-30 caracteres. Aceita apenas `A-Z`, `0-9`, `-` e `_`. É normalizado pra UPPERCASE no servidor (você pode mandar minúsculo). Único por conta.
Exemplo: VERAO2026
descriptionstringopcionalDescrição interna do cupom (não é mostrada ao cliente).
Exemplo: Promoção de verão — 15% off
discountType'PERCENTAGE' | 'FIXED'obrigatórioTipo do desconto. `PERCENTAGE` desconta uma porcentagem; `FIXED` desconta um valor absoluto. Esse campo fica **imutável** depois do primeiro uso do cupom.
Exemplo: PERCENTAGE
discountValuenumber (integer)obrigatórioValor do desconto. Para `PERCENTAGE`: basis points entre 1 (0,01%) e 10.000 (100%). Para `FIXED`: centavos entre 100 (R$ 1,00) e 5.000.000 (R$ 50.000,00). Imutável após primeiro uso.
Exemplo: 1500
maxUsesnumber | nullopcionalLimite global de usos do cupom. `null` ou ausente = ilimitado. O limite é respeitado mesmo em corrida de checkouts simultâneos. Máximo: 1.000.000.
Exemplo: 500
validFromstring (ISO 8601)opcionalData a partir da qual o cupom é válido. Pode estar no passado (não é validado). Se omitido, é válido desde sempre.
Exemplo: 2026-06-01T00:00:00.000Z
validUntilstring (ISO 8601)opcionalData de expiração. **Não pode estar no passado** — o servidor rejeita com 400. Se omitido, o cupom nunca expira.
Exemplo: 2026-12-31T23:59:59.000Z
minPurchaseAmountnumber | nullopcionalValor mínimo da fatura (em **centavos**) para o cupom ser aplicável. `null` = sem mínimo. Ex: `5000` = R$ 50,00.
Exemplo: 5000
offerIdsstring[]opcionalIDs das Offers onde o cupom funciona. Sem isso, o cupom existe mas não funciona em lugar nenhum até você adicionar via `POST /v2/coupons/:id/offers`. Todos os IDs precisam pertencer à sua conta.
Exemplo: ["off_plano_mensal", "off_plano_anual"]
metadataobjectopcionalObjeto JSON arbitrário para anexar informação do seu sistema.
Exemplo: { "campaign": "summer-launch" }
Body da requisição
Content-Type: application/json
// Cupom de 15% off, válido até 31/dez, mínimo R$ 50:
{
"code": "VERAO2026",
"description": "Promoção de verão — 15% off",
"discountType": "PERCENTAGE",
"discountValue": 1500,
"maxUses": 500,
"validFrom": "2026-06-01T00:00:00.000Z",
"validUntil": "2026-12-31T23:59:59.000Z",
"minPurchaseAmount": 5000,
"offerIds": ["off_plano_mensal", "off_plano_anual"],
"metadata": { "campaign": "summer-launch" }
}
// Cupom de R$ 10 fixo, ilimitado, sem mínimo:
{
"code": "BEM_VINDO",
"discountType": "FIXED",
"discountValue": 1000,
"maxUses": null,
"offerIds": ["off_plano_mensal"]
}Respostas
Cupom criado. Note que o response não inclui a whitelist — para ver as Offers vinculadas, faça GET /v2/coupons/:id.
{
"id": "cpn_a8f3d2e1-9b4c-4e5f-8a7d-2e5f6c1d4b9a",
"createdByUserId": "u-1234",
"code": "VERAO2026",
"description": "Promoção de verão — 15% off",
"discountType": "PERCENTAGE",
"discountValue": 1500,
"status": "ACTIVE",
"maxUses": 500,
"currentUses": 0,
"validFrom": "2026-06-01T00:00:00.000Z",
"validUntil": "2026-12-31T23:59:59.000Z",
"minPurchaseAmount": 5000,
"metadata": { "campaign": "summer-launch" },
"deletedAt": null,
"createdAt": "2026-05-26T14:32:01.923Z",
"updatedAt": "2026-05-26T14:32:01.923Z"
}Erro de validação. Causas comuns: código já existe, formato inválido, discountValue fora do range, validUntil no passado, ou alguma offerId não pertence à sua conta.
{
"status": 400,
"error": "Ja existe um cupom com codigo \"VERAO2026\""
}Exemplos
curl -X POST "https://api.pagnovo.com/v2/coupons" \
-H "Authorization: Basic $(echo -n 'secret:SUA_SECRET_KEY' | base64)" \
-H "Content-Type: application/json" \
-d '{
"code": "VERAO2026",
"discountType": "PERCENTAGE",
"discountValue": 1500,
"maxUses": 500,
"validUntil": "2026-12-31T23:59:59.000Z",
"offerIds": ["off_plano_mensal"]
}'const token = Buffer.from("secret:" + process.env.PAGNOVO_SECRET_KEY).toString("base64");
// 15% off, máximo 500 usos, expira 31/dez
const res = await fetch("https://api.pagnovo.com/v2/coupons", {
method: "POST",
headers: {
Authorization: `Basic ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "verao2026", // será normalizado pra VERAO2026
discountType: "PERCENTAGE",
discountValue: 1500, // 15% (basis points!)
maxUses: 500,
validUntil: "2026-12-31T23:59:59.000Z",
offerIds: ["off_plano_mensal"],
}),
});
if (!res.ok) {
const err = await res.json();
console.error(err.error);
return;
}
const coupon = await res.json();
console.log("criado:", coupon.code, "id:", coupon.id);