Skip to main content
Cada Afiliado divulga Links rastreáveis no formato /t/{token} que registram o Clique e redirecionam o visitante ao destino, e pode ter Cupons de desconto que atribuem comissões em canais sem clique (podcast, evento, rádio). Esta página descreve os tokens globalmente únicos, deep linking, a whitelist de domínios de destino, a reserva de token por 12 meses e a sincronização de cupons com o gateway de pagamento. O vínculo é sempre Link → Afiliado → Programa: um Link pertence a exatamente um Afiliado, que pertence a exatamente um Programa. O mesmo vale para o Cupom. O programId nunca é informado pelo cliente, e sim derivado do Afiliado. Um Link tem o identificador link_ + ULID e os campos principais:

Token globalmente único

O token é único entre todos os Programas e todas as organizações, não apenas dentro de um Programa. Isso é exigido pela rota pública GET /t/{token}, que resolve o Link apenas pelo token e descobre a organização a partir do Link encontrado.
Um token já em uso por outra organização sempre retorna 409 conflict, mesmo que o Link esteja inativo e com a reserva expirada. O conflito nunca revela a existência do Link de outra organização, e tokens de outra organização nunca são liberados.
Tokens são case-insensitive: BRUNO-PROMO e bruno-promo são o mesmo token. A entrada é normalizada para minúsculas antes da validação de unicidade e do redirect. Ao criar um Link sem token custom, o token é gerado a partir do nome do Afiliado: acentos removidos, minúsculas, sequências não alfanuméricas viram hífen, hífens das pontas aparados, corte em 50 caracteres. Slugs com menos de 3 caracteres ganham o prefixo aff- (ex.: “Li” → aff-li). Em caso de colisão, são tentados alguns candidatos com sufixo aleatório (<base>-<sufixo>); esgotadas as tentativas, a criação falha com 409 conflict. Todo Afiliado que entra no status approved recebe automaticamente um Link com isDefault: true, tanto na criação já aprovada quanto na primeira aprovação via mudança de status. A operação é idempotente: se o default já existe (ex.: re-aprovação), o Link existente é retornado sem criar outro. Veja Afiliados.

Sub-campanhas e deep linking

O subId segmenta campanhas do próprio Afiliado. Ele é gravado no Clique como sub-identificador padrão, mas pode ser sobrescrito por Clique via ?sub= no redirect. No momento do Clique, o subId efetivo é ?sub= ?? link.subId. Detalhes do redirect e do registro de Clique vivem em Tracking. O destinationUrl permite enviar o visitante direto para uma página específica. Quando ausente, o redirect usa a landingUrl do Programa como fallback.
O token já identifica o Afiliado na URL /t/{token}. Para sobrescrever a sub-campanha na divulgação, acrescente ?sub=. Exemplo de URL completa de um Afiliado: https://api.userepass.com/t/bruno-promo?sub=newsletter.

Whitelist de domínios de destino

Para impedir que o redirect do Repass seja usado como open redirector, todo destinationUrl precisa apontar para um domínio autorizado da organização. A whitelist é configurada por organização:
  • Cada entrada autoriza o domínio exato e seus subdomínios (example.com autoriza app.example.com).
  • Máximo de 50 domínios; normalizados para minúsculas e deduplicados na gravação.
  • Whitelist vazia ou não configurada ⇒ qualquer destinationUrl é bloqueado com 403 not_allowed.
A validação ocorre na criação e na atualização do Link. Limpar o destinationUrl (enviando null no PATCH) não consulta a whitelist e faz o redirect voltar a usar a landingUrl do Programa.
  • active: Link operacional. /t/{token} registra Clique e redireciona.
  • inactive: Link desativado. /t/{token} responde 410 Gone. O token fica reservado por 12 meses (365 dias exatos) a contar de deactivatedAt. Desativar um Link já inativo retorna 409 conflict.
A desativação é reversível por uma rota dedicada: POST /links/{linkId}/reactivate volta o Link a active e limpa deactivatedAt, desde que o token ainda não tenha sido aposentado e o Afiliado não esteja no teto de Links ativos.
O token é imutável via API. PATCH /links/{linkId} só aceita subId e destinationUrl. Não existe endpoint para trocar o token de um Link; o token só muda quando é aposentado após os 12 meses de reserva.

Reserva de token por 12 meses

Tokens não são reutilizáveis de imediato. O token de um Link inativo da própria organização fica reservado por exatamente 365 dias a partir de deactivatedAt, evitando que um Afiliado herde tráfego residual de outro. Quando um novo Link tenta reivindicar um token já existente, o resultado depende do estado atual do token: Após a expiração, quando outro Link da mesma organização reivindica o token, o Link antigo é aposentado (liberando o slug) e o evento link.token_retired é emitido. Reativar um Link cujo token já foi aposentado retorna 409 conflict: não há mais slug a restaurar. Cada Afiliado pode ter no máximo program.maxLinksPerAffiliate Links no Programa (default 50, configurável por Programa). A contagem considera apenas Links ativos: desativar libera vaga, reativar volta a ocupá-la (a reativação revalida o teto). Exceder o limite retorna 409 conflict. Todas as rotas exigem autenticação (sessão ou API key) e operam no escopo da organização ativa; nenhuma exige papel específico. Veja a aba Referência da API para o contrato completo de cada endpoint.
POST /affiliates/{affiliateId}/links aceita corpo vazio ({}): nesse caso o token é gerado do nome do Afiliado e o Link redireciona para a landingUrl do Programa.
A listagem GET /affiliates/{affiliateId}/links retorna { "data": [...] } com todos os Links do Afiliado (mais recentes primeiro), sem paginação por cursor: o teto prático é o maxLinksPerAffiliate do Programa. Cada mudança de estado emite um evento que você pode assinar via webhook ou consultar pela API de auditoria (veja Event store):

Cupons

O Cupom (coup_ + ULID) é um método de atribuição alternativo para canais sem clique: quando uma Conversão chega informando o código do cupom (mesmo sem nenhum Clique rastreado), a Comissão é atribuída ao Afiliado dono do Cupom. Um Cupom pertence a no máximo um Afiliado; não há Cupom genérico sem dono.
Na criação via API, o desconto percentual é informado como decimal (20 = 20%, 0,01 a 100, duas casas) e convertido para bps na persistência (202000). O desconto fixo é informado e persistido em centavos (inteiro ≥ 1). Na resposta, discountValue volta a decimal para percentuais e permanece em centavos para fixos.

Status do Cupom

  • pending: recém-criado, ainda não sincronizado. Estado inicial, mas a criação encadeia a sincronização imediatamente, então raramente é observável.
  • active: sincronizado com sucesso; único status que atribui Conversões.
  • sync_failed: sincronização com o gateway falhou; o Cupom fica bloqueado até um resync bem-sucedido.
  • inactive: desativado manualmente; não atribui mais.

Sincronização com o gateway

Todo Cupom criado no Repass é sincronizado automaticamente com o gateway de pagamento (como o Stripe) para que o desconto exista de fato no checkout. O Cupom só fica utilizável (active) após a sincronização bem-sucedida. A criação sempre retorna 201, mesmo quando a sincronização falha: você distingue o resultado pelos campos status e providerSync na resposta. O Cupom nasce em pending e emite o evento coupon.created; em seguida o resultado da sincronização (active ou sync_failed) é aplicado, com seu próprio evento. O objeto providerSync traz provider, status (synced ou failed), syncedAt (quando synced) e error (quando failed).
Resposta 201 (sync bem-sucedida)
Resposta 201 (sync falhou)
Um Cupom em sync_failed (ou pending) pode ser reenviado com POST /coupons/{couponId}/resync. Resync de um Cupom active ou inactive é rejeitado com 409 conflict.

Desativação

POST /coupons/{couponId}/deactivate leva qualquer Cupom (≠ inactive) para inactive, define deactivatedAt e propaga a desativação ao gateway. A desativação local acontece independentemente do resultado do gateway. Desativar um Cupom já inactive retorna 409 conflict. Não há reativação de Cupom: uma vez inactive, não há transição de volta.

Atribuição por Cupom

Na Conversão, o código é normalizado para maiúsculas e o Cupom precisa estar active. Cupom inexistente ou em qualquer outro status retorna 404 com param: couponCode. O conflito entre Cupom e Clique é resolvido pela política do Programa (couponAttributionPolicy):
  • coupon_wins (default): 100% ao Afiliado do Cupom.
  • click_wins: 100% ao Afiliado do Clique.
  • split_50_50: divide 5000/5000 bps, com o Cupom como primário.
Os detalhes completos de precedência vivem em Atribuição e Conversões e fraude.

Endpoints de Cupons

A listagem GET /coupons usa paginação por cursor (limit 1 a 100, default 25; starting_after/ending_before) com filtros opcionais program_id, affiliate_id, status.

Eventos de Cupom

Erros comuns

A whitelist de domínios está vazia ou o hostname não está autorizado. Configure a whitelist em PUT /settings/destination-domains antes de usar deep links.
Código duplicado no Programa (param: code), Afiliado banido na criação, resync de Cupom active/inactive, ou desativar um Cupom já inactive.
Afiliado inexistente na organização, Link/Cupom inexistente, ou (na Conversão) couponCode que não corresponde a um Cupom active.
Veja o envelope de erro padrão em Erros e o uso de Idempotency-Key em Idempotência.

Próximos passos

Tracking

Como o redirect /t/{token} registra o Clique e o cookie de visitante.

Atribuição

Janelas, precedência e a política de conflito Cupom × Clique.

Afiliados

Aprovação do Afiliado e criação do Link padrão automático.

Conversões e fraude

Como uma Conversão com couponCode gera a Comissão.