CR Gestão de Lojas
Entrar

API Cards Realm Gestão de Lojas v1

API REST para integrar sua loja com sistemas externos: estoque, pedidos e busca de cartas.

Autenticação

Gere uma chave de API na página de configurações da sua loja (Configurações → API) e envie em todas as requisições no cabeçalho:

Authorization: Bearer crk_live_xxxxxxxxxxxxxxxxxxxxxxxx

Guarde sua chave com segurança. Ela dá acesso total ao estoque e pedidos da sua loja e só é exibida uma vez, no momento em que é gerada.

Limites de uso

120 requisições/minuto para leitura, 60/minuto para escrita, 10/minuto para atualização de preços em massa, por chave de API.

Loja

GET /api/v1/store

Retorna os dados básicos da sua loja.

Estoque

GET /api/v1/products?page=1&q=ilha
POST /api/v1/products
PATCH /api/v1/products/<selling_id>
DELETE /api/v1/products/<selling_id>
POST /api/v1/products/bulk-price

Estes endpoints operam sobre CARTAS do catálogo. Produtos que não são carta (board game, sleeve, playmat, hora de estúdio) vivem em outra tabela e têm endpoints próprios - ver abaixo.

Produtos que não são carta

GET    /api/v1/custom-products
POST   /api/v1/custom-products
PATCH  /api/v1/custom-products/<id>
DELETE /api/v1/custom-products/<id>

Só name e price_cents são obrigatórios na criação; o restante (slug, descrição, categoria, código de barras, localização, atributos livres) é opcional. O PATCH é parcial: o que você não mandar fica como está.

O limite de produtos do seu plano conta cartas e produtos customizados JUNTOS - contar só um lado transformaria a API na porta dos fundos do limite.

Exemplo: adicionar uma carta ao estoque (use /api/v1/cards/search e /api/v1/cards/<card_id>/editions para descobrir os IDs):

curl -X POST https://SEU-DOMINIO/api/v1/products \
  -H "Authorization: Bearer crk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "card_id": 15377,
    "print_id": 222,
    "set_number": "273",
    "quality_id": 1,
    "language_id": 1,
    "quantity": 4,
    "price": "12.50",
    "foil": false
  }'

Atualização de preços em massa:

curl -X POST https://SEU-DOMINIO/api/v1/products/bulk-price \
  -H "Authorization: Bearer crk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"rule": "percent", "value": 10}'

rule: "percent" (percentual +/-), "fixed_add" (soma/subtrai valor fixo), "set_fixed" (define preço fixo). Filtros opcionais: print_id, price_min, price_max.

Carrinho pronto para enviar ao cliente

POST /api/v1/carts
{"items": [{"selling_id": 123, "quantity": 4},
           {"custom_product_id": 9, "quantity": 1},
           {"bundle_id": 3, "quantity": 1}]}

Monta um carrinho e devolve um link para você mandar ao cliente por WhatsApp, e-mail ou o que preferir. Quem abrir vê a lista com preço e estoque do momento e leva tudo para o próprio carrinho com um clique. Serve para recuperação de venda, orçamento e reserva.

{"token": "kR3...", "url": "https://sualoja.com.br/lista/kR3...",
 "lines": 3, "unavailable": [{"selling_id": 123, "requested": 4, "available": 1}]}

Cada item traz exatamente um entre selling_id, custom_product_id e bundle_id, mais um quantity opcional (padrão 1). Este endpoint aceita só JSON - lista de objetos não cabe em formulário. Até 100 linhas por chamada.

Um item que não é da sua loja recusa a chamada inteira (400), dizendo qual linha e por quê - gerar um link com parte da lista faria você achar que deu certo e o cliente receber menos do que foi prometido. Já estoque que acabou não é erro: o link sai assim mesmo e o que falta vem em unavailable, para a sua automação decidir se avisa, troca o item ou não envia.

O link não guarda preço, de propósito: preço e disponibilidade são lidos ao vivo quando o cliente abre, então um link de semanas atrás nunca vende pelo valor de antes. Se você precisa do valor para escrever a mensagem, leia GET /api/v1/products no mesmo fluxo.

Pedidos

GET /api/v1/orders
GET /api/v1/orders/<order_id>

O mesmo formato é usado nos webhooks de pedido:

{
  "order_id": 123,
  "status": "paid",
  "buyer_name": "...", "buyer_email": "...",
  "currency": "BRL",
  "subtotal_cents": 30000,
  "discount_cents": 6000,
  "coupon_code": "PROMO20",
  "commission_cents": 2020,
  "commission_rate": 0.08,
  "commission_fixed_cents": 100,
  "store_payout_cents": 21980,
  "shipping_cents": 1500, "shipping_carrier": "jadlog", "shipping_service": "Package",
  "insurance_cents": 0,
  "fulfillment_method": "shipping",
  "pickup_scheduled_at": null,
  "created_at": "...", "paid_at": "...",
  "items": [
    {
      "card_name": "...", "edition": "...", "quality": "NM", "language": "English",
      "quantity": 2, "unit_price_cents": 10000,
      "is_custom_product": false,
      "is_preorder": false, "full_price_cents": null,
      "bundle_name": null
    }
  ]
}

Atenção ao conferir valores: subtotal_cents é o valor ANTES do desconto. O que o cliente pagou pelos produtos é subtotal_cents - discount_cents, e é sobre esse valor que commission_cents é calculada. A comissão tem duas partes: commission_fixed_cents somado a commission_rate vezes esse valor - as duas ficam congeladas no pedido, então uma mudança de preço da plataforma nunca reescreve uma venda já feita. Em um item de pré-venda, unit_price_cents é apenas o depósito cobrado agora - full_price_cents traz o preço cheio.

Busca de cartas

GET /api/v1/cards/search?q=ilha
GET /api/v1/cards/<card_id>/editions

Marcar um pedido como enviado

POST /api/v1/orders/<order_id>/ship
  {"tracking_code": "AA123456789BR", "tracking_carrier": "correios"}

Guarda o rastreio, avisa o comprador por e-mail com o link da transportadora e dispara o evento order.shipped. Os dois campos são opcionais - dá para despachar sem código (entrega em mãos, motoboy), e o que muda a vida do comprador é saber que saiu. Só um pedido pago pode ser despachado.

Agendamento

Para um bot (n8n, WhatsApp) marcar horário e cobrar a caução sozinho. Estes endpoints obedecem exatamente as mesmas regras da página pública de agendamento - janela de atendimento, antecedência mínima, intervalo entre atendimentos e bloqueios -, então o bot nunca oferece um horário que a loja não abre.

GET  /api/v1/booking-services
GET  /api/v1/booking-services/<id>/slots?from=2026-09-01&days=30
POST /api/v1/bookings
GET  /api/v1/bookings            (filtro opcional ?status=confirmed)
GET  /api/v1/bookings/<id>
POST /api/v1/bookings/<id>/cancel
POST /api/v1/bookings/<id>/payment-link
GET  /api/v1/clients?phone=5521999998888
GET  /api/v1/clients?email=...&document=...
POST /api/v1/clients

Criar um agendamento (só JSON):

POST /api/v1/bookings
  {"service_id": 3, "starts_at": "2026-09-01T14:00",
   "customer_name": "Maria Silva", "customer_email": "maria@exemplo.com",
   "customer_phone": "21999998888", "notes": "primeira vez"}

O link da caução EXPIRA em 24h - `payment-link` gera outro reusando o mesmo pedido, em vez de criar uma cobrança nova a cada reenvio. É o que permite um bot marcar hoje e reenviar amanhã sem desmarcar.

A busca de cliente responde 200 com found: false quando o contato não é cliente - não é um erro, é a resposta à pergunta. O telefone é normalizado, e o código do país que o WhatsApp manda (55) é tratado. A resposta traz o saldo de horas do kit e se o cliente dispensa caução.

A resposta traz o agendamento e, quando o atendimento tem caução, payment_url - o link da Stripe para mandar na conversa. Enquanto ele não é pago o horário fica RESERVADO até hold_expires_at e depois volta a ficar livre sozinho. Atendimento gratuito nasce confirmed na hora e sem link.

Desmarcar libera o horário e NÃO estorna a caução: quanto volta depende da política da loja, e isso continua sendo feito na tela de vendas.

Webhooks

Configure uma URL em Configurações → Webhooks para receber uma notificação HTTP quando algo acontecer, em vez de precisar consultar GET /api/v1/orders repetidamente.

Eventos disponíveis: order.created, order.paid, order.shipped, order.refunded, return.requested, return.resolved, booking.created, booking.canceled, booking.reminder, além de ping (evento de teste).

Os eventos de agendamento carregam o mesmo objeto que GET /api/v1/bookings/<id>. booking.reminder sai uma vez por agendamento, cerca de 24h antes da hora marcada - é o gancho para mandar a lembrança no WhatsApp pelo n8n. NÃO existe um evento separado de "sinal pago": quando a caução cai, chega o order.paid de sempre, e a linha dele traz o booking_id do agendamento que acabou de ser confirmado.

Os eventos de devolução carregam return_id, order_id, resolution e refund_cents. Uma devolução parcial NÃO muda o status do pedido, que continua paid - o valor devolvido aparece em refunded_cents no pedido.

Cada requisição é assinada em HMAC-SHA256 (chave mostrada na página de Webhooks) e enviada com os cabeçalhos:

X-CardsRealm-Event: order.paid
X-CardsRealm-Signature: <hmac_sha256(secret, corpo_da_requisicao)>

Verifique a assinatura antes de confiar no conteúdo - calcule o HMAC-SHA256 do corpo bruto da requisição com sua chave e compare com o cabeçalho recebido.

Conectando com o n8n

O n8n fala HTTP puro, então não precisa de nada especial dos dois lados: um nó HTTP Request chama esta API, e um nó Webhook recebe os nossos eventos.

Nó pronto: montar o carrinho e pegar o link

Copie o bloco abaixo e cole direto na tela do n8n (Ctrl+V no canvas cria o nó). Troque a chave e o domínio, e ligue a saída dele no seu nó de WhatsApp ou e-mail - o link fica em url.

{
  "nodes": [
    {
      "parameters": {
        "method": "POST",
        "url": "https://SEU-DOMINIO/api/v1/carts",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            { "name": "Authorization", "value": "Bearer crk_live_SUA_CHAVE" }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ items: [ { selling_id: 123, quantity: 4 } ] }) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [0, 0],
      "name": "Criar carrinho"
    }
  ],
  "connections": {}
}

Para montar a lista a partir do que veio antes no fluxo, troque o conteúdo de items por uma expressão que devolva a mesma forma - por exemplo mapeando as linhas de uma planilha para { selling_id, quantity }.

Nó pronto: marcar horário e pegar o link da caução

Mesma ideia, para a agenda. A resposta traz payment_url quando o atendimento tem caução - é esse link que vai na conversa. Um atendimento gratuito devolve null ali e já nasce confirmado, então ramifique nesse campo em vez de assumir que sempre há cobrança.

{
  "nodes": [
    {
      "parameters": {
        "method": "POST",
        "url": "https://SEU-DOMINIO/api/v1/bookings",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            { "name": "Authorization", "value": "Bearer crk_live_SUA_CHAVE" }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ service_id: 3, starts_at: '2026-09-01T14:00', customer_name: $json.nome, customer_phone: $json.telefone, customer_email: $json.email }) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [0, 0],
      "name": "Marcar horario"
    }
  ],
  "connections": {}
}

Antes deste nó, chame GET /api/v1/booking-services/<id>/slots para oferecer à pessoa só os horários que existem de verdade - a API recusa qualquer outro com 409, e é melhor não oferecer do que oferecer e voltar atrás.

Recebendo nossos eventos no n8n

Crie um nó Webhook (método POST), copie a URL de produção dele e cole em Configurações → Webhooks. Confira a assinatura antes de confiar no conteúdo, num nó Code logo depois:

const crypto = require('crypto');
const segredo = 'SEU_SEGREDO_DE_WEBHOOK';
const corpo = JSON.stringify($input.first().json.body);
const esperado = crypto.createHmac('sha256', segredo).update(corpo).digest('hex');
const recebido = $input.first().json.headers['x-cardsrealm-signature'];
if (esperado !== recebido) { throw new Error('assinatura invalida'); }
return $input.all();

Ative "Raw body" no nó Webhook se a sua versão do n8n reordenar as chaves do JSON: a assinatura é calculada sobre os bytes exatos que enviamos, então qualquer reserialização pelo caminho muda o resultado.

Erros

Respostas de erro sempre têm o formato {"error": "mensagem"} com o código HTTP apropriado (400, 401, 404, 429).

← Voltar ao painel

Produto

  • Funcionalidades
  • Planos e preços
  • Taxas e comissão
  • Lojas na plataforma
  • Criar loja grátis

Para lojistas

  • Central de ajuda
  • Falar com a gente
  • Código de Conduta
  • Como recebo minhas vendas

Desenvolvedores

  • Documentação da API
  • llms.txt
  • security.txt
  • Status da plataforma

Institucional

  • Sobre a Cards Realm
  • Contato comercial
  • Canal de Denúncias
  • Acompanhar protocolo

Redes sociais

Fale com a gente:
gestao@cardsrealm.com

Termos de Uso Privacidade Código de Conduta Canal de Denúncias Status API
CARDS REALM TECNOLOGIA LTDA · CNPJ 58.339.744/0001-15
Av. Rio Branco, 156 — sala 2538, Centro, Rio de Janeiro/RJ, 20040-003