Pular para o conteúdo
MeWeb

Desenvolvedores

Referência da Storefront API

68 endpoints sob https://SUA-LOJA.meweb.com.br/api/v1. Rotas marcadas com sessão de cliente exigem os cookies de autenticação do cliente; as demais são públicas.

Loja e site

Dados institucionais da loja, layout publicado do site (seções da home), menus e o gate do modo em construção.

GET/storefront/store

Dados públicos da loja (nome, contatos, checkoutEnabled e os campos do checkout) para header/footer e checkout.

Resposta (publicStoreInfoSchema)

CampoTipoObrigatório
namestringsim
emailstring | nullsim
phonestring | nullsim
whatsappstring | nullsim
addressobjeto | nullsim
checkoutEnabledbooleannão
checkoutFieldsobjetonão
Exemplo (payload de "data")
{
  "name": "Minha Loja",
  "checkoutEnabled": true,
  "checkoutFields": {
    "cpfCnpj": "optional",
    "phone": "required",
    "company": "off",
    "birthDate": "off"
  },
  "email": "contato@minhaloja.com.br",
  "phone": "(11) 4000-0000",
  "whatsapp": "(11) 99999-0000",
  "address": {
    "street": "Av. Paulista",
    "number": "1000",
    "district": "Bela Vista",
    "city": "São Paulo",
    "state": "SP",
    "cep": "01310-100"
  }
}
  • checkoutEnabled: false (spec 19 RN-20, vitrine congelada) significa que a loja está no ar e navegável mas NÃO vende: esconda botões de compra, carrinho e "comprar agora". O checkout responde 422 PLAN_CHECKOUT_DISABLED de qualquer forma: a flag existe para o cliente final não bater no erro.
  • checkoutFields (spec 15 RN-08) diz o que a etapa de identificação deve pedir: off = não renderize, optional = renderize sem asterisco, required = renderize com asterisco e bloqueie o avanço. **Já vem resolvido**: o cpfCnpj chega required sozinho quando o lojista habilita boleto ou emissão de NF-e, então o tema não precisa (nem deve) refazer essa conta.
  • Tema que ignora checkoutFields quebra de dois jeitos, e os dois já aconteceram: com birthDate: "required" o POST /storefront/checkout/complete responde 422 por um campo que a tela não pediu; e com cpfCnpj: "optional" a tela segue exigindo um documento que a loja não usa, desfazendo a minimização que o servidor aplicou.
GET/storefront/site/layout

Layout publicado do site: seções da home com dados embutidos, identidade e tema.

Query string

  • preview (opcional): Token de preview do rascunho (emitido no admin da loja).

Resposta

Objeto PublicSiteLayout: sections (união discriminada por type: hero_banner, product_grid, collection_grid, image_text, newsletter, custom_code etc., cada uma com os dados já resolvidos), footerGroups (grupos do rodapé com menus), identity (cores, logo, banner de anúncios) e theme (tema ativo + settings + código custom).

  • Um front headless pode ignorar esta rota e definir o próprio layout; ela existe para quem quer reaproveitar as seções montadas no admin.
GET/storefront/site/preview-session

Diz o que um token de pré-visualização do site autoriza (spec 14 §11, D5).

Query string

  • token (obrigatório): Token de preview emitido no admin da loja.

Resposta

{ scope, editor, theme, designId?, editorOrigin? }: diz se o token abre o rascunho ou o publicado (scope), qual rascunho (designId, ausente no escopo publicado), se liga o modo editor e para qual origin a ponte responde (editorOrigin, ausente fora do modo editor) e o tema efetivo. Token inválido ou expirado responde 404 PREVIEW_EXPIRED.

  • Existe para que a vitrine valide o token SEM conhecer o segredo de assinatura da API: quem verifica a assinatura é a API, o front só recebe o veredito.
  • Não é cacheável: o token é de uso único do preview e tem validade de 1 hora.
GET/storefront/site/gate

Estado do modo "em construção" da loja (resolvido por host).

Resposta

{ enabled: boolean, locked: boolean, message: string }, em que locked indica visitante sem o cookie de desbloqueio.

GET/storefront/site/order-tracking

Configuração do rastreio de pedido da loja (spec 01 RN-24), resolvida por host.

Resposta (orderTrackingConfigSchema)

CampoTipoObrigatório
mode"native" | "external" | "both"não
externalKind"link" | "embed"não
externalUrlstring | nullnão
externalEmbedHtmlstring | nullnão
helpTextstring | nullnão
Exemplo (payload de "data")
{
  "mode": "both",
  "externalKind": "link",
  "externalUrl": "https://www.ordertracker.com/track/{codigo}",
  "externalEmbedHtml": null,
  "helpText": "O código de rastreio chega por e-mail em até 2 dias úteis."
}
  • Config EFETIVA: o fail-safe da RN-24 já vem aplicado, e modo externo sem externalUrl/externalEmbedHtml preenchido chega como native. Não reimplemente essa decisão no seu front.
  • externalUrl é um template: substitua {codigo} pelo código de rastreio do pedido (RN-09) para levar o cliente direto ao provedor.
  • externalEmbedHtml é HTML/JS do provedor, guardado sem sanitização (mesmo modelo de confiança do código customizado, spec 14 RN-16); renderize-o só na sua vitrine.
  • Cacheável: muda apenas quando o lojista salva a configuração no admin.
POST/storefront/site/unlock

Desbloqueia o modo em construção com a senha; emite o cookie sf_unlock.

Body (siteUnlockSchema)

CampoTipoObrigatório
passwordstringsim
Exemplo de requisição
{
  "password": "espia-ai"
}

Resposta

{ ok: true }

GET/storefront/menus/:handle

Árvore de menu resolvida por handle (ex.: menu principal e links rápidos).

Parâmetros de rota

  • handle (obrigatório): Handle do menu (ex.: main-menu).

Resposta (publicMenuSchema)

CampoTipoObrigatório
handlestringsim
titlestringsim
itemsobjeto[]sim
Exemplo (payload de "data")
{
  "handle": "main-menu",
  "title": "Menu principal",
  "items": [
    {
      "label": "Início",
      "url": "/",
      "children": []
    },
    {
      "label": "Coleções",
      "url": null,
      "children": [
        {
          "label": "Camisetas",
          "url": "/colecoes/camisetas"
        }
      ]
    }
  ]
}

Páginas institucionais

Páginas publicadas no admin (sobre, trocas, contato…), com HTML sanitizado.

GET/storefront/pages

Lista de páginas publicadas (para footer/sitemap).

Resposta ({ title, slug, updatedAt }[])

CampoTipoObrigatório
titlestringsim
slugstringsim
updatedAtstringsim
Exemplo (payload de "data")
[
  {
    "title": "Trocas e devoluções",
    "slug": "trocas",
    "updatedAt": "2026-07-01T12:00:00.000Z"
  }
]
GET/storefront/pages/preview

Preview de rascunho por token (emitido no admin).

Query string

  • token (obrigatório): Token de preview.

Resposta (publicPageResultSchema)

GET/storefront/pages/:slug

Página publicada por slug (ou redirect quando o slug mudou).

Parâmetros de rota

  • slug (obrigatório): Slug público da página.

Resposta (publicPageResultSchema)

Exemplo (payload de "data")
{
  "title": "Trocas e devoluções",
  "slug": "trocas",
  "bodyHtml": "<p>Você tem 30 dias para trocar.</p>",
  "seoTitle": "",
  "seoDescription": "",
  "publishedAt": "2026-07-01T12:00:00.000Z"
}
  • Slug antigo de página publicada responde { redirectTo: "/paginas/<novo>" }.

Blog

Posts publicados no admin (spec 25), com HTML sanitizado. Alimentam a listagem /blog, a página do post, o sitemap e o feed RSS do tema.

GET/storefront/blog/postspaginada

Posts publicados, do mais recente ao mais antigo, com filtro por tag.

Query string

  • tag (opcional): Filtra por uma tag (minúsculas).
  • limit (opcional): Itens por página (1–50, padrão 12).
  • cursor (opcional): endCursor da página anterior.

Resposta (publicBlogListItemDtoSchema, item de data[])

CampoTipoObrigatório
titlestringsim
slugstringsim
excerptstringsim
coverUrlstring | nullsim
tagsstring[]sim
publishedAtstringsim
Exemplo (payload de "data")
{
  "title": "Como escolher o tamanho certo",
  "slug": "como-escolher-o-tamanho-certo",
  "excerpt": "Um guia rápido para acertar de primeira.",
  "coverUrl": "/api/v1/media/renditions/01J0EXEMPLOMIDIA000000000-lg.webp",
  "tags": [
    "guias"
  ],
  "publishedAt": "2026-07-01T12:00:00.000Z"
}
  • Rascunhos nunca aparecem aqui (RN-04), nem na busca, no sitemap ou no RSS.
  • Query validada por listPublicBlogPostsQuerySchema. Para o RSS (RN-06), peça limit=20.
  • excerpt vazio no admin vem derivado do corpo do post.
GET/storefront/blog/posts/:slug

Post publicado por slug (ou redirect quando o slug mudou).

Parâmetros de rota

  • slug (obrigatório): Slug público do post.

Query string

  • preview (opcional): Token de preview do rascunho (emitido no admin da loja).

Resposta (publicBlogPostResultSchema)

Exemplo (payload de "data")
{
  "post": {
    "title": "Como escolher o tamanho certo",
    "slug": "como-escolher-o-tamanho-certo",
    "excerpt": "Um guia rápido para acertar de primeira.",
    "bodyHtml": "<p>Meça o busto e a cintura antes de comprar.</p>",
    "coverUrl": null,
    "authorName": "Equipe da Loja",
    "tags": [
      "guias"
    ],
    "seoTitle": "",
    "seoDescription": "",
    "publishedAt": "2026-07-01T12:00:00.000Z"
  }
}
  • Slug antigo de post publicado responde { redirectTo: "/blog/<novo>" }; sirva um 301/308.
  • Post draft ou inexistente responde 404 BLOG_POST_NOT_FOUND (RN-03/07).

Avaliações

Avaliações aprovadas de produto; envio exige cliente autenticado.

GET/storefront/products/:slug/reviewspaginada

Avaliações aprovadas do produto, paginadas, com resumo de notas.

Parâmetros de rota

  • slug (obrigatório): Slug público do produto.

Query string

  • limit (opcional): Itens por página (1–100, padrão 25).
  • cursor (opcional): endCursor da página anterior.

Resposta (publicReviewSchema, item de data[])

CampoTipoObrigatório
idstringsim
ratinginteirosim
titlestringsim
bodystringsim
authorNamestringsim
verifiedPurchasebooleansim
replystring | nullsim
createdAtstringsim
Exemplo (payload de "data")
{
  "id": "01J0EXEMPLOREVIEW00000000",
  "rating": 5,
  "title": "Excelente",
  "body": "Tecido ótimo, chegou rápido.",
  "authorName": "Ana P.",
  "verifiedPurchase": true,
  "reply": null,
  "createdAt": "2026-07-01T12:00:00.000Z"
}
  • Além de data/pageInfo, a resposta traz summary (reviewSummarySchema): média, total e contagem por estrela.
POST/storefront/products/:slug/reviewssessão de cliente

Envia avaliação do produto (entra em moderação).

Parâmetros de rota

  • slug (obrigatório): Slug público do produto.

Body (submitReviewInputSchema)

CampoTipoObrigatório
ratinginteirosim
titlestringnão
bodystringsim
Exemplo de requisição
{
  "rating": 5,
  "title": "Excelente",
  "body": "Tecido ótimo, chegou rápido."
}

Resposta

{ ok: true, status: "pending" }: publicada após moderação.

Carrinho

O carrinho vive num cookie httpOnly (sameSite: lax) emitido pela API na primeira interação, e por isso o padrão recomendado é um proxy same-origin (BFF) no seu front. Todas as rotas devolvem o carrinho atualizado.

GET/storefront/cart

Carrinho atual (cria um novo se não houver; emite/renova o cookie).

Resposta (cartSchema)

CampoTipoObrigatório
idstringsim
status"active" | "checkout_started" | "completed" | "abandoned" | "recovered" | "expired"sim
itemsobjeto[]sim
subtotalCentsinteirosim
contactobjeto | nullsim
shippingAddressobjeto | nullsim
shippingMethodobjeto | nullsim
discountobjeto | nullsim
discountErrorstring | nullsim
giftCardobjeto | nullsim
giftCardErrorstring | nullsim
giftCardOnlybooleansim
totalCentsinteirosim
giftCardAppliedCentsinteirosim
amountDueCentsinteirosim
Exemplo (payload de "data")
{
  "id": "01J0EXEMPLOCART0000000000",
  "status": "active",
  "items": [
    {
      "id": "01J0EXEMPLOITEM0000000000",
      "variantId": "01J0EXEMPLOVARIANTE000000",
      "productId": "01J0EXEMPLOPRODUTO0000000",
      "productTitle": "Camiseta Básica",
      "variantTitle": "P",
      "slug": "camiseta-basica",
      "thumbnailUrl": null,
      "unitPriceCents": 7990,
      "quantity": 2,
      "availableQuantity": 12,
      "totalCents": 15980,
      "isGiftCard": false,
      "giftMeta": null
    }
  ],
  "subtotalCents": 15980,
  "contact": null,
  "shippingAddress": null,
  "shippingMethod": null,
  "discount": null,
  "discountError": null,
  "giftCard": null,
  "giftCardError": null,
  "giftCardOnly": false,
  "totalCents": 15980,
  "giftCardAppliedCents": 0,
  "amountDueCents": 15980
}
  • totalCents é o total do PEDIDO (subtotal − cupom + frete) e **não desconta o cartão-presente**, porque cartão-presente é meio de pagamento, não desconto. O valor que vai ao PSP na conclusão é amountDueCents (= totalCents − giftCardAppliedCents); é ele que a tela deve mostrar como "a pagar".
POST/storefront/cart/items

Adiciona uma variante ao carrinho.

Body (addCartItemInputSchema)

CampoTipoObrigatório
variantIdstringsim
quantityinteironão
giftobjeto | nullnão
Exemplo de requisição
{
  "variantId": "01J0EXEMPLOVARIANTE000000",
  "quantity": 1
}

Resposta (cartSchema)

CampoTipoObrigatório
idstringsim
status"active" | "checkout_started" | "completed" | "abandoned" | "recovered" | "expired"sim
itemsobjeto[]sim
subtotalCentsinteirosim
contactobjeto | nullsim
shippingAddressobjeto | nullsim
shippingMethodobjeto | nullsim
discountobjeto | nullsim
discountErrorstring | nullsim
giftCardobjeto | nullsim
giftCardErrorstring | nullsim
giftCardOnlybooleansim
totalCentsinteirosim
giftCardAppliedCentsinteirosim
amountDueCentsinteirosim
  • gift (destinatário + mensagem) só se aplica ao produto especial cartão-presente.
PATCH/storefront/cart/items/:itemId

Altera a quantidade de um item.

Parâmetros de rota

  • itemId (obrigatório): ID do item no carrinho.

Body (updateCartItemInputSchema)

CampoTipoObrigatório
quantityinteirosim
Exemplo de requisição
{
  "quantity": 3
}

Resposta (cartSchema)

CampoTipoObrigatório
idstringsim
status"active" | "checkout_started" | "completed" | "abandoned" | "recovered" | "expired"sim
itemsobjeto[]sim
subtotalCentsinteirosim
contactobjeto | nullsim
shippingAddressobjeto | nullsim
shippingMethodobjeto | nullsim
discountobjeto | nullsim
discountErrorstring | nullsim
giftCardobjeto | nullsim
giftCardErrorstring | nullsim
giftCardOnlybooleansim
totalCentsinteirosim
giftCardAppliedCentsinteirosim
amountDueCentsinteirosim
DELETE/storefront/cart/items/:itemId

Remove um item do carrinho.

Parâmetros de rota

  • itemId (obrigatório): ID do item no carrinho.

Resposta (cartSchema)

CampoTipoObrigatório
idstringsim
status"active" | "checkout_started" | "completed" | "abandoned" | "recovered" | "expired"sim
itemsobjeto[]sim
subtotalCentsinteirosim
contactobjeto | nullsim
shippingAddressobjeto | nullsim
shippingMethodobjeto | nullsim
discountobjeto | nullsim
discountErrorstring | nullsim
giftCardobjeto | nullsim
giftCardErrorstring | nullsim
giftCardOnlybooleansim
totalCentsinteirosim
giftCardAppliedCentsinteirosim
amountDueCentsinteirosim
POST/storefront/cart/gift-card10 req/min

Aplica (ou remove, com code: null) um cartão-presente como pagamento.

Body (applyGiftCardInputSchema)

CampoTipoObrigatório
codestring | nullsim
Exemplo de requisição
{
  "code": "ABCD-1234-EFGH-5678"
}

Resposta (cartSchema)

CampoTipoObrigatório
idstringsim
status"active" | "checkout_started" | "completed" | "abandoned" | "recovered" | "expired"sim
itemsobjeto[]sim
subtotalCentsinteirosim
contactobjeto | nullsim
shippingAddressobjeto | nullsim
shippingMethodobjeto | nullsim
discountobjeto | nullsim
discountErrorstring | nullsim
giftCardobjeto | nullsim
giftCardErrorstring | nullsim
giftCardOnlybooleansim
totalCentsinteirosim
giftCardAppliedCentsinteirosim
amountDueCentsinteirosim
  • Código inválido não é erro HTTP: volta em giftCardError no carrinho.
POST/storefront/cart/discount-code10 req/min

Aplica (ou remove, com code: null) um cupom de desconto.

Body (applyDiscountCodeInputSchema)

CampoTipoObrigatório
codestring | nullsim
Exemplo de requisição
{
  "code": "BEMVINDO10"
}

Resposta (cartSchema)

CampoTipoObrigatório
idstringsim
status"active" | "checkout_started" | "completed" | "abandoned" | "recovered" | "expired"sim
itemsobjeto[]sim
subtotalCentsinteirosim
contactobjeto | nullsim
shippingAddressobjeto | nullsim
shippingMethodobjeto | nullsim
discountobjeto | nullsim
discountErrorstring | nullsim
giftCardobjeto | nullsim
giftCardErrorstring | nullsim
giftCardOnlybooleansim
totalCentsinteirosim
giftCardAppliedCentsinteirosim
amountDueCentsinteirosim
  • Cupom inválido não é erro HTTP: volta em discountError no carrinho.
GET/storefront/cart/recover/:token

Recupera um carrinho abandonado pelo link de e-mail (regrava o cookie).

Parâmetros de rota

  • token (obrigatório): Token do link de recuperação.

Query string

  • cupom (opcional): Cupom a aplicar junto da recuperação.

Resposta

{ ok: true }

Checkout

Checkout em 3 etapas retomáveis sobre o carrinho: identificação → entrega → conclusão com pagamento. A conclusão revalida preço/estoque e exige o header Idempotency-Key.

POST/storefront/checkout/identification

Etapa 1: dados de contato do comprador.

Body (checkoutIdentificationInputSchema)

CampoTipoObrigatório
emailstringsim
firstNamestringsim
lastNamestringnão
phonestringnão
cpfCnpjstringnão
companystringnão
birthDatestringnão
acceptsMarketingbooleannão
Exemplo de requisição
{
  "email": "cliente@example.com",
  "firstName": "Ana",
  "lastName": "Pereira",
  "phone": "(11) 99999-0000"
}

Resposta (cartSchema)

CampoTipoObrigatório
idstringsim
status"active" | "checkout_started" | "completed" | "abandoned" | "recovered" | "expired"sim
itemsobjeto[]sim
subtotalCentsinteirosim
contactobjeto | nullsim
shippingAddressobjeto | nullsim
shippingMethodobjeto | nullsim
discountobjeto | nullsim
discountErrorstring | nullsim
giftCardobjeto | nullsim
giftCardErrorstring | nullsim
giftCardOnlybooleansim
totalCentsinteirosim
giftCardAppliedCentsinteirosim
amountDueCentsinteirosim
  • Os campos exigidos além de email/firstName são configuráveis pela loja (ex.: telefone e CPF/CNPJ); um 400 devolve details por campo, então trate-os dinamicamente.
GET/storefront/checkout/shipping-methods

Métodos de entrega disponíveis para um CEP (tabela de frete da loja).

Query string

  • cep (obrigatório): CEP de destino (00000-000).

Resposta (shippingOptionSchema[])

CampoTipoObrigatório
idstringsim
labelstringsim
priceCentsinteirosim
deadlineDaysinteiro | nullsim
pickupbooleansim
carrierstring | nullnão
Exemplo (payload de "data")
[
  {
    "id": "01J0EXEMPLOFRETE000000000",
    "label": "Entrega padrão",
    "priceCents": 1990,
    "deadlineDays": 7,
    "pickup": false
  },
  {
    "id": "pickup",
    "label": "Retirada na loja",
    "priceCents": 0,
    "deadlineDays": null,
    "pickup": true
  }
]
POST/storefront/checkout/shipping

Etapa 2: endereço de entrega e método escolhido.

Body (checkoutShippingInputSchema)

CampoTipoObrigatório
addressobjetosim
shippingMethodIdstringsim
Exemplo de requisição
{
  "address": {
    "recipientName": "Ana Pereira",
    "cep": "01310-100",
    "street": "Av. Paulista",
    "number": "1000",
    "complement": "ap 42",
    "district": "Bela Vista",
    "city": "São Paulo",
    "state": "SP"
  },
  "shippingMethodId": "01J0EXEMPLOFRETE000000000"
}

Resposta (cartSchema)

CampoTipoObrigatório
idstringsim
status"active" | "checkout_started" | "completed" | "abandoned" | "recovered" | "expired"sim
itemsobjeto[]sim
subtotalCentsinteirosim
contactobjeto | nullsim
shippingAddressobjeto | nullsim
shippingMethodobjeto | nullsim
discountobjeto | nullsim
discountErrorstring | nullsim
giftCardobjeto | nullsim
giftCardErrorstring | nullsim
giftCardOnlybooleansim
totalCentsinteirosim
giftCardAppliedCentsinteirosim
amountDueCentsinteirosim
POST/storefront/checkout/complete10 req/min

Etapa 3: conclui o pedido e inicia o pagamento (Pix, cartão ou boleto).

Headers

  • Idempotency-Key (obrigatório): Chave única da tentativa (ex.: UUID). Reenvio com a mesma chave devolve a resposta original; payload diferente → 409.

Body (checkoutCompleteInputSchema)

CampoTipoObrigatório
method"pix" | "credit_card" | "boleto"sim
cardTokenstringnão
installmentsinteironão
acknowledgeChangesbooleannão
Exemplo de requisição
{
  "method": "pix",
  "installments": 1,
  "acknowledgeChanges": false
}

Resposta (checkoutCompleteResultSchema)

CampoTipoObrigatório
orderIdstringsim
orderNumberinteirosim
statusstringsim
confirmationTokenstringsim
paymentobjetosim
Exemplo (payload de "data")
{
  "orderId": "01J0EXEMPLOPEDIDO00000000",
  "orderNumber": 1042,
  "status": "pending_payment",
  "confirmationToken": "tKk3f8s2mN4pQ7rW9xZ1aB6c",
  "payment": {
    "method": "pix",
    "status": "pending",
    "pixQrCode": "00020126580014BR.GOV.BCB.PIX...",
    "boletoUrl": null,
    "boletoLine": null,
    "expiresAt": "2026-07-10T12:00:00.000Z"
  }
}
  • Cartão: envie cardToken gerado client-side pelo provedor (nunca dados crus do cartão).
  • Divergência de preço/estoque desde que o item entrou no carrinho → 422 com as mudanças; reenvie com acknowledgeChanges: true para aceitar.
  • Sucesso limpa o cookie do carrinho; use orderId + confirmationToken (?c=) para abrir a confirmação sem expor e-mail na URL (LGPD).
GET/storefront/checkout/payment-config

Config pública de pagamento: métodos ativos, chave pública e parcelamento.

Resposta (publicPaymentConfigSchema)

CampoTipoObrigatório
providerstringsim
publicKeystring | nullsim
providerConfigmapa<string, string | número | boolean>não
methods"pix" | "credit_card" | "boleto"[]sim
installmentsobjetosim
Exemplo (payload de "data")
{
  "provider": "pagarme",
  "publicKey": "pk_test_exemplo",
  "methods": [
    "pix",
    "credit_card",
    "boleto"
  ],
  "installments": {
    "max": 12,
    "minInstallmentCents": 500
  }
}

Autenticação do cliente

Sessão do cliente final em cookies httpOnly (customer_session ~15 min + customer_refresh rotativo ~30 dias, sameSite: lax). Em 401, chame refresh uma vez e repita a requisição.

POST/storefront/auth/register10 req/min

Cria a conta do cliente e já emite a sessão (cookies).

Body (customerRegisterInputSchema)

CampoTipoObrigatório
firstNamestringsim
lastNamestringnão
emailstringsim
passwordstringsim
acceptsMarketingbooleannão
Exemplo de requisição
{
  "firstName": "Ana",
  "lastName": "Pereira",
  "email": "cliente@example.com",
  "password": "SenhaForte123",
  "acceptsMarketing": true
}

Resposta (customerProfileSchema)

CampoTipoObrigatório
idstringsim
emailstringsim
firstNamestringsim
lastNamestringsim
phonestring | nullsim
cpfCnpjstring | nullsim
acceptsMarketingbooleansim
emailVerifiedbooleansim
POST/storefront/auth/login10 req/min

Autentica o cliente e emite a sessão (cookies).

Body (customerLoginInputSchema)

CampoTipoObrigatório
emailstringsim
passwordstringsim
turnstileTokenstringnão
Exemplo de requisição
{
  "email": "cliente@example.com",
  "password": "SenhaForte123"
}

Resposta (customerProfileSchema)

CampoTipoObrigatório
idstringsim
emailstringsim
firstNamestringsim
lastNamestringsim
phonestring | nullsim
cpfCnpjstring | nullsim
acceptsMarketingbooleansim
emailVerifiedbooleansim
Exemplo (payload de "data")
{
  "id": "01J0EXEMPLOCLIENTE0000000",
  "email": "cliente@example.com",
  "firstName": "Ana",
  "lastName": "Pereira",
  "phone": null,
  "cpfCnpj": null,
  "acceptsMarketing": true,
  "emailVerified": false
}
  • Desafio progressivo (spec 08 RN-11): após 5 falhas do MESMO e-mail em 15 min a conta entra em modo desafio por 15 min. Login correto zera o contador. Os códigos abaixo saem iguais para e-mail inexistente (anti-enumeração).
  • Em host de subdomínio da plataforma (<slug>.meweb.com.br), o modo desafio exige turnstileToken: sem token → 422 CAPTCHA_REQUIRED (renderize o widget do Cloudflare Turnstile e reenvie); token recusado → 422 CAPTCHA_FAILED (o token é de USO ÚNICO, então peça um novo a cada falha).
  • Em domínio próprio do lojista, onde o widget do Turnstile não renderiza, o modo desafio limita a 1 tentativa a cada 30 s por e-mail: 429 RATE_LIMITED com Retry-After, antes de verificar a senha.
POST/storefront/auth/refresh

Rotaciona a sessão usando o cookie customer_refresh.

Resposta

{ ok: boolean }, com false quando o refresh expirou (faça login).

POST/storefront/auth/logout

Encerra a sessão e limpa os cookies.

Resposta

{ ok: true }

POST/storefront/auth/password-reset

Solicita redefinição de senha por e-mail (resposta sempre ok).

Body (customerPasswordResetRequestSchema)

CampoTipoObrigatório
emailstringsim
Exemplo de requisição
{
  "email": "cliente@example.com"
}

Resposta

{ ok: true }

POST/storefront/auth/password-reset/confirm

Define a nova senha com o token recebido por e-mail.

Body (customerPasswordResetConfirmSchema)

CampoTipoObrigatório
tokenstringsim
passwordstringsim
Exemplo de requisição
{
  "token": "token-do-email",
  "password": "NovaSenhaForte123"
}

Resposta

{ ok: true }

POST/storefront/auth/verify-email

Confirma o e-mail do cliente com o token recebido.

Body ({ token: string })

CampoTipoObrigatório
tokenstringsim
Exemplo de requisição
{
  "token": "token-do-email"
}

Resposta

{ ok: true }

POST/storefront/newsletter

Opt-in de marketing por e-mail (newsletter) com double opt-in: envia link de confirmação, não marca consentimento na hora.

Body ({ email: string })

CampoTipoObrigatório
emailstringsim
Exemplo de requisição
{
  "email": "cliente@example.com"
}

Resposta

{ ok: true }

POST/storefront/newsletter/confirm

Confirma o opt-in de marketing com o token recebido por e-mail (double opt-in).

Body ({ token: string })

CampoTipoObrigatório
tokenstringsim
Exemplo de requisição
{
  "token": "token-do-email"
}

Resposta

{ ok: true }

GET/storefront/unsubscribe/:token

Descadastro de e-mails de marketing (LGPD art. 18 §2) via link do rodapé, com página de confirmação HTML.

Resposta

Página HTML de confirmação do descadastro (pt-BR).

POST/storefront/unsubscribe/:token

Descadastro em um clique (RFC 8058 List-Unsubscribe-Post): marca o cliente como acceptsMarketing=false.

Resposta

{ ok: true }

GET/storefront/marketing/unsubscribe/:token

Descadastro de marketing por token de MENSAGEM (spec 30 RN-03), com página HTML de confirmação, sem login.

Resposta

Página HTML (pt-BR). O token é opaco, emitido por mensagem promocional enviada; token desconhecido recebe resposta neutra.

  • É o alvo do header List-Unsubscribe dos e-mails promocionais do módulo de marketing (spec 30); o link visível no corpo aponta para a página /descadastrar/:token do storefront, que consome o POST abaixo.
POST/storefront/marketing/unsubscribe/:token

Descadastro em um clique (RFC 8058) do canal de e-mail promocional: revoga o consentimento e derruba as automações do cliente na hora (spec 30 RN-03).

Resposta

{ unsubscribed: boolean }: resposta neutra (HTTP 200) mesmo para token desconhecido ou já usado.

GET/storefront/marketing/channels

Canais promocionais que a loja sabe operar hoje (spec 30 §7): use para só mostrar o opt-in de WhatsApp quando o canal está conectado.

Resposta (storefrontMarketingChannelsSchema)

{ whatsappMarketing: boolean }, com true quando a loja tem o WhatsApp conectado. Não expõe número, provedor nem estado da conexão.

CampoTipoObrigatório
whatsappMarketingbooleansim
  • A caixa "Quero receber ofertas no WhatsApp" do checkout só deve aparecer quando isto for true **e** o cliente tiver telefone: não se pede consentimento para um canal que a loja não sabe usar.
GET/storefront/marketing/o/:token

Pixel de abertura de e-mail promocional (spec 30 RN-23): GIF 1×1 transparente, Cache-Control: no-store.

Resposta

Sempre o GIF (200), token válido ou não, porque pixel não erra. Token válido marca a abertura da mensagem (primeira vez em first_opened_at).

GET/storefront/marketing/c/:token

Clique envelopado de e-mail promocional (spec 30 RN-23): registra o clique e redireciona (302) ao destino.

Resposta

302 para o destino embutido no token assinado; token inválido ou expirado redireciona para a home da loja.

GET/storefront/l/:token

Link curto e opaco da fase 30.4 (spec 30 RN-27/RN-28): 302 para o checkout com o carrinho restaurado ou para a página de pagamento do pedido.

Resposta

302 para o destino resolvido pelo token. Token desconhecido, vencido ou de outra loja leva à página explicativa /l/expirado, nunca a um 4xx, porque quem clicou é um cliente.

  • A URL não carrega e-mail, id de cliente nem id de carrinho (M26/M28): o token é ULID + HMAC do tenant e todo o resto vive na linha de marketing_short_links.
  • Quando o carrinho já virou pedido, o link passa a apontar para o pedido (reação a CartRecovered) em vez de recriar um carrinho fantasma.

Conta do cliente

Área logada do cliente final: perfil, endereços, pedidos e cartões-presente. Exigem a sessão de cliente (401 CUSTOMER_UNAUTHENTICATED sem ela).

GET/storefront/account/profilesessão de cliente

Perfil do cliente logado.

Resposta (customerProfileSchema)

CampoTipoObrigatório
idstringsim
emailstringsim
firstNamestringsim
lastNamestringsim
phonestring | nullsim
cpfCnpjstring | nullsim
acceptsMarketingbooleansim
emailVerifiedbooleansim
PATCH/storefront/account/profilesessão de cliente

Atualiza dados do perfil.

Body (updateProfileInputSchema)

CampoTipoObrigatório
firstNamestringnão
lastNamestringnão
phonestring | nullnão
cpfCnpjstring | nullnão
acceptsMarketingbooleannão
Exemplo de requisição
{
  "firstName": "Ana",
  "phone": "(11) 98888-0000"
}

Resposta (customerProfileSchema)

CampoTipoObrigatório
idstringsim
emailstringsim
firstNamestringsim
lastNamestringsim
phonestring | nullsim
cpfCnpjstring | nullsim
acceptsMarketingbooleansim
emailVerifiedbooleansim
GET/storefront/account/marketing-preferencessessão de cliente

Preferências de comunicação do cliente por canal (e-mail / WhatsApp), com estado e pendência de confirmação (spec 30 RN-02).

Resposta (marketingPreferencesDtoSchema)

CampoTipoObrigatório
emailMarketingobjetosim
whatsappMarketingobjetosim
whatsappAvailablebooleannão
PATCH/storefront/account/marketing-preferencessessão de cliente

Liga/desliga cada canal: ligar e-mail dispara o double opt-in (só vira granted após confirmação); desligar revoga na hora (spec 30 RN-02/RN-03).

Body (updateMarketingPreferencesInputSchema)

CampoTipoObrigatório
emailMarketingbooleannão
whatsappMarketingbooleannão
source"checkout" | "account"não

Resposta (marketingPreferencesDtoSchema)

CampoTipoObrigatório
emailMarketingobjetosim
whatsappMarketingobjetosim
whatsappAvailablebooleannão
GET/storefront/account/addressessessão de cliente

Endereços salvos do cliente.

Resposta (customerAddressSchema[])

CampoTipoObrigatório
idstringsim
labelstring | nullsim
recipientNamestringsim
cepstringsim
streetstringsim
numberstringsim
complementstring | nullsim
districtstringsim
citystringsim
statestringsim
isDefaultbooleansim
Exemplo (payload de "data")
[
  {
    "id": "01J0EXEMPLOENDERECO000000",
    "label": "Casa",
    "recipientName": "Ana Pereira",
    "cep": "01310-100",
    "street": "Av. Paulista",
    "number": "1000",
    "complement": null,
    "district": "Bela Vista",
    "city": "São Paulo",
    "state": "SP",
    "isDefault": true
  }
]
POST/storefront/account/addressessessão de cliente

Cadastra um endereço.

Body (upsertAddressInputSchema)

CampoTipoObrigatório
labelstring | nullnão
recipientNamestringsim
cepobjetosim
streetstringsim
numberstringsim
complementstring | nullnão
districtstringsim
citystringsim
state"AC" | "AL" | "AP" | "AM" | "BA" | "CE" | "DF" | "ES" | "GO" | "MA" | "MT" | "MS" | "MG" | "PA" | "PB" | "PR" | "PE" | "PI" | "RJ" | "RN" | "RS" | "RO" | "RR" | "SC" | "SP" | "SE" | "TO"sim
isDefaultbooleannão
Exemplo de requisição
{
  "label": "Casa",
  "recipientName": "Ana Pereira",
  "cep": "01310-100",
  "street": "Av. Paulista",
  "number": "1000",
  "district": "Bela Vista",
  "city": "São Paulo",
  "state": "SP",
  "isDefault": true
}

Resposta (customerAddressSchema)

CampoTipoObrigatório
idstringsim
labelstring | nullsim
recipientNamestringsim
cepstringsim
streetstringsim
numberstringsim
complementstring | nullsim
districtstringsim
citystringsim
statestringsim
isDefaultbooleansim
PUT/storefront/account/addresses/:idsessão de cliente

Atualiza um endereço.

Parâmetros de rota

  • id (obrigatório): ID do endereço.

Body (upsertAddressInputSchema)

CampoTipoObrigatório
labelstring | nullnão
recipientNamestringsim
cepobjetosim
streetstringsim
numberstringsim
complementstring | nullnão
districtstringsim
citystringsim
state"AC" | "AL" | "AP" | "AM" | "BA" | "CE" | "DF" | "ES" | "GO" | "MA" | "MT" | "MS" | "MG" | "PA" | "PB" | "PR" | "PE" | "PI" | "RJ" | "RN" | "RS" | "RO" | "RR" | "SC" | "SP" | "SE" | "TO"sim
isDefaultbooleannão

Resposta (customerAddressSchema)

CampoTipoObrigatório
idstringsim
labelstring | nullsim
recipientNamestringsim
cepstringsim
streetstringsim
numberstringsim
complementstring | nullsim
districtstringsim
citystringsim
statestringsim
isDefaultbooleansim
DELETE/storefront/account/addresses/:idsessão de cliente

Remove um endereço.

Parâmetros de rota

  • id (obrigatório): ID do endereço.

Resposta

{ ok: true }

GET/storefront/account/orderssessão de cliente

Pedidos do cliente logado.

Resposta (accountOrderSchema[])

CampoTipoObrigatório
numberinteirosim
status"draft" | "pending_payment" | "paid" | "processing" | "shipped" | "delivered" | "cancelled" | "refunded"sim
fulfillmentStatus"unfulfilled" | "processing" | "shipped" | "delivered"sim
itemsCountinteirosim
totalCentsinteirosim
createdAtstringsim
Exemplo (payload de "data")
[
  {
    "number": 1042,
    "status": "paid",
    "fulfillmentStatus": "unfulfilled",
    "itemsCount": 2,
    "totalCents": 17970,
    "createdAt": "2026-07-09T12:00:00.000Z"
  }
]
GET/storefront/account/orders/:numbersessão de cliente

Detalhe de um pedido do cliente (itens, entrega, pagamento, rastreio).

Parâmetros de rota

  • number (obrigatório): Número do pedido.

Resposta (accountOrderDetailSchema)

CampoTipoObrigatório
numberinteirosim
status"draft" | "pending_payment" | "paid" | "processing" | "shipped" | "delivered" | "cancelled" | "refunded"sim
fulfillmentStatus"unfulfilled" | "processing" | "shipped" | "delivered"sim
itemsobjeto[]sim
subtotalCentsinteirosim
discountCentsinteirosim
shippingCentsinteirosim
totalCentsinteirosim
shippingAddressobjeto | nullsim
shippingMethodstring | nullsim
trackingCodestring | nullsim
trackingCarrierstring | nullsim
paymentobjeto | nullsim
paymentsobjeto[]sim
canCancelbooleansim
createdAtstringsim
POST/storefront/account/orders/:number/cancelsessão de cliente

Cancela um pedido ainda cancelável pelo cliente.

Parâmetros de rota

  • number (obrigatório): Número do pedido.

Resposta

{ ok: true }

GET/storefront/account/gift-cardssessão de cliente

Cartões-presente do cliente (saldo e validade).

Resposta (myGiftCardSchema[])

CampoTipoObrigatório
last4stringsim
status"pending_activation" | "active" | "depleted" | "expired" | "disabled"sim
balanceCentsinteirosim
expiresAtstring | nullsim
Exemplo (payload de "data")
[
  {
    "last4": "5678",
    "status": "active",
    "balanceCents": 5000,
    "expiresAt": null
  }
]
GET/storefront/account/exportsessão de cliente

LGPD arts. 18 II/V e 19: relatório com TUDO o que a loja tem sobre o titular logado: cadastro, endereços, pedidos, consentimentos, mensagens e conversas de WhatsApp.

Query string

  • format (opcional): json (padrão) ou csv; o CSV vem como anexo meus-dados.csv.

Resposta (customerDataExportSchema)

CampoTipoObrigatório
generatedAtstringsim
noticestringsim
subjectobjetosim
addressesobjeto[]sim
ordersobjeto[]sim
marketingobjetosim
sectionsUnavailablestring[]sim
Exemplo (payload de "data")
{
  "generatedAt": "2026-08-22T12:00:00.000Z",
  "notice": "Relatório dos dados pessoais que esta loja mantém sobre você…",
  "subject": {
    "id": "01J0EXEMPLOCLIENTE00000000",
    "email": "maria@example.com",
    "firstName": "Maria",
    "lastName": "Silva",
    "phone": "+5511999990000",
    "whatsapp": null,
    "cpfCnpj": "11144477735",
    "emailVerified": true,
    "acceptsMarketing": true,
    "tags": [
      "vip"
    ],
    "note": null,
    "createdAt": "2026-01-10T13:00:00.000Z",
    "ordersCount": 1,
    "totalSpentCents": 15900,
    "lastOrderAt": "2026-08-01T10:00:00.000Z"
  },
  "addresses": [
    {
      "id": "01J0EXEMPLOENDERECO0000000",
      "label": "Casa",
      "recipientName": "Maria Silva",
      "cep": "01310-100",
      "street": "Av. Paulista",
      "number": "1000",
      "complement": null,
      "district": "Bela Vista",
      "city": "São Paulo",
      "state": "SP",
      "isDefault": true
    }
  ],
  "orders": [
    {
      "number": 1042,
      "status": "paid",
      "fulfillmentStatus": "shipped",
      "createdAt": "2026-08-01T10:00:00.000Z",
      "paidAt": "2026-08-01T10:05:00.000Z",
      "cancelledAt": null,
      "subtotalCents": 14900,
      "discountCents": 0,
      "shippingCents": 1000,
      "totalCents": 15900,
      "shippingMethod": "PAC",
      "trackingCode": "AA123456789BR",
      "trackingCarrier": "Correios",
      "shippingAddress": {
        "recipientName": "Maria Silva",
        "cep": "01310-100",
        "street": "Av. Paulista",
        "number": "1000",
        "complement": null,
        "district": "Bela Vista",
        "city": "São Paulo",
        "state": "SP"
      },
      "items": [
        {
          "productTitle": "Camiseta",
          "variantTitle": "P / Preta",
          "quantity": 1,
          "unitPriceCents": 14900,
          "totalCents": 14900
        }
      ]
    }
  ],
  "marketing": {
    "consents": [
      {
        "channel": "email_marketing",
        "status": "granted",
        "source": "double_optin",
        "sourceDetail": "checkout",
        "consentText": "Quero receber ofertas, novidades e promoções desta loja por e-mail. Posso cancelar quando quiser.",
        "grantedAt": "2026-01-10T13:05:00.000Z",
        "revokedAt": null,
        "updatedAt": "2026-01-10T13:05:00.000Z"
      }
    ],
    "consentEvents": [
      {
        "channel": "email_marketing",
        "action": "grant",
        "source": "double_optin",
        "sourceDetail": "checkout",
        "consentText": "Quero receber ofertas, novidades e promoções desta loja por e-mail.",
        "occurredAt": "2026-01-10T13:05:00.000Z"
      }
    ],
    "messages": [
      {
        "channel": "email",
        "kind": "promotional",
        "subject": "Novidades da semana",
        "toIdentity": "maria@example.com",
        "status": "sent",
        "sentAt": "2026-02-01T12:00:00.000Z",
        "firstOpenedAt": "2026-02-01T12:30:00.000Z",
        "firstClickedAt": null
      }
    ],
    "whatsappConversations": []
  },
  "sectionsUnavailable": []
}
  • O escopo é SEMPRE o titular da sessão: a rota não aceita id de cliente, nem no path nem na query.
  • format=csv NÃO devolve o envelope { data } do doc 04: a resposta é o arquivo (text/csv; charset=utf-8, BOM + ; + CRLF, para abrir direto no Excel pt-BR).
  • Limite de 5 gerações por hora por titular (429 RATE_LIMITED): o relatório junta todas as seções numa resposta só.
  • sectionsUnavailable lista as seções que não puderam ser montadas nesta geração; vem vazio no caminho normal, e existe para o arquivo nunca omitir em silêncio.
POST/storefront/account/delete-requestsessão de cliente

LGPD art. 18 V: solicita a exclusão da própria conta; envia link de confirmação por e-mail (resposta sempre ok).

Resposta

{ ok: true }

POST/storefront/account/delete-confirm

Confirma a exclusão da conta com o token do e-mail: anonimiza a conta (RN-07) e derruba a sessão.

Body ({ token: string })

CampoTipoObrigatório
tokenstringsim
Exemplo de requisição
{
  "token": "token-do-email"
}

Resposta

{ ok: true }

Cartão-presente

Configuração pública do produto cartão-presente da loja.

GET/storefront/gift-card-config

Se a loja vende cartão-presente e em quais valores.

Resposta (publicGiftCardConfigSchema)

CampoTipoObrigatório
enabledbooleansim
valuesCentsinteiro[]sim
variantsobjeto[]sim
Exemplo (payload de "data")
{
  "enabled": true,
  "valuesCents": [
    5000,
    10000
  ],
  "variants": [
    {
      "variantId": "01J0EXEMPLOVARIANTEGC0000",
      "valueCents": 5000
    }
  ]
}

Pedido de convidado

Acesso ao pedido sem sessão: confirmação pós-checkout (ID + e-mail) e rastreio público (número + e-mail).

GET/storefront/orders/:id/confirmation

Dados de confirmação do pedido para a tela de obrigado.

Parâmetros de rota

  • id (obrigatório): orderId devolvido pelo checkout.

Query string

  • email (obrigatório): E-mail usado na identificação do checkout.

Resposta

{ number, status, subtotalCents, discountCents, discountCode, shippingCents, shippingMethod, giftCardUsedCents, totalCents, payment (resumo com Pix/boleto quando pendente) | null, items: { productTitle, variantTitle, quantity, totalCents }[] }.

  • O resumo de valores entrou no QA de 2026-08-21 (M1): sem ele a tela de obrigado mostrava itens e Total e a soma não fechava para quem usou cupom ou pagou frete. discountCents soma cupom + desconto manual, como em /storefront/account/orders/:number.
  • totalCents é o total do PEDIDO, anterior ao cartão-presente (spec 07 RN-05); o que ainda falta pagar é totalCents − giftCardUsedCents.
POST/storefront/orders/track10 req/min

Rastreia um pedido por número + e-mail (spec 01 RN-23).

Body (trackOrderInputSchema)

CampoTipoObrigatório
numberinteirosim
emailstringsim
Exemplo de requisição
{
  "number": 1001,
  "email": "cliente@exemplo.com"
}

Resposta (orderTrackingSchema)

CampoTipoObrigatório
numberinteirosim
status"draft" | "pending_payment" | "paid" | "processing" | "shipped" | "delivered" | "cancelled" | "refunded"sim
fulfillmentStatus"unfulfilled" | "processing" | "shipped" | "delivered"sim
trackingCarrierstring | nullsim
trackingCodestring | nullsim
createdAtstringsim
paidAtstring | nullsim
shippedAtstring | nullsim
deliveredAtstring | nullsim
Exemplo (payload de "data")
{
  "number": 1001,
  "status": "shipped",
  "fulfillmentStatus": "shipped",
  "trackingCarrier": "Correios",
  "trackingCode": "AA123456789BR",
  "createdAt": "2026-02-01T12:00:00.000Z",
  "paidAt": "2026-02-01T12:05:00.000Z",
  "shippedAt": "2026-02-02T09:00:00.000Z",
  "deliveredAt": null
}
  • Só responde quando o par número + e-mail confere exatamente; qualquer outro caso devolve 404 ORDER_NOT_FOUND genérico (anti-enumeração).
  • Nunca expõe endereço, itens, valores ou dados do cliente. Consulta sem efeito colateral.