Você integra um PDV, um delivery ou outro sistema de pedidos com a Zupy? Esse é o parceiro de integração, com
outra chave e outras rotas: veja Partner Onboarding. As rotas deste guia só aceitam a
chave de parceiro emissor.
Antes de começar
Você precisa de três coisas, todas configuradas com a equipe da Zupy:- Perfil de parceiro ativo, com o recurso Emissor. Sem isso, toda chamada responde
403comerror_codepartner-not-active. - Saldo de Z. A API não expõe o saldo.
- Pelo menos uma campanha, para creditar. A campanha define a ação (por exemplo, “Descarte correto de óleo”), a
unidade (
litro), o valor de cada unidade em Z$, a vigência e os limites. A equipe da Zupy cria a campanha e informa ocampaign_iddela. As suas campanhas, com o orçamento consumido, aparecem no painel em Integrações → Parceiro Zupy → Campanhas.
https://api.zupy.com
1. Gerar a chave de API
1
Abra o painel do parceiro
Entre no painel da Zupy como administrador da empresa parceira e abra Integrações → Parceiro Zupy
(
/integrations/partner). No cartão Chaves de parceiro, clique em Gerenciar chaves.2
Crie a chave com as permissões certas
Clique em Nova chave de API, dê um nome (por exemplo, “ERP”) e marque só as permissões de que a integração
precisa:
Não existe permissão curinga:
partner.* não libera nada.3
Guarde a chave na hora
A chave completa aparece uma única vez. Guarde-a no seu cofre de segredos. Se ela vazar ou se perder, revogue
no mesmo lugar e crie outra: a revogação vale na hora.
2. Autenticar
Envie a chave no cabeçalhoX-API-Key em toda chamada. A empresa parceira é sempre a dona da chave: nenhuma rota
aceita um id de parceiro no corpo, na query ou em outro cabeçalho.
3. Ler o seu perfil
GET /api/v2/partner/profile/ (permissão partner.read) devolve os recursos ligados e as condições comerciais.
As taxas são frações: 0.2000 é 20%.
4. Creditar Z$ a um usuário
POST /api/v2/partner/credits/ (permissão partner.credit) credita quantity unidades da ação de uma campanha
sua ao usuário que os identificadores apontam. O valor em Z$ é quantity × unit_value da campanha.
Resposta
201 (crédito novo):
Como o usuário é identificado
- A Zupy procura o usuário pelos identificadores enviados. Se ninguém for encontrado, cria a conta, uma vez só: o mesmo telefone ou e-mail nunca vira duas contas.
- Para criar a conta é preciso
phoneouemail. Com sócpfouwallet_addressde alguém que ainda não tem conta, a resposta é422customer-not-identified. - Se dois identificadores apontarem para pessoas diferentes, nada é creditado:
409partner-identity-conflict, comidentifierslistando só os tipos que discordam (phone,email,cpf,wallet), nunca os valores. - Identificador malformado (telefone inválido, CPF com dígito errado, carteira Solana inválida, e-mail inválido) é
recusado antes de qualquer busca, com
422validation-errorapontando o campo.
Idempotência: external_reference
Reenviar o mesmo external_reference com os mesmos dados é seguro: a resposta é 200 com o mesmo
crédito, e nada é creditado de novo. Use isso para repetir uma chamada que expirou ou caiu sem resposta.
Crédito e transferência têm espaços de referência separados: a mesma
external_reference num crédito e numa
transferência são operações diferentes.
Como o crédito aparece para o usuário
No extrato do usuário, o crédito mostra a origem e o motivo a partir do nome da empresa parceira e da ação da campanha, por exemplo:Z$ 200 via Biord: Descarte correto de óleo (1 litro). O crédito não tem campo de motivo
livre; o motivo é a ação da campanha.
Limites da campanha
A campanha pode ter orçamento total e teto por usuário. Um crédito que ultrapassaria um deles é recusado com422
credit-limit-exceeded, que traz limit (budget_total ou per_user), limit_value, used e requested. Uma
campanha pausada, encerrada ou fora da vigência responde 422 campaign-not-creditable.
status: o registro on-chain
5. Consultar créditos
GET /api/v2/partner/credits/(permissãopartner.read): os seus créditos, do mais novo para o mais antigo, paginados por cursor. Siga os linksnexteprevious.page_sizevai de 1 a 100 (padrão 25). Filtre pela sua referência com?external_reference=(busca exata).GET /api/v2/partner/credits/{credit_id}/(permissãopartner.read): um crédito seu, com ostatusatual. O crédito de outro parceiro responde404, igual a um que não existe.
6. Transferir Z$ a um usuário ou a uma empresa
POST /api/v2/partner/transfers/ (permissão partner.transfer) envia amount Z$ da carteira da empresa parceira
para exatamente um destino, sem campanha:
- um usuário, pelos identificadores (
phone,email,cpf,wallet_address), com as mesmas regras de identificação do crédito; ou - uma empresa, por
company_id. Uma filial é creditada na carteira da matriz: a resposta trazdestination_company_id(a empresa pedida) ecredited_company_id(a que recebeu). Você credita a empresa, mas nunca lê o saldo dela.
company_id juntos, ou nenhum dos dois, responde 422 validation-error.
Resposta
201, para um usuário:
customer_id vem null e os dois ids de empresa vêm preenchidos.
- Limites de transferência: a empresa parceira pode ter tetos por destinatário e no total, por dia, semana ou
mês. Uma transferência que ultrapassaria um deles responde
422transfer-limit-exceeded, comlimit,limit_value,usederequested. status: os mesmos valores do crédito. Para uma empresa, o registro on-chain tem duas etapas e só ficasettledquando as duas terminam. Nesse caso,settlement_failednão é final enquanto a Zupy reenvia a última etapa: o status volta asettlinge depois virasettled.- Não há
GETde transferências. Para saber o status atual, reenvie o mesmoPOSTcom a mesmaexternal_referencee os mesmos dados: a resposta200traz a transferência com o status de agora, sem transferir de novo.
7. Saldo
A API do parceiro não expõe o saldo. Acompanhe-o no painel (Integrações → Parceiro Zupy e Carteira Z$). Quando o saldo não cobre a operação, nada é movimentado:402insufficient-balance: o saldo de Z$ da carteira não cobre o crédito ou a transferência;402partner-not-funded-on-chain: a carteira ainda não recebeu Z$ confirmados on-chain (aguarde a confirmação do pacote comprado).
8. Webhooks
A Zupy avisa o seu sistema quando um crédito ou uma transferência é criado. O aviso sai depois que a operação é gravada, nunca dentro da requisição, e nunca para um reenvio idêntico (200).
Configurar
No painel, em Integrações → Parceiro Zupy → Webhook (/integrations/webhooks):
- Informe a URL do seu endpoint (
https://, pública). A Zupy recusa entregar para uma URL que resolva para um endereço interno ou privado. - Escolha os eventos. Sem nenhum evento marcado, você recebe todos.
- Guarde o segredo de assinatura (
whsec_…): ele aparece completo só ao ser gerado ou rotacionado. - Use Enviar teste para receber um evento
pingassinado.
O que chega
POST para a sua URL, com Content-Type: application/json e estes cabeçalhos:
data de partner.transfer.created traz transfer_id, external_reference, amount, reason, customer_id,
destination_company_id, credited_company_id e created_at, e o event_id é transfer:<transfer_id>:created.
Nenhum evento traz dado pessoal (telefone, e-mail, CPF ou carteira), saldo ou status on-chain: o status muda depois
do evento; leia-o em GET /api/v2/partner/credits/{credit_id}/.
Verificar a assinatura
Calcule o HMAC-SHA256 sobre os bytes recebidos, sem reformatar o JSON, e compare em tempo constante.Tentativas e duplicidade
- Responda
2xxrápido (o limite é 10 segundos) e processe depois. - Sem
2xx, a Zupy tenta de novo, com espera crescente, até 4 tentativas no total. Toda tentativa reenvia o mesmo corpo, com o mesmoevent_ide o mesmoX-Webhook-Id. - Deduplique pelo
event_id: o mesmo evento nunca é gerado duas vezes, mas a mesma entrega pode chegar mais de uma vez.
9. Erros
Todo erro vem em JSON no formato RFC 7807 (Problem Details). As recusas de parceiro trazem também umerror_code
estável, e o type termina nele. Trate pelo error_code: o detail é texto para pessoas e pode mudar.
422 validation-error,
com errors apontando cada campo:
Chave e acesso
Créditos e transferências
Em toda recusa, nada é gravado: nenhum Z$ sai da carteira.
10. Limites de requisição
600 requisições por minuto por empresa parceira, somando todas as chaves dela. Acima disso,429: espere e
tente de novo. Criar mais chaves não aumenta o limite.
Próximos passos
- Referência da API: as rotas na aba Partner.
- Coleção Postman: a pasta Issuer Partner (Z$ credits and transfers) usa a variável
partner_api_key. - Erros: cada
error_codetem a sua âncora.