SelectwinDOCS
Produtos

Variantes de produto

As variantes são as versões vendáveis de um produto(/docs/guide/products/overview): cada uma carrega seu próprio preço, SKU e regras de cobrança. Esta página mostra como criar, listar, consultar, atua

As variantes são as versões vendáveis de um produto: cada uma carrega seu próprio preço, SKU e regras de cobrança. Esta página mostra como criar, listar, consultar, atualizar e excluir variantes — e por que o pricing define se a variante é cobrada uma vez (transação) ou de forma recorrente (assinatura).

OperaçãoMétodoEndpoint
CriarPOST/v1/products/{productId}/variants
AtualizarPATCH/v1/products/{productId}/variants
Listar (de um produto)GET/v1/products/{productId}/variants
Listar todas, escopo da conta (array)GET/v1/variants
Listar todas (array enxuto)GET/v1/products/variants/listall
Consultar umaGET/v1/variants/{variantId}
ExcluirDELETE/v1/variants/{variantId}

Todas as chamadas usam a base https://api.selectwin.io/v1 e o header SelectKey (em produção sl_live_…, em sandbox sl_test_…). Nunca use Authorization: Bearer/Basic. Veja Autenticação.


Como funciona

Uma variante sempre pertence a um produto (productId) e é a unidade que realmente entra em uma cobrança. O objeto pricing segue o modelo de precificação de dois eixos, ambos imutáveis após a criação:

  • pricing.type — natureza da cobrança: oneTime (cobrada uma vez, em uma transação) ou recurring (assinatura). Default oneTime.
  • pricing.schema — como o preço é calculado: hoje só unit (preço por unidade).

Os campos de recorrência (billingType, billingFrequency, billingFrequencyCount, cycles, trialInterval, trialIntervalCount, billingExactDay) só fazem sentido quando type=recurring. Quando type=recurring, billingType, billingFrequency e billingFrequencyCount passam a ser obrigatórios. Veja a referência completa de campos em Visão geral de Produtos.

Imutabilidade

pricing.type e pricing.schema não podem ser alterados depois de criados. Para mudar a natureza ou o esquema de cobrança, crie uma nova variante e desative a antiga (enabled: false). Tentar alterá-los retorna pricingTypeImmutable ou pricingSchemaImmutable (422).

pricing.unitPrice é o preço; pricing.costPerItem é o custo

unitPrice (em centavos, 9900 = R$ 99,00) é o que o cliente paga. costPerItem (também em centavos, pode ser 0) é o custo do item (COGS) e serve de base para os relatórios de margem e lucro em analytics. O costPerItem só aparece na leitura individual (GET /v1/variants/{variantId}) e nas respostas de criar/atualizar — ele não vem nas variantes embutidas no produto nem na listagem por produto.


Quando usar

  • Crie variantes sempre que um produto tiver mais de uma forma de venda (mensal, anual, tamanhos, planos) — cada preço/SKU é uma variante.
  • Use oneTime para vendas avulsas (cobradas em uma transação) e recurring para assinaturas.
  • Não tente reaproveitar uma variante mudando seu type/schema — eles são imutáveis. Crie outra variante.
  • Para apenas ocultar uma variante sem perder o histórico, prefira enabled: false em vez de excluir.

Criar variantes

POST /v1/products/{productId}/variants

Cria uma variante sob o produto identificado por productId no caminho. A resposta sempre envolve o resultado em um array results, com um contador created.

Headers

HeaderObrigatórioValor
SelectKeysimsl_live_… (produção) ou sl_test_… (sandbox)
Content-Typesimapplication/json

Parâmetros de caminho

NomeTipoObrigatórioDescrição
productIdstringsimID do produto pai (ex.: prd_01hqzvabc)

Corpo

CampoTipoObrigatórioDescrição
namestringsimNome da variante
pricingobjectsimObjeto de precificação (abaixo)
descriptionstringnãoDescrição curta
skustringnãoStock keeping unit
imagesarraynãoURLs de imagens (máx. 1)
metadataobjectnãoMetadados livres
attributesobjectnãoPares chave-valor (máx. 20)
externalReferencestringnãoReferência no seu catálogo
enabledbooleannãoSe a variante está disponível para venda

Campos de pricing:

CampoTipo / enumObrigatórioDescrição
unitPriceinteger, 500..200000000simPreço unitário em centavos
currencyenum ["BRL"]simCódigo ISO 4217
schemaenum ["unit"]nãoEsquema de cálculo. Hoje só unit. Imutável
typeenum ["oneTime","recurring"]nãoNatureza da cobrança. Default oneTime. Imutável
oldPriceinteger, 500..200000000nãoPreço "de" (promocional), em centavos
costPerIteminteger, 0..200000000nãoCusto do item (COGS) em centavos. Pode ser 0
billingTypeenum ["prepaid","postpaid","exactday"]recurringTipo da cobrança recorrente. Obrigatório se type=recurring
billingFrequencyenum ["daily","weekly","monthly","yearly"]recurringUnidade da cadência. Obrigatório se type=recurring
billingFrequencyCountinteger, 1..999recurringMultiplicador (ex.: monthly + 3 = trimestral). Obrigatório se type=recurring
billingExactDayinteger, 1..31nãoDia fixo de cobrança, quando billingType=exactday
cyclesinteger, 1..999nãoTotal de ciclos. Omitir/null = infinito
trialIntervalenum ["day","week","month","year"]nãoUnidade do período de teste (só recurring)
trialIntervalCountinteger, 1..365nãoDuração do trial (ex.: 2 + week = 2 semanas)

Não envie daysTrial

daysTrial aparece apenas na leitura como a duração equivalente em dias (derivada de trialInterval + trialIntervalCount). Não a inclua ao criar ou atualizar — modele o trial sempre pelos dois campos trialInterval e trialIntervalCount.

Exemplo de uma variante de assinatura (recurring):

curl -X POST "https://api.selectwin.io/v1/products/prd_01hqzvabc/variants" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Plano Anual",
    "description": "Acesso por 12 meses",
    "sku": "SKU-001",
    "images": ["https://example.com/variant.png"],
    "metadata": { "tier": "pro" },
    "attributes": {},
    "externalReference": "VAR-EXT-1",
    "enabled": true,
    "pricing": {
      "unitPrice": 9900,
      "currency": "BRL",
      "schema": "unit",
      "type": "recurring",
      "oldPrice": 14900,
      "costPerItem": 4500,
      "billingType": "prepaid",
      "billingFrequency": "monthly",
      "billingFrequencyCount": 1,
      "billingExactDay": 10,
      "cycles": 12,
      "trialInterval": "day",
      "trialIntervalCount": 7
    }
  }'

Para uma variante de venda avulsa, use "type": "oneTime" e omita os campos de recorrência:

{
  "name": "Licença vitalícia",
  "pricing": { "unitPrice": 9900, "currency": "BRL", "schema": "unit", "type": "oneTime" }
}

Resposta 201 Created

{
  "status": "success",
  "created": 1,
  "results": [
    {
      "id": "var_01hqzvabc",
      "name": "Plano Anual",
      "enabled": true,
      "description": "Yearly billing",
      "sku": "SKU-001",
      "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,
        "costPerItem": 4500
      },
      "images": ["https://cdn.selectwin.io/v/var.png"],
      "metadata": { "tier": "pro" },
      "externalReference": "VAR-EXT-1",
      "attributes": {}
    }
  ]
}
CampoDescrição
status"success" em caso de êxito
createdQuantidade de variantes criadas
results[]Variantes criadas, já com id gerado e pricing completo (inclui daysTrial e costPerItem)

Atualizar variantes

PATCH /v1/products/{productId}/variants

Atualiza uma variante existente do produto. O corpo é o mesmo da criação, com um campo extra obrigatório: o id da variante a alterar.

Corpo (campos adicionais ao da criação)

CampoTipoObrigatórioDescrição
idstring, padrão ^[a-z]+_[A-Za-z0-9]+$simID público da variante a atualizar (ex.: var_01hqzvabc)

pricing.unitPrice e pricing.currency continuam obrigatórios dentro de pricing. Lembre-se: pricing.type e pricing.schema não podem ser alterados.

curl -X PATCH "https://api.selectwin.io/v1/products/prd_01hqzvabc/variants" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "var_01hqzvabc",
    "name": "Plano Anual (novo preço)",
    "pricing": {
      "unitPrice": 10900,
      "currency": "BRL",
      "type": "recurring",
      "billingType": "prepaid",
      "billingFrequency": "monthly",
      "billingFrequencyCount": 1
    }
  }'

Resposta 200 OK

{
  "status": "success",
  "updated": 1,
  "results": [
    {
      "id": "var_01hqzvabc",
      "name": "Plano Anual (novo preço)",
      "enabled": true,
      "pricing": {
        "oldPrice": 14900,
        "unitPrice": 10900,
        "currency": "BRL",
        "billingType": "prepaid",
        "billingFrequency": "monthly",
        "billingFrequencyCount": 1,
        "costPerItem": 4500
      },
      "images": ["https://cdn.selectwin.io/v/var.png"],
      "metadata": { "tier": "pro" },
      "externalReference": "VAR-EXT-1",
      "attributes": {}
    }
  ]
}

O contador agora é updated (em vez de created).


Listar variantes

Há três formas de listar, com finalidades diferentes.

De um produto — GET /v1/products/{productId}/variants (paginada)

Retorna as variantes de um produto, com paginação e filtros. Veja Paginação para o significado de offset, limit, hasMore e page.

Parâmetros de query

NomeTipo / enumDescrição
limitnumber, 1..100Itens por página (ex.: 20)
offsetnumber, 0..Quantos itens pular
sortenum ["ascending","descending"]Ordem dos resultados
skustringFiltra por SKU
currencyenum ["BRL"]Filtra pela moeda do preço
enabledbooleanFiltra pelo flag de disponibilidade
daterangestring (ISO 8601)Restringe a um único dia civil
daterangegt / daterangegtestring (ISO 8601)Limite inferior, exclusivo / inclusivo
daterangelt / daterangeltestring (ISO 8601)Limite superior, exclusivo / inclusivo
curl "https://api.selectwin.io/v1/products/prd_01hqzvabc/variants?limit=20&enabled=true" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ"
{
  "offset": 0,
  "limit": 20,
  "total": 2,
  "page": {
    "offset": { "first": 0, "prev": null, "next": null, "last": 0 },
    "current": 1,
    "total": 1
  },
  "data": [
    {
      "id": "var_01hqzvabc",
      "name": "Mensal",
      "enabled": true,
      "description": "Monthly billing",
      "sku": "SKU-M",
      "pricing": {
        "oldPrice": 14900,
        "unitPrice": 9900,
        "currency": "BRL",
        "schema": "unit",
        "type": null,
        "daysTrial": 7,
        "billingType": null,
        "billingFrequency": null,
        "billingFrequencyCount": null,
        "billingExactDay": null,
        "cycles": null,
        "trialInterval": null,
        "trialIntervalCount": null
      },
      "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"
    }
  ],
  "hasMore": false
}

Nesta projeção o objeto pricing não inclui costPerItem — use a consulta individual quando precisar do custo.

Todas — GET /v1/variants (array enxuto, escopo da conta)

Lista variantes em escopo global da conta (não preso a um produto). A resposta é o mesmo array direto e enxuto do listall — campos id, name, enabled, images, description, sku, o preço no campo plano price (em centavos), updatedAt e createdAt. Não há envelope de paginação (offset/limit/total/page/data/hasMore), nem objeto pricing, nem productId na resposta.

A diferença real para o listall está só nos filtros: aqui você pode restringir por id e productId (ambos no padrão ^[a-z]+_[A-Za-z0-9]+$), além dos filtros acima. O limit aceita até 999999.

curl "https://api.selectwin.io/v1/variants?productId=prd_01hqzvabc&sort=descending" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ"
[
  {
    "id": "var_01hqzvabc",
    "name": "Mensal",
    "enabled": true,
    "images": ["https://example.com/variant.png"],
    "description": "Monthly billing",
    "sku": "SKU-M",
    "price": 9900,
    "updatedAt": "2026-04-12T17:56:33.000Z",
    "createdAt": "2026-04-12T17:56:33.000Z"
  }
]

Todas — GET /v1/products/variants/listall (array enxuto)

Retorna um array direto (sem envelope de paginação) com campos resumidos. Aqui o preço é o campo plano price (em centavos) — não há objeto pricing, type nem productId. Útil para preencher seletores/menus rapidamente.

curl "https://api.selectwin.io/v1/products/variants/listall?enabled=true" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ"
[
  {
    "id": "var_01hqzvabc",
    "name": "Mensal",
    "enabled": true,
    "images": ["https://example.com/variant.png"],
    "description": "Monthly billing",
    "sku": "SKU-M",
    "price": 9900,
    "updatedAt": "2026-04-12T17:56:33.000Z",
    "createdAt": "2026-04-12T17:56:33.000Z"
  }
]
ListagemEnvelopePreçoInclui costPerItem / productId
/products/{productId}/variantspaginadoobjeto pricingnão / não
/variantsarray diretocampo plano pricenão / não
/products/variants/listallarray diretocampo plano pricenão / não

Consultar uma variante

GET /v1/variants/{variantId}

Lê uma variante pelo ID. É a única leitura que traz o objeto pricing completo, incluindo costPerItem, além de productId.

curl "https://api.selectwin.io/v1/variants/var_01hqzvabc" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ"
{
  "id": "var_01hqzvabc",
  "productId": "prd_01hqzvabc",
  "name": "Mensal",
  "enabled": true,
  "description": "Monthly billing",
  "sku": "SKU-M",
  "pricing": {
    "oldPrice": 14900,
    "unitPrice": 9900,
    "costPerItem": 5000,
    "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": null,
  "updatedAt": "2026-04-12T17:56:33.000Z",
  "createdAt": "2026-04-12T17:56:33.000Z"
}

Excluir uma variante

DELETE /v1/variants/{variantId}

Exclui a variante. A operação é definitiva — confirme antes que nenhuma assinatura ativa ou oferta dependa dela. Se a variante ainda estiver em uso, prefira desativá-la com enabled: false via atualização em vez de excluir.

curl -X DELETE "https://api.selectwin.io/v1/variants/var_01hqzvabc" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ"

Resposta 200 OK

{
  "id": "var_01hqzvabc",
  "resource": "variant",
  "deleted": true
}
CampoDescrição
idID da variante excluída
resourceTipo do recurso ("variant")
deletedtrue quando a exclusão é confirmada

Erros

error.codeHTTPQuando ocorre
variantIdNotFound404Variante inexistente
variantIdIsInvalid / productVariantIdIsInvalid400ID de variante inválido
productVariantIdIsRequired400Faltou o ID da variante (ex.: id no PATCH)
productIdIsInvalid400productId do caminho inválido
productIdNotFound404Produto pai não encontrado
pricingTypeImmutable422Tentou alterar pricing.type
pricingSchemaImmutable422Tentou alterar pricing.schema
variantRecurringNotAllowedInTransaction422Variante recurring usada em transação avulsa
oneTimeVariantNotAllowedInSubscription422Variante oneTime usada em assinatura

Erros transversais que valem para todos os endpoints: 401 unauthorized, 403 forbidden, 422 unprocessableEntity (corpo inválido), 429 tooManyRequests, 500 serverError. Veja o catálogo completo em Códigos de Erro e o formato do envelope em Erros.


Boas práticas

  • Use centavos, sempre. unitPrice, oldPrice e costPerItem são inteiros em centavos (9900 = R$ 99,00). Veja Convenções.
  • Defina type/schema certos na criação — são imutáveis. Para mudar, crie outra variante e desative a antiga.
  • Modele o trial com trialInterval + trialIntervalCount; não envie daysTrial (é só leitura).
  • Para recurring, sempre informe billingType, billingFrequency e billingFrequencyCount — sem eles a criação falha.
  • Prefira enabled: false a excluir quando a variante tem histórico ou está em assinaturas ativas — a exclusão é definitiva.
  • Escolha a listagem certa: listall (array com price plano) para menus rápidos; GET /v1/variants/{variantId} quando precisar de costPerItem.
  • Use externalReference para casar a variante com o ID do seu próprio catálogo e evitar duplicatas.

Veja também

How is this guide?

On this page