ZynzapAPI

Tratar cobrança na sua integração

Uma função quoteAndRun pronta para cotar e executar operações pagas, com limite de custo, cotação vencida e resultado incerto tratados.

Toda operação paga passa por cotação. Em vez de repetir isso em cada chamada, centralize numa função que:

  1. cota com o request exato;
  2. recusa se o custo máximo passar do seu teto ou se faltar saldo (dueCents > 0);
  3. executa com o quoteId;
  4. se a cotação venceu entre os passos (BILLING_QUOTE_EXPIRED), cota de novo uma vez;
  5. não repete em resultado incerto (OUTCOME_UNCERTAIN, RPC_DEADLINE_EXCEEDED): devolve o erro para você conferir o estado.

Valores em micro-reais: 1000000 = R$ 1,00.

const API = process.env.ZYNZAP_API_BASE_URL ?? "https://api.zynzap.com.br/v1";

class ZynzapError extends Error {
  constructor(status, error) {
    super(`${error.code} ${error.reason ?? ""}: ${error.message}`);
    Object.assign(this, { status, ...error });
  }
}

async function post(path, body) {
  const response = await fetch(`${API}${path}`, {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.ZYNZAP_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  const data = await response.json();
  if (!response.ok) throw new ZynzapError(response.status, data.error);
  return data;
}

/** Cota e executa uma operação paga. `maxMicrobrl`: teto aceito (ex.: 200000 = R$ 0,20). */
export async function quoteAndRun(method, request, { maxMicrobrl = 1_000_000 } = {}) {
  for (let attempt = 0; attempt < 2; attempt++) {
    const quote = await post("/billing/quotes", { method, request });
    if (BigInt(quote.maxMicrobrl) > BigInt(maxMicrobrl)) throw new Error(`Custo acima do teto: ${quote.maxMicrobrl}`);
    if (quote.dueCents > 0) throw new Error(`Saldo insuficiente: faltam ${quote.dueCents} centavos`);
    try {
      return await post(`/rpc/${method}`, { request, billing: { quoteId: quote.quoteId } });
    } catch (error) {
      if (error.reason === "BILLING_QUOTE_EXPIRED" && attempt === 0) continue; // cota de novo uma vez
      throw error; // inclui OUTCOME_UNCERTAIN: confira o estado antes de repetir
    }
  }
}

// Uso:
// await quoteAndRun("SendMessage", { session_id: "...", to_jid: "5511987654321", text: "Olá!" }, { maxMicrobrl: 200_000 });

Saldo e excedente

  • dueCents > 0 na cotação significa que falta saldo. Compre créditos ou habilite o excedente mensal no cartão no painel (Plano e consumo); a cotação mostra quanto entraria no excedente em monthlyOverageMicrobrl.
  • Campanhas grandes reservam o custo máximo inteiro na execução. O que não for usado volta para o saldo ao terminar ou cancelar.

Custos gratuitos

  • Leituras, configurações, contatos, CRM e eventos não custam nada.
  • Respostas pelo atendimento dentro de 24 horas da última mensagem do cliente saem com coverage: "inbox_window" e custo zero (ainda com cotação).
  • Isenções concedidas pelo Zynzap aparecem como coverage: "waived".