Criar Produto
POST /v1/products
Cria um produto e suas variantes na mesma requisição (variants[] é obrigatório, com pelo menos
uma variante). A precificação de cada variante vai em pricing (veja o
modelo de precificação).
Escopo requerido: products:create. Suporta chave de idempotência (X-Idempotency-Key).
Requisição
curl -X POST "https://api.selectwin.io/v1/products" \
-H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium Course",
"salesPage": "https://loja.com/premium",
"description": "Curso completo",
"enabled": true,
"type": "digital",
"language": "pt-BR",
"category": "courses",
"warrantyDays": 30,
"externalReference": "SKU-EXT-1",
"images": ["https://example.com/img1.png"],
"variants": [
{
"name": "Plano Anual",
"sku": "SKU-001",
"description": "Acesso por 12 meses",
"enabled": true,
"externalReference": "VAR-EXT-1",
"pricing": {
"unitPrice": 49900,
"currency": "BRL",
"schema": "unit",
"type": "oneTime",
"oldPrice": 69900,
"costPerItem": 12000
}
}
]
}'Campos do produto
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | Sim | Nome do produto (máx. 40 caracteres) |
category | string | Sim | Categoria de negócio (1–100 caracteres) |
warrantyDays | integer | Sim | Dias de garantia (7–3650) |
type | string | Sim | physical, digital ou other |
variants[] | array | Sim | Variantes do produto (1 a 100), cada uma com pricing |
language | string | Não | Idioma, até 10 caracteres (default pt-BR) |
enabled | boolean | Não | Ativo (default true) |
salesPage | string (url) | Não | Página de vendas (até 255 caracteres) |
description | string | Não | Descrição (até 400 caracteres) |
externalReference | string | Não | Sua referência externa (até 255 caracteres) |
images[] | array | Não | URLs de imagens (até 10) — ver Imagens abaixo |
fiscal | object | null | Não | O perfil fiscal — o que vai na nota fiscal da venda quando a loja emite nota (ver Perfil fiscal abaixo); null limpa |
Campos da variante (variants[])
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | Sim | Nome da variante (1–255 caracteres) |
pricing | object | Sim | Veja campos de pricing. type e schema são imutáveis depois de criados |
description | string | Não | Descrição (até 255 caracteres) |
sku | string | Não | SKU (até 50 caracteres) |
images[] | array | Não | URL de imagem (no máximo 1) — ver Imagens abaixo |
metadata | object | Não | Pares chave/valor livres |
attributes | object | Não | Atributos livres da variante |
externalReference | string | Não | Sua referência externa (até 255 caracteres) |
enabled | boolean | Não | Ativo (default true) |
fiscal | object | null | Não | O que a variante muda no perfil fiscal do produto (tipicamente o gtin); mesmo formato |
Perfil fiscal (fiscal)
Quando a loja emite nota fiscal ao comprador (o app Notas Fiscais, com o Notazz ou o eNotas), cada venda vira uma NFS-e (serviço) ou uma NF-e (mercadoria) com estes dados. Tudo é opcional: o que faltar sai dos padrões do app ou do cadastro no painel do emissor. A variante completa o produto.
| Campo | Tipo | Descrição |
|---|---|---|
kind | string | service (NFS-e) ou goods (NF-e). Sem ele: produto physical é mercadoria, os outros são serviço |
ncm | string | NCM, 8 dígitos (mercadoria) |
cest | string | CEST, 7 dígitos |
gtin | string | GTIN/EAN — 8, 12, 13 ou 14 dígitos |
origin | integer | Origem da mercadoria, 0 a 8 (0 = nacional) |
unit | string | Unidade comercial (UN, KG, CX…), até 6 caracteres |
cClassTrib | string | Classificação tributária do IBS/CBS, 6 dígitos |
serviceCode | string | Item da lista de serviços da LC 116 (01.07 ou 0107) |
cityServiceCode | string | Código do serviço na prefeitura, até 20 caracteres |
nbs | string | NBS, 9 dígitos (serviço) |
O CFOP não vai no produto: depende da venda (mesmo estado ou outro) e fica nos padrões do app. Campo desconhecido é recusado.
Imagens
images[] aceita somente URL — no produto e na variante. Mandar o arquivo embutido
({"name": "foto.jpg", "base64": "..."}) responde 422, com a mensagem apontando o caminho certo.
Para anexar um arquivo, use o fluxo de upload presigned e mande a URL do asset confirmado:
1. POST /v1/assets/uploads → { uploadUrl, asset: { id } }
2. PUT <uploadUrl> com o header Content-Type igual ao contentType pedido (o arquivo, direto no storage — não passa por esta API)
3. POST /v1/assets/uploads/{assetId}/confirm → { url }
4. POST /v1/products com "images": ["<url do passo 3>"]Detalhes, tetos de tamanho e o campo byteSize: Assets.
Uma URL externa (fora do nosso CDN) também é aceita: ela é baixada e re-hospedada no nosso storage de forma assíncrona, e a URL gravada passa a ser a nossa.
A imagem não se herda
images é de cada recurso. Mandar a imagem no produto não põe imagem em nenhuma variante: a
variante que não declara images é gravada com null, e é isso que GET /v1/products/{id},
GET /v1/variants e os webhooks product.created e variant.created devolvem. Se você quer a variante com foto, mande
images também dentro dela (no máximo 1).
Resposta 201 Created
Retorna o produto completo com suas variantes (mesmo formato de Consultar):
{
"id": "prd_01hqzvabc",
"name": "Premium Course",
"slug": "premium-course",
"salesPage": "https://loja.com/premium",
"description": "Curso completo",
"language": "pt-BR",
"enabled": true,
"salesQty": null,
"type": "digital",
"category": "courses",
"externalReference": "SKU-EXT-1",
"warrantyDays": 30,
"images": ["https://example.com/img1.png"],
"imageAssetIds": null,
"createdAt": "2026-04-12T17:56:33.000Z",
"updatedAt": "2026-04-12T17:56:33.000Z",
"variants": [
{
"id": "var_01hqzvabc",
"productId": "prd_01hqzvabc",
"name": "Plano Anual",
"slug": "plano-anual",
"description": "Acesso por 12 meses",
"sku": "SKU-001",
"primary": true,
"enabled": true,
"pricing": {
"unitPrice": 49900,
"oldPrice": 69900,
"costPerItem": 12000,
"currency": "BRL",
"schema": "unit",
"type": "oneTime"
},
"images": null,
"metadata": null,
"attributes": null,
"externalReference": "VAR-EXT-1",
"checkoutUrl": null,
"createdAt": "2026-04-12T17:56:33.000Z",
"updatedAt": "2026-04-12T17:56:33.000Z"
}
]
}Erros
error.code | HTTP | Quando |
|---|---|---|
invalidParameters | 400 | Corpo inválido (campo obrigatório ausente, fora de faixa etc.) |
invalidPricing | 422 | Configuração de pricing inconsistente com o schema/recorrência |
Catálogo completo: Códigos de Erro.
Atualizado em 21 de outubro de 2026