SaqSaq Docs
Boas práticas

Multi-tenant com virtualAccount

Se você opera várias marcas, lojas, filiais ou parceiros em uma só conta Saq, passe virtualAccount (até 50 caracteres) em cada criação. Esse campo:

  • Volta em todo callback, você sabe imediatamente de qual tenant é a transação.
  • Pode ser filtrado em GET /user/transactions e GET /pix, listas isoladas por tenant.
  • Dispensa contas filhas, uma só conta Saq serve N tenants.

Convenções de nome

PadrãoQuando usar
tenant-{slug}Plataforma SaaS multi-cliente.
loja-{cidade}-{numero}Rede com lojas físicas.
mkt-{partner}Marketplace com vários vendedores.
filial-{codigo}Filiais de uma mesma empresa.
branch-{branchId}Genérico, em inglês.

Tamanho máximo: 50 caracteres. Use formato estável e legível. Evite caracteres especiais e espaços.

Criar com virtualAccount

{
  "amount": 99.90,
  "clientReference": "order-1234",
  "virtualAccount": "loja-rj-01",
  "callbackUrl": "https://seusite.com.br/webhooks/saq"
}
{
  "id": "SAQ20251123104518DF75D20A8F",
  "status": "PENDING",
  "amount": 99.90,
  "clientReference": "order-1234",
  "virtualAccount": "loja-rj-01",
  "qrCodeText": "00020126870014br.gov.bcb.pix..."
}
{
  "id": "SAQ20251123104518DF75D20A8F",
  "type": "DEPOSIT",
  "status": "COMPLETED",
  "amount": 99.90,
  "clientReference": "order-1234",
  "virtualAccount": "loja-rj-01",
  "paidAt": "2025-11-23T10:46:26.986Z"
}

Listar só de um tenant

curl "https://api.saq.processamento.com/v1/user/transactions?virtualAccount=loja-rj-01&dateFrom=2025-11-01" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"
const url = new URL('https://api.saq.processamento.com/v1/user/transactions');
url.searchParams.set('virtualAccount', 'loja-rj-01');
url.searchParams.set('dateFrom', '2025-11-01');

const res = await fetch(url, {
  headers: {
    Authorization: `Bearer ${process.env.SAQ_TOKEN}`,
    'Content-Type': 'application/json',
  },
});
res = requests.get(
    'https://api.saq.processamento.com/v1/user/transactions',
    params={'virtualAccount': 'loja-rj-01', 'dateFrom': '2025-11-01'},
    headers={
        'Authorization': f'Bearer {os.environ["SAQ_TOKEN"]}',
        'Content-Type': 'application/json',
    },
)

Rotear o callback

async function saqWebhook(tx: saqCallback) {
  const tenantId = tx.virtualAccount;
  if (!tenantId) {
    log.warn('callback sem virtualAccount', { id: tx.id });
    return;
  }

  const handler = tenantHandlers[tenantId];
  if (!handler) {
    log.error('tenant desconhecido', { tenantId, id: tx.id });
    return;
  }

  await handler.process(tx);
}

virtualAccount vs clientReference

Os dois são campos independentes e complementares. Use os dois sempre.

CampoGranularidadePropósito
clientReferencePor transaçãoIdempotência + lookup por pedido.
virtualAccountPor tenantRoteamento + filtro de listagens.

Exemplo combinado:

{
  "amount": 99.90,
  "clientReference": "order-1234",
  "virtualAccount": "loja-rj-01",
  "callbackUrl": "https://seusite.com.br/webhooks/saq"
}

Armadilhas comuns

ArmadilhaSintoma
Usar clientReference pra identificar tenantNão filtra em listagens, complica lookup
virtualAccount muda toda vez (timestamp, slug variável)Listagem fica fragmentada
Não tratar callback sem virtualAccountRoteamento crasha em transações legadas
Hardcode de tenants no handlerOnboarding manual a cada novo cliente

On this page