# API Aprovei - Contrato completo ## GET /orders e GET /orders/{orderId} — Consulta de pedidos Documentação: https://app.aproveicheckout.com.br/docs/v1/pedidos Authorization: Bearer CREDENCIAL_FICTICIA, X-Store-Slug e Accept: application/json obrigatórios. Permissão explícita orders:read; * não basta. Em API Keys, abrir Permissões na chave existente, marcar orders:read e salvar. list_stores anuncia orders:read somente para chaves autorizadas. Não há tool de leitura de pedidos no MCP nesta versão. GET /orders exige exatamente um cabeçalho: X-Customer-Email ou X-Customer-CPF. Não enviar identificadores na URL. E-mail: trim + minúsculas, comparação exata, preservando pontos e +; não expandir para outros e-mails do mesmo CPF. CPF: 11 dígitos válidos, com ou sem máscara; reúne todos os cadastros correspondentes dentro da loja. CNPJ não é aceito. Query: page >= 1, per_page entre 1 e 50 (padrão 20). Ordenação: created_at DESC, id DESC; todos os status e todo o histórico. Resposta: {"orders":[],"pagination":{"page":1,"per_page":20,"total":0,"total_pages":1}}. Cada resumo inclui id, transactionId, status {id,slug,name}, currency, totalInCents, createdAt, updatedAt, paidAt, refundedAt e items. Itens resumidos: id, productId público, internalProductId interno, parentKitId interno e quantity. GET /orders/{orderId} usa orders.id interno positivo e não exige e-mail/CPF; resposta {"order":{...}}. Detalhe acrescenta subtotalInCents, discountInCents, shippingInCents, customer {name,email,cpf,phone}, shippingAddress, trackingCode, deliveryStatus, shipments (envios, itens/quantidades, rastreios e datas), payments e itens completos com nome/SKU históricos, valores, IDs externos e flags de oferta. Dados pessoais são do cadastro atual; endereço e valores são do pedido. Produtos excluídos não eliminam itens históricos. Valores monetários em centavos, datas ISO 8601 UTC, campos ausentes null. paidAt é o menor orders_payments.paid_at registrado; estorno não apaga essa data histórica. Pagamentos expõem method, gatewayStatus original, amountInCents, installments, createdAt e paidAt. Não expor payloads de gateway/cartão, credenciais, IPs, custos ou comissões. Não inferir vigência ou elegibilidade. 200 com lista vazia quando não há pedidos; 404 ORDER_NOT_FOUND para ID inexistente ou de outra loja. 401 credencial; 403 permissão/loja/IP; 422 identificadores/paginação inválidos; 429 limite; 503 ORDERS_UNAVAILABLE para falha de consulta. Erros: {status:"error",code,message,errors?}. Falha parcial nunca vira lista vazia. Cache-Control contém no-store. Limite conjunto de 60/min por chave+loja para as duas rotas, além de 600/min por IP antes da autenticação; respeitar Retry-After. Leitura sem alteração de pedidos, pagamentos, auditoria em banco ou last_used_at. Contadores do limitador podem mudar. Não registrar Authorization, X-Customer-Email, X-Customer-CPF ou conteúdo das respostas em logs/APM. Base URL: https://api.aproveicheckout.com.br/v1 Documentação canônica: https://app.aproveicheckout.com.br/docs Visão geral de integração com IA: https://app.aproveicheckout.com.br/docs/ia Guia para agentes via API REST: https://app.aproveicheckout.com.br/docs/ia-agents Versão curta deste arquivo: https://app.aproveicheckout.com.br/docs/llms.txt Spec OpenAPI 3.1: https://app.aproveicheckout.com.br/docs/openapi.json Use esta referência para Lovable, Cursor, v0, Bolt.new, agentes LLM e automações externas. Servidor MCP (para agentes operarem a loja direto no chat, sem escrever código): - Endpoint Streamable HTTP: https://api.aproveicheckout.com.br/mcp - Auth: mesma API Key Bearer da API Aprovei, sem header de loja (loja via parâmetro storeSlug por tool call) - MCP usa storeSlug; não envie storeId nas tool calls. - Tools: list_stores, get_store, list_products, get_product, search_products, create_checkout, list_webhooks, create_webhook, update_webhook, delete_webhook - Guia: https://app.aproveicheckout.com.br/docs/mcp ## Autenticação Headers obrigatórios em todas as requisições: - Authorization: Bearer - X-Store-Slug: Adicionais: - Content-Type: application/json em requisições POST - Idempotency-Key: em POST /cart/create-checkout (obrigatório) Crie a API Key no painel da Aprovei pelo avatar/menu do usuário no topo direito > API Keys. Permissões disponíveis: storefront:read, checkout:create, pix:create, webhooks:read, webhooks:create. O mesmo token pode acessar todas as lojas ativas do usuário dono do token; a loja alvo é definida por requisição via X-Store-Slug. O token pode ter allowlist de IPs configurada no painel. Segurança: - Nunca exponha o token Bearer em frontend público. - Guarde tokens em variáveis de ambiente ou backend/server function. - Não envie credenciais, tokens, gateways, billing ou dados sensíveis para usuários finais. ## Formato de erro (todas as rotas) { "status": "error", "code": "CODIGO_DO_ERRO", "message": "Descrição legível.", "errors": { "campo": ["detalhe"] } } O campo errors só aparece em VALIDATION_ERROR. Códigos de autenticação: - AUTH_MISSING_BEARER_TOKEN (401): header Authorization ausente. - AUTH_INVALID_BEARER_TOKEN (401): token inválido ou revogado. - AUTH_STORE_HEADER_MISSING (400): header X-Store-Slug ausente. - AUTH_STORE_FORBIDDEN (403): loja inexistente, inativa ou sem vínculo com o dono do token; também usado quando o token não tem a ability exigida. - AUTH_IP_NOT_ALLOWED (403): IP fora da allowlist do token. Códigos gerais: - UNSUPPORTED_MEDIA_TYPE (415): POST sem Content-Type: application/json. - VALIDATION_ERROR (422): payload ou query inválidos; veja errors. - NOT_FOUND (404): rota ou recurso inexistente. - RATE_LIMIT_EXCEEDED (429): limite excedido; respeite o header Retry-After. - INTERNAL_ERROR (500): erro interno. ## Rate limits - GET /store, /products, /products/{productId}, /search, /webhooks: 300 req/min por token+loja. - POST /cart/create-checkout, POST /v1/pix e POST /webhooks: 60 req/min por token+loja. - Teto global: 600 req/min por IP. ## Identificadores - productId e variantId usam o mesmo espaço de identificadores (products.product_id), sempre strings. - Produtos sem variações expõem a si mesmos como única variante (variantId == productId); produtos com variações exigem o variantId da variante. - O slug da loja é o identificador público da loja na API REST v1, enviado em X-Store-Slug. - Slug de produto NÃO é identificador público; use productId e variantId para produtos. - IDs internos do banco não devem ser usados por integrações externas. ## Endpoints ### GET /store Retorna dados públicos da loja do X-Store-Slug. Resposta 200: { "store": { "name": "Minha Loja", "slug": "minha-loja", "domain": "loja.exemplo.com.br", "logo": "https://.../logo.png", "currency": { "id": 1, "code": "BRL", "symbol": "R$" }, "language": { "id": 1, "code": "pt-BR", "name": "Português" }, "settings": { "header": { "logo": "https://.../logo.png", "menus": [] }, "footer": { "text": null, "menus": [] }, "cart": { "skip_cart": false, "checkoutUrl": "https://loja.exemplo.com.br/checkout" }, "theme": { "colors": [], "sections": [] } } } } ### GET /products Lista o catálogo paginado. Query params (todos opcionais): - page: inteiro >= 1 (default 1) - per_page: inteiro 1-50 (default 20) - sort_by: created_at | title | price - sort_order: asc | desc Resposta 200: { "products": [ ], "pagination": { "page": 1, "per_page": 20, "total": 42, "total_pages": 3 } } Objeto Product: { "id": "101", "productId": "101", "handle": "101", "title": "Nome do produto", "description": "Descrição", "image": "https://.../capa.png", "images": ["https://.../1.png"], "sku": "SKU-1", "barcode": "789...", "productType": "physical" | "digital", "availableForSale": true, "priceInCents": 9990, "compareAtPriceInCents": 12990, "inventoryQuantity": 10, "variants": [ ], "createdAt": "2026-01-01T00:00:00-03:00", "updatedAt": "2026-01-01T00:00:00-03:00" } Objeto Variant: { "id": "102", "variantId": "102", "title": "Tamanho M", "sku": "SKU-1-M", "barcode": null, "image": "https://.../m.png", "availableForSale": true, "priceInCents": 9990, "compareAtPriceInCents": null, "inventoryQuantity": 5 } Notas: - priceInCents é sempre o preço efetivo (promocional quando existir). - compareAtPriceInCents é o preço cheio quando há promoção; null caso contrário. - inventoryQuantity é null quando a loja não controla estoque. ### GET /products/{productId} Detalhe de um produto pelo productId. Resposta 200: { "product": } Erro 404: PRODUCT_NOT_FOUND. ### GET /search?q=termo Busca produtos por nome, descrição, SKU ou barcode. Query params: - q: obrigatório, string - page: inteiro >= 1 - per_page: inteiro 1-50 Resposta 200: { "query": "termo", "products": [ ], "pagination": { "page": 1, "per_page": 20, "total": 3, "total_pages": 1 } } ### POST /cart/create-checkout Cria um link de checkout com os itens informados. Exige header Idempotency-Key (chave única por checkout; reenviar a mesma chave com o mesmo body faz replay da resposta original). Body (nenhum campo extra é aceito): { "items": [ { "variantId": "101", "quantity": 1 } ] } Regras: items com 1 a 50 itens; variantId string; quantity inteiro entre 1 e 999. Resposta 200: { "active": true, "skip_cart": false, "checkout_direct_url": "https://loja.exemplo.com.br/checkout/token", "checkoutUrl": "https://loja.exemplo.com.br/checkout/token" } Redirecione o comprador para checkoutUrl (ou checkout_direct_url). Erros: - IDEMPOTENCY_KEY_MISSING (400): header ausente. - IDEMPOTENCY_KEY_CONFLICT (409): mesma chave com body diferente. - PRODUCT_NOT_FOUND (404): variantId inexistente na loja. - STOCK_UNAVAILABLE (400): estoque insuficiente. - CHECKOUT_DOMAIN_NOT_CONFIGURED (422): loja sem domínio de vendas ativo. Exemplo: curl -X POST "https://api.aproveicheckout.com.br/v1/cart/create-checkout" \ -H "Authorization: Bearer ${APROVEI_API_KEY}" \ -H "X-Store-Slug: ${APROVEI_STORE_SLUG}" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: checkout-${UNIQUE_ID}" \ -d '{"items":[{"variantId":"101","quantity":1}]}' ### POST /v1/pix Cria uma cobrança PIX para a loja do X-Store-Slug. Exige API Key com ability pix:create. Body mínimo: { "customer": { "name": "João da Silva", "email": "joao@example.com", "document": "12345678900", "phone": "+5511999999999" }, "items": [ { "externalRef": "produto-001", "title": "Produto Teste", "unitPrice": 9900, "quantity": 1, "isDigital": true } ], "payment": { "paymentMethod": "pix", "amount": 9900, "freightValue": 0, "discountValue": 0, "currency": "BRL" } } Keys do body: customer{name,email,document,phone,address{street,number,district,city,state,zipCode,complement}}, items[]{externalRef,title,unitPrice,quantity,isDigital,description?,imageUrl?,fileUrl?}, payment{paymentMethod,amount,freightValue,discountValue,externalRef?,currency}, postbackUrl?. Endereço (customer.address) é obrigatório quando algum item tiver isDigital=false. Frete (payment.freightValue) deve ser 0 em pedidos 100% digitais. Exemplo: curl -X POST "https://api.aproveicheckout.com.br/v1/pix" \ -H "Authorization: Bearer ${APROVEI_API_KEY}" \ -H "X-Store-Slug: ${APROVEI_STORE_SLUG}" \ -H "Content-Type: application/json" \ -d @payload.json Erros: - AUTH_STORE_FORBIDDEN (403): loja sem vínculo ou API Key sem pix:create. - VALIDATION_ERROR (422): payload inválido. - PIX_GATEWAY_NOT_CONFIGURED (422): loja sem gateway PIX ativo. ### GET /webhooks Lista os webhooks da loja. O secret completo nunca é retornado (apenas secret_preview). Resposta 200: { "webhooks": [ { "id": 1, "url": "https://exemplo.com/webhook", "events": ["order.paid"], "is_active": true, "created_at": "...", "updated_at": "...", "secret_preview": "whsec_...abc" } ] } ### POST /webhooks Cria um webhook. Máximo de 10 webhooks por loja. O secret completo é retornado SOMENTE nesta resposta de criação — armazene-o. Body: { "url": "https://exemplo.com/webhook", "events": ["order.paid", "cart.abandoned"] } Regras: url https obrigatório em produção (max 500 chars); events com 1+ eventos da lista de eventos públicos. Resposta 201: { "webhook": { ... }, "secret": "whsec_..." } Erro: WEBHOOK_LIMIT_EXCEEDED (422). ## Webhooks - entrega e assinatura - Assinatura outbound: header X-Aprovei-Signature: sha256= - HMAC-SHA256 calculado sobre o body JSON bruto enviado, usando o secret do webhook. - Entrega: timeout 5s, até 5 tentativas com backoff de 1min, 5min, 30min e 2h. - Sucesso na entrega = qualquer resposta HTTP 2xx. Eventos públicos: - order.created - payment.pix.created - payment.boleto.created - order.paid - order.processing - order.refused - order.cancelled - order.chargeback - order.refunded - order.expired - cart.abandoned ## Fluxo recomendado para agentes 1. Consulte GET /store. 2. Liste produtos em GET /products. 3. Use GET /search?q=termo para buscar por nome, descrição, SKU ou barcode. 4. Monte items usando variantId retornado pela API. 5. Envie POST /cart/create-checkout com Idempotency-Key. 6. Redirecione o usuário para checkoutUrl. ## Recursos não disponíveis nesta versão - APIs de coleções. - Produtos recomendados. - Busca de produto por slug.