API Youkeep
Documentação da API
Integre o sistema da sua empresa ao WhatsApp, à loja ZaptPos e aos leads de um site na Youkeep. O cliente gera a credencial no painel dele e envia o valor para você. Esta página não pede login e não entrega chave.
Endereço base: https://api.youkeep.com.br
Começo
Toda chamada é HTTPS, JSON, sem cache. A resposta de sucesso traz ok: true e data. O erro traz ok: false, error.code, error.message e request_id.
A credencial já sabe a conta e a propriedade. Não envie identificador de conta, de loja ou de site no corpo. Um campo desses é ignorado.
curl -sS "https://api.youkeep.com.br/v1/me" \
-H "Authorization: Bearer $YOUREKEEP_API_KEY"GET /v1/me devolve o id público da credencial, os produtos, as permissões, os recursos e os limites. Use para conferir se a chave que você recebeu é a esperada, antes de integrar.
Credenciais
Há dois tipos. Os dois começam com yk_, têm um id público e um segredo de 256 bits. O segredo aparece uma vez, no painel de quem criou. A Youkeep guarda só o HMAC, com um pepper que fica no cofre do banco. Nenhum aplicativo da plataforma conhece esse pepper.
- Secreta (
yk_sk_live_…). Só no servidor. Se a chamada vier de um navegador, a API responde 403. Serve para WhatsApp, pedidos e leads. - Publicável (
yk_pk_live_…). Pode ficar no site. Só lê o catálogo do ZaptPos e só nos domínios https cadastrados na credencial.
Cada credencial fica presa a uma propriedade: os canais de WhatsApp marcados, a loja e o catálogo do ZaptPos, ou a propriedade dos leads. Trocar de propriedade no painel não muda uma chave já emitida. Para outro site, o cliente cria outra credencial.
Revogar corta o acesso na hora. A lista guarda o histórico, sem o segredo. Há também credencial de teste (yk_sk_test_…), separada da de produção.
Autenticação
Envie a credencial inteira no cabeçalho, como os bancos e as APIs de pagamento fazem com a chave de servidor:
Authorization: Bearer yk_sk_live_…Não coloque a chave na URL, em log, em repositório ou no aplicativo do celular. No servidor, use uma variável de ambiente. A comparação do segredo no banco não revela se a chave existe, está revogada ou expirou: o código é sempre unauthorized.
Erros
400 invalid_request— corpo ou parâmetro inválido. A mensagem diz o campo.401 unauthorized— chave ausente, inválida, revogada ou expirada.403 forbidden_scope— a chave não tem a permissão, ou uma chave secreta foi usada no navegador.403 forbidden_product— o produto não está na chave ou o contrato não está ativo.403 forbidden_origin— a origem do navegador não está nos domínios da credencial publicável.404— recurso inexistente nesta conta. Canal de outra conta usa o mesmo código, de propósito.409— conflito: mesma Idempotency-Key com outro corpo, estoque, cotação de frete vencida ou template obrigatório.429 rate_limited— limite atingido. Espere o tempo deRetry-After.503 service_unavailable— tente de novo. Uma escrita que recebeu 503 não grava a idempotência e pode ser repetida.
Idempotência e limites
Toda escrita exige Idempotency-Key: 1 a 255 caracteres (A–Z, a–z, 0–9, _ . : -). A mesma chave com o mesmo corpo devolve a mesma resposta por 24 horas. Com outro corpo, 409 idempotency_conflict. Enquanto a primeira ainda corre, 409 idempotency_in_progress.
Há limite por minuto e por dia, por conta e por produto. O padrão é WhatsApp 60 por minuto e 1.000 por dia, ZaptPos 300 por minuto e 50.000 por dia, Growth 30 por minuto e 2.000 por dia. Leads têm mais 5 envios por minuto por visitante. Os cabeçalhos RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset dizem o minuto. O dia volta à meia-noite de Brasília.
Credencial secreta. Permissões: ler canais e templates (whatsapp.read) e enviar (whatsapp.send). A chave só enxerga os canais que o cliente marcou ao criá-la. O nome do provedor, o token e o telefone completo não vêm na resposta. O número aparece só com os 4 últimos dígitos.
curl -sS "https://api.youkeep.com.br/v1/whatsapp/channels" \
-H "Authorization: Bearer $YOUREKEEP_API_KEY"curl -sS "https://api.youkeep.com.br/v1/whatsapp/channels/ID_DO_CANAL/templates" \
-H "Authorization: Bearer $YOUREKEEP_API_KEY"O envio responde 202 com status: "queued". Texto e mídia, num canal que exige template, só entram na janela de 24 horas depois da última mensagem recebida. Fora dela, envie um template aprovado. A mídia é um link https público. Quem baixa o arquivo é o canal, não a Youkeep.
curl -sS -X POST "https://api.youkeep.com.br/v1/whatsapp/messages" \
-H "Authorization: Bearer $YOUREKEEP_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-1001" \
-d '{"channel_id":"ID_DO_CANAL","to":"+5511999999999","type":"text","text":"Olá"}'GET /v1/whatsapp/messages/ID devolve queued, sent, delivered, read ou failed, as datas e um código de erro, sem o telefone. Só mensagem criada por esta API, nesta conta.
ZaptPos
A loja, o catálogo e a página de checkout são os da credencial. O preço e o frete são calculados na Youkeep. O corpo não manda valor.
GET /v1/commerce/categorieseGET /v1/commerce/products— leitura. Filtros de produto:category,q,updated_since. Um item:GET /v1/commerce/products/ID_OU_SLUG.POST /v1/commerce/availability— preço e saldo das variantes. Credencial secreta com permissão de pedido.POST /v1/commerce/shipping/quotes— cotação de frete para os itens e o CEP.POST /v1/commerce/orders— cria o pedido, reserva o estoque e devolvecheckout_url. O comprador paga na página da Youkeep. A API não cobra.GET /v1/commerce/orders/ID— pedido criado por esta API nesta conta.
curl -sS -X POST "https://api.youkeep.com.br/v1/commerce/orders" \
-H "Authorization: Bearer $YOUREKEEP_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-2001" \
-d '{"items":[{"variant_id":"ID_DA_VARIANTE","qty":1}],"buyer":{"name":"Maria","email":"maria@exemplo.com.br"}}'Cotação vencida ou com valor diferente responde 409 shipping_quote_changed e devolve a cotação nova. Sem estoque, 409 out_of_stock. A credencial publicável não cria pedido.
Growth e Leads
POST /v1/leads com credencial secreta. Quem chama é o servidor do site, depois do anti-robô do formulário. A API não confere esse token. A resposta é 202:
id— atendimento aberto no Inbox, origem site.lead_id— lead do funil, ounullquando a conta não tem o módulo Leads.matched—truequando já havia um lead aberto com o mesmo e-mail ou o mesmo telefone.status—received.
No reaproveitamento, a etapa, a prioridade e o responsável ficam como estão. Campos vazios são preenchidos. Campos adicionais enviados entram no lead. Lead encerrado não é reaproveitado: abre outro.
curl -sS -X POST "https://api.youkeep.com.br/v1/leads" \
-H "Authorization: Bearer $YOUREKEEP_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: contato-0001" \
-d '{"form_id":"contato","name":"Maria","email":"maria@exemplo.com.br","phone":"+5511999999999","message":"Quero um orçamento","consent":{"accepted":true,"text":"Concordo em ser contatada."},"client_ip":"203.0.113.10"}'Campos do cadastro, todos opcionais além do nome (no contato), de e-mail ou telefone, do consentimento e do client_ip:
form_kind—contact(padrão) ounewsletter. Newsletter pode ir só com e-mail.company_name,product_interest,contact_subject,preferred_reply_channels(email,whatsapp,phone).document— CPF com 11 dígitos ou CNPJ com 14.city,state(UF),cep(8 dígitos),website(http ou https).priority—baixa,normal,alta,urgente. Padrãonormal. Vale na criação.assigned_to— id de um membro ativo da equipe com acesso a Leads. Fora da equipe, a API recusa antes de abrir o atendimento.stage—novo,em_atendimento,qualificado,proposta,negociacao,encerrado. Padrãonovo.outcome— obrigatório se a etapa éencerrado:ganho,perdido,nao_qualificado,cancelado,duplicado. Perda, desqualificação e cancelamento pedemoutcome_reason. Duplicado pededuplicate_ofde um lead da mesma propriedade.note,custom_fields(chaves dos campos adicionais ativos da propriedade),external_id(até 120 caracteres, só para você correlacionar),page_urlhttps eutm(source,medium,campaign,content,term).
Campo adicional desconhecido ou valor inválido responde 400 e não abre atendimento. Campo obrigatório do painel que o formulário não enviou não bloqueia o envio.
Webhooks
O cliente cadastra um endereço https no painel, no grupo WhatsApp, e escolhe os avisos whatsapp.message.received e whatsapp.message.status. O segredo de assinatura aparece uma vez. Na troca, o segredo anterior ainda assina por 24 horas.
Cabeçalhos: Youkeep-Signature (t=<unix>,v1=<hmac-sha256> de t + "." + corpo), Youkeep-Event e Youkeep-Delivery. Recuse assinatura com mais de 5 minutos e compare em tempo constante. O mesmo desenho usado por APIs de pagamento: carimbo de tempo mais HMAC, para um corpo reenviado não valer depois.
const crypto = require("node:crypto");
function ok(secret, rawBody, header) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Math.floor(Date.now() / 1000) - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(parts.t + "." + rawBody).digest("hex");
const given = Buffer.from(parts.v1 ?? "");
const want = Buffer.from(expected);
return given.length === want.length && crypto.timingSafeEqual(given, want);
}Segurança
O caminho segue o que APIs de pagamento e de banco usam para uma chave de servidor, sem pedir certificado mútuo nem um login OAuth a cada chamada. Quem integra é o sistema da empresa, com uma chave que o cliente entregou. O que isso exige dos dois lados:
- Chave secreta só em variável de ambiente ou cofre. Nunca no JavaScript do site, no aplicativo ou no Git.
- Uma credencial por propriedade e só com as permissões que o sistema usa. Ler catálogo não precisa da permissão de enviar WhatsApp.
- Credencial publicável só se o catálogo for lido no navegador, e só nos domínios exatos do site.
- Idempotency-Key nova por operação. Repetir a mesma chave só para refazer uma chamada que falhou na rede.
- Conferir a assinatura do webhook com o corpo cru, antes de interpretar o JSON.
- Se a chave vazar, o cliente revoga no painel e gera outra. A anterior para na mesma hora.
A conta pausada ou o contrato encerrado param o produto na próxima chamada, sem esperar a chave expirar.