komm
v1versão atual

API komm

Cria links de checkout, acompanha os pagamentos, recebe avisos e reembolsa — tudo a partir do teu sistema, do teu CRM ou de uma automação. É a mesma máquina que serve o painel da komm, exposta por HTTP.

Todos os endpoints vivem sob um único URL base. A versão faz parte do caminho: dentro da v1 só acrescentamos coisas, nunca quebramos o que já existe.

URL base
https://komm.pt/api/v1

Começar

Autenticação

Cada pedido leva uma chave de API no cabeçalho Authorization. Geras as tuas chaves no painel, em Conta → API, escolhendo o que cada uma pode fazer.

Cabeçalho
Authorization: Bearer kk_a_tua_chave_aqui

A chave aparece por inteiro uma única vez, no momento em que a crias — guardamos apenas uma impressão digital dela, por isso não conseguimos mostrá-la outra vez. Se a perderes, revoga e cria outra. Uma chave pertence sempre a uma loja e nunca vê dados de outra.

checkout:read

Ler checkouts e pedidos associados

checkout:write

Criar e gerir links de checkout

checkout:refund

Reembolsar pedidos

catalog:read

Ler catálogos, produtos e modelos

webhooks:read

Ler subscrições de webhook

webhooks:write

Gerir subscrições de webhook

Dá a cada chave só o que ela precisa. Se um pedido exigir um âmbito que a chave não tem, recebes 403 forbidden_scope com o âmbito que falta. Repara que checkout:refund é separado de checkout:write de propósito: devolver dinheiro merece um portão a mais.

Começar

Convenções

Quatro regras que valem para toda a API.

Dinheiro em cêntimos

Sempre números inteiros, nunca decimais: 1499 são 14,99 €. Vale para preços, totais e comissões. A moeda é o euro.

Datas em ISO 8601, UTC

2026-07-28T14:37:23.148Z. Também é este o formato do parâmetro cursor nas listagens.

Paginação por cursor

As listagens devolvem data e nextCursor. Para a página seguinte, reenvia o mesmo pedido com cursor igual a esse valor. Quando nextCursor vem null, chegaste ao fim.

Erros com um código estável

Programa contra o code, não contra a mensagem. O código nunca muda dentro da v1; a mensagem pode ser reescrita para ficar mais clara.

Começar

Idempotência

Os pedidos que criam um link ou movimentam dinheiro exigem o cabeçalho Idempotency-Key. É o que garante que uma rede instável não te deixa com dois links ou dois reembolsos.

Cabeçalho
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

Usa um valor único por operação — um UUID, ou o identificador da venda no teu sistema. Se repetires a chamada com a mesma chave e o mesmo corpo, devolvemos a resposta original sem executar nada de novo. Se usares a mesma chave com um corpo diferente, recebes 409 idempotency_conflict — é um sinal de que há um erro na tua lógica de repetição, não um pedido a rejeitar às cegas.

Começar

Como o modelo de checkout é escolhido

Cada link usa um modelo — a página que o cliente vê e os campos que lhe são pedidos. Não precisas de escolher: se não enviares modelId, a komm deriva o modo a partir dos produtos e usa o modelo adequado.

physicalTodos os itens são produtos físicos ou kits.
digitalTodos os itens são produtos digitais ou serviços.
hybridA venda mistura produtos físicos e digitais.
schedulingHá um produto de agenda. Nunca se mistura com outros, porque só o modelo de agenda sabe pedir o horário.

Se enviares um modelId que não sirva para os produtos, não te deixamos adivinhar: o erro diz o modo que os teus itens produziram e lista os modelos que aceitamos.

422 model_incompatible
{
  "error": {
    "code": "model_incompatible",
    "message": "Model 'digital-v1' does not fit these products; your items resolve to mode 'physical'. Compatible models: mvp-1, impulso-fisico-v1.",
    "details": {
      "requestedModelId": "digital-v1",
      "resolvedMode": "physical",
      "compatibleModelIds": ["mvp-1", "impulso-fisico-v1"]
    }
  }
}

Listar catálogos

GET/catalogs
âmbito catalog:read

Catálogos da tua loja, cada um com os produtos e o preço já resolvido. O preço de catálogo tem prioridade sobre o preço base do produto — é este valor que um checkout congela quando não envias um preço explícito.

Resposta
{
  "data": [
    {
      "id": "ee4e2f23-7342-4c85-8cbc-efdc97e2dc77",
      "name": "Catálogo Full",
      "products": [
        {
          "productId": "8920392f-3852-48f0-a57c-0b9b38e55a63",
          "name": "Colágeno Plus 300g",
          "type": "physical",
          "imageUrl": "https://…/colageno.png",
          "priceCurrent": 18999,
          "priceFrom": 24999,
          "currency": "EUR"
        }
      ]
    }
  ]
}
  • O catálogo determina as comissões de parceiros e afiliados na venda. Envia o catalogId no item quando quiseres que o split seja aplicado.
curl
curl -X GET 'https://komm.pt/api/v1/catalogs' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Listar produtos

GET/products
âmbito catalog:read

Produtos da tua loja com o preço ativo em EUR. É daqui que tiras os productId para montar um checkout.

Parâmetros de query

statusdraft | active | archivedFiltra pelo estado do produto.
typephysical | digital | service | kit | schedulingFiltra pelo tipo de produto.
limitnumber (1–100)Quantos resultados devolver. Por omissão 20.
cursorISO 8601Paginação: passa o nextCursor devolvido na resposta anterior.
Resposta
{
  "data": [
    {
      "id": "909c1a5e-b19d-4032-ae72-7a4408ef38d4",
      "name": "EBook - Sono Perfeito",
      "description": "Guia completo de higiene do sono",
      "sku": "EBOOK-SONO-01",
      "type": "digital",
      "status": "active",
      "priceCurrent": 999,
      "priceFrom": 1990,
      "currency": "EUR",
      "createdAt": "2026-07-24T15:01:04.146Z"
    }
  ],
  "nextCursor": null
}
curl
curl -X GET 'https://komm.pt/api/v1/products' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Listar modelos de checkout

GET/checkout-models
âmbito catalog:read

Modelos de checkout compatíveis com um modo. Raramente precisas deste endpoint: se não enviares modelId ao criar uma sessão, a komm escolhe o modelo adequado aos produtos.

Parâmetros de query

modephysical | digital | hybrid | schedulingO modo do checkout. Por omissão hybrid. Ver "Como o modelo é escolhido".
hasCustomertrue | falseInclui os modelos que exigem um cliente associado. Por omissão false.
Resposta
{
  "data": [
    {
      "id": "digital-v1",
      "name": "Entrega Digital",
      "description": "\"Para onde envio o teu acesso?\" — ultra-minimalista, só pede email.",
      "type": "digital",
      "requiresCustomer": false
    }
  ]
}
curl
curl -X GET 'https://komm.pt/api/v1/checkout-models' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Endpoints

Links de checkout

O coração da API. Criar o link que envias ao cliente, acompanhar o pagamento e reembolsar quando for preciso.

Criar link de checkout

POST/checkout-sessions
âmbito checkout:writeexige Idempotency-Key

Cria e publica um link de checkout. A resposta traz o url para enviares ao cliente e o id para consultares o estado depois.

Corpo do pedido

nameobrigatóriostringNome interno, para te orientares no painel. O cliente não o vê.
itemsobrigatórioarray (1–20)Os produtos a vender. Campos de cada item abaixo.
items[].productIdobrigatóriouuidProduto ativo da tua loja.
items[].quantitynumber (1–99)Quantidade. Por omissão 1.
items[].priceCurrentnumber (cêntimos)O que o cliente paga. Omitido, usa o preço do catálogo ou o preço base do produto.
items[].priceFromnumber (cêntimos)O preço riscado ("antes"). Tem de ser maior ou igual a priceCurrent.
items[].catalogIduuidCatálogo de origem da venda. Determina as comissões de parceiros e afiliados.
customerIduuidAssocia um cliente existente. Permite os modelos "com cliente", que dispensam pedir nome e email no checkout.
modelIdstringForça um modelo de checkout. Omitido, a komm escolhe o adequado aos produtos.
redirectUrlurlPara onde o cliente segue depois de pagar, através de um botão "Continuar".
Pedido
{
  "name": "Promo Omega 3 — campanha julho",
  "items": [
    {
      "productId": "7881700c-149e-4e8c-9138-2ac6e49add86",
      "quantity": 2,
      "priceCurrent": 1499,
      "priceFrom": 1999
    }
  ],
  "redirectUrl": "https://o-teu-site.pt/obrigado"
}
Resposta
{
  "id": "cab7a9f5-fe35-4a91-bec8-ed4dce5d957b",
  "code": "jg5qqe",
  "url": "https://a-tua-loja.komm.pt/ck/jg5qqe",
  "status": "published",
  "name": "Promo Omega 3 — campanha julho",
  "modelId": "impulso-fisico-v1",
  "redirectUrl": "https://o-teu-site.pt/obrigado",
  "customer": null,
  "items": [
    {
      "productId": "7881700c-149e-4e8c-9138-2ac6e49add86",
      "name": "Omega 3 - 60 Capsulas",
      "type": "physical",
      "quantity": 2,
      "priceFrom": 1999,
      "priceCurrent": 1499,
      "currency": "EUR",
      "catalogId": null
    }
  ],
  "order": null,
  "createdAt": "2026-07-28T14:37:23.148Z",
  "publishedAt": "2026-07-28T14:37:23.112Z"
}
curl
curl -X POST 'https://komm.pt/api/v1/checkout-sessions' \
  -H 'Authorization: Bearer A_TUA_CHAVE' \
  -H 'Idempotency-Key: $(uuidgen)' \
  -H 'Content-Type: application/json' \
  -d '{ "name": "Promo Omega 3 — campanha julho", "items": [ { "productId": "7881700c-149e-4e8c-9138-2ac6e49add86", "quantity": 2, "priceCurrent": 1499, "priceFrom": 1999 } ], "redirectUrl": "https://o-teu-site.pt/obrigado" }'

Consultar estado

GET/checkout-sessions/{id}
âmbito checkout:read

Estado do link e do pedido mais recente associado. É o endpoint de polling: enquanto ninguém comprar, order vem null.

Resposta
{
  "id": "cab7a9f5-fe35-4a91-bec8-ed4dce5d957b",
  "status": "published",
  "url": "https://a-tua-loja.komm.pt/ck/jg5qqe",
  "order": {
    "id": "7d1e0a44-1c9b-4a3e-9f77-2b6c5d0e8a13",
    "status": "paid",
    "total": 2998,
    "currency": "EUR",
    "paidAt": "2026-07-28T15:02:11.004Z",
    "failureReason": null,
    "createdAt": "2026-07-28T15:01:47.221Z"
  }
}
  • Em vez de fazer polling, subscreve um webhook: recebes o aviso no momento em que o pedido muda de estado.
  • order.status percorre pending → paid | failed, e paid → refunded quando reembolsas.
curl
curl -X GET 'https://komm.pt/api/v1/checkout-sessions/{id}' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Listar links

GET/checkout-sessions
âmbito checkout:read

Os teus links de checkout, mais recentes primeiro.

Parâmetros de query

statusdraft | published | paused | archivedFiltra pelo estado do link.
customerIduuidSó os links associados a este cliente.
limitnumber (1–100)Por omissão 20.
cursorISO 8601Passa o nextCursor da resposta anterior.
curl
curl -X GET 'https://komm.pt/api/v1/checkout-sessions' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Pausar, reativar ou arquivar

POST/checkout-sessions/{id}/pause
âmbito checkout:write

Três endpoints com o mesmo formato, sem corpo: /pause deixa de aceitar compras, /reactivate volta a publicar com o mesmo código curto, /archive encerra o link definitivamente.

Resposta
{
  "id": "cab7a9f5-fe35-4a91-bec8-ed4dce5d957b",
  "status": "paused"
}
  • Só podes pausar um link publicado e reativar um link pausado. Fora disso recebes 409 invalid_status_transition, com o estado atual em details.
  • Arquivar não é reversível pela API.
curl
curl -X POST 'https://komm.pt/api/v1/checkout-sessions/{id}/pause' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Reembolsar

POST/checkout-sessions/{id}/refund
âmbito checkout:refundexige Idempotency-Key

Reembolsa o pedido pago deste link. O reembolso é sempre total — não há reembolso parcial nesta versão.

Resposta
{
  "orderId": "7d1e0a44-1c9b-4a3e-9f77-2b6c5d0e8a13",
  "status": "refunded"
}
  • status refunded significa resolvido de imediato. status pending_refund aparece em pagamentos por cartão via Stripe: o reembolso conclui-se quando o gateway confirmar, e recebes o evento order.refunded nessa altura.
  • Se as comissões dos parceiros já tiverem sido transferidas, o reembolso automático é recusado com 409 transfers_already_paid — o dinheiro já saiu para terceiros e a reversão exige tratamento manual.
  • As comissões ainda não transferidas passam a anuladas, mantendo o histórico.
curl
curl -X POST 'https://komm.pt/api/v1/checkout-sessions/{id}/refund' \
  -H 'Authorization: Bearer A_TUA_CHAVE' \
  -H 'Idempotency-Key: $(uuidgen)'

Endpoints

Webhooks

Em vez de perguntares repetidamente se o pedido já foi pago, a komm avisa-te. Cada entrega vai assinada para que possas confirmar que veio de nós.

Criar subscrição

POST/webhook-subscriptions
âmbito webhooks:write

Registas um URL teu e a komm avisa-o quando um pedido muda de estado.

Corpo do pedido

urlobrigatóriourlO endpoint que vai receber os eventos, por POST.
eventTypesobrigatórioarray de stringsQuais os eventos a receber. Ver a lista de eventos.
Pedido
{
  "url": "https://o-teu-sistema.pt/komm/webhook",
  "eventTypes": ["order.paid", "order.failed", "order.refunded"]
}
Resposta
{
  "id": "fadb652c-721b-4f36-8c44-2e6e2cf9eb07",
  "url": "https://o-teu-sistema.pt/komm/webhook",
  "eventTypes": ["order.paid", "order.failed", "order.refunded"],
  "status": "active",
  "secret": "whsec_dqFnr_VDk8x2Lp…"
}
  • O secret é devolvido uma única vez, nesta resposta. Guarda-o: é com ele que validas a assinatura de cada entrega.
curl
curl -X POST 'https://komm.pt/api/v1/webhook-subscriptions' \
  -H 'Authorization: Bearer A_TUA_CHAVE' \
  -H 'Content-Type: application/json' \
  -d '{ "url": "https://o-teu-sistema.pt/komm/webhook", "eventTypes": ["order.paid", "order.failed", "order.refunded"] }'

Testar a entrega

POST/webhook-subscriptions/{id}/ping
âmbito webhooks:write

Dispara uma entrega de teste imediata, sem esperares por uma venda. Útil para validares a verificação da assinatura do teu lado.

Resposta
{ "delivered": true }
curl
curl -X POST 'https://komm.pt/api/v1/webhook-subscriptions/{id}/ping' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Ver entregas

GET/webhook-subscriptions/{id}/deliveries
âmbito webhooks:read

Histórico das últimas 50 tentativas de entrega: que evento, que código HTTP o teu endpoint devolveu, se teve sucesso e qual o erro em caso de falha.

Resposta
{
  "data": [
    {
      "id": "0b7c1a2e-3d4f-4a5b-8c6d-7e8f9a0b1c2d",
      "eventType": "order.paid",
      "attempt": 1,
      "statusCode": 200,
      "success": true,
      "error": null,
      "deliveredAt": "2026-07-28T15:02:12.310Z",
      "createdAt": "2026-07-28T15:02:12.108Z"
    }
  ]
}
curl
curl -X GET 'https://komm.pt/api/v1/webhook-subscriptions/{id}/deliveries' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Listar subscrições

GET/webhook-subscriptions
âmbito webhooks:read

As tuas subscrições ativas. Os secrets não são devolvidos.

curl
curl -X GET 'https://komm.pt/api/v1/webhook-subscriptions' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Remover subscrição

DELETE/webhook-subscriptions/{id}
âmbito webhooks:write

Apaga a subscrição. Responde 204, sem corpo.

curl
curl -X DELETE 'https://komm.pt/api/v1/webhook-subscriptions/{id}' \
  -H 'Authorization: Bearer A_TUA_CHAVE'

Webhooks

Eventos

Estes são os eventos que podes subscrever. Cada entrega é um POST ao teu URL com o corpo abaixo.

order.paidO pedido foi pago e confirmado.
order.failedA tentativa de pagamento falhou.
order.refundedO pedido foi reembolsado.
pingEntrega de teste, disparada por ti.
Corpo da entrega
{
  "id": "9f1c7b2e-4d3a-4f8b-9c1d-2e3f4a5b6c7d",
  "type": "order.paid",
  "createdAt": "2026-07-28T15:02:12.108Z",
  "data": {
    "orderId": "7d1e0a44-1c9b-4a3e-9f77-2b6c5d0e8a13",
    "checkoutSessionId": "cab7a9f5-fe35-4a91-bec8-ed4dce5d957b",
    "customerId": "b2c3d4e5-6f70-4812-9a3b-4c5d6e7f8091",
    "status": "paid",
    "total": 2998,
    "currency": "EUR",
    "paidAt": "2026-07-28T15:02:11.004Z",
    "failureReason": null
  }
}

Validar a assinatura

Cada entrega leva dois cabeçalhos: Komm-Signature e Komm-Timestamp. Calcula um HMAC-SHA256 sobre a string {timestamp}.{corpo} usando o segredo da subscrição, e compara com a assinatura recebida. O timestamp entra no cálculo para que uma entrega capturada não possa ser reenviada mais tarde por terceiros — rejeita pedidos com mais de cinco minutos.

Usa o corpo cru do pedido, exatamente como chegou. Se o teu framework fizer parse do JSON e tu voltares a serializá-lo, a assinatura deixa de bater.

Node.js
import { createHmac, timingSafeEqual } from 'node:crypto'

function verificar(corpoRaw, cabecalhos, segredo) {
  const timestamp = Number(cabecalhos['komm-timestamp'])
  const recebida = cabecalhos['komm-signature']

  // Rejeita entregas antigas (proteção contra reenvio).
  if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) return false

  const esperada =
    'sha256=' +
    createHmac('sha256', segredo)
      .update(`${timestamp}.${corpoRaw}`)
      .digest('hex')

  const a = Buffer.from(esperada)
  const b = Buffer.from(recebida)
  return a.length === b.length && timingSafeEqual(a, b)
}

Responde 2xx assim que receberes; qualquer outro código conta como falha e fica registado. Podes ver o resultado de cada tentativa em /webhook-subscriptions/{id}/deliveries ou no painel, em Conta → API. Para experimentar sem esperar por uma venda, usa o endpoint de ping.

Referência

Erros

Todos os erros têm a mesma forma. O code é um contrato estável; a message explica o que corrigir; details e fieldErrors dão o contexto quando ajuda.

Forma do erro
{
  "error": {
    "code": "product_not_found",
    "message": "productId does not match an active product of your store.",
    "details": { "productId": "3f2b1c4d-9a8e-4b7c-8d6f-1e2a3b4c5d6e" }
  }
}
HTTPCódigoQuando acontece
401unauthorizedChave ausente, malformada, revogada ou expirada.
403forbidden_scopeA chave é válida mas não tem o âmbito que o endpoint exige. details.requiredScope diz qual falta.
404not_foundO recurso não existe — ou existe e pertence a outra loja. Devolvemos 404 nos dois casos, de propósito, para não revelar a existência de dados de terceiros.
422validation_failedO corpo ou os parâmetros não passam a validação. fieldErrors diz que campo falhou e porquê.
422product_not_foundO productId não corresponde a um produto ativo da tua loja. Inclui produtos em rascunho ou arquivados.
422catalog_not_foundO catalogId não é um catálogo da tua loja.
422customer_not_foundO customerId não é um cliente da tua loja.
422model_incompatibleO modelId escolhido não serve para os produtos enviados. A resposta diz o modo resolvido e a lista de modelos compatíveis.
422model_requires_customerO modelo exige um cliente associado, mas não enviaste customerId.
422scheduling_single_itemUm checkout de agenda só pode ter um item — só esse modelo sabe pedir o horário.
400missing_idempotency_keyFalta o header Idempotency-Key num endpoint que cria ou movimenta dinheiro.
409idempotency_conflictReutilizaste um Idempotency-Key com um corpo diferente do original.
409not_refundableO pedido não está pago, ou já foi reembolsado. details.orderStatus mostra o estado atual.
409transfers_already_paidAs comissões já foram transferidas para os parceiros. O reembolso exige revisão manual.
409invalid_status_transitionA transição de estado não é possível — pausar um link não publicado, por exemplo.
429rate_limitedDemasiados pedidos num curto intervalo. details.retryAfterSeconds diz quanto esperar.
500internal_errorFalha do nosso lado. details.requestId identifica a ocorrência — envia-nos esse id.

Pronto para começar?

Cria a tua primeira chave no painel e usa o endpoint de produtos para descobrir o que já tens à venda. Daí a criar um link de checkout é uma chamada.