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 de Retry-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.

WhatsApp

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/categories e GET /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 devolve checkout_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, ou null quando a conta não tem o módulo Leads.
  • matched — true quando 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) ou newsletter. 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ão normal. 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ão novo.
  • outcome — obrigatório se a etapa é encerrado: ganho, perdido, nao_qualificado, cancelado, duplicado. Perda, desqualificação e cancelamento pedem outcome_reason. Duplicado pede duplicate_of de 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_url https e utm (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.