API REST
Versão: 1.0
Última atualização: 2025
Base URL (produção):https://ljvelasearomas.com.br
Base URL (local):http://localhost:3000
Sumário
- Visão Geral
- Autenticação
- Convenções de Resposta
- Auth — Conta e Sessão
- Produtos (público)
- Categorias (público)
- Pedidos (cliente autenticado)
- Pagamentos
- Frete
- Cupons
- Usuário — Área Logada
- Newsletter
- Contato
- Admin
- 14.1 Produtos
- 14.2 Variantes
- 14.3 Upload de Imagens
- 14.4 Pedidos
- 14.5 Cupons
- 14.6 Clientes
- 14.7 Avaliações
- 14.8 Estoque
- 14.9 Frete (Tarifas e Provedores)
- 14.10 Webhooks Outbound
- 14.11 API Keys
- 14.12 Dashboard
- 14.13 Configurações
- API v1 — Integração Programática
- Webhook Inbound — Estoque
1. Visão Geral
A API da LJ Velas & Aromas é uma API REST construída com Next.js App Router. Todas as rotas retornam JSON e aceitam corpos no formato application/json, salvo quando indicado multipart.
Ambiente e Base URL
| Ambiente | Base URL |
|---|---|
| Produção | https://ljvelasearomas.com.br |
| Desenvolvimento | http://localhost:3000 |
Todos os endpoints descritos neste documento são prefixados com /api/ (ex.: POST /api/auth/login).
Formato das Respostas
Todas as respostas usam o Content-Type application/json; charset=utf-8.
Paginação Padrão
Endpoints que retornam listas paginadas incluem o objeto pagination na resposta:
{
"data": [ ... ],
"pagination": {
"page": 1,
"limit": 20,
"total": 143,
"totalPages": 8
}
}
Os parâmetros de controle são enviados via query string:
| Parâmetro | Tipo | Padrão | Máximo | Descrição |
|---|---|---|---|---|
page | integer | 1 | — | Número da página |
limit | integer | 20 | 100 | Itens por página |
Códigos de Status HTTP Utilizados
| Código | Significado |
|---|---|
200 | Sucesso |
201 | Recurso criado com sucesso |
204 | Sucesso sem corpo de resposta |
400 | Requisição inválida (corpo malformado, validação) |
401 | Não autenticado (sem credenciais ou token inválido) |
403 | Acesso negado (autenticado, mas sem permissão) |
404 | Recurso não encontrado |
409 | Conflito (ex.: slug ou e-mail já existente) |
422 | Entidade não processável (validação semântica) |
429 | Muitas requisições (rate limit atingido) |
500 | Erro interno do servidor |
2. Autenticação
A API oferece três mecanismos de autenticação dependendo do contexto.
2.1 Cookie JWT (Loja + Admin)
Utilizado pelas rotas da loja (/api/auth/, /api/orders/, /api/user/, /api/payments/) e pelo painel Admin (/api/admin/).
Após um POST /api/auth/login bem-sucedido, um cookie HttpOnly chamado token é definido automaticamente pelo servidor. O browser envia este cookie em todas as requisições subsequentes para o mesmo domínio.
O token JWT contém: { userId, email, role }.
Nota: A resposta do
POST /api/auth/logintambém devolve o token JWT no campotokendo corpo JSON. Clientes SPA podem armazená-lo manualmente se necessário; clientes web browser utilizam o cookie automaticamente.
2.2 API Key Bearer (API v1 + Webhook Inbound)
Utilizado pelos endpoints /api/v1/ e /api/webhooks/stock.
Authorization: Bearer lvk_live_<token>
A chave é gerada pelo painel Admin (/api/admin/api-keys) e cada chave possui escopos que limitam as operações permitidas. O hash SHA-256 da chave é armazenado no banco; o valor bruto é exibido uma única vez na criação.
2.3 Rate Limiting em Login
O endpoint POST /api/auth/login aplica rate limiting por IP para proteção contra ataques de força bruta:
| Limite | Janela | Header de Resposta |
|---|---|---|
| 10 tentativas | 15 minutos | Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining |
Ao exceder o limite, a resposta será 429 Too Many Requests com a mensagem "Muitas tentativas. Tente novamente em X minutos.".
3. Convenções de Resposta
Resposta de Sucesso
// Recurso único
{
"id": "clx1234abcd",
"name": "Vela de Lavanda 200g",
"price": 39.90
}
// Coleção paginada
{
"data": [ { ... }, { ... } ],
"pagination": {
"page": 1,
"limit": 20,
"total": 48,
"totalPages": 3
}
}
Resposta de Erro
Todos os erros seguem o formato:
{
"error": "Descrição legível do erro"
}
// Erro de validação com detalhes
{
"error": "Payload inválido",
"details": {
"fieldErrors": { "email": ["Email inválido"] },
"formErrors": []
}
}
4. Auth — Conta e Sessão
POST /api/auth/register
Cria uma nova conta de cliente.
Autenticação: Nenhuma
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | ✅ | Nome completo |
email | string | ✅ | E-mail válido |
password | string | ✅ | Senha do usuário |
Exemplo de request:
{
"name": "Maria Silva",
"email": "maria@email.com",
"password": "MinhaSenh@123"
}
Resposta 201 Created:
{
"user": {
"id": "clx1a2b3c4d",
"name": "Maria Silva",
"email": "maria@email.com",
"role": "CUSTOMER"
}
}
Erros:
| Código | Motivo |
|---|---|
400 | Campos inválidos ou ausentes |
409 | E-mail já cadastrado |
500 | Erro interno |
Exemplo cURL:
curl -X POST https://ljvelasearomas.com.br/api/auth/register \
-H "Content-Type: application/json" \
-d '{"name":"Maria Silva","email":"maria@email.com","password":"MinhaSenh@123"}'
POST /api/auth/login
Autentica o usuário. Define o cookie token (JWT) e retorna os dados do usuário.
Autenticação: Nenhuma (rate limited: 10 req / 15 min por IP)
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
email | string | ✅ | |
password | string | ✅ | Senha |
Resposta 200 OK:
{
"user": {
"id": "clx1a2b3c4d",
"name": "Maria Silva",
"email": "maria@email.com",
"phone": null,
"role": "CUSTOMER"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
O cookie HttpOnly token é definido automaticamente na resposta.
Headers de resposta em rate limit (429):
| Header | Valor |
|---|---|
Retry-After | Segundos até liberar |
X-RateLimit-Limit | 10 |
X-RateLimit-Remaining | 0 |
Erros:
| Código | Motivo |
|---|---|
400 | Campos inválidos |
401 | Email ou senha inválidos |
403 | Conta suspensa |
429 | Rate limit atingido |
500 | Erro interno |
POST /api/auth/logout
Encerra a sessão limpando o cookie token.
Autenticação: Cookie JWT (opcional — o cookie é removido independente de ser válido)
Body: Nenhum
Resposta 200 OK:
{
"message": "Logout realizado com sucesso"
}
GET /api/auth/me
Retorna os dados do usuário autenticado pela sessão atual.
Autenticação: Cookie JWT ✅
Resposta 200 OK:
{
"id": "clx1a2b3c4d",
"name": "Maria Silva",
"email": "maria@email.com",
"phone": null,
"role": "CUSTOMER",
"createdAt": "2024-03-15T10:30:00.000Z"
}
Erros:
| Código | Motivo |
|---|---|
401 | Não autenticado |
PATCH /api/auth/me/password
Altera a senha do usuário autenticado.
Autenticação: Cookie JWT ✅
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
currentPassword | string | ✅ | Senha atual |
newPassword | string | ✅ | Nova senha |
Resposta 200 OK:
{
"message": "Senha alterada com sucesso"
}
Erros:
| Código | Motivo |
|---|---|
400 | Campos ausentes ou nova senha inválida |
401 | Não autenticado ou senha atual incorreta |
5. Produtos (público)
GET /api/products
Lista produtos ativos com filtros, ordenação e paginação.
Autenticação: Nenhuma
Query Params:
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
page | integer | 1 | Número da página |
limit | integer | 20 | Itens por página (máx. 100) |
search | string | — | Busca por nome ou descrição |
category | string | — | Slug da categoria |
featured | boolean | — | Somente produtos em destaque (true) |
sort | string | newest | price_asc, price_desc, newest, popular |
minPrice | number | — | Preço mínimo (R$) |
maxPrice | number | — | Preço máximo (R$) |
inStock | boolean | — | Somente produtos com estoque (true) |
Resposta 200 OK:
{
"data": [
{
"id": "clx1a2b3c4d",
"name": "Vela de Lavanda 200g",
"slug": "vela-lavanda-200g",
"price": 39.90,
"comparePrice": 49.90,
"stock": 25,
"featured": true,
"category": {
"id": "clxcat001",
"name": "Velas Aromáticas",
"slug": "velas-aromaticas"
},
"images": [
{
"url": "https://cdn.ljvelasearomas.com.br/vela-lavanda-200g-01.jpg",
"alt": "Vela de Lavanda 200g",
"isPrimary": true
}
],
"reviewCount": 12,
"averageRating": 4.8
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 48,
"totalPages": 3
}
}
Exemplo cURL:
curl "https://ljvelasearomas.com.br/api/products?category=velas-aromaticas&sort=price_asc&inStock=true&page=1&limit=12"
GET /api/products/[slug]
Retorna os detalhes completos de um produto pelo slug.
Autenticação: Nenhuma
Path Params:
| Parâmetro | Tipo | Descrição |
|---|---|---|
slug | string | Slug do produto |
Resposta 200 OK:
{
"id": "clx1a2b3c4d",
"name": "Vela de Lavanda 200g",
"slug": "vela-lavanda-200g",
"description": "Vela artesanal com aroma de lavanda...",
"price": 39.90,
"comparePrice": 49.90,
"stock": 25,
"sku": "VEL-LAV-200",
"weight": 0.3,
"featured": true,
"active": true,
"category": {
"id": "clxcat001",
"name": "Velas Aromáticas",
"slug": "velas-aromaticas"
},
"images": [
{
"id": "clximg001",
"url": "https://cdn.ljvelasearomas.com.br/vela-lavanda-200g-01.jpg",
"alt": "Vela de Lavanda 200g",
"isPrimary": true,
"order": 0
}
],
"variants": [
{
"id": "clxvar001",
"price": 39.90,
"stock": 10,
"sku": "VEL-LAV-200-ROXO",
"values": [
{ "variantType": "Cor", "value": "Roxo" }
]
}
],
"reviews": [
{
"id": "clxrev001",
"rating": 5,
"title": "Produto incrível!",
"body": "Aroma suave e duradouro.",
"author": "Maria S.",
"createdAt": "2024-04-10T08:00:00.000Z"
}
],
"reviewCount": 12,
"averageRating": 4.8,
"createdAt": "2024-01-20T12:00:00.000Z"
}
Erros:
| Código | Motivo |
|---|---|
404 | Produto não encontrado ou inativo |
GET /api/products/search
Busca produtos com highlight do termo pesquisado no nome e descrição.
Autenticação: Nenhuma
Query Params:
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
q | string | ✅ | Termo de busca |
page | integer | — | Página (padrão 1) |
limit | integer | — | Limite (padrão 20) |
Resposta 200 OK:
{
"data": [
{
"id": "clx1a2b3c4d",
"name": "Vela de <mark>Lavanda</mark> 200g",
"slug": "vela-lavanda-200g",
"price": 39.90,
"highlight": "aroma de <mark>lavanda</mark> pura..."
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 3,
"totalPages": 1
},
"query": "lavanda"
}
GET /api/products/[slug]/reviews
Lista as avaliações aprovadas de um produto.
Autenticação: Nenhuma
Resposta 200 OK:
{
"data": [
{
"id": "clxrev001",
"rating": 5,
"title": "Produto incrível!",
"body": "Aroma suave e duradouro. Vou comprar novamente.",
"author": "Maria S.",
"createdAt": "2024-04-10T08:00:00.000Z"
}
],
"summary": {
"average": 4.8,
"total": 12,
"distribution": {
"5": 9, "4": 2, "3": 1, "2": 0, "1": 0
}
}
}
POST /api/products/[slug]/reviews
Envia uma avaliação para um produto. Requer autenticação.
Autenticação: Cookie JWT ✅ (role: CUSTOMER)
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
rating | integer | ✅ | Nota de 1 a 5 |
title | string | — | Título da avaliação |
body | string | ✅ | Texto da avaliação |
Nota: A review é criada com
status: pendinge precisa ser aprovada por um administrador antes de aparecer publicamente.
Resposta 201 Created:
{
"id": "clxrev099",
"rating": 5,
"title": "Adorei!",
"body": "Entregou mais do que esperava.",
"status": "pending",
"createdAt": "2025-01-10T15:30:00.000Z"
}
Erros:
| Código | Motivo |
|---|---|
400 | Campos inválidos ou rating fora do intervalo |
401 | Não autenticado |
404 | Produto não encontrado |
6. Categorias (público)
GET /api/categories
Lista todas as categorias ativas com a contagem de produtos.
Autenticação: Nenhuma
Resposta 200 OK:
[
{
"id": "clxcat001",
"name": "Velas Aromáticas",
"slug": "velas-aromaticas",
"description": "Velas artesanais com fragrâncias naturais",
"image": "https://cdn.ljvelasearomas.com.br/cat-velas.jpg",
"active": true,
"productCount": 24
},
{
"id": "clxcat002",
"name": "Difusores",
"slug": "difusores",
"description": "Difusores de ambiente e aromatizadores",
"image": "https://cdn.ljvelasearomas.com.br/cat-difusores.jpg",
"active": true,
"productCount": 11
}
]
7. Pedidos (cliente autenticado)
POST /api/orders
Cria um novo pedido. Os itens podem ser enviados pelo cliente; o servidor sempre re-valida os preços contra o banco de dados, ignorando os preços enviados. O estoque é decrementado atomicamente dentro de uma transação.
Autenticação: Cookie JWT ✅ (role: CUSTOMER)
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
addressId | string | ✅ | ID do endereço de entrega (deve pertencer ao usuário) |
cartItems | array | — | Itens do pedido. Se omitido, usa o carrinho salvo no BD |
cartItems[].productId | string | ✅ | ID do produto |
cartItems[].quantity | integer | ✅ | Quantidade (mínimo 1) |
cartItems[].price | number | ✅ | Preço sugerido (ignorado — servidor usa preço do banco) |
notes | string | — | Observações do pedido |
couponId | string | — | ID do cupom de desconto (re-validado no servidor) |
⚠️ Re-validação de preço: O campo
priceenviado em cada item é ignorado pelo servidor. O preço final é sempre buscado no banco de dados para prevenir adulteração. Frete grátis é aplicado automaticamente para pedidos com subtotal ≥ R$ 150,00.
Exemplo de request:
{
"addressId": "clxaddr001",
"cartItems": [
{ "productId": "clx1a2b3c4d", "quantity": 2, "price": 39.90 },
{ "productId": "clx5e6f7g8h", "quantity": 1, "price": 89.90 }
],
"notes": "Presente — favor embalar para presente.",
"couponId": "clxcoupon01"
}
Resposta 201 Created:
{
"id": "clxord001",
"orderNumber": "VEL-20250110-0042",
"userId": "clx1a2b3c4d",
"addressId": "clxaddr001",
"subtotal": 169.70,
"shipping": 0.00,
"discount": 16.97,
"total": 152.73,
"status": "PENDING",
"notes": "Presente — favor embalar para presente.",
"items": [
{
"id": "clxitem001",
"productId": "clx1a2b3c4d",
"quantity": 2,
"price": 39.90,
"total": 79.80,
"productNameSnapshot": "Vela de Lavanda 200g"
}
],
"address": {
"street": "Rua das Flores",
"number": "123",
"city": "São Paulo",
"state": "SP",
"zipCode": "01310100"
},
"createdAt": "2025-01-10T15:30:00.000Z"
}
Erros:
| Código | Motivo |
|---|---|
400 | Carrinho vazio, estoque insuficiente, produto inativo |
401 | Não autenticado |
404 | Endereço não encontrado ou não pertence ao usuário |
500 | Erro interno |
GET /api/orders
Lista todos os pedidos do cliente autenticado, do mais recente ao mais antigo.
Autenticação: Cookie JWT ✅
Resposta 200 OK:
[
{
"id": "clxord001",
"orderNumber": "VEL-20250110-0042",
"status": "PROCESSING",
"total": 152.73,
"createdAt": "2025-01-10T15:30:00.000Z",
"items": [
{
"productId": "clx1a2b3c4d",
"quantity": 2,
"price": 39.90,
"product": {
"name": "Vela de Lavanda 200g",
"images": [{ "url": "...", "isPrimary": true }]
}
}
],
"payment": { "method": "PIX", "status": "PAID" },
"address": { "street": "Rua das Flores", "city": "São Paulo", "state": "SP" }
}
]
GET /api/orders/[id]
Retorna o detalhe de um pedido específico. Verifica se o pedido pertence ao usuário autenticado.
Autenticação: Cookie JWT ✅
Path Params:
| Parâmetro | Tipo | Descrição |
|---|---|---|
id | string | ID do pedido |
Resposta 200 OK: Mesmo formato da listagem, com dados completos.
Erros:
| Código | Motivo |
|---|---|
401 | Não autenticado |
404 | Pedido não encontrado ou não pertence ao usuário |
8. Pagamentos
POST /api/payments/pix
Gera um QR Code PIX via MercadoPago para o pagamento de um pedido. Implementa idempotência: se um QR válido (não expirado) já existir para o pedido, ele é retornado sem criar uma nova transação.
Autenticação: Cookie JWT ✅
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
orderId | string | ✅ | ID do pedido |
Resposta 200 OK:
{
"qrCode": "iVBORw0KGgoAAAANSUhEUgAA...",
"qrCodeText": "00020126580014BR.GOV.BCB.PIX0136...",
"expiresAt": "2025-01-10T16:00:00.000Z",
"amount": 152.73
}
| Campo | Descrição |
|---|---|
qrCode | Imagem do QR Code em Base64 (PNG) |
qrCodeText | Código Copia e Cola do PIX (string longa) |
expiresAt | Expiração do QR (30 minutos a partir da criação) |
amount | Valor total do pedido |
Erros:
| Código | Motivo |
|---|---|
400 | Pedido já pago |
401 | Não autenticado |
404 | Pedido não encontrado |
500 | Erro na integração com MercadoPago |
POST /api/payments/mercadopago/card
Processa pagamento com cartão de crédito via MP Brick (tokenização client-side).
Autenticação: Cookie JWT ✅
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
orderId | string | ✅ | ID do pedido |
token | string | ✅ | Token do cartão gerado pelo MP Brick |
paymentMethodId | string | ✅ | Método (ex.: visa, master) |
installments | integer | ✅ | Número de parcelas |
issuerId | string | ✅ | ID do banco emissor |
Resposta 200 OK:
{
"status": "approved",
"paymentId": "12345678901",
"orderId": "clxord001",
"installments": 3,
"lastFourDigits": "1234"
}
Erros:
| Código | Motivo |
|---|---|
400 | Token inválido, pedido já pago |
401 | Não autenticado |
404 | Pedido não encontrado |
422 | Pagamento rejeitado pela operadora |
500 | Erro na integração com MercadoPago |
POST /api/payments/mercadopago/webhook
Recebe notificações automáticas do MercadoPago (IPN/Webhooks). Valida a assinatura HMAC antes de processar.
Autenticação: Assinatura HMAC (header x-signature verificado pelo servidor)
Body: Payload padrão do MercadoPago (não manipulado diretamente pelo cliente).
Resposta 200 OK: { "received": true }
Nota: Este endpoint deve ser configurado no painel do MercadoPago como URL de notificação. O servidor verifica a assinatura HMAC em
x-signatureantes de processar qualquer evento.
POST /api/payments/stripe
Cria um PaymentIntent no Stripe para iniciar o fluxo de pagamento.
Autenticação: Cookie JWT ✅
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
orderId | string | ✅ | ID do pedido |
Resposta 200 OK:
{
"clientSecret": "pi_3Pq7vX2eZvKYlo2C0l3qfXWJ_secret_...",
"paymentIntentId": "pi_3Pq7vX2eZvKYlo2C0l3qfXWJ",
"amount": 15273,
"currency": "brl"
}
POST /api/payments/stripe/webhook
Recebe eventos do Stripe (ex.: payment_intent.succeeded). Verifica a assinatura via stripe-signature.
Autenticação: Assinatura Stripe (header stripe-signature)
Body: Raw body do Stripe (não deve passar por JSON parse intermediário).
Resposta 200 OK: { "received": true }
Fluxo Completo de Pagamento
1. Cliente cria pedido POST /api/orders → orderId
2a. PIX:
Cliente solicita QR POST /api/payments/pix → qrCode + qrCodeText
Cliente paga via app bancário
MercadoPago notifica POST /api/payments/mercadopago/webhook
Servidor atualiza order.status → PAID
2b. Cartão (MercadoPago Brick):
MP Brick tokeniza o cartão (client-side) → token
Cliente envia token POST /api/payments/mercadopago/card → aprovado/rejeitado
2c. Stripe:
Cliente solicita PaymentIntent POST /api/payments/stripe → clientSecret
Stripe Elements confirma (client-side)
Stripe notifica POST /api/payments/stripe/webhook
Servidor atualiza order.status → PAID
9. Frete
GET /api/shipping/calculate
Calcula opções de frete para um CEP e subtotal. Tenta cotação em tempo real nas transportadoras integradas (Melhor Envio / Correios / Superfrete); em caso de falha, aplica tarifas estáticas configuradas no admin.
Autenticação: Nenhuma
Query Params:
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
cep | string | ✅ | CEP de destino (8 dígitos, com ou sem -) |
subtotal | number | ✅ | Subtotal do pedido em R$ |
weight | number | — | Peso total em kg (padrão: 0.3) |
Resposta 200 OK — fonte API:
{
"cep": "01310100",
"state": "SP",
"city": "São Paulo",
"options": [
{
"id": "melhor-envio-sedex",
"name": "SEDEX",
"price": 18.50,
"estimatedDays": 2,
"free": false
},
{
"id": "melhor-envio-pac",
"name": "PAC",
"price": 0,
"estimatedDays": 7,
"free": true
}
],
"qualifiesForFree": true,
"freeShippingThreshold": 150.00,
"remaining": 0,
"source": "api"
}
Resposta 200 OK — fonte tarifas estáticas:
{
"cep": "20040020",
"state": "RJ",
"city": "Rio de Janeiro",
"options": [
{
"id": "static-rj-001",
"name": "Entrega Padrão",
"price": 15.90,
"estimatedDays": 5,
"free": false
}
],
"qualifiesForFree": false,
"freeShippingThreshold": 150.00,
"remaining": 30.00,
"source": "static"
}
| Campo | Descrição |
|---|---|
options[].free | true se o frete for grátis (subtotal ≥ R$ 150) |
qualifiesForFree | Se o subtotal já atinge o mínimo para frete grátis |
remaining | Quanto falta para obter frete grátis (R$) |
source | "api" (transportadora em tempo real) ou "static" |
Erros:
| Código | Motivo |
|---|---|
400 | CEP com formato inválido |
422 | CEP não encontrado na base ViaCEP |
Exemplo cURL:
curl "https://ljvelasearomas.com.br/api/shipping/calculate?cep=01310-100&subtotal=89.90"
10. Cupons
POST /api/coupons/validate
Valida um código de cupom antes de aplicá-lo ao pedido. Verifica: existência, status ativo, data de expiração, limite de usos e valor mínimo de pedido.
Autenticação: Nenhuma
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
code | string | ✅ | Código do cupom (case-insensitive) |
subtotal | number | ✅ | Subtotal do pedido em R$ |
Exemplo de request:
{
"code": "LAVANDA10",
"subtotal": 150.00
}
Resposta 200 OK — cupom válido:
{
"valid": true,
"coupon": {
"id": "clxcoupon01",
"code": "LAVANDA10",
"type": "PERCENT",
"value": 10,
"discount": 15.00
}
}
Resposta 200 OK — cupom inválido:
{
"valid": false,
"error": "Cupom expirado"
}
| Campo | Descrição |
|---|---|
coupon.type | PERCENT (percentual) ou FIXED (valor fixo) |
coupon.value | Percentual (ex.: 10 = 10%) ou valor em R$ |
coupon.discount | Valor do desconto calculado em R$ para o subtotal |
Erros possíveis no campo error:
| Mensagem | Motivo |
|---|---|
Cupom não encontrado | Código inexistente |
Cupom inativo | Cupom desativado pelo admin |
Cupom expirado | Data de expiração ultrapassada |
Limite de usos atingido | usedCount >= maxUses |
Pedido abaixo do valor mínimo | Subtotal < minOrderValue |
11. Usuário — Área Logada
Endereços
GET /api/user/addresses
Lista todos os endereços cadastrados pelo usuário.
Autenticação: Cookie JWT ✅
Resposta 200 OK:
[
{
"id": "clxaddr001",
"label": "Casa",
"street": "Rua das Flores",
"number": "123",
"complement": "Apto 4B",
"district": "Jardim Paulista",
"city": "São Paulo",
"state": "SP",
"zipCode": "01310100",
"isDefault": true
}
]
POST /api/user/addresses
Cria um novo endereço.
Autenticação: Cookie JWT ✅
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
label | string | ✅ | Rótulo (ex.: "Casa", "Trabalho") |
street | string | ✅ | Logradouro |
number | string | ✅ | Número |
complement | string | — | Complemento |
district | string | ✅ | Bairro |
city | string | ✅ | Cidade |
state | string | ✅ | UF (2 letras, ex.: SP) |
zipCode | string | ✅ | CEP (somente números) |
Resposta 201 Created: Objeto do endereço criado.
PATCH /api/user/addresses/[id]
Atualiza um endereço existente. Todos os campos são opcionais.
Autenticação: Cookie JWT ✅
Resposta 200 OK: Objeto do endereço atualizado.
Erros:
| Código | Motivo |
|---|---|
404 | Endereço não encontrado ou não pertence ao usuário |
DELETE /api/user/addresses/[id]
Remove um endereço.
Autenticação: Cookie JWT ✅
Resposta 200 OK: { "message": "Endereço removido" }
Lista de Favoritos (Wishlist)
GET /api/user/wishlist
Lista os produtos favoritados pelo usuário, incluindo dados do produto.
Autenticação: Cookie JWT ✅
Resposta 200 OK:
[
{
"id": "clxwish001",
"productId": "clx1a2b3c4d",
"addedAt": "2025-01-05T10:00:00.000Z",
"product": {
"id": "clx1a2b3c4d",
"name": "Vela de Lavanda 200g",
"slug": "vela-lavanda-200g",
"price": 39.90,
"stock": 25,
"images": [{ "url": "...", "isPrimary": true }]
}
}
]
POST /api/user/wishlist
Adiciona um produto à lista de favoritos.
Autenticação: Cookie JWT ✅
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
productId | string | ✅ | ID do produto |
Resposta 201 Created: Objeto do favorito criado.
Erros:
| Código | Motivo |
|---|---|
404 | Produto não encontrado |
409 | Produto já está na wishlist |
DELETE /api/user/wishlist/[productId]
Remove um produto da lista de favoritos.
Autenticação: Cookie JWT ✅
Resposta 200 OK: { "message": "Removido dos favoritos" }
Notificações
GET /api/user/notifications
Lista as notificações do usuário (ex.: atualização de status de pedido).
Autenticação: Cookie JWT ✅
Resposta 200 OK:
[
{
"id": "clxnotif001",
"type": "ORDER_STATUS",
"title": "Seu pedido foi enviado!",
"body": "Pedido #VEL-20250110-0042 saiu para entrega.",
"read": false,
"link": "/minha-conta/pedidos/clxord001",
"createdAt": "2025-01-11T09:00:00.000Z"
}
]
12. Newsletter
POST /api/newsletter/subscribe
Inscreve um e-mail na newsletter.
Autenticação: Nenhuma
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
email | string | ✅ | |
name | string | — | Nome |
Resposta 200 OK: { "message": "Inscrição realizada com sucesso" }
Erros:
| Código | Motivo |
|---|---|
400 | E-mail inválido |
409 | E-mail já inscrito |
POST /api/newsletter/unsubscribe
Cancela a inscrição de um e-mail na newsletter.
Autenticação: Nenhuma
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
email | string | ✅ |
Resposta 200 OK: { "message": "Inscrição cancelada" }
13. Contato
POST /api/contact
Envia uma mensagem de contato via formulário.
Autenticação: Nenhuma
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | ✅ | Nome do remetente |
email | string | ✅ | E-mail para resposta |
subject | string | ✅ | Assunto da mensagem |
message | string | ✅ | Corpo da mensagem |
Resposta 200 OK: { "message": "Mensagem enviada com sucesso" }
Erros:
| Código | Motivo |
|---|---|
400 | Campos obrigatórios ausentes |
429 | Rate limit (anti-spam) |
14. Admin
⚠️ Autenticação obrigatória em todos os endpoints
/api/admin/Todos os endpoints admin exigem Cookie JWT com
role: ADMIN. Qualquer requisição sem sessão válida de administrador recebe403 Forbiddencom{ "error": "Acesso negado" }.
14.1 Produtos (Admin)
GET /api/admin/products
Lista todos os produtos (incluindo inativos) com paginação.
Query Params:
| Parâmetro | Tipo | Descrição |
|---|---|---|
page | integer | Página (padrão: 1) |
limit | integer | Itens/página (padrão: 20, máx: 100) |
search | string | Busca por nome |
category | string | Filtrar por slug de categoria |
active | boolean | Filtrar por status |
featured | boolean | Filtrar produtos em destaque |
Resposta 200 OK:
{
"data": [
{
"id": "clx1a2b3c4d",
"name": "Vela de Lavanda 200g",
"slug": "vela-lavanda-200g",
"price": 39.90,
"stock": 25,
"active": true,
"featured": false,
"category": { "name": "Velas Aromáticas", "slug": "velas-aromaticas" },
"images": [{ "url": "...", "isPrimary": true }],
"_count": { "orderItems": 87 },
"createdAt": "2024-01-20T12:00:00.000Z"
}
],
"pagination": { "page": 1, "limit": 20, "total": 53, "totalPages": 3 }
}
POST /api/admin/products
Cria um novo produto. O slug é automaticamente verificado para unicidade (sufixo numérico adicionado se necessário).
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | ✅ | Nome do produto (mín. 2 chars) |
slug | string | ✅ | Slug URL-friendly (mín. 2 chars) |
description | string | — | Descrição |
price | number | ✅ | Preço em R$ (positivo) |
comparePrice | number | — | Preço "de" para exibir riscado |
stock | integer | ✅ | Estoque inicial (≥ 0) |
sku | string | — | Código SKU |
weight | number | — | Peso em kg |
categoryId | string | — | ID da categoria |
featured | boolean | — | Em destaque (padrão: false) |
active | boolean | — | Ativo (padrão: true) |
images | array | — | Ver estrutura abaixo |
images[].url | string | ✅ | URL da imagem |
images[].alt | string | — | Texto alternativo |
images[].isPrimary | boolean | — | Imagem principal |
images[].order | integer | — | Ordem de exibição |
Resposta 201 Created: Objeto completo do produto com imagens e categoria.
Erros:
| Código | Motivo |
|---|---|
400 | Validação falhou |
403 | Não é administrador |
409 | Slug já existe (conflict raro) |
GET /api/admin/products/[id]
Retorna detalhes completos do produto incluindo variantes e imagens.
Resposta 200 OK: Objeto completo do produto.
PATCH /api/admin/products/[id]
Atualiza campos de um produto. Todos os campos do schema são aceitos parcialmente.
Resposta 200 OK: Objeto do produto atualizado.
DELETE /api/admin/products/[id]
Remove um produto permanentemente.
Resposta 200 OK: { "message": "Produto removido" }
POST /api/admin/products/bulk
Executa operação em lote sobre múltiplos produtos.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
ids | string[] | ✅ | Array de IDs dos produtos |
action | string | ✅ | "delete", "activate" ou "deactivate" |
Exemplo de request:
{
"ids": ["clx1a2b3c4d", "clx5e6f7g8h", "clx9i0j1k2l"],
"action": "deactivate"
}
Resposta 200 OK: { "updated": 3 } ou { "deleted": 3 }
POST /api/admin/products/[id]/clone
Clona um produto. O novo slug é gerado automaticamente com sufixo (ex.: vela-lavanda-200g-2).
Resposta 201 Created: Objeto completo do produto clonado.
14.2 Variantes
Tipos de Variante
GET /api/admin/variant-types
Lista todos os tipos de variante (ex.: Cor, Tamanho, Aroma).
Resposta 200 OK:
[
{ "id": "clxvt001", "name": "Cor", "slug": "cor", "order": 0 },
{ "id": "clxvt002", "name": "Tamanho", "slug": "tamanho", "order": 1 }
]
POST /api/admin/variant-types
Cria um novo tipo de variante.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | ✅ | Nome do tipo |
slug | string | ✅ | Slug único |
order | integer | — | Ordem de exibição |
PATCH /api/admin/variant-types/[typeId]
Atualiza um tipo de variante.
DELETE /api/admin/variant-types/[typeId]
Remove um tipo de variante (e suas opções).
Opções de Variante
GET /api/admin/variant-types/[typeId]/options
Lista as opções de um tipo (ex.: Vermelho, Azul para o tipo Cor).
Resposta 200 OK:
POST /api/admin/variant-types/[typeId]/options
Cria uma nova opção para o tipo.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
value | string | ✅ | Valor da opção |
order | integer | — | Ordem |
PATCH /api/admin/variant-types/[typeId]/options/[optionId]
Atualiza uma opção.
DELETE /api/admin/variant-types/[typeId]/options/[optionId]
Remove uma opção.
Variantes do Produto
GET /api/admin/products/[id]/variants
Lista todas as variantes de um produto específico.
Resposta 200 OK:
[
{
"id": "clxvar001",
"price": 39.90,
"comparePrice": null,
"stock": 10,
"sku": "VEL-LAV-200-ROXO",
"values": [
{
"id": "clxvarval001",
"variantOptionId": "clxvo001",
"variantOption": { "value": "Roxo Lavanda", "variantType": { "name": "Cor" } }
}
]
}
]
POST /api/admin/products/[id]/variants
Cria uma nova variante para o produto.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
price | number | ✅ | Preço da variante |
stock | integer | ✅ | Estoque |
sku | string | — | SKU da variante |
comparePrice | number | — | Preço "de" |
values | array | ✅ | Opções que compõem a variante |
values[].variantOptionId | string | ✅ | ID da opção de variante |
Resposta 201 Created: Objeto da variante criada.
PATCH /api/admin/products/[id]/variants/[variantId]
Atualiza uma variante.
DELETE /api/admin/products/[id]/variants/[variantId]
Remove uma variante.
14.3 Upload de Imagens
POST /api/admin/upload
Faz upload de uma imagem para uso em produtos. Aceita multipart/form-data.
Autenticação: Cookie JWT ✅ (role: ADMIN)
Content-Type: multipart/form-data
Form Fields:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
file | File | ✅ | Arquivo de imagem (JPEG, PNG, WebP) |
Resposta 200 OK:
{
"url": "https://cdn.ljvelasearomas.com.br/uploads/vela-lavanda-200g-hero.jpg"
}
Exemplo cURL:
curl -X POST https://ljvelasearomas.com.br/api/admin/upload \
-H "Cookie: token=<JWT>" \
-F "file=@/path/to/imagem.jpg"
Erros:
| Código | Motivo |
|---|---|
400 | Nenhum arquivo enviado |
403 | Não é administrador |
413 | Arquivo muito grande |
14.4 Pedidos (Admin)
GET /api/admin/orders
Lista todos os pedidos com filtros avançados.
Query Params:
| Parâmetro | Tipo | Descrição |
|---|---|---|
status | string | Filtrar por status (ver tabela abaixo) |
q | string | Busca por número do pedido, nome ou e-mail |
from | string | Data inicial (ISO 8601, ex.: 2025-01-01) |
to | string | Data final (ISO 8601) |
page | integer | Página (padrão: 1) |
limit | integer | Itens/página (padrão: 20) |
Status de pedido disponíveis:
| Valor | Descrição |
|---|---|
PENDING | Aguardando pagamento |
PROCESSING | Pagamento confirmado, em preparo |
SHIPPED | Enviado / Em trânsito |
DELIVERED | Entregue |
CANCELLED | Cancelado |
REFUNDED | Reembolsado |
GET /api/admin/orders/[id]
Retorna detalhe completo de um pedido.
PATCH /api/admin/orders/[id]
Atualiza o status de um pedido.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
status | string | ✅ | Novo status (ver tabela acima) |
note | string | — | Nota interna sobre a atualização |
Resposta 200 OK: Objeto do pedido atualizado.
GET /api/admin/orders/export
Exporta pedidos como arquivo CSV.
Query Params:
| Parâmetro | Tipo | Descrição |
|---|---|---|
status | string | Filtrar por status |
from | string | Data inicial (ISO 8601) |
to | string | Data final (ISO 8601) |
Resposta 200 OK:
Content-Type: text/csv; charset=utf-8
Content-Disposition: attachment; filename="pedidos-2025-01-10.csv"
POST /api/admin/orders/[id]/notes
Adiciona uma nota interna ao pedido (visível apenas para admins).
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
note | string | ✅ | Texto da nota |
Resposta 201 Created: Objeto da nota criada.
NF-e (Nota Fiscal Eletrônica)
POST /api/admin/orders/[id]/nfe/emitir
Emite a NF-e para o pedido via integração fiscal.
Resposta 200 OK:
{
"status": "autorizada",
"chave": "35250112345678000100550010000001231234567890",
"numero": 123,
"serie": "001"
}
GET /api/admin/orders/[id]/nfe/danfe
Faz download do DANFE (PDF) da NF-e emitida.
Resposta 200 OK:
Content-Type: application/pdf
Content-Disposition: attachment; filename="danfe-clxord001.pdf"
POST /api/admin/orders/[id]/nfe/cancelar
Cancela uma NF-e emitida.
Resposta 200 OK: { "status": "cancelada" }
14.5 Cupons (Admin)
GET /api/admin/coupons
Lista todos os cupons cadastrados.
Resposta 200 OK:
[
{
"id": "clxcoupon01",
"code": "LAVANDA10",
"type": "PERCENT",
"value": 10,
"minOrderValue": 80.00,
"maxUses": 100,
"usedCount": 47,
"expiresAt": "2025-12-31T23:59:59.000Z",
"active": true,
"createdAt": "2025-01-01T00:00:00.000Z"
}
]
POST /api/admin/coupons
Cria um novo cupom de desconto.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
code | string | ✅ | Código do cupom (unique, uppercase recom.) |
type | string | ✅ | "PERCENT" ou "FIXED" |
value | number | ✅ | Percentual (0–100) ou valor em R$ |
minOrderValue | number | — | Valor mínimo do pedido para ativar |
maxUses | integer | — | Limite de usos totais |
expiresAt | string | — | Data de expiração (ISO 8601) |
active | boolean | ✅ | Se o cupom está ativo |
Resposta 201 Created: Objeto do cupom criado.
PATCH /api/admin/coupons/[id]
Atualiza um cupom.
DELETE /api/admin/coupons/[id]
Remove um cupom.
14.6 Clientes
GET /api/admin/customers
Lista todos os clientes cadastrados.
Query Params:
| Parâmetro | Tipo | Descrição |
|---|---|---|
search | string | Busca por nome ou e-mail |
active | boolean | Filtrar por status da conta |
Resposta 200 OK:
{
"data": [
{
"id": "clx1a2b3c4d",
"name": "Maria Silva",
"email": "maria@email.com",
"isActive": true,
"createdAt": "2024-03-15T10:30:00.000Z",
"_count": { "orders": 5 }
}
],
"pagination": { "page": 1, "limit": 20, "total": 312, "totalPages": 16 }
}
GET /api/admin/customers/[id]
Retorna dados completos do cliente, incluindo histórico de pedidos.
PATCH /api/admin/customers/[id]/status
Ativa ou suspende a conta de um cliente.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
active | boolean | ✅ | true ativa, false suspende |
Nota: Contas suspensas não conseguem fazer login (
403 Forbidden).
GET /api/admin/customers/export
Exporta a lista de clientes como CSV.
Resposta: Arquivo CSV com Content-Disposition: attachment.
14.7 Avaliações
GET /api/admin/reviews
Lista todas as avaliações de produtos.
Query Params:
| Parâmetro | Tipo | Descrição |
|---|---|---|
approved | boolean | true = aprovadas, false = pendentes |
PATCH /api/admin/reviews/[id]
Aprova ou rejeita uma avaliação.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
approved | boolean | ✅ | true aprova, false rejeita |
Resposta 200 OK: Objeto da review atualizada.
DELETE /api/admin/reviews/[id]
Remove uma avaliação permanentemente.
14.8 Estoque
GET /api/admin/stock
Lista produtos com estoque atual e movimentações recentes.
Resposta 200 OK:
[
{
"id": "clx1a2b3c4d",
"name": "Vela de Lavanda 200g",
"sku": "VEL-LAV-200",
"stock": 25,
"recentMovements": [
{
"id": "clxmov001",
"delta": -2,
"reason": "PURCHASE",
"source": "ORDER",
"createdAt": "2025-01-10T15:30:00.000Z"
}
]
}
]
Motivos de movimentação (reason):
| Valor | Descrição |
|---|---|
PURCHASE | Venda (decremento por pedido) |
ADJUSTMENT | Ajuste manual pelo admin ou API |
RETURN | Devolução |
PATCH /api/admin/stock/[productId]
Atualiza o estoque de um produto diretamente (ajuste manual).
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
stock | integer | ✅ | Novo estoque (≥ 0) |
Resposta 200 OK: { "productId": "...", "stock": 42 }
14.9 Frete (Tarifas e Provedores)
GET /api/admin/shipping
Lista todas as tarifas de frete estáticas cadastradas.
POST /api/admin/shipping
Cria uma nova tarifa estática.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | ✅ | Nome da tarifa (ex.: "Sul/Sudeste") |
states | string[] | ✅ | Lista de UFs cobertas (ex.: ["SP","RJ"]) |
price | number | ✅ | Preço em R$ |
estimatedDays | integer | ✅ | Prazo estimado em dias úteis |
PATCH /api/admin/shipping/[id]
Atualiza uma tarifa estática.
DELETE /api/admin/shipping/[id]
Remove uma tarifa estática.
GET /api/admin/shipping/providers
Retorna o status de conexão das integrações de transportadora.
Resposta 200 OK:
{
"providers": [
{ "name": "Melhor Envio", "status": "connected", "lastCheck": "2025-01-10T12:00:00.000Z" },
{ "name": "Correios", "status": "connected", "lastCheck": "2025-01-10T12:00:00.000Z" },
{ "name": "Superfrete", "status": "disconnected", "lastCheck": "2025-01-10T12:00:00.000Z" }
]
}
14.10 Webhooks Outbound
Webhooks outbound são chamadas HTTP que o sistema envia para endpoints externos quando eventos ocorrem (ex.: novo pedido, estoque baixo).
GET /api/admin/webhooks
Lista todos os endpoints de webhook cadastrados.
Resposta 200 OK:
[
{
"id": "clxwh001",
"url": "https://meuapp.com/webhooks/loja",
"events": ["order.created", "order.status_changed", "stock.low"],
"active": true,
"createdAt": "2025-01-01T00:00:00.000Z"
}
]
POST /api/admin/webhooks
Registra um novo endpoint de webhook.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
url | string | ✅ | URL HTTPS do endpoint |
secret | string | ✅ | Segredo para verificação HMAC |
events | string[] | ✅ | Eventos a escutar (ver lista abaixo) |
Eventos disponíveis:
| Evento | Descrição |
|---|---|
order.created | Novo pedido criado |
order.status_changed | Status do pedido atualizado |
order.paid | Pedido pago |
stock.low | Produto com estoque abaixo do limiar |
stock.updated | Estoque atualizado via API/webhook |
customer.created | Novo cliente cadastrado |
PATCH /api/admin/webhooks/[id]
Atualiza URL, secret ou eventos de um webhook.
DELETE /api/admin/webhooks/[id]
Remove o endpoint de webhook.
POST /api/admin/webhooks/[id]/test
Dispara um payload de teste para verificar conectividade do endpoint.
Resposta 200 OK:
{
"delivered": true,
"statusCode": 200,
"duration": 143
}
14.11 API Keys
GET /api/admin/api-keys
Lista todas as API Keys cadastradas. O valor da chave nunca é retornado — apenas metadados.
Resposta 200 OK:
[
{
"id": "clxkey001",
"name": "Integração ERP",
"scopes": ["products:read", "orders:read", "stock:write"],
"lastUsedAt": "2025-01-10T10:00:00.000Z",
"createdAt": "2025-01-01T00:00:00.000Z"
}
]
POST /api/admin/api-keys
Cria uma nova API Key. O valor bruto da chave (rawKey) é retornado apenas uma vez nesta resposta e não pode ser recuperado posteriormente. O servidor armazena somente o hash SHA-256.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | ✅ | Nome descritivo da chave |
scopes | string[] | ✅ | Escopos permitidos (ver tabela) |
Resposta 201 Created:
{
"id": "clxkey002",
"name": "Integração ERP",
"scopes": ["products:read", "stock:write"],
"rawKey": "lvk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"createdAt": "2025-01-10T16:00:00.000Z"
}
⚠️ Atenção: Copie e salve o
rawKeyimediatamente. Ele não será exibido novamente.
DELETE /api/admin/api-keys/[id]
Revoga (remove) uma API Key. Requisições que usarem a chave revogada receberão 401.
14.12 Dashboard
GET /api/admin/dashboard
Retorna métricas agregadas por período.
Query Params:
| Parâmetro | Tipo | Padrão | Valores |
|---|---|---|---|
period | string | 30d | 7d, 30d, 90d |
Resposta 200 OK:
{
"period": "30d",
"orders": {
"total": 142,
"change": 12.5
},
"revenue": {
"total": 18430.50,
"change": 8.3
},
"avgOrderValue": 129.79,
"newCustomers": 47,
"topProducts": [
{
"productId": "clx1a2b3c4d",
"name": "Vela de Lavanda 200g",
"sales": 38,
"revenue": 1516.20
}
]
}
GET /api/admin/dashboard/stats
Retorna indicadores rápidos em tempo real (sem período).
Resposta 200 OK:
{
"totalProducts": 53,
"totalCategories": 6,
"totalOrders": 1482,
"totalRevenue": 193450.70,
"pendingOrders": 8,
"recentOrders": [
{
"id": "clxord099",
"orderNumber": "VEL-20250110-0099",
"total": 89.90,
"status": "PENDING",
"user": { "name": "João Costa", "email": "joao@email.com" },
"createdAt": "2025-01-10T17:55:00.000Z"
}
]
}
14.13 Configurações
PATCH /api/admin/settings/puck
Salva o conteúdo da homepage construído com o editor Puck.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
data | object | ✅ | Objeto PuckData serializado |
Resposta 200 OK: { "saved": true }
PATCH /api/admin/settings/puck-about
Salva o conteúdo da página /sobre construído com Puck.
Body: Mesmo formato de /settings/puck.
PATCH /api/admin/settings/analytics
Salva as configurações dos serviços de analytics.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
ga4Id | string | — | Google Analytics 4 ID |
mpId | string | — | Meta Pixel ID |
clarityId | string | — | Microsoft Clarity Project ID |
Resposta 200 OK: { "saved": true }
PATCH /api/admin/settings/change-password
Altera a senha do administrador logado.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
currentPassword | string | ✅ | Senha atual |
newPassword | string | ✅ | Nova senha |
Resposta 200 OK: { "message": "Senha alterada" }
15. API v1 — Integração Programática
A API v1 é projetada para integrações server-to-server (ERPs, WMS, scripts de automação). Utiliza autenticação por API Key e tem CORS aberto para todas as origens.
Autenticação
Todas as requisições devem incluir o header:
Authorization: Bearer lvk_live_<token>
Cada chamada verifica:
- Existência e formato da chave (
lvk_live_prefix) - Hash SHA-256 contra o banco de dados
- Escopo necessário para a operação
Resposta em caso de falha:
HTTP/1.1 401 Unauthorized
{ "error": "API key inválida ou sem permissão" }
Escopos
| Escopo | Permissão |
|---|---|
products:read | Listar e consultar produtos |
products:write | Criar e editar produtos |
stock:write | Atualizar estoque via API e webhook |
orders:read | Listar e consultar pedidos |
orders:write | Atualizar status de pedidos |
CORS
Os endpoints /api/v1/* retornam os seguintes headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
GET /api/v1/products
Lista produtos com filtros. Escopo: products:read.
Query Params:
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
page | integer | 1 | Página |
limit | integer | 50 | Itens/página (máx. 100) |
active | boolean | true | false inclui inativos |
category | string | — | Slug da categoria |
low_stock | boolean | — | true retorna apenas produtos com estoque ≤ 5 |
Resposta 200 OK:
{
"data": [
{
"id": "clx1a2b3c4d",
"name": "Vela de Lavanda 200g",
"slug": "vela-lavanda-200g",
"sku": "VEL-LAV-200",
"price": "39.90",
"comparePrice": "49.90",
"stock": 25,
"active": true,
"featured": false,
"weight": 0.3,
"updatedAt": "2025-01-10T15:30:00.000Z",
"category": { "id": "clxcat001", "name": "Velas Aromáticas", "slug": "velas-aromaticas" },
"images": [{ "url": "...", "alt": "Vela de Lavanda 200g" }]
}
],
"pagination": { "page": 1, "limit": 50, "total": 53, "totalPages": 2 }
}
Exemplo cURL:
curl https://ljvelasearomas.com.br/api/v1/products?low_stock=true \
-H "Authorization: Bearer lvk_live_a1b2c3d4e5f6..."
GET /api/v1/products/[id]
Retorna detalhes de um produto por ID. Escopo: products:read.
Path Params:
| Parâmetro | Tipo | Descrição |
|---|---|---|
id | string | ID do produto |
Resposta 200 OK: Mesmo objeto do endpoint de listagem, campo único.
Erros:
| Código | Motivo |
|---|---|
401 | API Key inválida |
404 | Produto não encontrado |
PATCH /api/v1/products/[id]/stock
Atualiza o estoque de um produto. Escopo: stock:write.
Body:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
stock | integer | ✅ | Novo estoque (≥ 0) |
Resposta 200 OK:
{
"id": "clx1a2b3c4d",
"sku": "VEL-LAV-200",
"stock": 42,
"updatedAt": "2025-01-10T16:00:00.000Z"
}
Exemplo cURL:
curl -X PATCH https://ljvelasearomas.com.br/api/v1/products/clx1a2b3c4d/stock \
-H "Authorization: Bearer lvk_live_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{"stock": 42}'
GET /api/v1/orders
Lista pedidos. Escopo: orders:read.
Query Params:
| Parâmetro | Tipo | Descrição |
|---|---|---|
status | string | Filtrar por status do pedido |
page | integer | Página (padrão: 1) |
limit | integer | Itens/página (padrão: 50, máx: 100) |
Resposta 200 OK: Lista paginada de pedidos com itens e pagamento.
GET /api/v1/orders/[id]
Retorna detalhes de um pedido por ID. Escopo: orders:read.
Erros:
| Código | Motivo |
|---|---|
401 | API Key inválida |
404 | Pedido não encontrado |
16. Webhook Inbound — Estoque
POST /api/webhooks/stock
Atualiza o estoque de produtos via webhook externo (ex.: ERP, WMS). Registra movimentações no histórico de estoque e dispara webhooks outbound caso estoque fique abaixo do limiar.
Autenticação: API Key com escopo stock:write
Authorization: Bearer lvk_live_<token>
Content-Type: application/json
Modo sync — Sincronização em Lote por SKU
Define o estoque exato para uma lista de SKUs. Funciona com SKU de produto ou SKU de variante.
Body:
{
"type": "sync",
"items": [
{ "sku": "VEL-LAV-200", "stock": 42 },
{ "sku": "VEL-ROS-150", "stock": 18 },
{ "sku": "VEL-BAU-100-BRANCO", "stock": 0 }
]
}
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type | string | ✅ | "sync" |
items | array | ✅ | Lista de itens a sincronizar |
items[].sku | string | ✅ | SKU do produto ou variante |
items[].stock | integer | ✅ | Novo estoque exato (≥ 0) |
Modo set — Definir Estoque por ID
Define o estoque exato de um produto ou variante por ID.
Body:
{
"type": "set",
"productId": "clx1a2b3c4d",
"variantId": "clxvar001",
"stock": 42
}
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type | string | ✅ | "set" |
productId | string | ✅ | ID do produto |
variantId | string | — | ID da variante (omitir para o produto) |
stock | integer | ✅ | Novo estoque exato (≥ 0) |
Modo delta — Incremento/Decremento Relativo
Aplica um delta ao estoque atual. O valor mínimo resultante é 0 (clamp automático).
Body:
{
"type": "delta",
"productId": "clx1a2b3c4d",
"variantId": "clxvar001",
"delta": -3
}
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type | string | ✅ | "delta" |
productId | string | ✅ | ID do produto |
variantId | string | — | ID da variante (omitir para o produto) |
delta | integer | ✅ | Incremento positivo ou decremento negativo |
Exemplo: Se o estoque atual for
5edeltafor-8, o estoque resultante será0(não negativo).
Resposta 200 OK
{
"ok": true,
"updated": 3,
"errors": [
"SKU não encontrado: VEL-INEXISTENTE-999"
]
}
| Campo | Descrição |
|---|---|
ok | true se ao menos parte do payload foi processada |
updated | Número de produtos/variantes atualizados com sucesso |
errors | Array de mensagens para itens que falharam (não aborta a operação) |
Comportamento de erro parcial: Em modo
sync, SKUs não encontrados são acumulados emerrorse o processamento dos demais itens continua. Nos modossetedelta, se o produto/variante não for encontrado, a requisição retorna404imediatamente.
Webhooks Outbound Disparados
Após processar, o servidor dispara automaticamente:
| Evento | Condição |
|---|---|
stock.low | Estoque resultante ≤ limiar configurado (padrão: 5 unidades) |
stock.updated | Ao menos 1 item atualizado com sucesso |
Erros:
| Código | Motivo |
|---|---|
400 | JSON malformado no body |
401 | API Key ausente, inválida ou sem escopo |
404 | Produto ou variante não encontrado (set/delta) |
422 | Payload com estrutura inválida |
500 | Erro interno do servidor |
Exemplo cURL — sincronização em lote:
curl -X POST https://ljvelasearomas.com.br/api/webhooks/stock \
-H "Authorization: Bearer lvk_live_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"type": "sync",
"items": [
{ "sku": "VEL-LAV-200", "stock": 42 },
{ "sku": "VEL-ROS-150", "stock": 18 }
]
}'
Documentação gerada para LJ Velas & Aromas — ljvelasearomas.com.br