Referência da API

A API da RevRake.

Tudo o que a plataforma de um operador envia à RevRake, o que volta e o que a RevRake envia aos trackers dos afiliados e aos sistemas do próprio operador. Para a ordem em que fazer as coisas, leia o guia de integração.

URLs base

A API tem quatro endpoints assinados para a plataforma do operador, dois de escrita e dois de leitura, e o link de rastreamento que os visitantes abrem. Os endpoints assinados também estão publicados em um arquivo OpenAPI 3.1.

EndpointSignificado
POST https://app.revrake.com/v1/postbackInforma um evento ou um lote de eventos. Assinado.
POST https://app.revrake.com/v1/filesEnvia um arquivo diário REG ou SALES. Assinado.
GET https://app.revrake.com/v1/reportsLê um relatório, em JSON ou CSV. Assinado, com uma chave de operador ou de afiliado.
GET https://app.revrake.com/v1/commissionsLê o livro-razão de comissões, página por página. Assinado, com uma chave de operador ou de afiliado.
GET https://app.revrake.com/v1/affiliatesLista os afiliados da marca. Assinado, com a chave do operador.
GET https://app.revrake.com/v1/linksLista os links de rastreamento. Assinado, com uma chave de operador ou de afiliado.
GET https://app.revrake.com/v1/dealsLista os acordos com os termos em vigor. Assinado, com a chave do operador.
GET https://track.revrake.com/c/{code}O link de rastreamento: grava o clique e redireciona. Não assinado.

Os portais em app.revrake.com usam seus próprios endpoints, atrás de uma sessão autenticada. Eles não são uma API pública e podem mudar sem aviso. Os relatórios, o livro-razão e as linhas de pagamento também podem ser baixados ali em CSV.

Chaves

Há dois tipos de chave. Cada uma tem um ID de chave e um segredo, e todas assinam as requisições da mesma forma. As chaves nunca são compartilhadas: cada parte tem a sua.

O ID da chave começa comDe quem éO que ela pode fazer
pb_Do operador. Uma chave ativa por marca, na página Integração do portal do operador. Um admin do workspace pode ver o segredo e rotacionar a chave.Envia eventos e arquivos. Lê os relatórios e o livro-razão do programa inteiro, ou de um afiliado com affiliate=<number>.
ak_De um afiliado. Cada afiliado cria a sua na aba API do portal do afiliado. O segredo é exibido uma única vez, quando a chave é criada.Lê os relatórios e as comissões desse afiliado, e nada mais. Não pode enviar eventos: /v1/postback e /v1/files respondem 401 unknown_key.

Nunca entregue a chave do operador a um afiliado. Ela lê os números de todos os afiliados e pode gravar eventos. Um afiliado que quer acesso à API emite sua própria chave.

  • Uma nova chave revoga a antiga imediatamente, nos dois tipos. Um afiliado também pode revogar sua chave e ficar sem nenhuma.
  • Uma chave de afiliado só funciona enquanto o afiliado está aprovado no programa. Caso contrário, uma leitura responde 403 affiliate_inactive.
  • Com uma chave de afiliado, o parâmetro affiliate é ignorado e by=affiliate é respondido por dia.
  • O limite de taxa das leituras é contado por chave.

Autenticação

Toda requisição a /v1 é assinada com uma chave. Cada requisição leva três cabeçalhos:

CabeçalhoValor
X-RevRake-KeyO ID da sua chave. Começa com pb_ (operador) ou ak_ (afiliado).
X-RevRake-TimestampO horário Unix atual em segundos. Uma requisição é aceita em até cinco minutos antes ou depois dele.
X-RevRake-SignatureO HMAC-SHA256 em hexadecimal minúsculo, com o seu segredo, de <timestamp>.<body> para um POST e de <timestamp>.<path>?<query> para um GET.

Para assinar uma requisição:

  1. Monte o corpo e guarde os bytes exatos que você vai enviar.
  2. Pegue o horário Unix atual em segundos, como texto decimal.
  3. Concatene o timestamp, um ponto (.) e o corpo.
  4. Calcule o HMAC-SHA256 desse texto com o seu segredo e escreva-o em hexadecimal minúsculo.
  5. Envie o corpo sem alterações, com os três cabeçalhos.

Como assinar uma leitura

Um GET não tem corpo, então a assinatura cobre o alvo da requisição: o caminho e a query exatamente como enviados, por exemplo 1790000000./v1/reports?by=link&days=7. Sem query, é o timestamp, um ponto e o caminho. Monte o endereço uma vez, assine esse texto e envie esse texto: um cliente que reordena ou recodifica a query depois de assinar quebra a assinatura.

  • Primeiro verifica-se a chave, depois o timestamp, depois a assinatura.
  • O cabeçalho Content-Type não é obrigatório. Envie application/json para eventos e text/csv para arquivos.
  • Uma chave rotacionada para de funcionar imediatamente.

Exemplos de assinatura

Cada exemplo tem uma única função de assinatura, envia um cadastro e depois lê um relatório. Substitua o ID da chave e o segredo pelos seus.

curl
KEY="pb_your_key_id"
SECRET="your_secret"
BASE="https://app.revrake.com"

# sign <payload>: hex HMAC-SHA256 of "<timestamp>.<payload>" under the secret
sign() { printf '%s.%s' "$TS" "$1" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //'; }

# Write: the payload is the body.
TS=$(date +%s)
BODY='{"type":"registration","player_id":"PLR-1001","click_id":"CID_FROM_LANDING_URL"}'
curl -X POST "$BASE/v1/postback" \
  -H "Content-Type: application/json" \
  -H "X-RevRake-Key: $KEY" \
  -H "X-RevRake-Timestamp: $TS" \
  -H "X-RevRake-Signature: $(sign "$BODY")" \
  -d "$BODY"

# Read: the payload is the path and the query, exactly as sent.
TS=$(date +%s)
TARGET='/v1/reports?by=link&days=7'
curl "$BASE$TARGET" \
  -H "X-RevRake-Key: $KEY" \
  -H "X-RevRake-Timestamp: $TS" \
  -H "X-RevRake-Signature: $(sign "$TARGET")"
Node.js
import { createHmac } from 'node:crypto';

const KEY = 'pb_your_key_id';
const SECRET = 'your_secret';
const BASE = 'https://app.revrake.com';

// hex HMAC-SHA256 of "<timestamp>.<payload>" under the secret
function sign(secret, timestamp, payload) {
  return createHmac('sha256', secret).update(`${timestamp}.${payload}`).digest('hex');
}

function headers(payload) {
  const ts = Math.floor(Date.now() / 1000).toString();
  return { 'X-RevRake-Key': KEY, 'X-RevRake-Timestamp': ts, 'X-RevRake-Signature': sign(SECRET, ts, payload) };
}

// Write: the payload is the body. Send the same string you signed.
const body = JSON.stringify({ type: 'registration', player_id: 'PLR-1001', click_id: 'CID_FROM_LANDING_URL' });
const write = await fetch(`${BASE}/v1/postback`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', ...headers(body) },
  body,
});
console.log(write.status, await write.json());

// Read: the payload is the path and the query, exactly as sent.
const target = '/v1/reports?by=link&days=7';
const read = await fetch(BASE + target, { headers: headers(target) });
console.log(read.status, await read.json());
Python
import hashlib
import hmac
import json
import time
import urllib.error
import urllib.request

KEY = "pb_your_key_id"
SECRET = "your_secret"
BASE = "https://app.revrake.com"


def sign(secret: str, timestamp: str, payload: bytes) -> str:
    """hex HMAC-SHA256 of "<timestamp>.<payload>" under the secret"""
    return hmac.new(secret.encode(), timestamp.encode() + b"." + payload, hashlib.sha256).hexdigest()


def call(method: str, target: str, body: bytes | None = None):
    ts = str(int(time.time()))
    # Write: the payload is the body. Read: the path and the query, as sent.
    payload = body if body is not None else target.encode()
    headers = {"X-RevRake-Key": KEY, "X-RevRake-Timestamp": ts, "X-RevRake-Signature": sign(SECRET, ts, payload)}
    if body is not None:
        headers["Content-Type"] = "application/json"
    req = urllib.request.Request(BASE + target, data=body, method=method, headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=10) as res:
            return res.status, json.load(res)
    except urllib.error.HTTPError as e:
        return e.code, json.load(e)


event = {"type": "registration", "player_id": "PLR-1001", "click_id": "CID_FROM_LANDING_URL"}
print(*call("POST", "/v1/postback", json.dumps(event).encode()))
print(*call("GET", "/v1/reports?by=link&days=7"))
PHP
<?php
const KEY = 'pb_your_key_id';
const SECRET = 'your_secret';
const BASE = 'https://app.revrake.com';

// hex HMAC-SHA256 of "<timestamp>.<payload>" under the secret
function sign(string $secret, string $timestamp, string $payload): string
{
    return hash_hmac('sha256', $timestamp . '.' . $payload, $secret);
}

function call(string $method, string $target, ?string $body = null): array
{
    $ts = (string) time();
    // Write: the payload is the body. Read: the path and the query, as sent.
    $payload = $body ?? $target;
    $headers = [
        'X-RevRake-Key: ' . KEY,
        'X-RevRake-Timestamp: ' . $ts,
        'X-RevRake-Signature: ' . sign(SECRET, $ts, $payload),
    ];
    $ch = curl_init(BASE . $target);
    curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => $method, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10]);
    if ($body !== null) {
        $headers[] = 'Content-Type: application/json';
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
    }
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    $answer = curl_exec($ch);
    return [curl_getinfo($ch, CURLINFO_RESPONSE_CODE), json_decode($answer, true)];
}

$event = ['type' => 'registration', 'player_id' => 'PLR-1001', 'click_id' => 'CID_FROM_LANDING_URL'];
print_r(call('POST', '/v1/postback', json_encode($event)));
print_r(call('GET', '/v1/reports?by=link&days=7'));
Go
package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

const (
	key    = "pb_your_key_id"
	secret = "your_secret"
	base   = "https://app.revrake.com"
)

// sign is the hex HMAC-SHA256 of "<timestamp>.<payload>" under the secret.
func sign(secret, timestamp string, payload []byte) string {
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(timestamp + "."))
	mac.Write(payload)
	return hex.EncodeToString(mac.Sum(nil))
}

func call(method, target string, body []byte) (int, string, error) {
	ts := strconv.FormatInt(time.Now().Unix(), 10)
	// Write: the payload is the body. Read: the path and the query, as sent.
	payload := body
	if body == nil {
		payload = []byte(target)
	}
	req, err := http.NewRequest(method, base+target, bytes.NewReader(body))
	if err != nil {
		return 0, "", err
	}
	req.Header.Set("X-RevRake-Key", key)
	req.Header.Set("X-RevRake-Timestamp", ts)
	req.Header.Set("X-RevRake-Signature", sign(secret, ts, payload))
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	res, err := (&http.Client{Timeout: 10 * time.Second}).Do(req)
	if err != nil {
		return 0, "", err
	}
	defer res.Body.Close()
	answer, err := io.ReadAll(res.Body)
	return res.StatusCode, string(answer), err
}

func main() {
	event := []byte(`{"type":"registration","player_id":"PLR-1001","click_id":"CID_FROM_LANDING_URL"}`)
	fmt.Println(call("POST", "/v1/postback", event))
	fmt.Println(call("GET", "/v1/reports?by=link&days=7", nil))
}

Vetor de teste

Com estas entradas, seu código deve produzir exatamente estas assinaturas:

Segredo
test_secret
Timestamp
1791072000
Corpo
{"type":"registration","player_id":"PLR-1001","btag":"1001_1"}
Assinatura
115d8c73c6069d8953a36ca3a68c114da6232e4ba31889fd519d41ab1350a603
Alvo da requisição de uma leitura
/v1/reports?by=link&days=7
Assinatura da leitura
ed1ed72dd7ddef22c762f63b8919011e7c36c7214b55b72693c5927fb4b911d0

Limites

Estes são todos os limites que o produto aplica hoje a integrações, rastreamento e afiliados, com seus números. Login, redefinição de senha e candidaturas também têm limites contra abuso.

LimiteSignificado
120Leituras por chave, por minuto: GET /v1/reports e GET /v1/commissions somados
30 000Requisições de eventos por chave, por minuto: POST /v1/postback, até 1.000 de uma vez
60Arquivos por chave, por minuto: POST /v1/files, até 10 de uma vez
4 MBCorpo de POST /v1/postback, um evento ou um lote
32 MBCorpo de POST /v1/files
1 – 1000Eventos em um lote
200 000Linhas de dados em um arquivo
300 sDiferença entre X-RevRake-Timestamp e o horário real, para mais ou para menos
24 hAté quanto no futuro ts e period podem estar
100Tamanho de player_id, em caracteres
200Tamanho de txn_id, em caracteres
0 / 2Casas decimais de um valor: as da moeda (duas na maioria, nenhuma em moedas como XOF, UGX ou JPY)
1 – 365days de um relatório
10, 25, 50, 100, 500, 1000per_page do livro-razão: um destes valores
1 – 100 000page do livro-razão
100Tamanho de q, em caracteres; um texto mais longo é cortado
200Tamanho de sub1 a sub5, de cada tag UTM e de cada click ID de anúncio em um link de rastreamento, em caracteres; valores mais longos são cortados
10Destinos de postback por afiliado
2000Tamanho de um template de endereço de postback, em caracteres
5 sTempo que um tracker tem para responder a um postback
50Log de entrada exibido no portal: entradas mais recentes dos últimos 7 dias
2000Tamanho do endereço de um endpoint de webhook, em caracteres
5 sTempo que um endpoint de webhook tem para responder
20Tentativas de webhook falhas seguidas antes de o endpoint ser desligado
100Entregas por endpoint de webhook exibidas no log de entregas
2 – 40Tamanho de um código promocional: letras, dígitos, hifens e sublinhados
32 MBTamanho de um arquivo que a RevRake busca no seu endereço
60 sTempo que o seu endereço tem para entregar um arquivo buscado
0 – 3600 sSegundos após o clique para a regra de tempo de cadastro (0 a desativa)
0 – 365Dias até que um alerta de fraude aberto se decida sozinho (0 significa nunca)
1 – 200Alertas liberados ou confirmados numa única decisão em lote
0 – 10 000 (20)Cliques por minuto em um link a partir de um endereço antes de o alerta de rajada de cliques ser criado (0 desativa; 20 se você não mudar)
60 + 1 / minLinks de rastreamento que um afiliado pode criar: 60 de uma vez, depois um por minuto. Além disso a resposta é 429 com Retry-After
2 MBTamanho de uma imagem de banner enviada pelo portal: PNG, JPEG, GIF ou WebP

Os dois endpoints de escrita, POST /v1/postback e POST /v1/files, têm limite por chave (veja a tabela). Toda resposta traz X-RateLimit-Limit e X-RateLimit-Remaining; uma recusa é 429 com Retry-After. Envie no ritmo dos seus eventos e use lotes para uma carga retroativa. O link de rastreamento não tem limite de taxa; uma rajada incomum de cliques em um link a partir de um mesmo endereço gera um alerta de fraude e ainda é redirecionada.

O limite de taxa das leituras

  • Cada chave pode fazer 120 leituras por minuto. A cota é um balde de 120: uma leitura consome uma unidade, e uma unidade volta a cada meio segundo. Uma chave que ficou parada pode fazer 120 leituras de uma vez; depois disso, duas por segundo.
  • Toda resposta de leitura traz X-RateLimit-Limit (sempre 120) e X-RateLimit-Remaining (quantas leituras restam neste momento).
  • Acima do limite, a resposta é 429 rate_limited com um cabeçalho Retry-After: os segundos a esperar. Espere e envie a mesma requisição de novo, assinada com um timestamp novo.
  • O limite é contado por chave: a chave do operador e a chave de cada afiliado têm sua própria cota.
HTTP/1.1 200 OK
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
Retry-After: 1

{"error":"rate_limited","message":"Too many requests: at most 120 reads a minute per key. One more is allowed every half second; wait the seconds in Retry-After and try again."}

Como tratar um 429

// read(target) signs one GET with a fresh timestamp and sends it,
// as in the signing samples.
async function readWithRetry(target, tries = 5) {
  for (let i = 0; i < tries; i++) {
    const res = await read(target);
    if (res.status !== 429) return res;
    const seconds = Number(res.headers.get('Retry-After')) || 1;
    await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
  }
  throw new Error('still rate limited after ' + tries + ' tries');
}

POST /v1/postback

Informa um evento da plataforma do operador. O corpo é um objeto JSON com um type e os campos desse tipo, ou um array JSON desses objetos (um lote).

POST https://app.revrake.com/v1/postback
  • type é um destes: registration, deposit, chargeback, ngr.
  • Um campo que não esteja listado para o tipo é recusado com 422, então um erro de digitação nunca é ignorado em silêncio.
  • ts é o horário do evento no formato RFC 3339 (2026-10-04T08:15:00Z). Se for omitido, usa-se o horário da requisição. Ele precisa trazer um deslocamento UTC (Z ou +03:00); um horário sem deslocamento é recusado. Ele não pode estar mais de um dia no futuro.
  • Um valor é um decimal, como texto JSON ou número JSON: "25.00" ou 25.5. Sem separador de milhar, sem expoente e sem mais casas decimais do que a moeda tem.
  • currency é um código ISO 4217 de três letras. Minúsculas são aceitas. Se for omitido, usa-se a moeda de relatório do workspace.
  • Os espaços no início e no fim dos valores de texto são removidos, exceto em click_id e btag: envie-os exatamente como chegaram à landing page.

registration

Um novo jogador. Envie click_id, btag, promo_code, ou vários deles; sem nenhum, o jogador é registrado como não atribuído. O jogador é atribuído uma única vez, aqui. Resposta: attribution.

{"type":"registration","player_id":"PLR-1001","click_id":"01K6QF3M8Z5T7W9XB2C4D6E8GH","btag":"1001_1"}
CampoTipoObrigatórioExemploSignificado
player_idstringsim"PLR-1001"Seu ID para o jogador. Até 100 caracteres. É a chave do evento.
click_idstringnão"01K6QF3M8Z5T7W9XB2C4D6E8GH"O cid com que o jogador chegou: 26 caracteres, letras e dígitos; maiúsculas ou minúsculas.
btagstringnão"1001_1"O btag que sua plataforma guardou no jogador: <número do afiliado>_<número do link>, só dígitos.
promo_codestringnão"GARAGE50"O código promocional que o jogador digitou no cadastro. Não diferencia maiúsculas de minúsculas. Ele atribui o jogador apenas quando click_id e btag não o fazem.
email_hashstringnão"9f86d081…0f00a08"SHA-256 do e-mail em minúsculas, em hexadecimal. Usado apenas pela verificação de autoindicação.
email_domainstringnão"example.com"O domínio do e-mail do jogador, a parte depois do @ (example.com). Usado apenas pela regra de e-mail descartável. Envie só o domínio: se chegar um endereço completo, só o domínio fica no jogador, mas o log de entrada ainda mostra a requisição como chegou.
ipstringnão"203.0.113.7"O endereço IP no cadastro. Ele é convertido em hash e usado apenas pela verificação de autoindicação e para encontrar o clique correspondente a um btag.
countrystringnão"KE"O país do jogador: duas letras, ISO 3166-1 alpha-2 (GB, KE). Minúsculas são aceitas. Um valor que não tem duas letras deixa o jogador sem país. Sem o campo, vale o país do clique que trouxe o jogador. Um deal pode listar países pelos quais não paga.
tsstringnão"2026-10-04T08:15:00Z"Quando o jogador se cadastrou. A janela de atribuição é medida a partir dele.
{
  "status": "accepted",
  "event_type": "registration",
  "event_id": "0199b2c4-6f1e-7a53-9d0c-2b8f4e1a7c33",
  "attribution": {
    "attributed": true,
    "reason": "click",
    "affiliate_number": 1001,
    "link_number": 1,
    "click_id": "01K6QF3M8Z5T7W9XB2C4D6E8GH",
    "policy_version": 1,
    "window_days": 30,
    "model": "last_click"
  }
}

deposit

Envie um evento deposit para cada depósito, com seu ID de transação em txn_id. O deal define se o mínimo para CPA é comparado ao primeiro depósito ou aos depósitos acumulados, descontados os chargebacks. A qualificação cria uma comissão CPA por jogador, em retenção.

{"type":"deposit","player_id":"PLR-1001","txn_id":"TX-88213","amount":"25.00","currency":"KES","first":true}
CampoTipoObrigatórioExemploSignificado
player_idstringsim"PLR-1001"Seu ID para o jogador. Até 100 caracteres.
txn_idstringsim"TX-88213"Seu ID de transação. Até 200 caracteres. É a chave do evento.
amountstring | numbersim"25.00"O depósito, acima de zero.
currencystringnão"KES"A moeda do depósito. Qualquer moeda é aceita.
tsstringnão"2026-10-04T08:20:00Z"Quando o depósito foi feito. Ele decide qual versão do deal e a taxa de câmbio de qual dia se aplicam.
firstbooleannãotruetrue se este é o primeiro depósito do jogador. Se omitido, o depósito conta como primeiro se a RevRake não conhecer nenhum anterior do jogador.
{
  "status": "accepted",
  "event_type": "deposit",
  "event_id": "0199b2c9-03aa-7e10-b6d2-91c07f5e2a18",
  "commission": {
    "effect": "created",
    "type": "cpa",
    "amount_minor": 4000,
    "status": "hold"
  }
}

chargeback

Envie um evento chargeback com seu próprio txn_id e o txn_id do depósito original em ref_txn_id. Ele reverte o depósito inteiro. No CPA acumulado, a comissão só é retirada se o total qualificado restante ficar abaixo do mínimo: uma comissão em retenção é cancelada; uma aprovada ou paga recebe uma linha de estorno.

{"type":"chargeback","txn_id":"CB-5001","ref_txn_id":"TX-88213"}
CampoTipoObrigatórioExemploSignificado
txn_idstringsim"CB-5001"Seu ID para o próprio chargeback. Até 200 caracteres. É a chave do evento.
ref_txn_idstringsim"TX-88213"O txn_id do depósito que sofreu chargeback.
player_idstringnão"PLR-1001"Opcional. Se informado, deve corresponder ao jogador do depósito referenciado.
tsstringnão"2026-10-09T11:00:00Z"Quando o chargeback aconteceu.
{
  "status": "accepted",
  "event_type": "chargeback",
  "event_id": "0199cc10-7b2d-7c41-a3f0-5d2e8b9c1f47",
  "commission": {
    "effect": "cancelled",
    "type": "cpa",
    "amount_minor": 4000,
    "status": "rejected"
  }
}

ngr

A receita líquida de jogo (NGR) de um jogador em um dia ou em um mês. Ela rende o RevShare da versão do deal em vigor no fim do período, em retenção. O jogador precisa estar cadastrado. Resposta: commission.

{"type":"ngr","player_id":"PLR-1001","period":"2026-10-03","amount":"118.40","currency":"KES"}
CampoTipoObrigatórioExemploSignificado
player_idstringsim"PLR-1001"Seu ID para o jogador. Até 100 caracteres.
periodstringsim"2026-10-03"O dia (YYYY-MM-DD) ou o mês (YYYY-MM) a que o valor se refere. Um mês e o seu primeiro dia são períodos diferentes. Não pode estar mais de um dia no futuro.
amountstring | numbersim"118.40"A receita líquida de jogo (NGR) do período. Negativa quando o jogador ganhou.
currencystringnão"KES"A moeda do valor.
ggrstring | numbernão"240.00"Opcional. A receita bruta de jogo do período, na mesma moeda. Mostrada ao afiliado; não altera a comissão.
bonusesstring | numbernão"80.00"Opcional. Bônus descontados da receita bruta. Apenas mostrado.
feesstring | numbernão"21.60"Opcional. Taxas descontadas da receita bruta, como taxas de pagamento ou de plataforma. Apenas mostrado.
taxesstring | numbernão"20.00"Opcional. Impostos descontados da receita bruta. Apenas mostrado.
tsstringnão"2026-10-04T02:00:00Z"Quando o valor foi informado.
{
  "status": "accepted",
  "event_type": "ngr",
  "event_id": "0199b2d1-5c0e-7b42-8a17-3e9d6f0c4b21",
  "commission": {
    "effect": "created",
    "type": "revshare",
    "amount_minor": 2960,
    "status": "hold"
  }
}

Um click_id, btag, promo_code ou player_id que contém { ou } é recusado: isso significa que a sua plataforma enviou um placeholder sem preenchê-lo.

A resposta

Um evento que foi gravado, ou que já estava gravado, é respondido com 200 e um objeto JSON. Os campos que não se aplicam são omitidos.

CampoTipoSignificado
statusstringaccepted, ou duplicate quando o evento já estava gravado.
event_typestringO type que você enviou.
event_idstringO ID da RevRake para o evento gravado, um UUID. É o mesmo em toda repetição.
attributionobjectPara um cadastro: a decisão.
attribution.attributedbooleanSe o jogador pertence a um afiliado.
attribution.reasonstringO motivo. Veja a tabela abaixo.
attribution.affiliate_numberintegerO afiliado. Só quando atribuído.
attribution.link_numberintegerO link do afiliado. Só quando atribuído.
attribution.click_idstringO clique que correspondeu, quando há um.
attribution.policy_versionintegerA versão da política de atribuição da marca que decidiu.
attribution.window_daysintegerA janela de atribuição dessa versão, em dias.
attribution.modelstringlast_click ou first_click: o modelo dessa versão.
commissionobjectPara um depósito, um chargeback e ngr: o que o evento fez no livro-razão.
commission.effectstringcreated, cancelled (uma comissão em retenção foi rejeitada), reversed (uma linha de estorno foi adicionada) ou none.
commission.typestringcpa, revshare, adjustment ou reversal.
commission.amount_minorintegerO valor em unidades menores da moeda do deal (4000 é 40.00). Negativo para um estorno e para um ajuste para baixo.
commission.statusstringhold, approved ou rejected.
commission.reasonstringPor que nada foi criado, quando effect é none. Veja a tabela abaixo.
waiting_for_playerbooleantrue para um depósito cujo jogador ainda não está cadastrado. Ele é guardado e precificado quando o cadastro chegar.
duplicate_ofstringPara uma correspondência antiga de primeiro depósito entre eventos ao vivo e arquivos diários antigos: o event_id anterior. Os resumos SALES atuais usam cobertura diária e preservam cada ID de transação real.
detailstringCom duplicate_of: uma frase dizendo qual depósito anterior este repete.
{
  "status": "accepted",
  "event_type": "deposit",
  "event_id": "0199b2c9-03aa-7e10-b6d2-91c07f5e2a18",
  "waiting_for_player": true
}
{
  "status": "duplicate",
  "event_type": "deposit",
  "event_id": "0199b31f-8c02-7a66-b4e1-2d7f9a3c6e15",
  "duplicate_of": "0199b2c9-03aa-7e10-b6d2-91c07f5e2a18",
  "detail": "The same deposit (TX-88213) was already reported through the API; this one adds nothing."
}
{
  "status": "duplicate",
  "event_type": "deposit",
  "event_id": "0199b2ca-11d4-7f09-8c55-0a6e2d4b7f90",
  "commission": { "effect": "none", "reason": "not_first" }
}

Uma comissão é criada com o status hold. Ela é aprovada automaticamente quando o período de retenção do deal termina, a menos que algo ainda a retenha: um alerta de fraude aberto, um afiliado que não está aprovado ou uma taxa de câmbio ausente.

Motivos de atribuição

Primeiro tenta-se o click ID. Se ele não é um dos seus, tenta-se o btag. Quando a política diz que o btag vale e o btag corresponde a um link, o btag é tentado primeiro. Um btag que corresponde a um link atribui o jogador mesmo quando nenhum clique é encontrado. Um código promocional é tentado por último, só quando nem o click ID nem o btag atribuíram o jogador. A política, a janela e qual dos dois vale são definidas na página Integração; cada mudança é uma nova versão. Quando a política diz que vale o código promocional, um código que corresponde a um link é tentado antes dos dois, e um que não corresponde volta para eles. Um acordo pode definir sua própria janela, que substitui a da política para cliques nos links dos seus afiliados.

reasonSignificado
clickAtribuídoO click ID correspondeu a um clique dentro da janela.
first_clickAtribuídoA política é primeiro clique: o click ID correspondeu a um clique dentro da janela, e um clique contado anterior do mesmo visitante (mesmo endereço e dispositivo) dentro da janela foi creditado no lugar dele.
btag_clickAtribuídoO btag correspondeu a um link, e um clique nesse link foi encontrado dentro da janela.
btag_onlyAtribuídoO btag correspondeu a um link; nenhum clique foi encontrado dentro da janela.
promo_codeAtribuídoNem o click ID nem o btag atribuíram o jogador, e o código promocional correspondeu a um código de um afiliado aprovado.
no_tagNão atribuídoO cadastro não trazia nem click ID nem btag.
unknown_clickNão atribuídoO click ID não é um dos seus, e nenhum btag foi enviado.
unknown_btagNão atribuídoO btag está malformado ou não corresponde a nenhum link.
outside_windowNão atribuídoO clique é mais antigo que a janela de atribuição, ou posterior ao cadastro.
affiliate_inactiveNão atribuídoO afiliado não está aprovado, ou não estava quando o clique foi feito.
unknown_promo_codeNão atribuídoSó um código promocional foi enviado, e ele não corresponde a nenhum dos seus códigos.
promo_code_retiredNão atribuídoSó um código promocional foi enviado, e ele foi desativado.

Por que nenhuma comissão foi criada

commission.reasonSignificado
unattributedO jogador não pertence a nenhum afiliado.
not_firstNão é o primeiro depósito do jogador.
already_earnedO CPA do jogador já foi ganho. Um segundo "primeiro" depósito não paga nada e gera um alerta.
no_deal_versionO afiliado não tem deal, ou o deal não tinha versão no momento do evento.
country_blockedO jogador é de um país pelo qual o deal não paga. O jogador continua registrado e atribuído.
no_cpa_in_dealO deal não paga CPA.
below_thresholdO primeiro depósito ou os depósitos acumulados líquidos estão abaixo do mínimo do deal, conforme sua base de CPA.
no_commissionPara um chargeback: o depósito não tinha rendido nada.
no_revshare_in_dealPara ngr: o deal não paga RevShare.
fx_rate_missingPara ngr: ainda não há taxa de câmbio para o período. O informe é gravado e precificado quando existir uma taxa.
unchangedPara ngr: um valor corrigido que não muda nada na comissão.

Lotes

O corpo pode ser um array JSON de 1 a 1.000 eventos, enviado ao mesmo endpoint com os mesmos cabeçalhos. A assinatura cobre o array inteiro. Os eventos são tratados em ordem, cada um separadamente, então um evento recusado nunca interrompe os demais.

Um lote que pôde ser lido é respondido com 200, mesmo quando todos os eventos dele foram recusados. Olhe as contagens.

[
  {"type":"deposit","player_id":"PLR-1001","txn_id":"TX-88213","amount":"25.00","first":true},
  {"type":"ngr","player_id":"PLR-1001","period":"2026-10-03","amount":"118.40"},
  {"type":"ngr","player_id":"PLR-1002","period":"October","amount":"-12.50"}
]
{
  "results": [
    {
      "index": 0,
      "status": "accepted",
      "event_type": "deposit",
      "event_id": "0199b2c9-03aa-7e10-b6d2-91c07f5e2a18",
      "commission": { "effect": "created", "type": "cpa", "amount_minor": 4000, "status": "hold" }
    },
    {
      "index": 1,
      "status": "duplicate",
      "event_type": "ngr",
      "event_id": "0199b2d1-5c0e-7b42-8a17-3e9d6f0c4b21",
      "commission": { "effect": "created", "type": "revshare", "amount_minor": 2960, "status": "hold" }
    },
    {
      "index": 2,
      "status": "rejected",
      "field": "period",
      "message": "period must be a day (2026-10-03) or a month (2026-10), not in the future."
    }
  ],
  "accepted": 1,
  "duplicate": 1,
  "rejected": 1
}
CampoTipoSignificado
resultsarrayUma entrada por evento, na ordem de envio.
results[].indexintegerA posição do evento no array, a partir de 0.
results[].statusstringaccepted, duplicate ou rejected.
results[].event_typestringComo em uma resposta individual. Não aparece em um evento recusado.
results[].event_idstringComo em uma resposta individual. Não aparece em um evento recusado.
results[].attributionobjectComo em uma resposta individual.
results[].commissionobjectComo em uma resposta individual.
results[].waiting_for_playerbooleanComo em uma resposta individual.
results[].fieldstringPara um evento recusado: o campo a corrigir.
results[].messagestringPara um evento recusado: o que corrigir.
acceptedintegerQuantos eventos foram gravados.
duplicateintegerQuantos já estavam gravados.
rejectedintegerQuantos foram recusados.

POST /v1/files

Envia um arquivo CSV diário, como alternativa ao envio pela página Integração ou a deixar a RevRake buscá-lo num endereço onde a sua plataforma o publica. O corpo é o próprio arquivo; os cabeçalhos e a assinatura são os de um evento, e a assinatura cobre o timestamp e o arquivo. O nome do arquivo vai no parâmetro de query filename; ele é exibido no portal e não é usado para mais nada.

POST https://app.revrake.com/v1/files?filename=sales-2026-10-03.csv

A linha de cabeçalho decide qual arquivo é: um arquivo com uma coluna registration_date é um arquivo REG, um com uma coluna activitydate é um arquivo SALES. Os nomes das colunas são comparados sem diferenciar maiúsculas de minúsculas. Colunas que não estão listadas abaixo podem ficar no arquivo; são ignoradas.

As datas aceitas são YYYY-MM-DD, YYYY-MM-DD HH:MM:SS, YYYY-MM-DDTHH:MM:SS, RFC 3339 ou YYYY/MM/DD. Sem offset UTC, vale o fuso dos arquivos em Configurações do workspace (UTC por padrão). Um timestamp com offset identifica um instante exato; em SALES, usa-se seu dia de calendário no fuso dos arquivos. Dia e mês nunca são adivinhados: 30/09/2026 é recusado.

Arquivo REG: uma linha por jogador cadastrado

ColunaObrigatórioSignificado
registration_datesimQuando o jogador se cadastrou. Vira ts.
player_idsimSeu ID para o jogador.
btagsimO btag guardado no jogador. Pode ficar vazio: nesse caso o jogador não é atribuído, a menos que um código promocional o atribua.
promo_codenãoOpcional. O código promocional que o jogador digitou no cadastro, usado como o promo_code de um cadastro.
player_countrynãoOpcional. O código de duas letras do país do jogador (ISO 3166-1 alpha-2), usado como o country de um cadastro. Vazio: o país do clique.
brand_name,registration_date,btag,promo_code,player_id,player_country
Example,2026-10-03,1001_1,,PLR-1001,KE
Example,2026-10-03,,,PLR-1002,KE
Example,2026-10-03,,GARAGE50,PLR-1003,KE

Arquivo SALES: uma linha por jogador por dia

ColunaObrigatórioSignificado
activitydatesimO dia de atividade, determinado no fuso horário dos arquivos do workspace.
player_idsimSeu ID para o jogador.
currencysimA moeda dos valores da linha. Vazia: a moeda de relatório do workspace.
depositssimTotal bruto de depósitos do dia, incluindo o primeiro depósito ocorrido nesse dia. Não é o total histórico; não desconte chargebacks.
first_deposit_amountsimValor do primeiro depósito, somente no dia em que ocorreu; zero nos demais dias. Já está incluído em deposits e nunca é somado novamente. Não pode superar o total bruto de depósitos do dia.
chargebacksimTotal de chargebacks deste dia de atividade, separado dos depósitos brutos. Não é o total histórico. Reduz os depósitos acumulados para CPA, mas não identifica uma transação específica.
*_net_revenuenãoTodas as colunas com o nome net_revenue ou terminadas em _net_revenue. As células preenchidas são somadas na receita líquida do jogador naquele dia, informada como ngr. Uma linha sem nenhuma delas preenchida não informa receita.
*_gross_revenuenãoOpcional. Colunas com o nome gross_revenue ou terminadas em _gross_revenue, somadas no ggr do ngr do dia. Apenas mostrado ao afiliado.
*_bonusnãoOpcional. Colunas com o nome bonus ou terminadas em _bonus, somadas em bonuses. Apenas mostrado.
*_feesnãoOpcional. Colunas com o nome fees ou terminadas em _fees, somadas em fees. Apenas mostrado.
*_taxesnãoOpcional. Colunas com o nome taxes ou terminadas em _taxes, somadas em taxes. Apenas mostrado.
brand_name,activitydate,player_id,btag,currency,deposits,first_deposit_amount,chargeback,casino_net_revenue,sports_net_revenue
Example,2026-10-03,PLR-1001,1001_1,KES,3500.00,2500.00,0,118.40,0
Example,2026-10-03,PLR-1002,,KES,900.00,0,0,-12.50,
  • Cada linha SALES registra um resumo monetário diário e, quando há células de receita preenchidas, um evento NGR separado. São processados independentemente: um resumo monetário recusado não desfaz uma receita aceita, nem o contrário. Confira todos os problemas na resposta.
  • Um resumo por marca, jogador e dia de atividade; a primeira moeda aceita fica fixa. Valores monetários idênticos são replays mesmo após renomear ou reordenar o arquivo. Alterar valores, moeda ou limites de fuso horário de um dia aceito é conflito, inclusive ao alterar zeros. O dinheiro já registrado permanece inalterado.
  • deposits, first_deposit_amount e chargeback são colunas obrigatórias. Células vazias significam zero. Os valores devem ser decimais não negativos com a precisão da moeda da linha. Confirme essas definições com sua plataforma antes de conectar a exportação.
  • Envie REG antes de SALES. Resumos monetários recebidos antes são guardados e precificados quando o cadastro chega. NGR de jogador desconhecido é recusado: após REG, reenvie SALES ou NGR. O resumo monetário salvo será um replay.
  • O resumo cobre postbacks individuais do dia de atividade e da moeda no fuso dos arquivos, sem somar valores duas vezes. Os IDs de transação continuam referenciáveis. Depósitos ou chargebacks conhecidos acima do resumo, ou evidências inconsistentes de primeiro depósito, geram conflito.
  • No CPA acumulado, depósitos menos chargebacks devem atingir o mínimo do deal. Cada fluxo diário usa a taxa de câmbio da data de atividade; postbacks individuais usam seu próprio dia UTC. A falta de taxas mantém a liquidação em retenção sob fx_rate_missing. Taxas de chargeback ausentes bloqueiam o pagamento sob chargeback_fx_missing até a reconciliação, inclusive para comissões já incluídas em um pagamento.
  • No CPA de primeiro depósito, um chargeback agregado pode não identificar o depósito revertido. Um resumo monetário ambíguo de jogador já cadastrado é recusado. Se chegou antes de REG, é guardado e o cadastro é aceito, mas o CPA resultante permanece em retenção sob chargeback_reference_missing. Para revisar a referência da transação, envie um chargeback com ref_txn_id definido como o ID de transação do depósito original.
  • A receita é separada dos totais monetários imutáveis: alterar o NGR diário é uma reapresentação, e a diferença de comissão vira um ajuste.

A resposta

Um arquivo legível retorna 200. Os contadores são por evento, não apenas por linha: uma linha SALES pode registrar resumo monetário e NGR separadamente, inclusive com sucesso parcial. Sempre confira rejected e problems.

{
  "id": "0199b31e-2a40-7d18-9b6c-4f1e7a2c5d03",
  "filename": "sales-2026-10-03.csv",
  "kind": "sales",
  "uploaded_at": "2026-10-04T02:00:07Z",
  "rows_total": 2,
  "accepted": 3,
  "duplicates": 0,
  "rejected": 1,
  "skipped": 0,
  "problems": ["line 3: Player PLR-1002 is not registered. Send the registration first."]
}
CampoTipoSignificado
idstringO ID da RevRake para esta importação.
filenamestringO nome que você informou, ou reg.csv / sales.csv.
kindstringreg ou sales.
uploaded_atstringQuando foi recebido.
rows_totalintegerLinhas de dados no arquivo.
acceptedintegerEventos gravados.
duplicatesintegerEventos que já estavam gravados.
rejectedintegerLinhas ou eventos recusados. Cada um está no log de entrada com a própria linha.
skippedintegerLinhas sem nada a registrar.
problemsarray of stringAs primeiras 20 linhas recusadas, no formato line N: reason. Omitido quando não há nenhuma.

GET /v1/reports

Devolve o relatório da página Relatórios: cliques, cliques sinalizados, cadastros, primeiros depósitos e ganhos, agrupados de uma forma, para os dias até hoje no fuso horário do workspace. Os números de hoje são atualizados quando um relatório é solicitado, no máximo a cada 30 segundos em um programa movimentado.

GET https://app.revrake.com/v1/reports?by=link&days=7
ParâmetroTipoExemploSignificado
bystringlinkComo as linhas são agrupadas: day, link, sub1, country ou affiliate. Padrão: day. Com affiliate definido, by=affiliate é respondido como day.
daysinteger7Quantos dias, contando hoje: de 1 a 365. Padrão: 30.
fromstring2026-09-01O primeiro dia de qualquer período, YYYY-MM-DD no fuso horário do workspace. Com from ou to, days é ignorado. No máximo 366 dias.
tostring2026-09-30O último dia do período, incluído. Padrão: hoje.
affiliateinteger1001Um número de afiliado. Restringe o relatório a esse afiliado. Só com chave de operador; ignorado com uma chave de afiliado, que sempre lê o seu próprio afiliado.
tagstringInfluencersO nome de um grupo, como na página Grupos (repita para vários: qualquer um deles). Só com a chave do operador; ignorado com affiliate ou com uma chave de afiliado. Um nome desconhecido dá 422 unknown_tag.
formatstringcsvcsv devolve um arquivo CSV em vez de JSON.
{
  "by": "link",
  "from": "2026-09-28",
  "to": "2026-10-04",
  "currency": "KES",
  "rows": [
    {
      "key": "0199a7f0-41c2-7e55-b1d9-6c3a8e2f7b10",
      "label": "telegram-october",
      "clicks": 1840,
      "flagged_clicks": 212,
      "registrations": 96,
      "ftds": 31,
      "earnings_minor": 12400000
    }
  ],
  "total": {
    "key": "",
    "label": "",
    "clicks": 1840,
    "flagged_clicks": 212,
    "registrations": 96,
    "ftds": 31,
    "earnings_minor": 12400000
  }
}
CampoTipoSignificado
bystringO agrupamento que foi usado.
fromstringO primeiro dia do relatório, YYYY-MM-DD.
tostringO último dia: hoje no fuso horário do workspace, ou o to que você informou.
currencystringA moeda de relatório do workspace. Os ganhos estão nela.
rowsarrayUma entrada por grupo.
rows[].keystringO que identifica o grupo: o dia, o ID da RevRake para o link ou o afiliado, o valor de sub1 ou o país em duas letras.
rows[].labelstringO texto que o portal mostra para o grupo: o dia, o rótulo de campanha do link, o valor de sub1, o país, ou o nome e o número do afiliado.
rows[].clicksintegerCliques limpos.
rows[].flagged_clicksintegerCliques sinalizados como bots, repetições, rajadas ou de um afiliado que não está aprovado. Não entram em clicks.
rows[].registrationsintegerJogadores atribuídos.
rows[].ftdsintegerPrimeiros depósitos.
rows[].earnings_minorintegerComissões ganhas, em unidades menores da moeda de relatório, pela taxa do dia em que cada uma foi ganha.
totalobjectA soma das linhas, no formato de uma linha.

Em CSV

Com format=csv a resposta é um arquivo. A primeira coluna leva o nome do agrupamento e contém o rótulo; depois vêm clicks, flagged_clicks, registrations, first_deposits e earnings_<currency> como decimal. A última linha é o total.

link,clicks,flagged_clicks,registrations,first_deposits,earnings_KES
telegram-october,1840,212,96,31,124000.00
total,1840,212,96,31,124000.00

GET /v1/commissions

Devolve uma página do livro-razão de comissões, das mais recentes para as mais antigas, a menos que você ordene de outra forma. As linhas nunca têm seus valores editados e nunca são apagadas, mas o status de uma linha muda depois que ela é gravada: hold, depois approved, depois paid, ou rejected. Uma sincronização precisa ler um período de novo e atualizar as linhas pelo id, não apenas buscar linhas novas.

GET https://app.revrake.com/v1/commissions?from=2026-09-01&to=2026-10-01&per_page=500
ParâmetroTipoExemploSignificado
statusstringapprovedhold, approved, paid ou rejected. Padrão: todos.
fromstring2026-09-01Só linhas ocorridas neste horário ou depois dele: RFC 3339, ou um dia YYYY-MM-DD em UTC.
tostring2026-10-01Só linhas ocorridas antes deste horário. Mesmos formatos.
affiliateinteger1001Um número de afiliado. Restringe as linhas, totals e owed a esse afiliado. Só com chave de operador; ignorado com uma chave de afiliado.
tagstringInfluencersO nome de um grupo, como na página Grupos (repita para vários: qualquer um deles). Só com a chave do operador; ignorado com affiliate ou com uma chave de afiliado. Um nome desconhecido dá 422 unknown_tag.
qstringPLR-1001Linhas cujo ID do jogador ou nome do afiliado contém este texto. Até 100 caracteres.
sortstringwhenwhen, affiliate, player, status, deposit ou commission. Padrão: when.
dirstringascasc ou desc. Padrão: desc.
pageinteger2A página, a partir de 1. Padrão: 1.
per_pageinteger50010, 25, 50, 100, 500 ou 1000. Padrão: 25.
{
  "currency": "KES",
  "totals": { "hold": 1240000, "approved": 3720000, "paid": 9800000 },
  "owed": { "KES": { "hold": 1240000, "approved": 3720000, "paid": 9800000 } },
  "period": true,
  "commissions": [
    {
      "id": "0199b2c9-0a11-7c3e-9f20-7d4b1e6a2c58",
      "occurred_at": "2026-09-14T08:20:00Z",
      "type": "cpa",
      "currency": "KES",
      "gross_currency": "KES",
      "reporting_minor": 400000,
      "affiliate_name": "Odds Garage",
      "affiliate_number": 1001,
      "player_ref": "PLR-1001",
      "deal_name": "CPA 4000",
      "deal_version": 2,
      "gross_minor": 250000,
      "rate_snapshot": 400000,
      "amount_minor": 400000,
      "status": "approved",
      "status_reason": "",
      "hold_until": "2026-10-14T08:20:00Z",
      "status_note": ""
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 500
}
CampoTipoSignificado
currencystringA moeda de relatório do workspace.
totalsobjectA soma por status, em unidades menores da moeda de relatório: em todo o livro-razão, ou no período quando from ou to é informado. Não é restringida pelos outros filtros, exceto affiliate. Linhas que ainda aguardam uma taxa de câmbio não entram nela.
owedobjectA soma por moeda e status, na moeda própria de cada comissão: o que é devido. Sobre as mesmas linhas de totals.
periodbooleantrue quando from ou to foi informado, então totals e owed cobrem apenas esse período.
commissionsarrayAs linhas desta página.
totalintegerQuantas linhas correspondem aos filtros.
pageintegerA página que foi devolvida.
per_pageintegerO número de linhas por página que foi usado.

Uma linha de comissão

CampoTipoSignificado
idstringO ID da RevRake para a linha, um UUID. Ele nunca muda: atualize por ele.
occurred_atstringQuando a comissão foi ganha: o horário do depósito, o fim do período de receita ou o horário do chargeback.
typestringcpa, revshare, adjustment, reversal ou tier.
amount_minorintegerA comissão, em unidades menores de currency. Negativa para um estorno e para um ajuste para baixo.
currencystringA moeda da comissão: a do deal.
reporting_minorinteger | nullA comissão na moeda de relatório do workspace, pela taxa do dia em que foi ganha. null enquanto essa taxa estiver ausente.
gross_minorintegerO depósito ou a receita líquida por trás da comissão, em unidades menores de gross_currency.
gross_currencystringA moeda do depósito ou da receita.
rate_snapshotintegerOs termos que foram aplicados: o valor do CPA em unidades menores, ou o RevShare em centésimos de um por cento (2500 é 25%).
statusstringhold, approved, paid ou rejected. Veja os status de comissão.
status_reasonstringPor que uma linha ainda está em retenção ou foi rejeitada. Vazio nos demais casos. Todos os valores estão na tabela de motivos.
status_notestringA nota do operador sobre uma comissão retirada, como o afiliado a lê. Vazia nos outros casos.
hold_untilstring | nullQuando o período de retenção termina. null para um estorno.
affiliate_numberintegerO número do afiliado.
affiliate_namestringO nome do afiliado.
player_refstringSeu ID para o jogador.
deal_namestringO deal.
deal_versionintegerA versão do deal que precificou a linha.

Listas do programa: afiliados, links, acordos

Seu backend pode listar o programa como o portal mostra, assinado como as outras leituras e contado no mesmo limite. GET /v1/affiliates e GET /v1/deals exigem a chave do operador; com uma chave de afiliado respondem 403 operator_key_required. GET /v1/links responde os links da marca, ou os de um afiliado com affiliate=<número>; com uma chave de afiliado, os do próprio afiliado.

As listas são paginadas com page (a partir de 1) e per_page (de 1 a 500, padrão 100) e trazem total. GET /v1/affiliates aceita também status: pending, approved, rejected ou suspended. Criar afiliados ou links pela API ainda não é oferecido: conte-nos para que você usaria.

GET https://app.revrake.com/v1/links?affiliate=1001&per_page=100

Cada afiliado

CampoTipoSignificado
numberintegerO número do afiliado, a primeira parte do btag; num link, o número do link, a segunda parte.
namestringO nome do afiliado.
emailstringO e-mail do afiliado.
statusstringpending, approved, rejected ou suspended.
dealstringO nome do acordo do afiliado; vazio antes da aprovação.
created_atstringQuando foi criado, RFC 3339.
decided_atstring | nullQuando a inscrição foi aprovada ou recusada, ou a conta suspensa; null antes disso.

Cada link

CampoTipoSignificado
affiliate_numberintegerO número do afiliado dono do link.
numberintegerO número do afiliado, a primeira parte do btag; num link, o número do link, a segunda parte.
codestringO código no endereço do link.
urlstringO link de rastreamento, no domínio de rastreamento verificado da marca ou no compartilhado.
btagstring<affiliate_number>_<number>.
campaignstringO rótulo de campanha do link.
landing_pathstring | nullA página do seu site para onde o link leva; null para a padrão da marca.
subsstring[5]sub1 a sub5 fixados no link; textos vazios quando não definidos.
promo_codestring | nullO código promocional, num link criado para um; senão null.
vanity_urlstring | nullO endereço curto /go/<nome>, quando o afiliado definiu um; senão null.
created_atstringQuando foi criado, RFC 3339.

Cada acordo

CampoTipoSignificado
namestringO nome do afiliado.
currencystringA moeda em que o acordo rende e paga.
listedbooleanSe a página pública de inscrição mostra o acordo.
defaultbooleanSe novos afiliados recebem este acordo.
versionintegerA versão do acordo em vigor agora.
effective_atstringQuando essa versão entrou em vigor, RFC 3339.
termsobjectOs termos dessa versão: valores em unidades menores, percentuais em pontos-base (2500 = 25%), dias de retenção, regra de meses negativos, a janela de atribuição própria do acordo, faixas.

Exportação para data warehouse

Configurada na página Integração: toda noite o RevRake grava o dia anterior de cada marca no seu próprio bucket, em qualquer serviço compatível com S3, em <pasta>/<marca>/<AAAA-MM-DD>/, com o dia contado no fuso horário do workspace. Quatro arquivos CSV compactados com gzip (UTF-8, linha de cabeçalho, aspas conforme RFC 4180) e depois manifest.json. O manifesto é gravado por último: carregue um dia quando ele existir e confira rows, bytes e sha256 de cada arquivo.

Uma linha pertence ao dia em que chegou (um clique: quando aconteceu), então um arquivo diário que chega atrasado fica no dia em que chegou, nunca se perde. Horários em RFC 3339, UTC com milissegundos; valores em decimais na própria moeda. Os status são os do momento da exportação: o de uma comissão muda depois, então leia GET /v1/commissions de novo para o atual. Um dia com falha é tentado de novo a cada hora e recuperado até sete dias atrás; exportar um dia de novo sobrescreve seus arquivos. Colunas novas podem ser adicionadas no fim de um arquivo; schema_version no manifesto muda quando um carregador precisa mudar.

manifest.json: schema_version, workspace, brand, day, timezone, from e to (os limites do dia em UTC), generated_at e files: para cada um, name, rows, bytes, sha256 (do arquivo compactado).

clicks.csv.gz: todos os cliques nos links da marca naquele dia, inclusive os sinalizados.

CampoTipoSignificado
click_idstringO ID do clique, como passado à sua página de destino e nos postbacks.
clicked_atstringQuando o clique aconteceu.
affiliate_numberintegerO número do afiliado.
link_numberintegerO número do link.
btagstring<affiliate_number>_<link_number>.
countrystringCódigo do país com duas letras; vazio se desconhecido.
devicestringdesktop, mobile, tablet ou bot, pelo navegador.
osstringO sistema operacional, pelo navegador.
browserstringO navegador.
sub1 … sub5stringsub1 a sub5: os sub-IDs do clique.
utm_source … utm_contentstringutm_source a utm_content: as tags UTM do clique.
gclid, fbclid, ttclid, msclkid, wbraidstringgclid, fbclid, ttclid, msclkid, wbraid: IDs de clique de plataformas de anúncio na URL de destino.
refererstringA página de onde o visitante veio.
flagsstringPor que o clique não conta, separado por ;: bot, duplicate, burst, inactive. Vazio para um clique limpo.

players.csv.gz: jogadores cujo cadastro chegou naquele dia.

CampoTipoSignificado
player_idstringSeu ID de jogador.
registered_atstringO horário de cadastro que você enviou.
arrived_atstringQuando o RevRake o registrou.
countrystringCódigo do país com duas letras; vazio se desconhecido.
attributedbooleantrue quando um afiliado é creditado.
attribution_reasonstringPor quê, como na resposta do postback: click, btag_only, promo_code, no_tag…
affiliate_numberintegerO número do afiliado.
link_numberintegerO número do link.
btagstring<affiliate_number>_<link_number>.
click_idstringO clique que creditou o jogador; vazio se não houver.
policy_versionintegerA versão da política de atribuição que decidiu.

events.csv.gz: todos os eventos aceitos da marca que chegaram nesse dia, incluindo resumos diários SALES, com seu payload.

CampoTipoSignificado
event_idstringO ID do evento no RevRake.
typestringregistration, deposit, chargeback, daily_sales ou ngr; para uma comissão, cpa, revshare, tier, adjustment ou reversal.
external_idstringA chave do evento: o ID do jogador, o ID da transação, ou jogador e período.
player_idstringSeu ID de jogador.
occurred_atstringQuando aconteceu, como você enviou; numa comissão, quando foi ganha.
received_atstringQuando o RevRake aceitou o evento.
sourcestrings2s (a API) ou file (um arquivo diário).
payloadJSONO evento como o RevRake o leu, em JSON.
resultJSONO que o RevRake respondeu, em JSON.

commissions.csv.gz: linhas de comissão gravadas naquele dia.

CampoTipoSignificado
commission_idstringO ID da linha, o id em GET /v1/commissions.
created_atstringQuando a linha foi gravada.
occurred_atstringQuando aconteceu, como você enviou; numa comissão, quando foi ganha.
typestringregistration, deposit, chargeback, daily_sales ou ngr; para uma comissão, cpa, revshare, tier, adjustment ou reversal.
statusstringhold, approved, paid ou rejected, na exportação.
status_reasonstringPor que está retida ou rejeitada; vazio caso contrário.
affiliate_numberintegerO número do afiliado.
player_idstringSeu ID de jogador.
dealstringO nome do acordo.
deal_versionintegerA versão do acordo que a precificou.
grossdecimalO depósito ou a receita líquida por trás dela.
gross_currencystringA moeda dele.
amountdecimalA comissão.
currencystringA moeda do acordo.
reporting_amountdecimalA comissão na moeda de relatórios; vazio enquanto falta a taxa de câmbio.
reporting_currencystringA moeda de relatórios do workspace.
reverses_idstringNum estorno, a linha estornada.
payout_item_idstringA linha de pagamento que a incluiu, quando o mês fechou.

Idempotência

Cada evento tem uma chave natural. Uma tentativa idêntica retorna a resposta salva com "status":"duplicate". Um depósito aguardando cadastro tem sua resposta atualizada quando o cadastro chega. Fatos monetários alterados podem gerar conflito; reapresentações de NGR usam revisões. Após um timeout, tente novamente com um novo timestamp de assinatura.

EventoChaveA mesma chave de novo
registrationplayer_idA primeira resposta. A atribuição não é decidida de novo, mesmo que as tags sejam diferentes.
deposittxn_idA resposta salva, atualizada quando o cadastro pendente chega. Alterar jogador, valor ou moeda gera conflito. Um resumo SALES que cubra a transação não descarta seu ID.
chargebacktxn_idA primeira resposta. Um segundo chargeback do mesmo depósito com um novo txn_id é recusado.
ngrplayer_id e periodMesmo valor e mesma moeda: a primeira resposta. Valor diferente: uma correção, gravada como nova revisão, com a diferença como adjustment.
CSVLinha REG: player_idComo um registration.
CSVResumo monetário SALES: marca, jogador e dia de atividadeTotais idênticos são replays. Alterar depósitos, primeiro depósito, chargebacks, moeda ou limites do dia gera conflito, mesmo em um resumo antes zerado.
CSVLinha SALES, receita: o jogador e o diaComo ngr: um valor alterado em uma reexportação é uma correção.

Status e erros

Todas as respostas que a API pode dar e todos os status que um registro pode ter, em um só lugar. Um erro é respondido com um objeto JSON: error (o código), message (uma frase em inglês) e, para invalid, field.

{"error":"invalid","field":"amount","message":"amount must be a positive decimal in KES, for example 25.00."}

Status HTTP e códigos de erro

StatuserrorEndpointQuandoO que fazer
200–/v1/*A requisição foi tratada. Para um evento: gravado, ou já gravado. Para um lote ou um arquivo: lido; olhe as contagens, alguns itens podem ter sido recusados.
400bad_requestPOST /v1/postbackO corpo começa como um array, mas não é um array JSON de eventos.Corrija a requisição. A mesma requisição de novo recebe a mesma resposta.
401unknown_key/v1/*A chave em X-RevRake-Key é desconhecida ou foi revogada, ou é uma chave de afiliado em um endpoint de escrita.Use a chave atual. Enviar de novo não adianta.
401bad_timestamp/v1/*X-RevRake-Timestamp está ausente, não é um número inteiro de segundos ou está mais de cinco minutos fora do horário real.Verifique o relógio e o código de assinatura e envie de novo com um timestamp novo.
401bad_signature/v1/*A assinatura não corresponde ao timestamp e ao corpo (em uma leitura: ao caminho e à query).Verifique o relógio e o código de assinatura e envie de novo com um timestamp novo.
403affiliate_inactiveGET /v1/*Uma leitura com uma chave de afiliado cujo afiliado não está aprovado no programa: pendente, rejeitado ou suspenso.Não há nada a corrigir na requisição: a conta precisa ser aprovada pelo operador.
404unknown_affiliateGET /v1/*Uma leitura com uma chave de operador: nenhum afiliado tem o número informado em affiliate.Corrija a requisição. A mesma requisição de novo recebe a mesma resposta.
405–/v1/*O endpoint não aceita esse método. Sem corpo JSON.Corrija a requisição. A mesma requisição de novo recebe a mesma resposta.
413too_largePOST /v1/*O corpo é maior que o limite do endpoint.Corrija a requisição. A mesma requisição de novo recebe a mesma resposta.
422invalidPOST /v1/*, GET /v1/reportsO evento ou o arquivo não pode ser usado como está.Corrija a requisição. A mesma requisição de novo recebe a mesma resposta.
429rate_limitedGET /v1/*Mais leituras do que a cota da chave. A resposta traz Retry-After.Espere os segundos indicados em Retry-After e envie de novo.
500internal/v1/*Algo deu errado do nosso lado.Envie de novo.
  • Um lote sem eventos ou com mais de 1.000 é recusado com 422 invalid e sem field.
  • Um 401 por timestamp ou assinatura incorretos com uma chave de operador aparece no log de entrada como bad_signature. Uma requisição com chave desconhecida, ou com uma chave de afiliado, não é registrada.
  • Uma leitura responde 422 apenas para um período de relatório que não pode ser usado (from e to). Qualquer outro parâmetro com um valor não permitido é ignorado e usa-se o valor padrão. Confira by, page e per_page na resposta.
  • Reenvie em um timeout, em um 429 e em um 500. Não reenvie nenhum outro 4xx sem alterações.

O que field indica em um 422

fieldQuando
bodyO corpo não é JSON válido, ou traz um campo desconhecido ou malformado (por exemplo, um ts que não está em RFC 3339).
typetype está ausente ou não é um dos quatro.
player_idAusente ou com mais de 100 caracteres; um placeholder que não foi preenchido; para ngr, um jogador que não está cadastrado.
click_idUm placeholder que não foi preenchido. A mensagem começa com misconfigured_macro.
btagUm placeholder que não foi preenchido. A mensagem começa com misconfigured_macro.
promo_codeUm placeholder que não foi preenchido. A mensagem começa com misconfigured_macro.
txn_idAusente ou com mais de 200 caracteres.
ref_txn_idAusente; nenhum depósito com esse txn_id é conhecido; ou o depósito já sofreu chargeback.
amountNão é um decimal que a moeda permite; para um depósito, não está acima de zero.
currencyNão é um código de três letras.
ggrPara ngr: não é um valor decimal na moeda.
bonusesPara ngr: não é um valor decimal na moeda.
feesPara ngr: não é um valor decimal na moeda.
taxesPara ngr: não é um valor decimal na moeda.
tsMais de um dia no futuro.
periodNão é um dia nem um mês, ou está no futuro.
fromPara GET /v1/reports: from ou to não está no formato YYYY-MM-DD, from é posterior a to, ou os dois estão a mais de 366 dias de distância.
filePara /v1/files: o arquivo está vazio, não é CSV, não é um arquivo REG nem SALES, não tem uma coluna obrigatória ou tem mais de 200.000 linhas.
chargebackEm um arquivo SALES: a célula chargeback não pode ser aplicada. A mensagem diz por quê.

O status de um evento

Na resposta a um evento, e por evento em results de um lote.

statusSignificado
acceptedGravado agora. A resposta diz o que ele fez.
duplicateJá salvo com a mesma chave; um replay idêntico não altera nada. Uma correspondência antiga de primeiro depósito entre canais também pode retornar duplicate_of. Os resumos SALES atuais mantêm as transações reais dos postbacks e contam os valores diários cobertos apenas uma vez.
rejectedSó em um lote: o evento foi recusado, com field e message. Um evento individual recusado é um 422.

O efeito de um evento no livro-razão

commission.effectSignificado
createdUma linha de comissão foi criada: um CPA, um RevShare ou um ajuste por um valor corrigido.
cancelledUm chargeback rejeitou uma comissão que ainda estava em retenção.
reversedUm chargeback adicionou uma linha de estorno contra uma comissão já aprovada ou paga.
noneNenhuma linha foi criada. reason diz por quê.

Tipos de comissão

typeSignificado
cpaUma por jogador quando o mínimo do deal é atingido pelo primeiro depósito ou pelos depósitos acumulados menos chargebacks, conforme sua base de CPA.
revshareUm percentual da receita líquida de jogo (NGR) de um jogador em um período. Negativa em um período em que o jogador ganhou.
adjustmentA diferença causada por um valor de receita corrigido.
reversalO negativo de uma comissão retirada depois de ter sido aprovada ou paga: por um chargeback, ou pelo operador com um motivo. É aprovado na hora e é compensado no próximo pagamento.
tierUm complemento de uma faixa por volume do deal: quando um afiliado traz novos jogadores depositantes suficientes em um mês do calendário, a diferença entre o valor da faixa e o valor base é adicionada ao lado de cada comissão desse mês. Fica na mesma retenção que sua comissão, ou é aprovado na hora se ela já estiver aprovada. Negativo quando retira um complemento anterior, depois que sua comissão foi rejeitada ou estornada.

Status de comissão

Uma linha é gravada uma vez e seus valores nunca mudam. O status muda:

statusSignificado
holdAguardando: o fim do período de retenção do deal, ou o que status_reason indica.
approvedO período de retenção terminou e nada retém a linha. Ela entra no próximo lote de pagamento mensal. Estornos e alguns ajustes já nascem aprovados.
paidO item de pagamento que contém a linha foi marcado como pago.
rejectedNão será paga. status_reason diz por quê, e status_note traz a nota do operador quando houver.

Valores de status_reason

Vazio quando a linha aguarda apenas o período de retenção e normalmente em comissões aprovadas ou pagas. A falta de taxa de chargeback também pode bloquear uma linha aprovada. Retenções por fraude aguardam o operador ou a expiração configurada do alerta.

status_reasonCom o statusSignificado
fx_rate_missingholdFalta uma taxa de câmbio necessária. A liquidação aguarda a taxa; fluxos diários SALES usam a data de atividade, postbacks individuais usam seu dia UTC.
chargeback_fx_missinghold, approved, paidUm chargeback ainda não pode ser convertido. O CPA e seus adicionais de faixa não podem entrar em novo pagamento nem ser marcados como pagos em um existente até a taxa chegar e a reconciliação terminar.
chargeback_reference_missingholdUm resumo recebido antes do cadastro contém um chargeback agregado que não identifica o depósito revertido para CPA de primeiro depósito. O cadastro e o resumo são guardados; o CPA resultante fica em retenção até a revisão da referência da transação.
self_referralholdUm alerta de fraude aberto: o jogador parece ser o próprio afiliado. Aguarda o operador liberar ou confirmar o alerta.
click_velocityholdUm alerta de fraude aberto, ou um clique que fez parte de uma rajada: mais cliques em um link, de um mesmo endereço, em um minuto do que a marca permite. Aguarda o operador.
duplicate_conversionholdUm alerta de fraude aberto: um segundo "primeiro" depósito foi informado para o jogador. Aguarda o operador.
paid_trafficholdUm alerta de fraude aberto: o clique que trouxe o jogador trazia o click ID de uma rede de anúncios. Aguarda o operador.
disposable_emailholdUm alerta de fraude aberto: o jogador se cadastrou com um domínio de e-mail descartável. Aguarda o operador.
geo_mismatchholdUm alerta de fraude aberto: o jogador se cadastrou num país diferente do país do clique. Aguarda o operador.
fast_registrationholdUm alerta de fraude aberto: o jogador se cadastrou dentro do número de segundos definido após o clique. Aguarda o operador.
affiliate_pendingholdO afiliado ainda não estava aprovado quando a comissão foi ganha. Quando o afiliado é aprovado, a linha aguarda apenas o seu período de retenção normal.
affiliate_rejectedholdA candidatura do afiliado foi rejeitada.
affiliate_suspendedholdO afiliado estava suspenso quando a comissão foi ganha. Quando o afiliado é reativado, a linha aguarda apenas o seu período de retenção normal.
chargebackrejected, approvedEm uma linha rejeitada: um chargeback a cancelou enquanto estava em retenção. Em uma linha de estorno: ela estorna uma comissão após um chargeback.
below_thresholdrejectedQuando as taxas ficaram disponíveis, o primeiro depósito qualificado ou os depósitos acumulados líquidos ficaram abaixo do mínimo do deal.
fraudrejected, approvedO operador confirmou o alerta de fraude que retinha a linha, ou retirou a comissão por fraude.
base_rejectedrejectedUm complemento de faixa cuja comissão foi rejeitada enquanto os dois estavam em retenção.
self_excludedrejected, approvedO operador retirou a comissão: o jogador se autoexcluiu. Numa linha rejeitada, a comissão ainda não estava em um pagamento; numa linha de estorno, estava.
duplicate_accountrejected, approvedO operador retirou a comissão: o jogador tem outra conta.
bonus_abuserejected, approvedO operador retirou a comissão por abuso de bônus.
terms_breachrejected, approvedO operador retirou a comissão por violação dos termos do programa.
test_accountrejected, approvedO operador retirou a comissão: o jogador era uma conta de teste.
otherrejected, approvedO operador retirou a comissão por outro motivo, escrito em status_note.

Status de pagamento

Um mês fecha em um lote de pagamento com um item por afiliado e moeda. Lotes e itens são tratados no portal; a API mostra o resultado deles como o status paid das linhas de comissão.

Status do loteSignificado
draftO mês foi fechado. O operador revisa os itens.
approvedTravado. Com as faturas ativadas, cada item pagável recebe um número de fatura. O arquivo bancário (CSV) pode ser baixado e os itens, pagos.
paidTodos os itens a pagar foram marcados como pagos.
Status do itemSignificado
payableA pagar neste lote.
carriedAbaixo do pagamento mínimo, ou não acima de zero: passa para o lote seguinte.
heldO afiliado tem um alerta de fraude aberto, ou o programa retém pagamentos até a identidade ser verificada e a verificação ainda não foi registrada. O portal diz qual. O operador pode liberar o item, e registrar a verificação de identidade o libera.
no_detailsO afiliado não informou seus dados de pagamento. Fica fora do arquivo bancário.
written_offO mês terminou abaixo de zero e o deal do afiliado não leva um mês negativo adiante: o saldo é baixado no fechamento e nada passa para o próximo lote. Com um limite, o item fica carried e registra como baixada a parte acima do limite.
paidMarcado como pago. Suas linhas de comissão ficam paid, e o tracker do afiliado é avisado.

Por que uma linha de arquivo é recusada

Uma linha recusada é contada em rejected, listada em problems como line N: reason e mantida no log de entrada com a própria linha. Além dos motivos pelos quais um evento pode ser recusado:

  • A linha não é um CSV válido.
  • REG: registration_date ou player_id está ausente ou ilegível.
  • SALES: activitydate ou player_id está ausente ou ilegível.
  • SALES, receita: o jogador não está cadastrado.
  • SALES, dinheiro: um valor é negativo, ilegível ou tem casas decimais demais para a moeda.
  • SALES: o primeiro depósito supera os depósitos brutos.
  • SALES: um dia aceito foi reexportado com totais monetários, moeda ou limites de fuso horário alterados.
  • SALES: depósitos ou chargebacks individuais conhecidos superam o resumo do dia.
  • SALES: a evidência de primeiro depósito conflita com outro dia ou uma transação conhecida.
  • SALES: um chargeback agregado é ambíguo para CPA de primeiro depósito de um jogador já cadastrado.

Resultados no log de entrada

StatusSignificado
acceptedGravado, com o que fez.
duplicateJá gravado; a primeira resposta foi devolvida.
rejectedRecusado; o detalhe diz qual campo e por quê.
bad_signatureO timestamp ou a assinatura estava errado.
testUm evento de teste da página Integração. Passa pelo caminho real e não grava nada.

Estados de uma entrega de postback

StatusSignificado
queuedAguardando a primeira tentativa, ou o próximo reenvio. O portal a mostra como aguardando ou repetindo.
deliveredO tracker respondeu com um status 2xx.
deadTodos os reenvios falharam. O portal a mostra como desistida; o afiliado pode reenviá-la manualmente.

Status de um afiliado

StatusSignificado
pendingCandidatou-se; o operador ainda não decidiu.
approvedAtivo no programa.
rejectedA candidatura foi recusada.
suspendedInterrompido pelo operador.

O link de rastreamento

Um link de rastreamento é aberto por um visitante, não chamado pela sua plataforma. Ele grava o clique e responde com um redirecionamento 302 para a landing page da marca. Enquanto a marca não verificou o site, o visitante vê antes uma página curta dizendo para onde o link leva. O host de destino vem das configurações da marca; um link só pode escolher um caminho dentro dele.

GET https://track.revrake.com/c/a8Kq2Zx?sub1=TRACKER_CLICK_ID&utm_source=telegram
HTTP/1.1 302 Found
Location: https://your-site.example/landing?btag=1001_1&cid=01K6QF3M8Z5T7W9XB2C4D6E8GH&utm_source=telegram
Cache-Control: no-store

Parâmetros no link

ParâmetroSignificado
sub1 … sub5Os valores do próprio afiliado, gravados com o clique e devolvidos nos postbacks. Um valor no endereço substitui o que está fixado no link.
utm_source utm_medium utm_campaign utm_term utm_contentGravados com o clique e repassados à landing page. Um valor no endereço substitui o que está fixado no link.
gclid fbclid ttclid msclkid wbraidClick IDs das redes de anúncios. Gravados com o clique; disponíveis como macros de postback. Não são repassados.

Parâmetros acrescentados à URL de destino

ParâmetroSignificado
cidO click ID: 26 caracteres em base32 de Crockford (dígitos e letras maiúsculas sem I, L, O, U). Ele se ordena por tempo. Guarde-o como texto.
btag<número do afiliado>_<número do link>: de um a nove dígitos, um sublinhado, de um a nove dígitos.
utm_source … utm_contentAs tags UTM do clique, quando não estão vazias.
  • Se a URL de destino da marca contém {click_id} na query, o click ID é colocado ali e cid não é acrescentado.
  • Um clique no link de um afiliado que não está aprovado é gravado e sinalizado, e chega sem cid e btag: ele não pode ser atribuído.
  • Bots, prévias de link, requisições HEAD, requisições sem user agent e cliques repetidos dentro de uma hora são gravados e sinalizados, não descartados. Os relatórios os deixam de fora.
  • Outros parâmetros no link não são gravados nem repassados.
  • Um link a que o afiliado deu um nome curto também responde em /go/<name>, exatamente como em /c/<code>. Uma marca com o seu próprio domínio de rastreamento verificado serve os dois caminhos também nesse domínio.
  • Um código desconhecido é respondido com 404 e uma página de erro em HTML, assim como um link cuja marca ainda não definiu um destino.

Postbacks para os trackers dos afiliados

Esta parte não exige trabalho do operador. Cada afiliado adiciona até 10 destinos no portal dele: um template de endereço com macros e os eventos sobre os quais quer ser avisado. Quando um evento é registrado para um dos jogadores do afiliado, a RevRake preenche o template e o chama com uma requisição GET.

Eventos

Evento{status}Quando
registrationleadUm jogador foi atribuído ao afiliado.
ftdsaleUm primeiro depósito rendeu ao afiliado uma comissão CPA.
commission_approvedapprovedUma comissão foi aprovada ao fim do período de retenção.
commission_rejectedrejectedUma comissão em retenção foi rejeitada após uma análise.
commission_paidpaidUma comissão foi paga em um lote de pagamento.
chargebackrejectedUm chargeback retirou uma comissão.

Palavras de status por preset

Um destino criado a partir de um preset envia, em {status}, as palavras que o tracker dele conhece. Os presets do Voluum, do PeerClick e do FunnelFlux Pro, e "Outro / personalizado", enviam as palavras da tabela acima. As palavras seguem o preset que o destino leva; o endereço salvo dele nunca é alterado.

EventoKeitaroBinomRedTrackBeMob
registrationleadleadothernew
ftdsalesalependingapproved
commission_approvedsalesaleapprovedapproved
commission_rejectedrejectedrejectdeclineddeclined
commission_paidsalesaleapprovedapproved
chargebackrejectedrejectdeclinedchargeback

Macros

Uma macro é um nome entre chaves. É substituída pelo seu valor, codificado para URL. Os nomes são comparados sem diferenciar maiúsculas de minúsculas. Um nome que a RevRake não conhece é enviado como está, então um erro de digitação aparece no log de entregas. Um valor que não é conhecido fica vazio.

MacroSignificado
{click_id}O click ID da RevRake (o cid na URL de destino). Vazio quando o jogador foi atribuído só pelo btag.
{sub1} … {sub5}sub1 a sub5 do clique. sub1 normalmente leva o click ID do tracker do afiliado.
{event}O nome do evento, como na tabela acima.
{status}A palavra que os trackers usam para o evento, como na tabela acima, ou a palavra própria do tracker em um destino criado a partir de um preset (veja a próxima tabela).
{event_id}Diferente para cada evento e estável entre reenvios.
{conversion_id}Igual para todos os eventos de uma mesma comissão: ftd, commission_approved, commission_rejected, commission_paid e chargeback. Um cadastro tem o seu próprio. Use-o como ID de transação em um tracker que atualiza uma conversão que chega de novo com o mesmo ID, para que a receita seja contada uma vez.
{player_id} {affiliate_id} {brand_id} {link_id}O ID do jogador no operador, o número do afiliado, o programa e o número do link.
{campaign}O rótulo de campanha do link.
{commission} {commission_minor}A comissão, como decimal (40.00) ou em unidades menores (4000). Vazia para um cadastro, 0 para uma comissão rejeitada, negativa para um chargeback.
{amount} {amount_minor}O depósito ou a receita por trás da comissão, como decimal ou em unidades menores, na moeda em que foi feito. Vazio para um cadastro.
{amount_currency}O código ISO da moeda do depósito. Vazio para um cadastro.
{commission_currency}O código ISO da moeda da comissão: a do deal. Vazio para um cadastro.
{currency}O código ISO da moeda a que o postback se refere: a moeda da comissão quando há uma comissão, senão a do depósito, senão a moeda de relatório do workspace.
{occurred_at} {occurred_at_unix}Quando aconteceu, no formato RFC 3339 ou em segundos Unix.
{country} {device} {os} {browser} {referer}O que se sabe sobre o clique: país, tipo de dispositivo, sistema operacional, navegador, referenciador.
{utm_source} {utm_medium} {utm_campaign} {utm_term} {utm_content}As tags UTM do clique.
{gclid} {fbclid} {ttclid} {msclkid} {wbraid}Click IDs das redes de anúncios, quando o clique os trazia.

Outros nomes para as mesmas macros

Os trackers usam nomes diferentes para as mesmas coisas. Estes também são aceitos:

{subid}= {sub1}
{clickid}= {click_id}
{payout}= {commission}
{txid}= {event_id}
{transaction_id}= {event_id}

Presets

O portal do afiliado traz estes templates. O afiliado substitui o host pelo do seu tracker. Cada um segue a documentação do seu tracker: {conversion_id} fica no parâmetro de transação quando o tracker atualiza uma conversão que chega de novo com o mesmo ID, e {event_id} quando ele descartaria os eventos posteriores.

Keitarohttps://YOUR-TRACKER-DOMAIN/POSTBACK-KEY/postback?subid={sub1}&status={status}&payout={commission}&currency={currency}&tid={conversion_id}
Binomhttps://YOUR-TRACKER-DOMAIN/click?cnv_id={sub1}&payout={commission}&cnv_status={status}&cnv_currency={currency}
Voluumhttps://YOUR-TRACKER-DOMAIN/postback?cid={sub1}&payout={commission}&currency={currency}&txid={event_id}&et={event}
RedTrackhttps://YOUR-TRACKER-DOMAIN/postback?clickid={sub1}&sum={commission}&status={status}&type={event}&rdtk_event_id={event_id}
BeMobhttps://YOUR-TRACKER-DOMAIN/postback?cid={sub1}&payout={commission}&txid={conversion_id}&status={status}
PeerClickhttps://YOUR-TRACKER-DOMAIN/postback?cid={sub1}&payout={commission}&txid={conversion_id}&status={status}&currency={currency}
FunnelFlux Prohttps://YOUR-TRACKER-DOMAIN/pb/?hit={sub1}&tx={conversion_id}&rev={commission}&status={status}

Um exemplo

https://YOUR-TRACKER-DOMAIN/postback?click_id={sub1}&event={event}&status={status}&payout={commission}&currency={currency}&tid={conversion_id}&eid={event_id}
GET https://tracker.example/postback?click_id=TRACKER_CLICK_ID&event=ftd&status=sale&payout=40.00&currency=KES&tid=0199b2c9-0a11-7c3e-9f20-7d4b1e6a2c58&eid=0199b2c9-03aa-7e10-b6d2-91c07f5e2a18%3Aftd

Entrega

  • Um postback entra na fila na mesma transação do evento que ele informa, e é enviado em cerca de dez segundos, do mais antigo para o mais novo.
  • A requisição é um GET com o user agent RevRake-Postback/1. Ela segue no máximo três redirecionamentos e desiste após 5 segundos. Os certificados são verificados.
  • Uma resposta 2xx é um sucesso. Qualquer outra coisa é uma falha e é reenviada após 1 minuto, 5 minutos, 25 minutos, 2 horas, 10 horas e 24 horas.
  • Depois do último reenvio, a entrega é marcada como desistida. O afiliado pode reenviá-la manualmente.
  • Cada tentativa é registrada com a duração, o status e os primeiros 500 caracteres da resposta.
  • Depois de 5 e de 15 falhas seguidas, o dono do destino recebe um e-mail. Com 20, o destino é desligado. As entregas dele, inclusive as de eventos que acontecem enquanto está desligado, continuam entrando na fila e são enviadas quando o afiliado o liga de novo. Um destino que o próprio afiliado desligou não recebe nada novo.
  • A RevRake só chama endereços públicos. Endereços de loopback, privados, link-local e de carrier-grade NAT são recusados no momento da conexão, então um redirecionamento não consegue contornar isso.
  • O template deve ser um endereço http:// ou https:// completo, com no máximo 2.000 caracteres.

Por que uma tentativa falhou

timeoutSem resposta em 5 segundos.
dnsO nome do host não foi encontrado.
refusedA conexão foi recusada.
blocked_addressO endereço é privado ou local.
tlsUm problema no certificado.
too_many_redirectsMais de três redirecionamentos.
http_errorO tracker respondeu com um status diferente de 2xx.
otherA conexão falhou por outro motivo.

Webhooks do operador

Os webhooks informam os sistemas do próprio operador (um CRM, um data warehouse, um canal de chat) sobre o que acontece no programa. Um admin do workspace adiciona um endpoint na página Integração do portal do operador: o endereço no seu servidor, os eventos a enviar e uma descrição opcional. Para cada um desses eventos, a RevRake envia um POST JSON assinado para o endereço. Os outros membros da equipe não podem ver nem alterar os endpoints.

O endereço é um endereço https:// completo em um host público (http:// é aceito, mas use https://). Cada endpoint tem o seu próprio segredo, que começa com whsec_. Ele é mostrado uma única vez, quando o endpoint é criado ou o segredo é rotacionado: copie-o nesse momento. Rotacionar cria um novo segredo na hora, e o antigo deixa de assinar.

Um endpoint também pode ser um canal do Slack ou do Microsoft Teams: escolha “Mensagem no Slack” ou “Mensagem no Microsoft Teams” e cole o endereço de incoming webhook do canal (no Teams, um fluxo “Quando uma solicitação de webhook do Teams for recebida”). Cada evento chega então como uma frase no canal em vez de JSON assinado, e um valor pode manter só os primeiros depósitos a partir desse tamanho.

Eventos

Marque um ou mais por endpoint. Os campos de data estão descritos abaixo; um campo que não se aplica é omitido.

EventoCampos de dataQuando
affiliate.appliedaffiliate_number affiliate_name email promotes_on trafficAlguém se candidatou na página do seu programa ou por um link de convite de um deal. É enviado quando a candidatura é feita, o que pode ser antes de o candidato confirmar o e-mail. Um afiliado que você mesmo adiciona e convida envia affiliate.approved no lugar.
affiliate.approvedaffiliate_number affiliate_name email reason decided_by invitedVocê aprovou um afiliado: uma candidatura nova, ou uma que você tinha rejeitado ou suspendido. Um afiliado que você mesmo adiciona e convida é aprovado na hora e envia este evento com invited igual a true.
affiliate.rejectedaffiliate_number affiliate_name email reason decided_byVocê rejeitou uma candidatura.
affiliate.suspendedaffiliate_number affiliate_name email reason decided_byVocê suspendeu um afiliado aprovado.
player.registeredplayer_id occurred_at attributed affiliate_number affiliate_name attribution_reasonUm jogador foi cadastrado, por um evento assinado ou por uma linha de um arquivo REG, atribuído ou não. Uma vez por jogador.
commission.createdplayer_id occurred_at attributed affiliate_number affiliate_name commission_id commission_type amount amount_currency commission commission_currencyUma linha de comissão foi criada: CPA qualificado pela base de primeiro depósito ou depósitos acumulados do deal, ou revenue share ou ajuste da receita líquida informada. Adicionais de faixa e estornos não enviam esse evento.
commission.approvedplayer_id occurred_at attributed affiliate_number affiliate_name commission_id commission_type amount amount_currency commission commission_currencyUma comissão em retenção foi aprovada porque o período de retenção terminou, complementos de faixa incluídos. Uma linha aprovada desde o início não envia nenhum.
commission.rejectedplayer_id occurred_at attributed affiliate_number affiliate_name commission_id commission_type amount amount_currency commission commission_currencyUma comissão em retenção foi rejeitada porque você confirmou o alerta de fraude que a retinha. commission é zero. Um CPA cancelado por um chargeback envia commission.reversed no lugar.
commission.paidplayer_id occurred_at attributed affiliate_number affiliate_name commission_id commission_type amount amount_currency commission commission_currencyUma linha de comissão foi paga: um evento por linha de um item de pagamento marcado como pago, linhas de estorno e de faixa incluídas.
commission.reversedplayer_id occurred_at attributed affiliate_number affiliate_name commission_id commission_type amount amount_currency commission commission_currencyUm chargeback tomou de volta um CPA: cancelado enquanto em retenção, ou estornado por uma nova linha depois de aprovado ou pago. commission_id identifica o CPA, commission é o seu negativo e amount é o depósito que sofreu o chargeback.
payout.batch_closedbatch_id period reference items payable_by_currency closed_by_handUm mês foi fechado em um lote de pagamento em rascunho, por uma pessoa ou pelo fechamento agendado.
payout.batch_approvedbatch_idUm lote de pagamento em rascunho foi aprovado e travado.
payout.paidbatch_id item_id affiliate_number affiliate_name currency amountUm item de um lote aprovado foi marcado como pago. Um lote com todos os itens pagos não envia evento próprio.
tracking.health_alertbrand kind since quiet_hours clicks_at_leastOs links de uma marca receberam pelo menos o número de cliques definido nas horas definidas e nenhum cadastro chegou (página Integração, Saúde do rastreamento). Enviado uma vez; verificado a cada hora.
tracking.health_clearedbrand raised_at cleared_atChegou o primeiro cadastro após um alerta de saúde do rastreamento, e o alerta é encerrado.
flag.raisedkind detail affiliate_number affiliate_nameUm novo sinal de fraude foi criado sobre um afiliado (por uma regra, um primeiro depósito repetido ou autoindicação). Sinais de rajadas de cliques ficam só na página Sinais.
postback.destination_offdestination failures affiliate_number affiliate_nameO destino de postbacks de um afiliado foi desativado após as falhas seguidas de entrega que a escala de novas tentativas permite.
file.latesource dateUm arquivo diário buscado em horário programado não tinha chegado três horas depois do horário. Uma vez por arquivo e dia.
webhook.testmessageSomente pelo botão "Enviar teste". Não pode ser marcado, não é reenviado e não aparece no log de entregas.

O corpo

Toda requisição leva um objeto JSON:

CampoTipoSignificado
idstringO ID do evento, um UUID. O mesmo para todo endpoint que recebe o evento e em todo reenvio: use-o para descartar repetições.
typestringO evento, como na tabela acima.
created_atstringQuando o evento entrou na fila, no formato RFC 3339 em UTC, com frações de segundo.
dataobjectOs fatos do evento. Veja os campos abaixo.
{
  "id": "0199b2c9-0a11-7c3e-9f20-7d4b1e6a2c58",
  "type": "commission.created",
  "created_at": "2026-10-05T09:12:44.318204Z",
  "data": {
    "player_id": "PLR-1001",
    "occurred_at": "2026-10-05T09:12:40Z",
    "attributed": true,
    "affiliate_number": 1001,
    "affiliate_name": "Odds Garage",
    "commission_id": "0199b2c9-0a0f-7a51-8c3d-5e2f4a6b7c90",
    "commission_type": "cpa",
    "amount": "25.00",
    "amount_currency": "USD",
    "commission": "40.00",
    "commission_currency": "USD"
  }
}

As chaves podem vir em qualquer ordem, e o espaçamento do JSON não é fixo. Verifique os bytes como foram recebidos, depois interprete-os.

Cabeçalhos

CabeçalhoValor
RevRake-EventO evento, igual a type no corpo.
RevRake-DeliveryO ID desta entrega a este endpoint, um UUID. O mesmo em todo reenvio, e quando uma entrega é enviada de novo à mão.
RevRake-TimestampQuando esta tentativa foi enviada, em segundos Unix. Novo a cada tentativa.
RevRake-SignatureO HMAC-SHA256 em hex minúsculo, com o segredo do endpoint, de <timestamp>.<raw body>. Nova a cada tentativa.

A requisição também leva Content-Type: application/json e o user agent RevRake-Webhook/1.

POST /revrake-webhook HTTP/1.1
Host: example.com
Content-Type: application/json
User-Agent: RevRake-Webhook/1
RevRake-Event: commission.created
RevRake-Delivery: 0199b2c9-0b7e-7d02-8a41-3c5e6f708192
RevRake-Timestamp: 1791191571
RevRake-Signature: <hex HMAC-SHA256 of "1791191571.<raw body>">

Os campos de data

CampoTipoSignificado
affiliate_numberintegerO número do afiliado.
affiliate_namestringO nome do afiliado.
emailstringO endereço de e-mail com que o afiliado entra.
promotes_onstringO que o candidato escreveu sobre onde promove.
trafficarray of stringAs fontes de tráfego que o candidato marcou: seo, telegram, paid_social, youtube, email ou offline.
reasonstringO motivo informado ao afiliado. Sempre presente em uma rejeição ou suspensão; pode estar vazio em uma aprovação.
decided_bystringO endereço de e-mail do membro da equipe que decidiu, ou que adicionou o afiliado.
invitedbooleantrue quando você mesmo adicionou e convidou o afiliado, sem candidatura. Omitido nos outros casos.
player_idstringO seu ID do jogador.
occurred_atstringQuando aconteceu, no formato RFC 3339: o cadastro, o depósito, o fim do período de receita ou o chargeback. Em uma aprovação, rejeição ou pagamento: o momento da mudança.
attributedbooleanSe o jogador pertence a um afiliado. Sempre true para uma comissão.
attribution_reasonstringPor que o jogador foi ou não atribuído: um dos motivos de atribuição.
commission_idstringO ID da linha de comissão na RevRake: o id da linha em GET /v1/commissions. O mesmo em todo evento sobre essa comissão.
commission_typestringO tipo da linha de comissão: cpa, revshare, adjustment ou tier, e reversal para uma linha de estorno em commission.paid. Em commission.reversed, o tipo do CPA que foi estornado.
amountstringPara uma comissão: o depósito ou a receita líquida por trás dela, como decimal, na sua própria moeda. Para payout.paid: o valor do item, como decimal.
amount_currencystringO código ISO da moeda de amount.
commissionstringA comissão, como decimal na moeda do deal (40.00). Negativa em um estorno, um ajuste para baixo e um período em que o jogador ganhou.
commission_currencystringO código ISO da moeda da comissão: a do deal.
batch_idstringO ID do lote de pagamento na RevRake, um UUID.
periodstringO mês que o lote fecha, YYYY-MM.
referencestringA referência do lote, a mesma do arquivo bancário, como po_2026-09_demo.
itemsintegerQuantos itens o lote tem, qualquer que seja o status deles.
payable_by_currencyobjectPor moeda, a soma dos valores de todos os itens, como decimal (1250.00), qualquer que seja o status, itens que passam para o mês seguinte incluídos.
closed_by_handbooleantrue quando uma pessoa fechou o mês, false no fechamento agendado.
item_idstringO ID do item de pagamento na RevRake, um UUID.
currencystringA moeda do item.
messagestringUma frase dizendo que o teste chegou.

Verifique uma requisição

  1. Leia o corpo bruto como bytes, antes de qualquer interpretação do JSON.
  2. Verifique se RevRake-Timestamp está a no máximo 5 minutos do seu relógio, e recuse a requisição caso contrário, para que uma requisição gravada não possa ser reproduzida mais tarde.
  3. Calcule o HMAC-SHA256 em hex do timestamp, um ponto e o corpo bruto com o segredo do endpoint, e compare-o com RevRake-Signature em tempo constante.
  4. Só então interprete o corpo. Se você já tratou o seu id, responda 2xx e não faça nada.
  5. Responda com qualquer 2xx em até 5 segundos, e faça o trabalho demorado depois.

A mesma verificação em Node.js e Python, como a página Integração a mostra:

Node.js
const crypto = require("node:crypto");

// raw: the body exactly as received (a Buffer), before parsing it as JSON.
function verify(raw, headers, secret) {
  const ts = headers["revrake-timestamp"] ?? "";
  if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(ts + ".").update(raw).digest();
  const got = Buffer.from(headers["revrake-signature"] ?? "", "hex");
  return got.length === expected.length && crypto.timingSafeEqual(got, expected);
}
Python
import hashlib, hmac, time

# raw: the body exactly as received (bytes), before parsing it as JSON.
def verify(raw: bytes, headers, secret: str) -> bool:
    ts = headers.get("RevRake-Timestamp", "")
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
        return False
    expected = hmac.new(secret.encode(), ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, headers.get("RevRake-Signature", ""))

Entrega, reenvios e desligamento

  • Uma entrega entra na fila na mesma transação da mudança que ela informa, então um evento é enviado se e somente se a mudança foi gravada. Ela é enviada em cerca de dez segundos, as mais antigas primeiro.
  • A entrega é pelo menos uma vez: o mesmo evento pode chegar mais de uma vez, por exemplo quando a sua resposta se perde no caminho. Descarte repetições pelo id do corpo.
  • A ordem não é garantida: um reenvio pode chegar depois de um evento posterior. Ordene os eventos por created_at ou occurred_at, não pelo momento em que chegam.
  • Uma resposta 2xx em até 5 segundos é um sucesso. Qualquer outra coisa é uma falha e é reenviada após 1 minuto, 5 minutos, 25 minutos, 2 horas, 10 horas e 24 horas, com o mesmo corpo e um timestamp e uma assinatura novos.
  • Depois do último reenvio a entrega é marcada como "desistiu". Um admin pode enviá-la de novo pelo log de entregas, com o botão "Enviar novamente"; ela então recebe todo o calendário de reenvios outra vez.
  • Depois de 20 tentativas falhas seguidas o endpoint é desligado. Os seus eventos continuam na fila, e tudo o que entrou na fila é enviado quando um admin o liga de novo. Nenhum e-mail é enviado quando isso acontece: o estado de cada endpoint está na página Integração.
  • Um endpoint que você mesmo desliga não recebe nada novo: nenhum evento entra na fila para ele enquanto está desligado.
  • A RevRake só chama endereços públicos: desiste após 5 segundos e verifica os certificados. Endereços de loopback, privados, link-local e de NAT de operadora são recusados. Um redirecionamento não é seguido: uma resposta 3xx é uma falha com o erro redirect, e o log de entregas mostra o endereço para onde ela apontava.

Teste e leia o log de entregas

  • Clique em "Enviar teste" em um endpoint: um evento webhook.test é enviado na hora, assinado como um real, e você vê o status que o seu endpoint respondeu e quanto tempo levou. Um teste não é reenviado, não aparece no log de entregas e não conta como falha.
  • O log de entregas de um endpoint mostra as suas 100 entregas mais recentes: o evento, o estado ("aguardando", "repetindo", "desistiu" ou "entregue"), o número de tentativas, o último status ou erro, os primeiros 500 caracteres da sua resposta, a próxima tentativa e o corpo como foi enviado.
  • Uma tentativa falha é descrita com as mesmas palavras de um postback (veja por que uma tentativa falhou), mais redirect: o seu endpoint respondeu com um 3xx. Os estados são os de uma entrega de postback.
{
  "id": "0199b2d4-5c60-7b1a-9e3f-1a2b3c4d5e6f",
  "type": "webhook.test",
  "created_at": "2026-10-05T09:30:02.104377Z",
  "data": {
    "message": "A test from RevRake. If you can read this, your endpoint works."
  }
}

Boas práticas

  • Guarde o segredo no seu servidor, nunca em uma página ou em um app. Se ele vazar, rotacione-o e passe o seu endpoint para o novo na hora: o segredo antigo para imediatamente.
  • Responda a uma requisição cuja assinatura não confere com um 4xx. Ela é reenviada, assinada com o segredo atual, então uma entrega feita enquanto você rotaciona não se perde.
  • Guarde o id de cada evento que você tratou, e ignore um evento que você já viu.
  • Responda primeiro, trabalhe depois: coloque o evento em uma fila sua e responda 2xx, para que um banco de dados lento nunca faça a RevRake reenviar.
  • Informe o endereço final do seu endpoint. Um redirecionamento não é seguido: a tentativa falha com redirect, e o log de entregas mostra o endereço a usar no lugar.
  • Use commission_id para juntar os eventos sobre uma comissão, e concilie com GET /v1/commissions: uma leitura noturna dos últimos 60 dias pega qualquer coisa que tenha faltado.
  • Marque só os eventos que você usa. Cada um é uma requisição ao seu servidor.

Arquivo OpenAPI

Os quatro endpoints assinados, com todos os status e motivos como enumerações, estão descritos em openapi.yaml (OpenAPI 3.1), para geradores de código e clientes de API. O endereço dele não muda.

Pronto para conectar? Crie o seu programa e obtenha sua chave.

Você mesmo cria o seu programa: cadastre-se, confirme seu e-mail e em poucos minutos você está dentro. Gratuito para operadores, para sempre. Sem cartão.

Dúvidas? Escreva para hello@revrake.com