Comunidade conectada, com permissão

Desenvolva com a ZAS.

Propor uma parceria ↗

Esta API recebe eventos de atividade de projetos aprovados pela equipe. Eventos elegíveis concedem somente XP interno. Não emitem créditos, não abrem caixas e não compram ou entregam skins.

01 / CANDIDATURA

Apresente seu projeto

Vincule sua Steam, abra /loot no Discord e ative sua participação. No seu painel privado, envie a proposta em Parcerias.

02 / ANÁLISE MANUAL

Combine eventos verificáveis

A equipe avalia a proposta e a forma de comprovar atividade. O envio do formulário não concede acesso; a aprovação ocorre manualmente.

03 / BACKEND

Receba sua credencial

Após a aprovação, o responsável autorizado recebe um ID e uma chave por canal privado. Guarde a chave no servidor; nunca no navegador, repositório público ou URL.

Xplay, Dropskin e outros serviços externos não estão conectados automaticamente. Uma candidatura aprovada não implica parceria com essas plataformas nem autorização para coletar dados delas.

01 / Contrato de eventos

POST /api/partners/v1/events

Envie JSON UTF-8 com Content-Type: application/json. O corpo bruto pode ter até 8.192 bytes e deve conter somente os seis campos abaixo.

Backend → backend
{
  "eventId": "evento-unico-do-parceiro",
  "discordId": "ID_DISCORD_DO_MEMBRO",
  "steamId": "STEAMID64_DO_MESMO_MEMBRO",
  "kind": "activity",
  "seq": 1,
  "expiresAt": 0
}

Os marcadores de identidade e expiresAt: 0 acima são ilustrativos. Substitua por identidades autorizadas e uma expiração futura; não envie o exemplo literalmente.

CampoRegra
eventIdIdentificador estável e único por parceiro, de 1 a 160 caracteres: letras, números, ponto, sublinhado, dois-pontos ou hífen.
discordIdString numérica de 17 a 20 dígitos do membro participante.
steamIdString de 17 dígitos; deve corresponder à Steam atualmente vinculada àquele membro.
kindExatamente activity. Nenhum campo amount é aceito.
seqInteiro positivo seguro, estritamente crescente por parceiro. Persista o contador entre reinícios e coordene emissores concorrentes.
expiresAtUnix em segundos, futuro no processamento e no máximo timestamp + 300.

02 / Autenticação

Assine os bytes que serão enviados.

A assinatura usa HMAC-SHA256 em hexadecimal minúsculo. A chave do HMAC é o SHA256 da chave secreta aprovada, em bytes; não é a representação textual desse hash.

CabeçalhoValor
x-zas-keyID do parceiro aprovado.
x-zas-timestampUnix em segundos, como string de dez dígitos. Janela: até 300 segundos atrás ou 30 segundos à frente.
x-zas-nonceValor aleatório base64url, de 16 a 128 caracteres, único para cada evento novo.
x-zas-signatureHMAC-SHA256 com 64 caracteres hexadecimais minúsculos.
signingKey = SHA256(secret) em bytes
message = keyId + '\n' + timestamp + '\n' + nonce + '\n' + rawBody
signature = HMAC-SHA256(signingKey, message) em hexadecimal

Use quebras de linha LF entre as partes. Serialize o JSON uma vez: espaços, ordem de campos e quebras de linha fazem parte do corpo assinado. Não envie a chave secreta em um cabeçalho.

03 / Referência Node.js

Prepare, persista e envie.

Exemplo para backend com Node.js e fetch nativo. Configure ZAS_BASE_URL com a origem HTTPS da comunidade e as duas credenciais recebidas privadamente. Nenhum segredo real está neste guia.

import { createHash, createHmac, randomBytes } from 'node:crypto';

// Configure estas variáveis somente no backend do parceiro.
const baseUrl = new URL(process.env.ZAS_BASE_URL);
const keyId = process.env.ZAS_PARTNER_ID;
const secret = process.env.ZAS_PARTNER_SECRET;
if (baseUrl.protocol !== 'https:' || baseUrl.username || baseUrl.password
    || !keyId || !secret) throw new Error('Configure o acesso aprovado.');

export function prepareEvent({ eventId, discordId, steamId, seq }) {
  // seq deve vir de um contador persistente e atômico por parceiro.
  const timestamp = String(Math.floor(Date.now() / 1000));
  const nonce = randomBytes(24).toString('base64url');
  const rawBody = JSON.stringify({
    eventId, discordId, steamId, kind: 'activity', seq,
    expiresAt: Number(timestamp) + 120,
  });
  const signingKey = createHash('sha256').update(secret, 'utf8').digest();
  const message = [keyId, timestamp, nonce, rawBody].join('\n');
  const signature = createHmac('sha256', signingKey)
    .update(message, 'utf8').digest('hex');
  return {
    rawBody,
    headers: {
      'Content-Type': 'application/json',
      'x-zas-key': keyId,
      'x-zas-timestamp': timestamp,
      'x-zas-nonce': nonce,
      'x-zas-signature': signature,
    },
  };
}

export async function sendSavedEnvelope(envelope) {
  // Salve o envelope antes do envio; retries reutilizam os mesmos bytes.
  const response = await fetch(new URL('/api/partners/v1/events', baseUrl), {
    method: 'POST', redirect: 'error',
    headers: envelope.headers, body: envelope.rawBody,
    signal: AbortSignal.timeout(10_000),
  });
  const data = await response.json();
  if (!response.ok || data.ok !== true) {
    throw new Error('Evento não confirmado. HTTP ' + response.status);
  }
  return data.result;
}

Sua aplicação deve gerar o eventId para uma atividade comprovada, reservar a sequência em transação e salvar o envelope antes de enviar. O exemplo não implementa armazenamento nem comprovação da atividade.

04 / Entrega e limites

Confirme a resposta antes de avançar.

RETRY SEGURO

Mesmo evento, mesmo corpo

Dentro da janela válida, repita o envelope salvo após uma falha de rede. O mesmo evento e corpo devolvem o resultado anterior, sem repetir a recompensa de XP. Alterar o corpo mantendo o ID falha.

Se a janela expirou, confira a entrega com a equipe antes de emitir outro evento. Não reinicie a sequência nem gere novos IDs apenas para contornar uma resposta incerta.

SOMENTE XP

Limites por participação

Cada evento elegível concede 5 XP, até dez eventos por dia por membro e no máximo um por minuto. Não gera créditos nem tempo de voz para o check-in.

O receptor limita a 60 requisições válidas por minuto por parceiro e 12 por minuto por membro. Uma resposta aceita não promete pontos além desses limites.

ACESSO REVOGÁVEL

Vínculos continuam valendo

O destinatário precisa ser membro, ter Steam compatível e participação ativa. O titular da parceria também precisa manter seu vínculo e participação.

Chaves podem ser revogadas pela equipe. Sincronize o relógio do backend e proteja credenciais, envelopes e registros de identidade com acesso restrito.

Sucesso responde { "ok": true, "result": … }; falha responde { "ok": false, "message": … } com status HTTP correspondente. Leia o resultado, trate limites e não registre chaves ou assinaturas em logs públicos.