Criar um produto
Cria um produto no seu catálogo e, na mesma requisição, pelo menos uma variante com preço. O produto é o "guarda-chuva" comercial (nome, tipo, garantia); a variante é o item vendável de fato, com pric
Cria um produto no seu catálogo e, na mesma requisição, pelo menos uma variante com preço. O produto é o "guarda-chuva" comercial (nome, tipo, garantia); a variante é o item vendável de fato, com pricing. Use este endpoint sempre que cadastrar uma nova oferta — um curso, um plano de assinatura ou um produto físico.
POST /v1/products
Como funciona
Um produto agrupa informações de apresentação e regras de negócio (nome, descrição, tipo, garantia, categoria). Mas quem carrega o preço é a variante (variant): cada produto tem uma ou mais variantes, e é a variante que o cliente compra. Por isso a criação é atômica — você envia o produto e ao menos uma variante no mesmo corpo, e a API cria tudo de uma vez.
Pontos importantes do mecanismo:
- Atômico: se qualquer variante for inválida, nada é criado — você recebe
422e nenhumprd_*é gerado. pricing.typeepricing.schemasão definidos na criação e tratados como imutáveis. Para trocar entreoneTime(cobrança única) erecurring(assinatura), crie uma nova variante em vez de editar a existente — tentar alterar retornapricingTypeImmutable/pricingSchemaImmutable(422).- Dinheiro em centavos.
unitPrice: 9900= R$ 99,00. Veja Convenções. - IDs opacos. A resposta traz
iddo produto (prd_*) e de cada variante (var_*). Guarde a string inteira; não faça parsing.
Nota
"Variante" não significa só "tamanho/cor". Aqui ela é a unidade de preço: um mesmo curso pode ter a variante "Plano Anual" (oneTime) e "Plano Mensal" (recurring), cada uma com seu pricing. Para gerenciá-las depois da criação, veja Variants.
Quando usar
- Cadastrar uma nova oferta no catálogo (produto + sua(s) variante(s) de preço).
- Subir um catálogo do seu sistema externo, usando
externalReferencepara correlacionar.
Quando não usar:
- Para adicionar uma variante a um produto que já existe, use o endpoint de criação de variante (
POST /v1/products/{productId}/variants) — veja Variants. Não recrie o produto. - Para alterar dados do produto (nome, categoria, flags), use Update.
Requisição
Headers
| Cabeçalho | Valor | Obrigatório |
|---|---|---|
SelectKey | sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ (sandbox: sl_test_…) | Sim |
Content-Type | application/json | Sim |
X-Idempotency-Key | Chave única da operação | Recomendado |
A autenticação é sempre pelo header SelectKey — nunca Authorization: Bearer/Basic. Veja Autenticação. Como criar é uma operação de escrita, envie um X-Idempotency-Key: se a requisição for repetida (timeout, retry), o produto não é duplicado. Veja Idempotência.
Corpo — produto
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | Sim | Nome do produto |
type | string | Sim | Tipo. Enum: physical, digital, other |
category | string | Sim | Categoria de negócio (ex.: courses, saas) |
warrantyDays | integer | Sim | Dias de garantia. Faixa: 7–3650 |
variants[] | array | Sim | Ao menos uma variante; cada item exige name e pricing |
slug | string | Não | Identificador amigável para URLs |
description | string | Não | Descrição do produto |
language | string | Não | Código ISO do idioma (ex.: pt, pt-BR) |
salesPage | string (url) | Não | URL da página de vendas |
enabled | boolean | Não | Se o produto está ativo |
externalReference | string | Não | Sua referência externa (correlação com seu catálogo) |
Corpo — variants[]
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
variants[].name | string | Sim | Nome de exibição da variante |
variants[].pricing | object | Sim | Precificação da variante (tabela abaixo) |
variants[].description | string | Não | Descrição curta |
variants[].sku | string | Não | SKU (código de estoque) |
variants[].enabled | boolean | Não | Se a variante é vendável |
variants[].externalReference | string | Não | Referência externa da variante |
Corpo — variants[].pricing
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
unitPrice | integer (centavos) | Sim | Preço unitário. Faixa: 500–200000000 |
currency | string | Sim | Código da moeda. Enum: BRL (ISO 4217) |
schema | string | Não | Como o preço é calculado. Enum: unit (hoje, único valor). Imutável após criar |
type | string | Não | oneTime (cobrança única) ou recurring (assinatura). Default oneTime. Imutável após criar |
oldPrice | integer (centavos) | Não | Preço "de" / original (exibido riscado). Faixa: 500–200000000 |
trialInterval | string | Não¹ | Unidade do período de teste. Enum: day, week, month, year |
trialIntervalCount | integer | Não¹ | Quantidade de unidades do trial. Faixa: 1–365 |
billingType | string | Não² | Tipo de cobrança da assinatura. Enum: prepaid, postpaid, exactday |
billingFrequency | string | Não² | Cadência de cobrança. Enum: daily, weekly, monthly, yearly |
billingFrequencyCount | integer | Não² | Nº de unidades de frequência entre cobranças. Faixa: 1–999 |
billingExactDay | integer | Não³ | Dia do mês da cobrança. Faixa: 1–31 |
cycles | integer | Não | Total de ciclos de cobrança. Faixa: 1–999. Omitir/null = infinito |
¹ Aplica-se apenas quando type=recurring. ² Obrigatório quando type=recurring. ³ Usado quando billingType=exactday.
Exemplo — produto digital com variante de cobrança única
curl -X POST "https://api.selectwin.io/v1/products" \
-H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: 9c1f2a8e-create-premium-course" \
-d '{
"name": "Premium Course",
"slug": "premium-course",
"description": "Curso completo",
"type": "digital",
"language": "pt",
"category": "courses",
"warrantyDays": 30,
"enabled": true,
"externalReference": "SKU-EXT-1",
"variants": [
{
"name": "Plano Anual",
"description": "Acesso por 12 meses",
"sku": "SKU-001",
"enabled": true,
"externalReference": "VAR-EXT-1",
"pricing": {
"unitPrice": 49900,
"currency": "BRL",
"schema": "unit",
"type": "oneTime",
"oldPrice": 69900
}
}
]
}'Exemplo — variante recorrente (assinatura)
Para uma assinatura, use type: "recurring" e informe os campos de cobrança (billingType, billingFrequency, billingFrequencyCount). Opcionalmente, um período de teste:
{
"name": "SaaS Pro",
"type": "digital",
"category": "saas",
"warrantyDays": 7,
"variants": [
{
"name": "Mensal",
"sku": "SKU-M",
"pricing": {
"unitPrice": 9900,
"currency": "BRL",
"schema": "unit",
"type": "recurring",
"billingType": "prepaid",
"billingFrequency": "monthly",
"billingFrequencyCount": 1,
"trialInterval": "day",
"trialIntervalCount": 7,
"cycles": 12
}
}
]
}Resposta
201 Created. O corpo é o produto completo, com cada variante criada (incluindo seus id e timestamps). O pricing na resposta usa daysTrial (campo derivado, em dias) além dos campos de configuração que você enviou.
{
"id": "prd_01hqzvabc",
"enabled": true,
"name": "Plano Premium",
"description": "Full access plan",
"type": "digital",
"language": "pt-BR",
"category": "saas",
"slug": "plano-premium",
"images": ["https://cdn.selectwin.io/p/img1.png"],
"warrantyDays": 30,
"variants": [
{
"id": "var_01hqzvabc",
"name": "Mensal",
"enabled": true,
"description": "Monthly billing",
"sku": "SKU-M",
"pricing": {
"oldPrice": 14900,
"unitPrice": 9900,
"currency": "BRL",
"schema": "unit",
"type": "recurring",
"daysTrial": 7,
"billingType": "prepaid",
"billingFrequency": "monthly",
"billingFrequencyCount": 1,
"billingExactDay": 10,
"cycles": 12,
"trialInterval": "day",
"trialIntervalCount": 7
},
"images": ["https://cdn.selectwin.io/v/var.png"],
"metadata": { "tier": "pro" },
"externalReference": "VAR-EXT-1",
"attributes": {},
"updatedAt": "2026-04-12T17:56:33.000Z",
"createdAt": "2026-04-12T17:56:33.000Z"
}
],
"externalReference": "PROD-EXT-1",
"updatedAt": "2026-04-12T17:56:33.000Z",
"createdAt": "2026-04-12T17:56:33.000Z"
}Campos-chave da resposta
| Campo | Tipo | Descrição |
|---|---|---|
id | string | ID do produto (prd_*). Guarde-o |
variants[].id | string | ID de cada variante criada (var_*) — use no checkout e em transações |
warrantyDays | integer | Garantia em dias |
variants[].pricing.unitPrice | integer | Preço em centavos |
variants[].pricing.daysTrial | integer | Duração do trial em dias (derivado de trialInterval/trialIntervalCount) |
createdAt / updatedAt | string | Timestamps ISO 8601 UTC |
Erros
error.code | HTTP | Quando ocorre |
|---|---|---|
invalidParameters | 400 | Validação genérica de campos (veja error.params) |
productIdIsInvalid | 400 | Referência de produto inválida |
productCreationFailed | 422 | Falha ao criar o produto (regra de negócio) |
pricingTypeImmutable | 422 | Tentou definir/alterar pricing.type de forma não permitida |
pricingSchemaImmutable | 422 | Tentou definir/alterar pricing.schema de forma não permitida |
productCreateLimitReached | 403 | Limite de produtos do plano atingido |
unauthorized | 401 | SelectKey ausente, inválida ou revogada |
forbidden | 403 | Autenticado, mas sem permissão para criar |
unprocessableEntity | 422 | Entidade não processável (regra de negócio) |
tooManyRequests | 429 | Limite de requisições excedido (respeite error.retryAfterMinutes) |
serverError | 500 | Erro interno — repita com backoff |
Trate sempre pelo error.code (estável), não pela message. Catálogo completo: Códigos de Erro; estrutura do envelope: Tratamento de Erros.
Boas práticas
- Sempre envie
X-Idempotency-Keyao criar. Um retry após timeout não deve gerar dois produtos. - Decida
typeeschemada variante com cuidado — eles são imutáveis. Se precisar de cobrança única e recorrente, crie duas variantes no mesmo produto. - Converta para centavos antes de enviar
unitPriceeoldPrice.R$ 99,00→9900. Respeite a faixa mínima (500= R$ 5,00). - Para assinaturas (
type=recurring), lembre quebillingType,billingFrequencyebillingFrequencyCountsão obrigatórios; sem eles a criação falha na validação. - Use
externalReferenceno produto e na variante para amarrar ao seu catálogo e reconciliar depois sem depender do ID interno. - Guarde os
var_*, não só oprd_*: é a variante que entra no checkout e na transação. - Defina
oldPriceapenas para exibir o preço "de/por"; ele deve ser maior queunitPricepara fazer sentido comercial.
Veja também
- Overview — modelo do objeto, ciclo de vida e todas as operações.
- Read — recuperar o produto completo pelo
prd_*. - Update — alterar nome, categoria e flags do produto.
- List — listar e filtrar produtos do catálogo.
- Variants — criar/atualizar variantes de um produto existente.
- Convenções · Idempotência · Autenticação
How is this guide?