SelectwinDOCS
Produtos

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 422 e nenhum prd_* é gerado.
  • pricing.type e pricing.schema são definidos na criação e tratados como imutáveis. Para trocar entre oneTime (cobrança única) e recurring (assinatura), crie uma nova variante em vez de editar a existente — tentar alterar retorna pricingTypeImmutable / pricingSchemaImmutable (422).
  • Dinheiro em centavos. unitPrice: 9900 = R$ 99,00. Veja Convenções.
  • IDs opacos. A resposta traz id do 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 externalReference para 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çalhoValorObrigatório
SelectKeysl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ (sandbox: sl_test_…)Sim
Content-Typeapplication/jsonSim
X-Idempotency-KeyChave única da operaçãoRecomendado

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

CampoTipoObrigatórioDescrição
namestringSimNome do produto
typestringSimTipo. Enum: physical, digital, other
categorystringSimCategoria de negócio (ex.: courses, saas)
warrantyDaysintegerSimDias de garantia. Faixa: 73650
variants[]arraySimAo menos uma variante; cada item exige name e pricing
slugstringNãoIdentificador amigável para URLs
descriptionstringNãoDescrição do produto
languagestringNãoCódigo ISO do idioma (ex.: pt, pt-BR)
salesPagestring (url)NãoURL da página de vendas
enabledbooleanNãoSe o produto está ativo
externalReferencestringNãoSua referência externa (correlação com seu catálogo)

Corpo — variants[]

CampoTipoObrigatórioDescrição
variants[].namestringSimNome de exibição da variante
variants[].pricingobjectSimPrecificação da variante (tabela abaixo)
variants[].descriptionstringNãoDescrição curta
variants[].skustringNãoSKU (código de estoque)
variants[].enabledbooleanNãoSe a variante é vendável
variants[].externalReferencestringNãoReferência externa da variante

Corpo — variants[].pricing

CampoTipoObrigatórioDescrição
unitPriceinteger (centavos)SimPreço unitário. Faixa: 500200000000
currencystringSimCódigo da moeda. Enum: BRL (ISO 4217)
schemastringNãoComo o preço é calculado. Enum: unit (hoje, único valor). Imutável após criar
typestringNãooneTime (cobrança única) ou recurring (assinatura). Default oneTime. Imutável após criar
oldPriceinteger (centavos)NãoPreço "de" / original (exibido riscado). Faixa: 500200000000
trialIntervalstringNão¹Unidade do período de teste. Enum: day, week, month, year
trialIntervalCountintegerNão¹Quantidade de unidades do trial. Faixa: 1365
billingTypestringNão²Tipo de cobrança da assinatura. Enum: prepaid, postpaid, exactday
billingFrequencystringNão²Cadência de cobrança. Enum: daily, weekly, monthly, yearly
billingFrequencyCountintegerNão²Nº de unidades de frequência entre cobranças. Faixa: 1999
billingExactDayintegerNão³Dia do mês da cobrança. Faixa: 131
cyclesintegerNãoTotal de ciclos de cobrança. Faixa: 1999. 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

CampoTipoDescrição
idstringID do produto (prd_*). Guarde-o
variants[].idstringID de cada variante criada (var_*) — use no checkout e em transações
warrantyDaysintegerGarantia em dias
variants[].pricing.unitPriceintegerPreço em centavos
variants[].pricing.daysTrialintegerDuração do trial em dias (derivado de trialInterval/trialIntervalCount)
createdAt / updatedAtstringTimestamps ISO 8601 UTC

Nota

A resposta de criação traz o objeto completo (com pricing detalhado por variante). Já as listagens (List) retornam uma projeção mais leve. Para reler o produto inteiro depois, use Read.

Erros

error.codeHTTPQuando ocorre
invalidParameters400Validação genérica de campos (veja error.params)
productIdIsInvalid400Referência de produto inválida
productCreationFailed422Falha ao criar o produto (regra de negócio)
pricingTypeImmutable422Tentou definir/alterar pricing.type de forma não permitida
pricingSchemaImmutable422Tentou definir/alterar pricing.schema de forma não permitida
productCreateLimitReached403Limite de produtos do plano atingido
unauthorized401SelectKey ausente, inválida ou revogada
forbidden403Autenticado, mas sem permissão para criar
unprocessableEntity422Entidade não processável (regra de negócio)
tooManyRequests429Limite de requisições excedido (respeite error.retryAfterMinutes)
serverError500Erro 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-Key ao criar. Um retry após timeout não deve gerar dois produtos.
  • Decida type e schema da 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 unitPrice e oldPrice. R$ 99,009900. Respeite a faixa mínima (500 = R$ 5,00).
  • Para assinaturas (type=recurring), lembre que billingType, billingFrequency e billingFrequencyCount são obrigatórios; sem eles a criação falha na validação.
  • Use externalReference no produto e na variante para amarrar ao seu catálogo e reconciliar depois sem depender do ID interno.
  • Guarde os var_*, não só o prd_*: é a variante que entra no checkout e na transação.
  • Defina oldPrice apenas para exibir o preço "de/por"; ele deve ser maior que unitPrice para fazer sentido comercial.

Veja também

How is this guide?

On this page