# Selectwin (texto completo) > A Selectwin é a infraestrutura de pagamentos resiliente para empresas que pensam no futuro: checkout, link de pagamento, antifraude, split, recorrência, mensagens ao comprador e saldo numa plataforma só, com API pública, para quem vende no Brasil. - Empresa: BR Serviços de Marketing e Administrativos LTDA, CNPJ 37.582.324/0001-95, Conceição do Almeida/BA, Brasil. - Atendimento (Seg–sex, 8h às 18h): suporte em help@selectwin.io; vendas em sales@selectwin.io. Toda empresa cliente tem um gerente de conta desde o cadastro, por WhatsApp e e-mail. - Site: https://selectwin.io · Painel do lojista: https://my.selectwin.io · Criar conta: https://my.selectwin.io/auth/signup · Status: https://status.selectwin.io - Perfis oficiais: https://www.linkedin.com/company/selectwin · https://x.com/selectwinio · https://www.instagram.com/selectwin.io · https://www.youtube.com/@selectwin · https://github.com/selectwin · https://www.reclameaqui.com.br/empresa/br-servicos-digitais/ # Produtos --- ## Analytics — cada número com o próximo passo Fonte: https://selectwin.io/products/analytics **Quanto você vendeu, onde o checkout perdeu gente, por que o cartão recusou e quem vale chamar de volta. Com o que fazer em cada caso.** O Analytics da Selectwin lê o dado na fonte: a própria venda e a sessão do checkout. Não é pixel tentando adivinhar o que aconteceu depois do clique. É a transação, com a origem gravada nela. E ele não para no gráfico: a recusa chega com o que fazer, o abandono com o motivo provável, o cliente sumido com o botão de WhatsApp. Já vem pronto na sua conta, sem instalar nada e sem pagar nada a mais. ### O que faz - **Recusa vira tarefa com valor** — Cada recusa de cartão chega classificada, com o que fazer, um texto pronto e o valor em jogo. "Está aprovando menos" vira uma lista de ações. - **Onde o comprador trava** — O mapa de fricção mostra o campo, a etapa e o erro que mais seguram quem chega ao seu checkout. Sem gravar a tela de ninguém. - **Por que desistem e o que fazer** — Os abandonos agrupados pelo motivo provável, cada grupo com a ação recomendada: oferecer ajuda, oferecer desconto ou chamar o comprador por e-mail ou WhatsApp. - **Canal que traz cliente que fica** — A origem gravada na venda e medida pela vida do cliente: gasto, recompra e quem volta em 30 dias, por origem, mídia e campanha. - **Hora de chamar de volta** — Os clientes que passaram do próprio ritmo de compra, ordenados pelo valor em risco, com o WhatsApp a um clique. - **Saúde da conta de 0 a 100** — Uma nota única com aprovação, chargeback, disputa, reembolso e crescimento em semáforo, e o que resolver primeiro. - **Chargeback na régua das bandeiras** — A taxa de chargeback e de disputa contada sobre as vendas aprovadas, como no monitoramento das bandeiras, para você agir antes do aviso. - **Relatório de vendas para o contador** — Os números do painel numa planilha em português, por dia ou por hora, com valores em reais que somam no Excel. - **O número onde você já está** — O essencial no Início e no topo de Cobranças, Assinaturas, Clientes, Sessões e Upsells. ### O problema - **O painel de anúncios diz uma coisa, o extrato diz outra, e ninguém sabe em qual número confiar.** - **A aprovação cai e você só enxerga a taxa** — Não o motivo, nem quanto dava para salvar. - **O carrinho é abandonado e você não sabe em que campo, em que etapa, nem por quê.** - **O seu melhor cliente para de comprar e você só percebe meses depois.** ### Como funciona - **Você vende** — Pelo checkout, pelo link de pagamento ou pela API. A venda e a sessão do checkout já alimentam o painel, sem script para instalar. - **A origem vem junto** — As UTMs do link ficam gravadas na venda, e o checkout registra até onde cada comprador chegou. - **Você abre e investiga** — Escolha o período, compare, recorte e desça do total ao motivo. O número é calculado na hora em que a tela abre. - **Você age** — Chame o cliente de volta pelo WhatsApp, corrija o campo que trava, ofereça Pix a quem teve o cartão recusado ou leve tudo para a planilha. ### Para quem é - **Quem investe em mídia** — Vê qual campanha traz cliente que volta, não só clique. - **Operação que perdeu aprovação** — Recebe o motivo de cada recusa e o que fazer, não só a taxa. - **Negócio de assinatura** — MRR, churn, motivos de cancelamento e coortes no mesmo lugar. - **Quem presta contas ao financeiro** — Relatório de vendas em português, que soma igual à tela. ### Preço Incluído na sua conta Selectwin. Sem ferramenta de BI à parte, sem mensalidade extra, sem custo por consulta e sem cobrança por exportação. ### Perguntas **Preciso instalar algum script?** Não. O Analytics lê a própria venda e a sessão do checkout Selectwin. Basta o seu anúncio apontar para o link com as UTMs, e a origem fica gravada na venda. **A atribuição depende de cookie de terceiro?** Não. A origem vem do link e fica gravada na própria venda, não no navegador do comprador. **Consigo levar os números para a minha planilha?** Sim. Exporte cobranças, assinaturas, clientes ou o Relatório de vendas em Excel, CSV ou JSONL, com os filtros que você está vendo na tela. **Os números batem entre as telas?** Batem, por construção. Cada palavra tem uma definição só e robô fica fora. Com o mesmo período e os mesmos filtros, o Início, o funil, a lista de carrinhos abandonados e a visão geral dão o mesmo número, e a receita do Relatório de vendas soma ao centavo com a da tela. **O Analytics grava a tela dos meus compradores?** Não. O mapa de fricção usa a medição do próprio checkout. Não há gravação de tela nem mapa de clique. **Qual a diferença para o Lince?** O Analytics mostra todos os números, na hora em que você quiser olhar. O Lince lê as suas vendas e o seu checkout todo dia, antes do expediente, e entrega uma lista curta do que resolver primeiro. --- ## Checkout — o clique que fecha a venda Fonte: https://selectwin.io/products/checkout **A página de pagamento com a sua marca: o preço travado no clique, uma cobrança por compra e a venda que continua depois do pagamento.** O Checkout é a página de pagamento da Selectwin, montada por você sem uma linha de código, vendo cada mudança ao vivo. Pix, cartão de crédito em até 21x e boleto na mesma tela, com order bump, upsell de 1 clique no cartão, pop-up de saída, carrinho abandonado com diagnóstico, portal do cliente e pixel que só conta venda aprovada. É a infraestrutura da Selectwin exatamente onde a venda se decide: no último clique. ### O que faz - **Monte sem código, veja ao vivo** — 15 seções e mais de 90 ajustes num editor que redesenha o seu checkout a cada mudança, no computador e no celular. Sem salvar para olhar, sem cobrar ninguém e sem disparar o seu pixel. - **Preço que ninguém adultera** — Total, frete, cupons, desconto e parcelas calculados pela plataforma e travados no clique de pagar. Clique duplo, F5 ou rede caindo: uma compra, uma cobrança. - **Upsell de 1 clique no cartão** — Logo depois da compra aprovada no cartão, mais uma oferta, aceita sem digitar o cartão de novo, pelo preço exato que o comprador viu. - **Pix, cartão em até 21x e boleto** — Pix com QR na tela e confirmação automática quando o dinheiro cai, cartão com a bandeira reconhecida na hora e o valor de cada parcela (até 21x, conforme a sua conta), boleto com linha e PDF. E desconto por forma de pagamento para puxar o comprador para a que você prefere. - **Dois cartões ou cartão + Pix** — O comprador divide o pedido entre dois cartões, cada um com as suas parcelas, ou paga uma parte no cartão e o resto no Pix. Os cartões ficam reservados e só são cobrados quando todas as partes passam; se um cartão for recusado, ele troca só aquele. - **NuPay, sem cartão** — O comprador paga pelo app do Nubank, à vista ou parcelado, sem digitar cartão. O NuPay é liberado conta a conta, para empresas com CNPJ: peça ao seu gerente de conta. - **Recusa vira segunda chance** — O comprador vê o motivo em português claro, tenta de novo na hora e volta direto ao passo do cartão com os dados dele já preenchidos, com o convite ao Pix. - **Carrinho abandonado com diagnóstico** — Abandono percebido em minutos, um e-mail que remonta o carrinho e, para cada sessão, o que o comprador fez, a intenção de compra e a próxima ação recomendada. - **Pixel que bate com o dinheiro** — Meta, Google Analytics 4, Google Tag Manager, TikTok e Snap. A compra só conta com pagamento aprovado, sem os juros do parcelamento; teste e prévia nunca chegam à sua conta de anúncios; e na Meta a venda do navegador e a do servidor contam uma vez. - **Portal do cliente, sem senha** — O assinante paga a fatura vencida, troca o cartão, pausa e cancela sozinho, numa página com a sua marca. - **Plano e produto avulso juntos** — Assinatura e kit numa compra só, com teste grátis, ciclo e número de cobranças ditos antes do botão. ### O problema - **O comprador chega até o cartão e desiste** — Campo demais, CPF pedido sem motivo, uma página que não parece a da sua marca. - **Clique duplo, F5 ou internet caindo no meio do pagamento viram cobrança em dobro, estorno e disputa.** - **O cartão é recusado e a venda morre ali, sem segunda chance e sem ninguém saber por quê.** - **O carrinho abandonado é só um número no relatório, e o pixel conta clique, compra de teste e venda em dobro como se fosse dinheiro.** ### Como funciona - **Crie a sua conta** — Com a conta aprovada, o Checkout já funciona com o nome e a logo da sua empresa, antes de você configurar qualquer coisa. - **Monte do seu jeito** — Marca, formas de pagamento e parcelas vendo cada mudança ao vivo; ofertas, frete e pixel no mesmo editor. - **Compartilhe** — Por um link de pagamento do Linka na bio, no WhatsApp, no e-mail ou no anúncio, no endereço da Selectwin ou no seu domínio. - **Acompanhe** — Vendas e receita por checkout e por oferta, os carrinhos abandonados por intenção de compra e o diagnóstico de cada sessão. ### Para quem é - **Infoprodutores e creators** — Order bump, upsell de 1 clique no cartão, contagem regressiva e pop-up de saída, com produto digital que não pede endereço. - **Assinaturas e clubes** — Plano e produto avulso na mesma compra, teste grátis claro na tela e um portal em que o assinante paga a fatura atrasada, troca o cartão, pausa e cancela sozinho. - **E-commerce** — As suas opções de entrega, CEP que preenche o endereço, a variante em cada linha do carrinho e cobrança à prova de clique duplo. - **Quem escala em mídia paga** — Pixel que só conta venda aprovada, compra confirmada também pelo servidor para quem consentiu, UTM gravada em cada venda e funil limpo de robôs. ### Preço O Checkout vem incluso em toda conta Selectwin: sem mensalidade e sem custo por checkout. Editor, ofertas, pixel, portal do cliente e diagnóstico de abandono entram junto, e o domínio próprio vem incluso no seu plano, até o limite dele. A venda feita no Checkout segue as mesmas condições de qualquer venda da sua conta. ### Perguntas **O comprador pode pagar com dois cartões?** Pode, se você ligar no editor do checkout. Ele escolhe quanto vai no 1º cartão (ou no cartão, no Cartão + Pix), e o resto vai no 2º cartão ou no Pix. Os cartões ficam reservados e só são cobrados quando as duas partes passam; se um cartão for recusado, ele troca só aquele. Vale para a compra avulsa, não para assinatura, com no mínimo R$ 10 em cada parte, e depois de um pedido assim não há upsell de 1 clique. **O upsell exige o cartão de novo?** Não. Depois de uma compra aprovada no cartão, o comprador aceita com um clique, no mesmo cartão, e paga exatamente o preço que viu na oferta. **Clique duplo ou internet caindo pode cobrar duas vezes?** Não. Enquanto um pagamento está em andamento, um segundo envio não vira segunda cobrança. Uma compra, uma cobrança, inclusive no upsell. **Se eu mudar o preço no catálogo, o que o comprador paga?** O preço do seu catálogo no momento do clique de pagar. A partir dali o valor fica travado até a cobrança terminar, e nada do que mudar depois altera o que já está sendo cobrado. **O checkout pode abrir no meu domínio?** Pode, mas não precisa: ele funciona na hora no endereço da Selectwin. Quer a sua marca na barra de endereço? Cadastre um subdomínio, como pay.suamarca.com.br, e o comprador paga nele. **O checkout atende comprador de fora do Brasil?** Atende. A página abre em português, inglês ou espanhol, pelo país do comprador, e quem está fora do Brasil paga produto digital no cartão, sem CPF. A moeda é sempre o real, e a entrega de produto físico é só no Brasil. **Dá para testar sem cobrar ninguém?** Sim. No ambiente de teste, a compra roda inteira sem dinheiro de verdade, e nada chega à sua conta de anúncios. --- ## Club — venda e entrega no mesmo lugar Fonte: https://selectwin.io/products/club **A venda aprovada matricula o aluno. O estorno integral tira o acesso. E o seu vídeo não vira link de grupo.** O Club é a área de membros da Selectwin, construída dentro da mesma conta em que você vende. Monte o curso, suba o vídeo e diga qual produto libera qual curso: daí em diante, cada venda vira aluno sozinha, o estorno integral tira só o que aquela venda deu, e a sua escola abre no seu domínio, com a sua marca, num site com cara de streaming. ### O que faz - **A venda vira matrícula** — A compra aprovada matricula o aluno sozinha, em qualquer meio de pagamento. Estorno integral, chargeback e cancelamento tiram o acesso daquela venda. Sem planilha, sem integração. - **Vídeo que não vira link** — Cada vídeo sai criptografado, e a chave só vai para o aluno logado, com acesso àquela aula. O link copiado não toca, e a marca d'água põe o e-mail de quem assiste na tela. - **Experiência de streaming** — Vitrine com destaque em vídeo, "Continue assistindo" e um player que retoma no segundo exato, emenda a próxima aula e ajusta a qualidade à conexão, até 1080p. - **Construtor completo de cursos** — Curso, módulo e aula em vídeo, texto, e-book ou arquivo, com liberação programada, materiais para baixar e aula de amostra para os seus alunos. - **Entrada sem senha** — O aluno digita o e-mail e recebe um link de uso único, com o nome e o logo da sua escola. - **Certificado de frente e verso** — Você desenha o modelo, com até quatro assinaturas por lado. O aluno que conclui o curso emite, imprime ou salva em PDF, com código único. - **Comunidade sob o seu controle** — Comentários na aula com resposta do instrutor, aprovação prévia quando você quiser, avaliações de quem é aluno de verdade e anotações privadas no ponto exato do vídeo. - **Com a sua marca, no seu domínio** — Nome, cor, logo, capa e um subdomínio seu, como membros.suamarca.com.br. - **Controle total na sua mão** — Bloqueie um aluno e ele cai na hora. Desligue o Club e a área sai do ar, sem apagar nada. ### O problema - **Uma ferramenta para cobrar, outra para entregar, e o dia inteiro conciliando quem pagou com quem tem acesso.** - **O aluno pediu reembolso e continua assistindo, porque ninguém lembrou de tirar o acesso dele.** - **A aula vira arquivo, cai no grupo e é revendida sem deixar rastro.** - **Aluno que esquece a senha, chamado no suporte e desistência logo no primeiro acesso.** ### Como funciona - **Crie o Club e dê a sua cara** — Nome, cor, logo, capa e o seu subdomínio, onde o site do aluno abre. O guia de montagem mostra o próximo passo. - **Monte o curso** — Módulos, aulas, vídeos, materiais, liberação e certificado. O vídeo é processado e protegido sozinho. - **Ligue o produto ao curso** — Uma regra de acesso diz o que cada compra libera e por quanto tempo. - **Ligue o Club e venda** — Cada venda aprovada vira aluno, e o estorno integral tira o acesso daquela venda. ### Para quem é - **Infoprodutores e creators** — Que hoje pagam uma plataforma de curso separada de quem cobra. - **Quem já foi pirateado** — E precisa de vídeo que não vira arquivo de grupo. - **Escolas e mentorias** — Que liberam conteúdo por etapas e entregam certificado com a própria marca. - **Clubes de assinatura** — Biblioteca de cursos que acompanha a assinatura: paga libera, pausada ou cancelada suspende. ### Preço Comece grátis: o Club já vem no seu cadastro. Quando o volume de vídeo crescer, você troca de plano pelo próprio painel, vê a prévia da mudança e acompanha armazenamento e transferência contra a franquia antes de qualquer cobrança. ### Perguntas **Preciso de outra ferramenta para entregar o curso?** Não. Venda e entrega ficam na mesma conta: a compra aprovada matricula o aluno, e o estorno integral ou o chargeback tiram o acesso daquela venda. **Meu vídeo pode ser baixado e repassado?** O vídeo não é um arquivo: sai criptografado, e a chave só vai para o aluno logado com acesso à aula. O link copiado não toca, e a marca d'água põe o e-mail de quem assiste em qualquer gravação de tela. Repassar fica caro e deixa rastro. **O aluno precisa criar senha?** Não. Ele digita o e-mail e recebe um link de uso único, com o nome e o logo da sua escola. **O site dos alunos leva a minha marca?** Leva. Ele abre num subdomínio seu, como membros.suamarca.com.br, com o nome, a cor, o logo e a capa da sua escola, e o e-mail de acesso sai com o nome e o logo dela. **Consigo liberar o conteúdo aos poucos?** Sim: na matrícula, alguns dias depois dela ou numa data fixa, por módulo e por aula. O aluno vê a aula trancada com a data em que ela abre. **E se eu precisar tirar tudo do ar?** Um interruptor desliga o Club na hora e barra até quem já estava logado. Nada é apagado: religou, tudo volta como estava. --- ## Dismart — nenhuma disputa morre esquecida Fonte: https://selectwin.io/products/dismart **Você junta a prova. Nossa equipe revisa e envia a sua defesa. E nenhuma contestação passa sem você ver.** O Dismart é a defesa de disputas da Selectwin. Quando um comprador contesta uma venda no cartão, você fica sabendo na hora, vê o prazo exato e monta a defesa numa tela guiada pelo motivo da contestação. Mandou os comprovantes? Daqui para a frente é com a gente. ### O que faz - **Aviso na hora, prazo na tela** — A contestação chega no painel e no seu e-mail no momento em que abre, com valor, motivo e a data e a hora exatas para você se defender. Se quiser, as automações do SmartOps avisam também por SMS. - **Nossa equipe envia a sua defesa** — Você anexa os comprovantes; gente da Selectwin revisa, monta um dossiê completo e organizado e envia a defesa por você. - **Checklist pelo motivo da disputa** — Para cada motivo comum, a tela diz quais comprovantes pesam mais e mostra o seu progresso, documento a documento. - **Contra o relógio** — As disputas que precisam de você no topo, da que vence primeiro para a última, com a contagem regressiva em horas. - **Até 50 provas, sem fricção** — Arraste o PDF ou a foto direto na tela, ou cole um link. Rastreio, transportadora e um relato com as suas palavras entram junto. - **Lembretes que não deixam passar** — Se nada foi enviado, o Dismart volta a cobrar até duas vezes, a última perto do fim do prazo. - **Só a venda contestada fica retida** — O valor daquela venda aparece separado no saldo, e o resto continua livre para você operar. - **Ganhou? Tudo volta** — Cada disputa decidida fica registrada como ganha ou perdida, com data. Ganhou, o valor volta, e a taxa de chargeback também, se já tinha sido cobrada. - **Defesa pela mesma API que cobra** — Envie a defesa pelo seu sistema, com SDKs prontos e webhooks de disputa e de chargeback. ### O problema - **A contestação chega num e-mail que ninguém leu, e o prazo vence sem uma única prova.** - **Ninguém sabe o que o analista quer ver** — Nota fiscal, rastreio, print da conversa? - **Portal confuso, formulário sem fim e arquivo que precisa ser hospedado em outro lugar antes de anexar.** - **O dinheiro some do saldo, e você nunca sabe se ganhou ou perdeu.** ### Como funciona - **A disputa chega, você fica sabendo** — Na hora, no painel e por e-mail, com valor, motivo e a data e a hora limite. - **Você junta a prova** — Numa tela guiada pelo motivo: arquivo ou link, até 50 documentos, e o seu relato. - **Nossa equipe assume** — Com os seus comprovantes, a Selectwin revisa, monta o dossiê e envia a sua defesa. - **O desfecho fica registrado** — Ganha ou perdida, com data. Ganhou, o valor volta, e a taxa de chargeback também, se já tinha sido cobrada. ### Para quem é - **Loja de produto físico** — A defesa é a entrega, e rastreio, transportadora e comprovante entram direto na tela. - **Infoprodutor e escola online** — O acesso ao curso só cai se a disputa virar chargeback, e volta sozinho se o chargeback for revertido a seu favor. - **Coprodução e venda com divisão** — Retenção e perda divididas com quem recebeu parte, sem você arcar sozinho. - **Quem integra pela API** — Defesa, prazo e desfecho pela mesma API que cobra, com SDKs e webhooks. ### Preço O Dismart vem em toda conta Selectwin, sem custo extra. Não há mensalidade nem taxa por defesa. Se a disputa virar chargeback, vale a taxa de chargeback do seu contrato, e ela volta para você quando a disputa é revertida a seu favor. ### Perguntas **Preciso falar com o banco ou preencher algum portal?** Não. Você anexa os comprovantes na tela do Dismart, e a nossa equipe revisa e envia a sua defesa. **E se o meu prazo acabar?** Se você mandou comprovantes, daqui para a frente é com a gente. Sem nenhum comprovante não há defesa para enviar, e é por isso que o Dismart avisa na abertura e lembra até duas vezes. **O que acontece com o meu dinheiro durante a disputa?** Só o valor daquela venda fica retido, separado no saldo como valor retido por disputa. O resto continua livre. Ganhou, o valor volta. **Vocês garantem que eu ganho?** Ninguém sério garante: quem decide é o banco. O que o Dismart faz é avisar na hora, guiar você pelo motivo da contestação e transformar a sua prova num dossiê completo e organizado, que a nossa equipe revisa e envia. **Dá para defender pela API?** Sim, com a mesma chave que cria a cobrança, com os anexos por link. Os SDKs de Node, Python e PHP já trazem o método pronto. **Vale para quais vendas?** Para as contestações de vendas no cartão. --- ## Finance — o seu caixa com data marcada Fonte: https://selectwin.io/products/finance **O que dá para sacar agora, o que libera depois e em que dia, com saque automático todo dia. E uma barreira em cada passo do golpe da conta trocada.** O Finance é a conta da sua empresa dentro da Selectwin: onde cada venda entra, quando ela vira dinheiro livre e como sai para o seu banco. Você vê quanto pode sacar agora, quando o resto libera e por quê, e tira o dinheiro quando quiser ou no automático, todo dia. Tudo para a parte que mais importa: o dinheiro chegar à sua conta, e só à sua. ### O que faz - **Saldo claro** — O que você pode sacar agora e o que ainda vai liberar, cada um no seu número, com a data de cada liberação. Você sabe exatamente o que já é seu hoje. - **Cada venda com data para cair** — Toda venda aprovada vira um recebível com o dia em que o dinheiro fica livre. Você enxerga o calendário do caixa, e o valor libera já na virada do dia. - **Saque automático todo dia** — Escolha a conta e o percentual do disponível, e o saque sai sozinho, só depois que o dinheiro do dia foi liberado. Nunca sobre um saldo pela metade. - **Extrato que fala português** — Cada entrada e saída com nome, valor com sinal e o saldo logo depois. Da linha, um clique abre a venda, o recebível, o saque ou a fatura que a gerou. - **Senha roubada não saca** — No painel, sacar, cadastrar conta e criar ou alterar o saque automático pedem um fator forte: autenticador, código de recuperação ou passkey. Conta nova, por qualquer caminho, passa por uma janela de segurança, e o dono é avisado na hora. - **Saque à prova de erro** — Duplo clique ou queda de rede não geram dois saques. O valor sai do saldo no pedido e, se o banco recusar, volta sozinho, com a taxa. - **Sua taxa e seu prazo no painel** — A taxa de cada bandeira em cada parcela, até 21x, o prazo de cada meio de pagamento e a taxa e o mínimo de saque da sua conta. - **Disputa e dívida às claras** — O valor em disputa fica separado e volta inteiro se você ganhar. Saldo negativo avisa e se abate sozinho com as próximas vendas. - **O financeiro no seu sistema** — Saldo, extrato, recebíveis, contas, saques e simuladores pela API, com SDKs, webhooks e um ambiente de teste com o mesmo contrato da produção. ### O problema - **Um saldo só, somando o que já é seu com o que ainda vai liberar, e você decide no chute.** - **Ninguém sabe dizer em que dia cada venda vira dinheiro na conta.** - **Taxa, estorno e chargeback aparecem sem explicação, e o financeiro remonta o extrato na planilha.** - **Uma senha roubada basta para cadastrar outra conta de saque e levar o seu dinheiro.** ### Como funciona - **Cadastre a conta da empresa** — Banco, agência e conta no nome da sua empresa, com o autenticador ativado para mover dinheiro. - **Venda** — Cada venda aprovada vira um recebível com data, e você acompanha o calendário no painel. - **O dinheiro libera sozinho** — Na data de liberação, o valor fica disponível para saque, já na virada do dia. - **Saque do seu jeito** — Quando quiser, pelo painel ou pela API, ou no automático, com um percentual todo dia. ### Para quem é - **Quem planeja o caixa** — Saber o que entra na semana muda a decisão de compra, de estoque e de mídia. - **Times financeiros** — Extrato com o saldo depois de cada linha e a origem a um clique: menos planilha, menos chamado. - **Operações com split e marketplace** — Cada parceiro tem o próprio recebível, no próprio prazo e no próprio saldo. - **Quem integra pelo próprio sistema** — API, SDKs, webhooks e ambiente de teste para automatizar conciliação e saque. ### Preço Incluído na sua conta. Saldo, extrato, recebíveis, contas bancárias e as regras de saque automático não têm cobrança própria. Cada saque, manual ou automático, paga a taxa de saque da sua conta, quando ela existe: um valor fixo, à vista em Taxas e limites, no painel. ### Perguntas **O que dá para sacar agora?** O saldo disponível: as vendas que já chegaram à data de liberação. O resto é venda aprovada esperando a data dela, que aparece no recebível de cada venda. **Quando o dinheiro de uma venda fica disponível?** Na data de liberação que aparece no recebível da venda, já na virada do dia. O prazo da sua conta para cada meio de pagamento está em Taxas e limites, no painel. **Cadastrei uma conta bancária nova. Posso sacar na hora?** Conta nova passa por uma janela de segurança antes de receber o primeiro saque, e o dono é avisado no momento do cadastro. É isso que impede que uma conta invadida vire saque imediato. **E se o banco recusar o saque?** O valor e a taxa voltam sozinhos para o seu saldo, uma vez só, com linha própria no extrato. **Um chargeback pode deixar o meu saldo negativo?** Pode, se o dinheiro já tiver sido sacado. Você é avisado, e as próximas vendas liberadas abatem o valor sozinhas. Pela API, dá para quitar na hora com um Pix. **Quem da minha equipe pode sacar?** Proprietário, Administrador, Gerente e Financeiro. Operador e Visualizador acompanham saldo, recebíveis e saques, mas não sacam. --- ## Lince — todo dia, o que resolver primeiro Fonte: https://selectwin.io/products/lince **Todo dia às 6h, a lista do que pesa na sua empresa, com quanto está em jogo em cada ponto, os números e o que fazer. E a queda do meio do dia vira alerta no mesmo dia.** O Lince é o olho de lince da Selectwin sobre o seu negócio. Ele lê o que já passa pela plataforma (a venda, a sessão do checkout, o cupom, o order bump, a assinatura) e enxerga o que nenhuma ferramenta de fora enxerga: onde o comprador trava, qual canal traz o cliente que volta, se o seu Pix pago está abaixo do das outras empresas. Não usa IA generativa: é regra e estatística, com a conta escrita. Todo cadastro já começa com o Lince. ### O que faz - **A lista do dia, pronta às 6h** — Todo dia, os pontos que pesam na sua empresa, numa fila "Por onde começar": cada um com a área, quanto está em jogo, a frase pronta, os números e o próximo passo. - **A dica de dado** — As vendas de anúncio que chegam sem UTM, por rede, com o passo a passo para corrigir. Semanas depois, o Lince diz se melhorou. - **Pergunte ao Lince** — Uma conversa em qualquer tela do painel: o que ele encontrou, por onde começar, quanto está em jogo e como foram as vendas do dia, da semana ou do mês. Ele entende a pergunta por palavras-chave e responde com os seus dados, sem IA generativa; quando não sabe, diz que não sabe. - **Alerta no mesmo dia da queda** — Vendas pagas, aprovação no cartão, Pix pago e conversão do checkout vigiados de hora em hora, contra o mesmo horário das semanas anteriores. O alerta urgente chega por e-mail e no sininho do painel. - **Você contra o mercado** — O seu Pix não pago e a sua recusa no cartão comparados, de forma anônima, com as outras empresas da plataforma e, quando há empresas suficientes, só com as de tíquete parecido. - **Recusa com motivo e saída** — As compras recusadas no cartão (compras, não tentativas), com o motivo que mais pesou e a ação para cada um, inclusive o passo a passo para oferecer Pix a quem teve o cartão recusado. - **Chargeback antes da linha das bandeiras** — O aviso chega antes de os chargebacks levarem a sua empresa ao programa de monitoramento das bandeiras. - **O cupom que virou o caminho padrão** — O código que virou o caminho padrão de compra, em quantas vendas ele esteve e quanto custou. Com um clique e a sua confirmação, o Lince o desativa. - **Receita em risco** — Clientes recorrentes que passaram do próprio ritmo de compra e assinaturas que caem por falta de pagamento, com o valor em jogo. Para os clientes atrasados, o Lince cria num clique um cupom de retorno que só eles podem usar. ### O problema - **Você descobre que o checkout caiu ou que o Pix parou de ser pago quando o dia já acabou.** - **O relatório mostra o gráfico e deixa a pergunta mais cara com você: e agora, o que eu faço?** - **Você não sabe se o seu Pix não pago e a sua recusa no cartão são um problema seu ou do mercado.** - **Um cupom virou o caminho padrão de compra, o campo de CPF trava o checkout, o order bump ninguém leva, e ninguém te avisa.** ### Como funciona - **Você vende** — Pelo checkout, pelo link de pagamento ou pela API. A venda, a sessão do checkout, o cupom, o order bump e a assinatura já alimentam o Lince, sem nada para instalar. - **O Lince lê** — Todo dia às 6h, sobre os últimos 28 dias, e de hora em hora, atrás de quedas bruscas. - **Você recebe a lista** — Os pontos do dia, cada um com quanto está em jogo, a frase, os números e o próximo passo. O alerta urgente chega por e-mail e no sininho do painel, e toda segunda, quando há novidade, o resumo da semana vai para o seu e-mail. - **Você resolve** — Segue o atalho ou o passo a passo e marca "Já resolvi": o ponto sai da lista. Na maior parte dos pontos, semanas depois o Lince mede de novo e diz se melhorou; no cupom, ele faz por você, depois da sua confirmação. ### Para quem é - **Quem investe em mídia** — Vê quantas vendas de anúncio chegam sem UTM, qual anúncio vende cada produto e qual canal traz cliente que volta. - **Operação com muito Pix e cartão** — Vê o Pix não pago e a recusa contra o mercado parecido e fica sabendo da queda no mesmo dia. - **Negócio de recompra e assinatura** — Enxerga o cliente que passou do ritmo e a assinatura que caiu por falta de pagamento, enquanto ainda dá tempo de agir. - **Quem não tem analista de dados** — A análise chega pronta, em português, com a conta escrita e o próximo passo. ### Preço O Lince vem em toda conta Selectwin. Ele analisa as vendas e as sessões do checkout, e quanto ele analisa por mês e o detalhe de cada ponto acompanham o plano da conta: ponto acima do plano aparece com o tamanho, sem os números nem as ações. Robô e tráfego automatizado não contam, e o consumo do mês fica à vista no painel. Para analisar mais, fale com um especialista. ### Perguntas **Preciso instalar alguma coisa?** Não. O Lince lê as vendas e as sessões do checkout Selectwin. Sem pixel, sem tag, sem integração. **De onde vêm os números?** De regra e estatística escritas, sem IA generativa. Cada ponto mostra a confiança, a amostra, o período e como o Lince chegou ao número. Amostra pequena não vira conclusão. **Dá para conversar com o Lince?** Dá. O botão "Perguntar ao Lince" fica em qualquer tela do painel. Ele entende a pergunta por palavras-chave e responde com o que encontrou na sua empresa e com as suas vendas: o que resolver primeiro, quanto está em jogo, como foram as vendas. Não é IA generativa: quando não sabe, diz que não sabe. **Minha empresa aparece para as outras?** Não. A comparação usa a média agrupada de outras empresas. A sua nunca entra na própria média, nenhuma empresa fica identificável, e com poucas empresas não há comparação. **E se eu não estiver olhando o painel?** O alerta urgente chega assim que nasce, para o dono e os administradores, por e-mail e no sininho do painel. E toda segunda às 8h, quando há novidade, o resumo da semana chega no seu e-mail. Se quiser silêncio, dá para calar só os avisos do Lince nas preferências de notificação, sem perder os de venda. **Qual a diferença para o Analytics?** O Analytics mostra os números do negócio, para quando você quiser olhar. O Lince lê esses números todo dia e entrega a lista curta do que resolver primeiro, com o atalho para o detalhe. **Venda de teste entra na conta?** Não. O Lince analisa só as vendas reais. O ambiente de testes fica de fora. --- ## Linka — venda por link, sem site Fonte: https://selectwin.io/products/linka **Um link pronto para qualquer conversa. Mude a oferta quando quiser: o endereço continua o mesmo.** O Linka transforma o seu catálogo em links de pagamento prontos para o WhatsApp, a bio, o e-mail e o anúncio. Quem clica cai direto no checkout completo da Selectwin, com Pix, cartão, boleto e dois cartões, order bump, upsell e pixel, no nosso endereço ou no seu domínio. Toda a infraestrutura de pagamentos da Selectwin, num endereço curto. ### O que faz - **Link pronto, sem código** — Escolha os produtos do seu catálogo e receba um endereço curto, pronto para colar. Kit, combo, produto físico, digital ou assinatura, tudo a partir do mesmo catálogo. - **Mude a oferta, não o link** — Edite itens, cupons, checkout, datas e até o domínio, pause e religue com uma chave. O link que já circula continua valendo, e quem abre depois já vê a oferta nova. - **Sua marca na barra de endereço** — O link funciona na hora no endereço da Selectwin. Quer a sua marca? Escolha o seu domínio link a link: dois registros de DNS, e a verificação, a ativação e o HTTPS são automáticos. - **O link diz o quê, nunca quanto** — O endereço leva os produtos, não o valor: o preço é sempre o do seu catálogo, e mexer na URL não muda o que se cobra. - **Cada link com os seus números** — Sessões, vendas pagas e receita dos últimos 30 dias no card do próprio link. Você sabe exatamente qual link vende. - **Kit fechado de verdade** — Com o carrinho travado, o comprador não troca item nem quantidade, e a trava é garantida pela plataforma. O order bump e o upsell continuam vendendo. - **O link carrega a campanha** — Cupom já aplicado, Pix pré-selecionado, parcelas sugeridas, idioma, dados do cliente e UTMs no próprio link. Os dados pessoais saem da barra de endereço assim que a página abre. - **Teste sem cobrar ninguém** — Link de teste com selo próprio: nunca vira cobrança de verdade e não dispara o seu pixel real. - **Programável de ponta a ponta** — API pública, criação sem duplicar, SDKs oficiais em Node.js, Python e PHP e o nó do n8n. ### O problema - **Você precisa cobrar hoje, mas não tem site, nem desenvolvedor, nem tempo para integrar nada.** - **O preço mudou, o cupom venceu, a campanha acabou, e o link que já está no grupo, na bio e no anúncio vira link quebrado ou cobra errado.** - **Na hora de digitar o cartão, o cliente vê um endereço que não reconhece e desiste.** - **Você manda dezenas de links por semana e não sabe qual deles vende de verdade.** ### Como funciona - **Crie a sua conta** — Cadastre a sua empresa. Com a conta aprovada, o painel libera os links. - **Monte a oferta** — Escolha os produtos do catálogo, os cupons, o checkout e, se quiser, o seu domínio e as datas. - **Cole onde o cliente está** — WhatsApp, bio, e-mail ou anúncio. Ele abre direto no checkout e paga com Pix, cartão, boleto ou dois cartões. - **Acompanhe e ajuste** — Veja quanto cada link vende e mude a oferta quando quiser, no mesmo endereço. ### Para quem é - **Quem vende no WhatsApp, no direct e na bio** — Cobra hoje, sem site e sem integração. - **Quem vive de lançamento e tráfego pago** — Link com hora para abrir e fechar, cupom embutido, UTMs gravadas e um pixel por campanha. - **Times comerciais** — O vendedor manda o link com os dados do cliente já preenchidos, e cada papel da equipe só faz o que pode. - **Quem integra pela API** — Sistemas, CRMs e automações criam links sozinhos, sem duplicar, e recebem cada venda por webhook. ### Preço O Linka vem em toda conta Selectwin, sem mensalidade e sem custo por link. A venda feita pelo link segue as mesmas condições de qualquer venda da sua conta, e o domínio próprio vem incluso no seu plano, até o limite dele. ### Perguntas **Preciso ter site?** Não. Você monta o link no painel, a partir do seu catálogo, e cola onde o cliente está. O link abre direto no checkout. **Mudei a oferta. Preciso mandar um link novo?** Não. Edite itens, cupons, checkout, datas e até o domínio, ou pause e religue: o link que você já mandou continua valendo, e quem abre depois já vê a oferta nova. **Se eu mudar o preço no catálogo, o link acompanha?** Sim. O link guarda os produtos, nunca o valor: o preço cobrado é sempre o do seu catálogo. **Dá para usar o meu domínio no link?** Dá, e não é obrigatório: o link funciona na hora no endereço da Selectwin. Para a sua marca na barra de endereço, cadastre um subdomínio, publique os dois registros que o painel mostra, e a ativação e o HTTPS são automáticos. Você escolhe o domínio link a link. **Dá para testar sem cobrar ninguém?** Sim. No ambiente de teste do painel, o link ganha o selo Sandbox, nunca vira cobrança de verdade e não dispara o seu pixel real. **Qual a diferença para o Checkout?** O Linka é a porta de entrada: um endereço pronto para compartilhar. Por trás dele está o Checkout completo da Selectwin, com a sua marca, as suas ofertas e o seu pixel. --- ## Marketplace — vire a plataforma dos seus vendedores Fonte: https://selectwin.io/products/marketplace **Cada vendedor com a própria conta. Cada cobrança em nome dele com a sua comissão. E o dinheiro dele nunca passa pelo seu caixa.** O Marketplace transforma a sua conta Selectwin numa plataforma de verdade. Você abre uma conta para cada vendedor, ele faz o próprio cadastro por um link seguro, a Selectwin analisa e aprova, e você cobra em nome dele: cobrança avulsa, checkout ou assinatura. A venda nasce na conta do vendedor, e a sua comissão cai sozinha no seu saldo. A função é liberada depois de uma análise da sua conta: fale com a gente para começar. ### O que faz - **Uma conta para cada vendedor** — Cada vendedor ganha conta própria na Selectwin, com cadastro, saldo, recebíveis, saque e conta bancária em nome dele. E você entra na conta dele com um clique. - **Comissão que cai sozinha** — Em cada venda feita em nome do vendedor, a sua comissão é separada na mesma operação e entra no seu saldo. Em cada ciclo de assinatura, também. - **Cadastro do vendedor por link** — Um link seguro, sem login e de uso único: o vendedor informa o CNPJ, confere os dados que o cadastro traz, diz onde quer receber e envia para a análise. Enviado o cadastro, aquele link deixa de valer. - **A Selectwin credencia cada vendedor** — A gente analisa e aprova cada subconta antes da primeira venda. Você não precisa montar time de verificação de identidade. - **Planos de comissão por método** — Pix, boleto e crédito, em percentual ou valor fixo, com comissão própria para cada número de parcelas no crédito. Monte "Padrão", "Parceiro" e "Premium" uma vez e atribua a quem quiser. - **Você decide o prazo e ganha no saque** — O plano diz em quantos dias o dinheiro de cada método fica livre para o vendedor e qual a tarifa de saque dele, que cai no seu saldo na hora do saque. - **Cobre em nome do vendedor** — Cobrança avulsa, checkout e assinatura pela API, com a venda nascendo na conta dele. No painel, Pix e boleto direto da ficha do vendedor. - **Um webhook para a rede inteira** — Os eventos de todos os vendedores chegam no seu endpoint, cada um dizendo de qual conta veio, junto com os avisos de vendedor criado, aprovado, recusado e bloqueado. - **O console da sua rede** — Quem já vende, quem espera o cadastro, quem está em análise e quem travou, com o selo "Ação necessária" para nenhum vendedor ficar parado. ### O problema - **Receber o dinheiro de todo mundo na sua conta e repassar na mão: risco fiscal, conciliação sem fim e Pix um por um.** - **Cadastrar vendedor sem verificar quem ele é deixa a sua plataforma exposta a fraude.** - **Cada vendedor com uma comissão diferente, controlada numa planilha em que ninguém confia.** - **Vendedor travado no cadastro, e você sem saber se o problema é dele ou de quem analisa.** ### Como funciona - **A Selectwin libera a sua plataforma** — Fale com a gente: analisamos a sua conta e ligamos a função. - **Crie o vendedor e a comissão** — A subconta nasce com o nome dele e a taxa simples ou o plano que você escolher. - **Mande o link de cadastro** — O vendedor preenche o cadastro no navegador, sem login, e a Selectwin analisa e aprova. - **Cobre em nome dele** — A venda nasce na conta do vendedor, e a sua comissão cai no seu saldo. ### Para quem é - **Marketplaces com vendedores independentes** — Cada vendedor com conta, saldo e saque próprios, na mesma vitrine. - **Plataformas verticais** — Agendamento, delivery, educação: cobre o cliente final do seu cliente e fique com a sua margem. - **Franquias e redes** — Uma conta por unidade, com condição comercial própria e um webhook só para a rede inteira. - **ERPs e agregadores** — Ofereça pagamento dentro do seu produto, com a cobrança em nome do cliente pela API. ### Preço Incluído na sua conta, sem mensalidade. A função de plataforma não tem taxa própria: a margem sobre cada vendedor é você quem define, e a comissão cai no seu saldo. A função é liberada depois de análise da sua conta. ### Perguntas **Qual a diferença para o Repart?** O Repart divide uma venda que é sua entre parceiros. No Marketplace, a venda é do vendedor: conta própria, cadastro próprio, saldo próprio. E os dois se somam: as suas subcontas entram nas regras do Repart sem pedir autorização. **Preciso montar um time para verificar os vendedores?** Não. O vendedor faz o próprio cadastro pelo link, e a Selectwin analisa e aprova cada um. A subconta só vende depois de aprovada. **Como eu ganho com cada vendedor?** Com a comissão de cada venda feita em nome dele, separada sozinha, inclusive em cada ciclo de assinatura: o seu custo de processamento mais a margem que você define, por taxa simples ou por plano. E com a tarifa de saque do plano, que cai no seu saldo a cada saque. **O vendedor vê o próprio dinheiro?** Sim, se você quiser. Depois da aprovação, convide o vendedor para a conta dele, e ele acompanha saldo, extrato e saques sem abrir chamado com você. **Funciona com a minha integração?** Sim. Pela API você cobra em nome de cada vendedor, em cobrança avulsa, checkout e assinatura, e recebe os eventos de todos num webhook só. A criação dos vendedores e dos planos fica no painel. **Qualquer conta pode virar plataforma?** A função é liberada pela Selectwin depois de análise da sua conta. Abrir contas para terceiros pede isso, e é o que mantém a rede segura para você e para os seus vendedores. --- ## Nó Selectwin para n8n — pagamentos no piloto automático Fonte: https://selectwin.io/products/n8n **Cada venda, recusa e chargeback dispara o seu workflow. Cada cobrança, estorno e link de pagamento vira um bloco. Sem escrever uma integração.** O n8n é uma ferramenta de automação de código aberto; o nó oficial da Selectwin põe os seus pagamentos dentro dos workflows dele. Um nó opera a sua conta: cobra, estorna, cria links e pausa assinaturas. Um gatilho dispara a automação assim que algo acontece, e confere cada entrega antes de rodar. Open source, seguro de fábrica e ligado a tudo o que o seu n8n já conversa: CRM, planilha, help desk, e-mail. ### O que faz - **Mais de 50 eventos no gatilho** — Venda aprovada ou recusada, estorno, chargeback, disputa, assinatura em atraso, pausada ou cancelada, cartão vencido, carrinho abandonado, compra concluída no checkout, recebível, saque. Escolha quantos quiser num mesmo gatilho, e o workflow dispara assim que acontece. - **Pix, boleto e cartão num bloco** — Gere a cobrança a partir de qualquer gatilho: formulário, CRM, planilha, mensagem. Pix com o copia e cola entregue no evento da cobrança, boleto com vencimento de 1 a 365 dias e o cartão salvo do seu cliente em até 21 parcelas, sem ele digitar nada. - **Opera a sua conta, não só lê** — Estorne total ou parcialmente, pause e retome assinaturas, crie, desligue e renomeie links de pagamento. A automação resolve o problema, em vez de só avisar alguém para resolver no painel. - **Assinatura conferida em cada entrega** — O gatilho confere a assinatura de cada entrega antes de rodar o workflow, com uma janela de tempo contra reenvio que você ajusta. Entrega sem assinatura válida é recusada, e o workflow nem começa. - **Reentrega se o seu n8n cair** — A Selectwin tenta de novo sozinha, com intervalos crescentes, por até cerca de 3 dias. E cada entrega fica à vista na Workbench, com reenvio a um clique. - **Webhook que se instala sozinho** — Ativou o workflow, o endpoint nasce na Selectwin com os eventos que você escolheu. Desativou, ele some. Nada de painel para abrir, URL para colar ou segredo para copiar. - **Teste com o mesmo workflow** — Monte e teste com a chave de teste, vá ao ar trocando só a chave. Venda de teste nunca dispara o workflow de produção. - **Recusa explicada em português** — Cartão recusado chega com um motivo padronizado e uma mensagem em português pronta para o comprador. O workflow manda direto por e-mail ou mensagem, sem traduzir código de banco. - **Ferramenta para o seu agente de IA** — O nó entra como ferramenta do AI Agent do n8n. O agente consulta vendas, confere o saldo e cria links de pagamento a partir de um pedido escrito em linguagem natural. ### O problema - **Venda aprovada, e alguém copiando o pedido à mão para a planilha, para o CRM e para o grupo da equipe.** - **Toda automação simples, como "aprovou, avisa o time", esperando um desenvolvedor livre.** - **Um webhook genérico montado na mão, sem conferir assinatura, deixa qualquer um forjar uma "venda aprovada" e disparar o seu fluxo.** - **O n8n fora do ar por algumas horas, e os eventos daquele intervalo perdidos para sempre.** ### Como funciona - **Crie a conta e a chave** — Abra a sua conta Selectwin, ligue a verificação em duas etapas e gere a chave de API no painel: a de teste para montar, a de produção para valer. Restrita, se o workflow só precisa de parte da conta. - **Instale o nó** — No seu n8n autogerenciado (no n8n Cloud o nó ainda não está disponível), em Community Nodes, instale n8n-nodes-selectwin e cadastre a credencial Selectwin API com a sua chave. - **Monte o workflow** — Arraste o nó Selectwin para cobrar, consultar e estornar, ou o Selectwin Trigger para reagir a eventos. Teste tudo com a chave de teste. - **Ative** — O webhook se registra sozinho, cada entrega chega conferida e a Workbench mostra tudo o que foi enviado ao seu n8n. ### Para quem é - **Operações que já vivem no n8n** — Pagamento vira mais um bloco do fluxo, ao lado do CRM, da planilha e do help desk. - **Times enxutos** — Pós-venda, cobrança e financeiro automatizados arrastando blocos, sem esperar uma integração sob medida. - **Marketplaces e plataformas** — Os eventos de todas as subcontas chegam num gatilho só, cada um com a sua origem. - **Quem constrói agentes de IA** — O agente do n8n ganha o poder de consultar vendas, conferir o saldo e criar links de pagamento. ### Preço O nó é grátis e open source. Instalar e usar não custa nada à parte: a API, o gatilho e a Workbench vêm com a sua conta, sem mensalidade. Você paga as taxas da sua conta, à vista no painel. ### Perguntas **Preciso de n8n pago?** Não. O nó roda no n8n autogerenciado, com nós da comunidade habilitados. No n8n Cloud ele ainda não está disponível. **O que o meu n8n precisa para receber eventos?** Um endereço https público, que a internet alcance. Se o endereço não for https, o nó avisa na hora de ativar e diz o que configurar. **Como sei que o evento veio mesmo da Selectwin?** O gatilho confere a assinatura de cada entrega antes de rodar o workflow, com janela de tempo contra reenvio. O que não bate é recusado, e o workflow nem começa. **E se a operação que eu preciso não tiver um bloco pronto?** A mesma credencial funciona no nó HTTP Request do n8n. Você chama o resto da API pública da Selectwin, dentro do que a sua chave permite. **Dá para testar sem mexer em produção?** Sim. Com a chave de teste, o workflow cobra no ambiente de teste, e as vendas de teste só disparam o gatilho de teste. Para ir ao ar, troque a chave. **E se o meu n8n cair?** A Selectwin tenta de novo sozinha, com intervalos crescentes, por até cerca de 3 dias. Na Workbench você vê cada entrega e reenvia o que falhou. --- ## Repart — a venda que se divide sozinha Fonte: https://selectwin.io/products/repart **Escreva a regra uma vez. Cada venda chega dividida, e cada parceiro recebe a parte dele na própria conta.** Coprodutor, professor, agência, fornecedor: você diz quem recebe quanto, e o Repart divide cada venda no checkout, no link de pagamento, no order bump, no upsell e em cada ciclo de assinatura. Só entra quem aceitou, cada um recebe a própria parte e, se a venda volta, cada um devolve a sua. Split automático na venda, combinado com o parceiro e dividido até no estorno. ### O que faz - **Regra uma vez, divisão sempre** — Escolha o produto, a variante ou o catálogo inteiro, diga quem recebe quanto, e toda venda dele já chega dividida. - **Percentual ou valor fixo** — Até 20 parceiros por regra, cada um com a parte dele: uma porcentagem para o professor e um valor fixo para a agência, na mesma regra. - **Só recebe quem aceitou** — Nenhuma empresa de fora entra numa divisão sem autorizar, e a autorização é conferida de novo a cada venda. A parceria acabou? Na próxima venda, a parte fica com você. - **O funil inteiro e a recorrência** — Checkout, link de pagamento, order bump, upsell de um clique e cada ciclo de assinatura, inclusive a fatura atrasada que o assinante paga depois. - **Estorno e chargeback divididos** — Cada parte devolve a sua fatia, na mesma proporção, inclusive no estorno parcial. Ganhou a disputa? Todo mundo recebe de volta. - **Simulador antes de valer** — Monte uma venda de exemplo e veja quanto vai para cada um antes de salvar, com aviso de quem ficaria de fora. - **Recebível próprio para cada parceiro** — Cada um recebe na própria conta, no próprio prazo, com os próprios avisos. Sem Pix manual, sem conciliação. - **Proteção contra desvio** — Ligar uma divisão pede confirmação extra, e os donos são avisados sempre que uma regra passa a mandar dinheiro para alguém. Desligar é um clique. - **Nunca paga mais do que entrou** — Uma regra mal somada jamais tira do seu caixa o que a venda não trouxe, e a venda não trava por isso. ### O problema - **Planilha no fim do mês para descobrir quanto cada parceiro tem a receber.** - **Pix manual, um por um, com risco de mandar a mais, a menos ou para a conta errada.** - **A venda vira chargeback, e você paga sozinho a comissão que o parceiro já recebeu.** - **Uma regra mal somada ou uma senha roubada desviam receita sem ninguém perceber.** ### Como funciona - **Ligue o Repart** — Ele nasce desligado. Um interruptor liga a divisão para a sua conta. - **Traga os parceiros** — Peça autorização a quem já tem conta na Selectwin, ou use as suas subcontas do Marketplace. - **Crie e simule a regra** — Produto, quem recebe e quanto. Confira o resultado numa venda de exemplo e salve com confirmação extra. - **Venda** — Cada venda chega dividida, e cada parceiro recebe a própria parte, no próprio prazo. ### Para quem é - **Infoprodutores com coprodução** — Coprodutor e professor recebem a parte deles em cada venda e em cada mensalidade. - **Agências e parceiros de canal** — Comissão recorrente, ciclo a ciclo, sem planilha nem Pix manual. - **Marketplaces com subcontas** — Divida as vendas com os seus vendedores sem pedir autorização a cada um. - **Empresas com fornecedor ou licenciador** — Repasse ou royalty de um produto, ou só de uma variante, no automático. ### Preço Incluído na sua conta, sem custo extra. Não há mensalidade nem tarifa pelo Repart, e nada é descontado da parte do parceiro: a taxa de processamento da venda é a mesma, dividida ou não. ### Perguntas **Preciso ter um marketplace para usar?** Não. O Repart divide com qualquer empresa com conta na Selectwin que tenha aceitado dividir com você. Se você tem subcontas no Marketplace, elas recebem sem precisar autorizar. **O parceiro precisa ter conta na Selectwin?** Sim, com o cadastro concluído. A parte dele cai na conta dele, no prazo dele, e ele aceita participar antes. É isso que garante que o dinheiro chega a quem deve. **E se a venda for estornada ou virar chargeback?** Cada parte devolve a sua fatia, na mesma proporção. Se você ganhar a disputa, todo mundo recebe de volta. **Funciona em assinatura?** Sim. Cada ciclo cobrado é dividido de novo, inclusive a fatura atrasada que o assinante paga depois pelo portal. **Já calculo o split no meu código. Quebra?** Não. Se a cobrança ou a assinatura já vem com o split definido, o Repart respeita exatamente o que você mandou. Os parceiros só precisam ter autorizado você antes, no painel. **E se eu errar a soma das partes?** O Repart nunca paga mais do que entrou, e a venda não trava por isso. E, antes de salvar, o simulador mostra numa venda de exemplo quanto vai para cada um. --- ## Roteo — a orquestração por trás de cada cobrança Fonte: https://selectwin.io/products/roteo **Cada cobrança passa pelo Roteo: ele leva ao processamento, confere o resultado na origem e devolve tudo no mesmo vocabulário. A instabilidade fica com a gente.** O Roteo é o orquestrador de pagamentos inteligente da Selectwin, por trás de cada venda do checkout, do link de pagamento e da API. Você pede a cobrança; ele leva ao processamento, confere o resultado na origem e devolve tudo no mesmo vocabulário: aprovada, ou recusada com o motivo em português. Somar ou trocar o processamento por trás é trabalho nosso, sem mudar a sua integração, e a orquestração já vem na sua conta, sem custo adicional. ### O que faz - **Toda venda pelo mesmo motor** — Checkout, link de pagamento e API passam pelo Roteo: cartão de crédito, Pix, boleto e NuPay com o mesmo objeto de transação e os mesmos webhooks. - **Troque o motor, não o código** — A Selectwin soma ou troca o processamento por trás, e a sua integração não muda uma linha. - **"Pago" só com confirmação** — Antes de aprovar, liberar acesso ou creditar saldo, conferimos o pagamento na origem. Pago a menos não vira pago. - **Instabilidade é problema nosso** — Falha passageira de rede ou de processamento é tratada pela Selectwin, sem risco de cobrança dupla. E falha nossa nunca aparece para o comprador como cartão recusado. - **Recusa em português claro** — Cada recusa sai de um catálogo fechado de motivos estáveis, com quem precisa agir, texto pronto para o comprador e se vale tentar de novo. - **Venda encerrada nunca é cobrada depois** — Nem se o pedido chegar atrasado ao processamento. E o saque nunca sai duas vezes. - **Pix vencido conferido antes de cancelar** — Se o comprador pagou e o aviso se perdeu, a venda é aprovada, não cancelada. - **Captura e estorno, total ou parcial** — Pré-autorize no cartão e capture só o que vai entregar; estorne cartão e Pix, inteiro ou em parte. - **Um saldo, um saque** — O dinheiro aparece num saldo só e sai num saque só, venha de onde vier. ### O problema - **A integração fica presa a um gateway, e trocar o processamento vira projeto de meses com código reescrito.** - **A rede oscila no meio da cobrança, e o comprador vê "cartão recusado" por uma falha que não foi dele.** - **A recusa chega como um código cifrado, e ninguém sabe se vale tentar de novo.** - **O aviso de pagamento se perde, o pedido é cancelado, e você ouve o temido "paguei e vocês cancelaram".** ### Como funciona - **Você integra uma vez** — Pela API do Core, uma chamada para cartão, Pix e boleto. No checkout da Selectwin, nem isso: já está tudo pronto. - **O Roteo leva ao processamento** — A cobrança é aceita na hora e segue para o processamento, sem prender o seu sistema nem o seu checkout. - **Nós conferimos na origem** — O resultado só vira "aprovado" depois de confirmado, e a instabilidade passageira fica com a gente. - **Você recebe o desfecho** — Por webhook, no painel e no checkout: aprovada, ou recusada com o motivo em português e o que fazer. ### Para quem é - **Quem integra pela API** — Um contrato, um vocabulário e nenhuma linha de código por processador. - **Quem vende no checkout da Selectwin** — Você não integra nada: o Roteo já está por trás de cada venda. - **Quem já sofreu para trocar de gateway** — Aqui, mudar o processamento por trás é trabalho nosso. - **Operações de volume** — Conferência na origem e saque que não sai duas vezes: o dinheiro bate no fim do dia. ### Preço O Roteo vem em toda conta Selectwin, sem custo adicional pela orquestração. O custo do processamento já está dentro da taxa da sua conta: não há nada a ativar nem linha extra na fatura. ### Perguntas **Preciso refazer a integração se a Selectwin mudar o processamento?** Não. A sua integração fala com a Selectwin, e o que muda por trás é com a gente. **O que acontece quando uma cobrança é recusada?** Você recebe o motivo do nosso catálogo de recusas, em português, com quem precisa agir e se vale tentar de novo. No checkout, o comprador só vê "tentar novamente" quando isso pode dar certo. **Um aviso de pagamento basta para aprovar a venda?** Não. Antes de aprovar, liberar acesso ou creditar saldo, a Selectwin confere o pagamento na origem, e pago a menos não vira pago. E se o comprador pagou o Pix e o aviso se perdeu, a venda é aprovada, não cancelada. **Preciso ativar ou pagar pelo Roteo?** Não. Ele já está por trás de toda venda da sua conta, sem custo adicional. --- ## Selectwin Core — a API que trata dinheiro como dinheiro Fonte: https://selectwin.io/products/core **A API e a conta da sua empresa: cobrança em dobro barrada pela idempotência, sandbox na mesma conta, centavo exato e dinheiro que só sai com fator forte.** O Selectwin Core é a base de tudo na Selectwin: a API financeira e a conta da sua empresa. Por ele você cobra, captura, estorna e cadastra clientes, catálogo e cupons pelo código, e acompanha cada venda também no painel. E ele já vem na sua conta, sem custo à parte. ### O que faz - **Zero cobrança em dobro** — Com a chave de idempotência, repetir a requisição nas 24 horas seguintes devolve a resposta original, sem criar outra cobrança. E o teste no sandbox nunca gasta a chave da produção. - **Sandbox na mesma conta** — Um clique troca o ambiente. A venda de teste não move dinheiro, não manda mensagem ao cliente e não suja o seu pixel. - **SDKs, linha de comando e OpenAPI** — Node.js, Python e PHP, terminal pronto para scripts e especificação OpenAPI gerada do próprio código. - **Senha roubada não move dinheiro** — Pelo painel, saque, estorno, conta de saque e split só saem com autenticador, código de recuperação ou passkey. - **A parcela que ele vê é a cobrada** — A API aplica os juros do parcelamento pelo mesmo cálculo da tabela de parcelas. Sem diferença, sem juro engolido. - **Chaves com rédea curta** — Chave restrita por recurso, gerada com a sua confirmação forte, mostrada uma vez, com validade, último uso à vista e revogação com confirmação de identidade. - **Catálogo e cupons de verdade** — Produto com variações, plano recorrente no próprio produto, preço por faixa, pacote ou uso medido pela API, e cupons com limite, validade e escopo na assinatura. ### O problema - **A rede cai, o sistema repete, e o seu cliente paga duas vezes. Vira disputa, chamado e reembolso.** - **Um teste da integração escapa para a produção e cobra um cliente de verdade.** - **A parcela que o cliente viu não é a que foi cobrada, e o juro sai do seu bolso.** - **Uma senha roubada basta para trocar a conta de saque e levar o seu dinheiro.** ### Como funciona - **Crie a conta e ative a empresa** — Cadastro guiado, com o prazo previsto da análise na tela. - **Gere a sua chave** — De teste ou de produção, restrita ao que cada sistema precisa e confirmada com o seu autenticador ou a sua passkey. - **Integre no sandbox** — Mesma conta, mesma API, nenhum centavo real. Com SDKs em Node.js, Python e PHP. - **Vire a chave e venda** — Cobre, capture e estorne pela API e acompanhe tudo por webhook e no painel. ### Para quem é - **Times de tecnologia** — Controlam o dinheiro por código, com contrato estável e erros que dizem o que fazer. - **SaaS e negócios por assinatura** — Preço por faixa, pacote e uso medido, plano no próprio produto e cupom com escopo na assinatura. - **E-commerces em volume** — Idempotência, centavo exato e estorno à prova de erro operacional. - **Plataformas e marketplaces** — Cobrança em nome da subconta, split na venda e o ciclo de cada vendedor por webhook. ### Preço A API vem em toda conta Selectwin, sem custo à parte. Você paga a taxa por venda aprovada, própria da sua conta, que aparece no painel antes da primeira venda. Venda recusada, Pix não pago e boleto não compensado não pagam taxa, e SDKs, sandbox e webhooks não têm cobrança própria. ### Perguntas **Repetir uma requisição pode cobrar duas vezes?** Não, se você mandar a chave de idempotência: nas 24 horas seguintes, a mesma chave devolve a resposta original, sem criar outra cobrança. Os SDKs de Node.js e Python já mandam a chave sozinhos. **Posso testar sem dinheiro real?** Sim. Sandbox e produção vivem na mesma conta, e a chave de teste nunca toca dinheiro real. O sandbox abre assim que a sua empresa é ativada. **O meu servidor precisa lidar com o número do cartão?** Não precisa. Com o checkout hospedado, o comprador paga na página da Selectwin. Na sua própria página, os campos do cartão ficam num quadro seguro da Selectwin, e você recebe só um token de uso único para cobrar ou salvar o cartão: o número não passa pela sua página nem pelo seu servidor. Se preferir mandar os dados do cartão pela API, você os envia uma vez e passa a guardar só o token. **Quais linguagens têm SDK oficial?** Node.js, Python e PHP, além da linha de comando para o terminal. E a API está descrita numa especificação OpenAPI 3.1, gerada do próprio código. **Quais meios de pagamento a API cobra?** Cartão de crédito, em até 21x conforme a sua conta, Pix, boleto e NuPay, este liberado conta a conta. Quem leva cada cobrança ao processamento e explica cada recusa em português é o Roteo. **Uma senha roubada põe o meu dinheiro em risco?** Pelo painel, saque, estorno, conta de saque e split só saem com autenticador, código de recuperação ou passkey. E você recebe um e-mail a cada acesso de aparelho ou país novo. **Quanto custa a API?** Nada à parte. API, SDKs, sandbox e webhooks vêm na conta; você paga a taxa por venda aprovada, própria da sua conta, que aparece no painel antes da primeira venda. --- ## SmartOps — cada momento da venda, a mensagem certa Fonte: https://selectwin.io/products/smartops **Cada momento da venda dispara o e-mail, o WhatsApp ou o SMS certo, na hora certa, sem provedor para contratar. E o lembrete não chega a quem já pagou.** O SmartOps é a central de mensagens automáticas da Selectwin. Pix gerado, pagamento recusado, carrinho abandonado, assinatura em atraso, cartão vencido: a mensagem certa sai sozinha, com o nome da sua empresa e os dados da compra. O envio é nosso: o SMS liga em um clique, o e-mail sai com o remetente e o domínio da sua empresa, e o WhatsApp sai do número da sua empresa, direto com a Meta. É mensagem que nasce dentro do pagamento. ### O que faz - **13 momentos que disparam mensagem** — Para o seu cliente: Pix ou boleto gerado, pagamento aprovado, recusado, estornado ou cancelado, carrinho abandonado, assinatura ativada, em atraso, não paga ou cancelada e cartão vencido. Para você: o aviso de chargeback e o de disputa. - **WhatsApp direto com a Meta** — Conecte o WhatsApp Business da sua empresa com o login do Facebook, sem app para criar nem token para colar: a conta e o número continuam seus. A mensagem sai pela API oficial, com o modelo que a Meta aprovou, e mostra se foi entregue e lida. - **O lembrete confere antes de sair** — Na hora de sair, o lembrete do Pix confere se o cliente já pagou, e o do carrinho confere se ele já comprou. Se sim, a mensagem não sai. No SMS, a conferência vale até para o lembrete que já esperava a vez de sair, e o SMS que não saiu não conta. - **E-mail assinado pelo seu domínio** — Os registros de DNS prontos para copiar, o botão Verificar e nenhum e-mail saindo antes de SPF e DKIM. O seu domínio fica preso à sua empresa, e cada e-mail mostra se foi entregue, aberto ou se voltou. - **SMS no padrão das operadoras** — Sem acento, até 160 caracteres, com o nome da sua empresa no começo e o conteúdo que as operadoras bloqueiam barrado na hora de salvar. Quando precisa de link, vai o link curto e seguro da Selectwin: o comprovante, a nova tentativa de pagamento e o portal do assinante. - **Sem provedor para contratar** — O envio é da Selectwin. O SMS liga em um clique, sem conta em operadora nem chave para colar. O e-mail sai com o remetente e o nome da sua empresa, sem ferramenta de e-mail para assinar. E o WhatsApp entra com um login, sem plataforma de disparo para assinar. - **O botão que resolve, já no e-mail** — O carrinho abandonado volta para a mesma compra, o pagamento recusado no checkout leva a tentar de novo e a assinatura em atraso leva ao portal do cliente, onde ele paga e troca o cartão. O Pix copia-e-cola e o boleto já vão montados. - **Cada envio acompanhado** — A situação de cada mensagem, a linha do tempo e o motivo da falha em português, com o que fazer, e o reenvio em um clique. E o seu sistema pode receber cada desfecho por aviso assinado. - **Seu canal protegido de fábrica** — Mensagem só para cliente real da sua empresa, limite por destinatário, teto no carrinho abandonado e descadastro que só vale no clique de uma pessoa. ### O problema - **O cliente gerou o Pix, se distraiu, e ninguém lembrou de pagar.** - **O lembrete chega para quem já pagou, e a sua marca passa vergonha.** - **Para mandar e-mail, WhatsApp e SMS, você precisa contratar provedor, colar chave e configurar remetente, com uma ferramenta para cada canal.** - **O SMS com link cai no filtro da operadora, e o e-mail sem autenticação acaba no spam.** ### Como funciona - **Conecte o canal** — O SMS liga em um clique, e o WhatsApp, com o login do Facebook. No e-mail, informe o remetente da sua empresa, crie os registros no DNS do seu domínio e aperte Verificar. - **Escreva as mensagens** — O editor de e-mail monta o logo, os itens e o botão; o de SMS mostra a mensagem como ela vai sair, a partir das mensagens prontas em português; no WhatsApp, você escolhe o modelo aprovado pela Meta e o dado de cada campo. - **Desenhe a automação** — Gatilho, filtro, espera e canais num fluxograma. Teste com um pedido de exemplo e receba uma cópia real antes de ligar. - **Acompanhe cada envio** — O que saiu, o que chegou e o que falhou, com o motivo em português e o reenvio a um clique. ### Para quem é - **Quem vende no Pix e no boleto** — O lembrete sai na hora certa, com o Pix copia-e-cola e o boleto no e-mail, e desiste sozinho quando o pagamento cai. - **Assinaturas e SaaS** — Assinatura em atraso, não paga e cartão vencido levam o cliente ao portal, onde ele paga e troca o cartão, pelo botão do e-mail, pelo link no WhatsApp ou pelo link curto do SMS. - **E-commerce** — O carrinho abandonado leva de volta à mesma compra, e o pagamento recusado ganha o "tente de novo". - **Quem tem mais de uma empresa** — Monta as automações uma vez e leva para a outra empresa, vendo antes o que chega. ### Preço O SmartOps já vem na sua conta Selectwin, com uma franquia mensal de SMS incluída. O envio é da Selectwin: você não contrata provedor de e-mail, de WhatsApp nem de SMS. No WhatsApp, a Meta cobra o uso direto na conta WhatsApp Business da sua empresa, pela tabela dela. O uso da franquia do mês e a cota de cada dia ficam à vista no painel, com marketing e avisos contados à parte. ### Perguntas **Preciso contratar um provedor de e-mail, de WhatsApp ou de SMS?** Não. O envio é da Selectwin. O SMS liga em um clique, e o WhatsApp conecta com o login do Facebook, na conta WhatsApp Business da sua empresa. Para o e-mail, basta um domínio da sua empresa e acesso ao DNS dele, para criar os registros e apertar Verificar. **O WhatsApp é o oficial?** É. O SmartOps fala direto com a Meta, pela API oficial, e a mensagem sai do número da sua empresa: a conta e o número continuam seus. Para quem não conversou com você nas últimas 24 horas, a Meta só entrega o modelo de mensagem que ela aprovou: você cria o modelo no WhatsApp Manager, da Meta, e escolhe no SmartOps. **Posso usar um e-mail do Gmail ou do Hotmail como remetente?** Não. O remetente precisa ser do domínio da sua empresa: é ele que assina cada e-mail com SPF e DKIM, e nenhum e-mail sai antes de o domínio ser verificado. **O cliente que já pagou recebe o lembrete?** Não. Na hora de sair, o lembrete do Pix confere a cobrança, e o do carrinho confere a compra. Se o cliente já resolveu, a mensagem não sai. **O SMS pode levar link?** Pode, o link curto e seguro da Selectwin: para o comprovante, para tentar pagar de novo depois de uma recusa no checkout e para o portal do assinante. Ele expira e só leva a páginas da Selectwin ou ao checkout da sua empresa. Link de fora não passa, porque as operadoras filtram SMS com link. **Como sei se a mensagem chegou?** Cada e-mail mostra se foi entregue, aberto ou se voltou, com recibos que se configuram sozinhos. Cada WhatsApp mostra se foi entregue e lido. Cada SMS mostra quando foi enviado. E toda falha vem explicada em português, com o que fazer. **Dá para disparar para uma lista de contatos?** Não, e é de propósito. O SmartOps manda para o cliente de cada venda, e os testes só vão para os seus contatos ou para clientes cadastrados da sua empresa. É assim que o seu domínio e o seu canal continuam de pé. **Dá para testar sem mandar mensagem para cliente?** Dá. Venda feita no ambiente de teste nunca dispara mensagem. Teste o caminho da automação com um pedido de exemplo e mande uma cópia real para você antes de ligar. --- ## Subscriptions — a recorrência que cobra sozinha Fonte: https://selectwin.io/products/subscriptions **Cadastre o plano uma vez. Cada ciclo é cobrado no dia certo, no cartão, no Pix ou no boleto, e nunca duas vezes.** Subscriptions transforma venda pontual em receita recorrente. Plano mensal, anual ou a cada N períodos, teste grátis de verdade, plano de 12 cobranças que para na 12ª, novas tentativas automáticas quando o pagamento falha e um portal onde o assinante resolve tudo sozinho, sem senha. Com o preço travado na venda, o split em cada renovação e nenhuma cobrança em dobro. ### O que faz - **Cobra sozinho, no dia certo** — Diário, semanal, mensal ou anual, a cada N períodos, e dia fixo no mês nos planos mensais e anuais. No vencimento o ciclo é cobrado e o próximo já fica agendado. Sem rotina sua, sem planilha. - **Cada ciclo, uma cobrança** — Cada ciclo é cobrado uma vez só, mesmo que o assinante pague o atraso no portal na mesma hora da cobrança automática. Pausa e atraso longo não viram uma enxurrada de cobranças. - **Recusa não vira cancelamento** — O pagamento que falha ganha até 5 tentativas automáticas, com o motivo da falha, as tentativas feitas e a próxima data na sua tela. Pagou, a assinatura volta a ficar ativa sozinha. - **Portal do assinante, sem senha** — Por um link seguro, o cliente vê o que assinou, paga a fatura atrasada no Pix, no boleto ou no cartão, troca o cartão, pausa e cancela no fim do período. Com a sua marca, em três idiomas. - **Cartão, Pix e boleto na recorrência** — Venda assinatura também para quem não tem cartão. Cada ciclo gera um Pix ou um boleto novo, com a mesma régua de cobrança do cartão. - **Teste grátis de verdade** — O assinante entra em teste sem pagar nada, e a primeira cobrança dispara sozinha no fim. Cancelou durante o teste? Não paga nada. - **Preço e cupom travados na venda** — Reajuste o catálogo sem quebrar quem já assina. O cupom de entrada vale só na primeira cobrança e não passa para a renovação; pela API, ele pode durar N ciclos ou para sempre. - **O plano de 12 cobranças para na 12ª** — O plano fechado termina sozinho na última cobrança, o teste não conta, e mudar o catálogo não mexe em quem já comprou. - **Split em cada renovação** — Coprodutor, parceiro, agência e plataforma de marketplace recebem a parte deles a cada ciclo, inclusive quando o atraso é pago depois, pelo portal. ### O problema - **O cartão recusou uma vez, ninguém tentou de novo, e o cliente foi embora sem querer sair.** - **O teste grátis esqueceu de cobrar no fim, ou cobrou no primeiro dia e virou reclamação.** - **Duas cobranças no mesmo mês** — O chamado mais caro de quem vende assinatura, e a contestação logo atrás. - **O assinante precisa abrir chamado para trocar o cartão, pagar o atraso ou cancelar, e desiste no meio do caminho.** ### Como funciona - **Cadastre o plano** — No catálogo: preço, cadência, teste grátis e número de cobranças. O preço trava no dia de cada venda. - **Venda** — No checkout, sem código, até junto de um produto avulso, ou pela API. O comprador vê na tela o que está autorizando. - **Os ciclos rodam sozinhos** — No vencimento, o cartão é cobrado ou o Pix e o boleto são gerados, já com a parte de cada parceiro. - **Falhou? A régua trabalha** — Novas tentativas, o motivo na sua tela, e o assinante paga o atraso ou troca o cartão pelo portal. ### Para quem é - **SaaS e software** — Cobre por assento, por faixa, por pacote ou por consumo, com o plano travado e o MRR na tela. - **Infoprodutores e clubes de conteúdo** — O acesso ao Club acompanha a assinatura, e o coprodutor recebe cada mensalidade. - **Clubes de assinatura e reposição no e-commerce** — Pix e boleto para quem não tem cartão, e a régua para o cartão que falhou. - **Mentorias, consultorias e planos de serviço** — O plano de 12 cobranças termina sozinho, e o cliente que precisa de um respiro pausa em vez de cancelar. ### Preço Recorrência sem mensalidade e sem taxa própria. Cada ciclo cobrado paga a mesma taxa por venda aprovada da sua conta, igual a uma venda avulsa. A régua, o portal do assinante e o painel de recorrência vêm junto. ### Perguntas **Como vocês evitam cobrar duas vezes o mesmo mês?** Cada ciclo é cobrado uma vez só, venha o pagamento da cobrança automática ou do portal, e uma aprovação repetida não vira segunda cobrança. Os meses de uma pausa nunca são cobrados. **O que acontece quando a renovação falha?** A assinatura fica em atraso e a plataforma tenta de novo, até 5 tentativas. Você vê o motivo e a próxima data, e o assinante pode quitar o atraso pelo portal sem esperar a próxima tentativa. Esgotadas as tentativas, a assinatura vira não paga e é encerrada depois de uma carência. A régua já vem pronta e ligada: você não monta nada. **O meu cliente é avisado?** Sim, se você quiser. Você liga a automação no painel e o aviso sai pelo SmartOps, por e-mail, WhatsApp ou SMS, sem você contratar provedor de envio. No SMS, as mensagens de atraso, de assinatura não paga e de cartão vencido já vêm escritas, e você pode incluir o link do portal. No e-mail, você põe o botão pronto que leva direto ao portal, para pagar ou trocar o cartão. No WhatsApp, o link do portal vai no modelo de mensagem aprovado pela Meta. **Dá para vender assinatura no Pix?** Sim. Cada ciclo gera um Pix ou um boleto novo, com o prazo padrão ou o que você definir pela API. Você recebe o código para repassar, o assinante também paga pelo portal, e a régua é a mesma do cartão. **Mudei o preço do plano. Quem já assina muda?** Não. Preço e cupom ficam travados no dia da venda. O reajuste vale para as próximas assinaturas. **O assinante consegue cancelar sozinho?** Sim, pelo portal, no fim do período já pago, dizendo o motivo se quiser. Você é avisado na hora. E pode oferecer a pausa no lugar do cancelamento. --- ## Vael — o antifraude que barra a fraude na porta Fonte: https://selectwin.io/products/vael **A fraude para na porta. A venda boa segue. E nenhuma falha do antifraude derruba a sua venda.** O Vael é o antifraude da Selectwin, embutido na própria cobrança. Toda venda avulsa — no checkout, no link de pagamento, na API, no upsell de um clique e na cobrança de marketplace — passa por ele antes de chegar ao banco. A fraude evidente morre ali, antes de virar chargeback, e você só paga a análise quando a venda no cartão de crédito é aprovada. Segurança que decide antes do banco e nunca derruba a venda boa. ### O que faz - **Checagem antes de cobrar** — Toda venda avulsa passa pelo Vael antes de chegar ao banco: checkout, link de pagamento, API, upsell de um clique e marketplace. Você não muda uma linha da integração. - **Nunca derruba a sua venda** — Se o antifraude tiver um problema, ele sai da frente e a venda segue. Bloqueio só acontece por decisão, nunca por instabilidade. - **Chargeback fecha a porta** — Quem deu chargeback na sua empresa fica bloqueado nela, automaticamente. A próxima tentativa já bate na porta fechada — no cartão, no Pix e no boleto. - **Proteção de base em toda conta** — Mesmo sem a análise completa, toda conta Selectwin já nasce protegida contra rajada de tentativas, teste de cartão e fraudador conhecido, em todos os meios. Sem custo. - **Ataque em massa cortado na origem** — Rajada de tentativas e teste de cartão em massa são detectados e barrados antes de chegarem ao banco, com uma vigilância que protege toda a rede Selectwin. - **Paga só pela venda aprovada** — A análise completa é cobrada só sobre venda aprovada no cartão de crédito. Tentativa barrada, recusa do banco, Pix e boleto não pagam. - **Custo à vista, venda a venda** — Cada cobrança da análise aparece no seu extrato ligada à venda, e sai também pela API para você conciliar no seu sistema. - **Integração que já sabe o que aconteceu** — A API responde na hora se a venda foi aceita, ficou em análise ou foi barrada, e o evento de venda em análise já vem pronto nos SDKs e como gatilho no n8n. ### O problema - **Chargeback comendo a margem e colocando a sua conta na mira das bandeiras.** - **Teste de cartão em massa inflando a sua recusa e queimando a sua reputação com os bancos.** - **Antifraude que trava no pior momento e derruba a venda boa junto com a fraude.** - **Pagar por cada consulta do antifraude, inclusive pela fraude que ele mesmo barrou.** ### Como funciona - **A venda entra** — Pelo checkout, pelo link, pela API, pelo upsell ou pelo marketplace. Você não muda nada na integração. - **O Vael analisa** — Decide antes do banco, sem atrasar a venda boa. - **Sai a decisão** — A venda boa segue para o banco; a fraude evidente é barrada na porta, antes de virar chargeback. - **O ciclo se fecha** — O chargeback vira bloqueio automático na sua empresa, e você só paga a análise da venda aprovada no cartão de crédito. ### Para quem é - **E-commerce de margem apertada** — Onde um único chargeback apaga o lucro de várias vendas. - **Produto digital de entrega imediata** — Onde a fraude leva o produto na hora e só aparece semanas depois, no chargeback. - **Plataformas e marketplaces** — A cobrança feita em nome de cada subconta também passa pelo Vael. - **Quem está começando** — A proteção de base já vem ligada em toda conta, sem custo, desde a primeira venda. ### Preço Você só paga quando a venda no cartão de crédito é aprovada. A análise completa é cobrada em percentual sobre a venda aprovada no crédito: tentativa barrada, recusa do banco, Pix e boleto não pagam. Sem mensalidade e sem taxa de adesão. A proteção de base vem em toda conta, sem custo. Fale com um especialista para ligar a análise completa na sua conta. ### Perguntas **O Vael vai aumentar a minha recusa?** Não do lado do banco: o que o Vael barra para antes de chegar ao emissor e nem conta como tentativa recusada lá. O teste de cartão barrado não queima a sua reputação, e a venda boa segue para o banco normalmente. **E se o antifraude tiver um problema na hora da venda?** A venda segue. O Vael foi desenhado para sair da frente quando tem um problema: bloqueio só acontece por decisão, nunca por instabilidade. **Eu pago pela fraude que vocês barram?** Não. A análise só é cobrada sobre venda aprovada no cartão de crédito. Tentativa barrada não paga. **Funciona no Pix e no boleto?** Sim, com a proteção de base: rajada de tentativas e quem já deu chargeback na sua empresa são barrados em todos os meios, sem custo. A análise completa é do cartão de crédito. **Preciso integrar alguma coisa?** Não. O Vael roda dentro da cobrança. Quem integra pela API recebe a resposta na hora e o evento de webhook, já pronto nos SDKs. --- ## Webhooks — sua operação avisada na hora Fonte: https://selectwin.io/products/webhooks **Venda aprovada, estorno, assinatura em atraso, saque confirmado: o seu servidor fica sabendo por um aviso assinado. Se ele cair, a Selectwin tenta de novo sozinha. Se precisar, você reenvia com um clique.** Webhooks é como a Selectwin conta ao seu sistema o que acabou de acontecer na sua conta. Você aponta uma URL, escolhe os eventos e pronto: o ERP fatura, a área de membros libera o acesso e a automação dispara, sem ninguém perguntar à API se algo mudou. Cada entrega fica à vista na Workbench, e tudo vem incluído em toda conta. ### O que faz - **Mais de 50 eventos** — Venda aprovada, recusada, estornada, em disputa e em chargeback, assinatura, recebível, saque, cliente, cartão, situação da conta, carrinho abandonado e compra concluída no checkout. Cada endpoint ouve só o que interessa a ele. - **Assinatura em toda entrega** — Cada aviso chega assinado com o segredo do seu endpoint. Confira a assinatura, e quem descobrir a sua URL não consegue forjar uma "venda aprovada". - **Tenta de novo sozinha** — Servidor fora do ar? A Selectwin tenta de novo com intervalos crescentes: até 12 tentativas por entrega, ao longo de cerca de 3 dias. Queda curta se resolve sem ninguém mexer em nada. - **O objeto da API, com a origem da venda** — O aviso traz a venda no mesmo formato da leitura da API, com a campanha de origem e os cookies de rastreio prontos para a API de conversões das suas plataformas de anúncio. - **Um id por evento** — O mesmo id em toda nova tentativa e em todo reenvio. Processe cada id uma vez, e a entrega repetida nunca vira pedido em dobro. - **Assinatura dupla na troca de segredo** — Por 24 horas, cada entrega leva duas assinaturas, em cabeçalhos separados: a do segredo novo e a do antigo. O seu servidor confere a antiga enquanto você atualiza. - **Proteção automática** — Endpoint que só falha é desligado sozinho, e a Workbench diz por quê. - **Teste e reenvio também pela API** — Mande um evento de teste pelo painel, pela API ou pela linha de comando, e reenvie um evento ou uma entrega pelo painel ou pela API, sem abrir chamado. - **Marketplace num endpoint só** — A plataforma recebe os eventos de todos os vendedores, cada um marcado com a subconta de origem. ### O problema - **Consultar a API de minuto em minuto para saber se a venda aprovou: caro, lento e ainda assim atrasado.** - **O servidor fica fora do ar por dez minutos, e o pedido daquela hora nunca é liberado.** - **Não ter como provar que a chamada recebida veio mesmo da Selectwin, e não de alguém que descobriu a sua URL.** - **Trocar o segredo de assinatura abre uma janela de entregas recusadas até o seu servidor ser atualizado.** ### Como funciona - **Cadastre a URL** — No painel ou pela API, com endereço https. Escolha os eventos, ou todos, inclusive os que vierem depois. - **Guarde o segredo** — Ele aparece uma vez só, e é com ele que cada entrega chega assinada. - **Valide e processe** — Confira a assinatura (os SDKs fazem isso em uma linha), responda 2xx em até 30 segundos e descarte repetição pelo id do evento. - **Acompanhe** — A Workbench mostra o que entrou, o que falhou e o que o seu servidor respondeu. Corrigiu? Reenvie com um clique. ### Para quem é - **Quem integra ERP, CRM ou planilha** — A venda, o estorno e o recebível chegam sozinhos ao seu sistema, sem consulta em loop. - **Quem vende curso, acesso ou assinatura** — Libera o acesso no aprovado e corta no atraso ou no cancelamento, no momento do aviso. - **Plataformas de marketplace** — Um endpoint para os eventos de todos os vendedores, inclusive a aprovação do cadastro de cada um. - **Times de automação e de tráfego pago** — O n8n dispara sem uma linha de código, e a campanha de origem chega dentro do aviso. ### Preço Incluído em toda conta Selectwin, sem custo extra. Eventos, histórico, reenvio, teste e a Workbench fazem parte da plataforma. Não são módulo pago à parte. ### Perguntas **Como sei que o aviso veio da Selectwin?** Cada entrega vem assinada com o segredo do seu endpoint, que só você recebe e mais ninguém vê depois. Os SDKs conferem em uma linha. Quer ir além? Confira também a assinatura com carimbo de tempo e recuse entregas antigas reaproveitadas. **Meu servidor caiu. Perdi os eventos?** Os eventos ficam no histórico. A Selectwin tenta de novo sozinha, até 12 vezes por entrega, ao longo de cerca de 3 dias. Se o endpoint só falha, a proteção automática o desliga e o painel mostra o motivo. Depois de corrigir, você reativa e reenvia cada evento com um clique. **O mesmo evento pode chegar duas vezes?** Pode. Por isso cada evento tem um id que não muda, nem na nova tentativa nem no reenvio. Processe cada id uma vez e pronto. **Troquei o segredo. A integração cai?** Não precisa cair. Por 24 horas, cada entrega leva duas assinaturas, a do segredo novo e a do antigo, em cabeçalhos separados. Confira a antiga enquanto atualiza o seu servidor, e nenhuma entrega é recusada na troca. **Venda de teste chega ao meu endpoint de produção?** Não. O sandbox tem endpoints próprios: venda de teste só chega ao endpoint de teste. Para conferir a ligação sem venda nenhuma, use o "Enviar teste" da Workbench. **Posso usar sem programar?** Sim. O gatilho da Selectwin para o n8n cria o endpoint, guarda o segredo e valida a assinatura sozinho. Basta um n8n próprio, com endereço https público. --- ## Workbench — sua integração sob controle total Fonte: https://selectwin.io/products/workbench **Webhooks, eventos, falhas, logs e o Inspector numa bancada dentro do painel. Você vê o que quebrou, testa e reenvia na hora, sem abrir chamado.** A Workbench é a bancada do desenvolvedor da Selectwin: seis abas numa gaveta que sobe por cima do painel, encolhe a página em vez de cobri-la e continua aberta enquanto você navega. Ela mostra qual endpoint está falhando, o que o seu servidor respondeu, quem mudou o quê na conta e o que é aquele id que apareceu no log. E ela já vem na sua conta. ### O que faz - **Abre sem cobrir o seu trabalho** — A bancada sobe da parte de baixo do painel, a página encolhe para caber e a gaveta continua aberta enquanto você navega. Abra a venda e veja o webhook dela ao mesmo tempo. - **Aponta o que precisa de atenção** — A Visão geral lista só o endpoint que está falhando ou desligado, quantas entregas esperam e quando foi a última aceita. Sem problema? "Tudo entregando". - **"Enviar teste", com a resposta na tela** — Um clique dispara um evento de teste para o endpoint e mostra na hora o código, o tempo e o corpo que o seu servidor devolveu. - **Reenvio de um clique, do jeito certo** — Se a entrega ainda tem tentativas, ela vai agora. Se esgotou, o evento é reaberto com o mesmo corpo. O botão certo aparece sozinho. - **Logs: quem mudou o quê** — Toda criação, alteração e exclusão na conta, pela API ou pelo painel, com quem fez, de qual IP, o resultado e como o recurso ficou. - **Inspector: um id basta** — Cole o id de uma cobrança, de um cliente ou de um evento e veja o objeto como a API devolve, os avisos que ele gerou e as mudanças registradas. - **A ficha de cada endpoint** — Saúde, URL para copiar, eventos ouvidos, assinatura e política de novas tentativas, com o botão que troca o segredo. - **Desligamento explicado** — Endpoint parado diz quem desligou (você, a proteção automática ou a equipe Selectwin), quando e, se houver, quantas entregas foram canceladas. - **Cada clique também na API** — Toda ação de webhook da Workbench (criar, testar, reenviar, trocar o segredo) também está na API pública: monte o seu painel, reenvie e teste a cada publicação. ### O problema - **O pedido não liberou, o cliente reclamou, e ninguém sabe se o aviso de pagamento saiu, chegou ou travou no caminho.** - **Descobrir por que um webhook falhou exige um log do lado do seu servidor que ninguém guardou.** - **Reenviar um evento perdido vira chamado de suporte e dias de espera.** - **Alguém mudou um preço, estornou uma venda ou trocou uma configuração, e não há como saber quem foi.** ### Como funciona - **Abra a bancada** — Pelo menu Workbench ou por Configurações › Desenvolvedores. Ela mostra o ambiente em que você está, sandbox ou produção. - **Cadastre o endpoint** — Cole a URL https, escolha os eventos em português e guarde o segredo. "Enviar teste" confere a ligação na hora. - **Acompanhe** — A Visão geral mostra as entregas da semana e só o que precisa de atenção. Eventos, Falhas e Logs contam o resto. - **Conserte e reenvie** — Veja o que o seu servidor respondeu, corrija, teste de novo e reenvie cada aviso que faltou com um clique. ### Para quem é - **Quem integra pela API** — Vê o que a Selectwin mandou e o que o seu servidor respondeu, e conserta sem abrir chamado. - **Times de tecnologia** — Trilha de auditoria com quem fez, de onde e como ficou, e controle por papel para abrir a bancada a todo mundo. - **Suporte e operação** — Responde "o pedido não liberou?" sem esperar o desenvolvedor. - **Quem conecta ERP, área de membros ou automações** — O seu sistema recebe os avisos da Selectwin, e a Workbench mostra se cada um chegou. ### Preço A Workbench vem em toda conta Selectwin, sem custo adicional. Webhooks, API, Logs e Inspector não têm cobrança à parte: com a conta ativa, a bancada está lá. ### Perguntas **Preciso instalar alguma coisa?** Não. A Workbench já está no painel, e funciona também no navegador do celular. **O que a bancada mostra quando um endpoint falha?** O código e a última resposta do seu servidor, na ficha do endpoint e na aba Falhas, com o botão certo para reenviar: se a entrega ainda tem tentativas, ela vai agora; se esgotou, o evento é reaberto com o mesmo corpo. Endpoint desligado diz quem desligou e quando. **O que conta como entrega aceita?** Qualquer resposta 2xx do seu servidor em até 30 segundos. Redirecionamento não é seguido: cadastre o endereço final. **Quem da minha equipe pode mexer nos webhooks?** Quem tem permissão de alterar cria, edita, testa e reenvia. Quem só lê acompanha endpoints, eventos e falhas, sem botão de alteração. É o papel de cada pessoa na sua equipe que define. **Posso testar sem mexer na produção?** Sim. Sandbox e produção têm, cada um, a sua bancada, com endpoints e chaves próprios. E "Enviar teste" mostra na hora o que o seu servidor respondeu, sem esperar uma venda de verdade. **Dá para automatizar o que faço na tela?** Sim. Toda ação de webhook da Workbench também está na API pública: criar, editar, testar, reenviar e trocar o segredo. --- # Documentação da API A API da Selectwin é REST, em JSON, em https://api.selectwin.io/v1, autenticada pelo header SelectKey: pagamentos (Pix, cartão e boleto), assinaturas, checkout, split, saldo e saques, e webhooks. --- ## Introdução à API Selectwin Fonte: https://selectwin.io/docs/reference/introducao #### Visão Geral A Selectwin API foi desenvolvida seguindo os melhores padrões REST, proporcionando uma integração simples e eficiente para qualquer aplicação. As operações são realizadas por meio de requisições aos endpoints, utilizando os verbos HTTP apropriados e corpos de mensagem no formato JSON. As respostas são facilmente interpretadas por meio dos códigos de status HTTP, garantindo uma experiência de integração clara e intuitiva, independentemente da linguagem de programação escolhida. ##### URLs Base | Ambiente | URL Base | |----------|----------| | Produção | `https://api.selectwin.io/v1` | ##### Versionamento O versionamento está incorporado na URL base (`/v1`), garantindo que mudanças futuras na API não afetem integrações existentes. Quando uma nova versão for lançada, forneceremos documentação atualizada e um período de transição adequado. --- #### Ambiente Sandbox Para facilitar o desenvolvimento e testes, a Selectwin disponibiliza um ambiente sandbox (ambiente de testes) que simula as operações do ambiente de produção, sem realizar transações reais ou modificar dados em produção. ##### Características do Sandbox - Os endpoints de operação (clientes, cartões, transações, assinaturas, produtos, webhooks…) funcionam como em produção - Os recursos de **conta** (apps e integrações, pixels, área de membros, marketplace, encerramento) são **só leitura** em sandbox: altere-os em produção. Uma escrita neles com a chave `sk_test_` responde `409 accountLevelReadOnlyInSandbox` - Simula os comportamentos de sucesso e falha das operações - Permite testar fluxos completos de transações, do início ao fim - Não realiza cobranças reais em cartões de crédito - Não afeta o ambiente de produção ##### Como Utilizar o Sandbox A principal diferença entre os ambientes de sandbox e produção está na chave de API (`SelectKey`) utilizada. Para utilizar o ambiente sandbox, basta incluir uma chave de teste no cabeçalho das requisições: ```bash curl -X POST "https://api.selectwin.io/v1/transactions" \ -H "SelectKey: sk_test_1234567890abcdefghijklmn" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "amount": 10000, // ... outros parâmetros }' ``` > **Importante**: A chave de sandbox sempre começa com o prefixo `sk_test_`, enquanto a chave de produção começa com `sk_live_`. ##### Simulação de Cenários O ambiente sandbox permite simular diversos cenários para testar a robustez da sua aplicação: - **Transações Aprovadas**: Para testar fluxos de sucesso - **Transações Recusadas**: Para testar tratamento de erros - **Estornos e Cancelamentos**: Para testar fluxos de devolução - **Chargebacks**: Para testar disputas e contestações - **Notificações via Webhook**: Para testar sua infraestrutura de recebimento de notificações --- #### Migrando para Produção Quando sua integração estiver completa e testada no ambiente sandbox, a migração para produção é simples - basta substituir a chave de teste pela chave de produção em suas requisições: ```bash # Ambiente de sandbox SelectKey: sk_test_1234567890abcdefghijklmn # Ambiente de produção SelectKey: sk_live_1234567890abcdefghijklmn ``` Recomendamos configurar sua aplicação para utilizar diferentes chaves dependendo do ambiente (desenvolvimento, teste, produção), facilitando a migração entre ambientes sem alterações de código. --- #### Suporte à Integração Durante o processo de integração, disponibilizamos suporte técnico especializado para auxiliar no desenvolvimento e na resolução de problemas: - **Documentação**: Este guia completo com exemplos práticos - **Ambiente de Sandbox**: Para testes sem custos ou riscos - **Suporte Técnico**: Via e-mail em help@selectwin.io - **FAQ**: Respostas para as perguntas mais frequentes - **Tutoriais**: Passo a passo para implementações comuns Para obter suas credenciais de sandbox, acesse o painel administrativo da Selectwin ou entre em contato com nossa equipe de suporte. --- ## Autenticação Fonte: https://selectwin.io/docs/reference/autenticacao #### Visão Geral A API Selectwin autentica cada requisição por uma chave de API (API Key) enviada no cabeçalho. Não há login nem token de sessão para renovar: a chave vale até ser revogada ou, se você definir uma validade ao gerá-la, até essa data. --- #### A Chave SelectKey Toda requisição à API Selectwin deve incluir uma chave de API válida no cabeçalho HTTP `SelectKey`. Esta chave identifica sua conta, define permissões e registra as atividades realizadas, garantindo a segurança e a auditoria das operações. ```bash curl -X GET "https://api.selectwin.io/v1/transactions" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" ``` --- #### Ambientes e Prefixos A Selectwin oferece dois ambientes distintos, cada um com seu próprio formato de chave: | Ambiente | Prefixo da Chave | Finalidade | |----------|------------------|------------| | Sandbox | `sk_test_` | Testes e desenvolvimento sem impacto em produção | | Produção | `sk_live_` | Operações reais com processamento financeiro efetivo | O ambiente da requisição é determinado pelo namespace da própria chave (a API resolve o ambiente a partir da chave apresentada). O prefixo é uma conveniência de leitura — permite distinguir uma chave de teste de uma de produção rapidamente: ```bash # Ambiente de sandbox SelectKey: sk_test_aBcDeFgHiJkLmNoPqRsTuVwXyZ # Ambiente de produção SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ ``` --- #### Obtendo suas Chaves As chaves de API são geradas no Painel Administrativo da Selectwin: 1. Acesse o [Painel Administrativo](https://my.selectwin.io) 2. No seletor ao lado do logo, escolha o ambiente da chave: em **Sandbox** você gera `sk_test_…`; em **Produção**, `sk_live_…` 3. Navegue até **Configurações → Desenvolvedores** 4. Na seção **Chaves de API**, clique em **Gerar chave** Ao gerar, você define: | Campo | O que define | |-------|--------------| | **Descrição** | Um nome para reconhecer a chave depois (ex.: ERP, app da loja) | | **Expira em** | Opcional. Sem data, a chave não expira; com data, ela para de funcionar nesse momento | | **Acesso** | **Total** ou **Restrita** (veja a seção seguinte) | > **Importante**: A chave aparece inteira **uma vez só**, logo depois de gerada. Copie e guarde nesse momento: depois, o painel mostra apenas a chave mascarada. Gerar uma chave nova não invalida as anteriores; para tirar uma de uso, revogue-a (veja "Revogação de Chaves", abaixo). Só o proprietário da conta gera chaves de API. Webhooks, eventos e logs de entrega não ficam nesta página: no painel, estão na **Workbench**, que a página Desenvolvedores abre por um atalho. --- #### Acesso: Total ou Restrita - **Total**: a chave alcança todos os recursos da API pública da sua conta. - **Restrita**: a chave alcança só o que você marcou ao gerá-la, recurso a recurso. Para cada um (Vendas, Assinaturas, Clientes, Cartões, Produtos, Cupons, Checkouts, Webhooks, Saques e Recebíveis), escolha **Nada**, **Ler** (consultar) ou **Tudo** (consultar, criar, alterar, excluir e as demais ações do recurso, como estornar uma venda). Uma requisição fora do que a chave restrita permite recebe `403`, com o código `forbidden`. Para uma integração que só precisa de parte da API (um ERP que só consulta vendas, por exemplo), prefira uma chave restrita. --- #### Chave publicável (`pk_`) Cada chave gerada no painel vem com uma chave publicável do mesmo ambiente (`pk_test_…` ou `pk_live_…`), mostrada abaixo do identificador da chave. Diferente da chave secreta, ela **pode ir ao navegador**: só identifica a sua conta ao tokenizar um cartão e não autentica nenhuma outra chamada. Ela vale enquanto a chave secreta correspondente valer. Veja [Cartões - Tokenização no navegador](/docs/guide/cards/tokenization). --- #### Segurança das Chaves A chave secreta (`sk_…`) autentica em nome da sua conta (com acesso Total, em toda a API pública), portanto trate-a com a mesma segurança que uma senha: 1. **Nunca compartilhe suas chaves de produção**: Mantenha-as restritas apenas a pessoas autorizadas 2. **Não armazene chaves em código-fonte**: Utilize variáveis de ambiente ou sistemas seguros de gerenciamento de configuração 3. **Não inclua a chave secreta em aplicativos frontend**: Ela deve ser utilizada apenas em ambientes backend seguros. No navegador, só a chave publicável 4. **Dê só o acesso necessário**: Prefira chaves restritas e, para uma integração temporária, defina a data de expiração 5. **Rotação regular**: Substitua suas chaves periodicamente como medida de segurança adicional 6. **Monitoramento**: Verifique regularmente o uso da API para detectar atividades suspeitas ```javascript // INCORRETO - Chave exposta no código const selectKey = "sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ"; // CORRETO - Chave obtida de variável de ambiente const selectKey = process.env.SELECTWIN_API_KEY; ``` --- #### Múltiplas Chaves Você pode criar e utilizar múltiplas chaves de API para diferentes finalidades: - **Separação de ambientes**: Chaves distintas para desenvolvimento, teste e produção - **Integração de sistemas**: Chaves específicas para cada sistema ou serviço integrado - **Equipes distintas**: Chaves separadas para diferentes equipes ou departamentos - **Rastreabilidade**: Identificação facilitada da origem de cada requisição --- #### Revogação de Chaves Em caso de comprometimento de segurança ou quando não for mais necessária, você pode revogar uma chave a qualquer momento: 1. Acesse o Painel Administrativo, no ambiente da chave (Produção ou Sandbox) 2. Navegue até **Configurações → Desenvolvedores** 3. Em **Chaves de API**, clique em **Revogar** ao lado da chave e confirme A revogação é imediata e não pode ser desfeita: as requisições seguintes com essa chave recebem `401`, com o código `unauthorized`, e a chave publicável dela também deixa de funcionar. O mesmo acontece quando a chave chega à data definida em **Expira em**. --- #### Headers Adicionais Além do cabeçalho obrigatório `SelectKey`, recomendamos incluir os seguintes cabeçalhos em todas as requisições: ```bash curl -X POST "https://api.selectwin.io/v1/transactions" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -H "User-Agent: MeuApp/1.0.0" \ -H "X-Idempotency-Key: 2dfc6a63-5df5-4900-baca-e49d91ef393e" ``` | Cabeçalho | Descrição | |-----------|-----------| | `Content-Type` | Define o formato dos dados enviados (geralmente `application/json`) | | `Accept` | Define o formato da resposta esperada (geralmente `application/json`) | | `User-Agent` | Identifica seu aplicativo ou sistema (opcional, mas recomendado) | | `X-Idempotency-Key` | Garante que uma operação não seja processada múltiplas vezes (veja a seção sobre Idempotência) | --- #### Boas Práticas 1. **Valide respostas**: Sempre verifique os códigos de status HTTP e trate adequadamente erros de autenticação 2. **Configuração por ambiente**: Utilize configurações diferentes para sandbox e produção 3. **Registre tentativas de autenticação**: Mantenha logs para análise de segurança 4. **Rotação preventiva**: Substitua suas chaves periodicamente 5. **Verifique regularmente**: No painel, cada chave mostra a situação e o último uso; revogue as que não estão mais em uso --- ## Quickstart Fonte: https://selectwin.io/docs/reference/quickstart Sua primeira cobrança em poucos minutos. Você vai: autenticar, criar uma transação PIX, e receber a confirmação por webhook. #### 1. Obtenha sua chave No [Painel Administrativo](https://my.selectwin.io), com o seletor de ambiente em **Sandbox**, abra **Configurações → Desenvolvedores** e, em **Chaves de API**, clique em **Gerar chave**. A chave aparece inteira uma vez só: copie nesse momento. - Sandbox: `sk_test_...` · Produção: `sk_live_...` Toda requisição envia a chave no header `SelectKey`. Guarde-a como variável de ambiente. Acesso Total ou Restrita, validade e chave publicável: [Autenticação](/docs/reference/autenticacao). #### 2. Faça sua primeira chamada Liste transações (deve retornar uma lista paginada vazia ou existente): ```bash curl "https://api.selectwin.io/v1/transactions" \ -H "SelectKey: sk_test_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` #### 3. Crie uma transação (PIX) Valores são **inteiros em centavos** (`5000` = R$ 50,00): ```bash curl -X POST "https://api.selectwin.io/v1/transactions" \ -H "SelectKey: sk_test_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -H "X-Idempotency-Key: 2dfc6a63-5df5-4900-baca-e49d91ef393e" \ -d '{ "amount": 5000, "currency": "BRL", "externalReference": "PEDIDO-1001", "customer": { "firstName": "João", "lastName": "Silva", "email": "joao@example.com", "document": { "type": "cpf", "number": "12345678901" } }, "payment": { "method": "pix" } }' ``` Resposta (resumida): ```json { "id": "tra_01hqzvabc", "status": "pending", "amount": 5000, "method": "pix", "currency": "BRL" } ``` > A transação PIX nasce `pending` e vira `approved` quando o pagamento é confirmado. **Não faça polling** > para descobrir isso — use webhooks (passo 4). Veja o [ciclo de status](/docs/guide/transactions/overview#fluxo-de-status-da-transação). #### 4. Receba a confirmação por webhook Registre um endpoint para ouvir os eventos de transação: ```bash curl -X POST "https://api.selectwin.io/v1/webhooks" \ -H "SelectKey: sk_test_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Minha loja", "endpoint": "https://minhaloja.com/webhooks/selectwin", "events": ["transaction.approved", "transaction.failed"] }' ``` A resposta traz um `secret` (`whsec_...`) — **guarde-o** para verificar a assinatura das entregas. Quando o pagamento for confirmado, seu endpoint recebe: ```json { "id": "wbh_01hqzvabc", "type": "transaction.approved", "source": "automatic", "payload": { "object": { "id": "tra_01hqzvabc", "status": "approved", "amount": 5000 } }, "createdAt": "2026-04-12T17:56:33.000Z" } ``` > O objeto do recurso fica em **`payload.object`** (igual à leitura do recurso). Não há chave > `data`/`resource` no nível raiz. > ⚠️ **Sempre verifique a assinatura** `X-Selectwin-Signature` antes de confiar no payload — > veja [Verificando Assinaturas de Webhook](/docs/guide/webhook-events/verifying-signatures). #### Próximos passos - [Convenções](/docs/reference/convencoes) — centavos, datas, IDs opacos - [Cartões de teste](/docs/reference/cartoes-de-teste) — para testar crédito no sandbox - [Transações](/docs/guide/transactions/overview) — crédito, captura, estorno, disputa - [Tratamento de Erros](/docs/reference/erros) + [Catálogo de Códigos](/docs/reference/codigos-de-erro) - [Idempotência](/docs/reference/idempotencia) — repita requisições com segurança --- ## Convenções da API Fonte: https://selectwin.io/docs/reference/convencoes Esta página reúne as convenções transversais que valem para **todos** os recursos da API Selectwin. Conhecê-las evita os erros de integração mais comuns. #### Nomenclatura — camelCase Todos os campos de requisição e resposta usam **camelCase** (`firstName`, `createdAt`, `unitPrice`, `externalReference`). Não há campos em `snake_case` nas superfícies públicas. #### Valores monetários — centavos (inteiros) **Todo valor monetário é um inteiro em centavos.** Nunca use ponto flutuante para dinheiro. | Exibição | Valor na API | |----------|--------------| | R$ 99,00 | `9900` | | R$ 1,50 | `150` | | R$ 1.250,00 | `125000` | Aplica-se a `amount`, `unitPrice`, `oldPrice`, `costPerItem`, `fee`, `grossAmount`, etc. Converta para centavos antes de enviar e divida por 100 apenas para exibir. #### Moeda Use o código **ISO 4217** em maiúsculas. Atualmente a API opera em **`BRL`** (Real brasileiro). #### Datas e horários — ISO 8601 em UTC Todos os timestamps são **ISO 8601 com timezone UTC** (sufixo `Z`): ``` 2026-04-12T17:56:33.000Z ``` - Campos de data-hora: `createdAt`, `updatedAt`, `paidAt`, `approvedAt`, `dueDate`, `expectedOn`, … - Converta para o fuso do usuário apenas na exibição. - Campos de **data** (sem hora), quando existirem, usam `YYYY-MM-DD`. #### Identificadores (IDs) Todo recurso tem um identificador público no formato **`prefixo_valor`** (ex.: `cus_01hqzvabc`). O prefixo indica o tipo do recurso. > 🔑 **Trate os IDs como opacos.** Armazene a string inteira; **não** faça parsing, não confie no > comprimento e não construa IDs manualmente. O prefixo é uma conveniência para leitura humana — a > lógica do seu sistema deve tratar o ID como uma string opaca e estável. Prefixos dos recursos públicos (para referência): | Recurso | Prefixo | |---------|---------| | Customer | `cus_` | | Card | `card_` | | Address | `addr_` | | Transaction | `tra_` | | Wallet | `wall_` | | Withdrawal | `cash_` | | Receivable | `rec_` | | Subscription | `subs_` | | Subscription item | `sit_` (registros antigos podem vir com `item_`) | | Subscription split | `ssp_` (registros antigos podem vir com `split_`) | | Subscription cycle | `scy_` | | Transaction item | `item_` | | Transaction split | `split_` | | Product | `prd_` | | Variant | `var_` | | Coupon | `dis_` | | Webhook endpoint | `wbe_` | | Webhook event | `wbh_` | | Webhook dispatch | `wdi_` | | Webhook secret | `whsec_` | > Nas respostas, o ID público é sempre exposto como `id` (e como `Id` quando referenciado por > outro recurso, ex.: `customerId`, `transactionId`). O identificador numérico interno **nunca** é exposto. > Os IDs nos exemplos dos guias são ilustrativos — confie nesta tabela e no campo `id` real da resposta. #### `null` vs. campo ausente - Um campo presente com valor `null` significa "sem valor" (ex.: `paidAt: null` = ainda não pago). - Em respostas de **lista** (`data[]`), os itens são **projeções leves** — alguns campos do objeto completo são omitidos. Para o objeto completo, consulte o recurso individual (`GET .../{id}`). #### Requisições | Cabeçalho | Uso | |-----------|-----| | `SelectKey` | **Obrigatório** — sua chave de API (veja [Autenticação](/docs/reference/autenticacao)) | | `Content-Type: application/json` | Em requisições com corpo | | `X-Idempotency-Key` | Recomendado em operações de escrita (veja [Idempotência](/docs/reference/idempotencia)) | - Corpo das requisições em **JSON**. - Use `externalReference` e `metadata` (objeto livre) para correlacionar recursos com o seu sistema. #### Respostas - **Sucesso:** o corpo é o objeto do recurso (ou o objeto paginado em listagens). Veja [Paginação](/docs/reference/paginacao) e o padrão **HATEOAS** (`_links`) em [Recursos](/docs/reference/recursos). - **Erro:** envelope padrão com `error.code` estável — veja [Tratamento de Erros](/docs/reference/erros) e o [Catálogo de Códigos de Erro](/docs/reference/codigos-de-erro). - **Webhooks:** verifique sempre a assinatura — veja [Verificando Assinaturas de Webhook](/docs/guide/webhook-events/verifying-signatures). #### Resumo | Convenção | Regra | |-----------|-------| | Campos | camelCase | | Dinheiro | inteiro em centavos | | Moeda | ISO 4217 (`BRL`) | | Datas | ISO 8601 UTC (`...Z`) | | IDs | `prefixo_valor`, **opacos** | | Erros | envelope com `error.code` estável | | Atualizações de estado | via **webhooks**, nunca por polling | --- ## Tratamento de Erros Fonte: https://selectwin.io/docs/reference/erros #### Visão Geral A API Selectwin utiliza códigos de status HTTP padrão e uma estrutura de resposta de erro consistente para comunicar problemas durante o processamento das requisições. Este documento detalha os diferentes tipos de erros que podem ocorrer, como interpretá-los e as melhores práticas para lidar com eles em sua aplicação. > 📖 Para a lista de valores de `error.code` por recurso (com o status HTTP de cada um), veja o > [Catálogo de Códigos de Erro](/docs/reference/codigos-de-erro). --- #### Estrutura da Resposta de Erro Todas as respostas de erro da API Selectwin seguem uma estrutura padrão: ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "code": "invalidParameters", "message": "Validation errors occurred.", "resource": "transaction", "details": "Informações adicionais sobre o erro", "params": [ { "field1": "Mensagem de erro específica para o campo" } ] } } ``` > O campo `error.resource` está **sempre presente** e indica o domínio dono do erro (ex.: `transaction`, > `customer`, `server`). > **Use `error.code` para a lógica do seu sistema.** Ele é o identificador estável e legível por máquina do erro (ex.: `invalidParameters`, `transactionNotFound`, `tooManyRequests`). Diferentemente de `message`/`status`/`details` — textos que podem mudar —, o `code` faz parte do contrato da API e está sempre presente. ##### Campos Comuns | Campo | Tipo | Descrição | |-------|------|-----------| | `error.code` | string | **Identificador estável do erro (legível por máquina)** — use-o para tratar erros programaticamente. Sempre presente. | | `error.status` | string | Descrição textual do status HTTP | | `error.statusCode` | integer | Código numérico do status HTTP | | `error.category` | string | Categoria do erro: `validation`, `authentication`, `authorization`, `payment`, `client`, `server`, `rate_limit` ou `pending` | | `error.message` | string | Mensagem principal descrevendo o erro (texto, pode mudar) | | `error.resource` | string | Domínio dono do erro (ex.: `transaction`, `customer`, `server`). **Sempre presente.** | | `error.details` | string | Detalhes adicionais sobre o erro (presente em alguns tipos de erro) | ##### Campos Específicos Dependendo do tipo de erro, campos adicionais podem estar presentes: | Campo | Tipo | Descrição | |-------|------|-----------| | `error.params` | array | Para erros de validação, contém os campos com problemas e suas mensagens | | `error.docUrl` | string | URL para documentação relacionada ao erro (presente em alguns códigos) | | `error.supportUrls` | array | URLs de suporte relacionadas ao erro (presente em alguns códigos) | > **Rate limit (429):** o tempo de espera é informado no **cabeçalho HTTP `Retry-After`** (em segundos), > não no corpo. O envelope JSON do `429` não traz `retryAfter`/`retryAfterMinutes`. --- #### Códigos de Status HTTP A API Selectwin utiliza os seguintes códigos de status HTTP para indicar erros: ##### Erros do Cliente (4xx) | Código | Nome | Descrição | |--------|------|-----------| | 400 | Bad Request | A requisição contém sintaxe inválida ou campos obrigatórios ausentes | | 401 | Unauthorized | Autenticação necessária ou falha de autenticação (chave API inválida) | | 402 | Payment Required | Falha/recusa no processamento do pagamento — ver [Recusas de Pagamento](/docs/reference/recusas-de-pagamento) | | 403 | Forbidden | Acesso negado ao recurso solicitado | | 404 | Not Found | O recurso solicitado não existe | | 405 | Method Not Allowed | O método HTTP usado não é permitido para o endpoint | | 409 | Conflict | Conflito com o estado atual do recurso | | 422 | Unprocessable Entity | A requisição foi bem formada, mas não pode ser processada | | 429 | Too Many Requests | Limite de taxa excedido (rate limiting) | ##### Erros do Servidor (5xx) | Código | Nome | Descrição | |--------|------|-----------| | 500 | Internal Server Error | Erro interno no servidor | | 503 | Service Unavailable | Serviço temporariamente indisponível | --- #### Detalhes dos Erros ##### 400 - Bad Request Indica que a requisição é inválida, geralmente devido a problemas de validação. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation error occurred.", "params": [ { "field1": "Mensagem de erro específica para o campo" } ] } } ``` ###### Casos Comuns - Dados de entrada em formato incorreto - Campos obrigatórios ausentes - Valores inválidos para determinados campos ##### 401 - Unauthorized Indica problemas com a autenticação. ```json { "error": { "status": "Unauthorized", "statusCode": 401, "category": "authentication", "code": "unauthorized", "message": "Authentication is required to access this resource.", "resource": "server" } } ``` ###### Casos Comuns - Chave API ausente ou inválida - Chave API expirada - Credenciais incorretas ##### 402 - Payment Required Indica recusa/falha no processamento do pagamento (`category: "payment"`). O envelope segue a mesma estrutura padrão (`status`, `statusCode`, `category`, `code`, `message`, `resource`). ```json { "error": { "status": "Payment Required", "statusCode": 402, "category": "payment", "code": "paymentDeclined", "message": "The payment was declined by the issuer.", "resource": "payments" } } ``` > ⚠️ **Importante:** a criação de uma transação (`POST /v1/transactions`) é **assíncrona** — ela responde > `201 Created` com `status: "pending"` e a cobrança é processada em segundo plano. Uma recusa de cartão > normalmente **não** chega como um `402` síncrono nessa chamada; o resultado da cobrança aparece no > objeto da transação (`status: "failed"`) e no webhook **`transaction.failed`**. Veja > **[Recusas de Pagamento](/docs/reference/recusas-de-pagamento)**. ###### Casos Comuns - Cobrança recusada pelo emissor/adquirente - Falha no processamento do pagamento ##### 403 - Forbidden Indica que o cliente não tem permissão para acessar o recurso solicitado. ```json { "error": { "status": "Forbidden", "statusCode": 403, "category": "authorization", "code": "forbidden", "message": "You do not have permission to perform this action.", "resource": "server" } } ``` ###### Casos Comuns - Plano de assinatura não permite acesso ao recurso - Restrições de IP - Permissões insuficientes para a operação solicitada ##### 404 - Not Found Indica que o recurso solicitado não existe. ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "client", "code": "transactionNotFound", "message": "Transaction not found.", "resource": "transaction" } } ``` > O `error.code` e `error.resource` variam conforme o recurso consultado (ex.: `customerNotFound`/`customer`). ###### Casos Comuns - ID de recurso inexistente - URL incorreta - Recurso excluído ##### 405 - Method Not Allowed Indica que o método HTTP usado não é permitido para o endpoint. Indica que o método HTTP usado não é permitido para o endpoint (o caminho existe, mas não para esse verbo). ###### Casos Comuns - Tentativa de usar POST em um endpoint que só aceita GET - Método HTTP não suportado para o recurso ##### 409 - Conflict Indica um conflito com o estado atual do recurso. ```json { "error": { "status": "Conflict", "statusCode": 409, "category": "client", "code": "idempotencyMismatch", "message": "This idempotency key was already used with a different request payload.", "resource": "server" } } ``` ###### Casos Comuns - Violação de restrição de idempotência (`idempotencyMismatch`, `idempotencyInProgress`) — veja [Idempotência](/docs/reference/idempotencia) - Tentativa de criação duplicada de recurso - Condições de concorrência - Conflito com regras de negócio ##### 422 - Unprocessable Entity Indica que a requisição está bem formada, mas não pode ser processada devido a erros semânticos. ```json { "error": { "status": "Unprocessable Entity", "statusCode": 422, "category": "validation", "code": "installmentsExceedMax", "message": "The requested number of installments exceeds the maximum currently offered.", "resource": "transaction" } } ``` ###### Casos Comuns - Valores de campos em formato correto, mas semanticamente inválidos - CPF válido em formato, mas inexistente - Número de parcelas acima do máximo oferecido pela conta (`installmentsExceedMax`) > A criação de transação é assíncrona: uma recusa de cartão chega como `status: "failed"` no objeto da > transação e no webhook `transaction.failed`, não como um erro síncrono nessa chamada. Veja > [Recusas de Pagamento](/docs/reference/recusas-de-pagamento). ##### 429 - Too Many Requests Indica que o cliente excedeu o limite de requisições permitido. ```json { "error": { "status": "Too Many Requests", "statusCode": 429, "category": "rate_limit", "code": "tooManyRequests", "message": "Too many requests. Please slow down and retry after a short wait.", "resource": "server", "details": "This endpoint is rate limited. Check the Retry-After response header for when to retry." } } ``` > O tempo de espera vem no **cabeçalho HTTP `Retry-After`** (segundos), não no corpo. ###### Casos Comuns - Muitas requisições em curto período - Excedeu quota de uso da API ##### 500 - Internal Server Error Indica um erro inesperado no servidor. ```json { "error": { "status": "Internal Server Error", "statusCode": 500, "category": "server", "code": "serverError", "message": "An unexpected error occurred. Please try again later.", "resource": "server", "details": "If the problem persists, contact support with the correlationId from the response headers." } } ``` ###### Casos Comuns - Falhas inesperadas no servidor - Problemas de infraestrutura - Bugs no sistema ##### 503 - Service Unavailable Indica que o serviço está temporariamente indisponível. ```json { "error": { "status": "Service Unavailable", "statusCode": 503, "category": "server", "code": "providerUnavailable", "message": "The service is temporarily unavailable. Please try again later.", "resource": "server" } } ``` ###### Casos Comuns - Manutenção programada - Sobrecarga do sistema - Falha em serviços dependentes --- #### Categorias de Erro A API Selectwin agrupa os erros nas seguintes categorias: | Categoria | Descrição | |-----------|-----------| | `validation` | Erros de validação dos dados de entrada (400/422) | | `authentication` | Falha de autenticação (401) | | `authorization` | Falta de permissão (403) | | `payment` | Falha/recusa no processamento de pagamento (402) | | `client` | Outros erros causados pelo cliente (ex.: 404, 409) | | `server` | Erros internos do servidor (500/503) | | `rate_limit` | Limite de requisições excedido (429) | | `pending` | Operação ainda em processamento | > Conflitos de idempotência (`409`) usam a categoria `client` (não há categoria `idempotency`). --- #### Perguntas Frequentes ##### Como devo lidar com erros 5xx? Erros 5xx indicam problemas no servidor. Para estes casos: 1. Implemente retry com backoff para tentativas automáticas 2. Tenha um plano de fallback para operações críticas 3. Notifique sua equipe de suporte se o problema persistir ##### Qual é a diferença entre erros 400 e 422? * **400 (Bad Request)**: Problemas na sintaxe da requisição ou validação básica (tipos incorretos, campos obrigatórios ausentes) * **422 (Unprocessable Entity)**: A requisição está sintaticamente correta, mas há problemas semânticos (ex: CPF inválido, provedor não configurado). A criação de transação é assíncrona — recusas de cartão chegam como `status: "failed"`/webhook `transaction.failed`, não como erro síncrono. Ver [Recusas de Pagamento](/docs/reference/recusas-de-pagamento). ##### Como faço para interpretar erros de validação complexos? Para erros de validação (categoria `validation`), verifique o campo `error.params` que contém informações detalhadas sobre cada campo com problema. ##### O que devo fazer quando recebo um erro 429 (Rate Limit)? 1. Respeite o cabeçalho HTTP `Retry-After` (segundos) retornado na resposta 2. Implemente backoff exponencial para retentativas 3. Considere otimizar seu código para reduzir o número de requisições 4. Se o problema persistir, avalie a necessidade de um plano com limites maiores ##### Por que estou recebendo erros 409 (Conflict)? Erros 409 geralmente ocorrem nas seguintes situações: 1. Tentativa de criar um recurso que já existe 2. Uso incorreto de chaves de idempotência 3. Tentativas de atualização concorrente do mesmo recurso 4. Violação de regras de negócio que geram conflitos --- ## Catálogo de Códigos de Erro Fonte: https://selectwin.io/docs/reference/codigos-de-erro Este catálogo lista os valores de **`error.code`** que a API pode retornar, agrupados por status HTTP e por recurso. Para a **estrutura** do objeto de erro (envelope), veja [Tratamento de Erros](/docs/reference/erros). > **Trate erros pelo `error.code`, não pela `message`.** O `code` é o identificador estável e legível por > máquina (faz parte do contrato); a `message` é texto e pode mudar. Sempre presente na resposta. #### Status HTTP A API usa códigos de status HTTP padrão. Cada `error.code` tem sempre o mesmo `statusCode` e a mesma `category`: | `category` | Status | Significado | |------------|-------:|-------------| | `validation` | `400` | Requisição malformada ou parâmetro inválido (veja `error.params`) | | `authentication` | `401` | Credencial (`SelectKey` / login) ausente, inválida ou revogada | | `payment` | `402` | Pagamento recusado pelo emissor | | `authorization` | `403` | Autenticado, mas sem permissão / conta não ativa | | `client` | `404` | Recurso não encontrado | | `client` | `409` | Conflito (estado incompatível, duplicidade, idempotência) | | `client` | `413` | Corpo da requisição grande demais | | `validation` | `422` | Entidade não processável (regra de negócio) | | `rate_limit` | `429` | Limite de requisições excedido (veja [API Limits](/docs/reference/limites)) | | `server` | `500` | Erro interno — tente novamente; se persistir, contate o suporte | | `server` | `503` | Indisponibilidade temporária de um serviço — repita com *backoff* | > Observação: erros de validação de contrato são sempre **`400` / `validation`** (com `error.params`), nunca > `422`. O `422` é reservado para regras de negócio (entidade processável sintaticamente, mas rejeitada). > Em erros que admitem nova tentativa (ex.: `429`), respeite o cabeçalho **`Retry-After`** da resposta no > seu *backoff*. #### Códigos comuns (cross-cutting) Estes códigos vêm do kernel e podem aparecer em qualquer rota (recurso `server`, exceto onde indicado): | `error.code` | HTTP | Quando ocorre | |--------------|-----:|---------------| | `invalidParameters` | 400 | Falha de validação de contrato (veja `error.params` para os campos) — inclusive `metadata` acima do limite (50 chaves, chave ≤ 40 caracteres, valor ≤ 500, objeto ≤ 8 KiB; veja [Metadata](/docs/reference/metadata#limites)) | | `unauthorized` | 401 | `SelectKey`/token ausente, inválido ou revogado | | `actingAccountInvalid` | 401 | `X-Selectwin-Account` com uma credencial que não é chave de API, com uma conta que não é sub-conta da sua plataforma, ou com a sua plataforma fora de `active` ([Marketplace](/docs/guide/marketplace/acting-as)) | | `forbidden` | 403 | Autenticado, mas sem permissão para a operação | | `payloadTooLarge` | 413 | Corpo da requisição excede o limite (envie arquivos via `/v1/assets`) | | `tooManyRequests` | 429 | Rate limit atingido (veja [API Limits](/docs/reference/limites)) | | `idempotencyInProgress` | 409 | Já há uma requisição em andamento com esta `X-Idempotency-Key` | | `idempotencyOutcomeUnknown` | 409 | A primeira requisição com esta chave não terminou de responder (há mais de 10 min) — pode ter executado; confira e repita com uma chave nova | | `idempotencyMismatch` | 409 | Esta `X-Idempotency-Key` já foi usada com um payload diferente | | `accountLevelReadOnlyInSandbox` | 409 | Recurso `authentication`. Escrita num recurso de **conta** (apps/integrações, pixels, área de membros, marketplace, gate do Repart, encerramento) com credencial **sandbox** (`sk_test_…` ou painel em Sandbox). Esses recursos têm uma cópia só, a de produção — leia à vontade, altere em `live`. Cada operação afetada vem marcada com `x-account-level` na OpenAPI | | `serverError` | 500 | Erro interno inesperado | Erros de validação de campos trazem detalhes por campo em `error.params`: ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "code": "invalidParameters", "message": "Validation errors occurred.", "resource": "server", "params": [{ "amount": "amount must be a positive integer" }] } } ``` #### Por recurso ##### Transactions | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `transactionNotFound` | 404 | Transaction not found | | `customerNotFound` | 404 | The customer for this transaction was not found | | `cardNotFound` | 404 | The card for this transaction was not found | | `addressNotFound` | 404 | The billing address was not found | | `recipientNotFound` | 404 | A split recipient was not found | | `disputeNotFound` | 404 | No dispute was found for this transaction | | `cardCustomerMismatch` | 422 | The card does not belong to the customer | | `transactionNotRefundable` | 422 | This transaction cannot be refunded in its current status | | `refundAmountInvalid` | 422 | Refund amount inválido (≤ 0 ou acima do total já cobrado) | | `refundInProgress` | 409 | A refund for this transaction is already in progress | | `refundInsufficientBalance` | 422 | The available balance does not cover this refund — the funds for this sale were already paid out | | `refundSplitRecipientInsufficientBalance` | 422 | A split recipient of this sale does not have the available balance this refund draws from their released share | | `transactionNotCapturable` | 422 | This transaction is not awaiting capture | | `captureAmountInvalid` | 422 | Capture amount inválido (≤ 0 ou acima do autorizado) | | `transactionNotInDispute` | 422 | Defense evidence só pode ser enviada com a transação em disputa | | `discountExceedsAmount` | 422 | O desconto deixa a cobrança abaixo do mínimo | | `splitsExceedAmount` | 422 | A soma dos splits excede o valor cobrado | | `splitsInvalid` | 422 | Um split resolve para valor inválido (zero ou negativo) | | `installmentsExceedMax` | 422 | Parcelas acima do máximo oferecido no momento | | `onBehalfOfInvalid` | 422 | onBehalfOf deve referenciar uma sub-conta sua (marketplace habilitado) | | `sellerRatesMissing` | 422 | Conta sem taxa configurada — não pode transacionar | | `chargeCompanyNotActive` | 422 | Conta não está `active` — cobranças indisponíveis (numa cobrança `onBehalfOf`, avalia a SUB-conta) | Erros de **processamento do pagamento** (recurso `payments`): | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `paymentDeclined` | 402 | The payment was declined by the issuer | | `transactionNotFound` | 404 | Transaction not found at the provider | | `providerUnavailable` | 500 | The payment provider is temporarily unavailable | ##### Marketplace Sub-contas de uma plataforma (`/v1/marketplace/*` — [guia](/docs/guide/marketplace/overview)). Recurso **de conta**: toda escrita com chave de sandbox responde `409 accountLevelReadOnlyInSandbox`. | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `marketplaceNotEnabled` | 403 | A função de marketplace não está habilitada para esta conta | | `marketplaceNotApproved` | 403 | A função está habilitada, mas aguarda a aprovação de compliance antes de criar sub-contas | | `marketplaceNested` | 422 | Uma sub-conta não pode ser plataforma nem criar sub-contas | | `subAccountLimitReached` | 422 | A conta atingiu o limite de sub-contas — fale com o seu gerente de conta | | `marketplaceUserRequired` | 422 | A conta da plataforma não tem um titular registrado para receber a sub-conta — fale com o suporte | | `subAccountNotFound` | 404 | Sub-conta não encontrada (ou não pertence à sua plataforma) | | `subAccountAlreadyActive` | 422 | A sub-conta já está ativa — o link de credenciamento só vale para contas em cadastro | | `planNotFound` | 404 | Plano de taxas não encontrado (ou não pertence à sua plataforma) | | `planFeeTypeMismatch` | 422 | Um meio cobrado numa forma (percentual/fixo) diferente da sua própria taxa naquele meio (`params: [{ method, field }]`) | | `platformRatesMissing` | 422 | A sua conta ainda não tem taxa configurada, então o plano não pode ser validado | | `planReleaseBelowPlatform` | 422 | Um prazo de liberação do plano é menor que o prazo em que a plataforma recebe aquele meio (`params` traz o mínimo por campo) | | `planInUse` | 409 | O plano está atribuído a sub-contas e não pode ser excluído (`params[0].subAccounts`) | | `subAccountNameLocked` | 422 | O nome só muda antes de o cadastro ser enviado — depois ele é parte do registro em análise | | `subAccountNotSuspendable` | 422 | Só uma sub-conta `active` pode ser suspensa | | `subAccountNotReinstatable` | 422 | A sub-conta não foi suspensa pela sua plataforma (ou não está `disabled`) — só a Selectwin reabilita; fale com o suporte | | `subAccountStatusConflict` | 409 | O status da sub-conta mudou enquanto a requisição rodava — consulte e repita | | `closureRequestNotFound` | 404 | A sub-conta não tem pedido de encerramento (consulta ou desistência) — recurso `company` | | `closureRequestNotCancelable` | 409 | O encerramento já começou a ser executado e não pode mais ser desfeito — recurso `company` | | `kycLinkInvalid` | 404 | O link de credenciamento é inválido, venceu, foi revogado — ou a sub-conta já está ativa | | `kycUploadLimitReached` | 429 | Uploads demais pelo link de credenciamento na última hora (`params[0].limit`) | A plataforma agindo como a sub-conta (`X-Selectwin-Account`, [guia](/docs/guide/marketplace/acting-as)) — recurso `authentication`: | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `actingAccountInvalid` | 401 | O cabeçalho veio com uma credencial que não é chave de API, com uma conta que não é sub-conta da sua plataforma, ou a sua plataforma não está ativa | ##### Subscriptions `/v1/subscriptions` é **Selectwin-strict**: a precificação vem das variantes recorrentes; não há campo `amount` nem preço manual. | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `subscriptionNotFound` | 404 | Subscription not found | | `customerNotFound` | 404 | The customer for this subscription was not found | | `cardNotFound` | 404 | The card for this subscription was not found | | `variantNotFound` | 404 | One or more product variants were not found | | `cycleNotFound` | 404 | The billing cycle was not found for this subscription | | `itemNotFound` | 404 | The line item was not found for this subscription | | `splitNotFound` | 404 | The split was not found for this subscription | | `splitRecipientNotFound` | 404 | The split recipient company was not found | | `meteredItemNotFound` | 404 | Metered subscription item not found | | `thresholdNotFound` | 404 | Billing threshold not found | | `cardCustomerMismatch` | 422 | The card does not belong to the customer | | `variantNotRecurring` | 422 | A variant is not priced for recurring billing | | `subscriptionStartDateInPast` | 422 | billing.startDate cannot be in the past (São Paulo calendar day) | | `subscriptionAlreadyCanceled` | 422 | This subscription is already canceled | | `subscriptionNotModifiable` | 422 | A canceled subscription cannot be modified | | `subscriptionNotPausable` | 422 | This subscription cannot be paused in its current status | | `subscriptionNotResumable` | 422 | Only a paused subscription can be resumed | | `noScheduledCycle` | 422 | There is no scheduled cycle to renew for this subscription | | `subscriptionRequiresItem` | 422 | A subscription must keep at least one line item | | `itemCurrencyMismatch` | 422 | The item currency must match the subscription currency | | `duplicateSplitRecipient` | 422 | A split already exists for this recipient | | `splitValueInvalid` | 422 | A percentage split value must be between 0 and 100 | | `usageOnNonMeteredItem` | 422 | Usage can only be reported on metered items | | `usageActionUnsupported` | 422 | Usage action not supported (only 'increment') | | `thresholdMisconfigured` | 422 | Billing threshold misconfigured | | `thresholdAlreadyExists` | 409 | A billing threshold already exists for this item | | `onBehalfOfInvalid` | 422 | onBehalfOf deve referenciar uma sub-conta sua (marketplace habilitado) | | `fraudBlocked` (recurso `antifraud`) | 403 | A criação foi recusada pelo antifraude (bloqueio ou pedido de revisão manual — a assinatura não espera revisão) | ##### Customers | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `customerNotFound` | 404 | Customer not found | | `invalidCustomerId` | 404 | The provided customer id is invalid | | `customerEmailTaken` | 409 | A customer with this email already exists | | `customerDocumentTaken` | 409 | A customer with this document already exists | | `customerHasActiveSubscription` | 409 | Cliente com assinatura ativa não pode ser excluído (cancele a assinatura antes) | ##### Cards | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `cardIdNotFound` | 404 | Card ID not found | | `cardInUseBySubscription` | 422 | Uma assinatura de **cartão** viva deste cliente cobra este cartão no próximo ciclo; aponte a assinatura para outro antes de apagar. Assinaturas de pix/boleto não contam (geram QR/boleto novo a cada ciclo) | | `cardIdIsInvalid` | 400 | Card ID is invalid | | `cardExpired` | 400 | Card expired | | `invalidCardData` | 422 | Invalid card data | | `cardBrandNotSupported` | 422 | Card brand not supported | | `cardAssociationRateLimited` | 429 | Muitas associações de cartão (ou recusas) em pouco tempo nesta conta. Aguarde e tente de novo; se não foi você, fale com o suporte | ##### Addresses | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `addressIdNotFound` | 404 | Address not found | ##### Wallets (contas bancárias) | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `walletNotFound` | 404 | Wallet not found | | `walletAlreadyExists` | 409 | A wallet with these account details already exists | | `walletHasPendingWithdrawal` | 409 | Wallet com saque em andamento não pode ser excluída | | `bankCodeNotSupported` | 400 | This bank code is not supported | ##### Withdrawals (saques) | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `withdrawalNotFound` | 404 | Withdrawal not found | | `withdrawalCreateNotEnabled` | 400 | Withdrawals are not enabled for this company | | `withdrawalAmountLessThanMinimum` | 400 | O valor do saque está abaixo do mínimo — `error.params[0].minAmount` traz o mínimo em centavos (o mesmo de `GET /v1/companies/fees` → `withdrawal.minAmount`) | ##### Auto-cashout (saque automático) | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `autoCashoutNotFound` | 404 | Auto-cashout config not found | | `duplicateAutoCashoutForWallet` | 400 | An auto-cashout config already exists for this wallet | | `percentageOverlapDetected` | 400 | A soma das porcentagens habilitadas não pode exceder 100% (regras de conta removida não contam) | | `autoCashoutWalletArchived` | 422 | A conta de destino da regra foi removida — envie o `PUT` com outro `walletId` antes de ligá-la | ##### Finance (Balance / Receivables) | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `balanceNotFound` | 404 | No balance found for this company yet | | `balanceIsInsufficient` | 422 | Insufficient available balance for this withdrawal | | `balanceHasNoDebt` | 422 | Pedido de Pix de quitação (`POST /v1/balance/debt-settlements`) numa conta **sem** saldo negativo — não há dívida para quitar | | `debtSettlementInProgress` | 409 | Outro pedido de quitação da mesma conta está emitindo o Pix neste instante. Aguarde alguns segundos e repita: o Pix em aberto será devolvido | | `withdrawalBlockedByDebt` | 422 | A conta tem **saldo negativo** (dívida com a plataforma — em geral estorno ou chargeback depois de um saque). O saque fica bloqueado até a dívida ser quitada, e ela é **abatida automaticamente** pelos próximos recebíveis liberados. O valor em aberto vem em `params[0].owedAmount` (centavos) | | `withdrawalDuplicateSuspected` | 409 | Um segundo `POST /v1/withdrawals` **idêntico** (mesma carteira, mesmo valor) chegou em menos de 10 segundos **sem** `X-Idempotency-Key`. Nada foi debitado; `params[0].withdrawalId` é o saque que já existe. Mande sempre a chave de idempotência para reenviar com segurança | | `receivableNotFound` | 404 | Receivable not found | ##### Products & Variants | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `productNotFound` | 404 | Product not found | | `variantNotFound` | 404 | Variant not found | | `invalidPricing` | 422 | Invalid advanced pricing configuration | | `pricingTypeImmutable` | 422 | pricing.type is immutable | | `pricingSchemaImmutable` | 422 | pricing.schema is immutable | | `variantRenewalRulesUnavailable` | 503 | The renewal rules cannot be saved yet | ##### Coupons | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `couponNotFound` | 404 | The coupon code was not found | | `couponCodeConflict` | 409 | A coupon with this code already exists | | `couponDisabled` | 422 | This coupon is no longer active | | `couponNotYetActive` | 422 | This coupon is not active yet | | `couponExpired` | 422 | This coupon has expired | | `couponMinCartAmount` | 422 | Cart total abaixo do mínimo do cupom | | `couponMaxCartAmount` | 422 | Cart total acima do máximo do cupom | | `couponMinCartItems` | 422 | Menos itens do que o cupom exige | | `couponMaxCartItems` | 422 | Mais itens do que o cupom permite | | `couponNotApplicable` | 422 | This coupon does not apply to this cart | | `couponUsageLimitReached` | 422 | This coupon has reached its usage limit | | `couponCustomerLimitReached` | 422 | This coupon has already been used by this customer | | `couponExceedsTotal` | 422 | The coupon discount exceeds the chargeable total | | `couponAlreadyApplied` | 422 | O mesmo cupom foi aplicado mais de uma vez na mesma cobrança | | `couponNotCumulative` | 422 | Cupom `isCumulative:false` enviado junto de outro cupom | ##### Checkout | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `checkoutSessionNotFound` | 404 | Checkout session not found | | `sessionHasActivePayment` | 409 | Já há um pagamento em voo: 2º submit, ou patch nos campos que a cobrança usa (`items`, `coupons`, `customer`, `address`, `shippingOptionId`) (`params[0].jobId`) | | `sessionAlreadyPaid` | 409 | A compra desta sessão já está paga, ou aguardando o comprador, por OUTRO pagamento dela — nunca um 2º pagamento da mesma compra (`params[0].transactionId`, só no submit do lojista) | | `checkoutCartChangedDuringPayment` | 409 | `items`/`coupons` mudaram entre o cálculo do total e o claim do `/pay`; refaça `/totals` e `/pay` com o carrinho atual | | `checkoutTemplateMismatch` | 409 | O `templateId` do corpo não é o template com que a sessão nasceu; omita-o ou mande o da sessão | | `checkoutCartNotEditable` | 422 | O link (`editable:false`) ou o template trava essa mudança do carrinho (`params[0].rule`: `quantity`, `removal` ou `addition`) | | `exitCouponNotOffered` | 404 | Não há cupom de saída para esta sessão (popup desligado, sem código, ou código que não vale agora) | | `invalidOrderbump` | 400 | Linha de order bump com quantidade que o lojista não ofereceu, ou a mesma variante marcada como bump duas vezes | | `upsellPriceChanged` | 409 | O `expectedAmount`/`expectedPricingType` do aceite não bate com a oferta viva; **nada foi cobrado**. A oferta atual vem em `params[0]` (`{upsellId, amount, pricingType, currency}`) — releia `GET /checkout/v1/upsells` e mostre o preço que está de fato à venda, nunca reenvie às cegas | | `checkoutSessionNotUpdatable` | 409 | A completed checkout session can no longer be updated | | `checkoutSessionNotPayable` | 409 | This checkout session is not in a payable state | | `variantIdIsInvalid` | 422 | Um item referencia uma variante que não existe, está desligada, pertence a um **produto desligado** ou ao outro ambiente — `error.params[0].items` diz qual | | `onBehalfOfInvalid` | 422 | onBehalfOf deve referenciar uma sub-conta sua (marketplace habilitado) | | `checkoutPaymentLinkNotFound` | 404 | Payment link not found | | `checkoutPaymentLinkCreateFailed` | 500 | The payment link could not be created | | `checkoutTemplateNotFound` | 404 | Checkout template not found | | `checkoutTemplateIdIsInvalid` | 422 | The checkout template does not exist | | `orderbumpNotFound` | 404 | Order bump not found | | `reviewNotFound` | 404 | Checkout review not found | | `shippingOptionNotFound` | 404 | Shipping option not found | | `shippingOptionRequiresTemplateOrGlobal` | 422 | A shipping option must be global or bound to a checkout template | | `checkoutPixelNotFound` | 404 | Checkout tracking pixel not found | | `upsellNotFound` / `upsellOfferNotFound` | 404 | Upsell offer not found | | `upsellParentNotFound` | 404 | Parent transaction not found | | `upsellParentNotApproved` | 422 | A transação pai precisa estar aprovada para cobrar um upsell | | `upsellOfferExpired` | 422 | This upsell offer has expired | | `upsellOfferNotEligible` | 422 | This upsell offer is not eligible for the parent purchase | | `upsellAmountTooLow` | 422 | The upsell amount is below the minimum chargeable value | ##### Webhooks | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `webhookEndpointNotFound` | 404 | Webhook endpoint not found | | `webhookEventNotFound` | 404 | Webhook event not found | | `webhookDispatchNotFound` | 404 | Webhook dispatch not found | | `webhookUnknownResource` | 422 | Unknown event resource — cannot build payload | | `webhookSigningDisabled` | 422 | This endpoint has signing disabled (no secret) | ##### Domains | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `domainIdIsInvalid` | 404 | Domain ID is invalid (not found for this company) | | `domainAlreadyExists` | 409 | Domain already exists | | `domainLimitExceeded` | 409 | Domain limit exceeded | | `domainUpdateFailed` | 400 | Domain update failed | | `domainEdgeProvisionFailed` | 422 | Domain edge provisioning failed | ##### Permissões da conta Quando a conta tem uma lista de permissões (recursos e operações liberados), uma chave de API que tenta sair dela recebe: | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `resourceNotAllowed` | 403 | This resource is not allowed for this client | | `operationMethodNotAllowed` | 403 | This operation is not allowed for this client | ##### Exports | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `exportNotFound` | 404 | The export was not found | | `exportNotCancelable` | 422 | This export can no longer be canceled | | `exportTooLarge` | 422 | The export exceeds the maximum allowed row count | | `exportTooManyOpen` | 429 | Too many exports are still open for this account — at most 3 can be pending or processing at once; wait for one to finish or cancel one | ##### Assets (upload) | `error.code` | HTTP | Mensagem | |--------------|-----:|----------| | `assetNotFound` | 404 | The asset was not found | | `assetUploadIncomplete` | 422 | No uploaded object was found for this asset yet | | `assetContentTypeMismatch` | 422 | The uploaded file is not of the declared contentType; it was discarded (suba de novo com o `Content-Type` de `upload.headers`) | | `assetContentTypeUnverified` | 503 | The storage could not report the uploaded file type yet; nothing was discarded (chame o `confirm` de novo — não precisa subir o arquivo outra vez) | | `assetCompanyUnresolved` | 422 | Could not resolve the company for this upload | | `storageNotConfigured` | 503 | Object storage is not configured (indisponibilidade temporária) | --- #### Como tratar erros (recomendado) ```js const res = await fetch(url, { headers: { SelectKey: process.env.SELECTWIN_API_KEY } }); if (!res.ok) { const { error } = await res.json(); switch (error.code) { // ← key no code, nunca na message case 'customerNotFound': return handleMissingCustomer(); case 'balanceIsInsufficient': return promptTopUp(); case 'paymentDeclined': return promptAnotherCard(); default: if (error.statusCode === 429) return retryWithBackoff(); // rate limit if (error.statusCode >= 500) return retryWithBackoff(); // transitório throw new Error(`${error.code}: ${error.message}`); } } ``` > Esta lista cobre os erros mais comuns voltados ao desenvolvedor para cada recurso público. Outros > códigos específicos podem aparecer; trate sempre o `default` pelo `error.statusCode`/`category`. --- ## Idempotência Fonte: https://selectwin.io/docs/reference/idempotencia #### Visão Geral A API Selectwin suporta chaves de idempotência (Idempotency Keys) para garantir que requisições sensíveis não sejam processadas mais de uma vez, mesmo em caso de falhas de rede, timeouts ou retentativas de requisição. Esta funcionalidade é essencial para operações críticas como criação de transações, saques ou quaisquer operações que não devam ser duplicadas. #### O que é Idempotência? Na teoria de sistemas, uma operação é considerada idempotente quando produz o mesmo resultado independentemente de ser executada uma ou múltiplas vezes. No contexto de APIs, isso significa que a mesma requisição pode ser repetida sem efeitos colaterais indesejados. Por exemplo, se você envia uma requisição para criar uma transação de R$ 100, e ocorre um timeout antes de receber a resposta, você não sabe se a transação foi processada. Com idempotência, você pode enviar a mesma requisição novamente com segurança, garantindo que apenas uma única transação será criada. #### Como Utilizar Chaves de Idempotência Para usar esta funcionalidade, inclua um cabeçalho `X-Idempotency-Key` com um valor único em suas requisições: ```bash curl -X POST "https://api.selectwin.io/v1/transactions" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -H "X-Idempotency-Key: 2dfc6a63-5df5-4900-baca-e49d91ef393e" \ -d '{ "amount": 10000, ... Data }' ``` --- #### Requisitos para Chaves de Idempotência As chaves de idempotência devem seguir estas regras: 1. **Unicidade**: Cada chave deve ser única para cada operação distinta 2. **Formato**: Recomendamos utilizar UUIDs v4 (36 caracteres) 3. **Tamanho**: Mínimo de 10 e máximo de 255 caracteres 4. **Caracteres**: Alfanuméricos, hifens e underscores apenas 5. **Persistência**: A mesma chave deve ser reutilizada em caso de retry da mesma operação --- #### Comportamento da API com Chaves de Idempotência ##### Primeira Requisição Quando uma requisição com uma chave de idempotência nunca vista antes é recebida: 1. A API processa a requisição normalmente 2. A resposta é armazenada associada à chave de idempotência 3. Um código de status apropriado é retornado (geralmente 200 OK ou 201 Created) ##### Requisições Subsequentes com a Mesma Chave Quando uma requisição é recebida com uma chave de idempotência já utilizada: 1. A API verifica se o corpo da requisição é idêntico ao da requisição original 2. Se o corpo for idêntico **e** a requisição original já foi concluída, a API retorna a resposta armazenada (com o status original, ex.: `201 Created`), sem executar a operação novamente 3. Se o corpo for diferente, a API retorna **409 (Conflict)** com `code: "idempotencyMismatch"` 4. Se a requisição original ainda estiver em processamento, a API retorna **409 (Conflict)** com `code: "idempotencyInProgress"` — aguarde e repita com a mesma chave 5. Se a requisição original começou há mais de 10 minutos e nunca terminou de responder (falha no meio do caminho), a API retorna **409 (Conflict)** com `code: "idempotencyOutcomeUnknown"`: ela **pode** ter executado. Confira o recurso antes de repetir e, se for repetir, use uma **nova** chave 6. Uma chave é esquecida **24 horas** depois do primeiro uso: a mesma chave depois disso é tratada como nova ##### Exemplo de Resposta para Requisição com Corpo Divergente (409) ```json { "error": { "status": "Conflict", "statusCode": 409, "category": "client", "code": "idempotencyMismatch", "message": "This idempotency key was already used with a different request payload.", "resource": "server" } } ``` Quando a operação original ainda está em andamento, o `code` é `idempotencyInProgress` com a mensagem "A request with this idempotency key is already being processed." (mesmo `status`/`statusCode`/`category`). ##### Tempo de Expiração As chaves de idempotência e suas respostas associadas são mantidas por 24 horas. Após este período, a mesma chave pode ser reutilizada e será tratada como uma nova requisição. --- #### Implementação de Idempotência no Cliente ##### Geração de Chaves de Idempotência É importante gerar chaves únicas para cada operação. Aqui estão exemplos em diferentes linguagens: ##### As chaves de idempotência funcionam em todos os verbos HTTP? As chaves de idempotência são principalmente utilizadas em operações POST e PUT que modificam recursos. Elas não são necessárias para operações GET, DELETE ou HEAD, que já são naturalmente idempotentes. --- ## Paginação Fonte: https://selectwin.io/docs/reference/paginacao #### Visão Geral A API Selectwin utiliza um sistema de paginação consistente em todos os endpoints que retornam listas de recursos. Este mecanismo permite controlar a quantidade de dados retornados em cada requisição, facilitando o gerenciamento de conjuntos grandes de informações e melhorando o desempenho das aplicações. Este documento detalha como funciona a paginação na API, como utilizar os parâmetros de controle e como interpretar os metadados de paginação nas respostas. --- #### Estrutura da Resposta Paginada Todas as respostas paginadas da API Selectwin seguem uma estrutura padrão no nível raiz (o corpo da resposta HTTP é o objeto paginado diretamente), como mostrado abaixo: ```json { "offset": 0, "limit": 20, "total": 1, "hasMore": false, "page": { "current": 1, "total": 1, "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 } }, "data": [ { "id": "wall_01hqzvabc", "name": "Nubank", "bankName": "260 - Nu Pagamentos", "bankCode": "260", "holderName": "João Santos", "routingNumber": "0001", "accountNumber": "11111111-5", "primary": true, "enabled": true, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/wallets", "method": "GET", "description": "List all wallets." }, "create": { "href": "https://api.selectwin.io/v1/wallets", "method": "POST", "description": "Create a new wallet." } } } ``` **Nota:** Os itens dentro de `data` são projeções leves específicas do recurso (nem todos os campos do objeto completo — por exemplo, a lista de carteiras **não** inclui `accountType` nos itens, apenas o objeto individual via `GET /v1/wallets/{walletId}`). O objeto `merchant` e os `_links` HATEOAS estão presentes na maioria das respostas paginadas de listagem padrão. Endpoints `list-all` retornam um **array direto** (sem envelope de paginação, `merchant` ou `_links`), e `analytics` retorna objetos customizados próprios. ##### Campos da Resposta Paginada | Campo | Tipo | Descrição | |-------|------|-----------| | `offset` | integer | Posição inicial dos resultados retornados na coleção completa | | `limit` | integer | Quantidade máxima de registros retornados por página | | `total` | integer | Total de registros disponíveis para a consulta | | `hasMore` | boolean | Indica se existem mais resultados além dos retornados na página atual | | `page.current` | integer | Número da página atual (começando em 1) | | `page.total` | integer | Total de páginas disponíveis | | `page.offset.first` | integer | Offset da primeira página de resultados (sempre 0) | | `page.offset.prev` | integer | Offset da página anterior (`0` quando já está na primeira página) | | `page.offset.next` | integer | Offset da próxima página (igual ao `offset` atual quando já está na última página) | | `page.offset.last` | integer | Offset da última página de resultados | | `data` | array | Array contendo os registros (projeções leves) retornados pela consulta | | `merchant` | object | Merchant associado (contém `name`, `merchantId`, `isSubAccount`; presente na maioria das listas padrão) | | `_links` | object | Links HATEOAS (self, create, next, etc.) para descoberta e navegação | --- #### Parâmetros de Paginação Ao fazer requisições para endpoints que suportam paginação, você pode controlar os resultados usando os seguintes parâmetros de consulta: | Parâmetro | Tipo | Padrão | Descrição | |-----------|------|---------|-----------| | `limit` | integer | 20 | Define o número máximo de registros a serem retornados por página (mínimo: 1, máximo: 100). Alguns recursos legados usam um padrão de `10`. | | `offset` | integer | 0 | Define a posição inicial a partir da qual os registros serão retornados | | `sort` | string | — | Expressão de ordenação (ex.: `-createdAt`). A ordem padrão varia por recurso. | --- #### Combinando Paginação com Filtros Você pode combinar parâmetros de paginação com filtros específicos do recurso: ```javascript // Exemplo: Paginação com filtros const axios = require('axios'); async function listFilteredTransactions() { const response = await axios({ method: 'get', url: 'https://api.selectwin.io/v1/transactions', headers: { 'SelectKey': 'sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ' }, params: { offset: 0, limit: 20, sort: 'descending', ... Outros parâmetros } }); console.log(`Encontradas ${response.data.total} transações com sucesso em janeiro de 2023`); console.log(`Exibindo ${response.data.data.length} transações na página atual`); return response.data; } ``` --- #### Perguntas Frequentes ##### Os resultados são sempre retornados na mesma ordem? A ordem padrão varia de acordo com o recurso, mas geralmente é por `sort` data de criação decrescente. Para garantir consistência na paginação, use o parâmetro de ordenação específico do recurso. ##### Posso contar com o número total de registros (`total`) para calcular manualmente os offsets? Sim, o valor de `total` fornecido na resposta é preciso e pode ser usado para cálculos manuais de paginação. Porém, recomendamos usar os valores `page.offset.*` fornecidos na resposta para navegação. ##### A paginação afeta os limites de rate da API? Sim, cada requisição paginada conta para o seu limite de rate. Ao implementar iteração por todas as páginas, considere adicionar intervalos entre as requisições para evitar atingir limites de rate. --- ## Limites de uso Fonte: https://selectwin.io/docs/reference/limites #### Visão Geral A Selectwin implementa limites de uso na API para garantir a estabilidade, disponibilidade e desempenho consistente da plataforma para todos os integradores. Este documento detalha os diferentes tipos de limitações, como monitorá-las e as melhores práticas para evitar problemas relacionados a esses limites. --- #### Rate Limits O rate limiting é aplicado a todas as requisições à API Selectwin, limitando o número de chamadas que podem ser feitas em um determinado período. Estes limites protegem a infraestrutura contra sobrecargas, garantem uma distribuição justa de recursos e previnem abusos. A contagem é feita por **IP de origem** dentro de uma janela de tempo configurável. ##### Como funciona O limite é aplicado em **dois níveis**: - Um limite **padrão**, aplicado à maioria dos endpoints. - Um limite **mais estrito** para as operações de autenticação. Os valores exatos podem ser ajustados pela operação e para a sua empresa mediante solicitação. Ao exceder, a API responde `429 Too Many Requests`; o cabeçalho HTTP `Retry-After` (em segundos) indica quando tentar novamente. ##### Limites por Rota (representativo — não normativo) A tabela a seguir é **apenas ilustrativa** para dar uma noção de ordem de grandeza; os valores reais seguem o esquema de dois níveis descrito acima e podem diferir. Operações de **autenticação** recebem o limite mais estrito. | Grupo | Rota (exemplo) | Método | Limite/min | Bloqueio após excesso | |-------|----------------|--------|-----------:|----------------------| | Transações | `/v1/transactions` (listar/ler) | GET | 60 | — | | Transações | `/v1/transactions` (criar) | POST | 30 | 30s | | Transações | `/v1/transactions/{id}/refund` · `/capture` | POST | 10 | 2 min | | Transações | `/v1/transactions/{id}/dispute` | POST | 5 | 5 min | | Clientes / Produtos / Assinaturas | listar/ler | GET | 60 | — | | Carteiras / Saques / Cartões / Endereços / Cupons / Webhooks | listar/ler | GET | 30 | — | | Saques | `/v1/withdrawals` (criar) | POST | 10 | 60s | | Finance | `/v1/finance/balance` | GET | 30 | — | | Recebíveis (analytics) | `/v1/receivables/analytics` | GET | 15–20 | — | > A tabela acima é representativa; cada rota tem seu próprio alvo. Os limites podem ser ajustados para a sua empresa mediante solicitação (veja FAQ). ##### Resposta ao Exceder o Limite Ao ultrapassar um limite, a API responde com **HTTP `429 Too Many Requests`** (envelope de erro padrão, `category: "rate_limit"`). Aguarde o tempo indicado no cabeçalho `Retry-After` (segundos) antes de repetir a chamada e implemente *backoff* exponencial. O contador é por IP de origem e reinicia ao fim da janela. --- #### Melhores Práticas ##### 1. Use Webhooks em vez de Polling O uso de polling (consultas repetidas para verificar mudanças) é desencorajado e pode levar a problemas com rate limits. Em vez disso, utilize webhooks para ser notificado automaticamente sobre mudanças: ##### 2. Priorize Recursos Críticos Identifique quais operações são críticas para seu negócio e reserve capacidade para elas: ##### 3. Otimize o Tamanho das Requisições Reduza o tamanho das requisições para melhorar o desempenho e evitar problemas com limites de payload: - Remova campos desnecessários - Utilize compressão quando disponível (HTTP gzip) - Otimize o formato de imagens e arquivos anexados - Divida grandes volumes de dados em lotes menores #### Perguntas Frequentes ##### Posso solicitar limites personalizados? Sim, podemos aumentar os limites de uso da API conforme a necessidade da sua empresa. Entre em contato com nosso suporte técnico para discutir suas necessidades específicas e encontrar a melhor solução. ##### Os limites são os mesmos nos ambientes de sandbox e produção? Sim, os ambientes de sandbox e produção compartilham os mesmos limites de API para garantir que você possa testar adequadamente a integração com as mesmas condições que encontrará em produção. ##### Há diferença de limites entre horários de pico e fora de pico? Não, os limites são aplicados consistentemente independentemente do horário. No entanto, recomendamos agendar operações em lote, como relatórios e sincronizações, para horários de menor movimento (geralmente durante a madrugada). ##### Como fazer para solicitar o aumento de limite de requisições para minha empresa? Para solicitar o aumento de limite de requisições, entre em contato com nosso suporte técnico através do e-mail help@selectwin.io ou pelo Painel Administrativo. Nossa equipe avaliará sua solicitação e fornecerá as orientações necessárias. --- ## Proibição de Polling Fonte: https://selectwin.io/docs/reference/proibicao-de-polling #### Visão Geral A API Selectwin proíbe explicitamente o uso de polling (consultas recorrentes e frequentes) como método para obter atualizações de status de recursos. Esta política foi implementada para garantir a estabilidade, eficiência e escalabilidade da plataforma, bem como para proporcionar uma experiência consistente para todos os integradores. --- #### O que é Polling? Polling é a prática de verificar repetidamente o status de um recurso em intervalos regulares através de requisições consecutivas à API. Por exemplo: ```javascript // Exemplo de polling (PRÁTICA PROIBIDA) async function verificarStatusTransacao(transactionId) { let status = 'processing'; while (status === 'processing') { // Espera 1 segundo await new Promise(resolve => setTimeout(resolve, 1000)); // Faz uma nova requisição para verificar o status const response = await fetch(`https://api.selectwin.io/v1/transactions/${transactionId}`, { headers: { 'SelectKey': 'sua_chave_api' } }); const data = await response.json(); status = data.status; } return status; } ``` --- #### Por que o Polling é Proibido? ##### 1. Consumo Excessivo de Recursos O polling gera um volume significativo de requisições, muitas das quais não produzem alterações no estado do recurso. Este padrão: - Sobrecarrega os servidores com requisições desnecessárias - Reduz a capacidade de atendimento para outras solicitações legítimas - Aumenta os custos de infraestrutura sem benefício proporcional ##### 2. Escalabilidade Comprometida À medida que o número de usuários e transações cresce, o impacto negativo do polling se multiplica: - Cada cliente que utiliza polling pode gerar centenas de requisições desnecessárias - Em horários de pico, o efeito cumulativo pode degradar a performance para todos os usuários - Torna-se insustentável manter a qualidade de serviço sob volume elevado ##### 3. Rate Limiting Inevitável O uso de polling frequentemente resulta em: - Atingir rapidamente os limites de taxa (rate limits) impostos pela API - Aumento das requisições rejeitadas com código de status HTTP 429 (Too Many Requests) - Degradação da experiência para o usuário final ##### 4. Ineficiência Operacional O polling é inerentemente ineficiente pois: - Consome largura de banda desnecessariamente - Aumenta a latência da aplicação - Desperdiça ciclos de CPU tanto no cliente quanto no servidor - Eleva o consumo de energia, tornando-se menos sustentável #### Alternativas Recomendadas A Selectwin oferece alternativas mais eficientes para monitorar mudanças de estado: ##### 1. Webhooks (Método Recomendado) Os webhooks permitem receber notificações em tempo real quando ocorrem alterações no estado de um recurso: ```javascript // Exemplo de rota Express para receber webhooks const express = require('express'); const app = express(); app.post('/webhook/transactions', express.json(), (req, res) => { const event = req.body; // Verifica a assinatura do webhook (importante para segurança) // O header é enviado no formato: X-Selectwin-Signature: sha256= if (!isValidSignature(req.headers['x-selectwin-signature'], event, webhookSecret)) { return res.status(401).send('Assinatura inválida'); } // Processa o evento com base no tipo switch (event.type) { case 'transaction.approved': // Lógica para transação aprovada break; case 'transaction.dispute': // Lógica para transação em disputa break; case 'transaction.refunded': // Lógica para transação estornada break; } // Sempre retorne 200 para confirmar o recebimento res.status(200).send('Webhook recebido com sucesso'); }); ``` ###### Configuração de Webhooks Para configurar um endpoint de webhook: ```bash curl -X POST "https://api.selectwin.io/v1/webhooks" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Minha loja", "endpoint": "https://webhooks.mydomain.com/selectwin", "headerAuthorization": "Basic SEU_TOKEN_AQUI", "headerAuthorizationType": "Basic", "events": ["transaction.approved", "customer.updated"] }' ``` - `events` lista os tipos de evento que o endpoint recebe. Para receber **todos**, use `"forceActive": true` no lugar de `events`. - `headerAuthorization` é enviado, exatamente como você o escreveu, no cabeçalho `Authorization` de cada entrega (o tipo não é acrescentado na frente). `headerAuthorizationType` registra o tipo. Os dois são opcionais e nunca voltam nas respostas. ##### 2. Notificação por E-mail ou SMS (Para Baixo Volume) Para casos de baixo volume de operações ou notificações críticas: - Configure notificações por e-mail ou SMS através do painel administrativo - Utilize essas notificações como gatilho para verificação pontual da API - Não dependa exclusivamente deste método para processamento automático --- #### Recursos Adicionais - [Guia completo de webhooks](/docs/guide/webhook-endpoints/overview) - [Verificar a assinatura (HMAC)](/docs/guide/webhook-events/verifying-signatures) - [Catálogo de eventos](/docs/guide/webhook-events/event-catalog) --- ## Metadados Fonte: https://selectwin.io/docs/reference/metadata #### Visão Geral A funcionalidade de **Metadata** da Selectwin API permite anexar informações adicionais personalizadas aos principais recursos da plataforma. Esta característica oferece flexibilidade para armazenar dados específicos do seu negócio junto aos objetos do sistema, facilitando integrações, rastreamento e personalização dos processos de pagamento. #### O que é Metadata? Metadata são pares de chave-valor que você pode associar a objetos como transações, clientes, cartões, saques e outros recursos da API. Estas informações adicionais não afetam o comportamento padrão dos objetos, mas permitem: 1. **Personalização de Dados**: Armazenar informações específicas do seu negócio 2. **Integração Simplificada**: Facilitar o mapeamento entre os sistemas 3. **Rastreamento Avançado**: Adicionar identificadores para análise e reconciliação 4. **Segmentação e Categorização**: Organizar recursos com base em critérios personalizados 5. **Contexto Adicional**: Fornecer dados auxiliares para futuras referências #### Estrutura e Limitações O objeto de metadata é sempre incluído como um atributo chamado `metadata` nos recursos que o suportam: ```json { // ... outros atributos do objeto ... "metadata": { "order_id": "PED-12345", "customer_reference": "CLI-ABC-789", "campaign_source": "email_july", "internal_department": "sales_northeast" } } ``` ##### Limites O campo `metadata` é um objeto JSON livre — os valores podem ser texto, número, booleano, `null`, objeto ou lista. O **tamanho** tem limite, conferido em toda criação e atualização: | Aspecto | Limite | |---------|--------| | Chaves | até **50** no primeiro nível | | Nome da chave | até **40** caracteres | | Cada valor | até **500** caracteres — o texto, quando é uma string; o JSON do valor, nos demais tipos | | Objeto inteiro | até **8 KiB** (8 192 bytes do JSON, em UTF-8 — um acento conta 2 bytes) | Fora do limite, a requisição é recusada com o erro de validação de contrato — **`400 invalidParameters`** —, e `error.params` diz o campo e o motivo. O valor nunca é cortado em silêncio: ```json { "error": { "statusCode": 400, "category": "validation", "code": "invalidParameters", "params": [{ "metadata[observacao]": "value must have at most 500 characters" }] } } ``` | Motivo (`params`) | Quando | |-------------------|--------| | `{ "metadata": "must have at most 8192 bytes as JSON" }` | o objeto inteiro passou de 8 KiB | | `{ "metadata": "must have at most 50 keys" }` | mais de 50 chaves | | `{ "metadata": "keys must have at most 40 characters (\"…\")" }` | uma chave com mais de 40 caracteres (o começo dela vem na mensagem) | | `{ "metadata[]": "value must have at most 500 characters" }` | o valor daquela chave passou de 500 caracteres | O limite vale para o `metadata` de todo recurso (clientes, endereços, transações e seus itens, assinaturas e seus itens, produtos/variantes, links de pagamento, endpoints de webhook, registros de uso). Duas exceções, com números maiores porque o valor é configuração de estrutura conhecida: o `metadata` de uma **template** e de um **disparo** do motor de apps (o vínculo HSM do WhatsApp, a mídia) aceita até 8 KiB por valor e 16 KiB no total. O `metadata` de uma **sessão de checkout** (e o das linhas avulsas dela) é rascunho e aceita mais (até 100 chaves e 16 KiB por objeto), mas **desce para a cobrança só dentro do limite acima**: na transação (ou na assinatura) que a sessão produz entram, na ordem, as chaves que cabem; a que não cabe fica só na sessão e a cobrança sai sem ela — nada é recusado no pagamento do comprador. As chaves `consent`, `attribution` e `cookies` do `metadata` da sessão são reservadas à plataforma: não descem para a cobrança e são as únicas que a página do checkout escreve. #### Como Utilizar Metadata ##### Adicionando Metadata na Criação Você pode incluir metadata ao criar qualquer recurso que o suporte: ```javascript // Exemplo: Criando uma transação com metadata const axios = require('axios'); async function createTransactionWithMetadata() { const response = await axios({ method: 'post', url: 'https://api.selectwin.io/v1/transactions', headers: { 'SelectKey': 'sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ', 'Content-Type': 'application/json' }, data: { ... metadata: { order_id: 'ORDER-123456', customer_reference: 'CUST-789', product_ids: '10,14,15', shipping_address_id: 'ADR-456', promo_code: 'SUMMER10' } } }); return response.data; } ``` #### Casos de Uso ##### 1. Integração com E-commerce Use metadata para rastrear informações do pedido: ```javascript metadata: { order_number: "PO-28471", cart_id: "CART-17283", store_id: "STORE-05", shipping_method: "express", items_count: "3", source: "mobile_app" } ``` ##### 2. Reconciliação Financeira Facilite a reconciliação adicionando identificadores do seu sistema financeiro: ```javascript metadata: { invoice_id: "INV-20230915-001", cost_center: "CC-MARKETING", project_code: "PROJ-2023-Q3", department: "sales", account_code: "AC-4172" } ``` ##### 3. Segmentação de Clientes Adicione informações para segmentação e análise: ```javascript metadata: { customer_segment: "premium", acquisition_channel: "referral", lifetime_value: "15240", preferred_category: "electronics", language_preference: "pt-BR", subscription_tier: "pro" } ``` ##### 4. Campanhas de Marketing Rastreie a origem e desempenho das campanhas: ```javascript metadata: { campaign_id: "SUMMER2023", utm_source: "facebook", utm_medium: "cpc", utm_campaign: "back_to_school", discount_code: "SUMMER10", affiliate_id: "AFF123" } ``` ##### 5. Logística e Entrega Acompanhe informações de entrega nos pagamentos: ```javascript metadata: { tracking_number: "JB437291082", carrier: "fedex", estimated_delivery: "2023-09-20", package_weight: "1.5kg", delivery_instructions: "leave_at_door", warehouse_id: "WH-SOUTH" } ``` #### Melhores Práticas ##### 1. Nomenclatura Consistente Estabeleça um padrão de nomenclatura para suas chaves de metadata: - Use `snake_case` ou `camelCase` consistentemente - Evite abreviações obscuras - Seja descritivo mas conciso - Agrupe campos relacionados com prefixos comuns (ex: `shipping_*`, `invoice_*`) ##### 2. Dados Estruturados vs. Não Estruturados Um valor pode ser um objeto ou uma lista, mas cada valor tem o limite de 500 caracteres (o JSON dele). Para dados maiores, prefira guardar no seu sistema e deixar no metadata só o identificador. Duas abordagens comuns: 1. **Achatamento**: Use prefixos para representar estrutura ``` "customer_address_street": "Av. Paulista", "customer_address_number": "1000", "customer_address_city": "São Paulo" ``` 2. **Serialização**: Para estruturas mais complexas, serialize como JSON ``` "customer_address": "{\"street\":\"Av. Paulista\",\"number\":\"1000\",\"city\":\"São Paulo\"}" ``` ##### 3. Padronização de Valores Para campos que representam categorias ou estados: - Use enumerações consistentes (ex: `status: ["active", "inactive", "pending"]`) - Defina um conjunto fechado de valores possíveis - Documente internamente o significado de cada valor ##### 4. O que Não Armazenar em Metadata Evite armazenar: - **Dados sensíveis**: Informações de cartão de crédito, senhas, tokens - **Informações pessoais sensíveis**: Evite dados que aumentem exigências de conformidade - **Dados muito volumosos**: O objeto tem limite de 8 KiB (veja [Limites](#limites)) - **Informações temporárias**: A metadata deve ser relevante por toda a vida do objeto - **Dados críticos para processamento**: Não confie na metadata para lógica principal ##### 5. Backup de Metadata Não dependa apenas da metadata na Selectwin como fonte primária: - Mantenha uma cópia das informações críticas em seu sistema - Considere implementar recuperação de informações em caso de problemas - Faça download periódico dos dados para análise local #### Webhooks e Metadata Quando recursos são atualizados, incluindo suas informações de metadata, webhooks são disparados. Você pode utilizar estes eventos para manter seus sistemas sincronizados: ```json // Exemplo de payload de webhook com metadata (estrutura real do evento) { "id": "wbh_123456789", "type": "transaction.approved", "source": "automatic", "payload": { "object": { "id": "tra_987654321", "amount": 10000, "status": "approved", "metadata": { "order_id": "ORDER-123456", "shipping_status": "dispatched", "tracking_code": "BR123456789" } } }, "createdAt": "2026-04-12T17:56:33.000Z" } ``` > O objeto do recurso é entregue em **`payload.object`** (não em `data`). #### Perguntas Frequentes ##### A metadata é visível para os clientes finais? Não. A metadata é visível apenas para sua empresa através das APIs e do Dashboard administrativo. Os clientes finais não têm acesso a essas informações. ##### Posso atualizar apenas um campo de metadata sem afetar os outros? Não diretamente. A API substitui todo o objeto de metadata ao invés de mesclar. Para atualizar campos individuais, você deve primeiro obter o objeto atual, modificar o campo específico e enviar o objeto completo atualizado. ##### É possível remover um campo específico de metadata? Sim, basta enviar uma atualização com todo o objeto de metadata, omitindo o campo que deseja remover. Alternativamente, você pode definir o valor do campo específico como uma string vazia. ##### A metadata afeta as operações de processamento de pagamento? Não. A metadata é puramente informativa e não afeta o comportamento funcional do processamento de pagamentos, autorizações, capturas ou outros processos principais. ##### Posso usar metadata para identificar transações duplicadas? Sim, essa é uma prática comum. Ao fornecer identificadores consistentes em seu sistema dentro da metadata (como IDs de pedido), você pode consultar a API para verificar se uma transação já foi processada antes de tentar criá-la novamente. ##### A metadata é incluída em relatórios e exportações? Sim. Todos os relatórios e exportações de dados da plataforma incluem os campos de metadata associados aos recursos, facilitando a análise e reconciliação. --- ## Versionamento & Changelog Fonte: https://selectwin.io/docs/reference/versionamento #### Versão da API A versão está embutida na URL base: ``` https://api.selectwin.io/v1 ``` Enquanto você usar `/v1`, a API mantém **compatibilidade retroativa** — não removemos campos nem endpoints existentes dentro da mesma versão maior. #### O que é considerado uma mudança compatível (não-quebra) Estas mudanças podem acontecer **a qualquer momento** dentro de `/v1`. Sua integração deve tolerá-las: - **Novos endpoints** e novos recursos. - **Novos campos opcionais** em requisições. - **Novos campos** em respostas (não remova/ignore campos que não conhece). - **Novos valores** em enums (ex.: novos `status`, novos `type` de webhook). Trate valores desconhecidos com um caminho padrão. - **Novos tipos de evento** de webhook. Ignore com segurança os que você não trata. - Reordenação de chaves em objetos JSON. #### O que é uma mudança quebrável (breaking) Mudanças quebráveis **não** são feitas em `/v1`. Quando necessárias, são lançadas em uma **nova versão maior** (ex.: `/v2`), com período de transição e comunicação prévia. Exemplos: - Remover ou renomear um campo/endpoint. - Mudar o tipo de um campo ou o significado de um valor. - Tornar obrigatório um parâmetro antes opcional. #### Boas práticas para sobreviver a mudanças 1. **Trate IDs como opacos** — não dependa de comprimento/prefixo (veja [Convenções](/docs/reference/convencoes#identificadores-ids)). 2. **Ignore campos desconhecidos** em respostas, em vez de falhar. 3. **Tenha um caminho padrão** para `status`/`type` novos. 4. **Assine apenas os eventos** de webhook que você processa. 5. **Use `error.code`** (estável) para lógica, nunca a `message` (texto). #### Depreciação Quando um recurso for descontinuado, comunicaremos com antecedência pelos canais oficiais e, quando aplicável, marcaremos o recurso como *deprecated* na referência OpenAPI antes da remoção (que só ocorre em uma nova versão maior). #### Changelog As mudanças relevantes da API são publicadas no changelog oficial. Consulte a documentação pública para o histórico de versões e novidades. Para dúvidas sobre uma mudança específica, contate `help@selectwin.io`. --- ## Segurança e Conformidade PCI DSS Fonte: https://selectwin.io/docs/reference/seguranca-pci #### Visão Geral A Selectwin **tokeniza os cartões**: a sua integração trabalha com uma referência ao cartão, nunca com o número dele. Esta página explica como manter os dados do cartão fora dos seus sistemas e o que continua sendo responsabilidade da sua empresa. --- #### O que é PCI DSS? O PCI DSS (Payment Card Industry Data Security Standard) é o conjunto de requisitos de segurança estabelecido pelas bandeiras (Visa, Mastercard, American Express, Discover e JCB) para quem processa, armazena ou transmite dados de cartão. A conformidade é obrigatória para qualquer organização que lide com esses dados, independentemente do tamanho ou do volume de transações. --- #### Tokenização de Cartões Em vez de trafegar e guardar dados de cartão, a sua integração trabalha com **tokens** — identificadores que não têm valor fora do ambiente Selectwin. ##### Como funciona 1. O cartão chega à Selectwin pelo navegador do comprador ([Selectwin Elements ou integração direta](/docs/guide/cards/tokenization)), pela página hospedada ou pela nossa API, sempre em canal seguro (TLS 1.2+) 2. A Selectwin tokeniza o cartão 3. Você recebe e guarda apenas a referência (`card_...`) 4. Cobranças futuras usam a referência, nunca o número do cartão ##### Benefícios - **Nenhum dado de cartão do seu lado**: a sua integração guarda só a referência - **Menor risco**: um token vazado não é utilizável fora do ecossistema Selectwin - **Recorrência e 1-clique** sem guardar dado sensível --- #### Como manter dados de cartão fora dos seus servidores | Caminho | Por onde passa o número do cartão | |---|---| | Checkout hospedado ([Payment Links](/docs/guide/checkouts/payment-links), [Sessions](/docs/guide/checkouts/sessions)) | só a página da Selectwin | | [Selectwin Elements](/docs/guide/cards/tokenization) — os campos do cartão num **iframe da Selectwin** dentro da sua página | só o iframe da Selectwin | | [Integração direta](/docs/guide/cards/tokenization) — o seu JavaScript envia o cartão à Selectwin | o navegador (o seu JS), nunca o seu servidor | | [Criar Cartão](/docs/guide/cards/create) com os dados completos pela API | o seu servidor | - **Checkout hospedado** — o comprador informa os dados na página hospedada pela Selectwin, e o seu servidor nunca vê o cartão. - **Selectwin Elements** — os campos do cartão num iframe da Selectwin dentro da sua página (como o Stripe Elements); `createToken` devolve um token de uso único, e `payment.card.token` ou `POST /v1/cards { token }` o transformam num `card_...`. Para apps sem iframe, a integração direta da mesma página. - **Cartão pela API** — [Criar Cartão](/docs/guide/cards/create) com os dados completos devolve um `card_...`; é o caminho em que o número do cartão passa pelo seu servidor. Para página de pagamento própria, use o **Selectwin Elements**. O caminho dos dados completos existe para integrações servidor-a-servidor. --- #### Modelo de Responsabilidade Compartilhada ##### Responsabilidades da Selectwin - Segurança do ambiente da API e das páginas hospedadas - Tokenização dos cartões - Criptografia dos dados em trânsito (TLS 1.2+) - Autenticação e autorização de acesso à API - Monitoramento e resposta a incidentes - Gestão das chaves criptográficas do nosso ambiente ##### Responsabilidades do Cliente (sua empresa) - Implementação segura da integração com a nossa API - Segurança dos ambientes e sistemas que conversam com a API - Guarda adequada das chaves de API (nunca no código-fonte ou no front-end) - Treinamento da equipe em práticas de segurança - Acesso a dados sensíveis restrito a quem precisa - Controles de segurança adequados na sua aplicação - A conformidade PCI DSS dos seus próprios sistemas, se eles capturarem ou transmitirem dados de cartão --- #### Proteção de Dados em Trânsito Todas as comunicações com a API Selectwin são protegidas por TLS 1.2 ou superior: - Certificados com assinatura SHA-256 - Apenas cifras fortes, com Perfect Forward Secrecy - HSTS (HTTP Strict Transport Security) habilitado - Proteção contra downgrade e MITM (Man-in-the-Middle) --- #### Prevenção de Fraudes A Selectwin aplica, em toda cobrança com cartão: - **Análise antifraude** por transação - **Verificação do código de segurança (CVV)** - **3D Secure 2.0** como autenticação adicional quando aplicável --- #### Melhores Práticas de Segurança ##### Para sua aplicação 1. **Use TLS 1.2 ou superior** em todos os seus serviços 2. **Proteja suas chaves de API**: variáveis de ambiente ou cofre, nunca no código-fonte nem no navegador 3. **Autenticação forte** para acesso administrativo 4. **Mantenha sistemas e bibliotecas atualizados** 5. **Registre e monitore** acessos e falhas 6. **Menor privilégio** em todos os sistemas 7. **Teste sua própria superfície** regularmente ##### Para integradores de e-commerce 1. **Prefira o checkout hospedado** — evita lidar com dado de cartão 2. **Valide entradas** rigorosamente 3. **Proteja contra XSS e CSRF** 4. **Configure CSP** para mitigar injeção 5. **Configure CORS** corretamente 6. **Guarde só o necessário** sobre cada transação --- #### Notificação de Incidentes Se identificar uma vulnerabilidade ou incidente relacionado à API Selectwin: 1. Escreva imediatamente para security@selectwin.io 2. Descreva o problema com precisão 3. Inclua, se possível, os passos para reproduzir 4. Não divulgue publicamente antes da correção A Selectwin segue um processo de divulgação responsável e reconhece a sua contribuição após a resolução. --- #### Recursos Adicionais - [Guia da PCI Security Standards Council](https://www.pcisecuritystandards.org/) #### Contato Dúvidas sobre segurança ou conformidade: security@selectwin.io --- ## Orquestração de pagamentos Fonte: https://selectwin.io/docs/reference/payment-orchestration #### Visão Geral A Selectwin cuida de todo o processamento do pagamento por trás de uma única integração: você envia **uma** requisição e recebe **um** resultado, com cartão, PIX e boleto sob o mesmo contrato, os mesmos objetos e os mesmos webhooks. Tudo o que acontece entre a sua chamada e o resultado é responsabilidade nossa e não muda nada no seu código. --- #### O que você ganha - **Uma integração para todos os métodos** — crédito, PIX e boleto usam o mesmo `POST /v1/transactions`, o mesmo objeto de transação e a mesma família de eventos. - **Resultado único e confiável** — o desfecho chega pelo `status` da transação e pelos webhooks (`transaction.approved`, `transaction.failed`, …), independentemente do que tenha sido necessário para chegar até ele. - **Instabilidade passageira é problema nosso** — falha de rede ou tempo esgotado no processamento é tratada pela Selectwin, sem cobrança em dobro. Você não precisa implementar retentativa. A recusa chega com o motivo, pelo `status` e pelo webhook. - **Conciliação unificada** — recebíveis, taxas, saldo, reembolsos e disputas aparecem em um conjunto único de recursos, no mesmo formato, para qualquer método de pagamento. - **Sem custo adicional** — a orquestração está incluída em todos os planos. --- #### O que isso significa para a sua integração 1. **Crie a cobrança e acompanhe o resultado.** A criação é **assíncrona**: `POST /v1/transactions` responde com a transação registrada, e o desfecho chega depois. Veja [Recusas de Pagamento](/docs/reference/recusas-de-pagamento). 2. **Não faça polling.** Assine os webhooks — eles são a fonte do resultado ([Proibição de Polling](/docs/reference/proibicao-de-polling)). 3. **Envie o cabeçalho `X-Idempotency-Key`.** Repetir a mesma requisição com a mesma chave, dentro de 24 horas, nunca vira cobrança dupla ([Idempotência](/docs/reference/idempotencia)). 4. **Leia o `status` e o `error.code`**, nunca o texto da mensagem ([Tratamento de Erros](/docs/reference/erros)). --- #### Perguntas Frequentes ##### A orquestração gera custos adicionais? Não. Está incluída em todos os planos. ##### A orquestração afeta o tempo de resposta? Não de forma perceptível na sua integração: a criação da cobrança responde de imediato e o resultado chega por webhook. ##### Como acompanho o processamento de uma transação? Pelo `status` da transação, pelos webhooks e pelo `timeline` do objeto, que registra as mudanças de status. O campo `payment.acquirerTransactionNumber` traz a referência do processamento para conciliação e atendimento. --- ## Recusas de Pagamento Fonte: https://selectwin.io/docs/reference/recusas-de-pagamento #### Visão Geral A criação de uma cobrança na Selectwin é **assíncrona**. Quando você chama `POST /v1/transactions` (ou cria/renova uma [Assinatura](/docs/guide/subscriptions/overview)), a API responde imediatamente com **`201 Created`** e a transação nasce com `status: "pending"`. A cobrança junto ao adquirente é processada em segundo plano. Por isso, **uma recusa de cartão normalmente NÃO chega como um erro HTTP síncrono** nessa chamada. O resultado da cobrança — incluindo uma recusa — aparece de duas formas: 1. No **objeto da transação**, cujo `status` passa a `failed` (recusa). 2. Em um **webhook**: a recusa dispara o evento **`transaction.failed`** (ver [Catálogo de Eventos](/docs/guide/webhook-events/event-catalog)). > Por que assim? O processamento da cobrança é concluído depois da resposta da criação. > Acompanhar a transação pelos webhooks é o padrão recomendado — veja > [Proibição de Polling](/docs/reference/proibicao-de-polling). #### Como acompanhar o resultado da cobrança | Sinal | Onde aparece | Significado | |-------|--------------|-------------| | `status: "pending"` | resposta `201` da criação | Cobrança ainda em processamento | | `status: "approved"` | objeto da transação + webhook `transaction.approved` | Pagamento aprovado | | `status: "failed"` | objeto da transação + webhook `transaction.failed` | Cobrança recusada | Os status possíveis de uma transação incluem `pending`, `pre-authorized`, `approved`, `failed`, `refused`, `canceled`, `refunded`, `chargeback`, `dispute`, `fraud-review`, `analyzing` e `awaiting`. #### O motivo da recusa (`payment.refused`) Quando a cobrança falha, o objeto da transação (e o payload do webhook `transaction.failed`) traz o motivo em `payment.refused`. Nas demais transações o campo é `null`. ```json { "status": "failed", "payment": { "refused": { "code": "insufficientFundsError", "type": "cardError", "category": "payment", "message": "Insufficient funds or limit.", "displayMessage": "Saldo ou limite insuficiente para realizar esta compra. Verifique o limite disponível ou use outro cartão.", "reversible": false } } } ``` | Campo | O que é | |-------|---------| | `code` | Código estável da recusa (tabela abaixo). **É por ele que você deve decidir.** | | `type` | `cardError` · `invalidRequestError` · `processingError` | | `category` | Quem precisa agir: `payment` (o cartão/emissor recusou) · `validation` (dados enviados inválidos) · `businessRule` (a loja não pode receber este pagamento) · `processing` (não foi possível processar — não é culpa do comprador) | | `message` | Texto curto em inglês. Pode mudar — não decida por ele. | | `displayMessage` | Texto em português, pronto para mostrar ao comprador. | | `reversible` | `true` quando uma nova tentativa do **mesmo** pagamento pode dar certo. | Os textos são **sempre da Selectwin**. A resposta bruta da adquirente ou do processador nunca é exposta — nem na API, nem no webhook, nem no painel. | `code` | `category` | Significado | |--------|-----------|-------------| | `insufficientFundsError` | `payment` | Saldo ou limite insuficiente | | `expiredCardError` | `payment` | Cartão vencido | | `invalidCardNumber` | `payment` | Número do cartão inválido | | `invalidExpiryMonth` / `invalidExpiryYear` | `payment` | Validade incorreta | | `invalidSecurityCode` | `payment` | Código de segurança (CVV) inválido | | `invalidPinCode` | `payment` | Senha do cartão incorreta | | `invalidPaymentInformation` | `payment` | Dados do cartão inválidos (campo não identificado) | | `cardDeclined` | `payment` | Recusa do banco emissor, sem motivo detalhado | | `authorizationRefused` | `payment` | Autorização negada pelo emissor | | `cardCustomerNotAssociated` | `payment` | Não há cartão ativo para cobrar — o comprador precisa informar o cartão de novo | | `invalidParameters` | `validation` | Dados informados para o pagamento são inválidos | | `sellerAuthorizationRefused` | `businessRule` | A loja não está habilitada para receber pagamentos no momento | | `paymentMethodUnavailable` | `businessRule` | A forma de pagamento não está disponível para a loja | | `cardValidationUnavailable` | `processing` | O cartão não pôde ser validado neste momento (proteção contra teste de cartão na loja) — o emissor não foi consultado; vale tentar de novo mais tarde ou usar outra forma de pagamento | | `paymentProcessingError` | `processing` | O pagamento não pôde ser processado (indisponibilidade ou falha de processamento) — vale tentar de novo | > Transações antigas, gravadas antes deste vocabulário, são convertidas para ele na leitura: `payment.refused` > tem sempre este formato. #### O envelope de erro 402 (Payment Required) Em superfícies **síncronas** de pagamento, uma falha de processamento pode retornar **HTTP 402** com o envelope de erro **padrão** (os mesmos campos de qualquer erro — `status`, `statusCode`, `category`, `code`, `message`, `resource`; ver [Tratamento de Erros](/docs/reference/erros)): ```json { "error": { "status": "Payment Required", "statusCode": 402, "category": "payment", "code": "paymentDeclined", "message": "The payment was declined by the issuer.", "resource": "payments" } } ``` > **Importante:** o envelope de erro **não** carrega os campos `type`, `displayMessage` nem `reversible`. > Esses não fazem parte do contrato de erro público. Trate o resultado pelo **`error.code`** (em erros > síncronos) e pelo **`status` da transação** + **webhooks** (no fluxo assíncrono de criação). #### Boas Práticas 1. **Não faça polling** para descobrir o resultado — escute os webhooks `transaction.approved` / `transaction.failed` (ver [Proibição de Polling](/docs/reference/proibicao-de-polling)). 2. **Keye no `error.code`** (em respostas de erro síncronas) e no **`status`** da transação, não no texto de `message` (que pode mudar). 3. **Em caso de recusa (`failed`)**, para cobrar de novo inicie uma **nova** transação — idealmente com outro cartão ou forma de pagamento. 4. **Teste os cenários** com os [Cartões de Teste](/docs/reference/cartoes-de-teste) (ex.: o cartão de `failed`) antes de ir para produção. #### Notas - Forma de pagamento indisponível para a conta: a transação termina `failed`, com `payment.refused.code: "paymentMethodUnavailable"` (no objeto da transação e no webhook `transaction.failed`). - O catálogo de `error.code` por recurso (com o status HTTP de cada um) está em [Catálogo de Códigos de Erro](/docs/reference/codigos-de-erro). --- ## Entrega Fonte: https://selectwin.io/docs/reference/shipping #### Visão Geral O objeto **Shipping** na API Selectwin representa as informações de entrega associadas a uma transação. Esta estrutura permite armazenar e gerenciar todos os detalhes logísticos de uma venda, desde o endereço de entrega até o status de rastreamento, proporcionando visibilidade completa sobre o processo de envio. --- #### Estrutura do Objeto Shipping O objeto Shipping está disponível dentro do objeto Transaction e contém os seguintes campos: ```json { "shipping": { "customerCost": "R$ 100,00", "internalCost": "R$ 50,00", "recipientName": "João da Silva", "recipientDocument": "12345678900", "recipientDocumentType": "cpf", "recipientPhone": "5511999999999", "trackingSiteUrl": "https://www.correios.com.br/rastreamento", "trackingCode": "BR123456789BR", "addressIsValid": "true", "deliveryStatus": "in_transit", "deliveryDate": "2023-11-15", "deliveryTimeWindow24h": "10h/12h", "packageWeight": "1.5kg", "packageDimensions": "30x20x15", "instructions": "Deixar com o porteiro", "geolocationLatitude": "-23.5505", "geolocationLongitude": "-46.636108", "insurance": true, "method": "SEDEX", "address": { "id": "addr_1234567890", "street": "Avenida Paulista", "complement": "Conjunto 1501", "district": "Bela Vista", "number": "1000", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310-100" } } } ``` ##### Campos do Objeto Shipping | Campo | Tipo | Descrição | |-------|------|-----------| | `customerCost` | string | Custo de entrega cobrado do cliente (ex: "R$ 100,00") | | `internalCost` | string | Custo interno de entrega (valor pago à transportadora) (ex: "R$ 50,00") | | `recipientName` | string | Nome completo do destinatário | | `recipientDocument` | string | Número do documento do destinatário | | `recipientDocumentType` | string | Tipo do documento do destinatário (cpf, cnpj, passport) | | `recipientPhone` | string | Telefone do destinatário no formato internacional (55 seguido de 10-11 dígitos) | | `trackingSiteUrl` | string | URL do site de rastreamento da transportadora | | `trackingCode` | string | Código de rastreamento fornecido pela transportadora. **É o que a Selectwin lê:** a venda aprovada de produto físico (`shippable`) sem `trackingCode` por 5 dias vira `delivery.state = "late"` na leitura da cobrança, você recebe o aviso "Venda sem rastreio" e o atraso pesa no risco da sua conta — informe o envio assim que ele sair | | `addressIsValid` | string | Indicador se o endereço foi validado ("true" ou "false") | | `deliveryStatus` | string | Status atual da entrega (ex: "pending", "in_transit", "delivered") | | `deliveryDate` | string | Data prevista ou efetiva de entrega (formato YYYY-MM-DD) | | `deliveryTimeWindow24h` | string | Janela de horário para entrega no formato 24h (ex: "10h/12h") | | `packageWeight` | string | Peso do pacote com unidade (ex: "1.5kg") | | `packageDimensions` | string | Dimensões do pacote no formato LxAxP (ex: "30x20x15") | | `instructions` | string | Instruções especiais para entrega | | `geolocationLatitude` | string | Latitude da geolocalização do endereço de entrega | | `geolocationLongitude` | string | Longitude da geolocalização do endereço de entrega | | `insurance` | boolean | Indica se o envio possui seguro | | `method` | string | Método/serviço de entrega utilizado (ex: "SEDEX", "PAC") | ##### Objeto Address dentro de Shipping | Campo | Tipo | Descrição | |-------|------|-----------| | `id` | string | Identificador único do endereço | | `street` | string | Nome da rua/avenida | | `complement` | string | Complemento do endereço (apto, sala, etc.) | | `district` | string | Bairro | | `number` | string | Número do endereço | | `city` | string | Cidade | | `state` | string | Estado/província (sigla) | | `country` | string | País (código ISO 3166-1 de 2 letras) | | `postcode` | string | CEP/código postal (formato: 12345-678 ou 12345678) | #### Status de Entrega O campo `deliveryStatus` pode conter os seguintes valores: | Status | Descrição | |--------|-----------| | `pending` | Envio registrado, mas ainda não processado | | `shipped` | Encomenda entregue ao destinatário | --- #### Como Utilizar o Objeto Shipping ##### 1. Criando uma Transação com Dados de Shipping ```javascript // Exemplo: Criando uma transação com informações de entrega const axios = require('axios'); async function createTransactionWithShipping() { const response = await axios({ method: 'post', url: 'https://api.selectwin.io/v1/transactions', headers: { 'SelectKey': 'sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ', 'Content-Type': 'application/json' }, data: { ... Data shipping: { customerCost: "R$ 15,90", method: "SEDEX", recipientName: "Maria Silva", recipientDocument: "12345678900", recipientDocumentType: "cpf", recipientPhone: "5511987654321", packageWeight: "0.5kg", packageDimensions: "20x15x10", instructions: "Entregar somente ao destinatário", insurance: true, address: { street: "Rua das Flores", number: "123", complement: "Apto 45", district: "Jardim Primavera", city: "São Paulo", state: "SP", country: "BR", postcode: "04567-890" } } } }); return response.data; } ``` --- #### Melhores Práticas ##### 1. Validação de Endereços Para evitar problemas de entrega: - Sempre valide o CEP/código postal antes de confirmar o endereço - Utilize o campo `addressIsValid` para indicar se o endereço foi validado - Consulte o endpoint `/utils/validate-postcode/:code` para validar CEPs ##### 2. Dados do Destinatário Para garantir entregas bem-sucedidas: - Forneça sempre o nome completo do destinatário - Inclua um documento válido para identificação na entrega - Disponibilize um telefone de contato atualizado ##### 3. Rastreamento Para melhorar a experiência do cliente: - Forneça a URL correta do site de rastreamento da transportadora - Mantenha o status de entrega atualizado com base nas informações da transportadora --- ## Recursos da API Fonte: https://selectwin.io/docs/reference/recursos #### Estrutura de Recursos A API Selectwin é organizada em torno de recursos RESTful, onde cada recurso representa uma entidade de negócio específica. Todos os recursos seguem uma convenção de nomeação consistente e são acessados por meio de URLs previsíveis que facilitam a integração. --- #### Recursos Principais A API pública expõe os seguintes recursos (12 principais, com sub-recursos onde aplicável). Todos os identificadores públicos seguem o padrão `prefixo_valor` (ex.: `cus_...`, `tra_...`). ##### Gestão de Clientes e Relacionados - **Customers**: CRUD completo de clientes (com documentos, contatos, endereços e cartões associados em respostas de leitura/criação). - **Addresses**: CRUD de endereços (associados a clientes ou empresas). - **Cards**: Gerenciamento de cartões tokenizados (create/list/read/delete; dados sensíveis protegidos). ##### Pagamentos e Transações - **Transactions**: Criação, consulta, listagem, analytics e ações (capture, refund, dispute, export). Suporta crédito, PIX, boleto e outros métodos. - **Subscriptions**: Assinaturas recorrentes, com ciclos, itens e splits. Operações: create, list, read, cancel + sub-rotas /cycles, /items, /splits. - **Products** (e **Variants**): Catálogo de produtos e variantes com precificação. Listagem otimizada (muitas vezes array direto ou sem merchant/_links), batch create/update de variantes. ##### Financeiro e Recebimentos - **Wallets**: Carteiras/bancos para recebimento (create, list, read, delete; list-all disponível). - **Withdrawals**: Saques manuais (listagem, criação e consulta) e ordens de saque automático (`/v1/withdrawals/auto`). - **Receivables**: Consulta de recebíveis (listagem paginada com projeção completa de 19 campos incluindo `expectedOn`, `grossAmount`, `anticipationFee`; analytics com array `list`). - **Finance**: Saldos (`/balance`) e histórico de movimentações (`/balance/history`). ##### Cupons e Extensões - **Coupons**: Gerenciamento de cupons de desconto (list, create, read, update, delete, list-all como array direto). **Nota:** respostas de cupons não incluem `merchant` nem `_links`. ##### Marketplace (sub-contas) - **Marketplace**: para contas habilitadas como plataforma — estado da função (`/v1/marketplace`), sub-contas (create, list, read, taxa simples e plano), o link de credenciamento (KYC) de cada sub-conta (generate, status, revoke) e os planos de taxas (CRUD + atribuição). A cobrança em nome da sub-conta é o `onBehalfOf` de Transactions e Checkouts. **Nota:** recurso **de conta** — escritas só com chave de produção (`sk_live_`); respostas sem `merchant`/`_links`. ##### Notificações e Integrações - **Webhooks**: - **Endpoints**: Configuração de URLs de notificação (list paged, create/read/update/delete; singles de endpoint não retornam `merchant`/`_links`; create retorna `secret` `whsec_...`). - **Events**: Histórico de eventos disparados (list com dispatches aninhados). - **Dispatches**: Histórico de entregas por endpoint + reenvio/redispatch. - Analytics e resend/redispatch retornam indicadores como `sended: true` ou `redispatched: true`. **Observação:** Alguns recursos e operações omitem `merchant` e/ou `_links` intencionalmente (cupons, produtos/variantes, singles de webhook endpoints, delete de auto-cashout, list-all/analytics em vários casos). Listas padrão paginadas geralmente incluem `merchant` + `_links`. A **especificação OpenAPI** é a regra exata por operação. --- #### Navegação e Descoberta (HATEOAS) A API Selectwin implementa o padrão HATEOAS (Hypermedia as the Engine of Application State). A maioria das respostas de sucesso (criação, leitura, listagens paginadas) inclui um objeto `_links` no nível raiz com links para ações relacionadas e navegação. Exemplo realista (lista de wallets, fiel à implementação): ```json { "_links": { "self": { "href": "https://api.selectwin.io/v1/wallets", "method": "GET", "description": "List all wallets." }, "create": { "href": "https://api.selectwin.io/v1/wallets", "method": "POST", "description": "Create a new wallet." } } } ``` Outros contextos incluem chaves como `read`, `update`, `delete`, `next`, `capture`, `refund`, `dispute` etc., dependendo da operação e do recurso. **Importante**: Nem todas as respostas incluem `_links` ou `merchant` (veja notas em Recursos Principais e a especificação OpenAPI para exceções como cupons, produtos e singles de webhook endpoints). Estes links permitem que sua aplicação descubra dinamicamente os recursos relacionados disponíveis. Para a lista completa de operações, schemas de entrada e exemplos de resposta, consulte a **especificação OpenAPI** e o guia de cada recurso. --- ## Cartões de teste Fonte: https://selectwin.io/docs/reference/cartoes-de-teste ##### Teste todos os cenários de pagamento com nossa solução completa de cartões pré-configurados #### Visão Geral A Selectwin API oferece cartões de teste pré-configurados para simular diversos cenários de transações. Esta característica permite testar integrações e fluxos de pagamento sem a necessidade de utilizar cartões reais, garantindo que sua aplicação esteja preparada para lidar com todos os possíveis resultados de processamento de pagamentos. #### O que são os cartões de teste? Os cartões de teste são números de cartão fictícios que você pode utilizar em ambientes de desenvolvimento e homologação para testar o comportamento da plataforma sob diferentes condições. Cada cartão está configurado para gerar um resultado específico de processamento, permitindo: 1. **Testes Completos**: Verificar todos os fluxos possíveis de pagamento 2. **Validação de Integrações**: Assegurar que sua aplicação responde corretamente a cada cenário 3. **Desenvolvimento Seguro**: Testar sem expor dados reais de cartão 4. **Automação de Testes**: Incluir testes de diversos cenários em pipelines de CI/CD 5. **Reprodução de Cenários**: Simular condições específicas para depuração e desenvolvimento #### Cartões Disponíveis e Comportamentos A Selectwin disponibiliza os seguintes cartões de teste, cada um resultando em um comportamento específico durante o processamento: | Número do Cartão | Comportamento | |------------------|--------------| | 5267420000001234 | approved (Aprovado) | | 4539210000005678 | awaiting (Aguardando) | | 6011850000009101 | dispute (Em disputa) | | 4024000000003456 | chargeback (Estorno solicitado) | | 5200820000007890 | refunded (Reembolsado) | | 3759870000002345 | canceled (Cancelado) | | 5078230000006789 | fraud-review (Análise de fraude) | | 4551880000000123 | failed (Falha) | | 6221260000004567 | pending (Pendente) | | 4895370000008910 | pre-authorized (Pré-autorizado) | --- ## Clientes - Visão Geral Fonte: https://selectwin.io/docs/guide/customers/overview #### Introdução O recurso de Clientes é um componente fundamental da Selectwin API, permitindo o gerenciamento completo de informações de clientes em sua plataforma. Este recurso fornece suporte para criação, consulta, atualização, exclusão e listagem de perfis de clientes, sendo vital para operações comerciais, processamento de pagamentos e análise de dados. A API de Clientes foi projetada com foco em segurança, consistência e flexibilidade, permitindo que os desenvolvedores implementem soluções robustas para gestão de relacionamento com clientes (CRM) e operações de venda. #### Conceito e Funcionalidade Os Clientes representam entidades (pessoas físicas ou jurídicas) que realizam transações financeiras ou interagem com sua plataforma dentro do domínio de negócio. Este recurso é essencial para operações como processamento de pagamentos, gestão de endereços, análise de comportamento de compra e personalização da experiência do usuário. Os clientes podem ter múltiplos documentos, informações de contato e endereços associados. O recurso de Clientes mantém relacionamentos com outros recursos da API, como Cartões, Endereços, Transações e Carteiras, permitindo uma gestão centralizada e coesa das informações dos usuários. #### Estrutura do Objeto Cliente O objeto Cliente contém informações detalhadas sobre a pessoa física ou jurídica. A estrutura básica inclui: ```json { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva", "email": "joao@example.com", "document": { "type": "cpf", "number": "12345678900" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "11987654321" }, "birthdate": "1990-05-15", "gender": "male", "available": true, "delinquent": false, "externalReference": "CRM-98765", "additionalEmails": ["joao.trabalho@exemplo.com"], "metadata": { "source": "website" }, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` **Notas importantes (fidelidade à API):** - Respostas individuais (create/read/update) incluem `merchant` + `_links` no nível raiz. - **Read** (`GET /v1/customers/{customerId}`) adiciona ao objeto base os embeds `transactions[]`, `subscriptions[]`, `addresses[]` e `cards[]` (cada um o item do endpoint de lista do respectivo recurso, limitado aos 100 mais recentes). - **Create/Update** retornam apenas o objeto base do cliente (sem os embeds). - O objeto NÃO contém `geolocation`. `telephone` é um objeto `{ countryCode, areaCode, number, line }` (todas as partes podem ser `null`); `document` é `{ type, number }` (ou `null`). - List: envelope paginado com itens completos do cliente + `merchant` + `_links`. - List-all (`GET /v1/customers/listall`): array de projeções resumidas (sem `merchant`/`_links`). - Delete: `{ id, resource: "customer", deleted: true, merchant, _links }`. - Export: **use `POST /v1/exports`** (job assíncrono com download assinado). O antigo `POST /v1/customers/export` foi APOSENTADO e responde `410` — ele nunca gerou arquivo nenhum, apenas devolvia `{ succeeded: true }`. #### Recursos Disponíveis | Recurso | Método | Endpoint | Descrição | |---------|--------|----------|-----------| | [Criar Cliente](/docs/guide/customers/create) | POST | `/v1/customers` | Cria um novo cliente (retorna o objeto base) | | [Consultar Cliente](/docs/guide/customers/read) | GET | `/v1/customers/{customerId}` | Obtém detalhes completos (embeds: transactions, subscriptions, addresses, cards) | | [Atualizar Cliente](/docs/guide/customers/update) | PATCH | `/v1/customers/{customerId}` | Atualização parcial (mesmo shape do create) | | [Excluir Cliente](/docs/guide/customers/delete) | DELETE | `/v1/customers/{customerId}` | Remove (retorna confirmação com merchant/_links) | | [Listar Clientes](/docs/guide/customers/list) | GET | `/v1/customers` | Lista paginada (itens completos + merchant/_links) | Endpoint auxiliar: `GET /v1/customers/listall` (listagem resumida não paginada). Para exportar, use `POST /v1/exports`. #### Validações e Segurança O recurso de Clientes implementa diversas validações para garantir a integridade e precisão dos dados: 1. **Validação de Documentos** - CPF e CNPJ são validados quanto ao formato e dígitos verificadores; passaporte é alfanumérico (5–15 caracteres) 2. **Validação de E-mail** - Garante que os endereços de e-mail (principal e adicionais) estejam em formato válido 3. **Consistência de Telefone** - Cada parte do telefone (`countryCode`, `areaCode`, `number`, `line`) é validada como dígitos quando informada 4. **Data de Nascimento** - Validada no formato `YYYY-MM-DD` As informações dos clientes são armazenadas de forma segura, seguindo padrões rígidos de proteção de dados pessoais e em conformidade com regulamentações como LGPD (Brasil) e GDPR (Europa). #### Tipos de Documento Suportados O campo `document.type` aceita exatamente os valores abaixo (minúsculas): | Tipo | Descrição | Formato | |------|-----------|---------| | `cpf` | Cadastro de Pessoa Física (Brasil) | 11 dígitos, com dígito verificador validado | | `cnpj` | Cadastro Nacional de Pessoa Jurídica (Brasil) | 14 dígitos, com dígito verificador validado | | `passport` | Passaporte Internacional | Alfanumérico (5–15 caracteres) | `document.number` aceita de 5 a 20 caracteres. Quando `document.type` não é informado, o número é aceito como dígitos (8–20). CPF/CNPJ são normalizados para apenas dígitos. #### Webhooks Acionados | Evento | Descrição | |--------|-----------| | `customer.created` | Acionado quando um novo cliente é adicionado ao sistema | | `customer.updated` | Acionado quando os dados de um cliente são atualizados | | `customer.deleted` | Acionado quando um cliente é removido do sistema | | `address.created` | Acionado quando um novo endereço é criado | | `address.updated` | Acionado quando um endereço é atualizado | | `address.deleted` | Acionado quando um endereço é deletado | #### Casos de Uso Comuns - **Integração com CRM**: Sincronize dados de clientes entre seu CRM e a plataforma Selectwin - **Checkout Personalizado**: Utilize dados de clientes para oferecer uma experiência de checkout personalizada - **Análise de Dados**: Colete e analise informações de clientes para insights de negócio - **Gestão de Relacionamento**: Mantenha informações atualizadas para melhorar o relacionamento com clientes - **Segmentação de Público**: Crie segmentos de clientes para campanhas de marketing direcionadas - **Retenção de Clientes**: Analise comportamentos e histórico para estratégias de retenção - **Verificação de Risco**: Utilize informações de clientes para análises de risco em transações #### Melhores Práticas 1. **Validar Dados Antes do Envio** - Implemente validações do lado do cliente antes de enviar dados para a API 2. **Armazenar Referências** - Mantenha o ID do cliente para referências futuras em suas transações 3. **Utilizar Metadados** - Aproveite o campo de metadados para armazenar informações personalizadas do seu negócio 4. **Manter Dados Atualizados** - Atualize regularmente as informações dos clientes para garantir precisão 5. **Implementar Tratamento de Erros** - Desenvolva rotinas de tratamento para lidar com falhas nas requisições 6. **Validar Autenticidade** - Implemente medidas adicionais para verificar a autenticidade dos clientes 7. **Proteger Dados Sensíveis** - Trate informações pessoais com os devidos cuidados de segurança e privacidade #### Solução de Problemas Comuns | Problema | Possível Causa | Solução Recomendada | |----------|----------------|---------------------| | Falha na criação do cliente | Dados obrigatórios ausentes ou inválidos | Verifique se todos os campos obrigatórios foram preenchidos e são válidos | | Erro "Cliente já existente" | E-mail ou documento já cadastrado | Verifique se o cliente já existe no sistema usando a API de listagem com filtros | | Cliente não aparece na listagem | Filtros incorretos na consulta | Verifique os parâmetros de filtro ou tente sem filtros | | Erro de validação de documento | Formato ou dígitos verificadores incorretos | Certifique-se de que o documento está no formato correto e é válido | | Falha na atualização do cliente | Campos protegidos sendo atualizados | Verifique se está tentando atualizar campos que não podem ser modificados | | Erro "Dados inconsistentes" | Combinação inválida de informações | Verifique a consistência dos dados, como país vs formato de documento | | Conflito entre atualizações | Múltiplas atualizações simultâneas | Implemente verificação de concorrência com base no campo updatedAt | #### Integração com Outros Recursos O recurso de Clientes integra-se com: - **Endereços**: Associação de endereços a clientes para entregas e faturamento - **Cartões**: Associação de cartões de pagamento a perfis de clientes - **Transações**: Processamento de pagamentos relacionados a clientes específicos - **Carteiras**: Gerenciamento de carteiras digitais associadas a clientes - **Webhooks**: Notificações em tempo real sobre mudanças em clientes - **Utils**: Validação de documentos e outros dados de clientes --- ## Clientes - Criação Fonte: https://selectwin.io/docs/guide/customers/create #### Visão Geral Este endpoint permite cadastrar um novo cliente no sistema, fornecendo funcionalidades para armazenar informações pessoais, documentos, dados de contato e endereços. É especialmente útil para iniciar relacionamentos com novos usuários, possibilitar operações financeiras e garantir conformidade com requisitos legais. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Dados Sensíveis**: As informações de clientes são consideradas dados sensíveis e devem ser tratadas conforme as regulamentações de proteção de dados (LGPD/GDPR). - **Validação de Documentos**: Certifique-se de que os documentos fornecidos (CPF/CNPJ) sejam válidos para evitar problemas futuros. - **E-mails Únicos**: O sistema não permite dois clientes com o mesmo endereço de e-mail. #### Descrição O endpoint **Criar Cliente** permite **cadastrar um novo cliente no sistema**, armazenando informações essenciais como nome, documento, contato e endereço. Os dados são validados para garantir integridade e conformidade com requisitos legais, permitindo que você mantenha uma base de clientes consistente e atualizada. #### Requisição ``` POST /v1/customers ``` > Este endpoint é **idempotente**: reenvie o mesmo header de idempotência para não criar clientes duplicados em caso de retentativa. #### Parâmetros da Requisição ##### Corpo da Requisição (Body) ```json { "firstName": "João", "lastName": "Silva", "email": "joao.silva@exemplo.com", "document": { "type": "cpf", "number": "12345678900" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "11987654321" }, "gender": "male", "birthdate": "1990-01-01", "metadata": { "source": "website", "campaign": "black_friday" }, "externalReference": "CUSTOMER_ID_12345", "additionalEmails": [ "joao.trabalho@exemplo.com", "joao.pessoal@exemplo.com" ] } ``` > **Endereços não fazem parte do corpo do cliente.** Para associar um endereço, crie o cliente primeiro e depois use [Criar Endereço](/docs/guide/addresses/create) (`POST /v1/addresses`) informando o `customerId`. ##### Atributos do Corpo da Requisição | Atributo | Tipo | Obrigatório | Descrição | Exemplo | |----------|------|-------------|-----------|---------| | `firstName` | string | Sim | Nome do cliente (2–255 caracteres) | `"João"` | | `lastName` | string | Sim | Sobrenome do cliente (1–255 caracteres) | `"Silva"` | | `email` | string | Sim | Endereço de e-mail principal do cliente | `"joao.silva@exemplo.com"` | | `document` | object | Não | Documento do cliente (`type` + `number`, ambos opcionais) | — | | `document.type` | string | Não | Tipo de documento (`cpf`, `cnpj`, `passport`) | `"cpf"` | | `document.number` | string | Não | Número do documento (5–20 caracteres, sem formatação) | `"12345678900"` | | `telephone` | object | Não | Telefone do cliente; todas as partes são opcionais | — | | `telephone.countryCode` | string | Não | Código do país (2–5 dígitos) | `"55"` | | `telephone.areaCode` | string | Não | Código de área (2–5 dígitos) | `"11"` | | `telephone.number` | string | Não | Número do telefone (6–20 dígitos) | `"987654321"` | | `telephone.line` | string | Não | Linha completa do telefone (7–20 dígitos) | `"11987654321"` | | `gender` | string | Não | Gênero do cliente (`male`, `female`, `other`) | `"male"` | | `birthdate` | string | Não | Data de nascimento no formato `YYYY-MM-DD` | `"1990-01-01"` | | `metadata` | object | Não | Metadados adicionais personalizados | `{"source": "website"}` | | `externalReference` | string | Não | Referência externa para integração (máx. 255) | `"CUSTOMER_ID_12345"` | | `additionalEmails` | array | Não | E-mails adicionais do cliente (máx. 20) | `["joao.trabalho@exemplo.com"]` | #### Resposta - 201 Created Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 201 Created e os detalhes do cliente recém-criado, incluindo o ID gerado pelo sistema. ##### Exemplo de Resposta ```json { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva", "email": "joao.silva@exemplo.com", "document": { "type": "cpf", "number": "12345678900" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "11987654321" }, "birthdate": "1990-01-01", "gender": "male", "available": true, "delinquent": false, "externalReference": "CUSTOMER_ID_12345", "additionalEmails": [ "joao.trabalho@exemplo.com" ], "metadata": { "source": "website" }, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "GET", "description": "Read a customer." }, "create": { "href": "https://api.selectwin.io/v1/customers", "method": "POST", "description": "Create a new customer." }, "update": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "PUT", "description": "Update the customer." }, "delete": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "DELETE", "description": "Delete the customer." }, "list": { "href": "https://api.selectwin.io/v1/customers", "method": "GET", "description": "List all customers." } } } ``` ##### Atributos da Resposta Create retorna o objeto base do cliente (sem os embeds `transactions`/`subscriptions`/`addresses`/`cards`) + `merchant` + `_links`. | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador do cliente (`cus_*`) | | `firstName` / `lastName` | string | Nome e sobrenome | | `email` | string \| null | E-mail principal | | `document` | object \| null | `{ type, number }` (cada um pode ser `null`) | | `telephone` | object \| null | `{ countryCode, areaCode, number, line }` (cada parte pode ser `null`) | | `birthdate` | string \| null | Data de nascimento (`YYYY-MM-DD`) | | `gender` | string \| null | Gênero | | `available` / `delinquent` | boolean | Status do cliente | | `externalReference` | string \| null | Referência externa | | `additionalEmails` | array \| null | E-mails adicionais | | `metadata` | object \| null | Metadados | | `createdAt` / `updatedAt` | string | Timestamps ISO 8601 (UTC) | | `merchant` | object | `{ name, merchantId, isSubAccount }` | | `_links` | object | HATEOAS (self, create, update, delete, list) | #### Respostas de Erro ##### 400 Bad Request Ocorre quando a requisição contém dados inválidos ou está mal formatada. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "firstName": "The firstName field must be between 2 and 255 characters." } ] } } ``` ##### 409 Conflict Ocorre quando já existe um cliente com o mesmo e-mail (`customerEmailTaken`) ou documento (`customerDocumentTaken`). ```json { "error": { "status": "Conflict", "statusCode": 409, "category": "conflict", "message": "A customer with this email already exists." } } ``` #### Casos de Uso 1. **Novo Usuário**: Criar um perfil de cliente quando um novo usuário se registra em sua plataforma 2. **Checkout de Convidado**: Armazenar informações do cliente durante um processo de checkout sem conta 3. **Migração de Dados**: Importar clientes de sistemas legados para a plataforma Selectwin 4. **Prospecção**: Registrar potenciais clientes capturados em ações de marketing #### Melhores Práticas 1. **Validar Antes de Enviar**: Implemente validações do lado do cliente para todos os campos antes de enviar a requisição 2. **Armazenar o ID**: Guarde o ID do cliente retornado para referência em operações futuras 3. **Enviar Apenas o Necessário**: Inclua apenas os campos realmente necessários para sua operação 4. **Tratar Dados Sensíveis**: Adote medidas de segurança adicionais ao lidar com documentos e dados pessoais #### Integração com Outros Endpoints - Use [Consultar Cliente](/docs/guide/customers/read) para verificar os dados do cliente após a criação - Use [Atualizar Cliente](/docs/guide/customers/update) para complementar informações parcialmente enviadas - Use [Criar Endereço](/docs/guide/addresses/create) para adicionar múltiplos endereços ao cliente --- ## Clientes - Listagem Fonte: https://selectwin.io/docs/guide/customers/list #### Visão Geral Este endpoint permite recuperar uma lista paginada de clientes, com opções de filtragem e ordenação, facilitando a busca e visualização de múltiplos registros de clientes. É especialmente útil para interfaces de gerenciamento, análise de dados de clientes e relatórios administrativos. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Paginação**: Utilizar os parâmetros de paginação para limitar o volume de dados retornados por requisição. - **Filtros**: Aplicar filtros específicos para reduzir o conjunto de resultados e melhorar a performance. - **Volume de Dados**: Este endpoint pode retornar grandes volumes de dados, o que pode impactar o desempenho da aplicação. #### Descrição O endpoint **Listar Clientes** permite **recuperar uma coleção paginada de clientes** registrados no sistema. Os resultados podem ser filtrados por diversos parâmetros como e-mail ou documento, e ordenados conforme necessário. #### Requisição ``` GET /v1/customers ``` ##### Parâmetros de Consulta (Query) | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Localização | |-----------|------|-------------|-----------|---------|------------| | `limit` | integer | Não | Número máximo de registros (1–100, padrão: 20) | `20` | Query | | `offset` | integer | Não | Posição inicial para retornar registros (padrão: 0) | `0` | Query | | `sort` | string | Não | Expressão de ordenação (ex.: `-createdAt`) | `-createdAt` | Query | | `id` | string | Não | Filtra clientes pelo ID específico (`cus_*`) | `cus_01hqzvabc` | Query | | `name` | string | Não | Filtra clientes pelo nome ou sobrenome (busca parcial) | `joão` | Query | | `email` | string | Não | Filtra clientes pelo endereço de e-mail | `joao@exemplo.com` | Query | | `document` | string | Não | Filtra clientes pelo número do documento (11–20 dígitos) | `12345678900` | Query | | `telephoneline` | string | Não | Filtra clientes pela linha de telefone (7–20 dígitos, sem formatação) | `11987654321` | Query | | `externalreference` | string | Não | Filtra clientes pela referência externa | `CUSTOMER_ID_12345` | Query | Também são aceitos os filtros de intervalo de data: `daterange`, `daterangegt`, `daterangegte`, `daterangelt`, `daterangelte`. #### Resposta - 200 OK Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 200 OK e uma lista paginada de clientes. ##### Exemplo de Resposta ```json { "offset": 0, "limit": 20, "total": 142, "hasMore": true, "page": { "current": 1, "total": 8, "offset": { "first": 0, "prev": null, "next": 20, "last": 140 } }, "data": [ { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva", "email": "joao@example.com", "document": { "type": "cpf", "number": "12345678900" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "11987654321" }, "birthdate": "1990-05-15", "gender": "male", "available": true, "delinquent": false, "externalReference": "CRM-98765", "additionalEmails": ["joao.trabalho@exemplo.com"], "metadata": { "source": "website" }, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z", "_links": { "self": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "GET", "description": "Read a customer." } } } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/customers", "method": "GET", "description": "List all customers." }, "create": { "href": "https://api.selectwin.io/v1/customers", "method": "POST", "description": "Create a new customer." } } } ``` ##### Atributos da Resposta Paginada (lista de customers) | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` | integer | Posição inicial dos resultados retornados | | `limit` | integer | Quantidade máxima de registros retornados | | `total` | integer | Total de registros disponíveis para a consulta | | `hasMore` | boolean | Indica se existem mais resultados além dos retornados | | `page.current` | integer | Número da página atual | | `page.total` | integer | Total de páginas disponíveis | | `page.offset.first` / `.prev` / `.next` / `.last` | integer \| null | Offsets de navegação | | `data` | array | Lista de clientes — cada item é o objeto completo do cliente (sem os embeds `transactions`/`subscriptions`/`addresses`/`cards`), com seu próprio `_links` | | `merchant` | object | `{ name, merchantId, isSubAccount }` | | `_links` | object | Links HATEOAS da listagem (self, create) | #### Respostas de Erro ##### 400 Bad Request Ocorre quando a requisição contém parâmetros inválidos ou está mal formatada. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "limit": "O limite deve ser um número entre 1 e 100." } ] } } ``` #### Casos de Uso 1. **Interfaces Administrativas**: Exibir lista de clientes em painéis de controle 2. **Relatórios**: Gerar relatórios de clientes com base em filtros específicos 3. **Busca de Clientes**: Permitir que operadores busquem clientes por e-mail ou documento 4. **Exportação de Dados**: Base para extração de dados de clientes para sistemas externos #### Melhores Práticas 1. **Implementar Paginação Eficiente**: Sempre utilize os parâmetros `limit` e `offset` para controlar o volume de dados 2. **Utilizar Filtros**: Aproveite os filtros disponíveis para reduzir o conjunto de resultados 3. **Cache de Resultados**: Implemente cache nos resultados que não mudam com frequência 4. **Evitar Listagens Completas**: Sempre inclua algum filtro ao buscar clientes em bases muito grandes #### Integração com Outros Endpoints - Use [Consultar Cliente](/docs/guide/customers/read) para obter detalhes completos de um cliente específico após encontrá-lo na lista - Use [Atualizar Cliente](/docs/guide/customers/update) para modificar informações de clientes encontrados na listagem - Use [Excluir Cliente](/docs/guide/customers/delete) para remover clientes identificados através da listagem --- ## Clientes - Consulta Fonte: https://selectwin.io/docs/guide/customers/read #### Visão Geral Este endpoint permite recuperar informações detalhadas de um cliente específico, fornecendo todos os dados associados ao cliente através de seu identificador único. É especialmente útil para verificar informações de contato, visualizar dados de endereçamento e acessar metadados antes de realizar operações. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Permissões de Acesso**: Verifique se você possui permissões para acessar as informações do cliente solicitado. - **Proteção de Dados**: As informações retornadas contêm dados pessoais sensíveis e devem ser tratadas com segurança. - **Caching**: Considere implementar estratégias de cache para reduzir chamadas repetitivas à API. #### Descrição O endpoint **Consultar Cliente** permite **recuperar todos os detalhes de um cliente específico** a partir de seu ID único. A resposta inclui informações pessoais, documentos, dados de contato, endereços e qualquer metadado personalizado associado ao cliente. #### Requisição ``` GET /v1/customers/{customerId} ``` ##### Parâmetros de Caminho (Path) e Consulta (Query) | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Localização | |-----------|------|-------------|-----------|---------|------------| | `customerId` | string | Sim | ID único do cliente a ser consultado | `cus_01hqzvabc` | Path | #### Resposta - 200 OK Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 200 OK e os detalhes completos do cliente solicitado. ##### Exemplo de Resposta Resposta no nível raiz. O objeto base do cliente é estendido com quatro embeds — `transactions[]`, `subscriptions[]`, `addresses[]` e `cards[]` — cada um sendo o item do endpoint de lista do respectivo recurso (limitado aos 100 mais recentes; um embed vazio degrada para `[]`). Inclui também `merchant` + `_links`. ```json { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva", "email": "joao@example.com", "document": { "type": "cpf", "number": "12345678900" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "11987654321" }, "birthdate": "1990-05-15", "gender": "male", "available": true, "delinquent": false, "externalReference": "CRM-98765", "additionalEmails": [ "joao.trabalho@exemplo.com" ], "metadata": { "source": "website" }, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z", "transactions": [ { "id": "tra_01hqzvabc", "status": "approved", "amount": 5000 } ], "subscriptions": [ { "id": "sub_01hqzvabc", "status": "active" } ], "addresses": [ { "id": "addr_01hqzvabc", "ownerType": "customer", "ownerId": "cus_01hqzvabc", "street": "Av. Paulista", "city": "São Paulo", "primary": true } ], "cards": [ { "id": "card_01hqzvabc", "brand": "visa", "last4": "4242" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "GET", "description": "Read a customer." }, "create": { "href": "https://api.selectwin.io/v1/customers", "method": "POST", "description": "Create a new customer." }, "update": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "PUT", "description": "Update the customer." }, "delete": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "DELETE", "description": "Delete the customer." }, "list": { "href": "https://api.selectwin.io/v1/customers", "method": "GET", "description": "List all customers." } } } ``` ##### Atributos da Resposta Resposta inclui o objeto base + os embeds (`transactions`, `subscriptions`, `addresses`, `cards`) + `merchant` + `_links`. | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador do cliente (`cus_*`) | | `firstName` / `lastName` | string | Nome e sobrenome | | `email` | string \| null | E-mail principal | | `document` | object \| null | `{ type, number }` (cada um pode ser `null`) | | `telephone` | object \| null | `{ countryCode, areaCode, number, line }` (cada parte pode ser `null`) | | `birthdate` | string \| null | Data de nascimento (`YYYY-MM-DD`) | | `gender` | string \| null | Gênero do cliente (`male`, `female`, `other`) | | `available` / `delinquent` | boolean | Status do cliente | | `externalReference` | string \| null | Referência externa | | `additionalEmails` | array \| null | E-mails adicionais | | `metadata` | object \| null | Metadados | | `transactions` | array | Embed: transações recentes (itens do endpoint de lista de transações) | | `subscriptions` | array | Embed: assinaturas recentes (itens do endpoint de lista de assinaturas) | | `addresses` | array | Embed: endereços do cliente (itens do endpoint de lista de endereços) | | `cards` | array | Embed: cartões do cliente (itens do endpoint de lista de cartões) | | `createdAt` / `updatedAt` | string | Timestamps ISO 8601 (UTC) | | `merchant` | object | `{ name, merchantId, isSubAccount }` | | `_links` | object | HATEOAS (self, create, update, delete, list) | #### Respostas de Erro ##### 400 Bad Request Ocorre quando a requisição contém dados inválidos ou está mal formatada. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "customerId": "The format of the customer ID is invalid." } ] } } ``` ##### 404 Not Found Ocorre quando o `customerId` não existe ou não pertence à sua conta (`customerNotFound`). ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "Customer not found." } } ``` #### Casos de Uso 1. **Verificação de Perfil**: Exibir informações detalhadas do cliente em uma página de perfil 2. **Preparação para Checkout**: Recuperar dados de endereço e contato antes de iniciar um processo de pagamento 3. **Confirmação de Identidade**: Verificar documentos e dados de contato para processos de verificação 4. **Suporte ao Cliente**: Acessar detalhes do cliente durante atendimento de suporte #### Melhores Práticas 1. **Armazenar em Cache**: Implemente estratégias de cache para dados que não mudam com frequência 2. **Tratamento de Erros Robusto**: Implemente tratamento adequado para erros 404 (cliente não encontrado) 3. **Limitação de Exibição**: Ao exibir dados para usuários finais, considere ocultar partes sensíveis de documentos 4. **Verificação de Permissões**: Sempre verifique se o usuário tem permissão para acessar os dados do cliente #### Integração com Outros Endpoints - Use [Atualizar Cliente](/docs/guide/customers/update) para modificar as informações após visualizá-las - Use [Listar Clientes](/docs/guide/customers/list) para buscar múltiplos clientes com filtros - Use [Listar Endereços](/docs/guide/addresses/list) para visualizar todos os endereços associados ao cliente --- ## Clientes - Atualização Fonte: https://selectwin.io/docs/guide/customers/update #### Visão Geral Este endpoint permite atualizar informações de um cliente existente, oferecendo a capacidade de modificar os dados pessoais, documentos, informações de contato e endereços. É especialmente útil para manter registros atualizados, corrigir informações incorretas e complementar dados parcialmente fornecidos anteriormente. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Dados Sensíveis**: As informações de clientes são consideradas dados sensíveis e devem ser tratadas conforme as regulamentações de proteção de dados (LGPD/GDPR). - **ID do Cliente**: Certifique-se de fornecer o ID correto do cliente para evitar atualizações em registros errados. - **Atualização Parcial (PATCH)**: Este endpoint usa o método `PATCH` e aceita atualizações parciais. **Todos os campos do corpo são opcionais** — envie apenas os campos que deseja alterar; os campos omitidos permanecem inalterados. - **Troca de e-mail encerra os links do portal**: quando o `email` muda (não apenas maiúsculas/espaços), todos os links do portal do cliente já enviados deixam de abrir — eles foram para o endereço antigo. O cliente pede um link novo, que vai para o e-mail atual. Preencher um e-mail que o cadastro não tinha não encerra nada. #### Descrição O endpoint **Atualizar Cliente** permite **modificar as informações de um cliente existente** a partir de seu ID único. Como é uma atualização parcial, envie apenas os campos a alterar. #### Requisição ``` PATCH /v1/customers/{customerId} ``` > O método deste endpoint é `PATCH`. (O link `update` no bloco `_links` das respostas anuncia `PUT` por convenção do HATEOAS, mas a rota efetiva é `PATCH`.) ##### Parâmetros de Caminho (Path) | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Localização | |-----------|------|-------------|-----------|---------|------------| | `customerId` | string | Sim | ID único do cliente a ser atualizado | `cus_01hqzvabc` | Path | ##### Corpo da Requisição (Body) ```json { "lastName": "Silva Santos", "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "11987654321" }, "metadata": { "source": "website", "category": "premium", "lastPurchase": "2023-06-15" }, "externalReference": "CUSTOMER_ID_12345", "additionalEmails": [ "joao.trabalho@exemplo.com", "joao.pessoal@exemplo.com" ] } ``` > Endereços não fazem parte do corpo do cliente — gerencie endereços pelo recurso [Endereços](/docs/guide/addresses/overview). ##### Atributos do Corpo da Requisição Todos os campos são **opcionais** (atualização parcial). As mesmas regras de validação do [Criar Cliente](/docs/guide/customers/create) aplicam-se a cada campo enviado. | Atributo | Tipo | Obrigatório | Descrição | Exemplo | |----------|------|-------------|-----------|---------| | `firstName` | string | Não | Nome do cliente (2–255 caracteres) | `"João"` | | `lastName` | string | Não | Sobrenome do cliente (1–255 caracteres) | `"Silva Santos"` | | `email` | string | Não | E-mail principal do cliente | `"joao.silva@exemplo.com"` | | `document` | object | Não | `{ type, number }` (ambos opcionais) | — | | `telephone` | object | Não | `{ countryCode, areaCode, number, line }` (todas as partes opcionais) | — | | `gender` | string | Não | Gênero do cliente (`male`, `female`, `other`) | `"male"` | | `birthdate` | string | Não | Data de nascimento (`YYYY-MM-DD`) | `"1990-01-01"` | | `metadata` | object | Não | Metadados adicionais personalizados | `{"category": "premium"}` | | `externalReference` | string | Não | Referência externa (máx. 255) | `"CUSTOMER_ID_12345"` | | `additionalEmails` | array | Não | E-mails adicionais (máx. 20) | `["joao.trabalho@exemplo.com"]` | #### Resposta - 200 OK Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 200 OK e os detalhes atualizados do cliente. ##### Exemplo de Resposta ```json { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva Santos", "email": "joao.silva@exemplo.com", "document": { "type": "cpf", "number": "12345678900" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "11987654321" }, "birthdate": "1990-01-01", "gender": "male", "available": true, "delinquent": false, "externalReference": "CUSTOMER_ID_12345", "additionalEmails": [ "joao.trabalho@exemplo.com" ], "metadata": { "source": "website" }, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "GET", "description": "Read a customer." }, "create": { "href": "https://api.selectwin.io/v1/customers", "method": "POST", "description": "Create a new customer." }, "update": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "PUT", "description": "Update the customer." }, "delete": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "DELETE", "description": "Delete the customer." }, "list": { "href": "https://api.selectwin.io/v1/customers", "method": "GET", "description": "List all customers." } } } ``` ##### Atributos da Resposta Update retorna o objeto base do cliente (mesmo shape do create, sem os embeds `transactions`/`subscriptions`/`addresses`/`cards`) + `merchant` + `_links`. | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador do cliente (`cus_*`) | | `firstName` / `lastName` | string | Nome e sobrenome | | `email` | string \| null | E-mail principal | | `document` | object \| null | `{ type, number }` | | `telephone` | object \| null | `{ countryCode, areaCode, number, line }` | | `birthdate` | string \| null | Data de nascimento (`YYYY-MM-DD`) | | `gender` | string \| null | Gênero | | `available` / `delinquent` | boolean | Status | | `externalReference` | string \| null | Referência externa | | `additionalEmails` | array \| null | E-mails adicionais | | `metadata` | object \| null | Metadados | | `createdAt` / `updatedAt` | string | Timestamps ISO 8601 (UTC) | | `merchant` | object | `{ name, merchantId, isSubAccount }` | | `_links` | object | HATEOAS (self, create, update, delete, list) | #### Respostas de Erro ##### 400 Bad Request Ocorre quando a requisição contém dados inválidos ou está mal formatada. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "firstName": "The firstName field must be between 2 and 255 characters." } ] } } ``` ##### 404 Not Found Ocorre quando o `customerId` não existe ou não pertence à sua conta (`customerNotFound`). ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "Customer not found." } } ``` #### Casos de Uso 1. **Atualização de Perfil**: Permitir que clientes atualizem suas informações pessoais 2. **Correção de Informações**: Corrigir dados incorretos ou desatualizados 3. **Enriquecimento de Dados**: Adicionar informações complementares ao perfil do cliente 4. **Mudança de Endereço**: Atualizar informações de endereçamento quando o cliente se muda #### Melhores Práticas 1. **Consultar Antes de Atualizar**: Use o endpoint [Consultar Cliente](/docs/guide/customers/read) para obter os dados atuais antes de enviar atualizações 2. **Validar Dados**: Implemente validações do lado do cliente para todos os campos antes de enviar a requisição 3. **Manter Consistência**: Tenha cuidado ao atualizar documentos, pois podem estar vinculados a outras operações 4. **Informar Mudanças**: Notifique o cliente sobre alterações importantes em seus dados, como e-mail ou telefone #### Integração com Outros Endpoints - Use [Consultar Cliente](/docs/guide/customers/read) para verificar os dados do cliente antes e após a atualização - Use [Criar Endereço](/docs/guide/addresses/create) para adicionar endereços secundários ao cliente - Veja [Endereços](/docs/guide/addresses/overview) para gerenciar os endereços associados ao cliente --- ## Clientes - Exclusão Fonte: https://selectwin.io/docs/guide/customers/delete #### Visão Geral Este endpoint permite excluir permanentemente um cliente do sistema através de seu identificador único. Ele é especialmente útil para remover registros de clientes inativos, atender a solicitações de direito ao esquecimento (LGPD/GDPR), ou corrigir registros duplicados no sistema. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Operação Irreversível**: A exclusão de um cliente é uma operação permanente e não pode ser desfeita. - **Dependências**: Antes de excluir um cliente, verifique se ele não possui transações ativas ou outros recursos vinculados. - **Conformidade Legal**: Avaliar as implicações legais de remover os dados do cliente, especialmente em relação a registros fiscais. #### Descrição O endpoint **Excluir Cliente** permite **remover permanentemente um cliente específico** a partir de seu ID único. Após a exclusão, os dados pessoais do cliente são apagados (veja abaixo o que sai e o que fica), e o ID não poderá ser utilizado novamente para consultas ou atualizações. ##### O que a exclusão apaga Numa única operação, irreversível: - **Cadastro**: nome, e-mail, documento, telefone, nascimento, gênero, e-mails adicionais, referência externa e `metadata`. - **Endereços** do cliente. - **Sessões de checkout** do cliente (as que ele pagou e os carrinhos que abandonou, achados pelo cliente vinculado à sessão ou pelo e-mail/documento que ele digitou — este, só nas sessões sem cliente vinculado e sem pagamento em andamento: a sessão de outro cliente com o mesmo e-mail não é tocada; inclusive as vendas de marketplace feitas em nome da sua conta): os dados do comprador (`customer`, `address`, campos personalizados) e do navegador (IP, aparelho, cookies de atribuição). O pedido (itens, totais, cupons) continua. - **Links do portal do cliente**: todos os que estavam válidos deixam de abrir. - **Pagamentos combinados** (2 cartões ou cartão + Pix): a base de cobrança guardada para cobrar as partes (documento da compra, documento do destinatário do frete, IP e navegador). As transações das partes ficam, como qualquer transação. Um pedido combinado ainda em andamento deixa de cobrar a próxima parte e é desfeito. - **Conta na área de membros** desta loja, se o cliente tiver uma (o mesmo apagamento de quando o aluno apaga a própria conta): o e-mail de acesso, as sessões e os links de entrada, o texto de comentários e avaliações, as anotações e o progresso. A matrícula fica, anônima (o registro da venda), e o certificado emitido fica, sem o nome. - **Mensagens do SmartOps** (e-mail, SMS e WhatsApp) desta loja: das mensagens enviadas para o e-mail e o telefone do cliente (e das que foram para um contato antigo dele) saem o contato e o texto — fica só o registro de que a mensagem foi enviada (evento, status e datas), sem nada que identifique o cliente. Saem também os avisos ao seu sistema sobre elas, a fila do SMS (o número e o texto) e a janela de conversa do WhatsApp. O descadastro continua valendo, para o cliente não voltar a receber mensagens, sem guardar o e-mail ou o telefone dele. Num cliente de sandbox, só as mensagens de teste são alcançadas. **O que fica**: o histórico financeiro (transações e assinaturas guardam a cópia dos dados do comprador do momento da cobrança, por obrigação fiscal) e os eventos de webhook já emitidos, até o prazo de retenção deles. #### Requisição ``` DELETE /v1/customers/{customerId} ``` ##### Parâmetros de Caminho (Path) | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Localização | |-----------|------|-------------|-----------|---------|------------| | `customerId` | string | Sim | ID único do cliente a ser excluído | `cus_01hqzvabc` | Path | #### Resposta - 200 OK Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 200 OK com uma confirmação da exclusão. ##### Exemplo de Resposta ```json { "id": "cus_01hqzvabc", "resource": "customer", "deleted": true, "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "GET", "description": "Read a customer." }, "create": { "href": "https://api.selectwin.io/v1/customers", "method": "POST", "description": "Create a new customer." }, "update": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "PUT", "description": "Update the customer." }, "delete": { "href": "https://api.selectwin.io/v1/customers/cus_01hqzvabc", "method": "DELETE", "description": "Delete the customer." }, "list": { "href": "https://api.selectwin.io/v1/customers", "method": "GET", "description": "List all customers." } } } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | ID do cliente excluído (`cus_*`) | | `resource` | string | `"customer"` | | `deleted` | boolean | `true` | | `merchant` | object | `{ name, merchantId, isSubAccount }` | | `_links` | object | HATEOAS (self, create, update, delete, list) | #### Respostas de Erro ##### 400 Bad Request Ocorre quando a requisição contém dados inválidos ou está mal formatada. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "customerId": "The format of the customer ID is invalid." } ] } } ``` ##### 404 Not Found Ocorre quando o `customerId` não existe ou não pertence à sua conta (`customerNotFound`). ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "Customer not found." } } ``` ##### 409 Conflict Ocorre quando o cliente possui uma assinatura ativa (não cancelada). Cancele a(s) assinatura(s) antes de excluir (`customerHasActiveSubscription`). ```json { "error": { "status": "Conflict", "statusCode": 409, "category": "conflict", "message": "This customer has an active subscription and cannot be deleted. Cancel the subscription first." } } ``` #### Casos de Uso 1. **Direito ao Esquecimento**: Atender solicitações de remoção de dados conforme LGPD/GDPR 2. **Limpeza de Registros**: Remover clientes inativos ou duplicados do sistema 3. **Correção de Erros**: Eliminar registros criados incorretamente 4. **Cancelamento de Conta**: Processar solicitações de cancelamento de serviço #### Melhores Práticas 1. **Confirmar Antes de Excluir**: Implemente uma confirmação adicional antes de executar a exclusão 2. **Verificar Dependências**: Verifique recursos vinculados ao cliente antes de solicitar a exclusão 3. **Documentar Exclusões**: Mantenha registros de auditoria das exclusões realizadas 4. **Backup de Dados**: Considere realizar backup dos dados antes de excluir clientes importantes #### Integração com Outros Endpoints - Use [Consultar Cliente](/docs/guide/customers/read) para verificar os dados do cliente antes da exclusão - Use [Listar Clientes](/docs/guide/customers/list) para identificar clientes que podem ser removidos - Use [Listar Transações](/docs/guide/transactions/list) para verificar se o cliente possui transações ativas --- ## Endereços - Visão Geral Fonte: https://selectwin.io/docs/guide/addresses/overview #### Introdução O recurso de Endereços é um componente fundamental da Selectwin API, permitindo o gerenciamento eficiente das informações de localização física associadas a clientes e transações. Este recurso fornece suporte para armazenar e administrar diversos tipos de endereços, como residenciais, comerciais, de entrega e faturamento. A API de Endereços foi projetada com foco na flexibilidade, validação precisa e suporte internacional, permitindo que os desenvolvedores implementem soluções robustas para gerenciamento de endereços em diferentes contextos e regiões geográficas. #### Conceito e Funcionalidade Os endereços representam localizações físicas dentro da plataforma, permitindo o registro preciso de informações geográficas e dados de localização. Este recurso é essencial para operações como envio de produtos, faturamento, verificação de cobertura de serviços e análises geográficas. Cada endereço está associado a um cliente específico e pode ser configurado como endereço principal, facilitando operações que requerem um endereço padrão. O sistema suporta múltiplos formatos de endereço adaptados às convenções locais de diferentes países. #### Estrutura do Objeto Endereço O objeto Endereço contém informações detalhadas sobre uma localização física. A estrutura básica inclui: ```json { "id": "addr_01hqzvabc", "ownerType": "customer", "ownerId": "cus_01hqzvabc", "street": "Av. Paulista", "number": "1000", "complement": "Sala 1", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310100", "latitude": null, "longitude": null, "primary": true, "line": "Av. Paulista, 1000 - Bela Vista, São Paulo - SP, 01310100", "line1": "Av. Paulista", "line2": "1000", "line3": "Bela Vista", "metadata": { "source": "checkout" }, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` **Nota:** Esta é a estrutura do objeto de endereço retornado em operações de leitura/criação/atualização. Respostas completas de sucesso incluem também `merchant` e `_links` no nível raiz (ver guias de Create/Read para exemplos completos). Listagens usam projeções mais leves. #### Recursos Disponíveis | Recurso | Método | Endpoint | Descrição | |---------|--------|----------|-----------| | [Criar Endereço](/docs/guide/addresses/create) | POST | `/v1/addresses` | Cria um novo endereço para um cliente | | [Consultar Endereço](/docs/guide/addresses/read) | GET | `/v1/addresses/{addressId}` | Obtém detalhes de um endereço específico | | [Atualizar Endereço](/docs/guide/addresses/update) | PUT | `/v1/addresses/{addressId}` | Atualização parcial dos dados de um endereço (todos os campos do corpo são opcionais) | | [Listar Endereços](/docs/guide/addresses/list) | GET | `/v1/addresses` | Lista endereços com opções de paginação e filtros | | [Excluir Endereço](/docs/guide/addresses/delete) | DELETE | `/v1/addresses/{addressId}` | Remove um endereço | #### Validações e Segurança O recurso de Endereços implementa diversas validações para garantir a integridade e precisão dos dados: 1. **Validação de Código Postal** - Formatos específicos são validados de acordo com o país informado 2. **Códigos de País Padronizados** - Utilização de códigos ISO 3166-1 alpha-2 para identificação de países 3. **Verificação de Campos Obrigatórios** - Garantia de que informações essenciais sejam fornecidas 4. **Limitação de Tamanho de Campos** - Prevenção contra dados excessivamente longos ou inválidos 5. **Validação de Coordenadas Geográficas** - Verificação de valores de latitude e longitude dentro de intervalos válidos #### Códigos de País Suportados O sistema utiliza o padrão ISO 3166-1 alpha-2 para identificação de países. Alguns dos códigos mais comuns incluem: | Código | País | |--------|------| | `BR` | Brasil | | `US` | Estados Unidos | | `CA` | Canadá | | `AR` | Argentina | | `MX` | México | | `PT` | Portugal | | `ES` | Espanha | | `IT` | Itália | | `FR` | França | | `DE` | Alemanha | | `GB` | Reino Unido | | `JP` | Japão | #### Webhooks Acionados | Evento | Descrição | |--------|-----------| | `address.created` | Acionado quando um novo endereço é criado | | `address.updated` | Acionado quando um endereço é atualizado | | `address.deleted` | Acionado quando um endereço é deletado | #### Casos de Uso Comuns - **Checkout em E-commerce**: Gerenciamento de endereços de entrega e faturamento durante o processo de compra - **Perfil de Cliente**: Permitir que clientes gerenciem seus endereços cadastrados - **Logística**: Utilização de endereços para cálculo de frete e planejamento de entregas - **Faturamento**: Utilização de endereços para emissão de notas fiscais e documentos financeiros - **Análise Geográfica**: Estudo de distribuição geográfica de clientes e vendas - **Verificação de Cobertura**: Determinação de disponibilidade de serviços por região - **Entrega Localizada**: Configuração de serviços baseados em proximidade geográfica #### Melhores Práticas 1. **Sempre valide os dados de endereço** antes de enviá-los para a API 2. **Utilize o serviço de CEP** para preencher automaticamente campos de endereço 3. **Implemente tratamento de erros** adequado para lidar com falhas nas requisições 4. **Defina um endereço principal** para cada cliente para facilitar operações futuras 5. **Use o campo metadata** para armazenar informações adicionais específicas do seu negócio 6. **Mantenha os endereços atualizados** para garantir entregas bem-sucedidas 7. **Armazene coordenadas geográficas** para funcionalidades baseadas em localização #### Solução de Problemas Comuns | Problema | Possível Causa | Solução Recomendada | |----------|----------------|---------------------| | Falha na criação do endereço | Dados obrigatórios ausentes | Verifique se os campos obrigatórios (street, country, postcode) foram fornecidos | | Erro de validação de CEP | Formato de CEP inválido | Certifique-se de que o CEP está no formato correto para o país especificado | | Erro "Cliente não encontrado" | ID de cliente inválido | Verifique se o customerId fornecido existe e está ativo no sistema | | Coordenadas inválidas | `latitude`/`longitude` muito longos | Envie `latitude`/`longitude` como strings de no máximo 16 caracteres | | Endereço principal | Definição do endereço principal | Use o campo de entrada `primary: true`; a resposta também expõe o campo `primary`. | | Endereço não encontrado na listagem | Filtros incorretos na consulta | Verifique se os parâmetros de filtro estão corretos ou tente sem filtros | | Erro na validação do país | `country` fora de 2–3 caracteres | Envie o código do país com 2 a 3 caracteres (ex.: `BR`) | #### Integração com Outros Recursos O recurso de Endereços integra-se com: - **Customers**: Endereços são associados a clientes específicos através do `customerId` - **Transactions**: Endereços podem ser utilizados em transações para entrega e faturamento - **Utils/Postcode**: Serviço de consulta de CEP para preenchimento automático de campos - **Webhooks**: Notificações em tempo real sobre operações com endereços - **Finance**: Endereços são utilizados para documentos fiscais e financeiros --- ## Endereços - Criar Fonte: https://selectwin.io/docs/guide/addresses/create #### Visão Geral Este endpoint permite adicionar um novo endereço a um cliente existente no sistema. Permite o cadastro de endereços com informações completas, incluindo coordenadas geográficas e metadados personalizados, facilitando a gestão de informações de entrega e faturamento em sua plataforma. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **ID do Cliente**: Certifique-se de utilizar um ID de cliente válido e existente no sistema. - **Dados de Localização**: `latitude` e `longitude` são strings opcionais (máx. 16 caracteres cada). - **Formato do CEP**: O `postcode` é validado no formato brasileiro (CEP: 8 dígitos, com ou sem hífen) e é armazenado apenas com os dígitos. - **Campo primary**: Se definido como `true`, marca este endereço como principal do cliente. #### Descrição O endpoint **Criar Endereço** permite **adicionar um novo endereço a um cliente existente no sistema**, com validação de dados e opção de definir metadados personalizados. ##### Estrutura JSON - Parâmetros Obrigatórios ```json { "customerId": "cus_123456789", "street": "Avenida Paulista", "country": "BR", "postcode": "01310-100" } ``` ##### Estrutura JSON - Exemplo Completo ```json { "customerId": "cus_123456789", "street": "Avenida Paulista", "number": "1000", "complement": "Apto 123", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310-100", "latitude": "-23.5505", "longitude": "-46.6333", "primary": true, "metadata": { "type": "commercial", "notes": "Endereço comercial principal" } } ``` #### Parâmetros da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Restrições | |-----------|------|-------------|-----------|---------|------------| | `customerId` | string | Sim | Identificador único do cliente ao qual o endereço será associado (`cus_*`) | `cus_01hqzvabc` | Deve corresponder a um cliente existente | | `country` | string | Sim | País (padrão ISO 3166-1) | `"BR"` | 2 a 3 caracteres | | `postcode` | string | Sim | Código postal / CEP | `"01310-100"` | Formato brasileiro (CEP); armazenado só com dígitos | | `street` | string | Não | Nome da rua ou logradouro | `"Avenida Paulista"` | Máximo 255 caracteres | | `number` | string | Não | Número do endereço | `"1000"` | Máximo 100 caracteres | | `complement` | string | Não | Complemento do endereço (apartamento, sala, etc.) | `"Apto 123"` | Máximo 255 caracteres | | `district` | string | Não | Bairro ou distrito | `"Bela Vista"` | Máximo 100 caracteres | | `city` | string | Não | Cidade | `"São Paulo"` | Máximo 100 caracteres | | `state` | string | Não | Estado ou província | `"SP"` | Máximo 100 caracteres | | `latitude` | string | Não | Latitude da localização | `"-23.5505"` | Máximo 16 caracteres | | `longitude` | string | Não | Longitude da localização | `"-46.6333"` | Máximo 16 caracteres | | `primary` | boolean | Não | Indica se é o endereço principal do cliente | `true` | Padrão: `false` | | `metadata` | object | Não | Metadados adicionais para o endereço | `{"type": "commercial"}` | — | #### Resposta - 201 Created Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 201 Created e um objeto JSON com as informações do endereço criado. ##### Exemplo de Resposta Resposta root-level (conforme emitido pelo responser após mount "read" para create). ```json { "id": "addr_01hqzvabc", "ownerType": "customer", "ownerId": "cus_01hqzvabc", "street": "Av. Paulista", "number": "1000", "complement": "Sala 1", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310100", "latitude": null, "longitude": null, "primary": true, "line": "Av. Paulista, 1000 - Bela Vista, São Paulo - SP, 01310100", "line1": "Av. Paulista", "line2": "1000", "line3": "Bela Vista", "metadata": { "source": "checkout" }, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "GET", "description": "Read a address." }, "create": { "href": "https://api.selectwin.io/v1/addresses", "method": "POST", "description": "Create a new address." }, "update": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "PUT", "description": "Update the address." }, "delete": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "DELETE", "description": "Delete the address." }, "list": { "href": "https://api.selectwin.io/v1/addresses", "method": "GET", "description": "List all addresses." } } } ``` #### Atributos da Resposta A resposta é o objeto completo no nível raiz + `merchant` e `_links` (padrão para creates/reads). | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador único do endereço (`addr_*`) | | `ownerType` | string | Tipo do proprietário (`vendor`, `customer`, `company` ou `user`) | | `ownerId` | string | ID do proprietário (`cus_*` ou equivalente) | | `street` | string \| null | Nome da rua ou logradouro | | `number` | string \| null | Número do endereço | | `complement` | string \| null | Complemento | | `district` | string \| null | Bairro/distrito | | `city` | string \| null | Cidade | | `state` | string \| null | Estado | | `country` | string \| null | País (ISO 3166-1) | | `postcode` | string | CEP/código postal (somente dígitos) | | `latitude` | string \| null | Latitude | | `longitude` | string \| null | Longitude | | `primary` | boolean | Se é o endereço principal | | `line` | string \| null | Linha formatada completa | | `line1` / `line2` / `line3` | string \| null | Componentes da linha | | `metadata` | object \| null | Metadados | | `createdAt` | string (date-time) | Criação (ISO) | | `updatedAt` | string (date-time) | Atualização (ISO) | | `merchant` | object | Merchant (name, merchantId, isSubAccount) | | `_links` | object | Links HATEOAS (self, create, update, delete, list) | **Nota sobre input vs output:** A requisição aceita `customerId` (vínculo do dono); a resposta deriva `ownerType: "customer"` e `ownerId` (o `cus_*`). Os campos `line`/`line1`/`line2`/`line3` são compostos pelo servidor a partir dos componentes do endereço. #### Respostas de Erro ##### 400 Bad Request Esta resposta ocorre quando há erros de validação na requisição, como campos obrigatórios ausentes ou valores inválidos. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "country": "This field is required." }, { "postcode": "must be a Brazilian postal code" } ] } } ``` ##### 404 Not Found Ocorre quando o `customerId` informado não existe ou não pertence à sua conta (`invalidCustomerId`). ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "The provided customer id is invalid." } } ``` #### Casos de Uso 1. **Checkout em E-commerce**: Adição de endereços de entrega e faturamento durante o processo de compra 2. **Cadastro de Cliente**: Captura de endereços durante o registro inicial do cliente 3. **Perfil de Usuário**: Permitir que usuários adicionem múltiplos endereços em seu perfil 4. **Gestão Logística**: Cadastro de locais de entrega para planejamento de rotas #### Melhores Práticas 1. **Validação de Dados**: Valide sempre os dados de endereço antes de enviá-los para a API 2. **Utilização de Serviço de CEP**: Use o endpoint `/utils/postcode-validation` para preencher automaticamente campos de endereço 3. **Metadados Personalizados**: Utilize o campo `metadata` para armazenar informações específicas do seu negócio 4. **Tratamento de Erros**: Implemente tratamento adequado para todos os códigos de erro possíveis 5. **Endereço Principal**: Quando apropriado, marque um endereço como principal (`primary: true`) para facilitar operações futuras --- ## Endereços - Listar Fonte: https://selectwin.io/docs/guide/addresses/list #### Visão Geral Este endpoint permite recuperar uma lista paginada de endereços, com opções de filtragem e ordenação. É especialmente útil para implementação de interfaces de seleção de endereço em processos de checkout, perfis de usuário e painéis administrativos. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Filtro por Cliente**: O parâmetro `customerid` é **opcional**. Quando informado, filtra os endereços pelo dono e valida que o cliente existe (caso contrário retorna 404). Sem ele, são listados os endereços da sua conta. - **Paginação**: Este endpoint retorna resultados paginados. Utilize os parâmetros `limit` e `offset` para navegar entre as páginas. - **Volume de Dados**: Para muitos endereços, considere implementar paginação eficiente para melhor desempenho. #### Descrição O endpoint **Listar Endereços** permite **consultar a lista de endereços cadastrados, com suporte a paginação, ordenação e filtragem por cliente**. #### Requisição ``` GET /v1/addresses ``` #### Parâmetros da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Localização | |-----------|------|-------------|-----------|---------|------------| | `customerid` | string | Não | Filtra endereços pelo ID do cliente dono (`cus_*`) | `cus_123456789` | Query | | `limit` | integer | Não | Número máximo de registros (1–100, padrão: 20) | `20` | Query | | `offset` | integer | Não | Posição inicial dos resultados (mínimo 0, padrão: 0) | `0` | Query | | `sort` | string | Não | Direção da ordenação (`ascending` ou `descending`, padrão: `ascending`) | `ascending` | Query | Também são aceitos os filtros de intervalo de data: `daterange`, `daterangegt`, `daterangegte`, `daterangelt`, `daterangelte`. > **Atenção:** o parâmetro é `customerid` (tudo minúsculo) e **não** existe filtro `isPrimary` nesta rota. #### Resposta - 200 OK Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 200 OK e um objeto JSON com a lista paginada de endereços. ##### Exemplo de Resposta ```json { "offset": 0, "limit": 20, "total": 42, "hasMore": true, "page": { "current": 1, "total": 3, "offset": { "first": 0, "prev": null, "next": 20, "last": 40 } }, "data": [ { "id": "addr_01hqzvabc", "ownerType": "customer", "ownerId": "cus_01hqzvabc", "street": "Av. Paulista", "number": "1000", "complement": null, "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310100", "latitude": null, "longitude": null, "primary": true, "line": "Av. Paulista, 1000 - Bela Vista, São Paulo SP BR 01310100", "line1": "Av. Paulista, 1000", "line2": "Bela Vista", "line3": "São Paulo SP BR 01310100", "metadata": { "source": "checkout" }, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z", "_links": { "self": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "GET", "description": "Read a address." } } } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/addresses", "method": "GET", "description": "List all addresses." }, "create": { "href": "https://api.selectwin.io/v1/addresses", "method": "POST", "description": "Create a new address." } } } ``` #### Atributos da Resposta ##### Atributos de Paginação (resposta raiz) | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` | integer | Posição inicial dos resultados retornados | | `limit` | integer | Quantidade máxima de registros retornados | | `total` | integer | Total de registros disponíveis para a consulta | | `hasMore` | boolean | Indica se existem mais resultados além dos retornados | | `page.current` | integer | Número da página atual | | `page.total` | integer | Total de páginas disponíveis | | `page.offset.first` / `prev` / `next` / `last` | integer \| null | Offsets para navegação de páginas | | `data` | array | Array de projeções leves de endereços | | `merchant` | object | Merchant associado | | `_links` | object | Links HATEOAS | ##### Atributos de cada objeto de Endereço (array `data`) Cada item de `data` é o **objeto completo** do endereço (mesmo shape do [Consultar Endereço](/docs/guide/addresses/read)), com seu próprio `_links`: | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador único do endereço (`addr_*`) | | `ownerType` | string | Tipo do dono (`vendor`, `customer`, `company` ou `user`) | | `ownerId` | string | ID do dono (`cus_*` ou equivalente) | | `street` / `number` / `complement` / `district` / `city` / `state` / `country` | string \| null | Componentes do endereço | | `postcode` | string | Código postal ou CEP (somente dígitos) | | `latitude` / `longitude` | string \| null | Coordenadas geográficas | | `primary` | boolean | Indica se é o endereço principal | | `line` / `line1` / `line2` / `line3` | string \| null | Endereço formatado em linhas | | `metadata` | object \| null | Metadados | | `createdAt` / `updatedAt` | string (date-time) | Timestamps ISO 8601 (UTC) | | `_links` | object | Links HATEOAS do item | #### Respostas de Erro ##### 400 Bad Request Esta resposta ocorre quando há erros nos parâmetros da requisição (ex.: `customerid` em formato inválido). ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "customerid": "must be a valid cus_ id" } ] } } ``` ##### 404 Not Found Ocorre quando o `customerid` informado não corresponde a um cliente existente na sua conta (`invalidCustomerId`). ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "The provided customer id is invalid." } } ``` #### Casos de Uso 1. **Seleção de Endereço**: Permitir que o cliente escolha entre seus endereços durante o checkout 2. **Gestão de Perfil**: Exibir todos os endereços cadastrados na área do cliente 3. **Dashboard Administrativo**: Visualizar endereços associados a um cliente específico 4. **Análise Geográfica**: Coletar dados de localização para análises de mercado e logística #### Melhores Práticas 1. **Cache de Resultados**: Para clientes com muitos endereços, considere armazenar em cache os resultados desta chamada 2. **Implementação de Paginação**: Utilize os parâmetros `offset` e `limit` para implementar paginação eficiente 3. **Filtro por Cliente**: Utilize o parâmetro `customerid` para obter apenas os endereços de um cliente específico 4. **Validação no Cliente**: Valide os parâmetros de consulta no lado do cliente antes de enviar a requisição 5. **Tratamento de Lista Vazia**: Implemente um tratamento adequado para quando a lista de endereços estiver vazia #### Integração com Outros Endpoints - Use [Consultar Endereço](/docs/guide/addresses/read) para obter detalhes completos de um endereço específico da lista - Use [Criar Endereço](/docs/guide/addresses/create) para adicionar um novo endereço quando a lista estiver vazia - Use [Excluir Endereço](/docs/guide/addresses/delete) para remover endereços obsoletos da lista --- ## Endereços - Consultar Fonte: https://selectwin.io/docs/guide/addresses/read #### Visão Geral Este endpoint permite consultar informações detalhadas sobre um endereço específico armazenado na plataforma. Facilita a verificação e utilização de dados de endereço em processos de checkout, gestão de clientes e operações logísticas. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **ID do Endereço**: Certifique-se de utilizar um ID de endereço válido e existente no sistema. - **Permissões**: Verifique se você possui as permissões necessárias para acessar os dados do endereço. - **Cache**: Considere implementar cache para endereços frequentemente acessados, a fim de reduzir a carga em sua aplicação. #### Descrição O endpoint **Consultar Endereço** permite **obter informações detalhadas de um endereço específico a partir do seu ID** de maneira rápida e eficiente. #### Requisição ``` GET /v1/addresses/{addressId} ``` #### Parâmetros da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Localização | |-----------|------|-------------|-----------|---------|------------| | `addressId` | string | Sim | Identificador único do endereço a ser consultado | `addr_01hqzvabc` | Path (caminho da URL) | #### Resposta - 200 OK Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 200 OK e um objeto JSON com as informações completas do endereço. ##### Exemplo de Resposta Resposta root-level (mesmo shape de create/read via mount). ```json { "id": "addr_01hqzvabc", "ownerType": "customer", "ownerId": "cus_01hqzvabc", "street": "Av. Paulista", "number": "1000", "complement": "Sala 1", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310100", "latitude": null, "longitude": null, "primary": true, "line": "Av. Paulista, 1000 - Bela Vista, São Paulo - SP, 01310100", "line1": "Av. Paulista", "line2": "1000", "line3": "Bela Vista", "metadata": { "source": "checkout" }, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "GET", "description": "Read a address." }, "create": { "href": "https://api.selectwin.io/v1/addresses", "method": "POST", "description": "Create a new address." }, "update": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "PUT", "description": "Update the address." }, "delete": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "DELETE", "description": "Delete the address." }, "list": { "href": "https://api.selectwin.io/v1/addresses", "method": "GET", "description": "List all addresses." } } } ``` #### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador único do endereço (`addr_*`) | | `ownerType` | string | Tipo do proprietário (`vendor`, `customer`, `company` ou `user`) | | `ownerId` | string | ID do proprietário | | `street` / `number` / `complement` / `district` / `city` / `state` / `country` | string \| null | Componentes do endereço | | `postcode` | string | CEP/código postal (somente dígitos) | | `latitude` / `longitude` | string \| null | Coordenadas geográficas | | `primary` | boolean | Se é o principal | | `line` / `line1` / `line2` / `line3` | string \| null | Endereço formatado em linhas | | `metadata` | object \| null | Metadados | | `createdAt` / `updatedAt` | string (date-time) | Timestamps ISO 8601 (UTC) | | `merchant` | object | Merchant associado (`name`, `merchantId`, `isSubAccount`) | | `_links` | object | Links HATEOAS (self, create, update, delete, list) | #### Respostas de Erro ##### 400 Bad Request Esta resposta ocorre quando o ID do endereço fornecido é inválido. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "addressId": "The provided address ID is invalid. Please check the ID and try again." } ] } } ``` ##### 404 Not Found Ocorre quando o `addressId` não existe ou não pertence à sua conta (`addressIdNotFound`). ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "Address not found." } } ``` #### Casos de Uso 1. **Checkout em E-commerce**: Exibição de endereços salvos durante o processo de checkout 2. **Perfil de Cliente**: Visualização de detalhes de endereço na área do cliente 3. **Gestão Logística**: Consulta de endereços para cálculo de frete e planejamento de entregas 4. **Validação de Dados**: Verificação de informações de endereço antes de processar uma transação #### Melhores Práticas 1. **Cache de Resultados**: Considere armazenar em cache os resultados desta chamada para endereços frequentemente acessados 2. **Tratamento de Erros**: Implemente tratamento adequado para todos os códigos de erro possíveis 3. **Validação Visual**: Se exibir o endereço para o usuário, considere utilizar um mapa para validação visual 4. **Associação com Cliente**: Verifique se o endereço pertence ao cliente correto antes de utilizar em operações sensíveis 5. **Integração com Outros Serviços**: Utilize as coordenadas geográficas (latitude e longitude) para integração com serviços de geolocalização #### Integração com Outros Endpoints - Use [Listar Endereços](/docs/guide/addresses/list) para obter todos os endereços de um cliente - Use [Excluir Endereço](/docs/guide/addresses/delete) para remover o endereço após a consulta, se necessário - Use [Criar Endereço](/docs/guide/addresses/create) para adicionar um novo endereço com base em um existente --- ## Endereços - Atualizar Fonte: https://selectwin.io/docs/guide/addresses/update #### Visão Geral Este endpoint permite atualizar os dados de um endereço existente a partir do seu identificador único (`addr_*`). É útil para corrigir informações de logradouro, complementar dados parciais e manter os endereços de entrega/faturamento sempre atualizados. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **ID do Endereço**: Confirme que o `addressId` no path corresponde ao endereço correto — atualizar o registro errado pode afetar entregas e faturamento. - **Atualização parcial**: Na rota standalone (`PUT /v1/addresses/{addressId}`), **todos os campos do corpo são opcionais** — envie apenas os que deseja alterar; os omitidos permanecem inalterados. - **Formato do CEP**: Quando `postcode` é enviado, é validado no formato brasileiro (CEP) e armazenado apenas com os dígitos. - **Escopo multi-tenant**: Só é possível atualizar endereços que pertencem à sua conta (merchant). #### Descrição O endpoint **Atualizar Endereço** modifica os dados de um endereço já cadastrado. A identificação do endereço vem do path (`addressId`) e os novos valores vão no corpo da requisição. A resposta retorna o objeto de endereço completo, no nível raiz, acompanhado de `merchant` e `_links` (HATEOAS), no mesmo formato de **Consultar** e **Criar**. #### Requisição ``` PUT /v1/addresses/{addressId} ``` ##### Parâmetros de Caminho (Path) | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Localização | |-----------|------|-------------|-----------|---------|------------| | `addressId` | string | Sim | ID público do endereço a ser atualizado (`addr_*`) | `addr_01hqzvabc` | Path | ##### Corpo da Requisição (Body) ```json { "street": "Avenida Paulista", "number": "1000", "complement": "Apto 123", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310-100" } ``` ##### Atributos do Corpo da Requisição Todos os campos são **opcionais** (atualização parcial). As mesmas regras de validação do [Criar Endereço](/docs/guide/addresses/create) aplicam-se a cada campo enviado. | Atributo | Tipo | Obrigatório | Descrição | Exemplo | Restrições | |----------|------|-------------|-----------|---------|------------| | `country` | string | Não | País (padrão ISO 3166-1) | `"BR"` | 2 a 3 caracteres | | `postcode` | string | Não | Código postal / CEP | `"01310-100"` | Formato brasileiro (CEP) | | `street` | string | Não | Nome da rua ou logradouro | `"Avenida Paulista"` | Máx. 255 caracteres | | `number` | string | Não | Número do endereço | `"1000"` | Máx. 100 caracteres | | `complement` | string | Não | Complemento (apartamento, sala, etc.) | `"Apto 123"` | Máx. 255 caracteres | | `district` | string | Não | Bairro ou distrito | `"Bela Vista"` | Máx. 100 caracteres | | `city` | string | Não | Cidade | `"São Paulo"` | Máx. 100 caracteres | | `state` | string | Não | Estado ou província | `"SP"` | Máx. 100 caracteres | | `latitude` | string | Não | Latitude | `"-23.5505"` | Máx. 16 caracteres | | `longitude` | string | Não | Longitude | `"-46.6333"` | Máx. 16 caracteres | | `primary` | boolean | Não | Marca como endereço principal | `true` | — | | `metadata` | object | Não | Metadados | `{"type": "commercial"}` | — | > **Nota:** Este endpoint não altera o vínculo do proprietário (`ownerType`/`ownerId`). #### Resposta - 200 OK Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 200 OK e o objeto de endereço atualizado, no nível raiz, com `merchant` e `_links`. ##### Exemplo de Resposta ```json { "id": "addr_01hqzvabc", "ownerType": "customer", "ownerId": "cus_01hqzvabc", "street": "Av. Paulista", "number": "1000", "complement": "Apto 123", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310100", "latitude": null, "longitude": null, "primary": true, "line": "Av. Paulista, 1000 - Bela Vista, São Paulo - SP, 01310100", "line1": "Av. Paulista", "line2": "1000", "line3": "Bela Vista", "metadata": { "source": "checkout" }, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "GET", "description": "Read a address." }, "create": { "href": "https://api.selectwin.io/v1/addresses", "method": "POST", "description": "Create a new address." }, "update": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "PUT", "description": "Update the address." }, "delete": { "href": "https://api.selectwin.io/v1/addresses/addr_01hqzvabc", "method": "DELETE", "description": "Delete the address." }, "list": { "href": "https://api.selectwin.io/v1/addresses", "method": "GET", "description": "List all addresses." } } } ``` ##### Atributos da Resposta A resposta é o objeto completo no nível raiz + `merchant` e `_links` (mesmo formato de **Consultar**/**Criar**). | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador único do endereço (`addr_*`) | | `ownerType` | string | Tipo do proprietário (`vendor`, `customer`, `company` ou `user`) | | `ownerId` | string | ID do proprietário (`cus_*` ou equivalente) | | `street` | string \| null | Nome da rua ou logradouro | | `number` | string \| null | Número do endereço | | `complement` | string \| null | Complemento | | `district` | string \| null | Bairro/distrito | | `city` | string \| null | Cidade | | `state` | string \| null | Estado | | `country` | string \| null | País (ISO 3166-1) | | `postcode` | string | CEP/código postal (somente dígitos) | | `latitude` | string \| null | Latitude | | `longitude` | string \| null | Longitude | | `primary` | boolean | Se é o endereço principal | | `line` | string \| null | Linha formatada completa | | `line1` / `line2` / `line3` | string \| null | Componentes da linha | | `metadata` | object \| null | Metadados | | `createdAt` | string (date-time) | Criação (ISO) | | `updatedAt` | string (date-time) | Atualização (ISO) — refletirá o momento da atualização | | `merchant` | object | Merchant (name, merchantId, isSubAccount) | | `_links` | object | Links HATEOAS (self, create, update, delete, list) | #### Respostas de Erro ##### 400 Bad Request Ocorre quando há erros de validação na requisição, como valores fora do formato esperado. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "postcode": "must be a Brazilian postal code" } ] } } ``` ##### 404 Not Found Ocorre quando o `addressId` informado não existe ou não pertence à sua conta (`addressIdNotFound`). ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "Address not found." } } ``` #### Casos de Uso 1. **Correção de Endereço**: Ajustar logradouro, número ou CEP digitados incorretamente 2. **Atualização Pós-Mudança**: Atualizar o endereço quando o cliente muda de local 3. **Enriquecimento de Dados**: Complementar um endereço cadastrado parcialmente (ex.: adicionar bairro e complemento) 4. **Padronização**: Normalizar dados de endereço após validação de CEP #### Melhores Práticas 1. **Consultar Antes de Atualizar**: Use [Consultar Endereço](/docs/guide/addresses/read) para obter os dados atuais antes de enviar a atualização 2. **Validação de CEP**: Quando atualizar o `postcode`, garanta um valor válido para evitar falhas de validação e inconsistências de entrega 3. **Enviar Apenas o Necessário**: Como é uma atualização parcial, envie somente os campos que deseja alterar 4. **Tratamento de Erros**: Trate explicitamente `400` (validação) e `404` (endereço inexistente) #### Integração com Outros Endpoints - Use [Consultar Endereço](/docs/guide/addresses/read) para verificar os dados antes e depois da atualização - Veja [Endereços - Visão Geral](/docs/guide/addresses/overview) para o ciclo de vida completo do recurso - Use [Criar Endereço](/docs/guide/addresses/create) para cadastrar novos endereços --- ## Endereços - Excluir Fonte: https://selectwin.io/docs/guide/addresses/delete #### Visão Geral Este endpoint permite remover permanentemente um endereço do sistema. É especialmente útil para manter os dados do cliente organizados e atualizados, eliminando endereços obsoletos ou incorretos. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Operação Irreversível**: A exclusão de um endereço é uma operação irreversível. Todos os dados associados ao endereço serão permanentemente removidos. - **ID do Endereço**: Certifique-se de utilizar um ID de endereço válido e existente no sistema. - **Endereço Principal**: Se o endereço estiver marcado como `primary: true`, verifique se o cliente possui outros endereços antes de excluí-lo. #### Descrição O endpoint **Excluir Endereço** permite **remover permanentemente um endereço do sistema**, ajudando a manter os dados do cliente limpos e organizados. #### Requisição ``` DELETE /v1/addresses/{addressId} ``` #### Parâmetros da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Localização | |-----------|------|-------------|-----------|---------|------------| | `addressId` | string | Sim | Identificador único do endereço a ser excluído | `addr_01hqzvabc` | Path (caminho da URL) | #### Resposta - 200 OK A deleção retorna confirmação mínima com status 200 (não 204; o corpo confirma a ação). ```json { "id": "addr_01hqzvabc", "resource": "address", "deleted": true, "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false } } ``` > **Nota:** A confirmação de exclusão de endereço **não** inclui `_links`. ##### Atributos da Resposta de Deleção | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | ID do recurso excluído (`addr_*`) | | `resource` | string | Tipo: `"address"` | | `deleted` | boolean | Confirmação: `true` | | `merchant` | object | Merchant da conta (`name`, `merchantId`, `isSubAccount`) | #### Respostas de Erro ##### 400 Bad Request Esta resposta ocorre quando o ID do endereço fornecido é inválido. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "addressId": "The provided address ID is invalid. Please check the ID and try again." } ] } } ``` ##### 404 Not Found Ocorre quando o `addressId` não existe ou não pertence à sua conta (`addressIdNotFound`). ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "Address not found." } } ``` #### Casos de Uso 1. **Manutenção de Perfil**: Remoção de endereços obsoletos ou incorretos do perfil do cliente 2. **Mudança de Endereço**: Exclusão de endereços antigos após o cliente se mudar 3. **Privacidade de Dados**: Remoção de informações pessoais a pedido do cliente 4. **Correção de Dados**: Eliminação de endereços duplicados ou com informações inconsistentes #### Melhores Práticas 1. **Verificação Prévia**: Consulte o endereço antes de excluí-lo para confirmar que é o correto 2. **Gerenciamento de Endereço Principal**: Se excluir um endereço principal, certifique-se de definir outro como principal primeiro 3. **Confirmação do Usuário**: Solicite confirmação do usuário antes de executar a exclusão 4. **Backup de Dados**: Considere armazenar temporariamente os dados do endereço caso o cliente precise recuperá-los 5. **Atualização de Interfaces**: Atualize imediatamente quaisquer interfaces ou caches que possam estar exibindo o endereço excluído #### Integração com Outros Endpoints - Use [Consultar Endereço](/docs/guide/addresses/read) antes da exclusão para verificar os detalhes do endereço - Use [Listar Endereços](/docs/guide/addresses/list) para encontrar todos os endereços de um cliente e decidir qual excluir - Use [Criar Endereço](/docs/guide/addresses/create) se o cliente precisar adicionar um novo endereço após a exclusão --- ## Cartões - Visão Geral Fonte: https://selectwin.io/docs/guide/cards/overview #### Introdução O recurso de Cartões é um componente fundamental da Selectwin API, permitindo tokenizar e gerenciar os cartões dos clientes. Este recurso possibilita transações online, facilita pagamentos recorrentes e oferece uma experiência de compra simplificada para os usuários finais. A API de Cartões foi projetada com foco na segurança e na facilidade de integração, permitindo que os desenvolvedores implementem soluções de pagamento robustas e confiáveis. #### Conceito e Funcionalidade Os cartões representam instrumentos de pagamento tokenizados dentro da plataforma: a sua integração usa a referência (`card_...`), nunca o número do cartão. Este recurso permite que os clientes salvem seus métodos de pagamento para uso futuro, eliminando a necessidade de inserir dados de cartão repetidamente. Cada cartão está associado a um cliente específico e pode ser utilizado em diversas operações financeiras, como pagamentos únicos, assinaturas, cobranças recorrentes e reembolsos. #### Autenticação e Base URL - **Base URL**: `https://api.selectwin.io/v1` - **Header de autenticação**: `SelectKey: sk_test_...` (sandbox) ou `SelectKey: sk_live_...` (produção). O ambiente da chave (`live`/`sandbox`) define em qual namespace o cartão é criado e listado. - **Escopos**: `cards:create`, `cards:read`, `cards:list`, `cards:delete`. #### Como um cartão entra | Caminho | Por onde passa o número | Quando usar | |---|---|---| | [Tokenização no navegador](/docs/guide/cards/tokenization) → `token` | o navegador do comprador | página de pagamento própria (plugins, apps) | | [Criar Cartão](/docs/guide/cards/create) com os dados completos | o seu servidor | integrações servidor-a-servidor | | Checkout hospedado ([Sessions](/docs/guide/checkouts/sessions)) | só a página da Selectwin | quando você não quer a página | #### Estrutura do Objeto Cartão O objeto Cartão contém informações sobre o instrumento de pagamento, porém com restrições de segurança para proteger dados sensíveis. A estrutura básica inclui: ```json { "id": "card_01hqzvabc", "holderName": "João Silva", "brand": "Visa", "firstDigits": "411111", "lastDigits": "1111", "expirationMonth": "12", "expirationYear": "2030", "primary": true, "active": true, "valid": true, "verified": true, "associated": true, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/cards/card_01hqzvabc", "method": "GET", "description": "Read a card." } } } ``` **Notas importantes:** - O objeto de cartão **não** possui campo `metadata`. - As flags de saída usam os nomes `primary`, `active`, `valid`, `verified`, `associated` (sem o prefixo `is`). - `associated` pode ser `null` — ver abaixo. - `brand` é retornado capitalizado (ex.: `Visa`, `Mastercard`, `Amex`, `Elo`), não em minúsculas. - `holderName` é normalizado para *Title Case* na criação (ex.: `joão silva` → `João Silva`). - Exemplos de resposta de recursos individuais incluem `merchant` + `_links`. A estrutura de cada item de lista é mais leve (sem `merchant`/`_links` por item). #### Recursos Disponíveis | Recurso | Método | Endpoint | Descrição | |---------|--------|----------|-----------| | [Criar Cartão](/docs/guide/cards/create) | POST | `/v1/cards` | Cria um novo cartão para um cliente | | [Consultar Cartão](/docs/guide/cards/read) | GET | `/v1/cards/{cardId}` | Obtém detalhes de um cartão específico | | [Listar Cartões](/docs/guide/cards/list) | GET | `/v1/cards` | Lista cartões com opções de paginação e filtros | | [Excluir Cartão](/docs/guide/cards/delete) | DELETE | `/v1/cards/{cardId}` | Remove um cartão do sistema | #### Segurança 1. **Tokenização** - A Selectwin tokeniza o cartão: a sua integração nunca precisa guardar dados de cartão, só a referência (`card_...`) 2. **A resposta da API não devolve dados sensíveis** - Apenas bandeira e primeiros/últimos dígitos, para identificação 3. **Dados protegidos em trânsito** - Toda comunicação com a API usa TLS 1.2 ou superior #### Bandeiras Suportadas A bandeira é detectada automaticamente a partir do número do cartão. A coluna **Código** é o valor usado na lista de bandeiras aceitas; o campo **`brand`** retornado na resposta usa o valor capitalizado. | Bandeira | Código (`brandType`) | `brand` retornado | |----------|----------------------|-------------------| | Visa | `visa` | `Visa` | | Mastercard | `mastercard` | `Mastercard` | | American Express | `amex` | `Amex` | | Elo | `elo` | `Elo` | | Hipercard | `hipercard` | `Hipercard` | | Diners Club | `dinersclub` | `Dinersclub` | | Discover | `discover` | `Discover` | | JCB | `jcb` | `Jcb` | > **Nota:** as bandeiras aceitas são configuráveis por ambiente; por padrão a lista acima é aceita. Um cartão de bandeira não reconhecida é detectado como `Unknown` e a criação é rejeitada com `cardBrandNotSupported` (HTTP 400). #### Webhooks Acionados | Evento | Descrição | |--------|-----------| | `card.created` | Acionado quando um novo cartão é registrado no sistema | | `card.updated` | Acionado quando os dados de um cartão são alterados | | `card.deleted` | Acionado quando um cartão é removido do sistema | | `card.expired` | Acionado quando o token de um cartão expira | #### Casos de Uso Comuns - **Pagamentos Recorrentes**: Armazene cartões para cobranças automáticas em assinaturas - **Checkout Simplificado**: Permita que clientes utilizem cartões salvos para novas compras - **Marketplace**: Armazene cartões para pagamentos a múltiplos vendedores - **Gestão de Métodos de Pagamento**: Permita que clientes gerenciem seus cartões salvos - **Transações One-Click**: Implemente compras com um clique para melhorar a conversão - **Análise de Risco**: Utilize informações de cartão para avaliação de risco nas transações #### Melhores Práticas 1. **Sempre valide os dados do cartão** antes de enviá-los para a API 2. **Utilize tokenização** quando disponível para maior segurança 3. **Implemente tratamento de erros** adequado para lidar com falhas nas requisições 4. **Mantenha a interface do usuário atualizada** com os cartões disponíveis 5. **Verifique a validade dos cartões** antes de utilizá-los em transações 6. **Ofereça opções para definir cartão padrão** para melhorar a experiência do usuário 7. **Notifique clientes sobre cartões próximos ao vencimento** para evitar falhas em pagamentos futuros #### Solução de Problemas Comuns | Problema | Possível Causa | Solução Recomendada | |----------|----------------|---------------------| | Falha na criação do cartão | Dados do cartão inválidos | Verifique se o número, data de validade e nome do portador estão corretos | | Erro "Cartão não autorizado" | Cartão com restrições | Sugira ao cliente utilizar outro cartão ou entrar em contato com o banco emissor | | Cartão não aparece na listagem | Filtros incorretos na consulta | Verifique os parâmetros de filtro ou tente sem filtros | | Erro "Cartão expirado" | Data de validade ultrapassada | Solicite ao cliente um cartão com data de validade válida | | Falha ao processar pagamento | Fundos insuficientes ou cartão bloqueado | Sugira ao cliente verificar o saldo ou status do cartão com o banco emissor | | Erro na validação do CVV | Código de segurança incorreto | Solicite ao cliente verificar e reinserir o código de segurança correto | | Cartão recusado por análise de risco | Alto risco de fraude detectado | Sugira ao cliente utilizar outro método de pagamento ou entrar em contato com o suporte | #### Integração com Outros Recursos O recurso de Cartões integra-se com: - **Customers**: Cartões são associados a clientes específicos através do `customerId` - **Transactions**: Cartões podem ser utilizados em transações para processamento de pagamentos - **Wallets**: Cartões podem ser adicionados a carteiras digitais para facilitar pagamentos - **Utils/Card-Bin-Validation**: Serviço de validação de BIN para verificação e identificação de cartões - **Simulators**: Simulações de pagamento podem ser realizadas com base em informações de cartão #### `associated: null` — o cartão de porta do comprador antes da 1ª cobrança aprovada `associated` diz se o cartão foi associado ao cliente — a validação de **valor zero**, que pergunta ao emissor se o cartão é bom sem cobrar nada. Quando o cartão entra por uma porta do **comprador** — o checkout hospedado, o portal do assinante, o cartão enviado em `payment.card` numa cobrança ou assinatura e o `token` cunhado no navegador do comprador (também quando salvo pela conta em `POST /v1/cards`) — esse resultado **não é exibido à conta** no cartão criado, antes da primeira cobrança aprovada com ele: | Situação | `associated` | |---|---| | Cartão criado pela própria conta em `POST /v1/cards` com os **dados completos** | `false` na criação; depois `true` / `false`, como sempre | | Cartão do checkout hospedado, do portal, de `payment.card` numa cobrança/assinatura ou criado por **`token`** (em `POST /v1/cards` ou na cobrança), sem cobrança aprovada | `null` (pendente) — com a validação feita ou adiada, o cartão criado responde o mesmo | | O mesmo cartão depois de uma cobrança aprovada (aprovada, estornada, contestada) | `true` / `false` | A exceção é o **`token`** **recusado** na validação prévia — em `POST /v1/cards` ou em `payment.card.token` numa cobrança ou assinatura: a chamada responde `422 cardDeclined` e o cartão **não é criado** (veja os erros em [Criar Cartão](/docs/guide/cards/create) e em [Tokenização no navegador](/docs/guide/cards/tokenization)). Gere outro token com outro cartão. Vale em todos os lugares em que o cartão aparece: `GET /v1/cards/:id`, `GET /v1/cards` (inclusive `?holderid=`), o cartão embutido em `GET /v1/customers/:id` e nos eventos `customer.*`, e os eventos `card.created`, `card.updated` e `card.expired`. O motivo: a validação de valor zero responde "este cartão é válido?" sem cobrança, sem antifraude e sem tarifa — exibida para quem não a fez, vira um verificador de cartão roubado. A venda não depende do campo: a cobrança associa o cartão no caminho governado (antifraude, risco, tarifas) e o resultado dela é o da transação. --- ## Cartões - Criar Fonte: https://selectwin.io/docs/guide/cards/create #### Visão Geral Este endpoint permite registrar um novo cartão de crédito de forma segura, possibilitando usá-lo em transações futuras, mediante autorização do titular. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Segurança**: Os dados do cartão são transmitidos de forma segura usando criptografia TLS/SSL. - **Conformidade PCI**: Garanta que sua aplicação esteja em conformidade com as diretrizes PCI DSS ao capturar dados de cartões. - **Validação**: O sistema realiza validações básicas do número do cartão antes do processamento. - **Tokens**: Considere usar tokenização para minimizar o manuseio direto de dados de cartão. #### Descrição O endpoint **Criar Cartão** permite **cadastrar um novo cartão no sistema, associando-o a um cliente específico**. Os dados são validados e o cartão é tokenizado para uso em transações futuras. #### Requisição ``` POST /v1/cards ``` > Este endpoint aceita **duas formas**: os **dados completos do cartão** (número e CVV, que a Selectwin tokeniza) **ou** um **`token` cunhado pelo navegador do comprador** ([Tokenização no navegador](/docs/guide/cards/tokenization)) — nesse caso o número do cartão nunca passa pelo seu servidor. Para cobrar usando um cartão já salvo, informe o `id` do cartão no corpo da transação/assinatura (não neste endpoint). > **Reenviar o mesmo cartão do mesmo cliente não duplica.** O cadastro é deduplicado por cliente: o mesmo número, para o **mesmo** `customerId`, devolve o cartão que já existe (mesmo `id`, `201`). Para um cliente **diferente**, o mesmo número gera um cartão próprio — um cartão pertence ao seu titular, e é o titular que a cobrança confere (`The card does not belong to the customer.`, 422). É o caso do casal que divide o cartão e do comprador que volta com outro e-mail. #### Corpo da Requisição **Forma 1 — token do navegador** (recomendada para página própria): ```json { "customerId": "cus_123456789", "token": "a1b2c3d4e5f6…", "primary": true } ``` **Forma 2 — dados completos do cartão:** ```json { "numbering": "4111111111111111", "holderName": "João Silva", "expirationMonth": 12, "expirationYear": 2030, "securityCode": "123", "customerId": "cus_123456789", "primary": true } ``` #### Parâmetros da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `customerId` | string | Sim | ID do cliente (`cus_*`) ao qual o cartão será associado | `"cus_123456789"` | | `token` | string | Sim* | Token de uso único cunhado no navegador ([Tokenização no navegador](/docs/guide/cards/tokenization)). *Alternativa aos dados completos; quando enviado, nenhum campo de cartão pode vir junto | `"a1b2c3d4e5f6…"` | | `holderName` | string | Sim** | Nome completo do titular (3 a 255 caracteres). **Obrigatório sem `token` | `"João Silva"` | | `numbering` | string | Sim** | Número completo do cartão (validado por Luhn). **Obrigatório sem `token` | `"4111111111111111"` | | `expirationMonth` | integer | Sim** | Mês de validade (1 a 12). **Obrigatório sem `token` | `12` | | `expirationYear` | integer | Sim** | Ano de validade. Aceita 4 dígitos (`2030`) ou 2 dígitos (`30` → `2030`). Não pode estar no passado. **Obrigatório sem `token` | `2030` | | `securityCode` | string | Sim** | Código de segurança / CVV (3 ou 4 dígitos). Nunca é retornado. **Obrigatório sem `token` | `"123"` | | `primary` | boolean | Não | Define se este é o cartão principal do cliente. Quando omitido, assume `true` | `true` | > **Observações:** > - `expirationMonth`/`expirationYear` aceitam números ou strings numéricas. A combinação mês/ano deve ser futura, caso contrário a requisição é rejeitada com `cardExpired`. > - O campo é `primary` (não `isPrimary`). > - Não há campo `metadata` neste recurso. #### Resposta - 201 Created Se a requisição for bem-sucedida, um objeto JSON com os detalhes do cartão criado é retornado. ##### Exemplo de Resposta Resposta root (create retorna shape completo de leitura do cartão tokenizado + merchant/_links). ```json { "id": "card_01hqzvabc", "holderName": "João Silva", "brand": "Visa", "firstDigits": "411111", "lastDigits": "1111", "expirationMonth": "12", "expirationYear": "2030", "primary": true, "active": true, "valid": true, "verified": false, "associated": false, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/cards/card_01hqzvabc", "method": "GET", "description": "Read a card." }, "delete": { "href": "https://api.selectwin.io/v1/cards/card_01hqzvabc", "method": "DELETE", "description": "Delete the card." }, "list": { "href": "https://api.selectwin.io/v1/cards", "method": "GET", "description": "List all cards." } } } ``` #### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador único (`card_*`) | | `holderName` | string | Nome do titular (normalizado para Title Case) | | `brand` | string | Bandeira capitalizada (`Visa`, `Mastercard`, `Amex`, `Elo`, ...) | | `firstDigits` | string | BIN (6 primeiros dígitos). Num cartão criado por `token`, os **4** primeiros | | `lastDigits` | string | Últimos 4 dígitos | | `expirationMonth` | string | Mês de validade (2 dígitos, ex.: `"12"`) | | `expirationYear` | string | Ano de validade (4 dígitos, ex.: `"2030"`) | | `primary` | boolean | Cartão principal | | `active` | boolean | Ativo (`true` na criação) | | `valid` | boolean | Válido (`true` na criação) | | `verified` | boolean | Verificado (`false` na criação) | | `associated` | boolean \| null | Associado ao cliente. `false` na criação pelos dados completos (a associação roda em seguida; depois, `true` ou `false`). `null` (pendente) na criação por `token` — o token foi cunhado no navegador de um comprador, então é uma porta do comprador, como o checkout hospedado, o portal e o `payment.card` de uma cobrança/assinatura — até a 1ª cobrança aprovada com o cartão; o cartão já pode ser cobrado. Ver [Visão geral](/docs/guide/cards/overview#associated-null) | | `createdAt` / `updatedAt` | string (date-time) | Timestamps ISO 8601 | | `merchant` | object | Bloco do merchant (`name`, `merchantId`, `isSubAccount`) | | `_links` | object | HATEOAS (`self`, `create`, `delete`, `list`) | **Notas:** - O corpo da requisição usa `primary`; a resposta usa os nomes sem prefixo `is` (`primary`, `active`, `valid`, `verified`, `associated`). - O recurso de cartão **não** retorna `metadata`. - Cartões são criados com `verified: false`; a verificação ocorre posteriormente no fluxo de pagamento. #### Respostas de Erro Todas as respostas de erro seguem o envelope canônico `{ "error": { status, statusCode, category, code, message, resource, details?, params? } }`. ##### 400 Bad Request - Bandeira não suportada ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "code": "cardBrandNotSupported", "message": "Card brand not supported.", "resource": "card", "details": "The card brand is not supported. Please check the card brand and try again." } } ``` ##### 400 Bad Request - Cartão expirado ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "code": "cardExpired", "message": "Card expired", "resource": "card", "details": "The provided card has expired. Please use a valid card." } } ``` ##### 404 Not Found - Cliente inválido Retornado quando o `customerId` informado não existe. ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "client", "code": "invalidCustomerId", "message": "Invalid customer ID.", "resource": "customer" } } ``` ##### Códigos de erro deste endpoint | HTTP | `code` | Quando ocorre | |------|--------|---------------| | 400 | (validação de campo) | Campos ausentes/ inválidos no corpo (Luhn, CVV, mês/ano). Inclui o array `params` por campo | | 400 | `cardExpired` | Mês/ano de validade já passou | | 400 | `cardBrandNotSupported` | Bandeira detectada fora da lista de bandeiras aceitas | | 401 | — | `SelectKey` ausente ou inválida | | 403 | — | Chave sem o escopo `cards:create` | | 404 | `invalidCustomerId` | `customerId` não encontrado | | 409 | — | Conflito de idempotência | | 422 | `cardTokenInvalid` | `token` desconhecido, expirado (1 h) ou já usado | | 422 | `cardDeclined` | O cartão do `token` foi recusado na validação | | 422 | `cardTokenCustomerUnavailable` | O cliente não pôde ser cadastrado para receber o cartão (dados incompletos/recusados) | | 429 | `cardAssociationRateLimited` | Não é mais devolvido por este endpoint. O teto de associações da conta vale para o cadastro com o cartão completo, que é assíncrono: acima do teto o cartão é criado, mas fica `associated: false` e só é validado na primeira cobrança. Com `token` (cunhado no navegador do comprador) e nos cartões que os compradores digitam — no checkout hospedado, no portal e o cartão enviado DENTRO de uma cobrança ou assinatura (`payment.card`) — conta a janela de cada comprador e um teto da loja para essas portas, que nunca responde 429: no teto, a validação prévia é adiada e o cartão é validado antes da cobrança. O cartão do `token` sai `associated: null` até a 1ª cobrança aprovada | | 503 | `cardTokenAssociationFailed` | A validação do cartão do `token` não respondeu a tempo. Nada foi cobrado; tente de novo com um token novo | | 503 | `tokenizationNotConfigured` | Ambiente sem tokenização no navegador | | 500 | — | Erro interno | #### Casos de Uso 1. **Checkout em E-commerce**: Permitir que o cliente salve um novo cartão durante o processo de compra 2. **Registro em Aplicativos**: Permitir que usuários adicionem cartões ao se cadastrarem ou posteriormente 3. **Subscrições e Recorrências**: Adicionar um cartão para cobranças recorrentes futuras 4. **Atualização após Expiracao**: Substituir um cartão expirado por uma nova versão #### Melhores Práticas 1. **Validações no Cliente**: Realize validações básicas do número do cartão, data de validade e CVV no frontend antes de enviar para a API 2. **Mascaramento de Dados**: Mascare os dados do cartão na interface do usuário 3. **Criptografia**: Utilize conexões seguras (HTTPS) e considere métodos adicionais de criptografia 4. **Confirmação do Titular**: Implemente métodos para confirmar que o usuário tem permissão para usar o cartão 5. **Gestão de Erros**: Forneça feedback claro aos usuários quando houver problemas na validação do cartão #### Integração com Outros Endpoints - Para não lidar com dado de cartão no seu servidor, use a [Tokenização no navegador](/docs/guide/cards/tokenization) - Use [Listar Cartões](/docs/guide/cards/list) para verificar se o cartão foi adicionado com sucesso - Use [Excluir Cartão](/docs/guide/cards/delete) caso o cliente deseje remover o cartão imediatamente - Cartões são **imutáveis**: para alterar dados (ex.: validade), exclua o cartão e cadastre um novo --- ## Cartões - Listar Fonte: https://selectwin.io/docs/guide/cards/list #### Visão Geral Este endpoint permite recuperar informações sobre todos os cartões armazenados para um cliente específico, facilitando a gestão de métodos de pagamento e oferecendo opções de filtragem e paginação para uma consulta eficiente. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Paginação**: Este endpoint retorna resultados paginados. Utilize os parâmetros `limit` e `offset` para navegar entre as páginas. - **ID do Cliente**: Para listar cartões de um cliente específico, forneça o ID do cliente como parâmetro. - **Dados Sensíveis**: Por razões de segurança, os números completos dos cartões nunca são retornados, apenas os primeiros e últimos dígitos. #### Descrição O endpoint **Listar Cartões** permite **consultar a lista de cartões cadastrados no sistema, com opções de filtragem por cliente e paginação**. #### Requisição ``` GET /v1/cards?limit=10&offset=0&sort=ascending&holderid=cus_123456789 ``` #### Parâmetros da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Valor Padrão | |-----------|------|-------------|-----------|---------|--------------| | `limit` | integer | Não | Número máximo de registros por página (1 a 100) | `10` | `10` | | `offset` | integer | Não | Número de registros a pular (paginação) | `0` | `0` | | `sort` | string | Não | Direção da ordenação por data de criação. Valores: `ascending` ou `descending` | `ascending` | `ascending` | | `holderid` | string | Não | Filtrar cartões pelo ID do cliente (`cus_*`) | `cus_123456789` | — | | `id` | string | Não | Filtrar por um ID de cartão específico (`card_*`) | `card_01hqzvabc` | — | | `daterange` | string | Não | Filtrar por data de criação exata (`YYYY-MM-DD` ou ISO 8601) | `2026-04-12` | — | | `daterangegt` | string | Não | Criados após o instante informado (ISO 8601) | `2026-04-01T00:00:00Z` | — | | `daterangegte` | string | Não | Criados em/após o instante informado (ISO 8601) | `2026-04-01T00:00:00Z` | — | | `daterangelt` | string | Não | Criados antes do instante informado (ISO 8601) | `2026-05-01T00:00:00Z` | — | | `daterangelte` | string | Não | Criados em/até o instante informado (ISO 8601) | `2026-05-01T00:00:00Z` | — | #### Resposta - 200 OK Se a requisição for bem-sucedida, um objeto JSON com a lista de cartões e informações de paginação é retornado. ##### Exemplo de Resposta ```json { "offset": 0, "limit": 10, "total": 2, "page": { "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 }, "current": 1, "total": 1 }, "hasMore": false, "data": [ { "id": "card_01hqzvabc", "holderName": "João Silva", "brand": "Visa", "firstDigits": "411111", "lastDigits": "1111", "expirationMonth": "12", "expirationYear": "2030", "primary": true, "active": true, "valid": true, "verified": true, "associated": true, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" }, { "id": "card_02hqzvxyz", "holderName": "João Silva", "brand": "Mastercard", "firstDigits": "510000", "lastDigits": "5555", "expirationMonth": "06", "expirationYear": "2031", "primary": false, "active": true, "valid": true, "verified": true, "associated": true, "createdAt": "2026-03-15T10:20:00.000Z", "updatedAt": "2026-03-15T10:20:00.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/cards", "method": "GET", "description": "List all cards." }, "create": { "href": "https://api.selectwin.io/v1/cards", "method": "POST", "description": "Create a new card." } } } ``` #### Atributos da Resposta ##### Atributos de Paginação | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` | integer | Posição inicial dos resultados retornados | | `limit` | integer | Quantidade máxima de registros retornados | | `total` | integer | Total de registros disponíveis para a consulta | | `page.offset.first` | integer | Offset da primeira página (sempre `0`) | | `page.offset.prev` | integer | Offset da página anterior (`0` quando não há anterior) | | `page.offset.next` | integer | Offset da próxima página (permanece no offset atual na última página) | | `page.offset.last` | integer | Offset da última página | | `page.current` | integer | Número da página atual (1-based) | | `page.total` | integer | Total de páginas disponíveis | | `hasMore` | boolean | Indica se existem mais resultados além dos retornados | | `data` | array | Array contendo os registros retornados | | `merchant` | object | Bloco do merchant (`name`, `merchantId`, `isSubAccount`) | | `_links` | object | HATEOAS de lista (`self`, `create`) | > Os offsets em `page.offset` são sempre inteiros (nunca `null`). ##### Atributos de cada objeto de Cartão no array `data` Cada item de `data` segue o mesmo objeto de cartão da leitura, porém **sem** os blocos `merchant` e `_links` (que aparecem apenas no nível da lista). | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador único do cartão (`card_*`) | | `holderName` | string | Nome completo do titular do cartão | | `brand` | string | Bandeira do cartão, capitalizada (ex.: `Visa`, `Mastercard`, `Elo`) | | `firstDigits` | string | Primeiros 6 dígitos do número do cartão (BIN) | | `lastDigits` | string | Últimos 4 dígitos do número do cartão | | `expirationMonth` | string | Mês de validade do cartão (2 dígitos) | | `expirationYear` | string | Ano de validade do cartão (4 dígitos) | | `primary` | boolean | Indica se é o cartão principal do cliente | | `active` | boolean | Indica se o cartão está ativo | | `valid` | boolean | Indica se o cartão é válido (não expirado e passou nas validações) | | `verified` | boolean | Indica se o cartão foi verificado | | `associated` | boolean \| null | Associado ao cliente. **`null` = pendente**: o cartão entrou por uma porta do COMPRADOR (checkout hospedado, portal do assinante, o `payment.card` de uma cobrança ou assinatura, o `token` cunhado no navegador — também o salvo em `/v1/cards`) e ainda não tem cobrança aprovada — o resultado da validação de valor zero não é exibido antes disso (ver [Visão geral](/docs/guide/cards/overview#associated-null)). Nos cartões criados pela própria conta em `/v1/cards` com os dados completos, `true`/`false` como sempre | | `createdAt` | string (date-time) | Data e hora de criação do cartão (ISO 8601) | | `updatedAt` | string (date-time) | Data e hora da última atualização do cartão (ISO 8601) | #### Respostas de Erro ##### 404 Not Found - Cliente Não Encontrado Retornado quando o filtro `holderid` aponta para um cliente inexistente. ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "client", "code": "invalidCustomerId", "message": "Invalid customer ID.", "resource": "customer" } } ``` > Um `holderid`/`id` com formato de prefixo inválido (sem `cus_`/`card_`) retorna `400 Bad Request` (validação de campo) com o array `params`. #### Casos de Uso 1. **Exibição em Perfil do Cliente**: Mostrar todos os cartões disponíveis na área do cliente 2. **Seleção de Método de Pagamento**: Permitir que o cliente escolha entre seus cartões durante o checkout 3. **Gestão de Cartões**: Apresentar uma interface para que o cliente gerencie seus cartões 4. **Dashboard Administrativo**: Visualizar todos os cartões associados a um cliente específico #### Melhores Práticas 1. **Implementação de Paginação**: Utilize os parâmetros `offset` e `limit` para implementar paginação eficiente 2. **Cache de Resultados**: Considere armazenar em cache os resultados por um curto período para melhorar o desempenho 3. **Tratamento de Lista Vazia**: Implemente um tratamento adequado para quando a lista de cartões estiver vazia 4. **Exibição Segura**: Sempre exiba apenas informações mascaradas dos cartões na interface do usuário 5. **Filtro de Cartão Principal**: Utilize o atributo `primary` para destacar o cartão principal do cliente #### Integração com Outros Endpoints - Use [Consultar Cartão](/docs/guide/cards/read) para obter detalhes completos de um cartão específico da lista - Use [Criar Cartão](/docs/guide/cards/create) para adicionar um novo cartão quando a lista estiver vazia ou incompleta - Use [Excluir Cartão](/docs/guide/cards/delete) para remover cartões indesejados ou expirados da lista --- ## Cartões - Consultar Fonte: https://selectwin.io/docs/guide/cards/read #### Visão Geral Este endpoint permite consultar informações detalhadas sobre um cartão específico, identificado pelo seu ID único. Os dados retornados incluem informações parciais do cartão (por segurança), detalhes sobre o estado do cartão, e metadados associados. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Segurança**: Apenas dados parciais do cartão são retornados (primeiros e últimos dígitos), nunca o número completo. - **Autenticação**: Certifique-se de que o usuário tem permissão para acessar os dados do cartão especificado. - **ID do Cartão**: O ID do cartão deve ser válido e existir no sistema. #### Descrição O endpoint **Consultar Cartão** permite **obter todas as informações disponíveis sobre um cartão específico cadastrado no sistema**, utilizando seu identificador único. #### Requisição ``` GET /v1/cards/{cardId} ``` #### Parâmetros da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `cardId` | string | Sim | Identificador único do cartão | `"card_01hqzvabc"` | #### Resposta - 200 OK Se a requisição for bem-sucedida, um objeto JSON com detalhes do cartão solicitado é retornado. ##### Exemplo de Resposta ```json { "id": "card_01hqzvabc", "holderName": "João Silva", "brand": "Visa", "firstDigits": "411111", "lastDigits": "1111", "expirationMonth": "12", "expirationYear": "2030", "primary": true, "active": true, "valid": true, "verified": true, "associated": true, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/cards/card_01hqzvabc", "method": "GET", "description": "Read a card." }, "delete": { "href": "https://api.selectwin.io/v1/cards/card_01hqzvabc", "method": "DELETE", "description": "Delete the card." }, "list": { "href": "https://api.selectwin.io/v1/cards", "method": "GET", "description": "List all cards." } } } ``` #### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador único do cartão (`card_*`) | | `holderName` | string | Nome completo do titular do cartão | | `brand` | string | Bandeira do cartão, capitalizada (ex.: `Visa`, `Mastercard`, `Elo`) | | `firstDigits` | string | Primeiros 6 dígitos do número do cartão (BIN) | | `lastDigits` | string | Últimos 4 dígitos do número do cartão | | `expirationMonth` | string | Mês de validade do cartão (2 dígitos) | | `expirationYear` | string | Ano de validade do cartão (4 dígitos) | | `primary` | boolean | Indica se é o cartão principal do cliente | | `active` | boolean | Indica se o cartão está ativo | | `valid` | boolean | Indica se o cartão é válido (não expirado e passou nas validações) | | `verified` | boolean | Indica se o cartão foi verificado | | `associated` | boolean \| null | Associado ao cliente. **`null` = pendente**: o cartão entrou por uma porta do COMPRADOR (checkout hospedado, portal do assinante, o `payment.card` de uma cobrança ou assinatura, o `token` cunhado no navegador — também o salvo em `/v1/cards`) e ainda não tem cobrança aprovada — o resultado da validação de valor zero não é exibido antes disso (ver [Visão geral](/docs/guide/cards/overview#associated-null)). Nos cartões criados pela própria conta em `/v1/cards` com os dados completos, `true`/`false` como sempre | | `createdAt` | string (date-time) | Data e hora de criação do cartão (ISO 8601) | | `updatedAt` | string (date-time) | Data e hora da última atualização do cartão (ISO 8601) | | `merchant` | object | Bloco do merchant (`name`, `merchantId`, `isSubAccount`) | | `_links` | object | HATEOAS (`self`, `create`, `delete`, `list`) | > O recurso de cartão **não** retorna `metadata`. As flags usam os nomes acima (sem prefixo `is`). #### Respostas de Erro Todas as respostas de erro seguem o envelope `{ "error": { status, statusCode, category, code, message, resource, details?, params? } }`. ##### 400 Bad Request - Formato de ID Inválido Retornado quando o `cardId` não tem o prefixo `card_`. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "code": "validation", "message": "Validation errors occurred.", "resource": "card", "params": [ { "cardId": "must be a valid card_ id" } ] } } ``` ##### 404 Not Found - Cartão não encontrado ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "client", "code": "cardIdNotFound", "message": "Card ID not found.", "resource": "card", "details": "The card ID was not found. Please check the card ID and try again." } } ``` ##### 409 Conflict - Cartão expirado Retornado quando o cartão existe mas já está marcado como expirado. ```json { "error": { "status": "Conflict", "statusCode": 409, "category": "client", "code": "cardExpired", "message": "Card expired", "resource": "card", "details": "The provided card has expired. Please use a valid card." } } ``` #### Casos de Uso 1. **Exibição de Detalhes do Cartão**: Mostrar informações do cartão na interface do cliente 2. **Verificação de Status**: Verificar se um cartão está ativo, válido ou marcado como primário 3. **Confirmação Antes do Pagamento**: Exibir informações mascaradas do cartão para confirmação antes de efetuar uma transação 4. **Validação Antes de Operações**: Verificar a validade e o estado do cartão antes de operações críticas #### Melhores Práticas 1. **Caching Limitado**: Implemente cache com tempo de expiração curto para reduzir chamadas repetidas ao API 2. **Exibição Segura**: Sempre exiba apenas informações mascaradas dos cartões na interface do usuário 3. **Verificações Regulares**: Verifique periodicamente a validade dos cartões armazenados para notificar clientes sobre cartões prestes a expirar 4. **Tratamento de Erros**: Implemente tratamento adequado para os diferentes cenários de erro 5. **Verificação de Permissões**: Confirme que o usuário atual tem permissão para acessar as informações do cartão solicitado #### Integração com Outros Endpoints - Use [Listar Cartões](/docs/guide/cards/list) para obter todos os cartões de um cliente - Cartões são **imutáveis**: para alterar dados, exclua o cartão e cadastre um novo - Use [Excluir Cartão](/docs/guide/cards/delete) para remover um cartão que não será mais utilizado --- ## Cartões - Excluir Fonte: https://selectwin.io/docs/guide/cards/delete #### Visão Geral Este endpoint permite remover um cartão salvo de um cliente, facilitando a gestão de métodos de pagamento obsoletos ou não utilizados. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Operação Irreversível**: A exclusão de um cartão é uma operação irreversível: o cartão excluído não volta a ser listado nem pode ser usado em novas cobranças. - **ID do Cartão**: Certifique-se de utilizar um ID de cartão válido e existente no sistema. - **Cartão Principal**: Tenha cuidado ao excluir um cartão principal. Certifique-se de que o cliente tenha outro cartão definido como principal antes de excluir o atual. #### Descrição O endpoint **Excluir Cartão** permite **remover permanentemente um cartão do sistema**. #### Requisição ``` DELETE /v1/cards/{cardId} ``` #### Parâmetros da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | Localização | |-----------|------|-------------|-----------|---------|------------| | `cardId` | string | Sim | Identificador único do cartão a ser excluído | `card_01hqzvabc` | Path (caminho da URL) | #### Resposta - 200 OK Se a requisição for bem-sucedida, um objeto JSON com a confirmação da exclusão é retornado. ##### Exemplo de Resposta ```json { "id": "card_01hqzvabc", "resource": "card", "deleted": true, "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/cards/card_01hqzvabc", "method": "GET", "description": "Read a card." }, "create": { "href": "https://api.selectwin.io/v1/cards", "method": "POST", "description": "Create a new card." }, "delete": { "href": "https://api.selectwin.io/v1/cards/card_01hqzvabc", "method": "DELETE", "description": "Delete the card." }, "list": { "href": "https://api.selectwin.io/v1/cards", "method": "GET", "description": "List all cards." } } } ``` #### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | ID do cartão (`card_*`) | | `resource` | string | `"card"` | | `deleted` | boolean | `true` | | `merchant` | object | Bloco do merchant (`name`, `merchantId`, `isSubAccount`) | | `_links` | object | HATEOAS (`self`, `create`, `delete`, `list`) | #### Respostas de Erro ##### 404 Not Found - Cartão Não Encontrado ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "client", "code": "cardIdNotFound", "message": "Card ID not found.", "resource": "card", "details": "The card ID was not found. Please check the card ID and try again." } } ``` ##### Códigos de erro deste endpoint | HTTP | `code` | Quando ocorre | |------|--------|---------------| | 400 | (validação de campo) | `cardId` sem o prefixo `card_` | | 401 | — | `SelectKey` ausente ou inválida | | 403 | — | Chave sem o escopo `cards:delete` | | 404 | `cardIdNotFound` | Cartão não encontrado | | 500 | — | Erro interno | #### Casos de Uso 1. **Gestão de Métodos de Pagamento**: Permitir que o cliente remova cartões indesejados ou expirados 2. **Privacidade de Dados**: Remover informações de cartão quando solicitado pelo cliente 3. **Manutenção de Conta**: Limpar cartões obsoletos após a adição de novos métodos de pagamento 4. **Segurança**: Excluir cartões comprometidos após detecção de fraude #### Melhores Práticas 1. **Confirmação do Usuário**: Solicite confirmação explícita do usuário antes de excluir um cartão 2. **Verificação de Cartão Principal**: Verifique se o cartão a ser excluído é o principal e, se for, solicite que outro seja definido antes 3. **Feedback Claro**: Forneça feedback imediato e claro sobre o resultado da operação 4. **Tratamento de Erros**: Implemente tratamento adequado para todos os códigos de erro possíveis 5. **Auditoria**: Mantenha um registro das exclusões de cartão para fins de auditoria #### Integração com Outros Endpoints - Use [Consultar Cartão](/docs/guide/cards/read) antes da exclusão para verificar os detalhes do cartão - Use [Listar Cartões](/docs/guide/cards/list) para encontrar todos os cartões de um cliente - Use [Criar Cartão](/docs/guide/cards/create) para adicionar um novo cartão para substituir o excluído --- ## Cartões - Tokenização no navegador Fonte: https://selectwin.io/docs/guide/cards/tokenization #### Visão Geral Duas formas de aceitar cartão **na sua própria página** sem que o número passe pelo seu servidor: | Modelo | Por onde passa o número do cartão | |---|---| | Checkout hospedado ([Sessions](/docs/guide/checkouts/sessions)) | só a página da Selectwin | | **Selectwin Elements** (esta página, §A) — os campos do cartão num iframe da Selectwin dentro da sua página | só o iframe da Selectwin | | **Integração direta** (§B) — o seu JavaScript envia o cartão à Selectwin | o navegador (o seu JS) | | Cartão completo pela API ([Criar Cartão](/docs/guide/cards/create) com `numbering`) | o seu servidor | Nos dois modelos desta página a Selectwin tokeniza o cartão, e o que chega ao seu servidor, e depois à nossa API, é um **token de uso único** (uma hora). #### A. Selectwin Elements — recomendado É o modelo do Stripe Elements: você carrega um script da Selectwin, monta um elemento de cartão num `div` seu, e os campos (número, nome, validade, CVV) aparecem **dentro de um iframe servido pela Selectwin**. A sua página não tem o cartão no DOM; o `createToken` devolve o token e os dados mascarados. ```html
``` | Chamada | O que faz | |---|---| | `Selectwin(pk, { locale })` | Cria a instância com a sua chave publicável. `locale`: `pt-BR` (padrão), `en`, `es` | | `elements().create('card', { style, placeholders, title })` | O elemento de cartão. `style.base` aceita `color`, `fontFamily`, `fontSize`, `fontWeight`, `backgroundColor`, `borderColor`, `borderRadius`, `focusBorderColor`, `'::placeholder': { color }`; `style.invalid.color` | | `card.mount(seletor \| elemento)` · `unmount()` · `destroy()` | Coloca/retira o iframe. O iframe ajusta a própria altura | | `card.on('ready' \| 'change' \| 'focus' \| 'blur', fn)` | `change` traz `{ complete, empty, brand, error: { code, message } }` | | `card.focus()` · `card.clear()` · `card.update({ style })` | | | `selectwin.createToken(card)` | Promessa que **nunca rejeita**: `{ token, card }` ou `{ error: { code, message } }` | Se a sua página tiver CSP, permita `frame-src https://pay.selectwin.io; script-src https://pay.selectwin.io`. Nada mais: é o iframe que fala com a API. O token entra no seu servidor como qualquer campo do formulário e vai à API em `payment.card.token` ([Criar Transação](/docs/guide/transactions/create)), em [Criar Assinatura](/docs/guide/subscriptions/create) ou em `POST /v1/cards { customerId, token }` ([Criar Cartão](/docs/guide/cards/create)). A Selectwin associa o token ao cliente **na hora**: o cartão já pode ser cobrado. Como foi o comprador quem digitou o cartão, a API devolve `associated: null` até a 1ª cobrança aprovada dele (o mesmo que acontece com o cartão do checkout hospedado). #### B. Integração direta Para quem não pode usar um iframe (apps nativos, WebViews restritas): o seu próprio código envia o cartão à Selectwin, que o tokeniza. O número passa pelo seu JavaScript, mas nunca pelo seu servidor. ``` navegador ──(1) GET /v1/tokenization/config?pk=pk_…──▶ Selectwin navegador ◀── onde enviar o cartão + nomes dos campos ───┘ navegador ──(2) POST /v1/tokenization/tokens?pk=pk_… {holderName, numbering, …}──▶ Selectwin navegador ◀── { token: "…" } ──────────────────────────────────────────────────────┘ navegador ──(3) token ──▶ seu servidor ──(4) POST /v1/transactions { payment.card.token } ──▶ Selectwin ``` ##### B.1 Configuração de tokenização ``` GET /v1/tokenization/config?pk=pk_test_… ``` Sem autenticação além da chave publicável (ou, do servidor, a `SelectKey` sem `pk`). A resposta pode ser lida de qualquer origem (`Access-Control-Allow-Origin: *`) e não deve ser guardada em cache. ```json { "environment": "sandbox", "publicKey": "pk_test_…", "tokenization": { "method": "POST", "url": "https://api.selectwin.io/v1/tokenization/tokens?pk=pk_test_…", "headers": { "Content-Type": "application/json", "Accept": "application/json" }, "fields": { "holderName": "holderName", "number": "numbering", "expirationMonth": "expirationMonth", "expirationYear": "expirationYear", "securityCode": "securityCode" }, "tokenField": "token", "ttlSeconds": 3600 }, "acceptedBrands": ["visa", "mastercard", "elo", "amex", "hipercard"], "elements": { "scriptUrl": "https://pay.selectwin.io/elements/v1/elements.js", "fieldsUrl": "https://pay.selectwin.io/elements/v1/fields?pk=pk_test_…" } } ``` | Campo | Descrição | |---|---| | `environment` | O namespace da chave: `sandbox` gera tokens de teste; `live`, de produção | | `tokenization.url` / `headers` | Para onde e com que cabeçalhos o navegador envia o cartão. Envie exatamente como veio | | `elements.scriptUrl` / `elements.fieldsUrl` | O SDK e o iframe do Selectwin Elements (§A) | | `tokenization.fields` | Nome lógico → nome do campo no corpo. Mês com 2 dígitos, ano com 4 | | `tokenization.tokenField` | Onde está o token na resposta (`token`) | | `tokenization.ttlSeconds` | Validade do token (3600 s) | | `acceptedBrands` | Bandeiras que a sua conta aceita — recuse na página antes de enviar | **Erros** | HTTP | `code` | Quando | |---|---|---| | 401 | `publicKeyInvalid` | `pk` ausente, desconhecida, revogada ou expirada (a chave de API revogada leva a publicável junto) | | 422 | `publicKeyMissing` | A `SelectKey` usada é antiga e não tem chave publicável — crie uma chave de API nova | | 503 | `tokenizationNotConfigured` | A tokenização no navegador não está disponível no ambiente | ##### B.2 No navegador ```html ``` ``` POST /v1/tokenization/tokens?pk=pk_test_… ``` | Campo | Tipo | Descrição | |---|---|---| | `holderName` | string | Nome impresso no cartão (3 a 255) | | `numbering` | string | Número do cartão (validado por Luhn) | | `expirationMonth` | integer | 1 a 12 | | `expirationYear` | integer | 4 dígitos (ou 2 → 20xx). Não pode estar no passado | | `securityCode` | string | CVV (3 ou 4 dígitos) | Resposta `201`: ```json { "token": "a1b2c3d4…", "ttlSeconds": 3600, "card": { "brand": "Visa", "brandType": "visa", "firstDigits": "4111", "lastDigits": "1111", "expirationMonth": "12", "expirationYear": "2030", "holderName": "Ana Lima" } } ``` | HTTP | `code` | Quando | |---|---|---| | 400 | (validação) · `cardBrandNotSupported` | Campo inválido, Luhn, cartão vencido; bandeira que a sua conta não aceita | | 401 | `publicKeyInvalid` | `pk` desconhecida, revogada ou expirada | | 422 | `invalidCardNumber` · `invalidSecurityCode` · `invalidExpiryMonth` · `invalidExpiryYear` · `expiredCardError` · `invalidPaymentInformation` | O cartão foi recusado na tokenização | | 429 | `tokenizationRateLimited` | Teto por minuto: 15 por IP do comprador (na borda) e 120 por chave publicável | | 503 | `cardTokenizationUnavailable` | A tokenização não respondeu a tempo. Tente de novo | Regras que valem a pena seguir na página: - **Nunca** envie `numbering`/`securityCode` ao seu servidor — só o token. - Valide o Luhn e a bandeira (`acceptedBrands`) antes de enviar: uma recusa local é mais rápida. - Guarde a configuração só pelo tempo da página; peça de novo a cada carregamento (`Cache-Control: no-store`). - Com formulário público, considere um desafio anti-bot (o plugin do WooCommerce integra o Cloudflare Turnstile) para que a sua página não vire ferramenta de teste de cartões — além do teto por chave que a API aplica. #### No seu servidor (A e B) O token entra onde o cartão completo entraria: ```json POST /v1/transactions { "amount": 15990, "payment": { "method": "credit", "installments": 3, "card": { "token": "a1b2c3…" } }, "customer": { "id": "cus_…" } } ``` ```json POST /v1/cards { "customerId": "cus_…", "token": "a1b2c3…", "primary": true } ``` A resposta é o cartão de sempre ([Criar Cartão](/docs/guide/cards/create)), com `firstDigits` de **4 dígitos** e `associated: null` até a 1ª cobrança aprovada (o cartão foi digitado pelo comprador; ele já pode ser cobrado). **Erros do token** (além dos erros de cartão de [Criar Cartão](/docs/guide/cards/create)): | HTTP | `code` | Quando | |---|---|---| | 422 | `cardTokenInvalid` | Token desconhecido, expirado (1 h) ou já usado. Gere outro no navegador | | 422 | `cardDeclined` | O cartão foi recusado na validação (zero dollar) | | 422 | `cardTokenCustomerUnavailable` | O cliente não pôde ser cadastrado para receber o cartão (dados incompletos/recusados) | | 503 | `cardTokenAssociationFailed` | A validação do cartão não respondeu a tempo. Nada foi cobrado; tente de novo com um token novo | | 503 | `tokenizationNotConfigured` | Ambiente sem tokenização no navegador | O cadastro por `token` **não responde 429**: o token foi cunhado no navegador de um comprador, então conta na janela de cada comprador (o cliente e o endereço de onde o token foi gerado) e num teto da loja para os cartões dos compradores — nunca no teto da conta. Acima desse teto, ou com a janela do endereço fechada por outros compradores da mesma rede, o cartão é criado do mesmo jeito (201, `associated: null`) e validado logo em seguida, antes da 1ª cobrança. Só fica sem validar o cartão cujo próprio cliente já esgotou a janela dele (cartões demais recusados na hora) ou cujo endereço também esgotou as validações da hora: o token expira em 1 h e a 1ª cobrança com ele é recusada — peça o cartão de novo. #### Sandbox Com uma `pk_test_…` a tokenização acontece no ambiente de **testes**, e os [cartões de teste](/docs/reference/cartoes-de-teste) valem como sempre. Um token de teste nunca cobra em produção: a chave de API `sk_test_…` só enxerga o namespace `sandbox`. --- ## Transações - Visão Geral Fonte: https://selectwin.io/docs/guide/transactions/overview #### Introdução O recurso de Transações é um componente central da Selectwin API, permitindo o processamento e gerenciamento completo de pagamentos em sua plataforma. Este recurso oferece funcionalidades robustas para criar, consultar, capturar, reembolsar e disputar transações, com suporte a diversos métodos de pagamento como cartão de crédito, boleto, PIX e outros, atendendo às necessidades de diferentes modelos de negócio. A API de Transações foi projetada com foco na versatilidade, segurança e rastreabilidade, proporcionando uma solução completa para o processamento de pagamentos em ambientes de e-commerce, marketplaces e serviços por assinatura. #### Estrutura do Objeto Transação O objeto Transação contém informações detalhadas sobre o pagamento, cliente, itens, status e histórico. A estrutura básica inclui: ```json { "id": "tra_01hqzvabc", "customId": "E5D4C3B2A1", "amount": 1500, "originalAmount": 1500, "status": "approved", "method": "credit", "currency": "BRL", "payment": { "provider": "selectwin", "version": "1.1", "reusable": false, "cardFirstDigits": "553121", "cardLastDigits": "4567", "cardBrand": "Mastercard", "cardRegistered": true, "installments": 3, "acquirerTransactionNumber": "9876543210987654321098765432", "paidAt": "2026-03-10T14:23:45.000Z", "invoiceLink": null }, "discount": null, "discounts": null, "customer": { "id": "cus_987654321", "firstName": "Maria", "lastName": "Silva", "email": "maria.silva@exemplo.com", "document": { "type": "cpf", "number": "98765432100" } }, "billing": { "address": { "street": "Avenida Paulista", "number": "2000", "city": "São Paulo", "state": "SP", "postcode": "01310200", "country": "BR" } }, "shipping": null, "externalReference": "CUS_REF_123", "shippable": true, "spplited": false, "items": [ { "id": "item_123", "name": "Produto Exemplo", "quantity": 1, "unitPrice": 1500, "currency": "BRL" } ], "receivables": [], "splits": null, "refunds": null, "disputes": null, "timeline": [ { "id": "tl_123", "message": "Transaction approved", "type": "status_change", "createdAt": "2026-03-10T14:23:45.000Z" } ], "metadata": { "segment": "regular" }, "updatedAt": "2026-03-10T14:00:30.000Z", "createdAt": "2026-03-10T09:15:25.000Z" } ``` **Nota:** Esta é uma versão resumida. A resposta completa (nível raiz) inclui `merchant` e `_links` (`self`, `refund`, `capture`). O `customer` traz os dados do comprador, mas **não** embute `addresses`/`cards` (use `GET /v1/customers/:id`); `billing.address` é aninhado. Para o exemplo completo e fiel, consulte [Create](/docs/guide/transactions/create) / [Read](/docs/guide/transactions/read). A listagem usa projeção leve (ver [List](/docs/guide/transactions/list)). #### Status de Transação Uma transação pode passar por diversos status durante seu ciclo de vida: | Status | Descrição | |--------|-----------| | `pending` | Transação pendente (aguardando pagamento do boleto ou PIX) | | `pre-authorized` | Transação pré-autorizada (cartão de crédito com captura manual) | | `approved` | Transação aprovada | | `failed` | Falha no processamento | | `refused` | Transação recusada | | `canceled` | Transação cancelada | | `refunded` | Transação reembolsada (total ou parcialmente) | | `chargeback` | Transação com chargeback | | `dispute` | Transação em disputa | | `fraud-review` | Transação em revisão por suspeita de fraude | | `analyzing` | Transação em análise | | `awaiting` | Transação aguardando ação/processamento | > O alias `unauthorized` **não** é um status produzido pela API — ele é aceito apenas como filtro em [Listar](/docs/guide/transactions/list). #### Fluxo de Status da Transação ```mermaid stateDiagram-v2 state "fraud-review" as fraud state "pending" as pending state "pre-authorized" as preauth state "approved" as approved state "failed" as failed state "dispute" as dispute state "chargeback" as chargeback state "refunded" as refunded state "canceled" as canceled [*] --> pending: POST /v1/transactions [*] --> fraud: retida para análise de fraude fraud --> pending: revisão aprovada fraud --> failed: revisão reprovada pending --> preauth: crédito · capture=false pending --> approved: crédito aprovado · PIX ou boleto pago pending --> failed: recusada pending --> canceled: PIX vencido sem pagamento canceled --> approved: PIX pago depois do vencimento preauth --> approved: POST .../capture preauth --> canceled approved --> refunded: POST .../refund approved --> dispute: contestação do titular approved --> chargeback dispute --> chargeback dispute --> approved: disputa ganha failed --> [*] canceled --> [*] refunded --> [*] chargeback --> [*] ``` - Toda cobrança nasce `pending` (ou `fraud-review`, quando fica retida para análise). A recusa termina em `failed`. - Só o **PIX** vencido é cancelado automaticamente. O **boleto** fica `pending` até o vencimento, e o vencimento sozinho não muda o status: a Selectwin não cancela boleto vencido. Depois do vencimento ele continua `pending` até chegar um desfecho — pago, inclusive com atraso, vira `approved` (`transaction.approved`); baixado, vira `canceled` (`transaction.canceled`). Sem desfecho, continua `pending`. - A defesa de uma disputa é `POST /v1/transactions/{id}/dispute` (veja [Disputar](/docs/guide/transactions/dispute)); ela não abre a disputa, que começa quando a contestação chega. - `awaiting` e `analyzing` são estados intermediários fora do caminho principal: trate-os como pendentes, e o desfecho chega pelo webhook. #### Fluxo de Status dos Recebíveis ```mermaid stateDiagram-v2 state "scheduled" as scheduled state "pending" as pending state "paid" as paid state "dispute" as dispute state "chargeback" as chargeback state "refunded" as refunded state "canceled" as canceled [*] --> scheduled: recebível gerado (expectedOn futuro) [*] --> pending: aguardando liquidação scheduled --> paid: na data prevista pending --> paid: liberado scheduled --> canceled pending --> canceled paid --> refunded: transação reembolsada paid --> dispute: contestação dispute --> chargeback dispute --> paid: disputa resolvida paid --> [*] refunded --> [*] chargeback --> [*] canceled --> [*] ``` #### Métodos de Pagamento Suportados A API suporta os seguintes métodos de pagamento: | Método | Identificador | Descrição | |--------|--------------|-----------| | Cartão de Crédito | `credit` | Pagamentos via cartão de crédito, com suporte a parcelamento (1–21x) e financiamento automático de juros | | Boleto Bancário | `billet` | Pagamentos via boleto bancário, com prazo de vencimento configurável | | PIX | `pix` | Pagamentos instantâneos via PIX | | NuPay | `nupay` | Pagamento aprovado pelo comprador no app do Nubank. Só nas contas habilitadas para o NuPay; nas outras, a criação responde `422 paymentMethodNotAvailable` | > O identificador `debit` não é aceito na criação. #### Recursos Disponíveis ##### Criar Transação - **Endpoint**: `POST /v1/transactions` - **Descrição**: Cria uma nova transação (`credit`, `pix` ou `billet`). Retorna 201 (ou 202 em análise de fraude) - [Documentação detalhada](/docs/guide/transactions/create) ##### Consultar Transação - **Endpoint**: `GET /v1/transactions/{transactionId}` - **Descrição**: Obtém os detalhes completos de uma transação - [Documentação detalhada](/docs/guide/transactions/read) ##### Listar Transações - **Endpoint**: `GET /v1/transactions` - **Descrição**: Lista paginada com filtros (projeção leve) - [Documentação detalhada](/docs/guide/transactions/list) ##### Capturar Transação - **Endpoint**: `POST /v1/transactions/{transactionId}/capture` - **Descrição**: Captura uma transação pré-autorizada (**assíncrono**, HTTP 202) - [Documentação detalhada](/docs/guide/transactions/capture) ##### Reembolsar Transação - **Endpoint**: `POST /v1/transactions/{transactionId}/refund` - **Descrição**: Reembolsa uma transação aprovada, total ou parcialmente (**assíncrono**, HTTP 202) - [Documentação detalhada](/docs/guide/transactions/refund) ##### Disputar Transação (defesa) - **Endpoint**: `POST /v1/transactions/{transactionId}/dispute` - **Descrição**: Submete evidências para defender uma transação em disputa - [Documentação detalhada](/docs/guide/transactions/dispute) #### Funcionalidades Avançadas ##### Captura Manual vs Automática A API permite dois modos de captura para transações com cartão de crédito: - **Captura Automática (`capture: true`)**: O valor é capturado imediatamente após a autorização - **Captura Manual (`capture: false`)**: O valor é apenas pré-autorizado e deve ser capturado posteriormente via endpoint específico A captura manual é ideal para: - Reservas hoteleiras - Aluguel de veículos - Serviços com valor final indefinido - Verificação de fundos sem cobrança imediata ##### Financiamento automático de parcelas Em cobranças com cartão de crédito parceladas (`installments > 1`), o campo `payment.financeInstallments` (default `true`) faz a API **financiar automaticamente** o parcelamento: o valor base é inflado para o total que o comprador pagaria com os juros configurados pelo vendedor (mesmo motor do simulador). Envie `false` apenas quando você já tiver calculado e enviado o total financiado. ##### Cobrança em nome de sub-conta (marketplace) Plataformas de marketplace podem cobrar diretamente em nome de uma sub-conta que possuem usando `onBehalfOf` (o `publicId` da sub-conta). A sub-conta passa a ser o merchant de registro e a plataforma recolhe sua taxa de aplicação como um split adicional. Por pertencer à sub-conta, essa venda é consultada, capturada e estornada **como a sub-conta**: envie `X-Selectwin-Account: bus_…` junto da sua chave — veja [Operar como a sub-conta](/docs/guide/marketplace/acting-as). A plataforma recebe os webhooks dela (`transaction.*`) com o campo `account`. #### Webhooks Acionados | Evento | Descrição | |-------|-----------| | `transaction.created` | Transação criada. | | `transaction.pending` | Aguardando pagamento (boleto/PIX) ou processamento. | | `transaction.approved` | Transação aprovada com sucesso. | | `transaction.failed` | A tentativa de cobrança falhou (inclui recusa de cartão). | | `transaction.canceled` | Transação cancelada antes da conclusão. | | `transaction.pre-authorized` | Pré-autorização aprovada (aguardando captura). | | `transaction.unauthorized` | Pré-autorização não autorizada. | | `transaction.awaiting` | Em processamento / aguardando análise. | | `transaction.fraud-review` | Em análise de fraude. | | `transaction.dispute` | Transação em disputa. Sai de novo, com `id` próprio, se a disputa reabrir depois de encerrada a seu favor. | | `transaction.chargeback` | Chargeback aberto contra a transação. Sai de novo, com `id` próprio, se o chargeback voltar depois de revertido. | | `transaction.refunded` | Valor reembolsado ao cliente. | | `receivable.created` | Recebível gerado. | | `receivable.scheduled` | Recebível agendado para liberação. | | `receivable.pending` | Recebível aguardando liquidação. | | `receivable.paid` | Recebível liberado/pago. | | `receivable.canceled` | Recebível cancelado. | | `receivable.refunded` | Recebível reembolsado. | | `receivable.dispute` | Disputa afeta o recebível. Sai de novo, com `id` próprio, a cada disputa que reabre. | | `receivable.chargeback` | Chargeback afeta o recebível. Sai de novo, com `id` próprio, a cada chargeback que volta. | | `receivable.fraud-hold` | Recebível retido por análise de fraude. | > Não existe `transaction.refused` (use `transaction.failed`) nem eventos `transaction.method.*` / `receivable.deleted`. A lista completa de eventos está no [Catálogo de Eventos](/docs/guide/webhook-events/event-catalog). #### Casos de Uso Comuns - **E-commerce**: Processamento de pagamentos para lojas virtuais - **Assinaturas**: Cobranças recorrentes para serviços de assinatura - **Marketplace**: Processamento de pagamentos para múltiplos vendedores - **Aplicativos Móveis**: Integração de pagamentos em aplicativos - **Pontos de Venda**: Integração com sistemas de PDV físicos #### Melhores Práticas 1. **Sempre utilize webhooks** para receber notificações de mudanças de status 2. **Armazene o ID da transação** para consultas e operações futuras 3. **Utilize a referência externa** para facilitar a reconciliação com seu sistema 4. **Implemente tratamento de erros** adequado para lidar com falhas nas requisições 5. **Para transações de alto valor**, considere utilizar captura manual 6. **Mantenha registros detalhados** para facilitar a resolução de disputas #### Integração com Outros Recursos O recurso de Transações integra-se com: - **Customers**: Transações são associadas a clientes através do `customer.id` ou dados completos do cliente - **Cards**: Cartões salvos podem ser utilizados em transações através do `payment.card.id` - **Simulators**: Ferramentas de simulação para testar diferentes cenários de pagamento - **Webhooks**: Notificações em tempo real sobre mudanças de status nas transações --- ## Transações - Criar Fonte: https://selectwin.io/docs/guide/transactions/create #### Visão Geral Este endpoint cria uma nova transação de pagamento. Os métodos suportados na criação são **cartão de crédito** (`credit`), **PIX** (`pix`), **boleto** (`billet`) e, nas contas habilitadas, **NuPay** (`nupay`), com configurações flexíveis como captura automática ou manual, parcelamento, financiamento automático de juros, cupons empilháveis, split de marketplace, cobrança em nome de uma sub-conta (`onBehalfOf`), e **multa/juros/desconto no boleto**. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Métodos de Pagamento**: na criação são aceitos `credit`, `pix`, `billet` e `nupay` (este só nas contas habilitadas para o NuPay; nas outras, `422 paymentMethodNotAvailable`). Forneça todos os dados obrigatórios para o método escolhido (cartão para crédito; endereço de cobrança para boleto; `payment.nupay.returnUrl` e `payment.nupay.cancelUrl` para NuPay, e o CPF ou CNPJ do comprador: sem documento válido, `422 customerDocumentRequired`). - **Dados Sensíveis**: informações de cartão de crédito devem ser transmitidas com segurança. Recomenda-se **sempre** usar cartões tokenizados (`payment.card.id`) em vez de enviar os dados completos do cartão. - **Webhooks**: configure webhooks para receber as mudanças de status da transação — **não faça polling**. - **Idempotência**: utilize o header `X-Idempotency-Key` para evitar criação de transações duplicadas. #### Idempotência Para garantir que uma operação não seja executada mais de uma vez, utilize o header `X-Idempotency-Key`. | Header | Tipo | Obrigatório | Descrição | |--------|------|-------------|-----------| | `X-Idempotency-Key` | string | Recomendado | Chave única para garantir idempotência da requisição. Recomendamos a utilização de UUIDs v4. | Ao incluir este header, você evita a criação de transações duplicadas caso ocorram problemas de comunicação ou timeouts. Se a mesma chave for enviada novamente dentro da janela de idempotência, o servidor retornará a resposta original da primeira requisição bem-sucedida, sem criar uma nova transação. **Comportamento**: - Duas requisições idênticas com a mesma chave: apenas a primeira é processada; a segunda devolve a mesma resposta. - Se a primeira tentativa falhou com erro 5xx, uma nova tentativa com a mesma chave processa a transação. - Mesma chave com **corpo diferente** retorna erro **409 (Conflict)** com `code: "idempotencyMismatch"`. #### Definição do Valor da Transação O campo `amount` (em centavos) é **sempre obrigatório**. Ele é o valor **antes de descontos** (`originalAmount` na resposta). Limites: mínimo **500** (R$ 5,00) e máximo **20.000.000** (R$ 200.000,00). Opcionalmente, você pode enviar uma lista de `items`. Quando `items` é informado, a soma de `unitPrice × quantity` de todos os itens **deve ser exatamente igual** a `amount` — caso contrário a requisição é rejeitada com 422. Os `items` são uma itemização do carrinho; eles **não** substituem o `amount`. #### Requisição ``` POST /v1/transactions ``` ##### Exemplos de Requisição por Método de Pagamento ###### Cartão de Crédito com ID de Cartão ```json { "amount": 10000, "payment": { "method": "credit", "currency": "BRL", "capture": true, "installments": 3, "card": { "id": "card_1234567890" } }, "customer": { "id": "cus_123456789" } } ``` ###### Cartão de Crédito com Dados Completos (cliente inline + carrinho) O cliente pode ser enviado **inline** (sem `id`) — `firstName`, `lastName` e `email` são obrigatórios nesse caso; o cliente é localizado ou criado automaticamente antes da cobrança. ```json { "amount": 15000, "items": [ { "name": "Produto Premium", "description": "Produto de alta qualidade", "unitPrice": 10000, "quantity": 1 }, { "name": "Produto Medium", "description": "Produto de baixa qualidade", "unitPrice": 1000, "quantity": 5 } ], "payment": { "method": "credit", "currency": "BRL", "installments": 2, "card": { "holderName": "JOAO SILVA", "numbering": "4111111111111111", "expirationMonth": "12", "expirationYear": "2027", "securityCode": "123" } }, "customer": { "firstName": "João", "lastName": "Silva", "email": "joao.silva@exemplo.com", "document": { "type": "cpf", "number": "12345678900" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321" } } } ``` ###### Boleto Bancário Para boleto, o endereço do pagador é obrigatório e vai em **`billing.address`** (não em `customer`). Informe um `id` de endereço salvo **ou** os campos inline `postcode` + `street` + `number`. ```json { "amount": 18900, "payment": { "method": "billet", "currency": "BRL", "billet": { "expiresInDays": 3 } }, "customer": { "firstName": "Maria", "lastName": "Souza", "email": "maria.souza@exemplo.com", "document": { "type": "cpf", "number": "98765432100" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "912345678" } }, "billing": { "address": { "postcode": "01304-000", "street": "Rua Augusta", "number": "500", "district": "Consolação", "city": "São Paulo", "state": "SP", "country": "BR" } } } ``` ###### PIX ```json { "amount": 5000, "payment": { "method": "pix", "currency": "BRL", "pix": { "expiresInMinutes": 30 } }, "customer": { "firstName": "Carlos", "lastName": "Ferreira", "email": "carlos.ferreira@exemplo.com", "document": { "type": "cpf", "number": "45678912300" } } } ``` ###### Boleto com multa, juros e descontos (`billingInstructions`) O boleto aceita termos avançados em `payment.billet.billingInstructions`: **multa** por atraso (`lateFee`), **juros** (`interest`) e **descontos** por antecipação (`discounts[]`). Todos os offsets de data são **relativos ao vencimento** (não datas absolutas): `startDays` = dias **após** o vencimento; `daysBeforeDue` = dias **antes**. Valores em **centavos**; percentuais de **0 a 100**. ```json { "amount": 18900, "payment": { "method": "billet", "billet": { "expiresInDays": 5, "billingInstructions": { "lateFee": { "mode": "percentage", "percentage": 2, "startDays": 1 }, "interest": { "mode": "monthlyPercentage", "percentage": 1 }, "discounts": [ { "mode": "percentage", "percentage": 10, "daysBeforeDue": 3 } ] } } }, "customer": { "id": "cus_123456789" }, "billing": { "address": { "id": "addr_123456789" } } } ``` > Se `billingInstructions` for omitido, a cobrança **herda o default configurado na conta** (definido nas configurações da empresa). Enviar o objeto na transação **sobrescreve** o default para aquela cobrança. ###### Com Cupons (Descontos Empilhados) Aplique um ou mais cupons via o array `discounts[]`. Cada item é uma referência ao **código** de um cupom do catálogo: `{ "code": "SUMMER10" }`. Os cupons **empilham e compõem sequencialmente** — a ordem importa, pois cada cupom desconta sobre o saldo que restou do anterior. O valor do desconto **nunca é lido da requisição**: ele é sempre resolvido a partir do cupom no catálogo (validade, limites de uso, escopo). Máximo de **20** cupons. ```json { "amount": 10000, "payment": { "method": "pix", "currency": "BRL" }, "discounts": [ { "code": "SUMMER10" }, { "code": "WELCOME" } ], "customer": { "id": "cus_123456789" } } ``` **Regras da pilha** (as mesmas do checkout — HTTP 422): | Situação | `error.code` | |---|---| | O mesmo cupom aparece mais de uma vez em `discounts[]` (comparação sem diferenciar maiúsculas/espaços) | `couponAlreadyApplied` | | Um cupom com `isCumulative: false` enviado junto de qualquer outro | `couponNotCumulative` | > Um código repetido **não é ignorado, é recusado**: cada passada descontaria de novo sobre o saldo > restante (`SAVE50` duas vezes = 75% de desconto) enquanto o resgate é idempotente por cobrança — ou > seja, um cupom com `usageLimit: 1` descontaria N vezes contando **um** uso. Se a intenção era um > desconto maior, use um cupom de valor maior. > Os campos legados `discount` (objeto único) e `coupon` **foram removidos** — uma cobrança não pode mais carregar um desconto ad-hoc enviado na requisição. Use `discounts[]` com códigos do catálogo. Cupons inválidos (expirados, esgotados, fora de escopo) ou que deixem o valor abaixo do mínimo retornam **HTTP 422** com `error.code` específico (ex.: `couponExpired`, `couponUsageLimitReached`, `couponExceedsTotal`, `discountExceedsAmount`). ###### Cobrança em nome de uma sub-conta (marketplace) Plataformas de marketplace podem cobrar diretamente em nome de uma sub-conta que possuem, informando `onBehalfOf` com o `publicId` da sub-conta. A cobrança passa a ser feita **na sub-conta** (ela vira o merchant de registro) e a plataforma recolhe sua taxa de aplicação configurada como um split adicional. Veja [Splits](#parâmetros-de-split-marketplace). ```json { "amount": 10000, "payment": { "method": "pix", "currency": "BRL" }, "customer": { "id": "cus_123456789" }, "onBehalfOf": "bus_subseller123" } ``` #### Parâmetros da Requisição ##### Parâmetros Gerais | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `amount` | integer | Sim | Valor total da transação em centavos, **antes de descontos** (mín. 500, máx. 20.000.000) | | `payment` | object | Sim | Dados do pagamento (ver abaixo) | | `customer` | object | Sim | Cliente por `id` **ou** inline (ver abaixo) | | `billing` | object | Não* | `billing.address` — endereço do pagador (*obrigatório para boleto) | | `items` | array | Não | Itemização do carrinho (máx. 100). Se enviado, Σ(`unitPrice`×`quantity`) deve igualar `amount` | | `discounts` | array | Não | Cupons empilháveis (máx. 20). Cada item: `{ code }` (código do cupom no catálogo) | | `splits` | array | Não | Splits de marketplace (máx. 10) — ver abaixo | | `onBehalfOf` | string | Não | `publicId` de uma sub-conta sua: cobra direto na sub-conta com taxa de aplicação da plataforma | | `geolocation` | object | Não | Sinais de geolocalização/dispositivo para antifraude (`ipAddress`, `latitude`, `longitude`, `deviceFingerprint`, `userAgent`, `acceptLanguage`). `ipAddress` é o IP real do COMPRADOR: o da conexão do navegador dele — atrás de um CDN ou proxy reverso (Cloudflare, Sucuri, QUIC.cloud), o IP que ele repassa (`CF-Connecting-IP`, ou o `REMOTE_ADDR` restaurado pelo `mod_remoteip`), nunca o IP da borda do CDN; nunca um `X-Forwarded-For` que o navegador possa escrever, nem o IP do seu próprio servidor. Com ele, a proteção contra teste de cartão da sua loja conta as recusas também por endereço (sinal para a revisão de risco) | | `shipping` | object | Não | Bloco de entrega persistido como está e devolvido na leitura | | `callback` | object | Não | `callback.webhookUrl` (url) + `callback.active` (default `true`): webhook específico desta transação | | `description` | string | Não | Descrição livre (máx. 1000) — armazenada em `metadata.description` | | `externalReference` | string | Não | Identificador externo (máx. 255) | | `metadata` | object | Não | Metadados livres | | `utm` | object | Não | Atribuição da venda — ver [Atribuição (UTM)](#atribuição-utm) | | `source` | string | Não | Origem da transação (máx. 20). Default: `api` | ##### Atribuição (UTM) O objeto `utm` diz **de onde veio a venda**. Ele fica guardado na transação e é a dimensão que os relatórios de canal agrupam — sem ele, a venda aparece como `(direto)`. ```json { "utm": { "source": "instagram", "medium": "cpc", "campaign": "lancamento-agosto", "content": "criativo-v2", "term": "curso-online", "fbclid": "IwAR1x...", "gclid": "Cj0KCQ..." } } ``` | Campo | Descrição | | --- | --- | | `source` | Origem (`instagram`, `google`, `newsletter`) | | `medium` | Meio (`cpc`, `organic`, `email`) | | `campaign` | Campanha | | `content` | Criativo/variação | | `term` | Termo de busca | | `gclid` · `fbclid` · `ttclid` · `msclkid` · `twclid` · `wbraid` · `gbraid` · `irclickid` · `li_fat_id` · `epik` | Identificadores de clique do anunciante, guardados como vieram | **Duas grafias entram, uma sai.** Aceitamos `source`, `utm_source` e `utmSource` — a URL do anúncio carimba `utm_source`, e é normal repassá-la crua. O que é armazenado e devolvido é sempre a forma curta (`source`). Chave desconhecida é descartada; valor vazio, `null` ou o texto literal `"null"`/`"undefined"` é tratado como ausente (um objeto só de nulos equivale a não enviar `utm`). Apenas os cinco campos UTM são consultáveis; os identificadores de clique são guardados junto, mas não são dimensão de relatório. > Em uma venda de checkout você **não precisa enviar isto**: a atribuição é capturada na sessão (na > primeira visita, com os `utm_*` da URL) e desce sozinha para a transação no momento da cobrança. ##### Parâmetros de Pagamento (`payment`) | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `payment.method` | string | Sim | Método: `credit`, `pix` ou `billet` | | `payment.currency` | string | Não | Moeda (apenas `BRL`). Default: `BRL` | | `payment.installments` | integer | Não | Número de parcelas, 1–21. Default: `1`. (O máximo realmente ofertado é definido na conta e validado na cobrança — acima dele retorna 422 `installmentsExceedMax`.) | | `payment.capture` | boolean | Não | Captura automática (`true`) ou manual/pré-autorização (`false`). Default: `true`. Aplica-se a crédito | | `payment.financeInstallments` | boolean | Não | Default: `true`. Quando ligado, a API **financia automaticamente** o parcelamento, inflando o valor base para o total que o comprador pagaria com os juros configurados pelo vendedor (mesmo motor do simulador). Use `false` apenas se você já enviou o total financiado. Aplica-se a crédito com `installments > 1` | ###### Cartão (`payment.card`) — para crédito Forneça **um cartão salvo** (`payment.card.id`), **um token do navegador** (`payment.card.token`, ver [Tokenização no navegador](/docs/guide/cards/tokenization)) **ou** os dados completos do cartão. Para `method: "credit"`, um dos três é obrigatório. | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `payment.card.id` | string | Sim* | ID do cartão salvo (`card_...`). *Alternativa ao token e aos dados completos | | `payment.card.token` | string | Sim* | Token de uso único cunhado no navegador do comprador. O cartão é tokenizado e associado ao cliente na hora; a resposta traz o `payment.cardId` para as próximas cobranças. Quem digitou o cartão é o comprador: a validação prévia conta na janela dele (o cliente da cobrança) e num teto da loja — acima disso ela é pulada e a **própria cobrança** valida o cartão (um cartão recusado vira transação `failed`, nunca `429`). Erros: `cardTokenInvalid`, `cardDeclined`, `cardTokenAssociationFailed` | | `payment.card.holderName` | string | Sim** | Nome do titular. **Se `card.id` não for enviado | | `payment.card.numbering` | string | Sim** | Número do cartão (validado por Luhn). **Se `card.id` não for enviado | | `payment.card.expirationMonth` | string/integer | Sim** | Mês de expiração (1–12). **Se `card.id` não for enviado | | `payment.card.expirationYear` | string/integer | Sim** | Ano de expiração. **Se `card.id` não for enviado | | `payment.card.securityCode` | string | Sim** | CVV (3–4 dígitos). **Se `card.id` não for enviado | ###### Boleto (`payment.billet`) | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `payment.billet.expiresInDays` | integer | Não | Dias para expiração do boleto / vencimento (1–365). Se omitido, usa o default da conta | | `payment.billet.billingInstructions` | object | Não | Multa/juros/desconto — ver [billingInstructions](#billinginstructions--multa--juros--desconto). Se omitido, herda o default da conta | ###### PIX (`payment.pix`) | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `payment.pix.expiresInMinutes` | integer | Não | Minutos para expiração do PIX **imediato** (15–525600). Default do servidor: 30 | ###### `billingInstructions` — multa / juros / desconto Disponível no **boleto** (multa + juros + desconto). Os três blocos são opcionais e independentes. Valores em **centavos**; percentuais **0–100**; offsets de data **relativos ao vencimento**. **`lateFee` (multa por atraso)** | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `lateFee.mode` | string | Não (default `fixed`) | `fixed` (valor) ou `percentage` (% do boleto) | | `lateFee.amount` | integer | **se `mode=fixed`** | Valor fixo em centavos | | `lateFee.percentage` | number | **se `mode=percentage`** | Percentual 0–100 | | `lateFee.startDays` | integer | Não | Dias **após** o vencimento para começar a cobrar (carência), 0–60. `0` = a partir do vencimento | **`interest` (juros)** | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `interest.mode` | string | Não (default `dailyAmount`) | `dailyAmount` (valor/dia), `dailyPercentage` (%/dia) ou `monthlyPercentage` (%/mês) | | `interest.amount` | integer | **se `mode=dailyAmount`** | Valor fixo por dia, em centavos | | `interest.percentage` | number | **se `mode` percentual** | Percentual 0–100 | | `interest.startDays` | integer | Não | Dias **após** o vencimento para começar a cobrar, 0–60 | **`discounts[]` (descontos por antecipação) — 1 a 3 faixas** | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `discounts[].mode` | string | Não (default `fixed`) | `fixed` (valor) ou `percentage` | | `discounts[].amount` | integer | **se `mode=fixed`** | Valor fixo em centavos | | `discounts[].percentage` | number | **se `mode=percentage`** | Percentual 0–100 | | `discounts[].daysBeforeDue` | integer | Não (default `0`) | Dias **antes** do vencimento até quando o desconto vale, 0–365. `0` = até o vencimento | > **Disponibilidade:** os termos avançados do boleto (carência `startDays`, juros `dailyPercentage` e > mais de uma faixa de `discounts`) dependem do emissor do título e podem não ser aplicados. Quando um termo não é aplicado, a cobrança é > emitida sem ele — confirme o comportamento no sandbox antes de prometê-lo ao comprador. ##### Cliente (`customer`) — por ID ou inline Forneça `customer.id` (cliente já cadastrado) **ou** dados inline. Para o caminho inline, `firstName` + `lastName` + `email` são obrigatórios; os demais campos são opcionais. O endereço do pagador **não** vai aqui — vai em `billing.address`. | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `customer.id` | string | Sim* | ID do cliente (`cus_...`). *Alternativa aos dados inline | | `customer.firstName` | string | Sim** | Primeiro nome (mín. 2). **Se `customer.id` não for enviado | | `customer.lastName` | string | Sim** | Sobrenome. **Se `customer.id` não for enviado | | `customer.email` | string | Sim** | Email. **Se `customer.id` não for enviado | | `customer.document.type` | string | Não | `cpf`, `cnpj` ou `passport` | | `customer.document.number` | string | Não*** | Número do documento. ***Obrigatório no NuPay: CPF ou CNPJ válido, seu ou do cliente salvo (`customer.id`); sem ele, `422 customerDocumentRequired` | | `customer.telephone.countryCode` | string | Não | Código do país | | `customer.telephone.areaCode` | string | Não | DDD | | `customer.telephone.number` | string | Não | Número do telefone | | `customer.telephone.line` | string | Não | Telefone completo, só dígitos (`5511987654321`). Alternativa a enviar as partes | | `customer.gender` | string | Não | `male`, `female` ou `other` | | `customer.birthdate` | string | Não | Data de nascimento (`YYYY-MM-DD`) | | `customer.additionalEmails` | array | Não | Emails adicionais (máx. 20) | | `customer.externalReference` | string | Não | Referência externa do cliente | | `customer.metadata` | object | Não | Metadados do cliente | > **Telefone do comprador.** Envie as partes (`countryCode`/`areaCode`/`number`) **ou** o número completo em `line` — o que faltar é preenchido a partir do que você mandou. É esse telefone que segue para o adquirente (no boleto e no bloco 3-D Secure do cartão), então enviá-lo ajuda a aprovação. Campos fora dessa lista são **descartados**: mandar o número sob outro nome equivale a não mandar telefone. ##### Endereço de cobrança (`billing.address`) — obrigatório para boleto Forneça `billing.address.id` (endereço salvo) **ou** os campos inline. Para boleto, é obrigatório informar pelo menos `postcode` + `street` + `number`. | Parâmetro | Tipo | Descrição | |-----------|------|-----------| | `billing.address.id` | string | ID de endereço salvo (`addr_...`) | | `billing.address.postcode` | string | CEP | | `billing.address.street` | string | Logradouro | | `billing.address.number` | string | Número | | `billing.address.complement` | string | Complemento | | `billing.address.district` | string | Bairro | | `billing.address.city` | string | Cidade | | `billing.address.state` | string | Estado | | `billing.address.country` | string | País (2–3 letras) | ##### Itens (`items[]`) | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `items[].name` | string | Sim | Nome do item | | `items[].unitPrice` | integer | Sim | Preço unitário em centavos | | `items[].quantity` | integer | Sim | Quantidade (1–100.000) | | `items[].description` | string | Não | Descrição | | `items[].currency` | string | Não | Moeda (`BRL`) | | `items[].variantId` | string | Não | ID de variante de produto | | `items[].images` | array | Não | URLs de imagens (máx. 10) | | `items[].externalReference` | string | Não | Referência externa do item | | `items[].metadata` | object | Não | Metadados do item | ##### Parâmetros de Split (marketplace) Cada split aceita o formato em valor fixo (`recipientId` + `amountCents`) **ou** o formato tipado (`recipient` + `type` + `value`). Máximo de 10 splits; a soma não pode exceder o valor cobrado. | Parâmetro | Tipo | Descrição | |-----------|------|-----------| | `splits[].recipientId` ou `splits[].recipient` | string | `publicId` da empresa destinatária | | `splits[].amountCents` | integer | Valor fixo do split em centavos | | `splits[].type` | string | `percentage` ou `flat` (alternativa a `amountCents`) | | `splits[].value` | number | Percentual (≤ 100) ou valor, conforme `type` | > **3D Secure / cartão de débito / NuPay**: o endpoint de criação **não** aceita um bloco `threeDSecure` nem o método `debit`. O método `nupay` só é aceito em contas habilitadas para o NuPay; nas outras, a criação devolve `422 paymentMethodNotAvailable`. (`nupay` também é aceito como filtro em [Listar](/docs/guide/transactions/list).) #### Resposta ##### Sucesso (HTTP 201 Created) > A criação é **assíncrona**: a resposta é sempre **201**, com `status: "pending"` (ou `"fraud-review"`, quando a > cobrança fica retida para análise). O desfecho (`approved`, `failed`…) chega pelo webhook e pela leitura da > transação. O exemplo abaixo é uma cobrança no cartão com cupom. ```json { "id": "tra_987654321", "customId": "E5D4C3B2A1", "amount": 9500, "originalAmount": 10000, "fees": 475, "net": 9025, "status": "pending", "method": "credit", "currency": "BRL", "payment": { "provider": "selectwin", "version": "1.1", "refused": null, "reusable": false, "billetUrl": null, "billetBarcode": null, "billetSequence": null, "billetDocumentNumber": null, "billetReferenceNumber": null, "pixQrCodeEmv": null, "pixQrCodeUrl": null, "nupayPaymentUrl": null, "pixQrCodeImage": null, "acquirerTransactionNumber": null, "cardFirstDigits": "553121", "cardLastDigits": "4567", "cardBrand": "Mastercard", "cardRegistered": true, "cardId": "card_987654321", "installments": 3, "expirationDate": null, "paidAt": null, "allowRenewPayment": false, "invoiceLink": null, "invoicePathLink": null }, "discount": { "value": 500, "type": "percentage", "percentageOfAmount": 5 }, "discounts": [ { "source": "coupon", "couponId": 4821, "code": "SUMMER10", "type": "percentage", "value": 5, "appliedAmount": 500 } ], "customer": { "id": "cus_987654321", "firstName": "Maria", "lastName": "Silva", "email": "maria.silva@exemplo.com", "birthdate": "1985-06-12", "gender": "female", "document": { "type": "cpf", "number": "98765432100" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "5511987654321" }, "available": true, "delinquent": false, "externalReference": "Store_Ref_54321", "additionalEmails": ["maria.trabalho@exemplo.com"], "metadata": { "segment": "premium" }, "updatedAt": "2026-03-05T10:20:48.000Z", "createdAt": "2026-01-15T08:30:48.000Z" }, "billing": { "address": { "id": "addr_987654321", "ownerId": "cus_987654321", "ownerType": "customer", "street": "Avenida Paulista", "number": "2000", "complement": "Apto 501", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "postcode": "01310200", "country": "BR", "latitude": "-23.5489", "longitude": "-46.638821", "line": "Avenida Paulista, 2000 - Apto 501, Bela Vista, São Paulo - SP, 01310200, BR", "line1": "Avenida Paulista, 2000", "line2": "Apto 501", "line3": "Bela Vista", "updatedAt": "2026-03-05T10:15:22.745Z", "createdAt": "2026-01-15T08:30:48.000Z" } }, "shipping": null, "externalReference": "pedido_loja_9876", "shippable": true, "spplited": false, "items": [ { "id": "item_987654321", "name": "Produto Premium", "unitPrice": 10000, "quantity": 1, "currency": "BRL", "description": "Produto de alta qualidade", "images": ["https://selectwin.io/assets/produto_premium.png"], "isUpsell": false, "isOrderbump": false, "metadata": { "sku": "PRM-001" }, "variantId": null, "externalReference": null, "updatedAt": "2026-03-10T14:20:48.000Z", "createdAt": "2026-03-10T14:20:48.000Z" } ], "receivables": null, "splits": null, "refunds": null, "disputes": null, "timeline": [], "callback": { "webhookUrl": "https://meucomercio.com.br/webhook/notifications", "active": true }, "metadata": { "source": "mobile_app", "campaign": "promo_verao_2026" }, "utm": null, "processingTimeMs": null, "updatedAt": "2026-03-10T14:23:45.000Z", "createdAt": "2026-03-10T14:23:45.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/transactions/tra_987654321", "method": "GET", "description": "Read a transaction." }, "refund": { "href": "https://api.selectwin.io/v1/transactions/tra_987654321/refund", "method": "POST", "description": "Refund the transaction." }, "capture": { "href": "https://api.selectwin.io/v1/transactions/tra_987654321/capture", "method": "POST", "description": "Capture the transaction." } } } ``` ##### Onde chegam o QR do Pix e a linha digitável do boleto A resposta 201 vem `pending` e **ainda sem** os dados de pagamento do Pix e do boleto (os campos vêm `null`). Eles chegam logo depois, no webhook **`transaction.pending`**, e ficam na leitura da transação (`GET /v1/transactions/{id}`): | Método | Campo | O que é | |--------|-------|---------| | PIX | `payment.pixQrCodeEmv` | O copia e cola do PIX (gere a imagem do QR a partir dele) | | PIX | `payment.pixQrCodeUrl` | Link do QR Code | | Boleto | `payment.billetBarcode` | A linha digitável | | Boleto | `payment.billetUrl` | Link do boleto | | PIX e boleto | `payment.expirationDate` | O vencimento | | NuPay | `payment.nupayPaymentUrl` | O link de aprovação no app do Nubank, para onde o comprador vai | O pagamento confirmado chega como `transaction.approved`. Veja o [Catálogo de Eventos](/docs/guide/webhook-events/event-catalog). ##### Notas sobre a resposta - `amount` é o valor **cobrado** (após descontos/financiamento); `originalAmount` é o valor antes de descontos. - `payment.version` é `"1.1"` e `payment.provider` é `"selectwin"`. - `payment.pixQrCodeImage` é sempre `null` (a API não gera imagem; use `pixQrCodeEmv` para renderizar o QR ou `pixQrCodeUrl`). - `discount` é o bloco legado consolidado (`{ value, type, percentageOfAmount }`), e `discounts[]` é o detalhamento por cupom aplicado (`{ source, couponId, code, type, value, appliedAmount }`). Ambos podem ser `null` quando não há desconto. - O `customer` da transação traz os dados do comprador, mas **não** embute as listas `addresses`/`cards` — para isso use `GET /v1/customers/:id`. - `billing.address` é um objeto aninhado dentro de `billing` (todos os campos podem ser `null` quando não há endereço). - `processingTimeMs` é o tempo de processamento do nosso backend para a cobrança, em **milissegundos** (de `createdAt` até o primeiro resultado do adquirente ser registrado). Vem `null` enquanto o resultado ainda não chegou e é carimbado **uma única vez**, no primeiro resultado — o mesmo valor aparece na leitura e no webhook `transaction.*`. - `_links` traz `self`, `refund` e `capture`. #### Melhores Práticas 1. **Use cartões tokenizados** (`payment.card.id`) sempre que possível. 2. **Utilize `externalReference`** para rastrear a transação em seus sistemas. 3. **Reaja a webhooks** para acompanhar mudanças de status — não faça polling. 4. **Defina expiração adequada** para PIX e boleto. 5. **Use `X-Idempotency-Key`** em toda criação para evitar duplicidades. 6. **Para captura manual**, envie `payment.capture: false` e capture depois via [Capturar](/docs/guide/transactions/capture). --- ## Transações - Listar Fonte: https://selectwin.io/docs/guide/transactions/list #### Visão Geral Lista transações com filtros, ordenação e paginação. Os itens de `data` são uma **projeção leve** — diferente do objeto completo retornado por [Consultar](/docs/guide/transactions/read). Para os detalhes completos (cliente, itens, recebíveis, etc.), consulte cada transação individualmente. #### Precauções - Resposta paginada, com **limite máximo de 100 registros** por requisição. - Para conjuntos grandes, navegue com `limit`/`offset`. - Requisições frequentes podem estar sujeitas a rate limiting. #### Requisição ``` GET /v1/transactions ``` ##### Parâmetros de Consulta | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `status` | string | Não | Filtra pelo status | `approved` | | `method` | string | Não | Filtra pelo método | `credit` | | `id` | string | Não | Filtra por um ID de transação | `tra_1234567890` | | `customid` | string | Não | Filtra pelo identificador personalizado | `E5D4C3B2A1` | | `referenceid` | string | Não | Filtra pela referência externa | `pedido_loja_9876` | | `customerId` (ou `customerid`) | string | Não | Filtra pelo ID do cliente | `cus_1234567890` | | `customeremail` | string | Não | Filtra pelo email do cliente | `joao@exemplo.com` | | `customerdocument` | string | Não | Filtra pelo documento do cliente (CPF/CNPJ, **somente dígitos**) | `11122233344` | | `source` | string | Não | Filtra pela origem | `api` | | `ipaddress` | string | Não | Filtra pelo IP de origem | `203.0.113.45` | | `limit` | integer | Não | Registros por página (1–100, padrão: 20) | `50` | | `offset` | integer | Não | Registros a pular (paginação) | `20` | | `sort` | string | Não | Ordenação (`ascending`/`descending` ou `campo:asc`/`campo:desc`) | `descending` | ###### Valores Aceitos para `status` `pending`, `pre-authorized`, `approved`, `failed`, `refused`, `canceled`, `refunded`, `chargeback`, `dispute`, `fraud-review`, `analyzing`, `awaiting`. O alias `unauthorized` também é aceito como filtro (compatibilidade). ###### Valores Aceitos para `method` `credit`, `pix`, `billet`, `nupay`. ##### Exemplos de Requisição ``` GET /v1/transactions?status=approved&method=credit&limit=50&sort=descending GET /v1/transactions?status=pending&customerId=cus_1234567890 GET /v1/transactions?status=refunded&limit=20&sort=updatedAt:desc ``` #### Resposta ##### Sucesso (HTTP 200 OK) Resposta paginada no nível raiz. Os itens de `data` são projeções leves: `amount`/`originalAmount` vêm como **strings** e `fees` é um **único número** (total em centavos). ```json { "offset": 0, "limit": 20, "total": 142, "hasMore": true, "page": { "current": 1, "total": 8, "offset": { "first": 0, "prev": null, "next": 20, "last": 140 } }, "data": [ { "id": "tra_01hqzvabc", "customId": "E5D4C3B2A1", "customerId": "cus_987654321", "cardBrand": "Mastercard", "amount": "9500", "originalAmount": "10000", "status": "approved", "method": "credit", "currency": "BRL", "fees": 45, "approvedAt": "2026-06-15T14:23:45.000Z", "updatedAt": "2026-06-15T14:00:30.000Z", "createdAt": "2026-06-10T09:15:25.000Z", "images": [] } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/transactions", "method": "GET", "description": "List all transactions." }, "create": { "href": "https://api.selectwin.io/v1/transactions", "method": "POST", "description": "Create a new transaction." } } } ``` #### Atributos da Resposta Paginada | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` | integer | Posição inicial dos resultados | | `limit` | integer | Máximo de registros retornados | | `total` | integer | Total de registros disponíveis | | `page.offset.first/prev/next/last` | integer \| null | Offsets de navegação | | `page.current` | integer | Página atual | | `page.total` | integer | Total de páginas | | `hasMore` | boolean | Se há mais resultados | | `data` | array | Projeções leves das transações | | `merchant` | object | Merchant da conta | | `_links` | object | Links HATEOAS (`self`, `create`) | ##### Item de lista (projeção leve em `data`) | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador (`tra_*` / `trx_*`) | | `customId` | string \| null | Identificador personalizado | | `customerId` | string \| null | Cliente associado (`cus_*`) | | `cardBrand` | string \| null | Bandeira do cartão | | `amount` | string | Valor cobrado em centavos (como string) | | `originalAmount` | string | Valor original em centavos (como string) | | `status` | string | Status atual | | `method` | string | Método de pagamento | | `currency` | string | Moeda (`BRL`) | | `fees` | integer | Total de taxas em centavos | | `approvedAt` | string \| null | Data/hora de aprovação (ISO) | | `updatedAt` | string | Última atualização (ISO) | | `createdAt` | string | Criação (ISO) | | `images` | array | URLs de imagens dos itens (geralmente vazia na listagem) | #### Melhores Práticas 1. **Utilize filtros específicos** para reduzir o volume retornado. 2. **Pagine adequadamente** para grandes conjuntos. 3. **Para reconciliação**, combine filtros de status e período. 4. **Evite consultas muito frequentes** para não atingir rate limits. --- ## Transações - Consultar Fonte: https://selectwin.io/docs/guide/transactions/read #### Visão Geral Este endpoint retorna os detalhes completos de uma transação: status atual, dados de pagamento, cliente, itens, recebíveis, splits, reembolsos, disputas e a timeline de eventos. É essencial para acompanhar o ciclo de vida da transação, reconciliar pagamentos e dar suporte ao cliente. #### Precauções - O ID da transação é sensível a maiúsculas e minúsculas. São aceitos os prefixos `tra_` e `trx_`. - Apenas os primeiros e últimos dígitos do cartão são retornados; o CVV nunca é retornado. - **Plataformas de marketplace:** uma venda feita com `onBehalfOf` pertence à sub-conta. Para consultá-la, envie `X-Selectwin-Account: bus_…` (o `id` da sub-conta) — veja [Operar como a sub-conta](/docs/guide/marketplace/acting-as). Sem o cabeçalho, a resposta é `404`. #### Requisição ``` GET /v1/transactions/{transactionId} ``` ##### Parâmetros de URL | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `transactionId` | string | Sim | Identificador da transação (`tra_...` ou `trx_...`) | #### Resposta ##### Sucesso (HTTP 200 OK) ```json { "id": "tra_987654321", "customId": "E5D4C3B2A1", "amount": 10000, "originalAmount": 10000, "status": "approved", "method": "credit", "currency": "BRL", "payment": { "provider": "selectwin", "version": "1.1", "refused": null, "reusable": false, "billetUrl": null, "billetBarcode": null, "billetSequence": null, "billetDocumentNumber": null, "billetReferenceNumber": null, "pixQrCodeEmv": null, "pixQrCodeUrl": null, "pixQrCodeImage": null, "acquirerTransactionNumber": "9876543210987654321098765432", "cardFirstDigits": "553121", "cardLastDigits": "4567", "cardBrand": "Mastercard", "cardRegistered": true, "cardId": "card_01hqzvabc", "installments": 3, "expirationDate": null, "paidAt": "2026-06-15T14:30:50.000Z", "allowRenewPayment": false, "invoiceLink": null }, "discount": null, "discounts": null, "customer": { "id": "cus_123456789", "firstName": "João", "lastName": "Silva", "email": "joao.silva@exemplo.com", "birthdate": "1980-01-15", "gender": "male", "document": { "type": "cpf", "number": "12345678900" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "5511987654321" }, "available": true, "delinquent": false, "externalReference": "CUS_REF_123", "additionalEmails": ["joao.trabalho@exemplo.com"], "metadata": { "segment": "regular" }, "updatedAt": "2026-06-15T14:00:30.000Z", "createdAt": "2026-06-10T09:15:25.000Z" }, "billing": { "address": { "id": "addr_123456789", "ownerId": "cus_123456789", "ownerType": "customer", "street": "Rua Exemplo", "number": "123", "complement": "Apto 45", "district": "Centro", "city": "São Paulo", "state": "SP", "postcode": "01234567", "country": "BR", "latitude": null, "longitude": null, "line": "Rua Exemplo, 123 - Apto 45, Centro, São Paulo - SP, 01234567, BR", "line1": "Rua Exemplo, 123", "line2": "Apto 45", "line3": "Centro", "updatedAt": "2026-06-15T14:00:30.745Z", "createdAt": "2026-06-15T14:00:30.000Z" } }, "shipping": null, "externalReference": "PEDIDO-123", "shippable": true, "delivery": { "required": true, "shippedAt": null, "trackingCode": null, "trackingUrl": null, "daysSinceApproval": 2, "state": "pending" }, "spplited": false, "items": [ { "id": "item_123456789", "name": "Produto A", "unitPrice": 5000, "quantity": 2, "currency": "BRL", "description": "Descrição detalhada do produto A", "images": ["https://selectwin.io/assets/product_a_1.png"], "isUpsell": false, "isOrderbump": false, "metadata": { "sku": "PROD-A-001" }, "variantId": null, "externalReference": null, "updatedAt": "2026-06-15T14:30:45.000Z", "createdAt": "2026-06-15T14:30:45.000Z" } ], "receivables": [ { "id": "rec_123456781", "recipient": "bus_123456789", "split": null, "status": "paid", "amount": 3334, "grossAmount": 3334, "anticipationFee": 0, "installmentNumber": 1, "description": null, "currency": "BRL", "authorizationCode": "AUTH123456", "paidAt": "2026-06-15T14:30:50.000Z", "refundedAt": null, "canceledAt": null, "expectedOn": "2026-07-15T14:30:50.000Z", "liable": true, "chargeProcessingFee": true, "updatedAt": "2026-06-15T14:30:50.000Z", "createdAt": "2026-06-15T14:30:45.000Z" } ], "splits": null, "refunds": null, "disputes": null, "timeline": [ { "id": "tl_123", "message": "Transaction approved", "details": null, "type": "status_change", "updatedAt": "2026-06-15T14:23:45.000Z", "createdAt": "2026-06-15T14:23:45.000Z" } ], "callback": { "webhookUrl": "https://meucomercio.com.br/webhook/notifications", "active": true }, "metadata": { "source": "web_store", "campaign": "promo_inverno" }, "utm": { "source": "instagram", "medium": "cpc", "campaign": "lancamento-agosto" }, "processingTimeMs": 5523, "updatedAt": "2026-06-15T14:31:15.000Z", "createdAt": "2026-06-15T14:30:45.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/transactions/tra_987654321", "method": "GET", "description": "Read a transaction." }, "refund": { "href": "https://api.selectwin.io/v1/transactions/tra_987654321/refund", "method": "POST", "description": "Refund the transaction." }, "capture": { "href": "https://api.selectwin.io/v1/transactions/tra_987654321/capture", "method": "POST", "description": "Capture the transaction." } } } ``` #### Atributos da Resposta ##### Atributos Principais | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador da transação (`tra_*` / `trx_*`) | | `customId` | string \| null | Identificador personalizado | | `amount` | integer | Valor cobrado em centavos (após descontos) | | `originalAmount` | integer | Valor original em centavos (antes de descontos) | | `status` | string | Status atual (ver [Visão Geral](/docs/guide/transactions/overview#status-de-transação)) | | `method` | string | Método: `credit`, `pix` ou `billet` | | `currency` | string | Moeda (`BRL`) | ##### Objeto `payment` Um único objeto que consolida os dados de cartão, boleto, PIX e adquirente. Campos não aplicáveis ao método vêm como `null`. | Atributo | Tipo | Descrição | |----------|------|-----------| | `payment.provider` | string | Sempre `selectwin` | | `payment.version` | string | Sempre `1.1` | | `payment.cardFirstDigits` / `cardLastDigits` | string \| null | BIN e últimos dígitos do cartão | | `payment.cardBrand` | string \| null | Bandeira | | `payment.cardRegistered` | boolean | `true` quando a cobrança usou um cartão salvo | | `payment.cardId` | string \| null | O id (`card_…`) do cartão salvo que a cobrança usou — o mesmo de `GET /v1/cards`. Guarde-o para cobrar renovações com `payment.card.id` (assinaturas geridas no seu sistema). `null` sem cartão salvo | | `payment.installments` | integer \| null | Número de parcelas | | `payment.billetUrl` / `billetBarcode` / `billetSequence` / `billetDocumentNumber` / `billetReferenceNumber` | string \| null | Dados do boleto | | `payment.pixQrCodeEmv` | string \| null | Payload EMV do QR Code PIX (use para renderizar o QR) | | `payment.pixQrCodeUrl` | string \| null | URL do PIX | | `payment.pixQrCodeImage` | null | Sempre `null` (a API não gera imagem) | | `payment.acquirerTransactionNumber` | string \| null | Identificador da transação no adquirente | | `payment.expirationDate` | string \| null | Vencimento (boleto/pré-autorização) | | `payment.paidAt` | string \| null | Data/hora da liquidação. **Segue o `status`**: só `approved` e `dispute` carregam a data; estornada, cancelada ou com chargeback responde `null` mesmo tendo sido aprovada antes (o carimbo cru continua no `approvedAt` do item da listagem). Vale para o objeto lido e para o payload dos webhooks `transaction.*` | | `payment.allowRenewPayment` | boolean | Permite gerar novo pagamento (PIX/boleto) | | `payment.invoiceLink` | string \| null | **O comprovante hospedado** da cobrança — `{host}/r/{ambiente}/{token}`. `null` quando não dá para assinar (lojista sem chave) — ver a nota abaixo | | `payment.reusable` | boolean | Sempre `false` | | `payment.refused` | object \| null | Motivo da recusa (`code`, `type`, `category`, `message`, `displayMessage`, `reversible`) — ver [Recusas de Pagamento](/docs/reference/recusas-de-pagamento) | > ✅ **`payment.invoiceLink` voltou a ter destino** — e o destino é o **comprovante**, não o portal. > > Ele era uma string montada no código — `https://selectwin.io/invoices/{id}` — para **toda** transação, > sem olhar domínio, lojista ou ambiente, e esse endereço não existia em host nenhum: 404 tanto no > domínio da plataforma quanto no do lojista. Como o campo era obrigatório (`string`), não havia como > preenchê-lo sem inventar — e ele sai também na variável `transaction.invoiceUrl` das integrações, ou > seja, o primeiro lojista que a usasse mandaria o **comprador** para o 404. Virou `null` declarado, e > agora tem endereço de verdade. > > **A forma:** `{host}/r/{ambiente}/{token}`. O host vem do módulo de domínios — o **domínio próprio** do > lojista quando houver um, `pay.selectwin.io` quando não houver —, nunca literal no código, que foi > exatamente o que criou o 404. O `{token}` é assinado com a chave **daquele** lojista e amarra > transação + lojista + ambiente, com validade (`systemConfigs.checkoutReceiptLinkTtlHours`, padrão 180 > dias). Um id nu (`/r/live/tra_…` sem assinatura) **não é honrado**: seria o comprovante alheio ao > portador, e é o mesmo buraco que o link de recuperação de sessão fechou. > > **O que ele abre:** uma tela read-only de UMA compra — nome do lojista, itens, valor, método, data e, > se houve cartão, bandeira e quatro últimos. Nada de pagar, nada de listar outras compras, nada de > endereço ou documento: o link circula por e-mail encaminhável. **Não é o portal do cliente**, que é > escopo de CLIENTE e move dinheiro — as duas superfícies são independentes, e um token não abre a outra. > > **`null` continua possível**, e significa uma coisa só: não foi possível assinar (lojista sem chave de > assinatura). Um link que não prova posse não é melhor que nenhum link. ##### Outros objetos | Atributo | Tipo | Descrição | |----------|------|-----------| | `discount` | object \| null | Bloco consolidado de desconto (`value`, `type`, `percentageOfAmount`) | | `discounts` | array \| null | Cupons aplicados (`source`, `couponId`, `code`, `type`, `value`, `appliedAmount`) | | `customer` | object \| null | Dados do comprador (sem as listas `addresses`/`cards` — use `GET /v1/customers/:id`) | | `customer.document` | object | O documento que identificou o comprador **nesta cobrança**. Pode diferir do `document` atual em `GET /v1/customers/:id`: o cadastro é um só para todas as compras do mesmo e-mail, e editá-lo depois não reescreve transações passadas. Os demais campos de `customer` seguem o cadastro atual | | `billing.address` | object | Endereço de cobrança aninhado | | `items` | array \| null | Itens da transação | | `receivables` | array \| null | Recebíveis gerados | | `splits` | array \| null | Splits de marketplace | | `refunds` | array \| null | Reembolsos emitidos | | `disputes` | array \| null | Disputas abertas pelo comprador: motivo (`reason`, `reasonCode`, `reasonDescription`), `status`, `openedAt`/`closedAt`, o desfecho (`outcome`: `won`/`lost`), o prazo restante em `deadline` e as evidências já enviadas — ver [Disputar](/docs/guide/transactions/dispute) | | `timeline` | array | Histórico de eventos (`id`, `message`, `details`, `type`, `createdAt`, `updatedAt`) | | `callback` | object \| null | `webhookUrl` + `active` | | `metadata` | object \| null | Metadados | | `utm` | object \| null | **De onde veio a venda.** Chaves possíveis: `source`, `medium`, `campaign`, `content`, `term` — cada uma presente só se veio. `null` quando a venda não tem campanha. É o **mesmo objeto** que `checkout.session.*` expõe em `utm`: numa venda de checkout ele é herdado da sessão automaticamente | | `processingTimeMs` | integer \| null | Tempo de processamento do nosso backend para esta cobrança, em **milissegundos**: de `createdAt` até o primeiro resultado do processamento ser registrado. `null` enquanto o resultado não chega. Carimbado **uma única vez**, no primeiro resultado | | `merchant` | object | `name`, `merchantId`, `isSubAccount` | | `_links` | object | `self`, `refund`, `capture` | #### Melhores Práticas 1. **Reaja a webhooks** para mudanças de status em vez de consultar repetidamente este endpoint. 2. **Use a timeline** para rastrear o progresso da transação. 3. **Armazene os `_links`** para navegar entre as operações relacionadas. 4. **Trate adequadamente** os diferentes estados que uma transação pode assumir. --- ## Transações - Capturar Fonte: https://selectwin.io/docs/guide/transactions/capture #### Visão Geral Captura uma transação previamente **pré-autorizada** (criada com `payment.capture: false`). A captura confirma a cobrança no cartão do cliente. É possível capturar o valor total ou um valor parcial (menor que o autorizado). A captura é **assíncrona**: o endpoint aceita a solicitação (HTTP **202**) e o processamento ocorre em background. Acompanhe o resultado pelos webhooks (`transaction.approved`) ou consultando a transação. #### Precauções - Só pode ser usado em transações no status `pre-authorized`. - O valor da captura **não pode exceder** o valor autorizado. - Uma vez capturada, a transação não pode ser revertida — apenas reembolsada. - O ID da transação é sensível a maiúsculas e minúsculas (`tra_...` ou `trx_...`). - **Plataformas de marketplace:** a captura de uma venda feita com `onBehalfOf` é pedida **como a sub-conta**, com `X-Selectwin-Account: bus_…` — veja [Operar como a sub-conta](/docs/guide/marketplace/acting-as). #### Casos de uso - **Reservas hoteleiras**: autorizar no check-in e capturar após verificar consumos. - **Aluguel de veículos**: pré-autorizar caução e capturar apenas se houver danos. - **Serviços sob demanda**: autorizar valor estimado e capturar o valor real. - **Verificação de fundos**: garantir saldo antes de processar. #### Requisição ``` POST /v1/transactions/{transactionId}/capture ``` ##### Parâmetros de URL | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `transactionId` | string | Sim | Identificador da transação a capturar | ##### Parâmetros do Corpo da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `amount` | integer | Não | Valor a capturar em centavos. Se omitido, captura o valor total autorizado. Para captura parcial, informe um valor menor (`> 0` e `≤` autorizado) | ##### Exemplo de Requisição Captura total (corpo vazio ou sem `amount`): ```json {} ``` Captura parcial: ```json { "amount": 8000 } ``` #### Resposta ##### Aceito (HTTP 202 Accepted) A captura foi aceita para processamento assíncrono. ```json { "transactionId": "tra_987654321", "status": "pre-authorized", "accepted": true } ``` | Atributo | Tipo | Descrição | |----------|------|-----------| | `transactionId` | string | Identificador da transação | | `status` | string | Status no momento do aceite (a confirmação da captura chega por webhook) | | `accepted` | boolean | Sempre `true` | #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `transactionNotCapturable` | 422 | A transação não está aguardando captura (não está em `pre-authorized`) | | `captureAmountInvalid` | 422 | O valor de captura é zero/negativo ou excede o autorizado | | `transactionInPaymentGroup` | 409 | A transação é uma **parte de um pagamento combinado** (2 cartões ou cartão + Pix, no checkout hospedado). As partes são capturadas pelo próprio pagamento combinado, só quando todas estão garantidas — nunca por esta rota | | `transactionNotFound` | 404 | Transação inexistente | #### Melhores Práticas 1. **Verifique o status** (`pre-authorized`) antes de capturar. 2. **Reaja ao webhook `transaction.approved`** para confirmar a captura. 3. **Considere captura parcial** quando o valor final for menor que o autorizado. 4. **Lembre-se do prazo** de validade das pré-autorizações (normalmente alguns dias). --- ## Transações - Disputar (defesa) Fonte: https://selectwin.io/docs/guide/transactions/dispute #### Visão Geral Quando o titular do cartão contesta uma cobrança junto ao emissor, a transação entra no status `dispute` quando a contestação chega. Este endpoint permite que o vendedor **submeta evidências** para defender a transação. > ⚠️ Cada submissão **substitui** todo o conjunto de evidências anterior da disputa (é uma sobrescrita completa, não um acréscimo). Envie sempre o conjunto completo de documentos. #### Precauções - A transação precisa estar em `dispute` e já ter uma disputa aberta (caso contrário, retorna 422/404). - Envie o **maior número possível de documentos** relevantes — a ausência de documentação adequada pode encerrar a disputa em favor do cliente. - **Há um prazo para enviar os documentos, contado a partir da abertura da disputa.** Não presuma o valor: cada disputa devolve o seu prazo em `deadline` (ver [O objeto `disputes[]`](#o-objeto-disputes)). Envie antes de `deadline.hoursRemaining` chegar a zero. - O ID da transação é sensível a maiúsculas e minúsculas (`tra_...` ou `trx_...`). #### Requisição ``` POST /v1/transactions/{transactionId}/dispute ``` ##### Parâmetros de URL | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `transactionId` | string | Sim | Identificador da transação em disputa | ##### Parâmetros do Corpo da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `documents` | array | Sim | Lista de documentos de evidência (mínimo 1, máximo 50) | | `description` | string | Não | Descrição da situação (máx. 2000) | | `returnEmail` | string | Não | Email para notificações sobre a disputa (máx. 255) | ##### Objeto Document | Campo | Tipo | Obrigatório | Descrição | |-------|------|-------------|-----------| | `type` | string | Sim | Tipo do documento (ver tabela abaixo) | | `assetUrl` | string (url) | Condicional | URL pública do arquivo. **Um** entre `assetUrl` e `assetId` | | `assetId` | string (`asset_…`) | Condicional | Arquivo que você **subiu** para nós (`POST /v1/assets/uploads` → `/confirm`). **Um** entre `assetUrl` e `assetId` | | `description` | string | Não | Descrição do documento (máx. 1000) | | `shippingDate` | string | Não | Data de envio (apenas para `shipmentProof`) | | `trackingCode` | string | Não | Código de rastreamento (apenas para `shipmentProof`) | | `carrier` | string | Não | Transportadora (apenas para `shipmentProof`) | | `authentication` | object | Não | Credenciais para acessar o documento (`username`, `password`, `token`) | ##### Tipos de Documentos (`type`) | Tipo | Descrição | |------|-----------| | `deliveryProof` | Comprovante de entrega | | `shipmentProof` | Comprovante de envio | | `deliveryReceipt` | Recibo de entrega | | `fiscalDocument` | Documento fiscal (nota fiscal) | | `communicationProof` | Comprovante de comunicação com o cliente | ##### Exemplo de Requisição ```json { "returnEmail": "disputes@company.com", "description": "Disputa referente a inconsistência alegada na entrega do pedido.", "documents": [ { "type": "deliveryProof", "assetUrl": "https://exemplo.com/comprovante-entrega.png", "description": "Registro da assinatura no momento da entrega.", "authentication": { "token": "bearer ey..." } }, { "type": "shipmentProof", "assetUrl": "https://exemplo.com/comprovante-envio.png", "description": "Comprovante de envio da encomenda.", "shippingDate": "2026-06-10", "trackingCode": "CA1234CA", "carrier": "Correios" }, { "type": "fiscalDocument", "assetUrl": "https://exemplo.com/nota-fiscal.pdf", "description": "Nota fiscal da compra." } ] } ``` #### Resposta ##### Sucesso (HTTP 200 OK) Retorna a **transação completa atualizada**, com `status: "dispute"` e a disputa preenchida em `disputes[]` (as evidências agrupadas por tipo). A estrutura é a mesma de [Consultar](/docs/guide/transactions/read). ##### O objeto `disputes[]` Além das evidências que você enviou, cada disputa carrega o lado da adquirente — o motivo (que determina *quais* documentos reunir) e o prazo restante: | Campo | Tipo | Descrição | |-------|------|-----------| | `status` | string | `open` (ainda defensável) ou `closed` (já decidida — evidência enviada aqui não muda nada) | | `reason` | string | Família do motivo informado pela adquirente (ex.: `fraud`) | | `reasonCode` | string | Código do motivo (ex.: `F01`) — chave estável para mapear o checklist de documentos | | `reasonDescription` | string | Descrição legível do motivo | | `openedAt` | datetime | Abertura da disputa — é daqui que o prazo conta | | `closedAt` | datetime | Encerramento, quando já decidida (`null` enquanto aberta) | | `outcome` | string | null | Como terminou: `won` (a cobrança voltou a aprovada) ou `lost` (chargeback). Registrado quando o caso foi decidido — um estorno posterior não muda. `null` enquanto aberta e em disputas antigas sem desfecho registrado | | `decidedAt` | datetime | null | Quando o desfecho foi registrado | | `deadline.deadlineAt` | datetime | Instante em que a janela de defesa fecha | | `deadline.hoursRemaining` | integer | **Horas** restantes (negativo depois de vencido) | | `deadline.expired` | boolean | Janela fechada — a adquirente não aceitará mais evidências | | `deadline.urgent` | boolean | Ainda defensável, mas dentro do limiar de urgência | > Leia o prazo em **horas**, não em dias: a janela é curta, e "aberta há 2 dias" não distingue um caso com 10 horas de vida de um já encerrado para envio. `deadline` é o prazo **desta** disputa — não presuma um valor fixo. É `null` apenas quando a adquirente não informou a data de abertura. ```json { "id": "tra_123456789", "customId": "E5D4C3B2A1", "amount": 1229, "originalAmount": 1229, "status": "dispute", "method": "credit", "currency": "BRL", "payment": { "provider": "selectwin", "version": "1.1", "refused": null, "reusable": false, "cardFirstDigits": "555555", "cardLastDigits": "4444", "cardBrand": "Mastercard", "cardRegistered": true, "installments": 1, "expirationDate": null, "paidAt": "2026-02-04T12:48:08.000Z", "allowRenewPayment": false, "invoiceLink": null }, "discount": null, "discounts": null, "customer": { "id": "cus_123456789", "firstName": "Maria", "lastName": "Souza", "email": "maria.souza@exemplo.com", "birthdate": null, "gender": null, "document": { "type": "cpf", "number": "12345678909" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "987654321", "line": "5511987654321" }, "available": true, "delinquent": false, "externalReference": null, "additionalEmails": null, "metadata": null, "updatedAt": "2026-02-04T13:58:09.000Z", "createdAt": "2026-02-03T21:29:11.000Z" }, "billing": { "address": { "id": "addr_123456789", "ownerId": "cus_123456789", "ownerType": "customer", "street": "Avenida Paulista", "number": "302", "complement": "Conjunto 3B, Bloco 3", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "postcode": "01310000", "country": "BR", "latitude": null, "longitude": null, "line": "Avenida Paulista, 302 - Conjunto 3B, Bloco 3, Bela Vista, São Paulo - SP, 01310000, BR", "line1": "Avenida Paulista, 302", "line2": "Conjunto 3B, Bloco 3", "line3": "Bela Vista", "updatedAt": "2026-02-04T13:58:10.000Z", "createdAt": "2026-02-03T21:29:11.000Z" } }, "shipping": null, "externalReference": null, "shippable": true, "spplited": false, "items": null, "receivables": [ { "id": "rec_123456789", "recipient": "bus_123456789", "split": null, "status": "pending", "amount": 1229, "grossAmount": 1229, "anticipationFee": 0, "installmentNumber": 1, "description": null, "currency": "BRL", "authorizationCode": null, "paidAt": null, "refundedAt": null, "canceledAt": null, "expectedOn": null, "liable": true, "chargeProcessingFee": true, "updatedAt": "2026-02-04T12:48:08.000Z", "createdAt": "2026-02-04T12:48:08.000Z" } ], "splits": null, "refunds": null, "disputes": [ { "id": "trd_123456789", "returnEmail": "disputes@company.com", "description": "Disputa referente a inconsistência alegada na entrega do pedido.", "status": "open", "reason": "fraud", "reasonCode": "F01", "reasonDescription": "unauthorized", "openedAt": "2026-02-04T18:00:00.000Z", "closedAt": null, "outcome": null, "decidedAt": null, "deadline": { "deadlineAt": "2026-02-07T18:00:00.000Z", "hoursRemaining": 22, "expired": false, "urgent": true }, "deliveryProof": [ { "assetUrl": "https://exemplo.com/comprovante-entrega.png", "description": "Registro da assinatura no momento da entrega.", "authentication": { "token": "bearer ey..." } } ], "shipmentProof": [ { "carrier": "Correios", "assetUrl": "https://exemplo.com/comprovante-envio.png", "description": "Comprovante de envio da encomenda.", "shippingDate": "2026-06-10", "trackingCode": "CA1234CA" } ], "deliveryReceipts": null, "fiscalDocuments": [ { "assetUrl": "https://exemplo.com/nota-fiscal.pdf", "description": "Nota fiscal da compra." } ], "communicationProof": null, "updatedAt": "2026-02-04T19:03:52.000Z", "createdAt": "2026-02-04T19:03:52.000Z" } ], "timeline": [ { "id": "tl_900", "message": "Dispute evidence submitted", "details": null, "type": "dispute", "updatedAt": "2026-02-04T19:03:52.000Z", "createdAt": "2026-02-04T19:03:52.000Z" } ], "callback": null, "metadata": { "storeId": "str_123456", "storeName": "marketplace" }, "updatedAt": "2026-02-04T19:19:13.000Z", "createdAt": "2026-02-04T12:48:07.000Z", "merchant": { "name": "Selectwin Corp", "merchantId": "bus_123456789", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/transactions/tra_123456789", "method": "GET", "description": "Read a transaction." }, "refund": { "href": "https://api.selectwin.io/v1/transactions/tra_123456789/refund", "method": "POST", "description": "Refund the transaction." }, "capture": { "href": "https://api.selectwin.io/v1/transactions/tra_123456789/capture", "method": "POST", "description": "Capture the transaction." } } } ``` #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `transactionNotInDispute` | 422 | A transação não está em `dispute` | | `disputeNotFound` | 404 | Não há disputa aberta para esta transação | | `disputeAssetNotFound` | 422 | Um `assetId` enviado não existe ou não é seu | | `transactionNotFound` | 404 | Transação inexistente | #### Melhores Práticas 1. **Responda rapidamente** para maximizar as chances de resolução favorável. 2. **Envie o conjunto completo** de evidências em cada submissão (a submissão sobrescreve a anterior). 3. **Inclua comprovantes de entrega** com assinatura e dados de rastreamento. 4. **Documente a comunicação** com o cliente. 5. **Implemente medidas preventivas** (descrições claras de produto, políticas transparentes) para reduzir disputas. --- ## Transações - Reembolsar Fonte: https://selectwin.io/docs/guide/transactions/refund #### Visão Geral Reembolsa uma transação previamente aprovada, com suporte a reembolso total ou parcial. É a ferramenta para devoluções, cancelamentos e correções de cobrança. O reembolso é **assíncrono**: o endpoint aceita a solicitação (HTTP **202**) e o estorno é processado em background. Acompanhe o resultado pelos webhooks (`transaction.refunded`, `receivable.refunded`) ou consultando a transação. #### Precauções - Só pode reembolsar transações no status `approved` ou `pre-authorized`. - O valor do reembolso não pode exceder o valor da cobrança. - Cada venda aceita **um** reembolso: depois dele, mesmo parcial, a transação passa a `refunded`, e um novo pedido de reembolso responde `422 transactionNotRefundable`. - Há uma trava contra reembolsos concorrentes para a mesma transação. - **Plataformas de marketplace:** o reembolso de uma venda feita com `onBehalfOf` é pedido **como a sub-conta**, com `X-Selectwin-Account: bus_…` — veja [Operar como a sub-conta](/docs/guide/marketplace/acting-as). O estorno sai do saldo da sub-conta. - O reembolso afeta o saldo financeiro da conta. - O ID da transação é sensível a maiúsculas e minúsculas (`tra_...` ou `trx_...`). #### Casos de uso - **Cancelamento de compra** após a aprovação. - **Produto indisponível/defeituoso** — devolução. - **Cobrança incorreta** — estorno por valor errado. - **Acordo comercial** — reembolso parcial. #### Requisição ``` POST /v1/transactions/{transactionId}/refund ``` ##### Parâmetros de URL | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `transactionId` | string | Sim | Identificador da transação a reembolsar | ##### Parâmetros do Corpo da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `amount` | integer | Não | Valor a reembolsar em centavos. Se omitido, reembolsa o valor total | | `reason` | string | Não | Motivo do reembolso (máx. 255 caracteres) | ##### Exemplo — Reembolso Total ```json { "reason": "Cliente desistiu da compra e solicitou reembolso" } ``` ##### Exemplo — Reembolso Parcial ```json { "amount": 5000, "reason": "Cliente devolveu apenas um dos itens do pedido" } ``` #### Resposta ##### Aceito (HTTP 202 Accepted) O reembolso foi aceito para processamento assíncrono. ```json { "transactionId": "tra_987654321", "status": "approved", "accepted": true } ``` | Atributo | Tipo | Descrição | |----------|------|-----------| | `transactionId` | string | Identificador da transação | | `status` | string | Status no momento do aceite (a transição para `refunded` chega por webhook) | | `accepted` | boolean | Sempre `true` | #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `transactionNotRefundable` | 422 | A transação não pode ser reembolsada no status atual | | `refundAmountInvalid` | 422 | Valor zero/negativo, ou acima do valor da cobrança | | `refundInProgress` | 409 | Já existe um reembolso em andamento para esta transação | | `refundInsufficientBalance` | 422 | O saldo disponível não cobre o estorno: o valor desta venda já foi liberado (e sacado). Só se aplica quando algum recebível da transação já está liberado; venda ainda em `pending` estorna normalmente | | `transactionInPaymentGroup` | 409 | A transação é uma **parte de um pagamento combinado** (2 cartões ou cartão + Pix) que ainda não terminou: enquanto o pagamento combinado está em andamento, ele mesmo cancela ou estorna as partes. Depois de pago, cada parte se estorna como qualquer transação. `params[0]` traz o `paymentGroupId` e o `status` | | `refundSplitRecipientInsufficientBalance` | 422 | Um **recebedor da divisão** (split) desta venda não tem saldo disponível para a parte dele neste estorno: a fatia dele já foi liberada e sacada. O estorno reserva o valor de cada recebedor no aceite, como faz com o do vendedor | | `transactionNotFound` | 404 | Transação inexistente | ##### Exemplo de erro (HTTP 409) ```json { "error": { "status": "Conflict", "statusCode": 409, "category": "client", "code": "refundInProgress", "message": "A refund for this transaction is already in progress.", "resource": "transaction" } } ``` #### Melhores Práticas 1. **Verifique o status** antes de reembolsar. 2. **Reaja aos webhooks** `transaction.refunded` / `receivable.refunded` para confirmar o estorno. 3. **Registre o motivo** (`reason`) para auditoria. 4. **Para cartão de crédito**, lembre que o estorno pode levar de 7 a 30 dias para aparecer na fatura. --- ## Assinaturas - Visão Geral Fonte: https://selectwin.io/docs/guide/subscriptions/overview #### Introdução O recurso de **Assinaturas (Subscriptions)** gerencia cobranças recorrentes: planos mensais/anuais, trials, ciclos de cobrança, itens (variantes) e divisão de receita (splits). Cada assinatura agrupa um cliente, um ou mais itens recorrentes e um método de pagamento, e a Selectwin orquestra automaticamente as cobranças de cada ciclo, emitindo webhooks a cada mudança de status. #### Modelo Selectwin-strict O endpoint `POST /v1/subscriptions` é **Selectwin-strict**: o **preço vem das variantes** (`var_...`), nunca de valores manuais. Portanto: - **Não existe campo `amount`** no corpo da assinatura — o valor é sempre o snapshot da variante. - Cada item em `items[]` deve referenciar uma **variante recorrente** via `variantId`; você controla apenas `quantity` (e anotações). `unitPrice`/`currency`/`pricingSchema` são congelados a partir da variante. - Uma variante inexistente retorna **`variantNotFound`** (404); uma variante que não está precificada como recorrente retorna **`variantNotRecurring`** (422). Os `items` devem referenciar **variantes recorrentes** (`var_...`; o prefixo legado `prv_...` também é aceito). #### Estrutura do Objeto Assinatura ```json { "id": "subs_01hqzvabc", "status": "active", "currency": "BRL", "method": "credit", "type": "recurring", "currentCycle": { "id": "scy_01hqzvabc", "cycle": 1, "status": "paid", "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-05-01T00:00:00.000Z", "dueDate": "2026-04-01T00:00:00.000Z", "billedAt": "2026-04-01T10:15:00.000Z", "updatedAt": "2026-04-01T10:15:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z" }, "customer": { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva", "email": "joao@example.com" }, "billing": { "frequency": "monthly", "frequencyCount": 1, "endDate": null, "exactDay": null, "freeTrialDays": null, "autoRenew": true, "address": { "street": "Avenida Paulista", "number": "1000", "city": "São Paulo", "state": "SP", "postcode": "01310100", "country": "BR" } }, "canceledAt": null, "cancellation": null, "discount": null, "discounts": null, "renewalTerms": null, "shippable": false, "shipping": null, "spplited": false, "splits": null, "items": [ { "id": "sit_01hqzvabc", "name": "Premium Plan", "enabled": true, "pricingSchema": "recurring", "unitPrice": 9900, "quantity": 1, "currency": "BRL" } ], "callback": { "webhookUrl": "https://example.com/webhook", "active": true }, "geolocation": null, "externalReference": "ORDER-123", "metadata": { "campaign": "launch" }, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { } } ``` > **Valores em centavos.** `items[].unitPrice` é inteiro em centavos (ex.: `9900` = R$ 99,00). > O objeto `currentCycle` reflete o ciclo de cobrança vigente; a recorrência (frequência, dia, trial) > fica sob `billing`. Não há um campo `amount`/`card`/`name` no topo do objeto. #### Status da Assinatura | Status | Descrição | |--------|-----------| | `pending` | Criada, aguardando a confirmação da primeira cobrança | | `trialing` | Em período de teste (trial), antes da primeira cobrança efetiva | | `active` | Ativa e em dia | | `pastdue` | Cobrança de um ciclo falhou; em retentativa (ver [régua de inadimplência](#régua-de-inadimplência)) | | `unpaid` | Retentativas esgotadas sem pagamento; cancelada quando a carência acaba | | `paused` | Pausada (não cobra até ser retomada) | | `canceled` | Cancelada — não gera novos ciclos | #### Régua de inadimplência Quando a cobrança de um ciclo falha, a assinatura vai para `pastdue` e a cobrança é tentada de novo depois de um intervalo, até o teto de tentativas do ciclo (a primeira cobrança conta). Esgotadas as tentativas, ela vai para `unpaid` e, passada a carência, é cancelada (`subscription.canceled`). | Regra | Padrão | A loja pode escolher | |-------|-------:|----------------------| | Tentativas por ciclo (a 1ª incluída) | 5 | de 2 a 10 | | Intervalo entre uma falha e a próxima tentativa | 24 h | de 1 a 168 h | | Carência em `unpaid` até cancelar | 7 dias | de 0 a 30 dias | A loja ajusta a sua régua no painel (configurações de assinatura). Uma mudança vale para os ciclos que ainda não começaram a ser cobrados; se ela **aperta** a régua (menos tentativas, intervalo maior), vale também para a cobrança em andamento. **Afrouxar nunca alcança uma cobrança em andamento**: um ciclo que já está sendo cobrado segue, no máximo, com as tentativas e o intervalo que valiam na primeira tentativa dele. A tentativa que já estava agendada quando a régua mudou acontece. Na [lista de ciclos](/docs/guide/subscriptions/cycles), `attempts` e `nextAttemptAt` mostram onde cada ciclo está. #### O que cada renovação cobra Cada ciclo cobra os itens da assinatura − os cupons recorrentes. Uma assinatura que nasceu no checkout hospedado (a partir de 15/10/2026) cobra também o que as [regras da renovação do plano](/docs/guide/products/overview#regras-da-renovação) diziam no momento da compra, guardadas em `renewalTerms`: - **Frete** (`renewalTerms.shipping`): o frete cotado no checkout (`shippingAmount`, em centavos) entra em cada renovação. - **Desconto do método** (`renewalTerms.methodDiscount`): `discountPercentage`% sobre itens + frete, só nas renovações pagas por `discountMethod`. Uma renovação paga por outro método, por exemplo no cartão depois de trocar o meio de pagamento, não leva o desconto. As regras ficam **congeladas** na assinatura: mudar o plano depois não as altera. `renewalTerms: null` quer dizer que a assinatura não congelou regras (criada antes de 15/10/2026, pela API de assinaturas ou por um upsell), e as renovações dela cobram os itens − os cupons recorrentes. #### Ciclo de Vida ```mermaid stateDiagram-v2 state "pending" as pending state "trialing" as trialing state "active" as active state "pastdue" as pastdue state "unpaid" as unpaid state "paused" as paused state "canceled" as canceled [*] --> pending: POST /v1/subscriptions pending --> trialing: variante com trial pending --> active: 1ª cobrança aprovada trialing --> active: fim do trial, cobrança aprovada active --> pastdue: cobrança falhou pastdue --> active: retentativa aprovada pastdue --> unpaid: retentativas esgotadas active --> paused: POST .../pause paused --> active: POST .../resume active --> canceled: POST .../cancel unpaid --> canceled canceled --> [*] ``` #### Recursos Disponíveis | Recurso | Método | Endpoint | Descrição | |---------|--------|----------|-----------| | [Criar](/docs/guide/subscriptions/create) | POST | `/v1/subscriptions` | Cria uma assinatura recorrente | | [Listar](/docs/guide/subscriptions/list) | GET | `/v1/subscriptions` | Lista paginada de assinaturas | | [Consultar](/docs/guide/subscriptions/read) | GET | `/v1/subscriptions/{id}` | Detalhes de uma assinatura | | [Cancelar](/docs/guide/subscriptions/cancel) | POST | `/v1/subscriptions/{id}/cancel` | Cancela a assinatura (no fim do período ou imediatamente) | | Pausar | POST | `/v1/subscriptions/{id}/pause` | Pausa a assinatura (não cobra até retomar) | | Retomar | POST | `/v1/subscriptions/{id}/resume` | Retoma uma assinatura pausada | | [Ciclos](/docs/guide/subscriptions/cycles) | GET/POST | `/v1/subscriptions/{id}/cycles` | Lista, consulta e reagenda ciclos | | [Itens](/docs/guide/subscriptions/items) | GET/POST/PATCH/DELETE | `/v1/subscriptions/{id}/items` | Gerencia os itens (variantes) | | [Splits](/docs/guide/subscriptions/splits) | GET/POST/PATCH/DELETE | `/v1/subscriptions/{id}/splits` | Divisão de receita por destinatário | > **Cancelar, pausar e retomar** são `POST` em sub-rotas (`/cancel`, `/pause`, `/resume`) e retornam a > assinatura atualizada (`200 OK`). Não há `DELETE` para assinaturas. ##### Pausa e retomada - **Pausar suspende o acesso à Área de Membros** concedido por esta assinatura. Retomar devolve. O acesso que o comprador tem por outra compra não é tocado. - **Retomar reinicia o período hoje.** Um ciclo que venceu durante a pausa é cobrado uma vez, com vencimento hoje, e o seguinte vence um período depois. Os meses da pausa não são cobrados. Se uma renovação foi aprovada durante a pausa, a próxima cobrança fica para o fim do período que ela pagou. - **Quem pausou importa no portal do cliente.** O comprador só retoma pelo portal uma pausa feita por ele mesmo, e sem fatura em aberto. Uma pausa feita por você, pela API ou pelo painel, só você desfaz. ##### Data do próximo ciclo O próximo ciclo mantém o dia de cobrança quando o pagamento atrasa pouco. Se a data calculada a partir do ciclo pago já tiver passado (pagamento meses depois do vencimento), o próximo período conta a partir do pagamento. #### Webhooks Acionados | Evento | Descrição | |--------|-----------| | `subscription.created` | Assinatura criada | | `subscription.pending` | Criada, aguardando a confirmação da primeira cobrança | | `subscription.trialing` | Entrou em período de trial | | `subscription.active` | Assinatura ativada (em dia) | | `subscription.updated` | Assinatura alterada (itens, splits, etc.) | | `subscription.pastdue` | Cobrança de ciclo falhou; em retentativa | | `subscription.unpaid` | Retentativas esgotadas sem pagamento | | `subscription.paused` | Assinatura pausada | | `subscription.canceled` | Assinatura cancelada | > Não existem os eventos `subscription.renewal`/`subscription.reactivated`/`subscription.expired`. Cada ciclo cobrado gera uma [transação](/docs/guide/transactions/overview) com seus próprios eventos (`transaction.approved`/`failed`). A lista completa está no [Catálogo de Eventos](/docs/guide/webhook-events/event-catalog). > Sempre **verifique a assinatura do webhook** antes de processar — veja > [Verificando Assinaturas de Webhook](/docs/guide/webhook-events/verifying-signatures). #### Melhores Práticas 1. **Use variantes recorrentes** para os itens; o preço é definido na variante, não na assinatura. 2. **Reaja a webhooks** (`subscription.*`) em vez de fazer polling — veja [Proibição de Polling](/docs/reference/proibicao-de-polling). 3. **Trate `pastdue`/`unpaid`** notificando o cliente para atualizar o método de pagamento. 4. **Use `externalReference`/`metadata`** para correlacionar com o seu sistema. 5. **Idempotência:** envie `X-Idempotency-Key` ao criar — veja [Idempotência](/docs/reference/idempotencia). #### Integração com Outros Recursos - **Customers** — a assinatura é associada a um cliente (`customer.id` ou dados inline). - **Products / Variants** — os itens referenciam variantes recorrentes (`var_...`). - **Transactions** — cada ciclo cobrado gera uma transação. - **Coupons** — aplicáveis via `discounts[]` (códigos de cupom, re-aplicados por ciclo). - **Webhooks** — notificações em tempo real de mudanças de status. --- ## Criar Assinatura Fonte: https://selectwin.io/docs/guide/subscriptions/create `POST /v1/subscriptions` Cria uma assinatura recorrente para um cliente. A precificação vem das **variantes** referenciadas em `items` (modelo **Selectwin-strict** — veja [Visão Geral](/docs/guide/subscriptions/overview#modelo-selectwin-strict)). #### Requisição ```bash curl -X POST "https://api.selectwin.io/v1/subscriptions" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -H "X-Idempotency-Key: 2dfc6a63-5df5-4900-baca-e49d91ef393e" \ -d '{ "customer": { "firstName": "João", "lastName": "Silva", "email": "joao@example.com", "document": { "type": "cpf", "number": "12345678901" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "999999999" } }, "payment": { "method": "credit", "card": { "id": "card_abc123" }, "installments": 1 }, "items": [ { "variantId": "var_abc123", "quantity": 1 } ], "billing": { "frequency": "monthly", "frequencyCount": 1, "exactDay": 10, "startDate": "2026-08-01", "endDate": null, "freeTrial": { "enabled": true, "days": 7 }, "address": { "id": "addr_x" } }, "discounts": [{ "code": "WELCOME5" }], "externalReference": "SUB-1001", "metadata": { "source": "checkout" } }' ``` > **Agenda sob `billing`.** A agenda de cobrança (`frequency`, `frequencyCount`, `exactDay`, `startDate`, > `endDate`), o `freeTrial` e o endereço do pagador ficam **dentro de `billing`** — espelhando o bloco > `billing` da resposta. O objeto `billing` é opcional (se omitido, usa `frequency=monthly`, > `frequencyCount=1`); só o **valor** nunca vai no request (é derivado do catálogo). > > Enviar qualquer um desses campos na **raiz** (formato antigo) responde **400** dizendo para onde o campo > foi. Antes eles eram ignorados em silêncio, e isso cobrava: um `freeTrial` na raiz não criava período de > teste nenhum — a assinatura nascia cobrável e o cartão era cobrado no mesmo dia. ##### Plano de N ciclos (`cycles` da variante) Uma variante recorrente pode ser vendida com prazo — o `12×` que aparece no catálogo e no portal (`contractedCycles`). Desde **16/set** esse número é **cumprido**: até então ele era exibido e a assinatura renovava para sempre, cobrando o 13º. - O término é a data da **N-ésima cobrança**. O motor encerra quando o próximo vencimento passa do `endDate`, então o ciclo N ainda é cobrado e o N+1 não existe. - **O trial não conta como ciclo:** N ciclos são N *cobranças*, e o período de teste não cobra. A contagem começa na primeira cobrança de verdade. - **Carrinho com planos de prazos diferentes vale o MENOR.** Um item de 12× junto de um de 24× encerra em 12 — seguir cobrando um contrato que já se cumpriu seria cobrar por nada. - **`billing.endDate` do corpo vence sempre.** Quem o manda está declarando o prazo, e o catálogo não sobrepõe decisão do chamador. - ⚠️ **Editar `cycles` na variante não mexe em assinatura já vendida.** O prazo é congelado no dia da compra: o comprador contratou o que a tela mostrou. ##### Campos principais | Campo | Tipo | Obrigatório | Descrição | |-------|------|:-----------:|-----------| | `customer` | object | Sim | Cliente por `id` **ou** inline (`firstName` + `lastName` + `email` obrigatórios no caminho inline; `document`/`telephone`/`gender`/`birthdate`/`additionalEmails` opcionais) | | `items[]` | array | Sim | 1 a 20 itens, cada um referenciando uma **variante recorrente** | | `items[].variantId` | string | Sim | ID da variante (`var_...`; também aceita o prefixo legado `prv_...`) | | `items[].quantity` | integer | Não | Quantidade, 1–999 999 (default `1`) — o mesmo teto de adicionar/alterar um item. O preço vem da variante | | `items[].description` | string | Não | Anotação do item (máx. 2048) | | `items[].externalReference` | string | Não | Sua referência do item | | `items[].metadata` | object | Não | Metadados do item | | `payment.method` | enum | Não | `credit`, `pix` ou `billet` (default `credit`) | | `payment.card` | object | Sim* | Cartão por `id`, por `token` do navegador ([Tokenização no navegador](/docs/guide/cards/tokenization)) **ou** inline. *Obrigatório para `credit` (cobrado automaticamente a cada ciclo) | | `payment.installments` | integer | Não | Parcelas por cobrança, 1–21 (default `1`) | | `payment.pix.expiresInMinutes` | integer | Não | Expiração do PIX por ciclo (15–525600) | | `payment.billet.expiresInDays` | integer | Não | Expiração do boleto por ciclo (1–365) | | `billing` | object | Não | Agenda de cobrança + endereço do pagador (espelha o bloco `billing` da resposta). Se omitido, usa os defaults de intervalo | | `billing.frequency` | enum | Não | `daily`, `weekly`, `monthly`, `yearly` (default `monthly`) | | `billing.frequencyCount` | integer | Não | Multiplicador da frequência, 1–365 (default `1`). Ex.: `billing.frequency=monthly` + `billing.frequencyCount=3` = a cada 3 meses | | `billing.exactDay` | integer | Não | Dia fixo do mês para a cobrança (1–31) | | `billing.startDate` | string | Não | Data de início (`YYYY-MM-DD`; default: hoje). A 1ª cobrança vence nesta data, então ela **não pode ser anterior a hoje** (dia de Brasília) → **422 `subscriptionStartDateInPast`** | | `billing.endDate` | string | Não | Data de término (`YYYY-MM-DD`). **Omitindo**, e se a variante do catálogo declarar um plano de **N ciclos** (`productVariants.cycles`, o "12×" que a loja exibe), o término é derivado: a data da **N-ésima cobrança** — ver abaixo | | `billing.freeTrial` | object | Não | Período de teste: `{ "enabled": true, "days": 7 }` | | `billing.address` | object | Não* | Endereço de cobrança (`id` ou inline). *Obrigatório para `billet` | | `autoRenew` | boolean | Não | Renovação automática (default `true`) — na **raiz**, não é agenda | | `discounts` | array | Não | Cupons por **código** (`{ code }`), máx. 20. Resolvidos e **travados na criação**; cada ciclo re-aplica o cupom conforme seu escopo (`recurring` reaplica a cada ciclo até o limite; `firstCharge` só no 1º ciclo). Mesmas regras de pilha da cobrança avulsa: código repetido → **422 `couponAlreadyApplied`**; cupom `isCumulative:false` junto de outro → **422 `couponNotCumulative`**. Aqui pesa mais, porque a pilha é congelada e reaplicada em **todo** ciclo | | `splits[]` | array | Não | Divisão de receita por ciclo: `{ recipient, type, value }` (máx. 20) — veja [Splits](/docs/guide/subscriptions/splits) | | `name` | string | Não | Nome de exibição da assinatura | | `description` | string | Não | Descrição livre (aceita, mas não persistida na leitura) | | `externalReference` | string | Não | Sua referência externa | | `metadata` | object | Não | Pares chave-valor livres. As chaves `pixAutomatic`, `marketplaceApplicationFee`, `recurringPaymentWindow`, `sessionId` e `integrationId` são da plataforma e são descartadas se vierem no corpo (o Pix Automático se pede por `payment.pix.automatic`) | | `shipping` | object | Não | Dados de entrega (assinaturas com produto físico) | | `callback.webhookUrl` | string (url) | Não | Webhook específico desta assinatura (`callback.active` default `true`) | | `onBehalfOf` | string | Não | `publicId` de uma sub-conta sua: cria a assinatura na sub-conta e cobra a taxa de aplicação da plataforma a cada ciclo (marketplace) | > ⛔ **Selectwin-strict.** A assinatura **não** aceita `amount`, preço manual, nem itens sem variante: o > preço é sempre o snapshot da variante (`unitPrice`/`currency`/agenda congelados na assinatura). A > variante precisa ser **recorrente** (`pricingType = recurring`). #### Resposta `201 Created` ```json { "id": "subs_01hqzvabc", "status": "active", "currency": "BRL", "method": "credit", "type": "recurring", "currentCycle": { "id": "scy_01hqzvabc", "cycle": 1, "status": "paid", "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-05-01T00:00:00.000Z", "dueDate": "2026-04-01T00:00:00.000Z", "billedAt": "2026-04-01T10:15:00.000Z", "updatedAt": "2026-04-01T10:15:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z" }, "customer": { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva", "email": "joao@example.com", "birthdate": null, "gender": null, "document": { "type": "cpf", "number": "12345678901" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "999999999", "line": "5511999999999" }, "available": true, "delinquent": false, "externalReference": null, "additionalEmails": null, "metadata": null, "updatedAt": "2026-04-01T00:00:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z" }, "billing": { "frequency": "monthly", "frequencyCount": 1, "endDate": null, "exactDay": null, "freeTrialDays": null, "autoRenew": true, "address": { "street": "Avenida Paulista", "number": "1000", "complement": null, "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310100", "line": "Avenida Paulista, 1000, Bela Vista, São Paulo - SP, 01310100, BR", "fingerprint": null, "line1": "Avenida Paulista, 1000", "line2": null, "line3": "Bela Vista" } }, "canceledAt": null, "cancellation": null, "discount": { "value": 500, "type": "flat", "percentageOfAmount": null }, "discounts": [ { "source": "coupon", "couponId": 91, "code": "WELCOME5", "type": "flat", "value": 500, "appliedAmount": 500 } ], "shippable": false, "shipping": null, "spplited": false, "splits": null, "items": [ { "id": "sit_01hqzvabc", "name": "Premium Plan", "description": null, "enabled": true, "pricingSchema": "recurring", "unitPrice": 9900, "quantity": 1, "currency": "BRL", "images": null, "metadata": null, "externalReference": null, "updatedAt": "2026-04-01T00:00:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z" } ], "callback": null, "geolocation": null, "externalReference": "SUB-1001", "metadata": { "source": "checkout" }, "updatedAt": "2026-04-01T10:15:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc", "method": "GET", "description": "Read a subscription." } } } ``` > **Valores em centavos.** `items[].unitPrice` é inteiro em centavos. O objeto `currentCycle` reflete o > ciclo de cobrança vigente; o detalhe de cada cobrança está nas [transações](/docs/guide/transactions/overview) > geradas por ciclo. Campos node-only legados (`name`, `card`, `amount`, `currentCharge`) **não** fazem > parte do objeto; a recorrência fica sob `billing`. #### Erros comuns | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `variantNotFound` | 404 | `items[].variantId` não existe | | `variantNotRecurring` | 422 | A variante não está precificada como recorrente | | `subscriptionStartDateInPast` | 422 | `billing.startDate` anterior a hoje (dia de Brasília); `error.params[0]` traz `{ startDate, today }` | | `customerNotFound` | 404 | `customer.id` inexistente | | `cardNotFound` | 404 | Cartão (`payment.card.id`) inexistente | | `addressNotFound` | 404 | Endereço de cobrança inexistente | | `couponNotFound` | 404 | Código de cupom inválido | | `duplicateSplitRecipient` | 422 | Dois splits para o mesmo destinatário | | `splitRecipientNotFound` | 404 | Destinatário de split inexistente | | `onBehalfOfInvalid` | 422 | `onBehalfOf` não referencia uma sub-conta sua / marketplace não habilitado | | `fraudBlocked` | 403 | O antifraude recusou a criação — o mesmo motor da [cobrança avulsa](/docs/guide/transactions/create), com o contexto desta requisição (IP, cabeçalho `X-Device-Fingerprint`) e o do comprador (documento, e-mail, cartão). Uma decisão de **revisão manual** também recusa: a assinatura não fica parada esperando um analista. Nada é criado e nenhum ciclo é cobrado. Os ciclos seguintes não passam de novo pelo antifraude | Catálogo completo: [Códigos de Erro](/docs/reference/codigos-de-erro). --- ## Listar Assinaturas Fonte: https://selectwin.io/docs/guide/subscriptions/list `GET /v1/subscriptions` Retorna uma lista paginada de assinaturas. Cada item de `data` é o **objeto completo** da assinatura (mesma estrutura de [Consultar](/docs/guide/subscriptions/read)), apenas **sem** os `_links` por item. #### Requisição ```bash curl -X GET "https://api.selectwin.io/v1/subscriptions?limit=20&offset=0&sort=descending" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` ##### Parâmetros de consulta | Parâmetro | Tipo | Padrão | Descrição | |-----------|------|--------|-----------| | `limit` | integer | 20 | Itens por página (máx. 100) | | `offset` | integer | 0 | Deslocamento inicial | | `sort` | string | descending | Ordenação por data de criação | | `status` | string | — | Filtra por status (`active`, `trialing`, `pending`, `paused`, `pastdue`, `unpaid`, `canceled`) | | `method` | string | — | Filtra pelo meio de pagamento (`credit`, `pix`, `billet`). Valor fora da lista → `400` | | `customerId` (ou `customerid`) | string | — | Filtra por cliente (`cus_...`) | | `externalreference` | string | — | Filtra pela referência externa | | `customerdocument` | string | — | Filtra pelo documento do cliente (CPF/CNPJ, **somente dígitos**) | | `daterangegte` · `daterangelte` | datetime | — | Criadas a partir de / até este instante (inclusivo, ISO 8601) | | `daterangegt` · `daterangelt` | datetime | — | Idem, exclusivo | | `daterange` | date | — | Criadas neste dia (`YYYY-MM-DD`) | Veja [Paginação](/docs/reference/paginacao) para a estrutura completa de paginação. #### Resposta `200 OK` ```json { "offset": 0, "limit": 20, "total": 50, "hasMore": true, "page": { "current": 1, "total": 3, "offset": { "first": 0, "prev": null, "next": 20, "last": 40 } }, "data": [ { "id": "subs_01hqzvabc", "status": "active", "currency": "BRL", "method": "credit", "type": "recurring", "currentCycle": { "id": "scy_01hqzvabc", "cycle": 1, "status": "paid", "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-05-01T00:00:00.000Z", "dueDate": "2026-04-01T00:00:00.000Z", "billedAt": "2026-04-01T10:15:00.000Z", "updatedAt": "2026-04-01T10:15:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z" }, "customer": { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva", "email": "joao@example.com" }, "billing": { "frequency": "monthly", "frequencyCount": 1, "endDate": null, "exactDay": null, "freeTrialDays": null, "autoRenew": true, "address": { "city": "São Paulo", "state": "SP", "country": "BR" } }, "canceledAt": null, "cancellation": null, "discount": null, "discounts": null, "shippable": false, "shipping": null, "spplited": false, "splits": null, "items": [ { "id": "sit_01hqzvabc", "name": "Premium Plan", "enabled": true, "pricingSchema": "recurring", "unitPrice": 9900, "quantity": 1, "currency": "BRL" } ], "callback": null, "geolocation": null, "externalReference": "ORDER-123", "metadata": { "campaign": "launch" }, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions", "method": "GET", "description": "List all subscriptions." }, "create": { "href": "https://api.selectwin.io/v1/subscriptions", "method": "POST", "description": "Create a new subscription." } } } ``` > Os itens da lista carregam a mesma estrutura completa de [Consultar](/docs/guide/subscriptions/read) (com `currentCycle`, > `billing`, `items`, `customer`), apenas sem o bloco `_links` por item. --- ## Consultar Assinatura Fonte: https://selectwin.io/docs/guide/subscriptions/read `GET /v1/subscriptions/{subscriptionId}` Retorna os detalhes completos de uma assinatura, incluindo o ciclo vigente (`currentCycle`), itens, cliente, agenda de cobrança (`billing`) e splits. O parâmetro de URL aceita os prefixos `subs_` e `sub_`. #### Requisição ```bash curl -X GET "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` #### Resposta `200 OK` ```json { "id": "subs_01hqzvabc", "status": "active", "currency": "BRL", "method": "credit", "type": "recurring", "currentCycle": { "id": "scy_01hqzvabc", "cycle": 1, "status": "paid", "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-05-01T00:00:00.000Z", "dueDate": "2026-04-01T00:00:00.000Z", "billedAt": "2026-04-01T10:15:00.000Z", "updatedAt": "2026-04-01T10:15:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z" }, "customer": { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva", "email": "joao@example.com", "birthdate": null, "gender": null, "document": { "type": "cpf", "number": "12345678901" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "999999999", "line": "5511999999999" }, "available": true, "delinquent": false, "externalReference": null, "additionalEmails": null, "metadata": null, "updatedAt": "2026-04-01T00:00:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z" }, "billing": { "frequency": "monthly", "frequencyCount": 1, "endDate": null, "exactDay": null, "freeTrialDays": null, "autoRenew": true, "address": { "street": "Avenida Paulista", "number": "1000", "complement": null, "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310100", "line": "Avenida Paulista, 1000, Bela Vista, São Paulo - SP, 01310100, BR", "fingerprint": null, "line1": "Avenida Paulista, 1000", "line2": null, "line3": "Bela Vista" } }, "canceledAt": null, "cancellation": null, "discount": null, "discounts": null, "renewalTerms": { "shipping": true, "shippingAmount": 1500, "methodDiscount": false, "discountMethod": "pix", "discountPercentage": 5 }, "shippable": false, "shipping": null, "spplited": false, "splits": null, "items": [ { "id": "sit_01hqzvabc", "name": "Premium Plan", "description": null, "enabled": true, "pricingSchema": "recurring", "unitPrice": 9900, "quantity": 1, "currency": "BRL", "images": null, "metadata": null, "externalReference": null, "updatedAt": "2026-04-01T00:00:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z" } ], "callback": null, "geolocation": null, "externalReference": "ORDER-123", "metadata": { "campaign": "launch" }, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc", "method": "GET", "description": "Read a subscription." }, "cancel": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/cancel", "method": "POST", "description": "Cancel the subscription." } } } ``` #### Atributos da resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador (`subs_*` / `sub_*`) | | `status` | string | Status da assinatura (ver [Visão Geral](/docs/guide/subscriptions/overview#status-da-assinatura)) | | `method` | string \| null | Método de pagamento | | `type` | string \| null | Tipo (ex.: `recurring`) | | `currentCycle` | object \| null | Ciclo de cobrança vigente | | `customer` | object | Dados do comprador (inclui `additionalEmails` como lista) | | `billing` | object | Agenda de cobrança (`frequency`, `frequencyCount`, `exactDay`, `freeTrialDays`, `endDate`, `autoRenew`) + `address` | | `billing.autoRenew` | boolean | Se a assinatura se renova sozinha. É o que o [cancelamento no fim do período](/docs/guide/subscriptions/cancel) desliga — quando ele vira `false`, o ciclo pago corrente é o último | | `canceledAt` | string \| null | Quando a assinatura foi encerrada (cancelamento imediato). `null` enquanto ela estiver viva | | `cancellation` | object \| null | Por que foi cancelada: `reasonCode` (da [lista de motivos](/docs/guide/subscriptions/cancel#motivos-reasoncode), ou `null`), `reason` (o texto livre, ou `null`) e `canceledBy` (`seller`, `customer` ou `platform`). `null` enquanto ninguém pediu o cancelamento. Cancelamentos antigos trazem só o texto | | `discount` | object \| null | Bloco consolidado de desconto | | `discounts` | array \| null | Cupons aplicados (snapshot) | | `renewalTerms` | object \| null | O que cada renovação cobra além dos itens − cupons, congelado na compra pelo checkout ([o que cada renovação cobra](/docs/guide/subscriptions/overview#o-que-cada-renovação-cobra)). `shipping` (boolean): o frete entra em cada renovação; `shippingAmount` (centavos): o frete cotado no checkout; `methodDiscount` (boolean): o desconto do método vale nas renovações; `discountMethod` (`credit` \| `pix` \| `billet` \| `null`): o método que concede o desconto; `discountPercentage` (número): o percentual. `null` = nenhuma regra congelada: as renovações cobram itens − cupons | | `splits` | array \| null | Splits de receita | | `items` | array | Itens recorrentes | | `callback` | object \| null | `webhookUrl` + `active` | | `metadata` | object \| null | Metadados | | `merchant` | object | `name`, `merchantId`, `isSubAccount` | | `_links` | object | `self`, `cancel` | #### Erros | `error.code` | HTTP | |--------------|-----:| | `subscriptionNotFound` | 404 | Para listar várias assinaturas, veja [Listar](/docs/guide/subscriptions/list). --- ## Cancelar Assinatura Fonte: https://selectwin.io/docs/guide/subscriptions/cancel `POST /v1/subscriptions/{subscriptionId}/cancel` Cancela uma assinatura. Por padrão o cancelamento ocorre **no fim do período vigente** (o ciclo atual segue válido e nenhum novo ciclo é agendado). Para encerrar **imediatamente**, envie `immediately: true`. Retorna a assinatura atualizada. Ciclos já cobrados não são reembolsados automaticamente. #### Requisição ```bash curl -X POST "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/cancel" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "reasonCode": "switched_provider", "reason": "Cliente encontrou um plano mais barato", "immediately": false }' ``` ##### Corpo da requisição | Campo | Tipo | Obrigatório | Descrição | |-------|------|:-----------:|-----------| | `reasonCode` | string | Não | O motivo, escolhido na lista abaixo | | `reason` | string | Não | Complemento em texto livre (máx. 255, gravado inteiro na assinatura), para qualquer `reasonCode` | | `immediately` | boolean | Não | `true` cancela na hora (status → `canceled`, ciclos abertos cancelados); ausente/`false` cancela no fim do período (mantém o status atual e desliga a renovação) | ##### Motivos (`reasonCode`) | Código | Significado | |--------|-------------| | `price` | Preço | | `not_using` | Não uso mais | | `technical_issue` | Problema técnico | | `switched_provider` | Mudei de fornecedor | | `other` | Outro | Um código fora da lista é `400 invalidParameters`. O `reasonCode` é opcional para não quebrar quem já integrava só com o texto, mas recomendamos enviá-lo sempre: é ele que o painel agrupa. O mesmo catálogo vale para o cancelamento que o comprador faz no portal do cliente. #### Resposta `200 OK` Retorna o objeto completo da assinatura (mesma estrutura de [Consultar](/docs/guide/subscriptions/read)). Com `immediately: true`, `status` passa a `canceled` imediatamente e `canceledAt` é carimbado. **Como confirmar que o cancelamento pegou.** No fim do período o `status` continua o mesmo de propósito — a assinatura segue valendo até o ciclo pago acabar. O que muda é **`billing.autoRenew`, que vira `false`**, e **`billing.endDate`, que passa a ser o último dia do período pago**. O ciclo que estava agendado para o período seguinte é cancelado na hora: nenhuma cobrança sai depois do pedido. No `endDate` a assinatura vira `canceled` e o webhook `subscription.canceled` é enviado. No cancelamento imediato, olhe `status` e `canceledAt`. > ⚑ **Desde 15/09/2026.** Antes, o cancelamento no fim do período só desligava a renovação: o ciclo seguinte, > que já existia, era cobrado mesmo assim, e o `endDate` só aparecia depois dessa cobrança. ```json { "id": "subs_01hqzvabc", "status": "canceled", "method": "credit", "type": "recurring", "currentCycle": { "id": "scy_01hqzvabc", "cycle": 1, "status": "canceled", "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-05-01T00:00:00.000Z", "dueDate": "2026-04-01T00:00:00.000Z", "billedAt": null, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-01T00:00:00.000Z" }, "customer": { "id": "cus_01hqzvabc", "firstName": "João", "lastName": "Silva", "email": "joao@example.com" }, "billing": { "frequency": "monthly", "frequencyCount": 1, "endDate": null, "exactDay": null, "freeTrialDays": null, "autoRenew": false, "address": { "city": "São Paulo", "state": "SP", "country": "BR" } }, "canceledAt": "2026-04-12T17:56:33.000Z", "cancellation": { "reasonCode": "switched_provider", "reason": "Cliente encontrou um plano mais barato", "canceledBy": "seller" }, "items": [ { "id": "sit_01hqzvabc", "name": "Premium Plan", "enabled": true, "pricingSchema": "recurring", "unitPrice": 9900, "quantity": 1, "currency": "BRL" } ], "externalReference": "ORDER-123", "metadata": { "campaign": "launch" }, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false } } ``` O motivo volta em **`cancellation`** — na resposta, na [consulta](/docs/guide/subscriptions/read) e nos webhooks `subscription.*`: `reasonCode` (o código da lista), `reason` (o texto livre) e `canceledBy` (`seller` quando foi pela API ou pelo painel, `customer` quando foi o comprador no portal, `platform` nos cancelamentos automáticos abaixo). Cancelamentos anteriores à lista continuam com o texto em `reason` e `reasonCode: null`. O cancelamento imediato dispara o webhook `subscription.canceled`. O cancelamento no fim do período dispara `subscription.updated` na hora (com `autoRenew: false` e o `endDate`) e `subscription.canceled` quando o período acaba. #### Cancelamentos automáticos A assinatura também é cancelada na hora, sem chamada sua, quando: - a cobrança de um ciclo sofre **chargeback**; - a cobrança de um ciclo é **estornada por inteiro** e nenhuma outra cobrança aprovada paga aquele ciclo (estorno parcial não cancela); - ela nasceu de um checkout com item avulso e **o pagamento do item avulso falhou**; - ela nasceu de um checkout só de plano sem trial e **a 1ª cobrança, feita no próprio checkout, foi recusada ou o Pix/boleto dela venceu sem pagamento** (a compra não aconteceu — a assinatura não vai para `pastdue`); - a **régua de inadimplência** da loja se esgotou: as tentativas de um ciclo acabaram (`unpaid`) e a carência passou (ver [Visão Geral](/docs/guide/subscriptions/overview#régua-de-inadimplência)). Nesses casos `cancellation.canceledBy` é `platform` e `reasonCode` é `null`. #### Notas - Para **pausar temporariamente** em vez de cancelar, use `POST /v1/subscriptions/{id}/pause` (e `POST /v1/subscriptions/{id}/resume` para retomar). A assinatura entra em `paused` e pode voltar a `active`. - Reembolsos de transações de ciclos já cobrados são feitos pelo recurso de [Transações](/docs/guide/transactions/refund), individualmente. #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `subscriptionNotFound` | 404 | Assinatura inexistente | | `subscriptionAlreadyCanceled` | 422 | A assinatura já está cancelada | --- ## Ciclos da Assinatura Fonte: https://selectwin.io/docs/guide/subscriptions/cycles Cada assinatura é cobrada em **ciclos**. Um ciclo representa um período de cobrança (ex.: um mês), com sua data de vencimento (`dueDate`) e status próprio. A Selectwin gera e cobra os ciclos automaticamente. Os IDs de ciclo usam o prefixo `scy_` (também aceitamos o legado `cycle_`). | Operação | Método | Endpoint | Sucesso | |----------|--------|----------|---------| | Listar ciclos | GET | `/v1/subscriptions/{subscriptionId}/cycles` | 200 | | Consultar ciclo | GET | `/v1/subscriptions/{subscriptionId}/cycles/{cycleId}` | 200 | | Reagendar ciclo | POST | `/v1/subscriptions/{subscriptionId}/cycles` | 201 | ##### Status de ciclo `scheduled` (agendado), `processing` (em cobrança), `paid` (pago), `failed` (falhou), `canceled` (cancelado). --- #### Listar ciclos `GET /v1/subscriptions/{subscriptionId}/cycles` Aceita paginação (`limit` 1–100, padrão 20; `offset`), filtro de período (`daterange`, `daterangegt`, `daterangegte`, `daterangelt`, `daterangelte`) e `status` (um dos status de ciclo acima). ```bash curl -X GET "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/cycles" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` Resposta `200 OK` (paginada): ```json { "offset": 0, "limit": 20, "total": 3, "hasMore": false, "page": { "current": 1, "total": 1, "offset": { "first": 0, "prev": null, "next": null, "last": 0 } }, "data": [ { "id": "scy_01hqzvabc", "cycle": 1, "status": "paid", "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-05-01T00:00:00.000Z", "dueDate": "2026-04-01T00:00:00.000Z", "billedAt": "2026-04-01T10:15:00.000Z", "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-01T00:00:00.000Z", "amount": 19700, "transactionId": "tra_01hqzvxyz", "method": "credit", "expectedAmount": null, "attempts": 1, "nextAttemptAt": null, "failure": null } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/cycles", "method": "GET" } } } ``` Campos do ciclo: `id`, `cycle` (número sequencial), `status`, `startDate`, `endDate`, `dueDate`, `billedAt` (data da cobrança ou `null`), `updatedAt`, `createdAt`, e: | Campo | Tipo | O que é | |---|---|---| | `amount` | inteiro (centavos) | `null` | Valor da última cobrança ligada ao ciclo. `null` quando não há cobrança ligada (ciclo ainda não cobrado ou anterior a este registro) — nunca `0` | | `transactionId` | string | `null` | A última cobrança ligada ao ciclo (`tra_…`) | | `method` | `credit` | `pix` | `billet` | `null` | Meio de pagamento dessa cobrança | | `expectedAmount` | inteiro (centavos) | `null` | No ciclo em aberto (`scheduled` ou `failed`): o valor que a próxima cobrança levaria. `null` nos demais e quando o valor só se conhece na cobrança (uso medido ainda aberto) | | `attempts` | inteiro | Tentativas de cobrança já feitas neste ciclo (no máximo o teto da [régua de inadimplência](/docs/guide/subscriptions/overview#régua-de-inadimplência) da loja — 5 por padrão) | | `nextAttemptAt` | datetime | `null` | Num ciclo `failed` de assinatura `pastdue`: a partir de quando a próxima tentativa acontece. `null` quando as tentativas acabaram ou não se aplica | | `failure` | objeto | `null` | Num ciclo `failed`, por que falhou (abaixo). `null` nos demais, e em falhas antigas sem motivo registrado | `failure`: ```json { "stage": "charge", "code": "insufficientFundsError", "message": "Saldo ou limite insuficiente para realizar esta compra. Verifique o limite disponível ou use outro cartão.", "at": "2026-08-17T10:00:00.000Z" } ``` - `stage: "beforeCharge"` — a cobrança nem chegou a ser feita. `code`: `noCard` (sem cartão), `noBillingAddress` (boleto sem endereço), `unpriceableItems` (item sem preço), `internalError`, ou o código da recusa da cobrança (o mesmo vocabulário de erros da criação de transação). `message` é `null`. - `stage: "charge"` — a cobrança foi recusada. `code` e `message` são o `code` e o `displayMessage` (pt-BR) de `payment.refused` da transação ligada — ver [Recusas de Pagamento](/docs/reference/recusas-de-pagamento). --- #### Consultar ciclo `GET /v1/subscriptions/{subscriptionId}/cycles/{cycleId}` ```json { "id": "scy_01hqzvabc", "cycle": 1, "status": "paid", "startDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-05-01T00:00:00.000Z", "dueDate": "2026-04-01T00:00:00.000Z", "billedAt": "2026-04-01T10:15:00.000Z", "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-01T00:00:00.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/cycles/scy_01hqzvabc", "method": "GET" } } } ``` --- #### Reagendar ciclo `POST /v1/subscriptions/{subscriptionId}/cycles` > ⚠️ **Esta operação reagenda — não cobra.** Ela cancela o ciclo `scheduled` atual e cria um novo ciclo > com vencimento adiantado em um período. **Não** executa cobrança nem cria uma transação imediatamente; > a cobrança acontece quando o novo ciclo vencer. Use-a para deslocar a data de cobrança. Não exige corpo. ```bash curl -X POST "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/cycles" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` Resposta `201 Created` (o ciclo recém-agendado): ```json { "id": "scy_02hqzvdef", "cycle": 2, "status": "scheduled", "startDate": "2026-05-01T00:00:00.000Z", "endDate": "2026-06-01T00:00:00.000Z", "dueDate": "2026-05-01T00:00:00.000Z", "billedAt": null, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/cycles/scy_02hqzvdef", "method": "GET" } } } ``` #### Erros | `error.code` | HTTP | |--------------|-----:| | `subscriptionNotFound` | 404 | | `cycleNotFound` | 404 | --- ## Itens da Assinatura Fonte: https://selectwin.io/docs/guide/subscriptions/items Os **itens** são as linhas recorrentes de uma assinatura, cada uma referenciando uma variante (`var_...`). Você pode adicionar, listar, consultar, atualizar (quantidade) e remover itens. Os IDs de item usam o prefixo `sit_` (também aceitamos o legado `item_`). | Operação | Método | Endpoint | Retorna | |----------|--------|----------|---------| | Adicionar | POST | `/v1/subscriptions/{id}/items` | item criado (`201`) | | Listar | GET | `/v1/subscriptions/{id}/items` | lista paginada de itens | | Consultar | GET | `/v1/subscriptions/{id}/items/{itemId}` | item | | Atualizar | PATCH | `/v1/subscriptions/{id}/items/{itemId}` | item | | Remover | DELETE | `/v1/subscriptions/{id}/items/{itemId}` | confirmação de remoção | > O **preço vem da variante** (modelo Selectwin-strict). Você controla `quantity` (e anotações); o > `unitPrice`/`currency`/`pricingSchema` são um snapshot da variante e são imutáveis. A variante precisa > ser recorrente. Uma assinatura deve manter **pelo menos um** item. --- #### Adicionar item `POST /v1/subscriptions/{subscriptionId}/items` ```bash curl -X POST "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/items" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "variantId": "var_abc123", "quantity": 1, "description": "Add-on de suporte", "externalReference": "line-2", "metadata": { "sku": "SKU-1" } }' ``` | Campo | Tipo | Obrigatório | Descrição | |-------|------|:-----------:|-----------| | `variantId` | string | Sim | ID da variante recorrente (`var_...`; aceita `prv_...` legado) | | `quantity` | integer | Não | Quantidade (1–999999, default `1`) | | `description` | string | Não | Anotação (máx. 2048) | | `externalReference` | string | Não | Sua referência | | `metadata` | object | Não | Pares chave-valor | A resposta `201 Created` retorna o **item criado** (não a assinatura inteira). --- #### Listar itens `GET /v1/subscriptions/{subscriptionId}/items` ```json { "offset": 0, "limit": 20, "total": 1, "hasMore": false, "page": { "current": 1, "total": 1, "offset": { "first": 0, "prev": null, "next": null, "last": 0 } }, "data": [ { "id": "sit_01hqzvabc", "name": "Premium Plan", "description": "Monthly access", "enabled": true, "pricingSchema": "recurring", "unitPrice": 9900, "quantity": 1, "currency": "BRL", "images": null, "metadata": null, "externalReference": "var-premium", "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/items", "method": "GET" } } } ``` --- #### Consultar item `GET /v1/subscriptions/{subscriptionId}/items/{itemId}` — retorna o **objeto do item**: ```json { "id": "sit_01hqzvabc", "name": "Premium Plan", "description": "Monthly access", "enabled": true, "pricingSchema": "recurring", "unitPrice": 9900, "quantity": 1, "currency": "BRL", "images": null, "metadata": null, "externalReference": "var-premium", "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/items/sit_01hqzvabc", "method": "GET" } } } ``` --- #### Atualizar item (quantidade) `PATCH /v1/subscriptions/{subscriptionId}/items/{itemId}` A atualização é **apenas de quantidade** (preço e agenda são snapshots imutáveis da variante). A próxima cobrança já sai com a quantidade nova — e o ajuste proporcional do resto do período em curso entra nela (ver [Ajuste proporcional](#ajuste-proporcional)). ```bash curl -X PATCH "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/items/sit_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "quantity": 2 }' ``` | Campo | Tipo | Obrigatório | Descrição | |-------|------|:-----------:|-----------| | `quantity` | integer | Sim | Nova quantidade (1–999999) | Retorna o **objeto do item** atualizado (`200 OK`). --- #### Remover item `DELETE /v1/subscriptions/{subscriptionId}/items/{itemId}` Remove o item indicado. Retorna uma confirmação de remoção: ```json { "id": "sit_01hqzvabc", "resource": "item", "deleted": true } ``` > Uma assinatura deve manter pelo menos um item — remover o último retorna `422 subscriptionRequiresItem`. --- #### Ajuste proporcional A assinatura é cobrada **adiantada**: cada cobrança paga o período que começa nela. Quando você muda a quantidade, adiciona ou remove um item **no meio do período**, o período em curso já foi pago na quantidade antiga — então a diferença do **resto** dele é calculada na hora da mudança e **entra na próxima cobrança**, junto com o período seguinte já na quantidade nova: ```text ajuste = (valor da linha na quantidade nova − na antiga) × (tempo que falta do período ÷ duração do período) ``` - **Aumento** (mais unidades, item novo ou troca por um plano mais caro): o ajuste é somado à próxima cobrança. Ex.: plano de R$ 10 por unidade, de 2 para 4 unidades no meio do mês → a próxima cobrança é R$ 40 (4 unidades) + R$ 10 (2 unidades × meio período) = R$ 50. - **Redução** (menos unidades, item removido ou troca por um plano mais barato): o ajuste é um **crédito** abatido da próxima cobrança. Um crédito maior que a cobrança zera a cobrança e o que sobra fica para a seguinte — nunca vira dinheiro devolvido, nunca se perde. - **Uma vez só.** O ajuste é consumido quando a cobrança que o levou é **aprovada**. Uma cobrança recusada não consome: a retentativa leva o mesmo ajuste, e a cobrança seguinte à aprovada já não o traz. Duas mudanças simultâneas na mesma quantidade não geram dois ajustes. - **Trocar de plano** é adicionar o item novo e remover o antigo: o resultado é a diferença entre os dois, proporcional. - O cupom recorrente da assinatura **não** desconta o ajuste — ele corrige um período que já passou. - Só há ajuste em assinatura `active`, dentro do período em curso, e em linhas de preço unitário por quantidade (`licensed`). Em teste (`trialing`), em atraso (`pastdue`), não paga, pausada ou em linhas medidas por uso (`metered`) e escalonadas (`tiered`/`package`), a mudança vale a partir da próxima cobrança, sem ajuste. - O valor que a próxima cobrança vai levar, com o ajuste, aparece em `expectedAmount` do ciclo em aberto na [lista de ciclos](/docs/guide/subscriptions/cycles). #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `subscriptionNotFound` | 404 | Assinatura inexistente | | `itemNotFound` | 404 | Item inexistente | | `variantNotFound` | 404 | Variante inexistente (ao adicionar) | | `variantNotRecurring` | 422 | Variante não recorrente (ao adicionar) | | `subscriptionRequiresItem` | 422 | Tentativa de remover o último item | | `subscriptionNotModifiable` | 422 | A assinatura está cancelada | --- ## Splits da Assinatura Fonte: https://selectwin.io/docs/guide/subscriptions/splits Os **splits** dividem a receita de cada cobrança da assinatura entre destinatários (recipients). Cada split define um percentual ou um valor fixo para um destinatário. Os IDs de split usam o prefixo `ssp_` (também aceitamos o legado `split_`). | Operação | Método | Endpoint | Retorna | |----------|--------|----------|---------| | Adicionar | POST | `/v1/subscriptions/{id}/splits` | coleção de splits (`201`) | | Listar | GET | `/v1/subscriptions/{id}/splits` | lista paginada de splits | | Consultar | GET | `/v1/subscriptions/{id}/splits/{splitId}` | split | | Atualizar | PATCH | `/v1/subscriptions/{id}/splits/{splitId}` | split | | Remover | DELETE | `/v1/subscriptions/{id}/splits/{splitId}` | coleção de splits | ##### Campos do split **Na requisição** (adicionar/atualizar): | Campo | Tipo | Descrição | |-------|------|-----------| | `recipient` | string | `publicId` da empresa destinatária (ex.: `bus_...`). Apenas no adicionar | | `type` | enum | `percentage` ou `flat` | | `value` | number | Percentual (maior que 0, até 100) quando `type=percentage`, ou valor fixo em **centavos** (ex.: `1500` = R$ 15,00) quando `type=flat` | > **`flat` com casas decimais.** O `value` do `flat` é sempre lido em **centavos**, nunca em reais. A API aceita > decimal (até `999999.99`) e guarda o número com **duas casas** (`10.555` vira `10.56`); a leitura devolve o que > foi guardado (`10.5`). Na cobrança de cada ciclo, o valor é **arredondado para o centavo inteiro mais próximo** > (meio centavo para cima): `10.5` paga **11 centavos** ao destinatário, `10.4` paga 10, e um valor abaixo de > `0.5` arredonda para zero e o split não é aplicado. O `remainingAmount` desconta o valor guardado, sem arredondar > (`"9989.5"` numa base de `10000`). Para não depender do arredondamento, envie centavos inteiros. **Na resposta**, cada split também traz `chargeProcessingFee` e `liable` (booleans, regras de taxa e responsabilidade por chargeback), além de `id`, `updatedAt`, `createdAt`. --- #### Adicionar split `POST /v1/subscriptions/{subscriptionId}/splits` ```bash curl -X POST "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/splits" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "recipient": "bus_other", "type": "percentage", "value": 30 }' ``` A resposta `201 Created` é a **coleção completa** de splits da assinatura + totais (não a assinatura inteira). `remainingAmount` é uma **string** (centavos restantes da base recorrente após os splits `flat`). ```json { "subscription": "subs_01hqzvabc", "splits": [ { "id": "ssp_01hqzvabc", "recipient": "bus_other", "type": "percentage", "value": 30, "chargeProcessingFee": true, "liable": true, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z" } ], "totalPercentage": 30, "remainingPercentage": 70, "remainingAmount": "6930", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/splits", "method": "POST" }, "list": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/splits", "method": "GET" } } } ``` > O campo `subscription` é o **publicId** (string) da assinatura, não um objeto. > `remainingPercentage`/`remainingAmount` indicam quanto da receita ainda não foi alocado. --- #### Listar splits `GET /v1/subscriptions/{subscriptionId}/splits` ```json { "offset": 0, "limit": 20, "total": 1, "hasMore": false, "page": { "current": 1, "total": 1, "offset": { "first": 0, "prev": null, "next": null, "last": 0 } }, "data": [ { "id": "ssp_01hqzvabc", "recipient": "bus_other", "type": "percentage", "value": 30, "chargeProcessingFee": true, "liable": true, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/splits", "method": "GET" } } } ``` --- #### Consultar / Atualizar / Remover split - `GET /v1/subscriptions/{subscriptionId}/splits/{splitId}` — retorna o **objeto do split**. - `PATCH /v1/subscriptions/{subscriptionId}/splits/{splitId}` — atualiza `type` e `value` (o destinatário não muda) e retorna o **split atualizado** (`200 OK`). - `DELETE /v1/subscriptions/{subscriptionId}/splits/{splitId}` — remove o split e retorna a **coleção** atualizada (com `remainingPercentage`/`remainingAmount` recalculados). Exemplo de atualização: ```bash curl -X PATCH "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc/splits/ssp_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "type": "percentage", "value": 25 }' ``` #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `subscriptionNotFound` | 404 | Assinatura inexistente | | `splitNotFound` | 404 | Split inexistente | | `splitRecipientNotFound` | 404 | Destinatário inexistente | | `duplicateSplitRecipient` | 422 | Já existe split para esse destinatário | | `subscriptionNotModifiable` | 422 | A assinatura está cancelada | --- ## Recebíveis - Visão Geral Fonte: https://selectwin.io/docs/guide/receivables/overview #### Introdução Um **Recebível (Receivable)** representa um valor a receber na sua conta, gerado a partir de uma transação aprovada. Cada parcela de uma venda parcelada e cada destinatário de um split geram seus próprios recebíveis, com data prevista de liberação (`expectedOn`) e status próprio. O recurso de Recebíveis é **somente leitura** na API pública: você lista e consulta recebíveis; a criação e a mudança de status acontecem automaticamente conforme as transações são processadas. #### Estrutura do Recebível ```json { "id": "rec_01hqzvabc", "transactionId": "tra_01hqzvabc", "recipient": "com_123456789ABCDE001", "split": null, "status": "pending", "amount": 4850, "grossAmount": 5000, "antecipationFee": 0, "installmentNumber": 1, "currency": "BRL", "description": "Payment successfully completed.", "authorizationCode": "A1B2C3", "liable": true, "released": false, "expectedOn": "2026-04-20T00:00:00.000Z", "paidAt": null, "refundedAt": null, "canceledAt": null, "chargedBackAt": null, "disputedAt": null, "fraudChekingAt": null, "chargeProcessingFee": true, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` ##### Campos | Campo | Tipo | Descrição | |-------|------|-----------| | `id` | string | Identificador do recebível (`rec_...`) | | `transactionId` | string \| null | Transação que originou o recebível (`tra_*`; legado `trx_*`) | | `recipient` | string \| null | Empresa destinatária do valor | | `split` | string \| null | Split associado, se aplicável | | `status` | string | Status do recebível (veja abaixo) | | `amount` | number | Valor **líquido** em centavos | | `grossAmount` | number | Valor **bruto** em centavos | | `antecipationFee` | number \| null | Taxa de antecipação aplicada (centavos) — chave mantém a grafia histórica `antecipationFee` | | `installmentNumber` | integer \| null | Número da parcela | | `currency` | string | Moeda (ISO 4217) | | `description` | string \| null | Descrição do status | | `authorizationCode` | string \| null | Código de autorização da transação | | `liable` | boolean \| null | Se o destinatário responde por chargebacks | | `released` | boolean \| null | Se o recebível já foi liberado para o saldo disponível | | `expectedOn` | datetime \| null | Data prevista de liberação | | `paidAt` / `refundedAt` / `canceledAt` | datetime \| null | Marcos do ciclo de vida | | `chargedBackAt` / `disputedAt` | datetime \| null | Marcos de chargeback / disputa | | `fraudChekingAt` | datetime \| null | Início da verificação de fraude — chave mantém a grafia histórica `fraudChekingAt` | | `chargeProcessingFee` | boolean \| null | Se a taxa de processamento é cobrada | | `createdAt` / `updatedAt` | datetime | Timestamps ISO 8601 | > Valores em **centavos**. `amount` é o líquido (após taxas); `grossAmount` é o bruto. > As chaves usam `recipient`/`split` (não `recipientId`/`splitId`) e mantêm as grafias históricas `antecipationFee` e `fraudChekingAt`. #### Status do Recebível | Status | Descrição | |--------|-----------| | `scheduled` | Agendado para liberação futura (`expectedOn`) | | `pending` | Aguardando liquidação | | `paid` | Liberado/pago | | `canceled` | Cancelado | | `refunded` | Reembolsado (transação estornada) | | `dispute` | Afetado por disputa | | `chargeback` | Afetado por chargeback | O fluxo de status acompanha o da transação de origem — veja o [ciclo de status dos recebíveis](/docs/guide/transactions/overview#fluxo-de-status-dos-recebíveis). #### Recursos Disponíveis | Recurso | Método | Endpoint | Escopo | Descrição | |---------|--------|----------|--------|-----------| | [Listar](/docs/guide/receivables/list) | GET | `/v1/receivables` | `receivables:list` | Lista paginada de recebíveis (projeção completa) | | Consultar | GET | `/v1/receivables/{receivableId}` | `receivables:read` | Recupera um recebível pelo id (`rec_*`) | A resposta de consulta individual acrescenta `merchant` + `_links` ao objeto acima. #### Webhooks Os recebíveis emitem eventos `receivable.*` (`created`, `scheduled`, `paid`, `refunded`, `canceled`, `dispute`, `chargeback`) — veja o [Catálogo de Eventos](/docs/guide/webhook-events/event-catalog#recebíveis). O `payload.object` do webhook é idêntico ao objeto de leitura acima (sem `_links`). #### Integração - **Transactions** — cada recebível aponta para a transação de origem (`transactionId`). - **Finance** — o saldo (`/v1/balance`) reflete os recebíveis liberados. - **Subscriptions** — ciclos cobrados geram recebíveis como qualquer transação. --- ## Recebíveis - Listar Fonte: https://selectwin.io/docs/guide/receivables/list `GET /v1/receivables` Retorna uma lista paginada de recebíveis com a projeção completa de campos. #### Requisição ```bash curl -X GET "https://api.selectwin.io/v1/receivables?limit=20&offset=0" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` ##### Parâmetros de consulta | Parâmetro | Tipo | Descrição | |-----------|------|-----------| | `limit` / `offset` / `sort` | — | Paginação (1–100, padrão `limit=20`; `sort` = `ascending`/`descending`) | | `status` | string | Filtra por status (texto livre; ex.: `pending`, `paid`, `scheduled`, `fraud-review`) | | `released` | string | Filtra por liberação (`yes` / `no`) | | `transactionId` | string | Filtra pelos recebíveis de uma transação (`tra_*` ou legado `trx_*`) | | `id` | string | Filtra por um recebível específico (`rec_*`) | | `daterange*` | string | Filtros por intervalo de data de criação (`daterange`, `daterangegt`, `daterangegte`, `daterangelt`, `daterangelte`) | | `expectedOnrangegte` / `expectedOnrangelte` | string | Janela pela **data prevista de liberação** (`expectedOn`; instantes ISO 8601, inclusivos) — "o que libera entre hoje e o dia 30" | | `orderBy` | string | `createdAt` (padrão) ou `expectedOn` — por `expectedOn`, os recebíveis sem data vêm por último | #### Resposta `200 OK` ```json { "offset": 0, "limit": 20, "total": 128, "hasMore": true, "page": { "current": 1, "total": 7, "offset": { "first": 0, "prev": 0, "next": 20, "last": 120 } }, "data": [ { "id": "rec_01hqzvabc", "transactionId": "tra_01hqzvabc", "recipient": "com_123456789ABCDE001", "split": null, "status": "pending", "amount": 4850, "grossAmount": 5000, "antecipationFee": 0, "installmentNumber": 1, "currency": "BRL", "description": "Payment successfully completed.", "authorizationCode": "A1B2C3", "liable": true, "released": false, "expectedOn": "2026-04-20T00:00:00.000Z", "paidAt": null, "refundedAt": null, "canceledAt": null, "chargedBackAt": null, "disputedAt": null, "fraudChekingAt": null, "chargeProcessingFee": true, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/receivables", "method": "GET", "description": "List all receivables." } } } ``` > Os itens de `data` trazem a **projeção completa** do recebível (mesmo objeto descrito em > [Visão Geral](/docs/guide/receivables/overview), sem `_links` por item). Para consultar um único recebível com `merchant` > + `_links`, use `GET /v1/receivables/{receivableId}`. > Para o **gráfico de quando o dinheiro chega** (somas por dia, sem paginar), use > [`GET /v1/receivables/schedule`](/docs/guide/receivables/schedule). --- ## Recebíveis - Consultar Fonte: https://selectwin.io/docs/guide/receivables/analytics `GET /v1/receivables/{receivableId}` Recupera um único recebível pelo seu identificador. A resposta é o objeto de recebível completo, acrescido de `merchant` + `_links` no nível raiz. > **Não existe um endpoint de analytics de recebíveis** na API pública. Para um recorte de recebíveis, > use a [Listagem](/docs/guide/receivables/list) com os filtros de `status`, `released`, `transactionId` e `daterange*`. #### Requisição ```bash curl -X GET "https://api.selectwin.io/v1/receivables/rec_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` Escopo necessário: `receivables:read`. ##### Parâmetros de caminho | Parâmetro | Tipo | Obrigatório | Descrição | |-----------|------|-------------|-----------| | `receivableId` | string | Sim | Identificador do recebível (`rec_*`) | #### Resposta `200 OK` ```json { "id": "rec_01hqzvabc", "transactionId": "tra_01hqzvabc", "recipient": "com_123456789ABCDE001", "split": null, "status": "pending", "amount": 4850, "grossAmount": 5000, "antecipationFee": 0, "installmentNumber": 1, "currency": "BRL", "description": "Payment successfully completed.", "authorizationCode": "A1B2C3", "liable": true, "released": false, "expectedOn": "2026-04-20T00:00:00.000Z", "paidAt": null, "refundedAt": null, "canceledAt": null, "chargedBackAt": null, "disputedAt": null, "fraudChekingAt": null, "chargeProcessingFee": true, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/receivables/rec_01hqzvabc", "method": "GET", "description": "Read a receivable." }, "list": { "href": "https://api.selectwin.io/v1/receivables", "method": "GET", "description": "List all receivables." } } } ``` Veja a descrição de cada campo em [Visão Geral](/docs/guide/receivables/overview#campos). #### Respostas de Erro | Código | `code` | Descrição | |--------|--------|-----------| | 404 | `receivableNotFound` | Recebível inexistente ou fora do escopo da empresa | ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "client", "code": "receivableNotFound", "message": "Receivable not found." } } ``` --- ## Recebíveis - Agenda de liberação Fonte: https://selectwin.io/docs/guide/receivables/schedule `GET /v1/receivables/schedule` Quando o dinheiro chega: os recebíveis ainda **não liberados** somados por **dia previsto de liberação** (`expectedOn`), a garantia retida que volta ao saldo disponível em cada dia, e o que já passou do dia e segue na fila. Uma consulta, pronta para o gráfico — sem listar e somar milhares de recebíveis no cliente. #### Requisição ```bash curl -X GET "https://api.selectwin.io/v1/receivables/schedule?from=2026-10-20T03:00:00Z&to=2026-11-19T03:00:00Z" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` ##### Parâmetros de consulta | Parâmetro | Tipo | Descrição | |-----------|------|-----------| | `from` | string | Início da janela (instante ISO 8601). Padrão: hoje, 00:00 de São Paulo | | `to` | string | Fim da janela (ISO 8601, **exclusivo**). Padrão: 30 dias depois de `from`; teto de 366 dias. `to` antes de `from` devolve uma janela vazia | #### Resposta `200 OK` ```json { "environment": "live", "from": "2026-10-20T03:00:00.000Z", "to": "2026-11-19T03:00:00.000Z", "days": [ { "date": "2026-10-21", "pendingCents": 150000, "count": 3, "warrantyCents": 0 }, { "date": "2026-10-23", "pendingCents": 20000, "count": 1, "warrantyCents": 5000 } ], "totals": { "pendingCents": 170000, "count": 4, "warrantyCents": 5000 }, "overdue": { "pendingCents": 9900, "count": 2 } } ``` ##### Campos | Campo | Descrição | |-------|-----------| | `days[].date` | O dia (`YYYY-MM-DD`, fuso de São Paulo) | | `days[].pendingCents` / `days[].count` | O líquido (centavos) e a quantidade de recebíveis previstos para liberar nesse dia | | `days[].warrantyCents` | A garantia retida que volta ao saldo disponível nesse dia | | `totals` | A soma da janela | | `overdue` | O que deveria ter liberado **antes** de `from` e ainda está na fila — uma liberação travada (por exemplo, em análise antifraude) ou atrasada. Não some da agenda | Só os dias com algum valor aparecem em `days`. Entram os recebíveis com `released = false` e status `scheduled` ou `fraud-review`; os em disputa ficam de fora (o valor já está no saldo bloqueado). A data é a **nossa** data de liberação (`expectedOn`), nunca antes de o adquirente nos pagar. > O mesmo recorte por data vale na listagem: `GET /v1/receivables?expectedOnrangegte=…&expectedOnrangelte=…&orderBy=expectedOn` > — veja [Listar](/docs/guide/receivables/list). --- ## Antecipação de Recebíveis - Visão Geral Fonte: https://selectwin.io/docs/guide/anticipations/overview #### Introdução A **antecipação de recebíveis** traz para o seu saldo disponível, hoje, o valor de recebíveis que só liberariam na data prevista (`expectedOn`). O valor cai líquido da **taxa de antecipação** da sua conta (um percentual ao mês, cobrado proporcionalmente aos dias antecipados) e cada taxa aparece como uma linha própria no extrato. O fluxo tem três passos: **cotar** (o que é elegível e quanto custa), **pedir** (o pedido `antr_...`) e, quando o pedido fica acima do teto de aprovação automática, **aguardar a aprovação** da Selectwin. Dentro do teto, o dinheiro cai na hora. > A antecipação precisa estar **habilitada para a sua empresa**. Se não estiver, a cotação diz `blocked: > "anticipationNotEnabledForCompany"` — fale com o suporte. #### Estrutura do Pedido ```json { "id": "antr_01hqzvabc", "status": "executed", "grossCents": 150000, "feeCents": 3488, "netCents": 146512, "feePercentMonth": 2.99, "receivableCount": 2, "requestedAt": "2026-10-20T15:00:00.000Z", "decidedBy": "auto", "decidedAt": "2026-10-20T15:00:00.000Z", "decisionReason": null, "executedAt": "2026-10-20T15:00:00.000Z", "canceledAt": null, "createdAt": "2026-10-20T15:00:00.000Z", "items": [ { "id": "rec_01hqzvabc", "amountCents": 100000, "expectedOn": "2026-11-19T03:00:00.000Z", "days": 30, "feeCents": 2990, "netCents": 97010, "method": "credit" } ] } ``` ##### Campos | Campo | Tipo | Descrição | |-------|------|-----------| | `id` | string | Identificador do pedido (`antr_...`) | | `status` | string | `requested` (aguardando aprovação), `approved`, `executed` (o dinheiro caiu), `rejected`, `canceled` | | `grossCents` | integer | Soma dos recebíveis antecipados (centavos) | | `feeCents` | integer | Soma das taxas (centavos) | | `netCents` | integer | O que caiu (ou cairá) no saldo disponível | | `feePercentMonth` | number | A taxa da sua conta, % ao mês | | `receivableCount` | integer | Quantos recebíveis | | `decidedBy` | string \| null | `auto` (dentro do teto) ou `staff`; `null` enquanto aguarda | | `decisionReason` | string \| null | O motivo de uma recusa | | `items[]` | array | Os recebíveis (só na consulta de um pedido): `days` antecipados e `feeCents` de cada um; `expectedOn` é a data original | ##### Status | Status | Significado | |--------|-------------| | `requested` | Acima do teto de aprovação automática; aguarda a Selectwin. Pode ser cancelado | | `approved` | Aprovado pela Selectwin; executa em seguida | | `executed` | Concluído: o líquido está no saldo disponível e cada recebível saiu como `receivable.paid` | | `rejected` | Recusado (`decisionReason` diz por quê). Os recebíveis voltam a ser elegíveis | | `canceled` | Cancelado por você enquanto aguardava | #### Elegibilidade Entram os recebíveis **agendados** (`scheduled`), ainda **não liberados**, com `expectedOn` **no futuro**, sem disputa nem estorno em andamento e fora de outro pedido vivo. Até **500 recebíveis** por pedido. #### Eventos de webhook `anticipation.requested`, `anticipation.executed`, `anticipation.rejected`, `anticipation.canceled` — com o objeto do pedido. Cada recebível antecipado também dispara `receivable.paid`. #### Endpoints - [Cotar](/docs/guide/anticipations/quote) — `POST /v1/anticipations/quote` - [Pedir](/docs/guide/anticipations/create) — `POST /v1/anticipations` - [Listar](/docs/guide/anticipations/list) — `GET /v1/anticipations` - [Consultar](/docs/guide/anticipations/read) — `GET /v1/anticipations/{anticipationId}` - [Cancelar](/docs/guide/anticipations/cancel) — `POST /v1/anticipations/{anticipationId}/cancel` --- ## Antecipação de Recebíveis - Cotar Fonte: https://selectwin.io/docs/guide/anticipations/quote `POST /v1/anticipations/quote` Diz o que é elegível, a taxa de cada recebível, os totais e **a decisão que o pedido teria** (`auto` = cai na hora; `staff` = aguarda a Selectwin) — ou por que seria recusado. Não escreve nada: cote quantas vezes quiser. #### Requisição ```bash curl -X POST "https://api.selectwin.io/v1/anticipations/quote" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "all": true }' ``` ##### Corpo da Requisição Um dos três, obrigatoriamente: | Campo | Tipo | Descrição | |-------|------|-----------| | `receivableIds` | string[] | Recebíveis específicos (`rec_...`, até 500) | | `untilExpectedOn` | string | Tudo o que libera até este instante (ISO 8601) | | `all` | boolean | Tudo o que é elegível (até 500) | #### Resposta `200 OK` ```json { "items": [ { "id": "rec_01hqzvabc", "amountCents": 100000, "expectedOn": "2026-11-19T03:00:00.000Z", "days": 30, "feeCents": 2990, "netCents": 97010, "method": "credit" }, { "id": "rec_01hqzvabd", "amountCents": 50000, "expectedOn": "2026-10-30T03:00:00.000Z", "days": 10, "feeCents": 498, "netCents": 49502, "method": "pix" } ], "skipped": [{ "id": "rec_01hqzvabe", "reason": "notInFuture" }], "grossCents": 150000, "feeCents": 3488, "netCents": 146512, "feePercentMonth": 2.99, "decision": "auto", "blocked": null } ``` | Campo | Descrição | |-------|-----------| | `items[].days` | Dias de calendário antecipados — a taxa é `amountCents × feePercentMonth% × days / 30`, ao centavo | | `skipped[]` | O que não entra: `notInFuture` = libera hoje ou já passou (a liberação normal cuida) | | `decision` | `auto` (dentro do teto: executa na hora) ou `staff` (aguarda a Selectwin); `null` quando barrado | | `blocked` | O código do motivo pelo qual o pedido seria recusado (o mesmo do erro `422`); `null` quando pode ir | ##### Códigos de `blocked` | Código | Significado | |--------|-------------| | `anticipationDisabled` | A antecipação está desligada na plataforma | | `anticipationNotEnabledForCompany` | Não está habilitada para a sua empresa — fale com o suporte | | `payoutsFrozen` / `withdrawalCompanyNotActive` / `payoutHeldForRiskReview` | Os mesmos bloqueios do saque | | `anticipationNoAcquirerEvidence` | Ainda não há histórico suficiente de liquidação das suas vendas de cartão para antecipá-las (Pix e boleto podem ser cotados à parte) | | `anticipationExposureCapExceeded` | O pedido ultrapassaria o teto de exposição da plataforma — tente um valor menor | --- ## Antecipação de Recebíveis - Pedir Fonte: https://selectwin.io/docs/guide/anticipations/create `POST /v1/anticipations` Cria o pedido. **Dentro do teto de aprovação automática, o dinheiro cai no saldo disponível na mesma chamada** (`status: "executed"`). Acima dele, o pedido fica `requested` até a Selectwin aprovar — você recebe `anticipation.executed` ou `anticipation.rejected` pelo webhook. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Dinheiro**: a antecipação move dinheiro para o seu saldo disponível. Numa sessão do painel exige o segundo fator (como o saque); por chave de API, a chave precisa poder criar saques (`withdrawals:create`). - **Cote antes**: use [Cotar](/docs/guide/anticipations/quote) com o mesmo corpo — a resposta diz a taxa e se o pedido será automático. - **Idempotência**: envie **sempre** o header `X-Idempotency-Key` (um UUID por pedido); um reenvio devolve o mesmo pedido. - **Tudo ou nada**: se um recebível mudar entre o pedido e a execução (estorno, disputa, liberação), o pedido inteiro é recusado (`409 anticipationReceivableChanged`) — peça de novo com o que sobrou. #### Requisição ```bash curl -X POST "https://api.selectwin.io/v1/anticipations" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "X-Idempotency-Key: 6f1c2a1e-7d1d-4b1e-9a6b-2b1c7a2d3e4f" \ -H "Content-Type: application/json" \ -d '{ "receivableIds": ["rec_01hqzvabc", "rec_01hqzvabd"] }' ``` O corpo é o mesmo da cotação: `receivableIds[]`, `untilExpectedOn` ou `all: true`. #### Resposta `201 Created` O objeto do pedido (veja [Visão Geral](/docs/guide/anticipations/overview)), com `status` `executed` (automático) ou `requested` (aguardando). #### Respostas de Erro | Código | Status | Quando | |--------|--------|--------| | `anticipationNothingEligible` | 422 | Nenhum recebível elegível no que foi pedido | | `anticipationNotEnabledForCompany` | 422 | A antecipação não está habilitada para a empresa | | `anticipationNoAcquirerEvidence` | 422 | Sem histórico suficiente de liquidação das vendas de cartão | | `anticipationExposureCapExceeded` | 422 | O pedido ultrapassaria o teto de exposição da plataforma | | `payoutsFrozen` / `withdrawalCompanyNotActive` / `payoutHeldForRiskReview` | 422 | Os bloqueios do saque | | `anticipationReceivableChanged` | 409 | Um recebível mudou entre o pedido e a execução; o pedido foi recusado | | `stepUpRequired` | 401 | Sessão do painel sem o segundo fator recente | --- ## Antecipação de Recebíveis - Listar Fonte: https://selectwin.io/docs/guide/anticipations/list `GET /v1/anticipations` Lista os seus pedidos de antecipação, do mais recente ao mais antigo. #### Requisição ```bash curl -X GET "https://api.selectwin.io/v1/anticipations?status=requested&limit=20&offset=0" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` ##### Parâmetros de consulta | Parâmetro | Tipo | Descrição | |-----------|------|-----------| | `limit` / `offset` | — | Paginação (1–100, padrão `limit=20`) | | `status` | string | `requested`, `approved`, `executed`, `rejected` ou `canceled` | #### Resposta `200 OK` Paginação padrão (`offset`, `limit`, `total`, `hasMore`, `page`) com `data[]` de pedidos — o objeto de [Visão Geral](/docs/guide/anticipations/overview) sem `items` (para os recebíveis, [consulte um pedido](/docs/guide/anticipations/read)). --- ## Antecipação de Recebíveis - Consultar Fonte: https://selectwin.io/docs/guide/anticipations/read `GET /v1/anticipations/{anticipationId}` Devolve um pedido com os seus recebíveis (`items[]`: valor, data original de liberação, dias antecipados, taxa e líquido de cada um). #### Requisição ```bash curl -X GET "https://api.selectwin.io/v1/anticipations/antr_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` #### Resposta `200 OK` O objeto de [Visão Geral](/docs/guide/anticipations/overview), com `items[]`. #### Respostas de Erro | Código | Status | Quando | |--------|--------|--------| | `anticipationNotFound` | 404 | O pedido não existe ou não é desta empresa/ambiente | --- ## Antecipação de Recebíveis - Cancelar Fonte: https://selectwin.io/docs/guide/anticipations/cancel `POST /v1/anticipations/{anticipationId}/cancel` Cancela um pedido que ainda **aguarda aprovação** (`requested`). Um pedido já executado não se cancela — o dinheiro já está no saldo disponível. Idempotente: cancelar de novo devolve o mesmo pedido. #### Requisição ```bash curl -X POST "https://api.selectwin.io/v1/anticipations/antr_01hqzvabc/cancel" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` #### Resposta `200 OK` O pedido com `status: "canceled"` e `canceledAt`. Os recebíveis voltam a ser elegíveis. #### Respostas de Erro | Código | Status | Quando | |--------|--------|--------| | `anticipationNotCancelable` | 409 | O pedido não está mais aguardando (`approved`, `executed` ou `rejected`) | | `anticipationNotFound` | 404 | O pedido não existe ou não é desta empresa/ambiente | --- ## Finanças - Visão Geral Fonte: https://selectwin.io/docs/guide/finance/overview #### Visão Geral O recurso de Finanças na API Selectwin permite gerenciar todos os aspectos financeiros de sua conta, incluindo consulta de saldo, solicitação de saques e acompanhamento de transações financeiras. Este módulo é essencial para o controle financeiro, reconciliação contábil e gestão de fluxo de caixa de sua operação. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este módulo. - **Segurança**: Mantenha as credenciais de acesso à API seguras e implemente controles de acesso adequados para operações financeiras. - **Verificação Regular**: Implemente verificações periódicas do saldo para garantir que os valores estejam corretos. - **Conciliação**: Compare regularmente as transações financeiras com os extratos bancários para identificar divergências. - **Tratamento de Erros**: Implemente tratamento adequado para falhas em saques ou transações financeiras. #### Operações Disponíveis Base URL: `https://api.selectwin.io/v1`. Header de autenticação: `SelectKey` (`sk_test_*` / `sk_live_*`). | Método HTTP | Endpoint | Escopo | Descrição | Documentação | |-------------|----------|--------|-----------|--------------| | GET | `/v1/balance` | `finance:read` | Consultar saldo (buckets disponível/pendente/bloqueado/etc.) | [Consultar Saldo](/docs/guide/finance/balance) | | GET | `/v1/balance/history` | `finance:read` | Listar movimentações do saldo (histórico) | (ver Balance) | | GET | `/v1/receivables` | `receivables:list` | Listar valores a receber | [Listar Recebíveis](/docs/guide/finance/list-receivables) | | GET | `/v1/receivables/{receivableId}` | `receivables:read` | Consultar um recebível | [Recebíveis](/docs/guide/receivables/overview) | | POST | `/v1/withdrawals` | `withdrawals:create` | Solicitar um novo saque | [Criar Saque](/docs/guide/withdrawals/create-withdrawal) | | GET | `/v1/withdrawals` | `withdrawals:list` | Listar saques realizados | [Listar Saques](/docs/guide/withdrawals/list-withdrawals) | | GET | `/v1/withdrawals/{withdrawalId}` | `withdrawals:read` | Consultar um saque específico | [Consultar Saque](/docs/guide/withdrawals/read-withdrawal) | #### Objetos ##### Objeto de Saldo O objeto de saldo representa a situação financeira atual de sua conta na plataforma. Os valores são divididos em "buckets" (centavos, BRL). Resposta com `merchant` no root (sem `_links`). ```json { "availableBalance": 150000, "pendingBalance": 25000, "blockedBalance": 0, "refundedBalance": 12000, "warrantyBalance": 5000, "currency": "BRL", "updatedAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false } } ``` ###### Atributos do Saldo | Atributo | Tipo | Descrição | |----------|------|-----------| | `availableBalance` | number | Saldo disponível para saque imediato (centavos) | | `pendingBalance` | number | Saldo pendente / a liberar (centavos) | | `blockedBalance` | number | Saldo bloqueado (disputas em análise) (centavos) | | `refundedBalance` | number | Acumulado reembolsado (centavos) | | `warrantyBalance` | number | Reserva de garantia (centavos) | | `currency` | string | Sempre `BRL` | | `updatedAt` | string | Data e hora da última atualização do saldo (ISO 8601) | | `merchant` | object | Bloco merchant | > O objeto de saldo **não** possui `id`/`bal_`, `feePlan`, `debit`, `createdAt` nem `_links`. As chaves dos buckets são `availableBalance`/`pendingBalance`/`blockedBalance`/`refundedBalance`/`warrantyBalance`. ##### Objeto de Saque O objeto de saque representa uma solicitação de transferência de fundos da sua conta para uma carteira cadastrada (veja [Saques](/docs/guide/withdrawals/overview) para os detalhes completos). ```json { "id": "cash_123456789", "walletId": "wall_123456789", "amount": 45000, "fee": 15, "status": "pending", "method": "bankTransfer", "currency": "BRL", "transferCode": null, "paidAt": null, "approvedAt": null, "createdAt": "2025-02-07T00:12:33.000Z", "updatedAt": "2025-02-07T00:12:33.000Z" } ``` ###### Parâmetros para Criação de Saque | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `walletId` | string | Sim | Id da carteira (`wall_*`) que receberá o valor | `"wall_123456789"` | | `amount` | integer | Sim | Valor do saque em **centavos** | `45000` | > `amount` e `fee` são números inteiros em centavos. O status inicial é `pending` e o saldo é debitado no ato da criação. Detalhes de status, webhooks e do snapshot `receivingBankAccount` em [Saques](/docs/guide/withdrawals/overview). ##### Objeto de Recebível O objeto de recebível representa um valor a receber em sua conta, proveniente de vendas. Veja [Recebíveis](/docs/guide/receivables/overview) para a referência completa. ```json { "id": "rec_123abc456def789ghi", "transactionId": "tra_987xyz654uvw321rst", "recipient": "com_123456789ABCDE001", "split": null, "status": "paid", "amount": 50000, "grossAmount": 50000, "antecipationFee": 0, "installmentNumber": 1, "currency": "BRL", "description": "Payment successfully completed.", "authorizationCode": "A1B2C3", "liable": true, "released": true, "expectedOn": null, "paidAt": "2025-02-05T18:00:00.000Z", "refundedAt": null, "canceledAt": null, "chargedBackAt": null, "disputedAt": null, "fraudChekingAt": null, "chargeProcessingFee": true, "createdAt": "2025-02-05T17:45:00.000Z", "updatedAt": "2025-02-05T18:30:00.000Z" } ``` ###### Atributos do Recebível | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador único do recebível (`rec_*`) | | `transactionId` | string \| null | Transação que originou o recebível (`tra_*`) | | `recipient` | string \| null | Identificador da empresa destinatária | | `split` | string \| null | Identificador do split associado, se aplicável | | `status` | string | Status atual do recebível (veja abaixo) | | `amount` | number | Valor líquido em centavos | | `grossAmount` | number | Valor bruto em centavos | | `antecipationFee` | number \| null | Taxa de antecipação aplicada (chave mantém a grafia histórica `antecipationFee`) | | `installmentNumber` | integer \| null | Número da parcela | | `currency` | string | Código da moeda (ISO 4217) | | `description` | string \| null | Descrição do status ou informações adicionais | | `authorizationCode` | string \| null | Código de autorização da transação | | `liable` | boolean \| null | Indica se o destinatário responde por chargebacks | | `released` | boolean \| null | Indica se o recebível já foi liberado para o saldo disponível | | `expectedOn` | string \| null | Data prevista de liberação | | `paidAt` | string \| null | Quando o recebível foi pago/liberado | | `refundedAt` | string \| null | Quando o recebível foi reembolsado | | `canceledAt` | string \| null | Quando o recebível foi cancelado | | `chargedBackAt` | string \| null | Quando o recebível sofreu chargeback | | `disputedAt` | string \| null | Quando o recebível entrou em disputa | | `fraudChekingAt` | string \| null | Quando o recebível entrou em verificação de fraude (chave mantém a grafia histórica `fraudChekingAt`) | | `chargeProcessingFee` | boolean \| null | Indica se a taxa de processamento é cobrada | | `createdAt` / `updatedAt` | string | Timestamps ISO 8601 | ###### Fluxo de Status do Recebível ```mermaid stateDiagram-v2 state "scheduled" as scheduled state "pending" as pending state "paid" as paid state "dispute" as dispute state "chargeback" as chargeback state "refunded" as refunded state "canceled" as canceled [*] --> scheduled: recebível gerado (expectedOn futuro) [*] --> pending: aguardando liquidação scheduled --> paid: na data prevista pending --> paid: liberado scheduled --> canceled pending --> canceled paid --> refunded: transação reembolsada paid --> dispute: contestação dispute --> chargeback dispute --> paid: disputa resolvida paid --> [*] refunded --> [*] chargeback --> [*] canceled --> [*] ``` #### Webhooks Acionados **Saques:** | Evento | Descrição | |--------|-----------| | `withdrawal.created` | Saque criado. | | `withdrawal.pending` | Saque em estado pendente. | | `withdrawal.processing` | Saque em processamento. | | `withdrawal.confirmed` | Saque aprovado/confirmado. | | `withdrawal.refused` | Saque recusado/falhou. | | `withdrawal.canceled` | Saque cancelado. | **Recebíveis:** | Evento | Descrição | |--------|-----------| | `receivable.created` | Recebível criado (abertura). | | `receivable.scheduled` | Recebível agendado para liberação futura. | | `receivable.paid` | Recebível liberado/pago. | | `receivable.refunded` | Recebível reembolsado. | | `receivable.canceled` | Recebível cancelado. | | `receivable.dispute` | Recebível afetado por disputa. | | `receivable.chargeback` | Recebível revertido por chargeback. | #### Casos de Uso 1. **Gestão de Fluxo de Caixa**: Monitore entradas e saídas para planejamento financeiro e previsão de receitas. 2. **Reconciliação Contábil**: Exporte o histórico de transações financeiras para integração com sistemas contábeis. 3. **Automação de Saques**: Implemente rotinas automatizadas para solicitar saques quando o saldo disponível atingir determinados valores. 4. **Relatórios Financeiros**: Desenvolva dashboards e relatórios personalizados com base nos dados de transações financeiras. #### Melhores Práticas 1. **Verificar Saldo Disponível**: Sempre consulte o saldo disponível antes de solicitar um saque. 2. **Verificar Taxas**: Esteja ciente das taxas aplicáveis ao seu plano antes de solicitar um saque. 3. **Paginação de Resultados**: Use parâmetros de paginação para consultas de grandes volumes de dados financeiros. 4. **Filtros Eficientes**: Utilize filtros específicos nas consultas para obter apenas os dados relevantes. 5. **Monitoramento Regular**: Acompanhe regularmente o status dos saques e recebíveis para identificar qualquer problema. #### Integração com Outros Módulos - O módulo de Finanças se integra com o módulo de **Transações** para acompanhamento de recebíveis gerados por vendas. - Utilize o módulo de **Webhooks** para receber notificações automáticas sobre eventos financeiros importantes, como novos recebíveis ou mudanças de status em saques. --- ## Finanças - Listar Recebíveis Fonte: https://selectwin.io/docs/guide/finance/list-receivables #### Visão Geral Este endpoint permite recuperar uma lista completa de valores a receber em sua conta, com opções para filtragem e paginação. É uma ferramenta essencial para acompanhamento de fluxo de caixa, previsão de receitas e reconciliação financeira. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Paginação**: Utilize os parâmetros de paginação para controlar o volume de dados retornados. - **Valores Monetários**: Todos os valores monetários são apresentados em centavos. - **Status**: Verifique o status de cada recebível para saber se ele já foi pago ou ainda está pendente. #### Descrição O endpoint de Listagem de Recebíveis permite consultar todos os valores a receber em sua conta, incluindo aqueles já pagos e os ainda pendentes. A resposta inclui detalhes como o valor, data prevista de pagamento, status atual e origem do recebível. #### Requisição ``` GET /v1/receivables ``` Escopo necessário: `receivables:list`. ##### Parâmetros de Consulta | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `limit` | integer | Não | Número máximo de registros (1–100, padrão `20`) | `20` | | `offset` | integer | Não | Posição inicial para paginação (padrão `0`) | `0` | | `sort` | string | Não | Ordenação dos resultados (`ascending` / `descending`) | `descending` | | `status` | string | Não | Filtrar por status (texto livre; ex.: `pending`, `paid`, `scheduled`, `fraud-review`) | `pending` | | `released` | string | Não | Filtrar por liberação (`yes` / `no`) | `yes` | | `transactionId` | string | Não | Filtrar pelos recebíveis de uma transação (`tra_*` ou legado `trx_*`) | `tra_01hqzvabc` | | `id` | string | Não | Filtrar por um ID de recebível específico (`rec_*`) | `rec_123456789` | | `daterange` | string | Não | Filtra por data de criação (`YYYY-MM-DD` ou datetime ISO 8601) | `2026-04-12` | | `daterangegt` / `daterangegte` / `daterangelt` / `daterangelte` | string | Não | Filtros de intervalo por data de criação (datetime ISO 8601) | `2026-04-01T00:00:00Z` | | `expectedOnrangegte` / `expectedOnrangelte` | string | Não | Janela pela data prevista de liberação (`expectedOn`; instantes ISO 8601, inclusivos) | `2026-10-20T03:00:00Z` | | `orderBy` | string | Não | Ordenação: `createdAt` (padrão) ou `expectedOn` (os sem data por último) | `expectedOn` | > `status` é texto livre (validado contra a lista de status aceitos). `released` aceita `yes`/`no`. Um valor desconhecido em `status` simplesmente não retorna registros. #### Resposta - 200 OK Em caso de sucesso, o servidor responde com o código de status HTTP 200 e um objeto JSON contendo a lista de recebíveis e informações de paginação. ```json { "offset": 0, "limit": 10, "total": 35, "hasMore": true, "page": { "current": 1, "total": 4, "offset": { "first": 0, "prev": 0, "next": 10, "last": 30 } }, "data": [ { "id": "rec_123abc456def789ghi", "transactionId": "tra_987xyz654uvw321rst", "recipient": "com_123456789ABCDE001", "split": null, "status": "paid", "amount": 50000, "grossAmount": 50000, "antecipationFee": 0, "installmentNumber": 1, "currency": "BRL", "description": "Payment successfully completed.", "authorizationCode": "A1B2C3", "liable": true, "released": true, "expectedOn": null, "paidAt": "2025-02-05T18:00:00.000Z", "refundedAt": null, "canceledAt": null, "chargedBackAt": null, "disputedAt": null, "fraudChekingAt": null, "chargeProcessingFee": true, "createdAt": "2025-02-05T17:45:00.000Z", "updatedAt": "2025-02-05T18:30:00.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/receivables", "method": "GET", "description": "List all receivables." } } } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` | integer | Número de registros ignorados na consulta | | `limit` | integer | Número máximo de registros por página | | `total` | integer | Número total de registros disponíveis | | `page` | object | Estrutura de página `{ current, total, offset:{first,prev,next,last} }` (offsets são inteiros) | | `hasMore` | boolean | Indica se há mais registros além dos retornados | | `data` | array | Recebíveis (projeção completa — veja o objeto em [Recebíveis](/docs/guide/receivables/overview)) | | `merchant` | object | Merchant | | `_links` | object | Links HATEOAS (no nível raiz) | > As chaves do recebível usam `recipient`/`split` (não `recipientId`/`splitId`), `antecipationFee` e `fraudChekingAt` (grafias históricas mantidas). #### Respostas de Erro ##### 400 Bad Request Ocorre quando a requisição contém parâmetros inválidos ou está mal formatada. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "limit": "The limit must be a number between 1 and 100." } ] } } ``` #### Casos de Uso 1. **Previsão de Fluxo de Caixa**: Visualizar valores a receber para planejamento financeiro. 2. **Reconciliação Financeira**: Comparar recebíveis esperados com valores efetivamente recebidos. 3. **Monitoramento de Pendências**: Identificar recebíveis atrasados ou com problemas. 4. **Relatórios Gerenciais**: Gerar relatórios de receitas para análise de desempenho. #### Melhores Práticas 1. **Paginação Eficiente**: Utilize os parâmetros de paginação para consultas eficientes, especialmente para grandes volumes de dados. 2. **Filtros Específicos**: Use filtros por status, período ou transação para limitar os resultados às informações relevantes. 3. **Consultas Periódicas**: Implemente verificações regulares para atualizar os status dos recebíveis. 4. **Tratamento de Erros**: Implemente tratamento adequado para todos os possíveis códigos de erro. #### Integração com Outros Endpoints - **Consultar Saldo**: Use o endpoint [Consultar Saldo](/docs/guide/finance/balance) para verificar o saldo atual, que inclui os recebíveis já pagos. - **Listar Transações**: Use o endpoint de [Listar Transações](/docs/guide/transactions/list) para obter mais detalhes sobre as transações que originaram os recebíveis. - **Webhooks**: Configure [Webhooks](/docs/guide/webhook-endpoints/create) para receber notificações automáticas sobre mudanças no status dos recebíveis. --- ## Finanças - Consultar Saldo Fonte: https://selectwin.io/docs/guide/finance/balance #### Visão Geral Este endpoint permite recuperar informações detalhadas sobre o saldo financeiro atual da conta, incluindo valores disponíveis para saque, valores em processo de liquidação e valores bloqueados. É essencial para o planejamento financeiro, controle de fluxo de caixa e tomada de decisões sobre transações e saques. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Valores Monetários**: Todos os valores monetários são retornados em centavos (por exemplo, R$ 100,00 é representado como 10000). - **Atualização**: O saldo é atualizado em tempo real conforme as transações são processadas. - **Bloqueios**: Valores podem estar temporariamente bloqueados devido a disputas, chargebacks ou outros processos administrativos. - **Saldo zerado**: Uma empresa sem movimentações ainda não possui registro de saldo; nesse caso, a API retorna todos os buckets zerados (não retorna 404). #### Descrição O endpoint **Consultar Saldo** permite **recuperar a situação financeira atual** da conta, detalhando os valores disponíveis para saque imediato, valores pendentes, bloqueados, reembolsados e a reserva de garantia. É uma ferramenta essencial para o planejamento financeiro e gestão de fluxo de caixa. #### Requisição ``` GET /v1/balance ``` Escopo necessário: `finance:read`. #### Resposta - 200 OK Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 200 OK e os detalhes do saldo atual. ##### Exemplo de Resposta Resposta no nível raiz (com `merchant`, sem `_links`). ```json { "availableBalance": 150000, "pendingBalance": 25000, "blockedBalance": 0, "refundedBalance": 12000, "warrantyBalance": 5000, "currency": "BRL", "updatedAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false } } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `availableBalance` | number | Saldo disponível para saque imediato (centavos) | | `pendingBalance` | number | Saldo pendente / a liberar (centavos) | | `blockedBalance` | number | Saldo bloqueado por disputas em análise (centavos) | | `refundedBalance` | number | Acumulado reembolsado (centavos) | | `warrantyBalance` | number | Reserva de garantia (centavos) | | `currency` | string | Sempre `BRL` | | `updatedAt` | string | Timestamp ISO 8601 | | `merchant` | object | Merchant | > Não existem os campos `id`/`bal_`, `feePlan`, `debit`, `createdAt` nem `_links` nesta resposta. #### Histórico de Movimentações `GET /v1/balance/history` (escopo `finance:read`) lista as movimentações do saldo (envelope paginado padrão). Cada item: ```json { "id": 1024, "fromType": "pending", "toType": "available", "processedAmount": 50000, "oldBalance": { "availableBalance": 100000, "pendingBalance": 75000, "blockedBalance": 0, "refundedBalance": 0 }, "newBalance": { "availableBalance": 150000, "pendingBalance": 25000, "blockedBalance": 0, "refundedBalance": 0 }, "handledBy": "receivable.released", "type": "receivableReleased", "description": "Recebível liberado para saque", "createdAt": "2026-04-12T17:56:33.000Z" } ``` | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | integer | Identificador da movimentação | | `fromType` / `toType` | string \| null | Bucket de origem/destino da movimentação | | `processedAmount` | number | Valor movimentado (centavos) | | `oldBalance` / `newBalance` | object | Snapshots dos buckets antes/depois (`availableBalance`/`pendingBalance`/`blockedBalance`/`refundedBalance`) | | `handledBy` | string \| null | Rótulo de origem da movimentação. **Não é contrato** — pode mudar sem aviso; para classificar/filtrar, use `type` | | `type` | string | O QUE foi a movimentação, em slug estável — use este campo para classificar/filtrar. Valores: `receivableScheduled`, `receivableReleased`, `receivableChargeback`, `receivableDispute`, `receivableRefunded`, `receivableCanceled`, `receivableSettledExternally`, `warrantyHeld`, `warrantyReleased`, `warrantyCover`, `chargebackFee`, `chargebackFeeRefund`, `antifraudFee`, `withdrawalFee`, `withdrawal`, `withdrawalReversal`, `appUsageCharge`, `productSubscription`, `adjustment`, `other` | | `description` | string | A mesma informação em pt-BR, pronta para a linha do extrato ("Taxa de chargeback") | | `createdAt` | string | Timestamp ISO 8601 | > As taxas aparecem aqui como movimentações próprias: a **taxa de chargeback** (`type: "chargebackFee"`) e a **taxa de análise antifraude** (`type: "antifraudFee"`) são debitadas do `availableBalance` e cada uma tem sua linha no histórico. Aceita os parâmetros de paginação `limit` (1–100, padrão 20) e `offset`. #### Saldo negativo e quitação por Pix O `availableBalance` fica **negativo** quando a conta passa a dever à plataforma — em geral um estorno ou chargeback que chegou depois de o dinheiro já ter sido sacado. Enquanto isso durar: - **nenhum saque é possível** — `POST /v1/withdrawals` responde `422 withdrawalBlockedByDebt`, com o valor em aberto em `error.params[0].owedAmount` (centavos). Sacar menos não resolve; - **a dívida é abatida sozinha** pelos próximos recebíveis liberados. Não é preciso fazer nada se a conta continua vendendo. Para quem **não tem entrada** e quer quitar agora, existe o Pix de quitação. ##### Emitir o Pix de quitação ``` POST /v1/balance/debt-settlements ``` Escopo `finance:create`. Sem corpo. Emite um Pix pelo valor devido naquele instante — ou, se já houver um Pix de quitação pendente e dentro da validade, **devolve o mesmo** (um duplo clique nunca gera dois QR para pagar). ```json { "object": "debt_settlement", "id": "dset_rs83xah4c1977g6w", "status": "pending", "owedAtIssuance": 48496, "amount": 48496, "paidAmount": null, "refundedAmount": 0, "currency": "BRL", "pix": { "qrCodeEmv": "00020126…" }, "expiresAt": "2026-09-17T12:00:00.000Z", "paidAt": null, "createdAt": "2026-09-16T12:00:00.000Z" } ``` | Campo | Descrição | |-------|-----------| | `owedAtIssuance` | Quanto a conta devia quando o Pix foi emitido (centavos) | | `amount` | Valor do Pix (centavos) | | `paidAmount` | O valor **confirmado pelo banco** — `null` enquanto pendente | | `refundedAmount` | Quanto do pago o banco **devolveu** ao pagador e voltou a ser dívida (centavos). `0` quando nada foi devolvido | | `pix.qrCodeEmv` | Copia e cola. Só vem enquanto `pending` | | `status` | `pending`, `paid`, `expired` ou `refunded` (devolvido por inteiro) | - Sem saldo negativo, responde `422 balanceHasNoDebt`. - **Um Pix de cada vez.** Enquanto houver um Pix de quitação em aberto, o `POST` devolve **o mesmo** — e dois pedidos no mesmo instante (o segundo chega enquanto o primeiro ainda espera o banco) respondem `409 debtSettlementInProgress`: aguarde alguns segundos e peça de novo. A rota aceita `X-Idempotency-Key`. - O pagamento é confirmado **consultando o banco**, não só pelo aviso dele. Confirmado, o valor pago entra no `availableBalance` de uma vez, sem taxa e sem retenção, e aparece no histórico como `type: "debtSettlement"` — "Quitação de saldo negativo (Pix)". - **Pagou a mais, fica com o saldo.** A dívida pode diminuir entre a emissão e o pagamento (um recebível liberado no meio). O valor pago é creditado inteiro, e o que passar da dívida fica disponível para saque. - A validade do Pix é de 24 horas. Vencido, emita outro. - **Se o banco devolver o Pix** (MED, contestação ou devolução feita no banco), o crédito sai do `availableBalance` e a dívida volta — com o saque barrado de novo. A devolução é conferida no banco antes, debitada **uma vez** por devolução e nunca além do que foi pago, e aparece no histórico como `type: "debtSettlementRefund"`. Uma devolução parcial debita só a parte devolvida e mantém `status: "paid"`, com `refundedAmount` preenchido. ##### Listar as quitações ``` GET /v1/balance/debt-settlements?limit=20&offset=0 ``` Escopo `finance:read`. O registro de cada quitação emitida — pendente, paga, vencida ou devolvida —, da mais recente para a mais antiga. #### Respostas de Erro ##### 401 Unauthorized Ocorre quando a autenticação falha ou o token de acesso é inválido. ```json { "error": { "status": "Unauthorized", "statusCode": 401, "category": "client", "message": "Authentication credentials are missing or invalid." } } ``` #### Casos de Uso 1. **Planejamento Financeiro**: Verificar saldo disponível para planejar operações financeiras 2. **Controle de Fluxo de Caixa**: Monitorar entradas e saídas para gestão de caixa 3. **Decisões de Saque**: Determinar valores disponíveis para transferência para conta bancária 4. **Reconciliação Contábil**: Verificar saldos para reconciliação com registros contábeis 5. **Auditoria de Movimentações**: Use `GET /v1/balance/history` para auditar como o saldo mudou ao longo do tempo #### Melhores Práticas 1. **Consulta Regular**: Verifique o saldo regularmente para manter-se atualizado 2. **Antes de Saques**: Sempre consulte o saldo disponível antes de solicitar saques 3. **Monitoramento de Pendências**: Acompanhe os valores pendentes (`pendingBalance`) para planejamento financeiro 4. **Automação**: Considere integrar consultas automáticas de saldo em seus sistemas 5. **Tratamento de Saldo Zerado**: Trate o caso de uma conta nova, cujos buckets retornam todos `0` #### Integração com Outros Endpoints - Use [Listar Recebíveis](/docs/guide/finance/list-receivables) para verificar os valores a receber futuros - Use [Criar Saque](/docs/guide/withdrawals/create-withdrawal) para solicitar saques dos valores disponíveis - Use [Listar Saques](/docs/guide/withdrawals/list-withdrawals) para acompanhar os saques solicitados --- ## Saques - Visão Geral Fonte: https://selectwin.io/docs/guide/withdrawals/overview #### Visão Geral O módulo de Saques na API Selectwin permite gerenciar todas as operações relacionadas à transferência de fundos de sua conta para carteiras específicas. Este módulo é essencial para o controle de fluxo de caixa, permitindo solicitar, acompanhar e gerenciar saques de forma eficiente e segura. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este módulo. - **Segurança**: Mantenha as credenciais de acesso à API seguras e implemente controles de acesso adequados para operações de saque. - **Verificação de Saldo**: Sempre verifique o saldo disponível antes de solicitar um saque. - **Valores Mínimos**: Respeite os valores mínimos estabelecidos pelo seu plano de taxas. - **Tratamento de Erros**: Implemente tratamento adequado para falhas em saques ou transações financeiras. #### Operações Disponíveis Base URL: `https://api.selectwin.io/v1`. Header de autenticação: `SelectKey` (`sk_test_*` / `sk_live_*`). | Método HTTP | Endpoint | Escopo | Descrição | Documentação | |-------------|----------|--------|-----------|--------------| | POST | `/v1/withdrawals` | `withdrawals:create` | Solicitar um novo saque | [Criar Saque](/docs/guide/withdrawals/create-withdrawal) | | GET | `/v1/withdrawals` | `withdrawals:list` | Listar saques realizados | [Listar Saques](/docs/guide/withdrawals/list-withdrawals) | | GET | `/v1/withdrawals/{withdrawalId}` | `withdrawals:read` | Consultar um saque específico | [Consultar Saque](/docs/guide/withdrawals/read-withdrawal) | > Para consultar o saldo disponível antes de sacar, use [Consultar Saldo](/docs/guide/finance/balance) (`GET /v1/balance`). #### Objeto de Saque O objeto de saque representa uma solicitação de transferência de fundos da sua conta para uma carteira cadastrada. Respostas de recurso (create/read) incluem `merchant` + `_links` + o snapshot `receivingBankAccount` no root. Os itens da listagem (`/v1/withdrawals`) **não** carregam `receivingBankAccount` nem `_links` por item. ```json { "id": "cash_01hqzvabc", "walletId": "wall_01hqzvabc", "amount": 45000, "fee": 15, "status": "pending", "method": "bankTransfer", "currency": "BRL", "transferCode": null, "paidAt": null, "approvedAt": null, "createdAt": "2025-02-07T00:12:33.000Z", "updatedAt": "2025-02-07T00:12:33.000Z", "receivingBankAccount": { "holderName": "João Santos", "document": "12345678901", "bankCode": "260", "bankName": null, "routingNumber": "0001", "accountNumber": "11111111-5", "type": "checking", "pixKey": null }, "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/withdrawals/cash_01hqzvabc", "method": "GET", "description": "Read a withdrawal." } } } ``` ###### Parâmetros para Criação de Saque | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `walletId` | string | Sim | Id da carteira (`wall_*`) que receberá o valor | `"wall_01hqzvabc"` | | `amount` | integer | Sim | Valor do saque em **centavos** (mínimo 1; e ≥ valor mínimo do plano) | `45000` | ###### Atributos do Saque (Resposta) | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `cash_*` | | `walletId` | string \| null | Carteira de destino (`wall_*`) | | `amount` | number | Valor em centavos | | `fee` | number | Taxa do saque em centavos | | `status` | string | pending/processing/confirmed/approved/analysis/refused/canceled/failed | | `method` | string \| null | `bankTransfer` ou `pixTransfer` | | `currency` | string | `BRL` | | `transferCode` | string \| null | Código de referência da transferência | | `paidAt` / `approvedAt` | string \| null | Timestamps | | `createdAt` / `updatedAt` | string | Timestamps ISO 8601 | | `receivingBankAccount` | object \| null | Snapshot dos dados bancários no momento do saque (somente em read/create) — `holderName`, `document`, `bankCode`, `bankName`, `routingNumber`, `accountNumber`, `type`, `pixKey` | | `merchant` | object | Merchant (somente em respostas de recurso) | | `_links` | object | HATEOAS (somente em respostas de recurso) | > **Saque automático (auto-cashout):** regras recorrentes de saque por percentual estão disponíveis na API pública em `/v1/withdrawals/auto` (CRUD: POST/GET/PUT/DELETE, escopos `withdrawals:*`) — recurso `caa_*`. Esta referência cobre os saques manuais (`/v1/withdrawals`). > > - Criar ou alterar uma ordem de saque automático notifica os administradores da conta, com o destino > mascarado e o percentual. > - No saque automático a **taxa sai de dentro** do percentual: do saldo disponível sai exatamente o percentual > configurado, e o saque gerado (`amount`) é esse valor menos a taxa (`fee`). Assim uma ordem de 100% zera o > saldo disponível. Quando o valor menos a taxa fica abaixo do valor mínimo de saque, nada é debitado e o ciclo > é pulado. No saque manual (`POST /v1/withdrawals`) nada muda: a taxa é cobrada além do valor pedido. > - **Várias regras saem sobre o mesmo saldo do dia.** O percentual de cada regra é calculado sobre o saldo > disponível lido **uma vez** para a empresa na rodada — não sobre o que a regra anterior deixou. Duas regras de > 50% levam 100% entre si (antes levavam ~75%). É por isso que a soma dos percentuais ligados é limitada a 100%. > - **`lastOutcome`** — o que a última rodada fez: `{ status, reason, at, withdrawalId, amount }` ou `null` quando > a regra nunca rodou. `status` é `executed` (saiu: `withdrawalId` é o saque criado e `amount` o valor transferido), > `skipped` (nada foi debitado; `reason` diz por quê: `walletNotFound` = a conta de destino foi removida, > `insufficientBalance` = sem saldo disponível, `belowMinimumAfterFee` = o percentual menos a taxa ficou abaixo do > mínimo) ou `failed` (o saque foi recusado; `reason` é o código da recusa, como `payoutsFrozen` ou > `balanceIsInsufficient`). `lastRunAt` continua sendo o instante em que a regra foi **considerada**, não o resultado. > - **`walletArchived`** — a conta de destino foi removida (`DELETE /v1/wallets/{walletId}`). Remover a conta > **desliga** a regra (`enabled: false`) e a tira da soma dos 100%; para voltar, envie o `PUT` com outro `walletId`. > Ligar a regra apontando para a conta removida é recusado com `422 autoCashoutWalletArchived`. > - Quando uma regra passa **3 dias seguidos** sem sair, os administradores da conta recebem um aviso com o motivo > (uma vez por sequência). ###### Status do Saque | Status | Descrição | |--------|-----------| | `pending` | Saque aguardando processamento | | `processing` | Saque em processamento | | `analysis` | Saque em análise | | `approved` / `confirmed` | Saque aprovado/confirmado | | `paid` | Saque concluído com sucesso | | `refused` / `failed` | Saque recusado/falhou | | `canceled` | Saque cancelado | #### Fluxo de Status do Saque ```mermaid stateDiagram-v2 state "pending" as pending state "processing" as processing state "confirmed" as confirmed state "paid" as paid state "refused" as refused state "canceled" as canceled [*] --> pending: POST /v1/withdrawals (debita o saldo disponível) pending --> processing: em processamento pending --> canceled: cancelado pending --> refused: recusado processing --> confirmed: confirmado processing --> paid: concluído processing --> refused: falhou confirmed --> paid paid --> [*] refused --> [*] canceled --> [*] ``` > O saldo disponível é debitado **no momento da criação** do saque (débito antes do envio ao provedor, para evitar gasto duplicado). Uma falha posterior reverte o débito. #### Webhooks Acionados | Evento | Descrição | |--------|-----------| | `withdrawal.created` | Acionado quando o saque é criado. | | `withdrawal.pending` | Acionado quando o saque entra em estado pendente (emitido junto do `created`). | | `withdrawal.processing` | Acionado quando o saque está em processamento. | | `withdrawal.confirmed` | Acionado quando o saque é aprovado/confirmado (status `approved`/`confirmed`). | | `withdrawal.refused` | Acionado quando o saque é recusado (status `failed`). | | `withdrawal.canceled` | Acionado quando o saque é cancelado. | > O nome do evento de webhook difere do status do saque: `approved`/`confirmed` → `withdrawal.confirmed`; `failed` → `withdrawal.refused`. #### Casos de Uso 1. **Gestão de Fluxo de Caixa**: Monitore saques para controle de saídas e planejamento financeiro. 2. **Automação de Saques**: Implemente rotinas automatizadas para solicitar saques quando o saldo disponível atingir determinados valores. 3. **Relatórios de Saques**: Desenvolva dashboards e relatórios personalizados com base no histórico de saques. 4. **Auditoria de Transferências**: Acompanhe o histórico completo de saques para fins de auditoria e compliance. #### Melhores Práticas 1. **Verificar Saldo Disponível**: Sempre consulte o saldo disponível antes de solicitar um saque. 2. **Verificar Taxas**: Esteja ciente das taxas aplicáveis ao seu plano antes de solicitar um saque. 3. **Paginação de Resultados**: Use parâmetros de paginação para consultas de grandes volumes de saques. 4. **Filtros Eficientes**: Utilize filtros específicos nas consultas para obter apenas os saques relevantes. 5. **Monitoramento Regular**: Acompanhe regularmente o status dos saques para identificar qualquer problema. 6. **Tratamento de Webhooks**: Implemente handlers adequados para os eventos de webhook relacionados a saques. #### Integração com Outros Módulos - O módulo de Saques se integra com o módulo de **Finanças** para verificação de saldo disponível. - Utilize o módulo de **Webhooks** para receber notificações automáticas sobre mudanças de status em saques. --- ## Finanças - Criar Saque Fonte: https://selectwin.io/docs/guide/withdrawals/create-withdrawal #### Visão Geral Este endpoint permite solicitar a transferência de fundos disponíveis em sua conta para uma carteira específica. É utilizado para iniciar o processo de saque, que será posteriormente processado e transferido para a carteira associada ao walletId fornecido. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Saldo Disponível**: Verifique sempre o saldo disponível antes de solicitar um saque usando o endpoint [Consultar Saldo](/docs/guide/finance/balance). O valor (mais a taxa) é debitado do saldo disponível **no ato da criação**. - **Saques habilitados**: O saque precisa estar habilitado para a empresa; caso contrário, a criação retorna `400 withdrawalCreateNotEnabled`. - **Valor Mínimo**: Existe um valor mínimo de saque por empresa (padrão R$ 50,00 = `5000` centavos quando não configurado). Abaixo dele a API retorna `400 withdrawalAmountLessThanMinimum`. - **Processamento**: Saques não são instantâneos e passam por um processo de verificação antes de serem aprovados e processados. - **Carência de carteira nova**: Uma carteira **recém-cadastrada** não pode receber saque até completar o período de carência de segurança — a API retorna `422 withdrawalWalletInCooldown`. Vale apenas no ambiente live. - **Idempotência**: A criação é idempotente — envie **sempre** o header `X-Idempotency-Key` (um UUID por pedido) para que um reenvio do mesmo pedido devolva o mesmo saque em vez de criar outro. Sem a chave, a API ainda recusa com `409 withdrawalDuplicateSuspected` um segundo pedido **idêntico** (mesma carteira e mesmo valor) feito em menos de 10 segundos — nada é debitado por ele; mas essa rede só pega o duplo clique, não substitui a chave. - **Valores Monetários**: Todos os valores monetários devem ser informados em centavos (por exemplo, R$ 450,00 deve ser enviado como `45000`, um número inteiro). #### Descrição O endpoint de Criação de Saque permite **solicitar a transferência de fundos disponíveis** em sua conta para uma carteira específica. Ao fornecer o ID da carteira e o valor desejado, o sistema iniciará o processo de saque, que será verificado, aprovado e processado de acordo com as políticas e prazos vigentes. #### Requisição ``` POST /v1/withdrawals ``` ##### Corpo da Requisição ```json { "walletId": "wall_01hqzvabc", "amount": 45000 } ``` ##### Parâmetros do Corpo | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `walletId` | string | Sim | Identificador da carteira (`wall_*`) que receberá o valor | `"wall_01hqzvabc"` | | `amount` | integer | Sim | Valor do saque em centavos (mínimo 1; e ≥ valor mínimo do plano) | `45000` | #### Resposta - 201 Created Em caso de sucesso, o servidor responde com o código de status HTTP 201 Created e um objeto JSON contendo as informações do saque criado. ```json { "id": "cash_01hqzvabc", "walletId": "wall_01hqzvabc", "amount": 45000, "fee": 15, "status": "pending", "method": "bankTransfer", "currency": "BRL", "transferCode": null, "paidAt": null, "approvedAt": null, "createdAt": "2025-02-07T00:12:33.000Z", "updatedAt": "2025-02-07T00:12:33.000Z", "receivingBankAccount": { "holderName": "João Santos", "document": "12345678901", "bankCode": "260", "bankName": null, "routingNumber": "0001", "accountNumber": "11111111-5", "type": "checking", "pixKey": null }, "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/withdrawals/cash_01hqzvabc", "method": "GET", "description": "Read a withdrawal." }, "create": { "href": "https://api.selectwin.io/v1/withdrawals", "method": "POST", "description": "Create a new withdrawal." }, "list": { "href": "https://api.selectwin.io/v1/withdrawals", "method": "GET", "description": "List all withdrawals." } } } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `cash_*` | | `walletId` | string \| null | Carteira de destino (`wall_*`) | | `amount` | number | Em centavos | | `fee` | number | Taxa em centavos | | `status` | string | `pending` ao criar | | `method` | string \| null | `bankTransfer` ou `pixTransfer` | | `currency` | string | `BRL` | | `transferCode` | string \| null | Ref da transferência (nulo ao criar) | | `paidAt` / `approvedAt` | string \| null | Timestamps | | `createdAt` / `updatedAt` | string | Timestamps | | `receivingBankAccount` | object \| null | Snapshot dos dados bancários no momento do saque | | `merchant` | object | Merchant | | `_links` | object | HATEOAS | #### Respostas de Erro | Código | `code` | Quando ocorre | |--------|--------|---------------| | 400 | (validation) | Corpo inválido (ex.: `amount` ausente ou < 1, `walletId` mal formado) | | 400 | `withdrawalCreateNotEnabled` | Saques não estão habilitados para a empresa | | 400 | `withdrawalAmountLessThanMinimum` | Valor abaixo do mínimo permitido | | 404 | `walletNotFound` | A carteira informada não existe | | 409 | `withdrawalDuplicateSuspected` | Já existe um saque `pending` desta conta com a **mesma carteira e o mesmo valor** criado há menos de 10 segundos e este pedido veio **sem** `X-Idempotency-Key` — nada foi debitado. `params[0].withdrawalId` é o saque que já existe. Para reenviar com segurança, mande a chave de idempotência | | 422 | `withdrawalWalletInCooldown` | A carteira de destino foi cadastrada há pouco tempo e ainda está na carência de segurança (live) | | 422 | `payoutsFrozen` | Saques da conta temporariamente bloqueados pela plataforma | | 422 | `balanceIsInsufficient` | Saldo disponível insuficiente para o valor + taxa | | 422 | `withdrawalBlockedByDebt` | A conta está com **saldo negativo** — uma dívida com a plataforma. Nenhum saque é possível até quitar; os próximos recebíveis liberados abatem a dívida sozinhos. `params[0].owedAmount` traz o valor em aberto, em centavos. Diferente do `balanceIsInsufficient`: aqui não adianta sacar menos | ##### 400 Bad Request (validação) Ocorre quando a requisição contém parâmetros inválidos ou está mal formatada. ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "amount": "The withdrawal amount must be greater than zero." } ] } } ``` ##### 422 Unprocessable Entity (saldo insuficiente) ```json { "error": { "status": "Unprocessable Entity", "statusCode": 422, "category": "validation", "code": "balanceIsInsufficient", "message": "Insufficient available balance for this withdrawal." } } ``` #### Casos de Uso 1. **Pagamento de Fornecedores**: Transferir fundos para pagamento de fornecedores ou prestadores de serviço. 2. **Retirada de Lucros**: Realizar a retirada de lucros para os sócios ou proprietários do negócio. 3. **Transferência Entre Contas**: Mover fundos entre diferentes contas da mesma empresa. 4. **Pagamentos Recorrentes**: Configurar saques programados para pagamentos recorrentes. #### Melhores Práticas 1. **Verificar Saldo**: Sempre verifique o saldo disponível antes de solicitar um saque. 2. **Confirmar Valores**: Confirme o valor correto em centavos para evitar erros. 3. **Acompanhar Status**: Utilize o endpoint [Consultar Saque](/docs/guide/withdrawals/read-withdrawal) para acompanhar o status do saque após sua criação. 4. **Tratamento de Erros**: Implemente tratamento adequado para todas as possíveis respostas de erro. 5. **Registro de Histórico**: Mantenha um registro de todos os saques realizados para fins de auditoria. #### Integração com Outros Endpoints - **Consultar Saldo**: Use o endpoint [Consultar Saldo](/docs/guide/finance/balance) para verificar o saldo disponível antes de solicitar um saque. - **Consultar Saque**: Use o endpoint [Consultar Saque](/docs/guide/withdrawals/read-withdrawal) para acompanhar o status do saque após sua criação. - **Listar Saques**: Use o endpoint [Listar Saques](/docs/guide/withdrawals/list-withdrawals) para obter o histórico completo de saques. --- ## Finanças - Listar Saques Fonte: https://selectwin.io/docs/guide/withdrawals/list-withdrawals #### Visão Geral Este endpoint permite recuperar um histórico detalhado de todos os saques solicitados na conta, com opções de filtragem por status, período e paginação. Este recurso é essencial para acompanhamento de transferências bancárias, controle de fluxo de caixa e reconciliação financeira com extratos bancários. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Paginação**: Utilize os parâmetros de paginação (`limit` e `offset`) para controlar o volume de dados retornados. - **Filtros**: Para consultas com muitos resultados, use filtros específicos para reduzir o conjunto de dados. - **Valores Monetários**: Todos os valores monetários são retornados em centavos (por exemplo, R$ 100,00 é representado como 10000). - **Histórico**: Os dados de saques são mantidos por tempo indeterminado para fins de auditoria. #### Descrição O endpoint **Listar Saques** permite **consultar o histórico completo de saques** solicitados na conta, incluindo saques pendentes, processados, concluídos ou cancelados. Os resultados podem ser filtrados por diversos parâmetros como status, data ou valor. #### Requisição ``` GET /v1/withdrawals ``` ##### Parâmetros de Consulta (Query) | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `limit` | integer | Não | Número máximo de registros a serem retornados (1–100, padrão `20`) | `20` | | `offset` | integer | Não | Posição inicial para retornar registros (paginação, padrão `0`) | `0` | | `sort` | string | Não | Ordenação dos resultados (`ascending` / `descending`) | `descending` | | `id` | string | Não | Filtrar por ID específico do saque (`cash_*`) | `cash_01hqzvabc` | | `status` | string | Não | Filtrar por status (texto livre): `pending`, `processing`, `analysis`, `approved`, `confirmed`, `refused`, `canceled`, `failed` | `pending` | | `method` | string | Não | Filtrar por método (texto livre): `bankTransfer` ou `pixTransfer` | `bankTransfer` | | `daterange` | string | Não | Filtra por data de criação (`YYYY-MM-DD` ou datetime ISO 8601) | `2026-04-12` | | `daterangegt` / `daterangegte` / `daterangelt` / `daterangelte` | string | Não | Filtros de intervalo por data de criação (datetime ISO 8601) | `2026-04-01T00:00:00Z` | > `status` e `method` são filtros de texto livre: um valor desconhecido simplesmente retorna nenhum registro (não gera erro 400). #### Resposta - 200 OK Se a requisição for bem-sucedida, o servidor retornará um código de status HTTP 200 OK e uma lista paginada de saques. ##### Exemplo de Resposta ```json { "offset": 0, "limit": 20, "total": 2, "hasMore": false, "page": { "current": 1, "total": 1, "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 } }, "data": [ { "id": "cash_01hqzvabc", "walletId": "wall_01hqzvabc", "amount": 45000, "fee": 15, "status": "pending", "method": "bankTransfer", "currency": "BRL", "transferCode": null, "paidAt": null, "approvedAt": null, "createdAt": "2025-02-07T00:12:33.000Z", "updatedAt": "2025-02-07T00:12:33.000Z" }, { "id": "cash_01hqzvdef", "walletId": "wall_01hqzvabc", "amount": 25000, "fee": 15, "status": "paid", "method": "bankTransfer", "currency": "BRL", "transferCode": "TRF123456", "paidAt": "2025-02-05T14:20:30.000Z", "approvedAt": "2025-02-05T10:15:22.000Z", "createdAt": "2025-02-05T09:15:00.000Z", "updatedAt": "2025-02-05T14:20:30.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/withdrawals", "method": "GET", "description": "List all withdrawals." }, "create": { "href": "https://api.selectwin.io/v1/withdrawals", "method": "POST", "description": "Create a new withdrawal." } } } ``` ##### Atributos da Resposta Paginada | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` | integer | Posição inicial dos resultados retornados | | `limit` | integer | Quantidade máxima de registros retornados | | `total` | integer | Total de registros disponíveis para a consulta | | `hasMore` | boolean | Indica se existem mais resultados além dos retornados | | `page.current` / `total` / `offset.*` | | Estrutura de página padrão (offsets são inteiros) | | `data` | array | Saques (cada item carrega `walletId`, mas **sem** `receivingBankAccount` nem `_links` por item) | | `merchant` | object | Merchant | | `_links` | object | Links HATEOAS (no nível raiz) | #### Casos de Uso 1. **Reconciliação Bancária**: Comparar saques realizados com entradas em extratos bancários 2. **Histórico Financeiro**: Manter registro completo de todas as movimentações financeiras 3. **Monitoramento de Saques**: Acompanhar o status de saques pendentes ou agendados 4. **Relatórios de Auditoria**: Gerar relatórios de saques para fins de auditoria financeira #### Melhores Práticas 1. **Implementar Paginação Eficiente**: Utilize os parâmetros `limit` e `offset` para controlar o volume de dados 2. **Utilizar Filtros**: Aplique filtros específicos para reduzir o conjunto de resultados 3. **Verificação Regular**: Consulte periodicamente os saques pendentes para identificar falhas ou atrasos 4. **Exportação de Dados**: Considere exportar os dados de saques para backup ou análise externa #### Integração com Outros Endpoints - Use [Consultar Saque](/docs/guide/withdrawals/read-withdrawal) para obter detalhes completos de um saque específico - Use [Criar Saque](/docs/guide/withdrawals/create-withdrawal) para solicitar um novo saque - Use [Consultar Saldo](/docs/guide/finance/balance) para verificar o saldo disponível para saques --- ## Finanças - Consultar Saque Fonte: https://selectwin.io/docs/guide/withdrawals/read-withdrawal #### Visão Geral Este endpoint permite recuperar informações detalhadas sobre um saque específico, incluindo seu status atual, valor, método de transferência e datas de processamento. Isso é útil para acompanhar o status de saques em processamento, verificar detalhes de saques anteriores ou auditar movimentações financeiras. #### Precauções > **ATENÇÃO!** Por favor, leia estas informações importantes antes de usar este endpoint. - **Identificador do Saque**: Certifique-se de utilizar o identificador correto do saque para obter informações precisas. - **Valores Monetários**: Todos os valores monetários são retornados em centavos. - **Histórico**: Os registros de saques são mantidos por tempo indeterminado para fins de auditoria, mesmo após concluídos ou cancelados. #### Descrição O endpoint de Consulta de Saque permite recuperar informações detalhadas sobre um saque através do seu identificador único. As informações retornadas incluem o valor do saque, status atual, método de transferência, taxas aplicadas (se houver) e datas relevantes, como criação, aprovação e pagamento. #### Requisição ``` GET /v1/withdrawals/{withdrawalId} ``` ##### Parâmetros do Caminho | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `withdrawalId` | string | Sim | Identificador único do saque a ser consultado (`cash_*`) | `cash_01hqzvabc` | #### Resposta - 200 OK Em caso de sucesso, o servidor responde com o código de status HTTP 200 e um objeto JSON contendo todas as informações do saque. ```json { "id": "cash_01hqzvabc", "walletId": "wall_01hqzvabc", "amount": 45000, "fee": 15, "status": "paid", "method": "bankTransfer", "currency": "BRL", "transferCode": "TRF987654", "paidAt": "2025-02-08T11:20:00.000Z", "approvedAt": "2025-02-07T18:00:00.000Z", "createdAt": "2025-02-07T00:12:33.000Z", "updatedAt": "2025-02-08T11:20:00.000Z", "receivingBankAccount": { "holderName": "João Santos", "document": "12345678901", "bankCode": "260", "bankName": null, "routingNumber": "0001", "accountNumber": "11111111-5", "type": "checking", "pixKey": null }, "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/withdrawals/cash_01hqzvabc", "method": "GET", "description": "Read a withdrawal." }, "create": { "href": "https://api.selectwin.io/v1/withdrawals", "method": "POST", "description": "Create a new withdrawal." }, "list": { "href": "https://api.selectwin.io/v1/withdrawals", "method": "GET", "description": "List all withdrawals." } } } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `cash_*` | | `walletId` | string \| null | Carteira (`wall_*`) | | `amount` | number | Valor em centavos | | `fee` | number | Taxa em centavos | | `status` | string | pending/processing/confirmed/approved/analysis/refused/canceled/failed | | `method` | string \| null | `bankTransfer` ou `pixTransfer` | | `currency` | string | `BRL` | | `transferCode` | string \| null | Ref da transferência | | `paidAt` / `approvedAt` | string \| null | Timestamps | | `createdAt` / `updatedAt` | string | Timestamps | | `receivingBankAccount` | object \| null | Snapshot dos dados bancários no momento do saque | | `merchant` | object | Merchant | | `_links` | object | HATEOAS | #### Respostas de Erro ##### 404 Not Found Ocorre quando o saque informado não existe. ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "client", "code": "withdrawalNotFound", "message": "Withdrawal not found." } } ``` #### Casos de Uso 1. **Acompanhamento de Status**: Verificar se um saque pendente foi processado ou pago. 2. **Histórico Financeiro**: Acessar detalhes de saques anteriores para fins de registro. 3. **Auditorias Financeiras**: Obter informações detalhadas sobre saques específicos para reconciliação financeira. 4. **Suporte ao Cliente**: Verificar o status e detalhes de um saque quando um cliente questiona sobre o mesmo. #### Melhores Práticas 1. **Verificação Regular**: Para saques pendentes, verifique regularmente o status usando este endpoint. 2. **Tratamento de Erros**: Implemente tratamento adequado para todos os possíveis códigos de erro. 3. **Webhook de Notificações**: Em vez de consultar frequentemente, considere usar webhooks para receber notificações automáticas de mudanças no status do saque. 4. **Armazenamento em Cache**: Evite consultas repetidas para saques que já estão em estados finais (paid, failed, canceled). #### Integração com Outros Endpoints - **Criar Saque**: Use o endpoint [Criar Saque](/docs/guide/withdrawals/create-withdrawal) para iniciar um novo saque. - **Listar Saques**: Use o endpoint [Listar Saques](/docs/guide/withdrawals/list-withdrawals) para obter uma lista de todos os saques. - **Consultar Saldo**: Use o endpoint [Consultar Saldo](/docs/guide/finance/balance) para verificar o saldo disponível antes de solicitar um saque. --- ## Carteiras (Wallets) - Visão Geral Fonte: https://selectwin.io/docs/guide/wallets/overview #### Introdução O recurso de Carteiras é um componente fundamental da Selectwin API, permitindo o gerenciamento de contas bancárias para recebimento de valores de transações processadas pela plataforma. Este recurso possibilita configurar múltiplas contas de destino para liquidação, com suporte para diferentes bancos e tipos de conta, facilitando a gestão financeira de sua empresa. A API de Carteiras foi projetada com foco na segurança, integração bancária e flexibilidade, permitindo que os desenvolvedores implementem soluções robustas para gestão financeira e recebimento de valores. #### Conceito e Funcionalidade As carteiras representam contas bancárias dentro da plataforma, armazenando de forma segura as informações necessárias para processar transferências e liquidações financeiras. Este recurso permite que os vendedores configurem destinos para recebimento dos valores provenientes de vendas e outras operações financeiras. Cada carteira está associada a um vendedor específico e pode ser utilizada em diversas operações financeiras, como recebimento de liquidações, saques e transferências. Uma carteira pode ser marcada como principal, sendo utilizada como destino padrão para as liquidações automáticas. #### Estrutura do Objeto Carteira O objeto Carteira contém informações detalhadas sobre a conta bancária, titular e configurações. A estrutura completa (create/read) inclui: ```json { "id": "wall_01hqzvabc", "name": "Minha Carteira Principal", "holderName": "João Santos", "bankName": null, "bankCode": "260", "routingNumber": "0001", "accountNumber": "11111111-5", "accountType": "checking", "pixKey": null, "primary": true, "enabled": true, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/wallets/wall_01hqzvabc", "method": "GET", "description": "Read a wallet." } } } ``` ##### Campos do Objeto | Campo | Tipo | Descrição | |-------|------|-----------| | `id` | string | Identificador (`wall_*`) | | `name` | string | Nome descritivo | | `holderName` | string | Titular (preenchido a partir dos dados da empresa) | | `bankName` | string \| null | Nome do banco (atualmente sempre `null` — lookup COMPE é evolução futura) | | `bankCode` | string \| null | Código do banco | | `routingNumber` | string \| null | Agência | | `accountNumber` | string \| null | Conta | | `accountType` | string | Tipo de conta — sempre `checking` | | `pixKey` | string \| null | Chave PIX (atualmente sempre `null` — carteiras PIX são evolução futura) | | `primary` | boolean | Carteira principal (destino padrão de liquidação) | | `enabled` | boolean | Carteira habilitada | | `createdAt` / `updatedAt` | string | Timestamps ISO 8601 | | `merchant` | object | Bloco merchant (`name`, `merchantId`, `isSubAccount`) — apenas em respostas de recurso | | `_links` | object | HATEOAS — apenas em respostas de recurso (omitido nos itens de `list-all`) | **Notas de fidelidade (respostas da API):** - Respostas de recurso (create/read/set-primary) incluem `merchant` + `_links` no root. - List (`/v1/wallets`): envelope paginado `{ offset, limit, total, page, hasMore, data, merchant, _links }`. - List-all (`/v1/wallets/listall`): array simples de carteiras (cada item carrega o objeto completo, mas **sem** `_links`). - Delete: `{ id, resource:"wallet", deleted:true, merchant, _links }`. > **Sem campo de status:** a carteira não possui um campo `status` (não há fluxo `pending → enabled → rejected`). O estado da carteira é descrito pelos booleanos `primary` e `enabled`. Não existem endpoints de atualização de dados bancários nem de habilitar/desabilitar pela API pública; a única mutação além de criar/excluir é definir a carteira como principal (`set-primary`). #### Recursos Disponíveis | Recurso | Método | Endpoint | Escopo | Descrição | |---------|--------|----------|--------|-----------| | [Criar Carteira](/docs/guide/wallets/create-wallet) | POST | `/v1/wallets` | `wallets:create` | Cria uma nova carteira (retorna o objeto completo com merchant/_links) | | [Consultar Carteira](/docs/guide/wallets/read-wallet) | GET | `/v1/wallets/{walletId}` | `wallets:read` | Obtém detalhes completos | | [Listar Carteiras](/docs/guide/wallets/list-wallets) | GET | `/v1/wallets` | `wallets:list` | Lista paginada (com merchant/_links) | | List All | GET | `/v1/wallets/listall` | `wallets:list` | Lista sem paginação (array simples) | | Definir como Principal | PATCH | `/v1/wallets/{walletId}/set-primary` | `wallets:update` | Marca a carteira como destino padrão de liquidação | | [Excluir Carteira](/docs/guide/wallets/delete-wallet) | DELETE | `/v1/wallets/{walletId}` | `wallets:delete` | Arquiva a carteira (confirmação com merchant/_links) | #### Webhooks Acionados | Evento | Descrição | |--------|-----------| | `wallet.created` | Acionado quando uma nova carteira é criada no sistema. | | `wallet.deleted` | Acionado quando uma carteira é removida (arquivada) do sistema. | > Apenas `wallet.created` e `wallet.deleted` são emitidos. Não há eventos `wallet.pending`/`enabled`/`disabled`/`rejected`. #### Casos de Uso Comuns - **Marketplace**: Configuração de múltiplas carteiras para diferentes vendedores ou lojas - **E-commerce**: Configuração de carteira principal para recebimento de valores de vendas - **Gestão Financeira**: Direcionamento de recebíveis para diferentes contas baseado em regras de negócio - **Contabilidade**: Separação de recebimentos por categoria ou área de negócio - **Empresas com Múltiplas Filiais**: Configuração de carteiras específicas para cada unidade #### Melhores Práticas 1. **Sempre valide os dados bancários** antes de criar uma carteira para evitar problemas de liquidação 2. **Utilize o campo `primary`** para designar uma carteira como principal quando apropriado 3. **Adote nomes descritivos** para facilitar a identificação das carteiras 4. **Verifique se a carteira existe** antes de tentar operações como exclusão 5. **Utilize filtros na listagem** para localizar carteiras específicas mais facilmente 6. **Considere o contexto de uso** ao decidir entre criar múltiplas carteiras ou centralizar em uma única 7. **Mantenha as informações atualizadas** para evitar falhas nas transferências bancárias #### Solução de Problemas Comuns | Problema | Possível Causa | Solução Recomendada | |----------|---------------|---------------------| | `409 Conflict` ao criar carteira | Já existe uma carteira com os mesmos dados bancários (dedupe) | Reutilize a carteira existente em vez de recriá-la | | `400 bankCodeNotSupported` | Código do banco não suportado | Verifique se o código do banco é válido e suportado | | Valores sendo direcionados para carteira errada | Múltiplas carteiras com flag primary | Use `PATCH /v1/wallets/{walletId}/set-primary` para definir a carteira principal correta | | `404 walletNotFound` | ID de carteira inválido ou carteira arquivada | Verifique o `wall_*` informado | | `409 walletHasPendingWithdrawal` ao excluir | Há um saque em andamento para esta carteira | Aguarde a conclusão do saque antes de excluir a carteira | #### Integração com Outros Recursos O recurso de Carteiras integra-se com: - **Finance**: Carteiras são destinos para saques e liquidações financeiras - **Transactions**: Valores processados em transações são direcionados para carteiras - **Webhooks**: Notificações em tempo real sobre mudanças de status nas carteiras - **Receivables**: Recebíveis são liquidados nas carteiras configuradas --- ## Criar Carteira Fonte: https://selectwin.io/docs/guide/wallets/create-wallet #### Visão Geral Este endpoint permite criar uma nova carteira (conta bancária) para um vendedor. A carteira criada poderá ser utilizada para receber valores de transações e saques. #### Precauções - Certifique-se de fornecer todos os campos obrigatórios (`name`, `bankCode`, `routingNumber`, `accountNumber`). - O campo `primary` determina se esta carteira será definida como a principal. Se definido como `true`, qualquer outra carteira principal será automaticamente definida como secundária. A **primeira** carteira criada vira principal automaticamente. - Os dados bancários fornecidos devem ser válidos e corresponder a uma conta bancária existente. - A criação é **idempotente** (use a header `X-Idempotency-Key`) e faz dedupe: dados bancários idênticos a uma carteira já existente retornam `409 Conflict`. - O `holderName` é preenchido automaticamente a partir dos dados cadastrais da empresa autenticada — não é informado no corpo. - **Carência para saque**: Uma carteira recém-criada só pode **receber saque** após o período de carência de segurança — ver [Criar Saque](/docs/guide/withdrawals/create-withdrawal). Os administradores da conta são notificados sempre que um destino de saque é adicionado ou alterado. #### Descrição A criação de carteiras permite configurar múltiplas contas bancárias para recebimento de valores, possibilitando maior flexibilidade na gestão financeira. Cada vendedor pode ter várias carteiras, mas apenas uma pode ser definida como principal. #### Requisição ``` POST /v1/wallets ``` ##### Parâmetros do Corpo | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `name` | string | Sim | Nome descritivo para a carteira (4–50 caracteres) | `"Minha Carteira Principal"` | | `bankCode` | string | Sim | Código do banco (1–10 caracteres) | `"341"` | | `routingNumber` | string | Sim | Número da agência (1–60 caracteres) | `"1234"` | | `accountNumber` | string | Sim | Número da conta (1–60 caracteres) | `"12345678901234"` | | `primary` | boolean | Não | Indica se é a carteira padrão do vendedor | `true` | ##### Exemplo de Requisição ```json { "name": "Minha Carteira Principal", "bankCode": "341", "routingNumber": "1234", "accountNumber": "12345678901234", "primary": true } ``` #### Resposta ##### Sucesso (201 Created) ```json { "id": "wall_01hqzvabc", "name": "Minha Carteira Principal", "holderName": "João Santos", "bankName": null, "bankCode": "341", "routingNumber": "1234", "accountNumber": "12345678901234", "accountType": "checking", "pixKey": null, "primary": true, "enabled": true, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/wallets/wall_01hqzvabc", "method": "GET", "description": "Read a wallet." }, "delete": { "href": "https://api.selectwin.io/v1/wallets/wall_01hqzvabc", "method": "DELETE", "description": "Delete the wallet." }, "list": { "href": "https://api.selectwin.io/v1/wallets", "method": "GET", "description": "List all wallets." } } } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `wall_*` | | `name` | string | Nome descritivo | | `holderName` | string | Titular (derivado da empresa) | | `bankName` | string \| null | Nome do banco (atualmente sempre `null`) | | `bankCode` | string \| null | Código do banco | | `routingNumber` | string \| null | Agência | | `accountNumber` | string \| null | Conta | | `accountType` | string | Tipo de conta — sempre `checking` | | `pixKey` | string \| null | Chave PIX (atualmente sempre `null`) | | `primary` | boolean | Principal | | `enabled` | boolean | Habilitada | | `createdAt` / `updatedAt` | string | Timestamps | | `merchant` | object | Merchant | | `_links` | object | HATEOAS | #### Erros Comuns | Código | `code` | Descrição | |--------|--------|-----------| | 400 | `bankCodeNotSupported` | O código do banco informado não é suportado | | 409 | `walletAlreadyExists` | Já existe uma carteira com os mesmos dados bancários | #### Melhores Práticas 1. **Forneça nomes descritivos** para facilitar a identificação das carteiras. 2. **Valide os dados bancários** antes de enviá-los para evitar problemas de liquidação. 3. **Considere a necessidade de múltiplas carteiras**, utilizando nomes claros para diferenciá-las. 4. **Designe uma carteira como principal** (`primary: true` na criação ou `PATCH /set-primary`) quando apropriado. 5. **Armazene o ID da carteira** para uso em operações subsequentes, como consultas e exclusões. --- ## Listar Carteiras Fonte: https://selectwin.io/docs/guide/wallets/list-wallets #### Visão Geral Este endpoint permite listar todas as carteiras associadas a um vendedor específico ou filtrar por determinados critérios. #### Precauções - Utilize os parâmetros de paginação para gerenciar grandes volumes de dados. - Certifique-se de utilizar o ID correto do vendedor para obter as carteiras associadas. - Os resultados são ordenados por data de criação, do mais recente para o mais antigo. #### Descrição A listagem de carteiras permite visualizar todas as contas bancárias configuradas para um vendedor. Este endpoint é útil para verificar quais carteiras estão disponíveis antes de realizar operações como exclusão ou atualização. #### Requisição ``` GET /v1/wallets ``` ##### Parâmetros de Consulta | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `limit` | integer | Não | Número de registros por página (1–100, padrão `20`) | `25` | | `offset` | integer | Não | Posição inicial para paginação (padrão `0`) | `0` | | `sort` | string | Não | Critério de ordenação dos resultados (`ascending` / `descending`) | `descending` | | `id` | string | Não | Filtra por um ID de carteira específico (`wall_*`) | `"wall_01hqzvabc"` | | `daterange` | string | Não | Filtra por data de criação (`YYYY-MM-DD` ou datetime ISO 8601) | `2026-04-12` | | `daterangegt` / `daterangegte` / `daterangelt` / `daterangelte` | string | Não | Filtros de intervalo por data de criação (datetime ISO 8601) | `2026-04-01T00:00:00Z` | > Não há filtro `primary`. Para a carteira principal, identifique o item com `primary: true` no resultado. ##### Exemplo de Requisição ``` GET /v1/wallets?limit=25&offset=0 ``` #### Resposta ##### Sucesso (200 OK) Resposta paginada no nível raiz (com `merchant` e `_links`). ```json { "offset": 0, "limit": 25, "total": 1, "hasMore": false, "page": { "current": 1, "total": 1, "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 } }, "data": [ { "id": "wall_01hqzvabc", "name": "Minha Carteira Principal", "holderName": "João Santos", "bankName": null, "bankCode": "260", "routingNumber": "0001", "accountNumber": "11111111-5", "accountType": "checking", "pixKey": null, "primary": true, "enabled": true, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/wallets", "method": "GET", "description": "List all wallets." }, "create": { "href": "https://api.selectwin.io/v1/wallets", "method": "POST", "description": "Create a new wallet." } } } ``` ##### Atributos da Resposta Paginada | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` | integer | Posição inicial dos resultados retornados | | `limit` | integer | Quantidade máxima de registros retornados | | `total` | integer | Total de registros disponíveis para a consulta | | `hasMore` | boolean | Indica se existem mais resultados além dos retornados | | `page.current` | integer | Número da página atual | | `page.total` | integer | Total de páginas disponíveis | | `page.offset.first` / `.prev` / `.next` / `.last` | integer | Offsets de navegação de páginas | | `data` | array | Carteiras (objeto completo por item, sem `_links` no item) | | `merchant` | object | Merchant da conta | | `_links` | object | Links HATEOAS (no nível raiz) | #### Melhores Práticas 1. **Utilize paginação** para lidar com grandes volumes de dados, especialmente para vendedores com muitas carteiras. 2. **Identifique a carteira principal** pelo item com `primary: true` no resultado (não há filtro de consulta para isso). 3. **Verifique a resposta de paginação** para garantir que você tenha acessado todos os registros disponíveis. 4. **Utilize cache quando apropriado** para melhorar o desempenho ao fazer consultas frequentes. 5. **Limite o número de requisições** para respeitar os limites de taxa da API. 6. **Armazene os IDs das carteiras** para referência futura quando precisar interagir com elas individualmente. --- ## Consultar Carteira Fonte: https://selectwin.io/docs/guide/wallets/read-wallet #### Visão Geral Este endpoint permite recuperar detalhes completos de uma carteira específica através do seu ID único. #### Precauções - Certifique-se de utilizar um ID de carteira válido no formato `wall_XXXXXXXXX`. - Verifique se você tem permissão para acessar a carteira requisitada. - A carteira deve pertencer ao vendedor autenticado ou a um vendedor para o qual você tenha acesso. #### Descrição A consulta de carteira é útil para obter todos os detalhes de uma carteira específica, incluindo informações bancárias, status, e dados de associação. #### Requisição ``` GET /v1/wallets/{walletId} ``` ##### Parâmetros de Caminho | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `walletId` | string | Sim | Identificador único da carteira | `"wall_01hqzvabc"` | #### Resposta ##### Sucesso (200 OK) ```json { "id": "wall_01hqzvabc", "name": "Minha Carteira Principal", "holderName": "João Santos", "bankName": null, "bankCode": "260", "routingNumber": "0001", "accountNumber": "11111111-5", "accountType": "checking", "pixKey": null, "primary": true, "enabled": true, "updatedAt": "2026-04-12T17:56:33.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/wallets/wall_01hqzvabc", "method": "GET", "description": "Read a wallet." }, "create": { "href": "https://api.selectwin.io/v1/wallets", "method": "POST", "description": "Create a new wallet." }, "delete": { "href": "https://api.selectwin.io/v1/wallets/wall_01hqzvabc", "method": "DELETE", "description": "Delete the wallet." }, "list": { "href": "https://api.selectwin.io/v1/wallets", "method": "GET", "description": "List all wallets." } } } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `wall_*` | | `name` | string | Nome descritivo | | `holderName` | string | Titular (derivado da empresa) | | `bankName` | string \| null | Nome do banco (atualmente sempre `null`) | | `bankCode` | string \| null | Código do banco | | `routingNumber` | string \| null | Agência | | `accountNumber` | string \| null | Conta | | `accountType` | string | Tipo de conta — sempre `checking` | | `pixKey` | string \| null | Chave PIX (atualmente sempre `null`) | | `primary` | boolean | Principal | | `enabled` | boolean | Ativa | | `createdAt` / `updatedAt` | string | Timestamps | | `merchant` | object | Merchant | | `_links` | object | HATEOAS | #### Melhores Práticas 1. **Armazene o ID da carteira** quando ela é criada para facilitar consultas futuras. 2. **Implemente cache** para resultados frequentemente acessados, considerando um TTL (Time To Live) apropriado. 3. **Verifique se a resposta retorna o atributo `primary`** para determinar se esta é a carteira principal do vendedor. 4. **Valide o formato do ID da carteira** antes de fazer a requisição para evitar erros de validação. 5. **Implemente tratamento de erros** adequado para lidar com casos onde a carteira não é encontrada. 1. **Armazene os IDs das carteiras** em seu sistema para referência futura. 2. **Utilize este endpoint** para verificar o status atual da carteira antes de realizar operações com ela. 3. **Implemente cache** para informações de carteiras frequentemente acessadas. 4. **Trate adequadamente os erros** para fornecer feedback claro aos usuários. 5. **Não armazene informações sensíveis da carteira** em logs ou bancos de dados desprotegidos. 6. **Verifique se a carteira é a principal** (`primary: true`) antes de realizar operações que dependam da carteira padrão. --- ## Excluir Carteira Fonte: https://selectwin.io/docs/guide/wallets/delete-wallet #### Visão Geral Este endpoint arquiva (remove) uma carteira do sistema. A operação é irreversível: os dados bancários sensíveis são apagados (scrub de PII) e a carteira é marcada como arquivada. #### Precauções - A exclusão de uma carteira é irreversível — os dados bancários são removidos no processo. - Não é possível excluir uma carteira que possui um **saque em andamento** para ela (`409 walletHasPendingWithdrawal`). Aguarde a conclusão do saque. - Saques em andamento mantêm seu próprio snapshot dos dados bancários (`receivingBankAccount`), capturado no momento da solicitação, portanto continuam consultáveis mesmo após a carteira ser arquivada. - Apenas carteiras da empresa autenticada podem ser excluídas. - Se existir uma **regra de saque automático** (`/v1/withdrawals/auto`) para esta carteira, ela é **desligada** na mesma operação (`enabled: false`, `walletArchived: true`) e os administradores da conta são avisados. A regra continua existindo para ser apontada para outra carteira com o `PUT`. #### Descrição A exclusão de carteira é útil quando você deseja remover uma conta bancária que não será mais utilizada para recebimentos. Isso ajuda a manter sua lista de carteiras organizada e atualizada. #### Requisição ``` DELETE /v1/wallets/{walletId} ``` ##### Parâmetros de Caminho | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `walletId` | string | Sim | Identificador único da carteira a ser excluída | `"wall_01hqzvabc"` | ##### Exemplo de Requisição ``` DELETE /v1/wallets/wall_01hqzvabc ``` #### Resposta ##### Sucesso (200 OK) ```json { "id": "wall_01hqzvabc", "resource": "wallet", "deleted": true, "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/wallets/wall_01hqzvabc", "method": "DELETE", "description": "Delete the wallet." }, "create": { "href": "https://api.selectwin.io/v1/wallets", "method": "POST", "description": "Create a new wallet." }, "list": { "href": "https://api.selectwin.io/v1/wallets", "method": "GET", "description": "List all wallets." } } } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `wall_*` excluída | | `resource` | string | "wallet" | | `deleted` | boolean | `true` | | `merchant` | object | Merchant | | `_links` | object | HATEOAS | ##### Erros Comuns | Código | `code` | Descrição | |--------|--------|-----------| | 404 | `walletNotFound` | Carteira inexistente ou já arquivada | | 409 | `walletHasPendingWithdrawal` | Há um saque em andamento para esta carteira | #### Melhores Práticas 1. **Verifique se não há saque em andamento** para a carteira antes de tentar excluí-la (caso contrário recebe `409`). 2. **Confirme a exclusão com o usuário** antes de fazer a requisição, pois a operação é irreversível. 3. **Implemente tratamento de erros** para lidar com casos onde a carteira não pode ser excluída. 4. **Mantenha outra carteira principal** caso exclua a atual `primary`, usando `PATCH /v1/wallets/{walletId}/set-primary`. --- ## Checkouts Fonte: https://selectwin.io/docs/guide/checkouts/overview O recurso de **Checkouts** reúne duas formas de vender pela API: - **[Checkout Sessions](/docs/guide/checkouts/sessions)** — carrinho hospedado para **uma intenção de compra específica** (`/v1/checkouts/sessions`). Você cria a sessão, recebe uma `url` de checkout e a entrega ao comprador. - **[Payment Links](/docs/guide/checkouts/payment-links)** — link de checkout reutilizável e compartilhável, com assinatura (`/v1/checkouts/payment-links`). Recurso persistente que você cria, consulta, lista, atualiza e remove. #### Recursos Disponíveis | Recurso | Operação | Método | Endpoint | |---------|----------|--------|----------| | Sessions | Criar | POST | `/v1/checkouts/sessions` | | Sessions | Listar | GET | `/v1/checkouts/sessions` | | Sessions | Consultar | GET | `/v1/checkouts/sessions/{sessionId}` | | Sessions | Atualizar | PUT | `/v1/checkouts/sessions/{sessionId}` | | Sessions | Cancelar | DELETE | `/v1/checkouts/sessions/{sessionId}` | | Sessions | Pagar (async) | POST | `/v1/checkouts/sessions/{sessionId}/pay` | | Payment Links | Criar | POST | `/v1/checkouts/payment-links` | | Payment Links | Listar | GET | `/v1/checkouts/payment-links` | | Payment Links | Consultar | GET | `/v1/checkouts/payment-links/{paymentLinkId}` | | Payment Links | Atualizar | PUT | `/v1/checkouts/payment-links/{paymentLinkId}` | | Payment Links | Excluir | DELETE | `/v1/checkouts/payment-links/{paymentLinkId}` | #### Autenticação Todos os endpoints de checkout usam o header `SelectKey` (`sk_test_…`/`sk_live_…`) — veja [Autenticação](/docs/reference/autenticacao). #### URL hospedada e assinatura Ao criar um link, a resposta traz `accessFullUrl` — a URL pronta para compartilhar — e uma `signature` **HMAC SHA256** que protege a integridade dos parâmetros do link. A página hospedada revalida a assinatura (e a janela de validade) antes de exibir o checkout. ```mermaid sequenceDiagram autonumber participant L as Loja (Backend) participant API as Selectwin API participant B as Comprador (Browser) L->>API: POST /v1/checkouts/payment-links (name, items, ...) Note over API: persiste o link e assina (HMAC SHA256) API-->>L: 201 { id: link_..., accessFullUrl, signature } L-->>B: compartilha accessFullUrl B->>API: abre a página de checkout (sign na query) Note over API: revalida assinatura + janela (expiresAt/availableAt) alt válido API-->>B: página de pagamento else inválido / expirado API-->>B: 4xx (link inválido) end ``` #### Itens Os `items` referenciam variantes a vender, no formato `{ id, quantity }`: ```json { "items": [ { "id": "var_01hqzvabc", "quantity": 1 }, { "id": "var_1234567890abcdef", "quantity": 2 } ] } ``` #### Janela de validade - `expiresAt` — instante após o qual o link não pode mais ser usado. - `availableAt` — instante a partir do qual o link passa a valer. - Cada item precisa de `quantity` ≥ 1. Detalhes de cada operação (campos, exemplos e respostas) em **[Payment Links](/docs/guide/checkouts/payment-links)** e **[Checkout Sessions](/docs/guide/checkouts/sessions)**. --- ## Links de pagamento Fonte: https://selectwin.io/docs/guide/checkouts/payment-links Um **Payment Link** é uma página de checkout hospedada e compartilhável (URL com assinatura) para vender um ou mais itens. Você cria, consulta, lista, atualiza e remove links como um recurso persistente. | Operação | Método | Endpoint | |----------|--------|----------| | Criar | POST | `/v1/checkouts/payment-links` | | Listar | GET | `/v1/checkouts/payment-links` | | Consultar | GET | `/v1/checkouts/payment-links/{paymentLinkId}` | | Atualizar | PUT | `/v1/checkouts/payment-links/{paymentLinkId}` | | Excluir | DELETE | `/v1/checkouts/payment-links/{paymentLinkId}` | Todas as requisições usam o header `SelectKey` (veja [Autenticação](/docs/reference/autenticacao)). O ID público de um link tem o prefixo `link_`. --- #### Criar `POST /v1/checkouts/payment-links` ```bash curl -X POST "https://api.selectwin.io/v1/checkouts/payment-links" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Black Friday PIX", "items": [ { "id": "var_01hqzvabc", "quantity": 1 } ], "editable": false, "expiresAt": "2026-12-31T23:59:59Z" }' ``` ##### Campos da requisição | Campo | Tipo | Obrigatório | Descrição | |-------|------|:-----------:|-----------| | `name` | string | **Sim** | Nome de exibição (1–255 caracteres) | | `items[]` | array | **Sim** | Itens do link: `{ id, quantity }` (1–100 itens). `id` é a variante (`var_...` ou `prv_...`); `quantity` é inteiro ≥ 1 (default `1`, máx. 100000) | | `discounts[]` | array | Não | Códigos de cupom pré-aplicados (strings, 1–50 caracteres cada, máx. 20). A validação/retenção autoritativa acontece na cobrança | | `customInputs[]` | array | Não | Campos de formulário customizados (objetos livres, máx. 50) | | `expiresAt` | datetime | Não | A partir deste instante o link não pode mais ser usado | | `availableAt` | datetime | Não | Instante a partir do qual o link passa a valer | | `editable` | boolean | Não | Permite o cliente editar o carrinho na página (default `false`). **Com `false` a trava vale no servidor** (desde 15/09/2026): o autosave da sessão recusa trocar item, mudar quantidade ou adicionar linha (`422 checkoutCartNotEditable`); order bump e upsell seguem regra própria. Antes era só de tela, e o cupom do link passava a valer para qualquer carrinho. | | `templateId` | string | Não | Template de checkout (`tmpl_...` ou `ckt_...`). Não é só visual: o template define desconto por método, parcelas, endereço obrigatório e fretes. **O do link vence** um `?template_id=` na URL, e a sessão aberta pelo link é cobrada por ele — o checkout recusa outro template no pagamento. | | `domainId` | string | Não | Em que **domínio** a `accessFullUrl` é montada (veja abaixo) | | `integrationId` | string | Não | A **integração** (`app_...`) a que uma venda por este link pertence (veja abaixo) | | `metadata` | object | Não | Metadados livres | > **Não há mais campo `type`.** O antigo link "static" foi removido; existe apenas um tipo de link > (persistido). Um `type` enviado por clientes antigos é silenciosamente ignorado. ##### `domainId` — o domínio do link `domainId` escolhe o **host** da `accessFullUrl`. Os valores aceitos são os de `GET /v1/domains/listall`: - **um domínio próprio seu**, desde que **pronto** — verificado com TLS ativo (`active` e `ssl`), com `checkout` no `purpose` e sem vínculo vivo de área de membros; - **`dmn_shared`** — o domínio compartilhado da plataforma (`pay.selectwin.io`), disponível para **toda** empresa. É a única forma de colocar um link no host da plataforma quando você já tem domínio próprio pronto. **Omitido**, o comportamento é o de sempre: o seu domínio de checkout pronto mais antigo, e na falta dele o `pay.selectwin.io`. Um `domainId` que não resolva (de outra empresa, não verificado, sem `checkout` no `purpose`, ou de área de membros) devolve **`422 checkoutDomainIdIsInvalid`** e o link **não** é criado. Não existe fallback silencioso aqui de propósito: um link montado em host de outra empresa abre **404** para o comprador, porque o checkout resolve o lojista pelo host e recusa quando host e link discordam do dono. A resposta devolve em `domainId` o domínio em que o link **está** — `dmn_shared` para os que vivem no host da plataforma, inclusive os criados antes deste campo existir. ##### `integrationId` — a que integração a venda pertence `integrationId` amarra o link a uma das **suas** integrações (`GET /v1/integrations/list-all`). Quando um comprador abre o link, a **sessão de checkout** nasce carimbada com: | campo da sessão | valor | |---|---| | `integrationType` | o *slug* do tipo da integração — `woocommerce`, `shopify`, … | | `integrationId` | o `app_...` da integração | É assim que um pedido gerado por um link volta a ser atribuído à loja que o criou, sem que a URL do comprador precise carregar `?integration=`. Se a URL **trouxer** `?integration=`, ela vence — o parâmetro é um override explícito, e como ele nomeia um **tipo** (não uma linha sua), o `integrationId` da sessão fica nulo nesse caso, em vez de atribuir a venda a uma integração que ninguém pediu. Uma integração `deleted` ou `quarantined` não resolve ⇒ **`422 checkoutIntegrationIdIsInvalid`**. Uma integração apenas **desabilitada** resolve normalmente: pausar uma integração não pode quebrar a criação de links. > ⚠️ A assinatura do link (`signature`) é calculada sobre `items=<...>&integration=<...>`. Trocar o > `integrationId` — ou os `items` — **re-assina**, sempre a partir do par efetivo. ##### Resposta `201 Created` ```json { "id": "link_01hqzvabc", "name": "Black Friday PIX", "type": "dynamic", "enabled": true, "accessId": "k3m9x2qp7ab4", "signature": "f62a0cb72d7a5d55052c96301013e64627133231f90c4614b10d0f50b9887f8d", "accessFullUrl": "https://pay.selectwin.io/l/live/k3m9x2qp7ab4", "sessionId": null, "editable": false, "items": [ { "id": "var_01hqzvabc", "quantity": 1 } ], "discounts": [], "customInputs": [], "expiresAt": "2026-12-31T23:59:59.000Z", "availableAt": null, "templateId": null, "integrationId": null, "domainId": null, "metadata": {}, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` | Campo | Descrição | |-------|-----------| | `id` | ID público do link (`link_*`). | | `type` | Palavra de tipo ecoada do banco (compatibilidade). | | `enabled` | Se o link está ativo. | | `accessId` / `signature` / `accessFullUrl` | Identificador de acesso, assinatura HMAC e URL pronta para compartilhar. O `accessId` é uma chave **curta e opaca** de 12 caracteres (`a-z0-9`), única na plataforma, e é o que a `accessFullUrl` carrega em `/l/{environment}/{accessId}`. Links criados antes de setembro/2026 têm ids de 32 caracteres e **continuam válidos** — nada reescreve o `accessId`, então trate-o sempre como string opaca de tamanho variável, nunca com tamanho fixo. | | `sessionId` | Sempre `null` (a vinculação link→sessão é resolvida no fluxo público de abertura). | | `templateId` | `tmpl_*` quando há template; senão `null`. | | `integrationId` / `domainId` | Sempre `null` (binding ainda não exposto). | > `accessFullUrl` é a URL pronta para compartilhar (já assinada). A assinatura HMAC em `signature` > garante a integridade dos parâmetros do link. As respostas de payment link **não** incluem > `merchant` nem `_links`. > ⚠️ **`signature` não é identificador.** Ela é o HMAC do **carrinho** (`items` + `integrationId`), então > dois links com os mesmos itens e a mesma integração têm a **mesma** `signature` — o que os distingue > (nome, template, domínio, cupons, janela de disponibilidade) está fora do payload assinado. Duplicar uma > oferta é normal e permitido. Para identificar o link use o `id` (`link_*`); para abri-lo, o `accessId`. --- #### Consultar `GET /v1/checkouts/payment-links/{paymentLinkId}` Retorna o objeto completo (mesma forma da criação). ```bash curl -X GET "https://api.selectwin.io/v1/checkouts/payment-links/link_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` --- #### Listar `GET /v1/checkouts/payment-links` Lista paginada. Cada item traz o **objeto completo** do link (mesma forma da consulta individual). ##### Filtros | Parâmetro | Tipo | Descrição | |-----------|------|-----------| | `limit` / `offset` / `sort` | — | Paginação (`limit` default 20, máx. 100; `offset` default 0). Veja [Paginação](/docs/reference/paginacao) | | `status` | enum | `enabled` ou `disabled` | | `domainId` | string | Os links que abrem **neste host** (`dmn_…`, ou `dmn_shared` para o host da plataforma): pelo `domainId` gravado no link ou, quando o host foi escolhido no automático, pela URL do link | | `daterange*` | — | Filtros por intervalo de datas (`daterange`, `daterangegt`, `daterangegte`, `daterangelt`, `daterangelte`) | ##### Resposta `200 OK` ```json { "offset": 0, "limit": 20, "total": 3, "page": { "current": 1, "total": 1, "offset": { "first": 0, "prev": null, "next": null, "last": 0 } }, "data": [ { "id": "link_01hqzvabc", "name": "Black Friday PIX", "type": "dynamic", "enabled": true, "accessId": "k3m9x2qp7ab4", "signature": "f62a0cb72d7a5d55052c96301013e64627133231f90c4614b10d0f50b9887f8d", "accessFullUrl": "https://pay.selectwin.io/l/live/k3m9x2qp7ab4", "sessionId": null, "editable": false, "items": [ { "id": "var_01hqzvabc", "quantity": 1 } ], "discounts": [], "customInputs": [], "expiresAt": "2026-12-31T23:59:59.000Z", "availableAt": null, "templateId": null, "integrationId": null, "domainId": null, "metadata": {}, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ], "hasMore": false } ``` > A lista **não** inclui `merchant`/`_links` (envelope plano `{ offset, limit, total, page, data, hasMore }`). --- #### Atualizar `PUT /v1/checkouts/payment-links/{paymentLinkId}` Envie apenas os campos a alterar: `name`, `items`, `discounts`, `customInputs`, `expiresAt`, `availableAt`, `editable`, `templateId`, `domainId`, `integrationId`, `metadata`. O corpo é **estrito** — campos não reconhecidos são rejeitados com `400`. Retorna o objeto completo (`200 OK`), mesma forma da criação. > **Trocar o `domainId` reescreve a `accessFullUrl` no host novo, mantendo o `accessId`.** Os links já > distribuídos continuam abrindo (o host antigo ainda é seu, e o checkout continua concordando sobre o dono); > você passa a divulgar o novo. Omitir `domainId` **não** re-escolhe o host — o link fica onde está. ```bash curl -X PUT "https://api.selectwin.io/v1/checkouts/payment-links/link_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Campanha atualizada", "expiresAt": "2027-01-31T23:59:59Z" }' ``` --- #### Testar em sandbox `POST /v1/checkouts/payment-links/{paymentLinkId}/test` Cria o **link de teste** de um link de **produção**, sem trocar de ambiente: a chamada é feita com a chave (ou a sessão) `live`, e o link criado é de `sandbox`. O ambiente fica dentro da URL (`…/l/sandbox/…`), então o link de teste nunca abre em produção e não cobra de verdade. Sem corpo; aceita `X-Idempotency-Key`. - **Os itens são cópias.** O catálogo de cada ambiente é separado, então cada variante do link ganha uma cópia no catálogo de `sandbox`, com o mesmo nome, preço, recorrência e imagens. O `metadata` da cópia guarda de onde ela veio (`selectwinLiveVariantId`, `selectwinLiveProductId`). - **Um link de teste por link de produção.** Chamar de novo refaz o mesmo link de teste, **com a mesma URL**, e atualiza as cópias a partir da produção (o preço novo, por exemplo). O `metadata` do link de teste guarda o link de produção (`selectwinLiveLinkId`). A primeira chamada responde `201`; as seguintes, `200`. - **Abre na hora.** O link de teste não herda `availableAt` nem `expiresAt`, para dar para testar antes de divulgar. - **O que não vai junto vem em `warnings`.** O link de teste abre no checkout padrão do `sandbox` (o checkout próprio do link é de produção) e não é de nenhuma integração. Item desativado na produção fica de fora, e a cópia dele é desativada. Cupom sem cupom de mesmo código no `sandbox` não dá desconto no teste. - **Um teste por vez, por conta.** Enquanto um link de teste da sua conta está sendo preparado, outra chamada a `/test` (de qualquer link) responde `409 checkoutPaymentLinkTestBusy` na hora, sem gravar nada. Espere alguns segundos e chame de novo com **outra** `X-Idempotency-Key` (a chave da tentativa recusada continua devolvendo o 409). - **Até 100 itens.** O teste copia no máximo 100 itens, o mesmo limite do link. Um link antigo com mais itens que isso responde `422 checkoutPaymentLinkTestTooManyItems`. ```bash curl -X POST "https://api.selectwin.io/v1/checkouts/payment-links/link_01hqzvabc/test" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "X-Idempotency-Key: 4f1c2a9e-teste-link" ``` ```json { "resource": "checkout-paymentLinkTest", "liveLinkId": "link_01hqzvabc", "created": true, "link": { "id": "link_01hqzvtst", "name": "Campanha de lançamento", "accessFullUrl": "https://pay.selectwin.io/l/sandbox/k3v9q2m7x1ab", "items": [{ "id": "var_01hqzvcpy", "quantity": 1 }], "...": "o mesmo objeto da criação" }, "items": [ { "liveVariantId": "var_01hqzvabc", "variantId": "var_01hqzvcpy", "productId": "prd_01hqzvcpy", "quantity": 1, "copiedNow": true } ], "warnings": [ { "code": "couponsNotInSandbox", "values": ["LANCAMENTO10"] }, { "code": "templateNotCarried" } ] } ``` | `warnings[].code` | Significado | |---|---| | `variantsNotOnSale` | Variantes de produção desativadas, ou de produto desativado, ficaram fora do teste (`values`: os ids de produção) | | `couponsNotInSandbox` | Estes códigos não existem no `sandbox`, e o teste abre sem o desconto deles (`values`: os códigos) | | `templateNotCarried` | O link tem checkout próprio, que é de produção. O teste abre no checkout padrão do `sandbox` | | `integrationNotCarried` | O link é de uma integração, e a venda de teste não é atribuída a ela | --- #### Excluir `DELETE /v1/checkouts/payment-links/{paymentLinkId}` ```json { "id": "link_01hqzvabc", "resource": "checkout-paymentLink", "deleted": true } ``` --- #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `invalidParameters` | 400 | Validação de campos (veja `error.params`) | | `checkoutPaymentLinkNotFound` | 404 | Link não encontrado | | `variantIdIsInvalid` | 422 | Algum `items[].id` referencia uma variante inexistente, **desativada ou de outro ambiente** (um link `live` não aceita variante de `sandbox`) | | `checkoutTemplateIdIsInvalid` | 422 | `templateId` não existe | | `checkoutDomainIdIsInvalid` | 422 | `domainId` não é um domínio que esta empresa pode usar no checkout | | `checkoutIntegrationIdIsInvalid` | 422 | `integrationId` não é uma integração desta empresa (ou está `deleted`/`quarantined`) | | `checkoutPaymentLinkTestFromSandbox` | 409 | `/test` chamado com chave ou sessão de `sandbox`. Lá, todo link já é de teste | | `checkoutPaymentLinkTestNothingToSell` | 422 | `/test`: nenhum item do link está à venda na produção | | `checkoutPaymentLinkTestTooManyItems` | 422 | `/test`: o link tem mais de 100 itens (`error.params[0].items` diz quantos) | | `checkoutPaymentLinkTestBusy` | 409 | `/test`: outro link de teste da conta está sendo preparado agora. Nada foi gravado: tente de novo em instantes, com outra `X-Idempotency-Key` | | `checkoutPaymentLinkCreateFailed` | 500 | Falha ao criar o link | Catálogo geral: [Códigos de Erro](/docs/reference/codigos-de-erro). --- ## Sessões de checkout Fonte: https://selectwin.io/docs/guide/checkouts/sessions #### Introdução Uma **Checkout Session** (`checkout.session`) é um carrinho hospedado: você cria a sessão pela API, recebe uma **URL de checkout** pronta e a entrega ao comprador (redirect, e-mail, SMS ou QR code). O comprador preenche os dados e paga na página hospedada da Selectwin; você acompanha o resultado por **webhook** (`checkout.session.completed` + a transação correspondente). Diferente do [Payment Link](/docs/guide/checkouts/payment-links) (reutilizável, sem comprador fixo), a session representa **uma intenção de compra específica** — com itens já precificados pelo backend, cliente/endereço opcionais e um `externalTransactionId` para reconciliação com o seu ERP. #### Modelo de precificação (autoritativo no backend) **Quem decide o preço é a Selectwin, não o navegador do comprador.** Os itens enviados são validados contra o seu catálogo no momento do *submit* (`/pay`) — não na criação da sessão. Cada `lineItems[]` é resolvido nesse momento: - **Catálogo** — informe `id` de uma variante (`var_...`). O preço vem do seu catálogo. Envie só `id` + `quantity`; opcionalmente sobrescreva com `unitPrice`. - **Ad-hoc** — sem variante de catálogo: informe `name` **e** `unitPrice` (centavos). Útil para serviços avulsos. O carrinho é **payment-type-agnostic**: pode misturar itens `oneTime` e variantes `recurring` (a sessão vira assinatura no momento da cobrança, lendo `billingType`/`cycles`/`trialInterval` da variante — você **não** envia esses campos). **O que é cobrado no pagamento** (desde 2026-10-15): o comprador paga na hora exatamente o total que o checkout mostrou. Num carrinho **só de variantes recorrentes sem trial**, esse total é a **1ª cobrança da assinatura** — itens + frete − cupons − desconto do método, numa **transação** (`tra_…`) ligada à assinatura, com o Pix/boleto na tela e a recusa do cartão na hora; as cobranças seguintes são o valor dos itens (menos os cupons recorrentes), sem frete nem desconto do método. Se essa 1ª cobrança é recusada, ou o Pix/boleto vence sem pagamento, a assinatura é cancelada. Num carrinho **só em trial**, nada é cobrado no pagamento (nem o frete): a 1ª cobrança é no fim do trial, no mesmo horário da compra. > **Valores em centavos.** `unitPrice`/`totals.*` são inteiros (ex.: `49700` = R$ 497,00). #### Estrutura do Objeto `checkout.session` ```json { "object": "checkout.session", "id": "sess_a1b2c3d4e5f6789012ab", "url": "https://checkout.selectwin.io/p/live/sess_a1b2c3d4e5f6789012ab", "status": "active", "ambient": "live", "source": "api", "currency": "BRL", "lineItems": [ { "id": "var_main_product", "name": "Curso Premium", "quantity": 1, "unitPrice": 49700, "currency": "BRL", "isOrderbump": false, "isUpsell": false, "metadata": { "sku": "ABC-123" }, "externalReference": "erp-ref-9" } ], "totals": { "amount": 46523, "discount": 5167, "shipping": 0, "tax": 0 }, "coupons": [{ "code": "DESCONTO10", "type": "percentage", "value": 10 }], "customer": { "email": "maria@example.com", "fullName": "Maria Silva", "document": { "number": "12345678909", "type": "cpf" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "999998888" } }, "address": { "postcode": "01310-100", "street": "Av. Paulista", "number": "1000", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR" }, "paymentMethods": ["pix", "credit", "billet"], "paymentSelection": null, "redirectUrls": { "paid": "https://seller.com/obrigado", "refused": "https://seller.com/recusado", "pending": "https://seller.com/processando" }, "utm": { "source": "newsletter", "campaign": "junho" }, "cookies": { "_fbp": "fb.1.1790000000000.1234567890", "_fbc": "fb.1.1790000000000.IwAR1x...", "_ga": "GA1.1.1234567890.1790000000", "_gcl_au": "1.1.1234567890.1790000000" }, "metadata": { "erpOrderId": "9921" }, "customInputs": null, "integrationType": "shopify", "integrationId": "cmp_shop_123", "externalTransactionId": "pedido-7781", "firstInitialization": true, "online": false, "expiresAt": "2026-06-12T12:00:00.000Z", "createdAt": "2026-06-08T12:00:00.000Z", "updatedAt": "2026-06-08T12:00:00.000Z" } ``` | Campo | Descrição | |-------|-----------| | `id` | Identificador público (`sess_*`). Use em `GET`/`PUT`/`DELETE` e na reconciliação. | | `url` | **A URL hospedada, assinada.** Entregue ao comprador (redirect, e-mail, QR). Formato `{host}/p/{ambient}?session_id={id}&session_token=…` — o `host` é o seu domínio de checkout quando você tem um pronto, senão `pay.selectwin.io`. O token prova que o portador recebeu a URL de quem criou a sessão: é ele que faz o checkout **retomar esta sessão**, com o comprador e o carrinho que você mandou, e vale enquanto a sessão vive (a mesma URL abre de novo se o comprador voltar ao pedido). Só a **primeira** abertura vem com os dados do comprador preenchidos; as seguintes retomam a sessão sem devolver o que foi digitado (quem tem a URL não é necessariamente quem digitou — 2026-10-10). E depois de uma segunda abertura, **nenhuma** tela do checkout devolve os dados digitados — nem ao recarregar a página (2026-10-12): quem abriu primeiro também não lê o que outra pessoa digitar depois. Os dados continuam na sessão e valem para o pagamento; você os lê por `GET /v1/checkouts/sessions/{id}`. `null` só em sessão terminal ou quando a conta não tem segredo de assinatura. | | `status` | Estado da sessão (ver Ciclo de Vida). | | `ambient` | `live` \| `sandbox`. | | `lineItems` | Itens já com preço resolvido pelo backend. | | `totals` | Totais **recalculados** pelo backend (centavos). `amount` já é líquido do desconto. **Os quatro campos são anuláveis**: `null` significa *esta sessão nunca teve um carrinho precificado* — não R$ 0,00. O espelho nasce no `GET /checkout/v1/checkout/init` quando a sessão é criada com carrinho (itens do catálogo + cupons do link, método pré-selecionado se houver, **sem frete**), é reescrito pelo `POST /checkout/v1/totals` sempre que ele recebe um `sessionId`, e congelado pelo `/pay`. Assim o `checkout.session.abandoned` de quem abre o link e sai antes de a página calcular já leva o valor do carrinho. Só uma sessão **sem itens** continua `null`, para a tela poder mostrar `—` em vez de afirmar um zero. | | `coupons` | Cupons aplicados/validados. | | `customer` / `address` | Snapshot do comprador (parcial até ele preencher; objeto JSON, não cria linha ainda). | | `paymentMethods` | Métodos habilitados (do corpo, ou do template global da empresa). | | `redirectUrls` | URLs de pós-pagamento (do corpo, ou do template global). Cada valor é uma URL absoluta `https://` (ou `""`/`null` para não definir); `javascript:`, `data:` e `http://` são recusados com `400`. | | `metadata` / `externalTransactionId` | Ecoam o que você enviou — e **descem para a cobrança**: a transação (ou a assinatura) que a sessão produz nasce com `externalReference = externalTransactionId` e com o seu `metadata` (mais `cookies`). É por eles que o seu sistema fecha o pedido ao receber `transaction.approved`. **O comprador não altera o seu `metadata`** (2026-10-12): a página do checkout só escreve as chaves reservadas à plataforma — `consent` (o registro de consentimento do comprador) e `attribution` —, nunca troca nem apaga uma chave sua. Não use `consent`, `attribution` nem `cookies` para dados seus: são da plataforma e não descem para a cobrança. **Na cobrança vale o limite de [Metadata](/docs/reference/metadata)** (50 chaves, chave até 40 caracteres, valor até 500, objeto até 8 KiB): uma chave que não cabe fica só na sessão, e a cobrança sai sem ela — mantenha a chave de reconciliação curta. | | `utm` | Atribuição da venda, **normalizada** (ver abaixo). Capturada uma única vez e imutável — e é ela que vira a dimensão de canal da transação cobrada. | | `cookies` | **Cookies de rastreio do comprador**, com o nome do cookie como chave e o valor como veio do navegador: `_fbp` e `_fbc` (Meta), `_ga` e `_ga_` (GA4), `_gcl_au` (Google Ads)… É com eles que você casa, no seu CAPI/Conversions API, um carrinho abandonado com o clique do anúncio. Capturados pela página do checkout e **mesclados** a cada autosave (o `_fbc` só existe depois do clique e pode chegar depois do `_fbp`). `null` quando nada foi capturado — a página não chegou a rodar, ou o comprador não consentiu. Apagados da sessão 90 dias após a última atividade. Na venda aprovada, os mesmos valores descem para `transaction.metadata.cookies`. | | `firstInitialization` | `true` enquanto o comprador não abriu a `url`. | | `online` | **O comprador está com o checkout aberto agora.** Sessão `active` cujo último sinal de presença (heartbeat, telemetria ou autosave) tem menos de 90s. É calculado na leitura, nunca um estado guardado: não existe "evento de despedida" quando alguém fecha a aba. | | `expiresAt` | Expiração informada por você. Se omitida, a sessão expira automaticamente após a idade máxima (**24h** a partir de `createdAt`). | #### Atribuição (`utm`) De onde veio a venda. **Capturada uma vez, na criação, e imutável**: um `PUT` posterior com outra `utm` é ignorado enquanto a sessão já tiver atribuição — o afiliado que trouxe o comprador não pode ser sobrescrito no meio do checkout. Quando a sessão é cobrada, os cinco campos UTM passam para a transação e alimentam os relatórios de canal. ```json { "utm": { "source": "newsletter", "medium": "email", "campaign": "junho", "content": "banner-topo", "term": "curso", "gclid": "Cj0KCQ...", "fbclid": "IwAR1x..." } } ``` **Duas grafias entram, uma sai.** `source`, `utm_source` e `utmSource` são todas aceitas — a URL do anúncio carimba `utm_source`, e repassá-la crua funciona. O que fica armazenado e é devolvido na leitura é sempre a forma curta (`source`, `medium`, `campaign`, `content`, `term`), acompanhada dos identificadores de clique que vierem (`gclid`, `fbclid`, `ttclid`, `msclkid`, `twclid`, `wbraid`, `gbraid`, `irclickid`, `li_fat_id`, `epik`). Chave desconhecida é descartada. Valor vazio, `null`, ou o texto literal `"null"`/`"undefined"` conta como ausente — então **um objeto só de nulos equivale a não enviar `utm`**, e não consome a captura única: a campanha real ainda pode ser gravada depois. > No checkout hospedado nada disso precisa ser enviado: os `utm_*` da URL de acesso são lidos e > normalizados automaticamente na primeira inicialização da sessão. #### Ciclo de Vida (`status`) ```mermaid stateDiagram-v2 state "active" as active state "completed" as completed state "abandoned" as abandoned state "cancelled" as cancelled state "expired" as expired [*] --> active: POST /v1/checkouts/sessions active --> completed: pagamento aprovado active --> abandoned: inatividade do comprador abandoned --> active: comprador retorna (heartbeat / reativação) active --> cancelled: DELETE (soft-cancel) active --> expired: passou de expiresAt completed --> [*] cancelled --> [*] expired --> [*] ``` | `status` | Significado | Terminal? | |----------|-------------|-----------| | `active` | Criada e válida; aguardando/recebendo o comprador. | não | | `completed` | Pagamento aprovado. | **sim** | | `abandoned` | Marcada como abandonada por inatividade; pode **reabrir** para `active` se o comprador retornar. | não | | `cancelled` | Cancelada via `DELETE` (soft-cancel). | **sim** | | `expired` | Passou de `expiresAt`. | **sim** | > O enum nativo de status é `active`, `completed`, `cancelled`, `abandoned`, `expired` — **não** existe > status `pending`. > > Em status terminal a `url` redireciona para uma página de estado em vez de renderizar o checkout. #### Recursos Disponíveis | Recurso | Método | Endpoint | Descrição | |---------|--------|----------|-----------| | Criar | POST | `/v1/checkouts/sessions` | Cria a sessão e devolve a `url` hospedada | | Listar | GET | `/v1/checkouts/sessions` | Lista paginada (filtros por `status`, `source`, `id`, datas) | | Consultar | GET | `/v1/checkouts/sessions/{id}` | Recupera a sessão atual (reconciliação) | | Atualizar | PUT | `/v1/checkouts/sessions/{id}` | Atualiza itens/cliente/endereço/cupons/metadata (só em status não-terminal) | | Cancelar | DELETE | `/v1/checkouts/sessions/{id}` | **Soft-cancel** → `status=cancelled` (preserva a linha e o histórico) | | Pagar | POST | `/v1/checkouts/sessions/{id}/pay` | Submete a sessão para pagamento (assíncrono → job `202`) | | Token de telemetria | POST | `/v1/checkouts/sessions/{id}/telemetry-token` | Emite a capability que o front-end do checkout usa para enviar telemetria de interatividade (só sessão `active`) | | Comportamento | GET | `/v1/checkouts/sessions/{id}/behavior` | Roll-up de engajamento + diagnóstico da sessão (intenção, risco de abandono, atrito, ação recomendada) | | Eventos | GET | `/v1/checkouts/sessions/{id}/events` | Traço bruto de interatividade, evento a evento (`limit` 1–500, cursor `afterSeq`) | > **Filtros da listagem:** `status` (enum), `source` (texto, máx. 20), `id` (`sess_`/`ckts_`/`ses_`), > `daterange*` (por `createdAt`), `abandonedrangegte`/`abandonedrangelte` (por `abandonedAt`) e paginação > (`limit`/`offset`/`sort`). Não há filtro por `externalTransactionId`. ##### Listar: scores e ordenação Cada item da lista traz os três scores do motor de telemetria, de 0 a 100: `intentScore` (intenção de compra), `abandonRiskScore` (risco de abandono) e `automationScore` (indício de automação, apenas consultivo). `null` significa que nenhum evento de telemetria chegou para aquela sessão. Os valores são os do **último** evento recebido: numa sessão já encerrada, descrevem como ela estava quando parou. O diagnóstico completo (estágio, pontos de atrito, razões, ação recomendada) fica em `GET /v1/checkouts/sessions/{id}/behavior`. > O mesmo número se chama `abandonmentRisk` dentro de `behavior.insights` e `abandonRiskScore` na lista > (o nome da coluna ordenável). `sort` aceita `createdAt`, `intentScore`, `abandonRiskScore` ou `abandonedAt`. O prefixo `-` inverte para decrescente. Qualquer outro valor responde `400 invalidParameters`. `ascending` e `descending` continuam valendo e ordenam por `createdAt`. Sem `sort`, a lista vem da mais recente para a mais antiga. Ao ordenar por score — ou por `abandonedAt` — as sessões sem valor (`null`) vão para o **fim** nas duas direções, e o desempate é pela mais recente. | Fila | Consulta sugerida | |------|-------------------| | Carrinhos abandonados: de quem vale correr atrás primeiro | `?abandonedrangegte=2026-09-09T00:00:00Z&sort=-intentScore` | | Os mesmos, na ordem em que caíram | `?abandonedrangegte=2026-09-09T00:00:00Z&sort=-abandonedAt` | | Sessões abertas: quem está prestes a sair | `?status=active&sort=-abandonRiskScore` | Numa sessão já abandonada, `abandonRiskScore` é um risco que já se concretizou. Para essa fila, ordene por `intentScore`. ##### Listar: a fila de recuperação recorta por `abandonedAt`, não por `status` **O abandono não fica em `abandoned` para sempre.** A sessão sem pagamento passa a `expired` 24 horas depois de criada, inclusive a abandonada — enquanto o **link de recuperação vale 7 dias**. Do 2º ao 7º dia o carrinho tem link vivo, é recuperável, e `status=abandoned` já não o encontra. Por isso a fila de recuperação se monta com `abandonedrangegte`/`abandonedrangelte`, que recortam por **quando o carrinho foi abandonado**: ```http GET /v1/checkouts/sessions?abandonedrangegte=2026-09-09T00:00:00Z&sort=-intentScore ``` A **presença** de qualquer um dos dois já implica `abandonedAt` preenchido — a lista devolve só o que de fato virou carrinho abandonado, em qualquer `status` de agora (`abandoned`, `expired`, e também o `completed` de quem voltou e comprou). O `status` continua em cada linha: expirado ≠ abandonado é informação real, e o filtro não a esconde. Os dois recortes de data convivem e respondem a perguntas diferentes: `daterange*` é *quando a sessão nasceu*, `abandonedrange*` é *quando o carrinho caiu*. ##### Criar ```http POST /v1/checkouts/sessions SelectKey: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx Content-Type: application/json { "externalTransactionId": "pedido-7781", "lineItems": [ { "id": "var_main_product", "quantity": 1 }, { "id": "var_orderbump_book", "quantity": 1, "isOrderbump": true } ], "customer": { "email": "maria@example.com", "fullName": "Maria Silva" }, "coupons": [{ "code": "DESCONTO10" }], "redirectUrls": { "paid": "https://seller.com/obrigado" }, "metadata": { "erpOrderId": "9921" } } ``` Resposta `201` = o objeto `checkout.session` acima. Redirecione o comprador para `url`. > **Campos aceitos** (todos opcionais): `items`/`lineItems` (carrinho), `customer`, `address`, `client` > (carrega UTM), `coupons`, `paymentMethods`, `redirectUrls`, `shippingDelivery`, `source`, `language`, > `timezone`, `totalAmount`/`totalDiscount`/`totalShipping`/`totalTax` (centavos), `utm`, `metadata`, > `integrationType`, `integrationId`, `externalTransactionId`, `expiresAt` e `onBehalfOf`. > **`onBehalfOf`** (`publicId` de um sub-vendedor que você possui) faz o *charge único* da sessão ser > liquidado **no** sub-vendedor (marketplace direct-charge) e você (plataforma) recebe a *application fee*; > a posse + função de marketplace são validadas na criação (senão `onBehalfOfInvalid`, 422). O campo > `ambient` é aceito por compatibilidade mas **ignorado** — o ambiente vem do principal (namespace da chave). > > **Limite dos objetos livres**: `customer`, `address`, `client`, `cookies`, `shippingDelivery`, `utm` e `metadata` > aceitam até **100 chaves** (chave até 100 caracteres) e até **16 KiB** de JSON cada. Acima disso a requisição é > recusada com `400 invalidParameters` e o campo em `error.params` — nada é cortado em silêncio. **Preço ad-hoc** (sem catálogo): ```json { "lineItems": [{ "name": "Consultoria 1h", "quantity": 2, "unitPrice": 25000 }] } ``` ##### Pedido precificado fora: linhas manuais, frete da loja, carrinho travado É o caso de uma loja (WooCommerce, Shopify, um ERP) que já calculou o pedido — cupom próprio, frete por CEP e peso, taxa de embalagem — e só quer que o comprador **pague** na página hospedada: ```json { "externalTransactionId": "7781", "lineItems": [ { "id": "var_camiseta_m", "quantity": 2 }, { "name": "Embalagem para presente", "unitPrice": 990, "quantity": 1, "externalReference": "fee:gift" }, { "name": "Desconto BEMVINDO10", "unitPrice": 0, "quantity": 1 } ], "shippingDelivery": { "external": true, "name": "SEDEX", "price": 2490, "deliveryTime": { "min": 2, "max": 5 }, "carrier": "Correios" }, "customer": { "fullName": "Maria Silva", "email": "maria@example.com", "document": { "type": "cpf", "number": "12345678909" } }, "address": { "postcode": "01310-100", "street": "Av. Paulista", "number": "1000", "city": "São Paulo", "state": "SP", "country": "BR" }, "paymentMethods": ["pix", "credit"], "redirectUrls": { "paid": "https://loja.com/obrigado/7781" }, "metadata": { "orderKey": "wc_order_abc" } } ``` - **Linha manual** — uma `lineItems[]` com `name` + `unitPrice` (centavos inteiros) e sem `id`. Ela recebe um id `line_…` na resposta, é exibida e somada na página com o preço que você mandou, e chega à transação como item (com `externalReference`/`metadata`, se enviados). Um desconto da sua loja entra como preço já descontado nas linhas, nunca como valor negativo. - **Frete da loja** — `shippingDelivery` com `external: true`, `name` e `price` (centavos; `0` = grátis). Entra no total como frete, é congelado com esse nome na cobrança (`shipping` da transação) e **dispensa o passo de frete**: a página não pede CEP nem opção do seu catálogo de entregas. Mande o `address` para o boleto. - **Carrinho travado** — **toda** sessão criada por esta API é o pedido da sua loja: não tem stepper, "remover", campo de cupom nem pop-up de cupom de saída, e o servidor recusa (`422 checkoutCartNotEditable`) qualquer mudança de quantidade, item, cupom ou do frete da loja pelo comprador (a prévia de total também só considera os cupons que a sua loja gravou na sessão). O **template que precifica é o global vigente da sua conta** — um `template_id` na URL de pagamento é ignorado. Os únicos ajustes de valor que podem acontecer são os descontos por **método** (Pix/cartão) do template global e o **juro** do parcelamento: confira `amount` na transação contra o total do pedido menos o desconto do método escolhido; qualquer diferença além disso é anomalia — deixe o pedido em espera e fale com o suporte (antes de 10/10/2026 esta página mandava tratar QUALQUER diferença como desconto; a 20ª varredura de segurança fechou os caminhos que a produziam). - **Métodos** — `paymentMethods` **restringe** os métodos do template (nunca liga um desligado). Ausente, vazia ou sem interseção com os trilhos (`credit`/`pix`/`billet`) **ligados no template**: a lista não restringe e valem **todos** os do template — inclusive o NuPay, como qualquer outro método. Ex.: `["nupay"]` sozinho, ou `["billet", "nupay"]` com o boleto desligado no template, servem o template inteiro. Numa lista que restringe (algum trilho dela está ligado), o NuPay só aparece se `nupay` estiver nela (`["pix", "nupay"]` → Pix e NuPay; `["pix", "credit"]` → sem NuPay). Em todos os casos o NuPay só aparece se a sua conta estiver habilitada para ele (o comprador aprova no app do Nubank, à vista, sem cartão; carrinho com assinatura não oferece). A sessão fica `completed` quando o link de aprovação é exibido — como no Pix, quem diz que foi pago é o `checkout.session.completed` / `transaction.approved`. Se o comprador desistir no app, a NuPay vencer (30 min) ou for recusada, você recebe o `transaction.canceled`/`transaction.failed` dessa cobrança e a sessão **não** volta: como depois de um Pix vencido, o comprador paga num checkout **novo** — crie outra sessão (ou mande o link de pagamento de novo) para o mesmo pedido. - Itens do catálogo (`id` de variante) e linhas manuais convivem na mesma sessão; uma linha manual não exige CPF nem endereço por si (o Pix e o boleto continuam exigindo o CPF do pagador). ##### Atualizar A semântica do `PUT` é de **merge parcial**, com recompute seguro de totais: - Campos simples (`customer`, `address`, `metadata`, `redirectUrls`, `externalTransactionId`, `expiresAt`) — atualizam sem recalcular o carrinho. - `lineItems` enviado → **re-precifica** itens + totais e **zera os cupons** (reenvie `coupons` no mesmo PUT para reaplicar). - `coupons` enviado sem `lineItems` → re-precifica os itens atuais para recalcular o desconto. - O corpo aceita também `status`, restrito a `active` ou `abandoned` (um `abandoned` voltando para `active` **reativa** a mesma sessão e emite `checkout.session.reactivated`). - Só funciona em status **não-terminal**; em `completed`/`cancelled`/`expired` → `checkoutSessionNotUpdatable` (409). - **Selada enquanto há cobrança em andamento.** Depois que o pagamento é submetido e enquanto ele não chega a um estado terminal, um PUT que toque os campos de dinheiro (`items`/`lineItems`, `coupons`, `totalAmount`, `totalDiscount`, `totalShipping`) responde **409 `sessionHasActivePayment`**. Os demais campos continuam atualizáveis. Espere o pagamento terminar (ou abra uma nova sessão). - **O preço cobrado é o do momento do submit.** Ao submeter o pagamento, os totais são fixados; é essa a quantia cobrada, mesmo que a sessão mude depois. O selo trava a **mercadoria**, o momento do submit trava o **preço**. ##### Cancelar `DELETE` é **soft-cancel**: marca `status=cancelled` e preserva a linha (os dados de funil/abandono vivem nela); sessões já `completed` não são afetadas. Resposta `200`: ```json { "id": "sess_a1b2c3d4e5f6789012ab", "resource": "checkoutSession", "deleted": true } ``` Se a sessão não existir → `checkoutSessionNotFound` (404). ##### Pagar (assíncrono) `POST /v1/checkouts/sessions/{id}/pay` registra a **seleção de pagamento** do comprador e devolve `202` — a cobrança é processada de forma assíncrona, sobre os dados já fixados na sessão. ```json { "payment": { "method": "credit", "cardId": "card_01hqzv...", "installments": 3, "capture": true } } ``` | Campo | Tipo | Obrigatório | Descrição | |-------|------|:-----------:|-----------| | `payment.method` | enum | **Sim** | `credit`, `pix` ou `billet` | | `payment.cardId` | string | condicional | Token de cartão salvo (`card_...`). **Obrigatório** quando `method=credit` | | `payment.installments` | int | Não | 1–21 (default `1`) | | `payment.capture` | boolean | Não | default `true`. `false` **pré-autoriza** o cartão: o job fica em `processing` até você capturar a transação (`POST /v1/transactions/{id}/capture`) ou cancelá-la. Vale só para esta rota do lojista — o `/pay` do checkout hospedado sempre captura | Resposta `202 Accepted` (envelope de job, **sem** `_links`): ```json { "jobId": "payjob_01hqzv...", "status": "pending", "sessionId": "sess_a1b2c3d4e5f6789012ab" } ``` `status` do job pode ser `pending`, `processing`, `requiresAction`, `completed` ou `failed`. Só sessões `active` são pagáveis; senão `checkoutSessionNotPayable` (409). Sessão inexistente → `checkoutSessionNotFound` (404). **Um pagamento por vez por sessão.** Enquanto um pagamento da sessão está em andamento (job `pending`, `processing` ou `requiresAction` — criado por esta rota ou pelo checkout hospedado), um novo submit responde **409 `sessionHasActivePayment`** com `params[0].jobId` = o job que segura a sessão, e nada é cobrado de novo. Uma `Idempotency-Key` nova não abre outra cobrança: acompanhe o job em andamento e, se ele terminar `failed` (cartão recusado), submeta de novo. Os totais cobrados são os da sessão no instante do submit. **Uma compra, um pagamento.** Normalmente a sessão de uma compra paga ou aguardando o comprador já está `completed` (o 409 acima). Se mesmo assim ela estiver `active` com outro pagamento dela aprovado ou aguardando (Pix/boleto gerado, NuPay aguardando aprovação, cartão pré-autorizado), o submit responde **409 `sessionAlreadyPaid`** com `params[0].transactionId` = essa cobrança, e nada é cobrado. Acompanhe-a em vez de cobrar de novo. #### Configuração (paymentMethods / redirectUrls) Você pode enviá-los no corpo da sessão. Quando **omitidos**, o backend usa o **checkout template global** da empresa (`isGlobal`) para os métodos habilitados e as URLs de redirect; o `redirectUrls` do corpo sobrescreve por sessão. Sem template e sem corpo, o default é `["pix", "credit", "billet"]`. Um `paymentMethods` enviado só **restringe** o template (interseção) — ver a seção do pedido precificado fora. #### Reconciliação: o que volta para o seu sistema | Sinal | Quando | O que trazer | |-------|--------|--------------| | `transaction.approved` (e `failed`, `canceled`, `refunded`, `chargeback`, `dispute`) | a cobrança da sessão muda de estado | `externalReference` = o seu `externalTransactionId`; `metadata` = o seu `metadata` + `cookies`; `items[]` com os `externalReference` de cada linha; `shipping` com o frete da loja; `payment.cardId` quando um cartão ficou salvo no cliente (para cobrar renovações por `POST /v1/transactions` com `payment.card.id`) | | `checkout.session.completed` | a compra da sessão foi **paga** (cartão aprovado, Pix/boleto pago — inclusive a 1ª cobrança de uma assinatura sem trial) ou, num carrinho só em trial, a assinatura foi criada | o objeto `checkout.session` inteiro (`lineItems`, `customer`, `address`, `totals`, `externalTransactionId`, `metadata`) mais `transactionId`/`subscriptionId` (os dois, quando a compra criou uma assinatura e cobrou uma transação) | | `GET /v1/checkouts/sessions/{id}` | reconciliação pontual | `status`, `transactionId` | > O status `completed` da sessão lida pela API é o estado da **página** (o Pix já foi exibido); o evento > `checkout.session.completed` é o estado do **dinheiro**. Use o evento, ou `transaction.*`, para liberar o pedido. #### Cupons Envie `coupons` por **código** — aceita tanto a forma de string (`["DESCONTO10"]`) quanto de objeto (`[{ "code": "DESCONTO10" }]`); ambas são guardadas verbatim e ecoadas como `{ code, type, value }`. A elegibilidade (datas, limite de uso, mínimo de carrinho, itens permitidos) e o desconto são validados **na cobrança** (no `/pay`), não na criação da sessão. Cupom inexistente → `couponNotFound`; inelegível → `couponExpired` / `couponMinCartAmount` / etc. (catálogo de Coupons). #### Telemetria de interatividade Enquanto o comprador está na página, o checkout hospedado envia **telemetria de interatividade** — forma e tempo da interação (etapa atual, tempo ativo, edições por campo, tentativas de pagamento, trocas de parcelamento), **nunca o valor digitado em campo nenhum**. Sobre esse traço o backend calcula, por sessão, um diagnóstico de intenção e abandono. Você lê o resultado por dois endpoints; o terceiro só interessa a quem renderiza o próprio front-end do checkout. ##### Token de telemetria — POST /v1/checkouts/sessions/{id}/telemetry-token Emite a capability que o **front-end** usa para enviar os ticks de telemetria. Só serve para escrever telemetria **nessa** sessão e só é emitida para sessão `active` (`409 checkoutSessionNotTrackable` nas demais). O checkout hospedado pela Selectwin já faz isso sozinho — você só precisa deste endpoint se renderiza o checkout por conta própria em cima de uma sessão. ```json { "object": "checkout.session.telemetryToken", "id": "sess_abc123", "token": "eyJhbGciOiJSUzI1NiIs...", "expiresAt": "2026-07-15T12:00:00.000Z", "endpoint": "/core/checkout/v1/sessions/sess_abc123/telemetry", "intervalMs": 10000 } ``` ##### Comportamento — GET /v1/checkouts/sessions/{id}/behavior O roll-up da sessão (`engagement`) e o diagnóstico calculado sobre ele (`insights`): ```json { "object": "checkout.session.behavior", "id": "sess_abc123", "status": "active", "engagement": { "totalTimeSpentMs": 384000, "activeTimeMs": 250000, "maxStepReached": 4, "visitCount": 2, "eventCount": 137, "lastTelemetryAt": "2026-07-14T12:06:11.000Z" }, "insights": { "intentScore": 88, "abandonmentRisk": 64, "automationScore": 5, "confidence": 92, "stage": "stuck", "frictionPoints": [ { "area": "pagamento", "severity": "high", "evidence": "2 tentativa(s) de pagamento recusada(s)." } ], "recommendedAction": "offerHelp", "reasons": ["2 tentativa(s) de pagamento registrada(s) — a intenção de compra é concreta."], "summary": "Comprador travado há ~6min, na etapa 4 de 4. Intenção 88/100, risco de abandono 64/100.", "computedAt": "2026-07-14T12:06:11.000Z" }, "behavior": { "...": "o roll-up cru" } } ``` | Campo | Descrição | |-------|-----------| | `engagement.totalTimeSpentMs` / `activeTimeMs` | Tempo total desde a abertura e tempo com a aba focada/interagindo | | `engagement.maxStepReached` / `visitCount` / `eventCount` | Etapa máxima alcançada, visitas e eventos recebidos | | `insights.intentScore` / `abandonmentRisk` / `automationScore` | Escores 0–100: intenção de compra, risco de abandono, probabilidade de automação (bot) | | `insights.confidence` | Confiança 0–100 do diagnóstico — **exiba sempre**; abaixo de 35% a ação recomendada é `wait` | | `insights.stage` | `browsing`, `filling`, `paying`, `stuck`, `leaving` ou `converted` | | `insights.frictionPoints[]` | Onde o comprador travou (`area`, `severity` `low`/`medium`/`high`, `evidence`) | | `insights.recommendedAction` | O que fazer agora (ex.: `none`, `wait`, `offerHelp`) | > **`insights: null` significa "não há telemetria desta sessão"** — não "intenção zero". Não pinte `null` > como `0` na tela. ##### Eventos — GET /v1/checkouts/sessions/{id}/events O traço evento a evento, em ordem, para "replay" da sessão. Paginação por cursor: `limit` (1–500, default `200`) e `afterSeq` (devolve só eventos com `seq` maior). ```json { "object": "list", "id": "sess_abc123", "total": 137, "hasMore": false, "data": [ { "seq": 42, "type": "field.paste", "step": 4, "payload": { "field": "cardNumber" }, "occurredAt": "2026-07-14T12:00:03.100Z", "receivedAt": "2026-07-14T12:00:05.000Z" } ] } ``` O vocabulário de `type` é fechado (`session.view`, `step.enter`, `field.edit`, `field.paste`, `payment.submit`, `payment.result`, `click.rage`, `exit.intent`…) e o `payload` carrega **forma e tempo**, jamais conteúdo: quantas correções, se houve colagem, qual validação falhou — nunca o que foi digitado. #### Webhooks | Evento | Quando | |--------|--------| | `checkout.session.created` | Sessão recém-criada (auditoria). | | `checkout.session.completed` | O comprador concluiu o pagamento na sessão (use isto, não polling, para liberar o pedido). | | `checkout.session.abandoned` | A sessão passou a `abandoned` por inatividade do comprador. | | `checkout.session.reactivated` | Sessão `abandoned` reaberta (comprador retornou). | | `checkout.session.expired` | Sessão expirada (passou da idade máxima). | > O *payload* desses eventos é o objeto `checkout.session` completo (mesma forma da leitura). > > O caminho recomendado de confirmação é **webhook + a transação correspondente** — veja > [Proibição de Polling](/docs/reference/proibicao-de-polling) e > [Verificando Assinaturas de Webhook](/docs/guide/webhook-events/verifying-signatures). #### Melhores Práticas 1. **Resolva preço pelo catálogo** (`var_*`) sempre que possível; use ad-hoc só para itens fora do catálogo. 2. **Use `externalTransactionId`** para reconciliar (campo ecoado em cada sessão; a listagem filtra por `status`/`source`/`id`/datas, não por `externalTransactionId`). 3. **Reaja a `checkout.session.completed`** em vez de fazer polling no `GET`. 4. **Reenvie `coupons` ao alterar `lineItems`** num PUT, já que mudar o carrinho zera os descontos. 5. **Defina `expiresAt`** se a idade máxima padrão (24h) não fizer sentido para o seu fluxo. #### Integração com Outros Recursos - **Products / Variants** — `lineItems[].id` referencia variantes (`var_*`); o preço vem da variante. - **Coupons** — aplicados por código; validados/precificados no create/update. - **Transactions** — a sessão concluída gera uma transação (cobrança). - **Customers / Addresses** — o snapshot da sessão é resolvido em cliente/endereço no momento do pagamento. - **Webhooks** — `checkout.session.completed` para confirmação assíncrona. --- ## Cupons - Visão Geral Fonte: https://selectwin.io/docs/guide/coupons/overview #### Introdução O recurso de Cupons permite gerenciar descontos promocionais que podem ser aplicados em transações e assinaturas. Suporta descontos percentuais ou de valor fixo, restrições de carrinho (valor e itens), datas de validade, limites de uso e escopo para assinaturas recorrentes. Cupons são aplicados em uma cobrança informando o **código** no array `discounts` da transação/assinatura (`"discounts": [{ "code": "BLACKFRIDAY25" }]`). O valor do desconto nunca vem do request — é resolvido a partir do cupom cadastrado no catálogo. Base URL: `https://api.selectwin.io/v1`. Autenticação via header `SelectKey` (`sk_test_` / `sk_live_`). #### Estrutura do Objeto Cupom Respostas de create/read/update retornam o objeto completo no root level (sem `merchant` nem `_links`, conforme o mount-object de coupons): ```json { "id": "dis_01hqzvabc", "name": "Black Friday 25%", "code": "BLACKFRIDAY25", "type": "percentage", "value": 25, "enabled": true, "minCartAmount": 10000, "maxCartAmount": null, "minCartItems": null, "maxCartItems": null, "usageLimit": 100, "usageQuantity": 0, "redemptionsCount": 0, "limitOneUsePerCustomer": true, "isCumulative": false, "initDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-04-30T23:59:59.000Z", "allowedItemIds": null, "allowedCustomerIds": null, "scope": "firstCharge", "recurringCycles": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` ##### Tipo e unidade de `value` | `type` | Significado de `value` | |--------|------------------------| | `percentage` | Taxa percentual de 0 a 100 (ex.: `25` = 25%). | | `flat` | Valor fixo em **REAIS** (decimal, ex.: `12.30`). O motor converte reais → centavos no momento da cobrança. | > Atenção: para `flat` o `value` é em **reais** (não centavos). Já os campos de carrinho `minCartAmount` / `maxCartAmount` são em **centavos** (BRL). ##### Cupom restrito a itens (`allowedItemIds`) Com `allowedItemIds` preenchido (ids de **variante**, `var_…` — para liberar um produto inteiro, liste as variantes dele), o cupom só vale se o carrinho tiver pelo menos um dos itens — e o desconto é calculado **só sobre esses itens**, não sobre o carrinho inteiro. Um cupom de 50% restrito a uma caneca de R$ 10,00, num carrinho que também tem um notebook de R$ 5.000,00, desconta R$ 5,00. Um cupom `flat` restrito fica limitado ao valor dos itens permitidos. > ⚑ **Desde 15/09/2026.** Antes, `allowedItemIds` só decidia se o cupom valia, e o desconto saía sobre o > carrinho inteiro. Vale no checkout, no `POST /v1/transactions` com `items` e no teto antifraude da cobrança > do checkout. O `POST /v1/coupons/validate` não recebe o valor de cada item, então a prévia dele continua > calculando sobre o `amount` informado. ##### Escopo recorrente - `scope: "firstCharge"` (padrão) — o desconto se aplica apenas à 1ª cobrança/ciclo. - `scope: "recurring"` — o desconto é fixado na assinatura e reaplicado a cada ciclo, até `recurringCycles` ciclos (`null` = todos os ciclos enquanto a assinatura existir). **Regras de respostas (fidelidade ao código):** - Create/read/update: objeto completo (acima), sem `merchant` ou `_links`. - Delete: `{ "id", "resource": "coupon", "deleted": true }` (sem merchant/_links). - List: paginado `{ offset, limit, total, page, data: [...], hasMore }` — itens completos (objeto `CouponResource`), sem merchant/_links no envelope. - Listall: array direto de itens leves (root = array, sem paginação/mount). Itens de `listall` (projeção leve): `id`, `name`, `code`, `type`, `value`, `enabled`, `initDate`, `endDate`, `createdAt`, `updatedAt`. #### Operações | Método | Path | Descrição | |--------|------|-----------| | POST | `/v1/coupons/validate` | Validar/prever desconto de um código contra um carrinho (sem cobrar) | | POST | `/v1/coupons` | Criar (201, objeto completo) | | GET | `/v1/coupons` | Listar (paginado) | | GET | `/v1/coupons/listall` | Listar todos (array, sem paginação) | | GET | `/v1/coupons/{couponId}` | Ler (objeto completo) | | PUT \| PATCH | `/v1/coupons/{couponId}` | Atualizar (200, objeto completo) | | DELETE | `/v1/coupons/{couponId}` | Excluir (confirmação mínima) | > O `couponId` aceita os prefixos `dis_` (cupons criados pela API node) e `coup_` (cupons legados). Consulte os arquivos específicos para exemplos de requisição/resposta e atributos. #### Boas Práticas 1. Use `code` único e descritivo para fácil aplicação pelo cliente. 2. Defina `initDate`/`endDate` para controlar a validade. 3. Limite via `usageLimit` e `limitOneUsePerCustomer` para evitar abuso. 4. Acompanhe `redemptionsCount` nas leituras — é o que o `usageLimit` conta. `usageQuantity` é o limite por cliente, não um contador. 5. Use `POST /v1/coupons/validate` para pré-visualizar o desconto no checkout antes de cobrar. #### Integração Cupons são aplicados em transações/assinaturas via o array `discounts: [{ code }]`. Use `POST /v1/coupons/validate` para prever o resultado antes da cobrança. --- ## Cupons - Criar Fonte: https://selectwin.io/docs/guide/coupons/create `POST /v1/coupons` Cria um novo cupom de desconto com regras de aplicação, validade e limites. #### Requisição ```bash curl -X POST "https://api.selectwin.io/v1/coupons" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Black Friday 25%", "code": "BLACKFRIDAY25", "type": "percentage", "value": 25, "enabled": true, "minCartAmount": 10000, "usageLimit": 100, "limitOneUsePerCustomer": true, "initDate": "2026-04-01T00:00:00Z", "endDate": "2026-04-30T23:59:59Z" }' ``` ##### Campos do corpo | Campo | Tipo | Obrig. | Descrição | |-------|------|:------:|-----------| | `name` | string (1–80) | sim | Nome descritivo do cupom. | | `code` | string (1–50) | sim | Código aplicado pelo cliente (único por empresa). | | `type` | enum | sim | `flat` (valor fixo em **reais**) ou `percentage` (taxa 0–100). | | `value` | number | sim | Desconto. Se `percentage`: 0–100. Se `flat`: valor em **reais** (decimal, ex.: `12.30`). | | `enabled` | boolean | sim | Cupom ativo. | | `minCartAmount` / `maxCartAmount` | number (centavos) | não | Faixa de valor do carrinho em centavos. | | `minCartItems` / `maxCartItems` | number (int) | não | Faixa de quantidade de itens. | | `usageLimit` | number (int) | não | Limite total de usos. | | `usageQuantity` | number (int) | não | Quantas vezes **cada cliente** pode usar o cupom (`0` = sem limite por cliente). Com `limitOneUsePerCustomer: true`, vale 1. **Não é contador** — quantas vezes o cupom já foi usado vem em `redemptionsCount`, na leitura. | | `limitOneUsePerCustomer` | boolean | não | Restringe a 1 uso por cliente. | | `isCumulative` | boolean | não | Permite acumular com outros descontos. Default `false`. | | `initDate` / `endDate` | datetime ISO 8601 | não | Janela de validade. | | `allowedItemIds` | array de ids de **variante** (`var_…`, ≤ 1000) | não | Restringe a variantes específicas — o cupom só vale com uma delas no carrinho, e **só desconta esses itens**. Cada id precisa ser uma variante da sua conta, no mesmo ambiente do cupom; um id de produto (`prd_…`) ou de outra conta volta **400** `invalidParameters` no campo `allowedItemIds[i]` (ver [Visão Geral](/docs/guide/coupons/overview#cupom-restrito-a-itens-alloweditemids)). | | `allowedCustomerIds` | array de `cus_*` (≤ 1000) | não | Restringe a clientes específicos. | | `scope` | enum | não | `firstCharge` (default) ou `recurring`. | | `recurringCycles` | number (1–999) \| null | não | Quando `scope=recurring`: máximo de ciclos (`null` = todos). | > Campos opcionais enviados como `null`/`""` são tratados como ausentes (empty-to-absent). > Para `type=percentage`, `value` acima de 100 é rejeitado (422). `minCartAmount` acima de `maxCartAmount`, ou > `minCartItems` acima de `maxCartItems`, também: `422 invalidParameters` (0 = sem limite, nunca conflita). #### Resposta - 201 Created ```json { "id": "dis_01hqzvabc", "name": "Black Friday 25%", "code": "BLACKFRIDAY25", "type": "percentage", "value": 25, "enabled": true, "minCartAmount": 10000, "maxCartAmount": null, "minCartItems": null, "maxCartItems": null, "usageLimit": 100, "usageQuantity": 0, "redemptionsCount": 0, "limitOneUsePerCustomer": true, "isCumulative": false, "initDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-04-30T23:59:59.000Z", "allowedItemIds": null, "allowedCustomerIds": null, "scope": "firstCharge", "recurringCycles": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` **Nota:** Root level, sem `merchant` ou `_links`. #### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | publicId `dis_*` (ou `coup_*` para legados). | | `name` | string \| null | Nome descritivo. | | `code` | string \| null | Código para aplicação. | | `type` | string | `flat` ou `percentage`. | | `value` | number | Desconto: 0–100 se `percentage`; reais (decimal) se `flat`. | | `enabled` | boolean | Ativo. | | `minCartAmount` / `maxCartAmount` | number \| null | Faixa de valor do carrinho (centavos). | | `minCartItems` / `maxCartItems` | number \| null | Faixa de quantidade de itens. | | `usageLimit` | number \| null | Limite total de usos. | | `usageQuantity` | number \| null | Limite de usos **por cliente** (`0` = sem limite). Não é contador. | | `redemptionsCount` | number | Quantas vezes o cupom já foi usado (resgates em andamento e confirmados) — a mesma contagem que o `usageLimit` barra. Esgotado quando `usageLimit > 0` e `redemptionsCount >= usageLimit`. | | `limitOneUsePerCustomer` | boolean | Uma vez por cliente. | | `isCumulative` | boolean | Acumulável. | | `initDate` / `endDate` | string \| null | Validade (ISO 8601). | | `allowedItemIds` / `allowedCustomerIds` | array \| null | Restrições. | | `scope` | string | `firstCharge` ou `recurring`. | | `recurringCycles` | number \| null | Máx. de ciclos quando `scope=recurring`. | | `createdAt` / `updatedAt` | string | Timestamps ISO 8601. | #### Respostas de Erro | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `couponCodeConflict` | 409 | Já existe um cupom com este `code`. | Erros de validação do corpo retornam `400` (campos inválidos) ou `422` (ex.: `percentage` > 100). #### Melhores Práticas - Use codes únicos e memoráveis. - Defina datas de validade (`initDate`/`endDate`). - Acompanhe `redemptionsCount` nas leituras para saber quando o cupom vai esgotar. --- ## Cupons - Listar Fonte: https://selectwin.io/docs/guide/coupons/list `GET /v1/coupons` Lista cupons com paginação. Os itens de `data` são objetos **completos** (`CouponResource`); o envelope não inclui `merchant` nem `_links`. #### Requisição ```bash curl "https://api.selectwin.io/v1/coupons?limit=20&type=percentage&status=active" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` ##### Parâmetros de query | Parâmetro | Tipo | Default | Descrição | |-----------|------|---------|-----------| | `limit` | int (1–50) | 20 | Itens por página (máx. **50**). | | `offset` | int (≥ 0) | 0 | Deslocamento. | | `sort` | string | — | Expressão de ordenação, ex.: `-createdAt`. | | `dir` | enum | — | `ascending` ou `descending` (alias legado; usado quando `sort` ausente). | | `id` | string | — | Filtra por publicId (`dis_*` ou `coup_*`). | | `name` | string (≤ 80) | — | Filtra por nome. | | `code` | string (≤ 50) | — | Filtra por código. | | `type` | enum | — | `flat` ou `percentage`. | | `status` | enum | — | `active` ou `inactive`. | | `daterange` / `daterangegt` / `daterangegte` / `daterangelt` / `daterangelte` | date ou datetime ISO | — | Filtros por intervalo de datas. | #### Resposta - 200 OK ```json { "offset": 0, "limit": 20, "total": 12, "page": { "current": 1, "total": 1, "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 } }, "hasMore": false, "data": [ { "id": "dis_01hqzvabc", "name": "Black Friday 25%", "code": "BLACKFRIDAY25", "type": "percentage", "value": 25, "enabled": true, "minCartAmount": 10000, "maxCartAmount": null, "minCartItems": null, "maxCartItems": null, "usageLimit": 100, "usageQuantity": 0, "redemptionsCount": 0, "limitOneUsePerCustomer": true, "isCumulative": false, "initDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-04-30T23:59:59.000Z", "allowedItemIds": null, "allowedCustomerIds": null, "scope": "firstCharge", "recurringCycles": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ] } ``` **Nota:** Os campos de `page.offset` (`first`/`prev`/`next`/`last`) são sempre inteiros (não `null`). O envelope e os itens não incluem `merchant`/`_links`. #### Atributos da Resposta Paginada | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` / `limit` / `total` / `hasMore` / `page` | - | Paginação padrão. | | `data` | array | Objetos `CouponResource` completos (ver [Read.md](/docs/guide/coupons/read)). | #### List All `GET /v1/coupons/listall` Retorna o conjunto completo (até 1000 cupons) como um **array direto** no root (sem paginação/mount), com itens em projeção **leve**: `id`, `name`, `code`, `type`, `value`, `enabled`, `initDate`, `endDate`, `createdAt`, `updatedAt`. ```json [ { "id": "dis_01hqzvabc", "name": "Black Friday 25%", "code": "BLACKFRIDAY25", "type": "percentage", "value": 25, "enabled": true, "initDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-04-30T23:59:59.000Z", "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ] ``` #### Melhores Práticas - Use os filtros (`type`, `status`, `code`, intervalos de data) para grandes volumes. - Para a lista completa sem paginação (ex.: preencher um seletor), use `/listall`. --- ## Cupons - Ler Fonte: https://selectwin.io/docs/guide/coupons/read `GET /v1/coupons/{couponId}` Obtém os detalhes completos de um cupom. #### Requisição ```bash curl "https://api.selectwin.io/v1/coupons/dis_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` > O `couponId` aceita os prefixos `dis_` e `coup_` (cupons legados). #### Resposta - 200 OK ```json { "id": "dis_01hqzvabc", "name": "Black Friday 25%", "code": "BLACKFRIDAY25", "type": "percentage", "value": 25, "enabled": true, "minCartAmount": 10000, "maxCartAmount": null, "minCartItems": null, "maxCartItems": null, "usageLimit": 100, "usageQuantity": 0, "redemptionsCount": 0, "limitOneUsePerCustomer": true, "isCumulative": false, "initDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-04-30T23:59:59.000Z", "allowedItemIds": null, "allowedCustomerIds": null, "scope": "firstCharge", "recurringCycles": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` **Nota:** Root level, objeto completo, sem `merchant` nem `_links`. #### Atributos da Resposta Mesmos do Create (ver [Create.md](/docs/guide/coupons/create)). #### Respostas de Erro | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `couponNotFound` | 404 | Nenhum cupom com o `couponId` informado. | #### Melhores Práticas - Use `redemptionsCount` (contra `usageLimit`) para saber quanto do cupom ainda resta antes de promovê-lo. --- ## Cupons - Atualizar Fonte: https://selectwin.io/docs/guide/coupons/update `PUT /v1/coupons/{couponId}` ou `PATCH /v1/coupons/{couponId}` Atualiza um cupom existente. Ambos os métodos têm o mesmo comportamento (envie apenas os campos que deseja alterar — todos são opcionais). #### Requisição ```bash curl -X PUT "https://api.selectwin.io/v1/coupons/dis_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Black Friday 30%", "value": 30, "enabled": true, "endDate": "2026-12-31T23:59:59Z" }' ``` > O `couponId` aceita os prefixos `dis_` e `coup_` (cupons legados). ##### Campos (todos opcionais no update) | Campo | Tipo | Descrição | |-------|------|-----------| | `name` | string (1–80) | Nome do cupom. | | `code` | string (1–50) | Código aplicado pelo cliente. | | `type` | enum | `flat` (valor fixo em **reais**) ou `percentage` (taxa 0–100). | | `value` | number | Desconto: 0–100 se `percentage`; reais (decimal) se `flat`. | | `enabled` | boolean | Ativa/desativa o cupom. | | `minCartAmount` / `maxCartAmount` | number (centavos) | Faixa de valor do carrinho. | | `minCartItems` / `maxCartItems` | number (int) | Faixa de quantidade de itens. | | `usageLimit` | number (int) | Limite total de usos. | | `usageQuantity` | number (int) | Quantas vezes **cada cliente** pode usar o cupom (`0` = sem limite por cliente). Com `limitOneUsePerCustomer: true`, vale 1. **Não é contador** — quantas vezes o cupom já foi usado vem em `redemptionsCount`, na leitura. | | `limitOneUsePerCustomer` | boolean | Restringe a 1 uso por cliente. | | `isCumulative` | boolean | Permite acumular com outros descontos. | | `initDate` / `endDate` | datetime ISO 8601 \| `null` | Janela de validade. `null` remove o limite (sem início / sem término); campo ausente mantém o valor atual. Mande a hora **com fuso** (`2026-12-31T23:59:59-03:00` ou em UTC): uma hora sem fuso é lida como UTC. | | `allowedItemIds` | array de publicIds (≤ 1000) | Restringe a produtos/variantes específicos. | | `allowedCustomerIds` | array de `cus_*` (≤ 1000) | Restringe a clientes específicos. | | `scope` | enum | `firstCharge` ou `recurring`. | | `recurringCycles` | number (1–999) \| null | Máx. de ciclos quando `scope=recurring`. | > Campos opcionais enviados como `null`/`""` são tratados como ausentes (empty-to-absent), exceto `recurringCycles`, que aceita `null` explícito para limpar o limite. > Campos não reconhecidos são rejeitados (body estrito). #### Resposta - 200 OK ```json { "id": "dis_01hqzvabc", "name": "Black Friday 30%", "code": "BLACKFRIDAY25", "type": "percentage", "value": 30, "enabled": true, "minCartAmount": 10000, "maxCartAmount": null, "minCartItems": null, "maxCartItems": null, "usageLimit": 100, "usageQuantity": 0, "redemptionsCount": 0, "limitOneUsePerCustomer": true, "isCumulative": false, "initDate": "2026-04-01T00:00:00.000Z", "endDate": "2026-12-31T23:59:59.000Z", "allowedItemIds": null, "allowedCustomerIds": null, "scope": "firstCharge", "recurringCycles": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` > As respostas de cupom **não** incluem `merchant` nem `_links`. #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `couponNotFound` | 404 | Nenhum cupom com o `couponId` informado. | | `couponCodeConflict` | 409 | O novo `code` já é usado por outro cupom. | Erros de validação do corpo retornam `400` (campos inválidos) ou `422` (ex.: `percentage` > 100). **O estado FINAL é validado, não só o corpo.** Um update que muda um campo só é conferido junto com o que já está gravado: `{"value": 5000}` num cupom `percentage` responde `422 invalidParameters` (`params[0].value`), mesmo sem `type` no corpo — antes era gravado como 5000%. O mesmo vale para `minCartAmount` acima de `maxCartAmount` e `minCartItems` acima de `maxCartItems` (0 = sem limite, nunca conflita). --- ## Cupons - Excluir Fonte: https://selectwin.io/docs/guide/coupons/delete `DELETE /v1/coupons/{couponId}` Remove um cupom. Após a exclusão, o código não pode mais ser aplicado. #### Requisição ```bash curl -X DELETE "https://api.selectwin.io/v1/coupons/dis_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` > O `couponId` aceita os prefixos `dis_` e `coup_` (cupons legados). #### Resposta - 200 OK ```json { "id": "dis_01hqzvabc", "resource": "coupon", "deleted": true } ``` **Nota:** Confirmação mínima, sem `merchant` nem `_links`. #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `couponNotFound` | 404 | Nenhum cupom com o `couponId` informado. | --- ## Marketplace (Sub-contas) - Visão Geral Fonte: https://selectwin.io/docs/guide/marketplace/overview #### Introdução O recurso de Marketplace permite que a sua conta opere como uma **plataforma**: você cadastra **sub-contas** (os vendedores que vendem por meio de você), acompanha o credenciamento de cada uma, define quanto a plataforma leva em cada venda e cobra **em nome** delas. É o modelo "Connect": cada sub-conta é um vendedor com cadastro, saldo e saques próprios, e a plataforma recebe a sua parte automaticamente. O recurso é **habilitado por conta**. A Selectwin liga a função para a sua conta e a submete a uma aprovação de compliance; só depois disso a API aceita criar sub-contas. Consulte o estado com `GET /v1/marketplace`. #### Conceito e Funcionalidade Uma sub-conta nasce com o status `pending` e passa pelo mesmo credenciamento (KYC) de qualquer conta Selectwin. Há dois caminhos para completá-lo: - **Link de credenciamento**: você gera um link secreto para a sub-conta e o envia ao vendedor; ele preenche os dados e documentos numa página hospedada pela Selectwin, sem precisar de login. Veja [Link de credenciamento](/docs/guide/marketplace/kyc-link). - **Pelo painel**: quem tem acesso à plataforma alterna para a sub-conta e preenche o cadastro. Enquanto a sub-conta não está `active`, ela **não cobra nem saca**. Assim que é aprovada, você passa a cobrar em nome dela com `onBehalfOf` em [Transações](/docs/guide/transactions/create#cobrança-em-nome-de-uma-sub-conta-marketplace) e em [Sessões de checkout](/docs/guide/checkouts/sessions), e a plataforma recolhe a sua taxa como um split adicional. A **taxa da plataforma** é definida de uma de duas formas, por sub-conta: | Forma | Como | Quando usar | |-------|------|-------------| | `platformFee` | Um percentual (`percentage`, até 100) ou um valor fixo em centavos (`flat`) sobre cada venda | Uma regra simples, igual para todos os meios de pagamento | | `planId` | Um [plano de taxas](/docs/guide/marketplace/plans) (`rate_…`) com taxa por meio de pagamento, parcelas, prazo de liberação e tarifa de saque | Tabelas diferentes por grupo de vendedores | Uma sub-conta usa **uma** das duas (nunca ambas). Sem nenhuma, a plataforma não recolhe taxa nas vendas dela. #### Fluxo típico 1. `GET /v1/marketplace` — confirme `enabled: true` e `approvalStatus: "approved"`. 2. `POST /v1/marketplace/sub-accounts` — [crie a sub-conta](/docs/guide/marketplace/create) com o nome do vendedor e a taxa (ou o plano). 3. `POST /v1/marketplace/sub-accounts/{companyId}/kyc-link` — [gere o link](/docs/guide/marketplace/kyc-link) e envie ao vendedor. 4. Acompanhe pelos webhooks `seller.*` (`seller.analyzing`, `seller.approved`, `seller.refused`…) ou por [consulta](/docs/guide/marketplace/read): `status`, `kycSubmittedAt` e `approvedAt`. 5. Com a sub-conta `active`, cobre com `onBehalfOf: "bus_…"`. 6. Para consultar, capturar ou estornar essa venda, ler o saldo ou pedir um saque **da sub-conta**, [opere como ela](/docs/guide/marketplace/acting-as) com o cabeçalho `X-Selectwin-Account`. #### Operar como a sub-conta A venda feita com `onBehalfOf` pertence à sub-conta. Tudo o que é dela — vendas, saldo, recebíveis, carteiras, saques, clientes — a plataforma alcança enviando `X-Selectwin-Account: bus_…` junto da sua chave de API: a requisição roda na sub-conta e a resposta vem com `merchant.isSubAccount: true`. Só chave de API, só sub-conta sua; o cabeçalho nunca é ignorado. Guia: [Operar como a sub-conta](/docs/guide/marketplace/acting-as). #### Estrutura do Objeto Sub-conta ```json { "id": "bus_01hqzvsub1", "name": "Loja da Maria", "status": "pending", "platformFee": { "type": "percentage", "value": 10 }, "planId": null, "createdAt": "2026-10-17T12:00:00.000Z", "kycSubmittedAt": null, "approvedAt": null, "volume30dCents": 0 } ``` | Campo | Tipo | Descrição | |-------|------|-----------| | `id` | string | Identificador da sub-conta (`bus_…`). É o valor de `onBehalfOf` nas cobranças | | `name` | string | Nome do vendedor | | `status` | string | `pending` (cadastro em aberto), `analyzing` (em análise), `awaiting-documents` (documentos pedidos de novo), `active` (aprovada: cobra e saca), `blocked`, `disabled`. Trate valores novos com um caminho padrão | | `platformFee` | object \| null | A taxa simples da plataforma: `{ "type": "percentage" \| "flat", "value": número }`. `null` quando a sub-conta usa um plano ou não tem taxa | | `planId` | string \| null | O plano de taxas atribuído (`rate_…`), ou `null` | | `createdAt` | string | Quando a sub-conta foi criada (ISO 8601, UTC) | | `kycSubmittedAt` | string \| null | Quando o vendedor **enviou** o cadastro para análise. `null` enquanto não enviou | | `approvedAt` | string \| null | Quando o cadastro foi **aprovado** (a última passagem para `active`). `null` se nunca | | `volume30dCents` | integer \| null | Volume **aprovado** da sub-conta nos últimos 30 dias, em centavos, em produção. `null` quando a leitura não estava disponível (a consulta nunca falha por isso) | > **Sub-conta parada:** `kycSubmittedAt` preenchido com `status` ainda `pending` significa que o vendedor terminou o cadastro mas faltou um dado essencial (nome, e-mail, documento ou país), e por isso ele **não entrou** na análise. Trate como ação necessária: peça ao vendedor que complete o cadastro pelo mesmo link. #### Estado da plataforma ``` GET /v1/marketplace ``` ```json { "enabled": true, "subAccountCount": 12, "subAccountLimit": 20, "isSubAccount": false, "approvalStatus": "approved" } ``` | Campo | Descrição | |-------|-----------| | `enabled` | A função de marketplace está ligada para a sua conta | | `subAccountCount` · `subAccountLimit` | Quantas sub-contas existem e quantas a conta pode ter. Para subir o limite, fale com o seu gerente de conta | | `isSubAccount` | `true` quando a conta autenticada é ela mesma uma sub-conta (uma sub-conta não cria sub-contas) | | `approvalStatus` | `pending` (aguardando compliance), `approved` (pode criar sub-contas) ou `rejected`; `null` quando a função nunca foi ligada | #### Precauções - **Chave de API:** o recurso é alcançado por chaves de acesso **Total**. A opção de restringir uma chave só a este recurso ainda não está disponível no painel. - **Sandbox:** as sub-contas são um recurso **de conta**, com uma cópia só (a de produção). Uma escrita com chave `sk_test_` responde `409 accountLevelReadOnlyInSandbox`; crie e altere sub-contas com `sk_live_`. As leituras funcionam nos dois ambientes. - **Idempotência:** a criação de sub-conta aceita `X-Idempotency-Key` — use-a em toda retentativa para não cadastrar o mesmo vendedor duas vezes. - **Dados do vendedor:** o cadastro (documentos, conta de saque) é preenchido pelo próprio vendedor pelo link, ou por você no painel. Não há como enviar documentos de terceiros por esta API. - **Limite:** ao atingir `subAccountLimit`, a criação responde `422 subAccountLimitReached`. #### Webhooks A plataforma recebe nos seus endpoints de webhook **todos** os eventos de cada sub-conta a que estiver inscrita — os `seller.*` do credenciamento (`seller.created`, `seller.analyzing`, `seller.approved`, `seller.refused`, `seller.kycRequired`, `seller.blocked`, `seller.disabled`, `seller.reinstated`) e também os das vendas, recebíveis e saques dela (`transaction.*`, `receivable.*`, `withdrawal.*`…). Cada um desses eventos traz, no envelope, o campo **`account`** com o `id` da sub-conta a que pertence; os eventos da sua própria conta não têm o campo. Veja o [Catálogo de eventos](/docs/guide/webhook-events/event-catalog#vendedores-sellers) e a [estrutura do evento](/docs/guide/webhook-events/overview#estrutura-do-objeto-evento-respostas-da-api). #### Recursos Disponíveis | Operação | Método e caminho | Guia | |----------|------------------|------| | Estado da plataforma | `GET /v1/marketplace` | esta página | | Criar sub-conta | `POST /v1/marketplace/sub-accounts` | [Criar](/docs/guide/marketplace/create) | | Listar sub-contas | `GET /v1/marketplace/sub-accounts` | [Listar](/docs/guide/marketplace/list) | | Consultar sub-conta | `GET /v1/marketplace/sub-accounts/{companyId}` | [Consultar](/docs/guide/marketplace/read) | | Alterar taxa / plano | `PATCH /v1/marketplace/sub-accounts/{companyId}` · `PUT …/{companyId}/plan` | [Atualizar](/docs/guide/marketplace/update) | | Link de credenciamento | `POST` · `GET` · `DELETE /v1/marketplace/sub-accounts/{companyId}/kyc-link` | [Link de credenciamento](/docs/guide/marketplace/kyc-link) | | Planos de taxas | `/v1/marketplace/plans` e `/v1/marketplace/plans/{planId}` | [Planos](/docs/guide/marketplace/plans) | | Andamento do cadastro | `GET /v1/marketplace/sub-accounts/{companyId}/onboarding` | [Andamento do cadastro](/docs/guide/marketplace/onboarding) | | Suspender e reabilitar | `POST …/{companyId}/suspend` · `POST …/{companyId}/reinstate` | [Suspender e reabilitar](/docs/guide/marketplace/suspend) | | Excluir (pedir o encerramento) | `POST` · `GET` · `DELETE …/{companyId}/closure-request` | [Excluir](/docs/guide/marketplace/close) | | Credenciamento pela sua tela | `/v1/marketplace/kyc/{token}/…` | [Credenciamento pela sua própria tela](/docs/guide/marketplace/kyc-steps) | | Operar como a sub-conta | cabeçalho `X-Selectwin-Account` em toda a API | [Operar como a sub-conta](/docs/guide/marketplace/acting-as) | Códigos de erro deste recurso: [Catálogo de Códigos de Erro](/docs/reference/codigos-de-erro#marketplace). --- ## Sub-contas - Criar Fonte: https://selectwin.io/docs/guide/marketplace/create #### Visão Geral Este endpoint cadastra uma nova sub-conta sob a sua plataforma: o vendedor que passará a vender por meio de você. A sub-conta nasce com o status `pending` e precisa completar o credenciamento (pelo [link](/docs/guide/marketplace/kyc-link) ou pelo painel) antes de cobrar e sacar. #### Precauções > **ATENÇÃO!** Leia antes de usar este endpoint. - A sua conta precisa ter a função de marketplace **ligada e aprovada** (`GET /v1/marketplace` → `enabled: true`, `approvalStatus: "approved"`). Caso contrário a API responde `403`. - Use `X-Idempotency-Key`: a criação é **idempotente**. Uma retentativa com a mesma chave devolve a mesma sub-conta em vez de cadastrar o vendedor duas vezes. - Só uma forma de taxa por sub-conta: `platformFee` **ou** `planId`. Enviar os dois é recusado com `400`. - O `planId` precisa ser um [plano de taxas](/docs/guide/marketplace/plans) da sua plataforma; um plano inexistente responde `404` **antes** de criar a sub-conta. - Recurso de conta: use uma chave `sk_live_` (com `sk_test_` a resposta é `409 accountLevelReadOnlyInSandbox`). - O limite de sub-contas da conta (`subAccountLimit`) vale aqui: no teto, `422 subAccountLimitReached`. #### Requisição ``` POST /v1/marketplace/sub-accounts ``` ##### Corpo da Requisição ```json { "companyName": "Loja da Maria", "platformFee": { "type": "percentage", "value": 10 } } ``` Ou, com um plano de taxas: ```json { "companyName": "Loja da Maria", "planId": "rate_01hqzvplan1" } ``` ##### Atributos do Corpo | Atributo | Tipo | Obrigatório | Descrição | Exemplo | |----------|------|-------------|-----------|---------| | `companyName` | string | Sim | Nome do vendedor (1–100 caracteres). O vendedor completa a razão social e os documentos no credenciamento | `"Loja da Maria"` | | `platformFee` | object | Não | A taxa simples da plataforma sobre cada venda da sub-conta | — | | `platformFee.type` | string | Sim* | `percentage` (percentual da venda) ou `flat` (valor fixo por venda) | `"percentage"` | | `platformFee.value` | number | Sim* | Com `percentage`, um número maior que 0 até 100; com `flat`, o valor em **centavos** (maior que 0) | `10` | | `planId` | string | Não | Um plano de taxas da plataforma (`rate_…`). Exclui `platformFee` | `"rate_01hqzvplan1"` | \* obrigatório quando `platformFee` é enviado. #### Resposta ##### Sucesso (201 Created) ```json { "id": "bus_01hqzvsub1", "name": "Loja da Maria", "status": "pending", "platformFee": { "type": "percentage", "value": 10 }, "planId": null, "createdAt": "2026-10-17T12:00:00.000Z", "kycSubmittedAt": null, "approvedAt": null, "volume30dCents": null } ``` Os campos estão descritos em [Visão Geral](/docs/guide/marketplace/overview#estrutura-do-objeto-sub-conta). Guarde o `id`: é o valor de `onBehalfOf` nas cobranças e o `{companyId}` das demais operações. O webhook `seller.created` é enviado aos seus endpoints assim que a sub-conta existe. ##### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 400 | `invalidParameters` | Corpo inválido (nome vazio, taxa fora da faixa, `platformFee` e `planId` juntos) | | 403 | `marketplaceNotEnabled` | A função de marketplace não está ligada para a sua conta | | 403 | `marketplaceNotApproved` | A função está ligada, mas ainda aguarda (ou não recebeu) a aprovação de compliance | | 403 | `companyNotActive` | A sua própria conta não está ativa | | 404 | `planNotFound` | O `planId` não é um plano da sua plataforma | | 409 | `accountLevelReadOnlyInSandbox` | Escrita com chave de sandbox | | 422 | `marketplaceNested` | A conta autenticada é uma sub-conta — sub-conta não cria sub-conta | | 422 | `subAccountLimitReached` | A conta atingiu o limite de sub-contas. Fale com o seu gerente de conta para ampliá-lo | | 422 | `marketplaceUserRequired` | A conta da plataforma não tem um titular registrado. Fale com o suporte | ##### Exemplo ```bash curl -X POST "https://api.selectwin.io/v1/marketplace/sub-accounts" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -H "X-Idempotency-Key: 7a1f2c0e-4b7d-4d0a-9c3e-1f2a3b4c5d6e" \ -d '{ "companyName": "Loja da Maria", "platformFee": { "type": "percentage", "value": 10 } }' ``` #### Próximo passo Gere o [link de credenciamento](/docs/guide/marketplace/kyc-link) e envie ao vendedor. A sub-conta só cobra e saca depois de `active`. --- ## Sub-contas - Listar Fonte: https://selectwin.io/docs/guide/marketplace/list #### Visão Geral Lista as sub-contas da sua plataforma, com paginação e filtros por status, nome e plano. Cada item traz o mesmo objeto de [Consultar](/docs/guide/marketplace/read), incluindo o volume aprovado dos últimos 30 dias. #### Requisição ``` GET /v1/marketplace/sub-accounts ``` ##### Parâmetros de Consulta | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `limit` | integer | Não | Itens por página (1–100; padrão 20) | `50` | | `offset` | integer | Não | Deslocamento (padrão 0) | `100` | | `status` | string | Não | Só as sub-contas neste status (`pending`, `analyzing`, `awaiting-documents`, `active`, `blocked`, `disabled`) | `"analyzing"` | | `q` | string | Não | Parte do nome (sem distinguir maiúsculas) | `"maria"` | | `planId` | string | Não | Só as sub-contas neste plano (`rate_…`), ou `none` para as que não têm plano | `"none"` | #### Resposta ##### Sucesso (200 OK) ```json { "offset": 0, "limit": 20, "total": 2, "page": { "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 }, "current": 1, "total": 1 }, "data": [ { "id": "bus_01hqzvsub1", "name": "Loja da Maria", "status": "active", "platformFee": null, "planId": "rate_01hqzvplan1", "createdAt": "2026-10-17T12:00:00.000Z", "kycSubmittedAt": "2026-10-17T15:20:11.000Z", "approvedAt": "2026-10-18T10:02:45.000Z", "volume30dCents": 1250000 }, { "id": "bus_01hqzvsub2", "name": "Bazar do João", "status": "pending", "platformFee": { "type": "flat", "value": 300 }, "planId": null, "createdAt": "2026-10-17T13:30:00.000Z", "kycSubmittedAt": null, "approvedAt": null, "volume30dCents": 0 } ], "hasMore": false } ``` | Campo | Descrição | |-------|-----------| | `total` | Quantas sub-contas atendem aos filtros | | `page` | Navegação por página: os `offset` da primeira, anterior, próxima e última página, a página atual e o total de páginas | | `data[]` | As sub-contas — campos em [Visão Geral](/docs/guide/marketplace/overview#estrutura-do-objeto-sub-conta) | | `hasMore` | `true` quando há mais itens depois desta página | `volume30dCents` vem `null` em todos os itens quando a leitura do volume não estava disponível; a lista nunca falha por isso. ##### Exemplo ```bash curl "https://api.selectwin.io/v1/marketplace/sub-accounts?status=analyzing&limit=50" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` --- ## Sub-contas - Consultar Fonte: https://selectwin.io/docs/guide/marketplace/read #### Visão Geral Consulta uma sub-conta da sua plataforma pelo `id`: o status do credenciamento, a taxa (ou o plano) e o volume aprovado dos últimos 30 dias. #### Requisição ``` GET /v1/marketplace/sub-accounts/{companyId} ``` | Parâmetro | Onde | Descrição | |-----------|------|-----------| | `companyId` | caminho | O `id` da sub-conta (`bus_…`) | #### Resposta ##### Sucesso (200 OK) ```json { "id": "bus_01hqzvsub1", "name": "Loja da Maria", "status": "active", "platformFee": null, "planId": "rate_01hqzvplan1", "createdAt": "2026-10-17T12:00:00.000Z", "kycSubmittedAt": "2026-10-17T15:20:11.000Z", "approvedAt": "2026-10-18T10:02:45.000Z", "volume30dCents": 1250000 } ``` Os campos estão descritos em [Visão Geral](/docs/guide/marketplace/overview#estrutura-do-objeto-sub-conta). Use `status`, `kycSubmittedAt` e `approvedAt` para saber em que ponto o credenciamento está: | `status` | `kycSubmittedAt` | O que significa | |----------|------------------|-----------------| | `pending` | `null` | O vendedor ainda não enviou o cadastro | | `pending` | preenchido | O vendedor terminou, mas faltou um dado essencial e ele **não entrou** na análise — peça que complete pelo mesmo link | | `analyzing` | preenchido | Em análise | | `awaiting-documents` | preenchido | A análise pediu documentos de novo; a sub-conta não cobra nem saca até reenviar | | `active` | preenchido | Aprovada: cobra e saca. `approvedAt` diz desde quando | > Prefira os webhooks `seller.*` a consultar este endpoint em laço — veja [Proibição de Polling](/docs/reference/proibicao-de-polling). ##### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 404 | `subAccountNotFound` | O `id` não é uma sub-conta da sua plataforma | ##### Exemplo ```bash curl "https://api.selectwin.io/v1/marketplace/sub-accounts/bus_01hqzvsub1" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` --- ## Sub-contas - Atualizar nome, taxa e plano Fonte: https://selectwin.io/docs/guide/marketplace/update #### Visão Geral Três coisas mudam numa sub-conta pela plataforma: o **nome** (só enquanto o cadastro não foi enviado), a **taxa simples** (alterar ou remover) e o **plano de taxas** (atribuir ou retirar). A mudança de taxa vale para as **próximas** cobranças; as já feitas não são recalculadas. #### Renomear ``` PATCH /v1/marketplace/sub-accounts/{companyId} ``` ```json { "name": "Loja da Maria Confecções" } ``` | Atributo | Tipo | Obrigatório | Descrição | |----------|------|-------------|-----------| | `name` | string | Não | O nome do vendedor (1–100 caracteres). Aceito **só com o cadastro ainda não enviado** (`status: "pending"` e sem `kycSubmittedAt`): depois disso o nome faz parte do registro em análise e do que o provedor recebeu, e a resposta é `422 subAccountNameLocked`. Pode vir junto de `platformFee` no mesmo corpo | O webhook `seller.updated` sai para o vendedor (e para a plataforma, com `account`). Uma sub-conta usa **uma** forma de taxa: ao atribuir um plano, a taxa simples deixa de valer; ao definir uma taxa simples numa sub-conta com plano, o plano continua atribuído e **prevalece** — retire o plano primeiro (`planId: null`) se a intenção é voltar à taxa simples. #### Alterar a taxa simples ``` PATCH /v1/marketplace/sub-accounts/{companyId} ``` ##### Corpo da Requisição ```json { "platformFee": { "type": "flat", "value": 300 } } ``` Para remover a taxa simples: ```json { "platformFee": null } ``` | Atributo | Tipo | Obrigatório | Descrição | |----------|------|-------------|-----------| | `platformFee` | object \| null | Sim | A nova taxa (`type`: `percentage` até 100, ou `flat` em centavos; `value` maior que 0), ou `null` para remover. Nenhum outro campo é aceito | ##### Resposta (200 OK) O objeto da sub-conta atualizado (campos em [Visão Geral](/docs/guide/marketplace/overview#estrutura-do-objeto-sub-conta)). #### Atribuir ou retirar um plano ``` PUT /v1/marketplace/sub-accounts/{companyId}/plan ``` ##### Corpo da Requisição ```json { "planId": "rate_01hqzvplan1" } ``` Para retirar o plano (a sub-conta volta à taxa simples, se houver): ```json { "planId": null } ``` | Atributo | Tipo | Obrigatório | Descrição | |----------|------|-------------|-----------| | `planId` | string \| null | Sim | Um [plano](/docs/guide/marketplace/plans) da sua plataforma (`rate_…`), ou `null` | ##### Resposta (200 OK) ```json { "id": "bus_01hqzvsub1", "planId": "rate_01hqzvplan1" } ``` #### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 400 | `invalidParameters` | Corpo inválido (campo desconhecido, taxa fora da faixa) | | 404 | `subAccountNotFound` | O `companyId` não é uma sub-conta da sua plataforma | | 422 | `subAccountNameLocked` | `name` numa sub-conta cujo cadastro já foi enviado | | 404 | `planNotFound` | O `planId` não é um plano da sua plataforma | | 409 | `accountLevelReadOnlyInSandbox` | Escrita com chave de sandbox | #### Exemplo ```bash curl -X PUT "https://api.selectwin.io/v1/marketplace/sub-accounts/bus_01hqzvsub1/plan" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "planId": "rate_01hqzvplan1" }' ``` --- ## Marketplace - Operar como a sub-conta Fonte: https://selectwin.io/docs/guide/marketplace/acting-as #### Visão Geral Uma venda feita com `onBehalfOf` pertence à **sub-conta**: é ela o vendedor de registro, é o saldo dela que recebe. Para consultar, capturar ou estornar essa venda, ler o saldo e os recebíveis da sub-conta, ou pedir um saque para ela, a plataforma **age como a sub-conta** com o cabeçalho `X-Selectwin-Account`. ```bash curl "https://api.selectwin.io/v1/transactions/tra_01hqzvtx1" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "X-Selectwin-Account: bus_01hqzvsub1" ``` Com o cabeçalho, a requisição roda **na sub-conta** indicada: os dados, as vendas, o saldo, os recebíveis, as carteiras e os saques são os dela, e a resposta diz isso em `merchant` (`merchantId` é a sub-conta e `isSubAccount` é `true`). A credencial continua sendo a sua chave de API, com as permissões dela. #### Onde vale Em toda a API pública, exceto no próprio recurso Marketplace (`/v1/marketplace/*`, que já é da plataforma) e nas leituras de referência (`/v1/utils`, `/v1/simulators`, `/v1/public`). Os usos típicos: | Para | Chame | |------|-------| | Ver a venda feita em nome da sub-conta | `GET /v1/transactions/{transactionId}` · `GET /v1/transactions` | | Capturar ou estornar essa venda | `POST /v1/transactions/{transactionId}/capture` · `…/refund` | | Saldo e extrato da sub-conta | `GET /v1/balance` · `GET /v1/balance/history` | | Recebíveis da sub-conta | `GET /v1/receivables` | | Carteiras e saques da sub-conta | `/v1/wallets` · `/v1/withdrawals` | | Clientes, cartões e assinaturas registrados na sub-conta | `/v1/customers` · `/v1/cards` · `/v1/subscriptions` | Um saque pedido assim vai para a **conta bancária da sub-conta** (a carteira dela), nunca para a plataforma. A taxa da plataforma continua chegando pelo caminho de sempre: o split na venda. #### Regras - **Só chave de API.** O cabeçalho é aceito apenas com `SelectKey`. Com qualquer outra credencial, a requisição é recusada. - **Só sub-conta sua.** A conta tem de ser uma sub-conta da sua plataforma (a mesma regra do `onBehalfOf`). Caso contrário a resposta é `401 actingAccountInvalid` — o cabeçalho nunca é ignorado em silêncio, para uma requisição que queria a sub-conta não rodar na sua conta por engano. - **A sua plataforma precisa estar ativa.** Uma plataforma bloqueada, desabilitada ou ainda em análise não opera pelas sub-contas: com o cabeçalho a resposta é `401 actingAccountInvalid`, mesmo com a sub-conta ativa — o mesmo que já valia para a venda com `onBehalfOf`. - **Os bloqueios da sua plataforma valem na sub-conta.** Um recurso que a Selectwin bloqueou para a sua plataforma continua recusado (403) quando ela age pela sub-conta, somado aos bloqueios da própria sub-conta. - **A sub-conta precisa estar ativa** para a maior parte das operações, como se o próprio vendedor chamasse: uma sub-conta `pending`, em análise ou suspensa responde `403 companyNotActive` fora do cadastro. - **Ambiente:** o da sua chave (`sk_live_` ou `sk_test_`). As vendas de teste da sub-conta ficam no sandbox dela. - **Idempotência:** o `X-Idempotency-Key` vale por sub-conta — a mesma chave em sub-contas diferentes são operações diferentes. - **Limites de uso:** contam para a plataforma, não para cada sub-conta. - **Auditoria:** a ação fica registrada na sub-conta, com a sua plataforma identificada como quem agiu. #### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 401 | `actingAccountInvalid` | O cabeçalho veio com uma credencial que não é chave de API, com uma conta que não é sub-conta da sua plataforma, ou a sua plataforma não está ativa | | 403 | `companyNotActive` | A sub-conta não está ativa (cadastro em aberto, em análise ou suspensa) | #### Exemplo: criar, acompanhar e estornar ```bash # 1. a venda, em nome da sub-conta curl -X POST "https://api.selectwin.io/v1/transactions" \ -H "SelectKey: sk_live_…" -H "Content-Type: application/json" \ -d '{ "amount": 10000, "payment": { "method": "pix" }, "customer": { "id": "cus_…" }, "onBehalfOf": "bus_01hqzvsub1" }' # 2. a mesma venda, lida como a sub-conta curl "https://api.selectwin.io/v1/transactions/tra_01hqzvtx1" \ -H "SelectKey: sk_live_…" -H "X-Selectwin-Account: bus_01hqzvsub1" # 3. o estorno, como a sub-conta curl -X POST "https://api.selectwin.io/v1/transactions/tra_01hqzvtx1/refund" \ -H "SelectKey: sk_live_…" -H "X-Selectwin-Account: bus_01hqzvsub1" \ -H "X-Idempotency-Key: 9c0d…" -H "Content-Type: application/json" -d '{}' ``` --- ## Sub-contas - Excluir (pedir o encerramento) Fonte: https://selectwin.io/docs/guide/marketplace/close #### Visão Geral Excluir uma sub-conta é **pedir o encerramento** dela à Selectwin. O pedido abre um registro de encerramento; a Selectwin confere o que ainda prende a conta (saldo a liquidar, vendas em andamento, a janela de contestação das últimas vendas) e executa: chaves de API revogadas, cadastro no provedor de pagamento encerrado, assinaturas canceladas, webhooks desligados, domínios suspensos e a conta desativada. Até a execução, a sub-conta continua como está, e você pode desistir. É um pedido, e não um botão, pelo mesmo motivo que vale para a sua própria conta: a API vê o que está aberto **agora**, mas um chargeback pode chegar semanas depois de uma venda, e uma conta já encerrada que o recebe fica com saldo negativo sem ninguém para acertar. Por isso uma pessoa fica entre a intenção e o irreversível. > Para tirar uma sub-conta de operação **sem** encerrá-la (um vendedor em revisão, por exemplo), use [Suspender](/docs/guide/marketplace/suspend). #### Precauções - Só a plataforma dona da sub-conta pede o encerramento dela. Um segundo pedido com um já aberto devolve o mesmo pedido, sem erro. - O `reason` fica no registro e na trilha de auditoria. O vendedor é avisado da desativação quando ela acontecer, não do motivo. - Enquanto o pedido está `requested`, a sub-conta opera normalmente. Se quiser que ela pare de cobrar já, [suspenda](/docs/guide/marketplace/suspend) e peça o encerramento. - Resolva os `blockers` antes, ou o pedido fica parado: a Selectwin não encerra uma conta com saldo a pagar ao vendedor ou vendas em andamento. - Recurso de conta: chave `sk_live_` (com `sk_test_`, `409 accountLevelReadOnlyInSandbox`). O pedido é idempotente (`X-Idempotency-Key`). #### Pedir o encerramento ``` POST /v1/marketplace/sub-accounts/{companyId}/closure-request ``` ##### Corpo da Requisição ```json { "reason": "Vendedor encerrou o contrato com a plataforma em 15/10." } ``` | Atributo | Tipo | Obrigatório | Descrição | |----------|------|-------------|-----------| | `reason` | string | Sim | Por que a sub-conta está sendo encerrada (1–500 caracteres) | ##### Resposta (200 OK) ```json { "id": "clo_01hqzvclo1", "status": "requested", "reason": "Vendedor encerrou o contrato com a plataforma em 15/10.", "blockers": [ { "reason": "settleableBalance", "detail": "125000 cents still owed to the seller" } ], "decisionNote": null, "decidedAt": null, "createdAt": "2026-10-17T18:00:00.000Z" } ``` | Campo | Descrição | |-------|-----------| | `id` | O pedido (`clo_…`) | | `status` | `requested` (aguardando a Selectwin), `running` (executando), `completed` (encerrada), `blocked` (parado pelos bloqueadores), `failed` (uma etapa falhou; a Selectwin retoma), `rejected` (recusado) ou `canceled` (você desistiu) | | `blockers[]` | O que impede a execução **agora**, recalculado a cada leitura: `settleableBalance` (saldo a pagar ao vendedor, em centavos no `detail`) e `openTransactions` (vendas em andamento). Vazio = nada prende | | `decisionNote` · `decidedAt` | Por que e quando a Selectwin recusou, quando recusou | #### Consultar o pedido ``` GET /v1/marketplace/sub-accounts/{companyId}/closure-request ``` Devolve o pedido mais recente da sub-conta, com os bloqueadores de agora. Sem pedido nenhum, `404 closureRequestNotFound`. #### Desistir ``` DELETE /v1/marketplace/sub-accounts/{companyId}/closure-request ``` Só enquanto o pedido está `requested`. Depois que a execução começa não há o que desistir (chaves já revogadas, assinaturas já canceladas): `409 closureRequestNotCancelable`. #### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 404 | `subAccountNotFound` | O `companyId` não é uma sub-conta da sua plataforma | | 404 | `closureRequestNotFound` | Consulta ou desistência sem pedido aberto | | 409 | `closureRequestNotCancelable` | A execução já começou | | 409 | `accountLevelReadOnlyInSandbox` | Escrita com chave de sandbox | #### Exemplo ```bash curl -X POST "https://api.selectwin.io/v1/marketplace/sub-accounts/bus_01hqzvsub1/closure-request" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -H "X-Idempotency-Key: 8e1c…" \ -d '{ "reason": "Vendedor encerrou o contrato com a plataforma em 15/10." }' ``` --- ## Sub-contas - Suspender e reabilitar Fonte: https://selectwin.io/docs/guide/marketplace/suspend #### Visão Geral A plataforma pode **suspender** uma sub-conta ativa (ela para de cobrar e de sacar) e **reabilitar** o que ela mesma suspendeu. É a ferramenta para um vendedor com disputas em série, um contrato encerrado ou uma investigação em curso. Uma suspensão pela Selectwin (análise de risco ou compliance) é diferente: aparece em [Andamento do cadastro](/docs/guide/marketplace/onboarding) com `lastStatusChange.by: "staff"` e só a Selectwin a desfaz. Para encerrar a sub-conta de vez, veja [Excluir](/docs/guide/marketplace/close): é um pedido de encerramento, executado pela Selectwin. #### Precauções - Suspender muda o status da sub-conta para `disabled`: toda cobrança nova dela é recusada (inclusive as suas com `onBehalfOf`), os saques param, e as assinaturas dela não cobram os próximos ciclos. O saldo fica guardado. - O vendedor é **avisado** (no painel e por e-mail) de que a conta foi suspensa e desde quando. O `reason` que você envia **não** vai para ele: fica no histórico da conta e na trilha de auditoria. - Os sistemas do vendedor recebem `seller.disabled` e, na volta, `seller.reinstated`. A sua plataforma recebe os mesmos eventos, com `account` apontando a sub-conta. - Só uma sub-conta `active` pode ser suspensa; só uma sub-conta que **a sua plataforma** suspendeu pode ser reabilitada por aqui. - As duas operações são idempotentes (`X-Idempotency-Key`). Recurso de conta: chave `sk_live_` (com `sk_test_`, `409 accountLevelReadOnlyInSandbox`). #### Suspender ``` POST /v1/marketplace/sub-accounts/{companyId}/suspend ``` ##### Corpo da Requisição ```json { "reason": "Três chargebacks em sete dias; contrato em revisão." } ``` | Atributo | Tipo | Obrigatório | Descrição | |----------|------|-------------|-----------| | `reason` | string | Sim | Por quê (1–500 caracteres). Registro interno: vai ao histórico e à auditoria, nunca ao vendedor | ##### Resposta (200 OK) ```json { "id": "bus_01hqzvsub1", "status": "disabled", "suspended": true } ``` #### Reabilitar ``` POST /v1/marketplace/sub-accounts/{companyId}/reinstate ``` Sem corpo. A sub-conta volta a `active` e opera de novo na hora. ##### Resposta (200 OK) ```json { "id": "bus_01hqzvsub1", "status": "active", "reinstated": true } ``` #### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 404 | `subAccountNotFound` | O `companyId` não é uma sub-conta da sua plataforma | | 422 | `subAccountNotSuspendable` | A sub-conta não está `active` (ainda em cadastro, em análise, ou já suspensa) | | 422 | `subAccountNotReinstatable` | A sub-conta não está suspensa pela sua plataforma — a suspensão é da Selectwin, ou ela não está `disabled`. Fale com o suporte | | 409 | `subAccountStatusConflict` | O status mudou enquanto a requisição rodava (a análise, o vendedor). Consulte e repita | | 409 | `accountLevelReadOnlyInSandbox` | Escrita com chave de sandbox | #### Exemplo ```bash curl -X POST "https://api.selectwin.io/v1/marketplace/sub-accounts/bus_01hqzvsub1/suspend" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -H "X-Idempotency-Key: 5d2b…" \ -d '{ "reason": "Três chargebacks em sete dias; contrato em revisão." }' ``` --- ## Sub-contas - Link de credenciamento (KYC) Fonte: https://selectwin.io/docs/guide/marketplace/kyc-link #### Visão Geral O link de credenciamento é a forma de o **vendedor** completar o cadastro da sub-conta sem ter login na Selectwin: você gera o link e o envia a ele (por e-mail, WhatsApp, pelo seu próprio sistema); ele abre uma página hospedada pela Selectwin, preenche os dados da empresa e do responsável, envia os documentos e a conta de saque, e o cadastro entra em análise. Você acompanha pelos webhooks `seller.*` ou por [consulta](/docs/guide/marketplace/read). #### Precauções > **ATENÇÃO!** O link é uma credencial. - O `token` (e a `url`, que o contém) aparece **uma vez só**, na resposta da geração. Ele não é devolvido depois e não aparece na consulta de status. Guarde-o só o tempo de enviar ao vendedor e **não o registre em logs**. - Quem tem o link preenche o cadastro da sub-conta. Envie-o apenas ao vendedor, por um canal que ele controle. - O link vale por **7 dias**. Gerar um novo link **revoga o anterior**. - Só uma sub-conta ainda **não ativa** recebe link. Para uma sub-conta `active`, a resposta é `422 subAccountAlreadyActive`. - Recurso de conta: gere e revogue links com uma chave `sk_live_` (com `sk_test_`, `409 accountLevelReadOnlyInSandbox`). > **Prefere a sua própria tela?** Os mesmos passos existem pela API, com o `token` do link no caminho: veja > [Credenciamento pela sua própria tela](/docs/guide/marketplace/kyc-steps). #### Gerar o link ``` POST /v1/marketplace/sub-accounts/{companyId}/kyc-link ``` Sem corpo. Gera um link novo (e revoga o que estivesse ativo). ##### Resposta (201 Created) ```json { "id": "kycl_01hqzvlink1", "token": "4f8c…e21b", "url": "https://…/marketplace/kyc/4f8c…e21b", "expiresAt": "2026-10-24T12:00:00.000Z", "status": "active" } ``` | Campo | Descrição | |-------|-----------| | `id` | Identificador do link (`kycl_…`) — serve para reconhecê-lo na consulta de status; não dá acesso a nada | | `token` | O segredo do link. Mostrado **só aqui** | | `url` | A página que o vendedor abre. Envie esta URL a ele | | `expiresAt` | Quando o link deixa de valer (7 dias) | | `status` | Sempre `active` na geração | #### Consultar o status do link ``` GET /v1/marketplace/sub-accounts/{companyId}/kyc-link ``` ##### Resposta (200 OK) ```json { "active": true, "id": "kycl_01hqzvlink1", "expiresAt": "2026-10-24T12:00:00.000Z" } ``` Sem link ativo: `{ "active": false, "id": null, "expiresAt": null }`. O token nunca é devolvido aqui. #### Revogar o link ``` DELETE /v1/marketplace/sub-accounts/{companyId}/kyc-link ``` Invalida o link ativo na hora (um link vazado, ou enviado por engano). Gere outro quando precisar. ##### Resposta (200 OK) ```json { "id": "bus_01hqzvsub1", "revoked": true } ``` #### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 404 | `subAccountNotFound` | O `companyId` não é uma sub-conta da sua plataforma | | 422 | `subAccountAlreadyActive` | A sub-conta já está ativa — o link é um instrumento de credenciamento | | 409 | `accountLevelReadOnlyInSandbox` | Escrita com chave de sandbox | #### Depois do envio Quando o vendedor termina, a sub-conta passa a `analyzing` (webhook `seller.analyzing`, `kycSubmittedAt` preenchido). O resultado chega por `seller.approved` (a sub-conta fica `active`) ou `seller.refused`. Se a análise pedir documentos de novo, `seller.kycRequired` (status `awaiting-documents`): gere um novo link e peça ao vendedor que reenvie. #### Exemplo ```bash curl -X POST "https://api.selectwin.io/v1/marketplace/sub-accounts/bus_01hqzvsub1/kyc-link" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` --- ## Marketplace - Credenciamento pela sua própria tela Fonte: https://selectwin.io/docs/guide/marketplace/kyc-steps #### Visão Geral O [link de credenciamento](/docs/guide/marketplace/kyc-link) abre uma página hospedada pela Selectwin. Se a sua plataforma prefere que o vendedor preencha o cadastro **dentro do seu produto**, use os mesmos passos pela API: o `token` do link é a credencial, e cada chamada leva o token no caminho, sem chave de API. ``` GET /v1/marketplace/kyc/{token} valida o link e devolve o nome da sub-conta GET /v1/marketplace/kyc/{token}/onboarding o cadastro como está (passo atual, o que falta) GET /v1/marketplace/kyc/{token}/onboarding/document-types tipos de documento que o passo 1 pode escolher GET /v1/marketplace/kyc/{token}/onboarding/document-requirements o que o passo 3 tem de entregar e em que pé está POST /v1/marketplace/kyc/{token}/onboarding/invite-code/validate confere um código de convite sem consumir POST /v1/marketplace/kyc/{token}/assets/uploads pede uma URL de upload para um documento POST /v1/marketplace/kyc/{token}/assets/uploads/{assetId}/confirm confirma o upload POST /v1/marketplace/kyc/{token}/onboarding/step-1 … step-6 os seis passos ``` Os corpos de cada passo estão na [referência OpenAPI](/api-specs/openapi.json) (operações `submitKycStep1` a `submitKycStep6`); abaixo, o que cada um faz e os campos que importam. #### Precauções - **O token é uma credencial.** Guarde-o só no seu servidor e nunca o exponha no navegador do vendedor nem em logs. Se a sua tela roda no navegador, o seu backend faz as chamadas. - O link vale **7 dias** e só para uma sub-conta **ainda não ativa**. Gerar um novo link invalida o anterior. - Os passos são os mesmos do cadastro normal: cada envio grava e pode ser reenviado para corrigir, **até o passo 6**, que fecha o cadastro e o põe em análise. - Os uploads têm limite por hora por sub-conta (`429 kycUploadLimitReached`). - Dados de pessoas (CPF, nascimento, endereço) são dados pessoais: colete só o que o passo pede e trate conforme a LGPD. #### Os passos | Passo | O que envia | Campos principais | |-------|-------------|-------------------| | 1 | O tipo de conta | `documentType` (`cpf` ou `cnpj`), `country`, `revenue` (faturamento mensal em centavos, mínimo 5000). CPF pode exigir `inviteCode` quando o cadastro de pessoa física está fechado — confira antes com `invite-code/validate` | | 2 | A identidade | **CNPJ:** `legalBusinessName`, `companyName` (nome fantasia, opcional), `document` (CNPJ), `address`, `phone` e `owner` (o representante: `firstName`, `lastName`, `document` CPF, `birthDate`, `phone`, `address` opcional). **CPF:** `firstName`, `lastName`, `birthDate`, `document`, `address`, `phone`, `isPoliticallyExposed` | | 3 | Os documentos | `documents[]` com `documentType` e `assetId` de cada arquivo enviado pelos uploads. O que é exigido vem de `document-requirements` — uma alternativa completa por exigência basta | | 4 | Contato e fatura do cartão | `softDescriptor` (até 13 caracteres, o que aparece na fatura do comprador), `email`, `phone` | | 5 | A conta de saque | `bankCode`, `routingNumber` (agência), `accountNumber`. Vira a carteira principal da sub-conta | | 6 | O fechamento | `category`, `description`, `monthlyBillingEstimate` (centavos, mínimo 5000), `website` e `referrer` opcionais. Fecha o cadastro: a sub-conta passa a `analyzing` e o webhook `seller.analyzing` sai | ##### Documentos: o fluxo de upload 1. `GET …/onboarding/document-requirements` — para cada exigência (`identity`, `selfie`, `proof_of_address_company`…), as alternativas aceitas e o `status` de cada uma. 2. `POST …/assets/uploads` com `{ filename, contentType, byteSize }` — responde a URL assinada e o `assetId`. Envie o arquivo para a URL. 3. `POST …/assets/uploads/{assetId}/confirm` — confirma. 4. `POST …/onboarding/step-3` com `documents: [{ documentType, assetId }]`. A resposta de `document-requirements` traz também `captureQuality`: a régua que uma foto de documento deve atingir (resolução, nitidez, exposição) para não ser recusada na análise. Use-a para avisar o vendedor enquanto ele ainda pode fotografar de novo. #### Depois do passo 6 Acompanhe como no fluxo pelo link: `seller.analyzing` → `seller.approved` (a sub-conta fica `active`) ou `seller.refused`; se a análise pedir documentos de novo, `seller.kycRequired` e a sub-conta volta a `awaiting-documents`. A plataforma lê o estado a qualquer momento em [Andamento do cadastro](/docs/guide/marketplace/onboarding), sem o token. #### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 404 | `kycLinkInvalid` | Token inválido, vencido ou revogado — ou a sub-conta já está ativa | | 400 | `invalidParameters` | Corpo do passo inválido (`error.params` diz o campo) | | 422 | (regra do passo) | Uma regra do cadastro, por exemplo um documento que não serve para a exigência | | 429 | `kycUploadLimitReached` | Uploads demais pelo link na última hora | --- ## Sub-contas - Andamento do cadastro Fonte: https://selectwin.io/docs/guide/marketplace/onboarding #### Visão Geral Mostra em que pé está o credenciamento de uma sub-conta: o status, o passo do cadastro, cada documento exigido e o estado dele, e a última mudança de status (para onde, quando e quem). É o que a plataforma consulta quando a sub-conta não avança: um cadastro parado, documentos recusados ou pedidos de novo, uma suspensão. #### Requisição ``` GET /v1/marketplace/sub-accounts/{companyId}/onboarding ``` #### Resposta ##### Sucesso (200 OK) ```json { "id": "bus_01hqzvsub1", "status": "awaiting-documents", "reviewDueAt": "2026-10-22T12:00:00.000Z", "onboarding": { "currentStep": "6", "isCompleted": true, "completedAt": "2026-10-17T15:20:11.000Z" }, "documents": { "documentType": "cnpj", "complete": false, "requirements": [ { "key": "identity", "subject": "holder", "required": true, "status": "rejected", "alternatives": [ { "key": "cnh_front+cnh_back", "status": "rejected", "documents": [ { "type": "cnh_front", "status": "rejected", "rejectionReason": "image_unreadable" }, { "type": "cnh_back", "status": "approved" } ] }, { "key": "rg_front+rg_back", "status": "missing", "documents": [ { "type": "rg_front", "status": "missing" }, { "type": "rg_back", "status": "missing" } ] } ] }, { "key": "cnpj_card", "subject": "company", "required": true, "status": "approved", "alternatives": [ { "key": "cnpj_card", "status": "approved", "documents": [ { "type": "cnpj_card", "status": "approved" } ] } ] } ] }, "lastStatusChange": { "from": "analyzing", "to": "awaiting-documents", "by": "staff", "at": "2026-10-18T10:02:45.000Z" } } ``` | Campo | Descrição | |-------|-----------| | `status` | O status da sub-conta (`pending`, `analyzing`, `awaiting-documents`, `active`, `blocked`, `disabled`) | | `reviewDueAt` | Até quando a análise responde, quando há um prazo; `null` fora da análise | | `onboarding.currentStep` · `isCompleted` · `completedAt` | O passo em que o cadastro está, se foi fechado (passo 6) e quando | | `documents.documentType` | `cpf` ou `cnpj`, ou `null` antes do passo 1 | | `documents.complete` | Toda exigência obrigatória tem uma alternativa enviada ou aprovada | | `documents.requirements[]` | Cada exigência: `key` (o que é), `subject` (`holder` = a pessoa, `company` = a empresa), `required`, `status` (`approved` > `pending` > `rejected` > `missing`) e as `alternatives[]` — **uma** alternativa completa basta; cada documento dela traz `type`, `status` e, quando recusado, `rejectionReason` | | `lastStatusChange` | A última transição: `from`, `to`, `at` e `by` — `staff` (a análise da Selectwin), `provider` (o provedor de pagamento), `seller` (o próprio vendedor, ao enviar o cadastro ou os documentos), `platform` (a sua plataforma, por [suspensão ou reabilitação](/docs/guide/marketplace/suspend)) ou `system`. `null` sem histórico | > **O motivo da análise não é exposto.** O texto que o analista escreve ao recusar ou pedir documentos é registro interno. O que esta leitura diz é **o que** aconteceu, **desde quando** e **o que falta**: os documentos recusados vêm marcados com `rejectionReason`, e o prazo em `reviewDueAt`. Dúvidas sobre uma decisão são tratadas com o suporte. #### Como usar - `status: "pending"` com `onboarding.isCompleted: true` — o vendedor terminou, mas faltou um dado essencial e o cadastro **não entrou** na análise. Peça que complete pelo [link](/docs/guide/marketplace/kyc-link) (gere um novo, se o anterior venceu). - `status: "awaiting-documents"` — veja em `documents.requirements[]` quais estão `rejected` ou `missing` e peça exatamente esses ao vendedor. - `status: "analyzing"` — aguarde `seller.approved` ou `seller.refused`; `reviewDueAt` diz até quando. - `status: "disabled"` com `lastStatusChange.by: "platform"` — foi a sua plataforma que suspendeu; [reabilite](/docs/guide/marketplace/suspend) quando quiser. Com `by: "staff"`, a suspensão é da Selectwin. #### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 404 | `subAccountNotFound` | O `companyId` não é uma sub-conta da sua plataforma | --- ## Marketplace - Planos de taxas Fonte: https://selectwin.io/docs/guide/marketplace/plans #### Visão Geral Um **plano de taxas** é uma tabela que a plataforma aplica a um grupo de sub-contas: a taxa por meio de pagamento (Pix, boleto, crédito, débito), a taxa por quantidade de parcelas no crédito, o **prazo de liberação** de cada meio e a **tarifa de saque**. Em vez de uma taxa simples por sub-conta ([`platformFee`](/docs/guide/marketplace/create)), você cria o plano uma vez e o [atribui](/docs/guide/marketplace/update#atribuir-ou-retirar-um-plano) a quantas sub-contas quiser. Nas vendas de uma sub-conta com plano, a plataforma recolhe o que o plano define para aquele meio (e parcelas); os recebíveis da sub-conta seguem o prazo de liberação do plano; e cada saque dela paga a tarifa do plano. #### Precauções - **Mesma forma que a sua taxa.** Para cada meio, o plano só cobra na **mesma forma** (percentual ou fixo) que a taxa da sua própria conta usa naquele meio; caso contrário, `422 planFeeTypeMismatch` com `params: [{ "method": "credit", "field": "percent" }]`. Se a sua conta ainda não tem taxa configurada, `422 platformRatesMissing`. - **Prazo de liberação nunca menor que o seu.** O `releaseDays` de um meio não pode ser menor que o prazo em que a sua plataforma recebe aquele meio (a sub-conta não pode ser paga antes de você): `422 planReleaseBelowPlatform`, com o mínimo por campo em `params`. - **Plano em uso não é apagado.** `DELETE` de um plano atribuído a sub-contas responde `409 planInUse` com `params[0].subAccounts` (quantas o usam). Mova-as para outro plano (ou retire o plano) antes. - Alterar um plano vale para as **próximas** cobranças de todas as sub-contas que o usam. - Recurso de conta: crie e altere planos com uma chave `sk_live_` (com `sk_test_`, `409 accountLevelReadOnlyInSandbox`). #### Estrutura do Objeto Plano ```json { "id": "rate_01hqzvplan1", "name": "Vendedores Prata", "pix": { "percent": 1.5, "fixed": 0, "releaseDays": 1 }, "boleto": { "percent": 0, "fixed": 250, "releaseDays": 2 }, "credit": { "percent": 4.5, "fixed": 0, "installments": [ { "n": 2, "percent": 5.5 }, { "n": 3, "percent": 6.5 } ], "releaseDays": 30 }, "debit": { "percent": 2, "fixed": 0, "releaseDays": 1 }, "withdrawalFee": 350, "createdAt": "2026-10-17T12:00:00.000Z" } ``` | Campo | Descrição | |-------|-----------| | `id` | Identificador do plano (`rate_…`) — o `planId` das sub-contas | | `name` | Nome do plano | | `pix` · `boleto` · `debit` | Por meio: `percent` (percentual da venda), `fixed` (centavos por venda) e `releaseDays` (dias até o valor ficar disponível para a sub-conta; `null` = o prazo padrão) | | `credit` | Idem, mais `installments[]`: o percentual por quantidade de parcelas (`n`), ou `null` quando não há tabela por parcela | | `withdrawalFee` | Tarifa fixa por saque da sub-conta, em centavos; `null` quando não definida | #### Criar um plano ``` POST /v1/marketplace/plans ``` ##### Corpo da Requisição ```json { "name": "Vendedores Prata", "pix": { "percent": 1.5, "releaseDays": 1 }, "boleto": { "fixed": 250, "releaseDays": 2 }, "credit": { "percent": 4.5, "installments": [ { "n": 2, "percent": 5.5 }, { "n": 3, "percent": 6.5 } ], "releaseDays": 30 }, "debit": { "percent": 2, "releaseDays": 1 }, "withdrawalFee": 350 } ``` | Atributo | Tipo | Obrigatório | Descrição | |----------|------|-------------|-----------| | `name` | string | Sim | Nome do plano (1–100 caracteres) | | `pix` · `boleto` · `debit` | object | Não | `percent` (0–100), `fixed` (centavos, inteiro ≥ 0), `releaseDays` (0–365). Todos opcionais | | `credit` | object | Não | Os mesmos campos, mais `installments[]` (até 36 itens `{ n: 1–36, percent: 0–100 }`) | | `withdrawalFee` | integer | Não | Tarifa de saque em centavos (≥ 0) | ##### Resposta (201 Created) O objeto do plano. #### Listar e consultar ``` GET /v1/marketplace/plans GET /v1/marketplace/plans/{planId} ``` A lista responde `{ "data": [ …planos ], "count": 2 }`; a consulta responde o objeto do plano. #### Alterar um plano ``` PATCH /v1/marketplace/plans/{planId} ``` Mesmos campos da criação, todos opcionais; campos desconhecidos são recusados com `400`. Responde o plano atualizado. #### Excluir um plano ``` DELETE /v1/marketplace/plans/{planId} ``` Responde `{ "id": "rate_01hqzvplan1", "deleted": true }`. Só um plano **sem** sub-contas pode ser excluído. #### Atribuir a uma sub-conta `PUT /v1/marketplace/sub-accounts/{companyId}/plan` com `{ "planId": "rate_…" }` (ou `null` para retirar) — veja [Atualizar](/docs/guide/marketplace/update#atribuir-ou-retirar-um-plano). Na criação da sub-conta, envie `planId` direto ([Criar](/docs/guide/marketplace/create)). #### Erros | HTTP | `error.code` | Quando | |-----:|--------------|--------| | 400 | `invalidParameters` | Corpo inválido (faixa, tipo, campo desconhecido no `PATCH`) | | 404 | `planNotFound` | O `planId` não é um plano da sua plataforma | | 409 | `planInUse` | Exclusão de um plano atribuído a sub-contas (`params[0].subAccounts`) | | 409 | `accountLevelReadOnlyInSandbox` | Escrita com chave de sandbox | | 422 | `planFeeTypeMismatch` | Um meio cobrado numa forma (percentual/fixo) diferente da sua taxa naquele meio (`params: [{ method, field }]`) | | 422 | `platformRatesMissing` | A sua conta ainda não tem taxa configurada | | 422 | `planReleaseBelowPlatform` | Um `releaseDays` menor que o prazo em que a sua plataforma recebe aquele meio (`params` traz o mínimo por campo) | #### Exemplo ```bash curl -X POST "https://api.selectwin.io/v1/marketplace/plans" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Vendedores Prata", "pix": { "percent": 1.5, "releaseDays": 1 }, "credit": { "percent": 4.5, "releaseDays": 30 }, "withdrawalFee": 350 }' ``` --- ## Produtos e Variantes - Visão Geral Fonte: https://selectwin.io/docs/guide/products/overview #### Introdução O recurso de **Produtos (Products)** representa seu catálogo. Cada produto agrupa uma ou mais **Variantes (Variants)** — e é a **variante** que carrega a precificação. Um produto "Plano Premium" pode ter as variantes "Mensal", "Anual" e "Vitalício", cada uma com seu preço e regras de cobrança. As variantes são o que você referencia em [Transações](/docs/guide/transactions/overview) (variantes `oneTime`) e em [Assinaturas](/docs/guide/subscriptions/overview) (variantes `recurring`). #### Estrutura do Produto ```json { "id": "prd_01hqzvabc", "name": "Plano Premium", "slug": "plano-premium", "salesPage": "https://example.com/sales", "description": "Full access plan", "language": "pt-BR", "enabled": true, "salesQty": 42, "type": "digital", "category": "saas", "externalReference": "SKU-EXT-1", "warrantyDays": 7, "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": "Mensal", "slug": "mensal", "description": "Monthly billing", "sku": "SKU-M", "primary": true, "enabled": true, "pricing": { "unitPrice": 9900, "oldPrice": null, "costPerItem": null, "currency": "BRL", "schema": "unit", "type": "recurring", "billingType": "prepaid", "billingFrequency": "monthly", "billingFrequencyCount": 1 }, "images": null, "metadata": null, "attributes": null, "externalReference": null, "checkoutUrl": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ] } ``` > Respostas de produto e de variante **não incluem** `merchant` nem `_links`. Valores monetários > (`unitPrice`, `oldPrice`, `costPerItem`) são inteiros em **centavos** (BRL). Datas em ISO 8601. ##### Campos do produto | Campo | Tipo | Descrição | |-------|------|-----------| | `id` | string | Identificador público (`prd_...`; ids legados `pro_` também são lidos) | | `name` | string | Nome do produto | | `slug` | string \| null | Identificador legível para URLs (**gerado pela API** a partir do `name`) | | `salesPage` | string \| null | Página de vendas | | `description` | string \| null | Descrição | | `language` | string \| null | Idioma (`pt-BR`, …) | | `enabled` | boolean | Se o produto está ativo. **Desligado, o produto não vende**: suas variantes deixam de resolver no carrinho do checkout, num link de pagamento novo e numa assinatura nova (`422 variantIdIsInvalid` / `404 variantNotFound`), mesmo com a variante ligada. As variantes guardam o próprio `enabled`: ao religar o produto, voltam como estavam. Assinaturas já contratadas continuam cobrando | | `salesQty` | integer \| null | Quantidade vendida (somente leitura) | | `type` | string | Tipo: `physical`, `digital` ou `other` | | `category` | string | Categoria de negócio | | `externalReference` | string \| null | Sua referência externa | | `warrantyDays` | integer | Dias de garantia | | `images` | array \| null | URLs de imagens | | `imageAssetIds` | qualquer \| null | Referências internas das imagens hospedadas | | `fiscal` | object \| null | O perfil fiscal (NCM, CEST, GTIN, origem, unidade, LC 116, NBS…) — ver [Criar](/docs/guide/products/create#perfil-fiscal-fiscal) | | `createdAt` / `updatedAt` | string (ISO 8601) | Datas | | `variants` | array | Variantes do produto (com `pricing`) | #### Modelo de Precificação (dual-axis) A precificação vive na **variante**, em `pricing`, organizada em **dois eixos independentes**: ##### Eixo 1 — `type` (natureza da cobrança) · **imutável** | `pricing.type` | Significado | Onde é usada | |----------------|-------------|--------------| | `oneTime` | Cobrança única (**default**) | [Transações](/docs/guide/transactions/overview) | | `recurring` | Cobrança recorrente | [Assinaturas](/docs/guide/subscriptions/overview) | - Uma variante `oneTime` **não** pode ser usada em assinatura (a assinatura exige variante recorrente) → `variantNotRecurring` (422). - O `type` é **imutável** após a criação → `pricingTypeImmutable` (422). ##### Eixo 2 — `schema` (como o preço é calculado) · **imutável** | `pricing.schema` | Significado | |------------------|-------------| | `unit` | Preço por unidade — `unitPrice` × `quantity` (**default**) | | `tiered` | Preço por faixas (`tiersMode` + `tiers[]`) | | `package` | Preço por pacote (`transformQuantity`) | > O `schema` é **imutável** após a criação → `pricingSchemaImmutable` (422). Configurações de > precificação inconsistentes com o `schema` escolhido retornam `invalidPricing` (422). ##### Campos de `pricing` | Campo | Aplica a | Descrição | |-------|----------|-----------| | `unitPrice` | ambos | Preço unitário em centavos (mín. 1; em `schema=unit`, mín. 500). Obrigatório exceto em `schema=tiered` | | `oldPrice` | ambos | Preço "de" (riscado/promocional), em centavos | | `costPerItem` | ambos | Custo do item em centavos (para margem; mín. 0) | | `currency` | ambos | ISO 4217 — apenas `BRL` (default `BRL`) | | `schema` | ambos | `unit` \| `tiered` \| `package` (default `unit`, imutável) | | `type` | ambos | `oneTime` \| `recurring` (default `oneTime`, imutável) | | `daysTrial` | recurring | Dias de trial (1–365) | | `billingType` | recurring | `prepaid`, `postpaid` ou `exactday` (obrigatório quando `type=recurring`) | | `billingFrequency` | recurring | `daily`, `weekly`, `monthly`, `yearly` (obrigatório quando `type=recurring`) | | `billingFrequencyCount` | recurring | Multiplicador da frequência, 1–999 (obrigatório quando `type=recurring`) | | `billingExactDay` | recurring | Dia fixo de cobrança, 1–31 (obrigatório quando `billingType=exactday`) | | `cycles` | recurring | Nº de ciclos, 1–999 (`null` = indefinido) | | `trialInterval` | recurring | Unidade do trial: `day`, `week`, `month`, `year` | | `trialIntervalCount` | recurring | Duração do trial em unidades de `trialInterval` (1–365) | | `tiersMode` | tiered | `graduated` ou `volume` (obrigatório quando `schema=tiered`) | | `tiers` | tiered | Faixas `{ upTo, unitPrice, flatPrice }` (obrigatório quando `schema=tiered`) | | `transformQuantity` | package | `{ divideBy, round }` (obrigatório quando `schema=package`) | | `usageType` | recurring | `licensed` (default) ou `metered` | | `usageAggregation` | metered | `sum`, `max`, `lastDuringPeriod`, `lastEver` (obrigatório quando `usageType=metered`) | | `meterEventName` | metered | Nome do evento de medição (obrigatório quando `usageType=metered`) | | `renewalShipping` | recurring | Cobrar o frete em **cada renovação** (default `true`). Ver [regras da renovação](#regras-da-renovação) | | `renewalMethodDiscount` | recurring | Estender o desconto do método de pagamento às **renovações** (default `false`). Ver [regras da renovação](#regras-da-renovação) | > Em `usageType=metered`, a cobrança recorrente exige `billingType=postpaid`. Na **resposta**, `pricing` traz todos os campos acima (os não aplicáveis vêm `null`); `unitPrice`, `oldPrice` e `costPerItem` sempre presentes (centavos ou `null`). `renewalShipping` e `renewalMethodDiscount` vêm sempre como booleano: a regra que vale para o plano (a sua escolha, ou o default). ##### Regras da renovação Num plano recorrente vendido pelo checkout hospedado, a **1ª cobrança** é o total do pedido: itens + frete − cupons − desconto do método de pagamento (Pix, boleto ou cartão, conforme o template do checkout). As cobranças seguintes (as **renovações**) levam os itens − os cupons recorrentes e mais o que estas duas regras do plano dizem: | Regra | Default | Com `true` | Com `false` | |-------|---------|------------|-------------| | `renewalShipping` | `true` | O frete cotado no checkout entra em cada renovação (quando a compra teve frete) | O frete fica só na 1ª cobrança | | `renewalMethodDiscount` | `false` | O desconto do método vale em cada renovação **paga pelo mesmo método** (uma renovação no cartão não leva o desconto do Pix) | O desconto fica só na 1ª cobrança | - **Por plano.** Cada variante recorrente tem as suas regras: um plano físico e um digital da mesma loja podem ter regras diferentes. Num plano digital, sem frete, `renewalShipping` não muda nada. - **A assinatura congela as regras.** No momento da compra, a assinatura guarda as duas regras, o frete cotado e o percentual do desconto (o [`renewalTerms`](/docs/guide/subscriptions/read#atributos-da-resposta) dela). Mudar o plano depois vale só para quem assinar depois. - **O checkout mostra o valor de cada renovação** já com essas regras, antes do comprador pagar. - Assinaturas criadas **antes de 15/10/2026**, pela [API de assinaturas](/docs/guide/subscriptions/create) ou por um upsell não congelam regras: as renovações delas levam os itens − os cupons recorrentes. #### Recursos Disponíveis | Recurso | Método | Endpoint | Descrição | |---------|--------|----------|-----------| | [Criar produto](/docs/guide/products/create) | POST | `/v1/products` | Cria produto (com variantes inline) | | [Listar produtos](/docs/guide/products/list) | GET | `/v1/products` · `/v1/products/listall` | Lista paginada · array completo | | [Consultar produto](/docs/guide/products/read) | GET | `/v1/products/{productId}` | Produto + variantes | | [Atualizar / Excluir](/docs/guide/products/update) | PUT/PATCH/DELETE | `/v1/products/{productId}` | Atualiza ou remove | | [Variantes](/docs/guide/products/variants) | GET/POST/PATCH/DELETE | `/v1/products/{id}/variants` · `/v1/variants` | Gerencia variantes | #### Integração com Outros Recursos - **Transactions** — variantes `oneTime` são cobradas em transações avulsas. - **Subscriptions** — variantes `recurring` compõem os itens da assinatura. - **Coupons** — descontos podem ser limitados a produtos/variantes específicos. - **Checkouts** — links de pagamento referenciam variantes por `id`. #### Exportar produtos Use **`POST /v1/exports`** — job assíncrono com dedup, TTL e download assinado. O antigo `POST /v1/products/export` foi **APOSENTADO** e responde `410` apontando para o substituto. Ele nunca gerou arquivo nenhum: devolvia `{ succeeded: true }` a partir de uma função sem argumentos e sem saída — um controle que dizia existir. A rota continua registrada de propósito, para o `410` nomear o substituto em vez de o gateway devolver um `404` mudo. --- ## Criar Produto Fonte: https://selectwin.io/docs/guide/products/create `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](/docs/guide/products/overview#modelo-de-precificação-dual-axis)). Escopo requerido: `products:create`. Suporta chave de idempotência (`X-Idempotency-Key`). #### Requisição ```bash 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 } } ] }' ``` > O `slug` **não é aceito na requisição** — a API o gera automaticamente a partir do `name` > (tanto para o produto quanto para cada variante). ##### 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`](/docs/guide/products/overview#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. > A **primeira** variante do array é marcada como `primary: true` na resposta. ##### 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. > 🧩 Até 06/set/2026 o objeto era aceito **em silêncio** e nunca virava imagem: o produto ficava salvo > "com imagem" e a imagem não existia. Hoje a chamada responde `422` e diz o que enviar. 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 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": [""] ``` 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). > 🧩 O **checkout** cai para a imagem do produto quando a variante não tem a sua, e o mesmo instantâneo > alimenta o item da transação, o recibo e o webhook da venda. Ou seja: a venda mostra a foto mesmo com a > variante vazia — mas a leitura do catálogo continua dizendo a verdade, `null`. #### Resposta `201 Created` Retorna o produto completo com suas variantes (mesmo formato de [Consultar](/docs/guide/products/read)): ```json { "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](/docs/reference/codigos-de-erro). --- ## Listar Produtos Fonte: https://selectwin.io/docs/guide/products/list A API oferece duas formas de listar produtos: | Endpoint | Formato | Uso | |----------|---------|-----| | `GET /v1/products` | **paginado** (envelope) | Listagens com paginação/filtros | | `GET /v1/products/listall` | **array direto** (sem paginação) | Carregar o catálogo inteiro de uma vez (ex.: selects) | Ambos exigem o escopo `products:list` e retornam produtos completos (com `variants`). ##### Filtros (querystring) Aceitos por ambos os endpoints: | Parâmetro | Tipo | Descrição | |-----------|------|-----------| | `id` | string | Filtra por `id` do produto (`prd_...` / `pro_...`) | | `slug` | string | Filtra por slug (1–100 caracteres) | | `name` | string | Filtra por nome (1–40 caracteres) | | `language` | string | Filtra por idioma | | `category` | string | Filtra por categoria | | `enabled` | boolean | Filtra por ativo/inativo | | `externalreference` | string | Filtra por referência externa | | `daterange` (e `daterangegt/gte/lt/lte`) | string | Filtra por intervalo de datas | Paginação (`GET /v1/products`): `limit` default `20`, máx. `100`; `offset` default `0`. Em `listall`, `limit` default é `999999` e há `sort` (`ascending` \| `descending`, default `ascending`). --- #### Lista paginada `GET /v1/products` ```bash curl -X GET "https://api.selectwin.io/v1/products?limit=20&offset=0" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` ```json { "offset": 0, "limit": 20, "total": 1, "page": { "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 }, "current": 1, "total": 1 }, "hasMore": false, "data": [ { "id": "prd_01hqzvabc", "name": "Plano Premium", "slug": "plano-premium", "salesPage": "https://example.com/sales", "description": "Full access plan", "language": "pt-BR", "enabled": true, "salesQty": 42, "type": "digital", "category": "saas", "externalReference": null, "warrantyDays": 7, "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": "Mensal", "slug": "mensal", "description": "Monthly billing", "sku": "SKU-M", "primary": true, "enabled": true, "pricing": { "unitPrice": 9900, "oldPrice": null, "costPerItem": null, "currency": "BRL", "schema": "unit", "type": "recurring", "billingType": "prepaid", "billingFrequency": "monthly", "billingFrequencyCount": 1 }, "images": null, "metadata": null, "attributes": null, "externalReference": null, "checkoutUrl": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ] } ] } ``` > Cada item de `data` é o **produto completo** (incluindo `variants`) — o mesmo objeto de > [Consultar](/docs/guide/products/read). A lista paginada de produtos **não** inclui `merchant`/`_links`. Os campos de > `page.offset` (`first`, `prev`, `next`, `last`) são **sempre inteiros** (nunca `null`). Mais detalhes > em [Paginação](/docs/reference/paginacao). --- #### Lista completa (array direto) `GET /v1/products/listall` Retorna **um array** com todos os produtos (sem envelope de paginação), cada item com o objeto completo do produto (incluindo `variants`): ```json [ { "id": "prd_01hqzvabc", "name": "Plano Premium", "slug": "plano-premium", "salesPage": "https://example.com/sales", "description": "Full access plan", "language": "pt-BR", "enabled": true, "salesQty": 42, "type": "digital", "category": "saas", "externalReference": null, "warrantyDays": 7, "images": ["https://example.com/img1.png"], "imageAssetIds": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z", "variants": [ /* ... */ ] } ] ``` > `listall` é sempre um **array** no nível raiz — não há `offset`/`limit`/`total`/`data`. Use-o para > popular seletores ou caches locais; para volumes grandes, prefira a [lista paginada](#lista-paginada). --- ## Consultar Produto Fonte: https://selectwin.io/docs/guide/products/read `GET /v1/products/{productId}` Retorna o produto completo, incluindo todas as suas variantes com `pricing`. Escopo requerido: `products:read`. #### Requisição ```bash curl -X GET "https://api.selectwin.io/v1/products/prd_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` #### Resposta `200 OK` ```json { "id": "prd_01hqzvabc", "name": "Plano Premium", "slug": "plano-premium", "salesPage": "https://example.com/sales", "description": "Full access plan", "language": "pt-BR", "enabled": true, "salesQty": 42, "type": "digital", "category": "saas", "externalReference": null, "warrantyDays": 7, "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": "Mensal", "slug": "mensal", "description": "Monthly billing", "sku": "SKU-M", "primary": true, "enabled": true, "pricing": { "unitPrice": 9900, "oldPrice": null, "costPerItem": 5000, "currency": "BRL", "schema": "unit", "type": "recurring", "billingType": "prepaid", "billingFrequency": "monthly", "billingFrequencyCount": 1 }, "images": null, "metadata": null, "attributes": null, "externalReference": null, "checkoutUrl": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ] } ``` > Cada variante embutida no produto traz o objeto completo (incluindo `productId`, `slug`, `primary`, > `pricing.costPerItem`, `metadata`, `attributes` e `checkoutUrl`) — o mesmo objeto retornado na leitura > individual da variante (`GET /v1/variants/{variantId}`, veja [Variantes](/docs/guide/products/variants)). Campos não > aplicáveis ou ausentes vêm `null`. #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `productNotFound` | 404 | Produto não encontrado para esta conta | | `invalidParameters` | 400 | `productId` com prefixo/formato inválido | Para listar vários produtos, veja [Listar](/docs/guide/products/list). --- ## Atualizar e Excluir Produto Fonte: https://selectwin.io/docs/guide/products/update | Operação | Método | Endpoint | |----------|--------|----------| | Atualizar (parcial) | PATCH | `/v1/products/{productId}` | | Atualizar (alias) | PUT | `/v1/products/{productId}` | | Excluir | DELETE | `/v1/products/{productId}` | `PUT` e `PATCH` usam o **mesmo handler** e são equivalentes — ambos aplicam um patch parcial; envie apenas os campos que deseja alterar (ao menos um). Escopos: `products:update` / `products:delete`. --- #### Atualizar produto `PATCH` / `PUT /v1/products/{productId}` Atualiza apenas os **dados do produto**. As variantes **não** são alteradas por aqui — use os endpoints de [Variantes](/docs/guide/products/variants). O `slug` é regenerado automaticamente quando o `name` muda (não é aceito na requisição). Os campos aceitos são os mesmos da [criação](/docs/guide/products/create#campos-do-produto) (exceto `variants`), todos opcionais. > **`enabled: false` para a venda.** Um produto desligado não vende: o carrinho do checkout, um link de pagamento > novo e uma assinatura nova recusam as variantes dele, mesmo que cada variante siga com `enabled: true`. As variantes > **não** são alteradas — ao religar o produto, voltam a vender como estavam. Vendas já feitas e assinaturas já > contratadas não são afetadas. Para parar **uma** variante só, desligue a variante. ```bash curl -X PATCH "https://api.selectwin.io/v1/products/prd_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Premium Course", "description": "Curso completo (2026)", "enabled": true, "type": "digital", "language": "pt-BR", "category": "courses", "warrantyDays": 30, "salesPage": "https://loja.com/premium", "externalReference": "SKU-EXT-1" }' ``` ##### Resposta `200 OK` Retorna o produto completo (mesmo formato de [Consultar](/docs/guide/products/read)), já com as variantes: ```json { "id": "prd_01hqzvabc", "name": "Premium Course", "slug": "premium-course", "salesPage": "https://loja.com/premium", "description": "Curso completo (2026)", "language": "pt-BR", "enabled": true, "salesQty": 42, "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-12T18:00:00.000Z", "variants": [ { "id": "var_01hqzvabc", "productId": "prd_01hqzvabc", "name": "Mensal", "slug": "mensal", "description": "Monthly billing", "sku": "SKU-M", "primary": true, "enabled": true, "pricing": { "unitPrice": 9900, "oldPrice": null, "costPerItem": null, "currency": "BRL", "schema": "unit", "type": "recurring", "billingType": "prepaid", "billingFrequency": "monthly", "billingFrequencyCount": 1 }, "images": null, "metadata": null, "attributes": null, "externalReference": null, "checkoutUrl": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ] } ``` > Para alterar a precificação ou os dados das variantes, use os endpoints de > [Variantes](/docs/guide/products/variants) — não o update do produto. --- #### Excluir produto `DELETE /v1/products/{productId}` ```bash curl -X DELETE "https://api.selectwin.io/v1/products/prd_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" ``` ```json { "id": "prd_01hqzvabc", "resource": "product", "deleted": true } ``` #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `productNotFound` | 404 | Produto não encontrado para esta conta | | `invalidParameters` | 400 | `productId` inválido ou corpo de atualização vazio | --- ## Variantes Fonte: https://selectwin.io/docs/guide/products/variants As **variantes** carregam a precificação de um produto. Você as cria, atualiza, lista, consulta e remove **individualmente**. Cada operação atua sobre uma única variante. | Operação | Método | Endpoint | Retorna | |----------|--------|----------|---------| | Criar | POST | `/v1/products/{productId}/variants` | variante (`201`) | | Listar (de um produto) | GET | `/v1/products/{productId}/variants` | lista paginada | | Consultar (sob o produto) | GET | `/v1/products/{productId}/variants/{variantId}` | variante | | Atualizar | PATCH / PUT | `/v1/products/{productId}/variants/{variantId}` | variante | | Excluir (sob o produto) | DELETE | `/v1/products/{productId}/variants/{variantId}` | confirmação | | Listar todas (array) | GET | `/v1/products/variants/listall` · `/v1/variants` | array direto | | Consultar (por id) | GET | `/v1/variants/{variantId}` | variante | | Excluir (por id) | DELETE | `/v1/variants/{variantId}` | confirmação | > O `id` da variante usa o prefixo `var_` (ids legados `prv_` também são lidos). O `pricing` segue o > [modelo dual-axis](/docs/guide/products/overview#modelo-de-precificação-dual-axis): `pricing.type` e `pricing.schema` > são **imutáveis** após a criação. Escopos: `products:variants:{create,read,list,update,delete}`. --- #### Criar variante `POST /v1/products/{productId}/variants` Cria **uma** variante sob o produto (corpo = um único objeto). Veja os [campos da variante](/docs/guide/products/create#campos-da-variante-variants). Suporta `X-Idempotency-Key`. ```bash curl -X POST "https://api.selectwin.io/v1/products/prd_01hqzvabc/variants" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Plano Anual", "description": "Acesso por 12 meses", "sku": "SKU-001", "images": ["https://example.com/v.png"], "metadata": { "tier": "pro" }, "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, "renewalShipping": true, "renewalMethodDiscount": false } }' ``` Resposta `201 Created` (a variante criada): ```json { "id": "var_01hqzvabc", "productId": "prd_01hqzvabc", "name": "Plano Anual", "slug": "plano-anual", "description": "Acesso por 12 meses", "sku": "SKU-001", "primary": false, "enabled": true, "pricing": { "unitPrice": 9900, "oldPrice": 14900, "costPerItem": 4500, "currency": "BRL", "schema": "unit", "type": "recurring", "daysTrial": null, "billingType": "prepaid", "billingFrequency": "monthly", "billingFrequencyCount": 1, "billingExactDay": 10, "cycles": 12, "trialInterval": "day", "trialIntervalCount": 7, "tiersMode": null, "tiers": null, "transformQuantity": null, "usageType": "licensed", "usageAggregation": null, "meterEventName": null, "renewalShipping": true, "renewalMethodDiscount": false }, "images": ["https://example.com/v.png"], "metadata": { "tier": "pro" }, "attributes": null, "externalReference": "VAR-EXT-1", "checkoutUrl": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` --- #### Atualizar variante `PATCH` / `PUT /v1/products/{productId}/variants/{variantId}` Patch parcial de **uma** variante (envie ao menos um campo). `pricing.type` e `pricing.schema` **não** podem ser alterados — qualquer tentativa retorna `422`. `PUT` é um alias de `PATCH`. ```bash curl -X PATCH "https://api.selectwin.io/v1/products/prd_01hqzvabc/variants/var_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "name": "Mensal Atualizado", "pricing": { "unitPrice": 10900 } }' ``` Resposta `200 OK`: a variante atualizada (mesmo objeto do `201` acima). ##### Regras da renovação de um plano Num plano recorrente, `pricing.renewalShipping` (default `true`) decide se o frete cotado no checkout entra em cada renovação, e `pricing.renewalMethodDiscount` (default `false`) se o desconto do método de pagamento vale nas renovações pagas pelo mesmo método. As assinaturas que já existem não mudam: cada uma guarda as regras do momento da compra. Veja [regras da renovação](/docs/guide/products/overview#regras-da-renovação). ```bash curl -X PATCH "https://api.selectwin.io/v1/products/prd_01hqzvabc/variants/var_01hqzvabc" \ -H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \ -H "Content-Type: application/json" \ -d '{ "pricing": { "renewalShipping": false, "renewalMethodDiscount": true } }' ``` --- #### Listar variantes ##### De um produto — `GET /v1/products/{productId}/variants` (paginada) Filtros: `id`, `sku`, `currency`, `enabled`, `daterange*`. Paginação: `limit` default `20`, máx. `100`; `offset` default `0`. ```json { "offset": 0, "limit": 20, "total": 1, "page": { "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 }, "current": 1, "total": 1 }, "hasMore": false, "data": [ { "id": "var_01hqzvabc", "productId": "prd_01hqzvabc", "name": "Mensal", "slug": "mensal", "description": "Monthly billing", "sku": "SKU-M", "primary": true, "enabled": true, "pricing": { "unitPrice": 9900, "oldPrice": null, "costPerItem": null, "currency": "BRL", "schema": "unit", "type": "recurring", "billingType": "prepaid", "billingFrequency": "monthly", "billingFrequencyCount": 1 }, "images": null, "metadata": null, "attributes": null, "externalReference": null, "checkoutUrl": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ] } ``` ##### Todas (array direto) — `GET /v1/products/variants/listall` ou `GET /v1/variants` Ambos retornam um **array** no nível raiz (sem envelope de paginação) com o objeto **completo** de cada variante (mesmo shape do item acima, com `pricing` completo e `productId`). Filtros aceitos: `id`, `sku`, `currency`, `enabled`, `productId`, `daterange*`; `sort` (`ascending` | `descending`, default `ascending`). Em `GET /v1/variants` a janela é controlada por `limit` (1–2000, default **2000**) e `offset` (default `0`): o array continua sem envelope, mas acima de 2000 variantes você precisa paginar com `offset` — o teto antigo de `999999` não existe mais. ```json [ { "id": "var_01hqzvabc", "productId": "prd_01hqzvabc", "name": "Mensal", "slug": "mensal", "description": "Monthly billing", "sku": "SKU-M", "primary": true, "enabled": true, "pricing": { "unitPrice": 9900, "oldPrice": null, "costPerItem": null, "currency": "BRL", "schema": "unit", "type": "recurring", "billingType": "prepaid", "billingFrequency": "monthly", "billingFrequencyCount": 1 }, "images": null, "metadata": null, "attributes": null, "externalReference": null, "checkoutUrl": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ] ``` --- #### Consultar variante `GET /v1/products/{productId}/variants/{variantId}` ou `GET /v1/variants/{variantId}` Ambos retornam o objeto completo da variante (incluindo `pricing.costPerItem`): ```json { "id": "var_01hqzvabc", "productId": "prd_01hqzvabc", "name": "Mensal", "slug": "mensal", "description": "Monthly billing", "sku": "SKU-M", "primary": true, "enabled": true, "pricing": { "unitPrice": 9900, "oldPrice": null, "costPerItem": 5000, "currency": "BRL", "schema": "unit", "type": "recurring", "billingType": "prepaid", "billingFrequency": "monthly", "billingFrequencyCount": 1 }, "images": null, "metadata": null, "attributes": null, "externalReference": null, "checkoutUrl": null, "createdAt": "2026-04-12T17:56:33.000Z", "updatedAt": "2026-04-12T17:56:33.000Z" } ``` --- #### Excluir variante `DELETE /v1/products/{productId}/variants/{variantId}` ou `DELETE /v1/variants/{variantId}` ```json { "id": "var_01hqzvabc", "resource": "variant", "deleted": true } ``` #### Erros | `error.code` | HTTP | Quando | |--------------|-----:|--------| | `variantNotFound` | 404 | Variante não encontrada para esta conta | | `productNotFound` | 404 | Produto-pai inexistente (rotas aninhadas / criação) | | `invalidParameters` | 400 | `productId`/`variantId` inválido ou corpo de atualização vazio | | `invalidPricing` | 422 | Configuração de `pricing` inconsistente com o `schema`/recorrência | | `pricingTypeImmutable` | 422 | Tentou alterar `pricing.type` | | `pricingSchemaImmutable` | 422 | Tentou alterar `pricing.schema` | | `variantNotRecurring` | 422 | Variante `oneTime` usada em uma assinatura | | `variantRenewalRulesUnavailable` | 503 | `pricing.renewalShipping`/`pricing.renewalMethodDiscount` ainda não podem ser gravados. Nada foi gravado: reenvie sem eles ou tente mais tarde | Catálogo completo: [Códigos de Erro](/docs/reference/codigos-de-erro). --- ## Endpoints de webhook - Visão Geral Fonte: https://selectwin.io/docs/guide/webhook-endpoints/overview #### Introdução Os Webhook Endpoints permitem que você configure URLs em seu sistema para receber notificações em tempo real sobre eventos que ocorrem na API da SelectWin. Em vez de constantemente consultar a API para verificar atualizações, os webhooks enviam automaticamente dados para seu sistema assim que os eventos ocorrem. #### Conceitos Fundamentais - **Webhook Endpoint**: Uma URL (HTTPS) em seu sistema que receberá as notificações de eventos. - **Eventos**: Ocorrências específicas no sistema da SelectWin, como a criação de uma transação ou uma mudança de status. - **Assinaturas de Eventos**: A lista de tipos de eventos que você deseja receber no endpoint (campo `events[]`). - **Secret**: Segredo de assinatura (`whsec_...`) retornado **uma única vez** na criação. Usado para verificar o cabeçalho `X-Selectwin-Signature` de cada entrega. #### Autenticação Todas as chamadas usam o cabeçalho `SelectKey` com sua chave de API (`sk_test_` em sandbox, `sk_live_` em produção). Base URL: `https://api.selectwin.io/v1`. #### Estrutura do Webhook Endpoint (respostas da API) Note os nomes **`endpoint`** (URL) e **`enabled`** (booleano) — não `url`/`status`: - `id` (`wbe_*`; reads também aceitam endpoints legados `wbh_*`) - `name` - `endpoint` (URL HTTPS) - `enabled` (boolean) - `events` (array de strings | null) - `forceActive` (boolean) - `shotsQty`, `failedShotsQty` (integer) - `lastDeliveryAt` (string ISO | null) - `metadata` (objeto | null) - `createdAt`, `updatedAt` (string ISO) - `merchant`, `_links` (presentes em todas as respostas autenticadas) **Regras de respostas (fidelidade ao código):** - **Create / rotate-secret**: incluem `secret` (`whsec_...`) no root, retornado **apenas uma vez**. - **Read / Update / List**: nunca retornam `secret`. - As credenciais de autenticação no envio (`headerAuthorization` / `headerAuthorizationType`) são **write-only** — você as define na criação/atualização, mas elas **não** retornam em nenhuma resposta. - **Delete**: `{ id, resource: "webhook", deleted: true, merchant, _links }`. ##### Formato do Payload de Evento (enviado para seu endpoint) Quando um evento ocorre, a SelectWin envia uma requisição `POST` para o endpoint configurado com um envelope JSON e o objeto do recurso em `payload.object`: ```json { "id": "wbh_123456789", "type": "transaction.approved", "source": "automatic", "payload": { "object": { "...": "o objeto completo do recurso (mesma forma da leitura do recurso)" } }, "correlationId": null, "updatedAt": "2026-06-20T14:30:40.000Z", "createdAt": "2026-06-20T14:30:40.000Z" } ``` > O objeto do recurso fica em `payload.object` (não em `data`). Cada entrega é assinada — veja > [Verificando Assinaturas de Webhook](/docs/guide/webhook-events/verifying-signatures). #### Webhooks Acionados | Evento | Descrição | |--------|-----------| | `webhook.created` | Acionado quando um novo webhook endpoint é configurado. | | `webhook.updated` | Acionado quando um webhook endpoint é atualizado. | | `webhook.deleted` | Acionado quando um webhook endpoint é removido. | | `webhook.disabled` | Acionado quando um webhook é desativado por falhas de entrega. | A lista completa de eventos subscríveis está no [Catálogo de Eventos](/docs/guide/webhook-events/event-catalog). #### Boas Práticas 1. **Idempotência**: Seu endpoint deve ser idempotente, capaz de receber o mesmo evento múltiplas vezes sem efeitos colaterais. Deduplique pelo `id` do evento (`wbh_...`). 2. **Resposta Rápida**: Responda com `2xx` rapidamente (a entrega tem timeout de **30 s**); processe de forma assíncrona. Um endpoint que demora (5 s ou mais) passa a receber pela fila agendada por alguns minutos — os eventos chegam, com alguns segundos de atraso e **sem ordem garantida**. 3. **Verificação de Segurança**: Valide o cabeçalho `X-Selectwin-Signature` (HMAC-SHA256) antes de processar o payload. 4. **Use HTTPS**: O campo `endpoint` exige uma URL `https://` (URLs `http://` são rejeitadas com 400). 5. **Monitoramento**: Use os [Webhook Dispatches](/docs/guide/webhook-dispatches/overview) para auditar entregas e diagnosticar falhas por endpoint. #### Recursos Disponíveis ##### Endpoints | Método | Caminho | Descrição | |--------|---------|-----------| | POST | `/v1/webhooks` | Criar um novo webhook endpoint | | GET | `/v1/webhooks` | Listar webhook endpoints | | GET | `/v1/webhooks/{webhookId}` | Recuperar um webhook endpoint | | PUT | `/v1/webhooks/{webhookId}` | Atualizar um webhook endpoint | | DELETE | `/v1/webhooks/{webhookId}` | Excluir um webhook endpoint | | POST | `/v1/webhooks/{webhookId}/rotate-secret` | Rotacionar o segredo de assinatura | | POST | `/v1/webhooks/{webhookId}/test` | Enviar um ping sintético para testar a integração | > O `{webhookId}` aceita o prefixo atual `wbe_` e também endpoints legados `wbh_`. ##### Tipos de Eventos A SelectWin suporta diversos tipos de eventos, agrupados por recurso (transação, recebível, assinatura, cliente, cartão, carteira, saque, vendedor, checkout e webhook). A lista completa e autoritativa está no [Catálogo de Eventos de Webhook](/docs/guide/webhook-events/event-catalog) — apenas os valores listados lá são aceitos no campo `events[]`. #### Considerações de Segurança 1. **Use HTTPS**: Endpoints exigem HTTPS para proteger os dados em trânsito. 2. **Verifique a assinatura**: Valide `X-Selectwin-Signature` em toda requisição recebida. 3. **Valide os Dados**: Verifique sempre a estrutura e o conteúdo do payload recebido. 4. **Nunca logue o secret** (`whsec_...`): trate-o como uma senha. 5. **Audite Eventos**: Acompanhe os [Webhook Dispatches](/docs/guide/webhook-dispatches/overview) para fins de auditoria. #### Próximos Passos - Leia sobre como [Criar um Webhook Endpoint](/docs/guide/webhook-endpoints/create) - Aprenda a [Listar seus Webhook Endpoints](/docs/guide/webhook-endpoints/list) - Saiba como [Testar / Rotacionar o segredo de um Webhook Endpoint](/docs/guide/webhook-endpoints/analyze) --- ## Endpoints de webhook - Criar Fonte: https://selectwin.io/docs/guide/webhook-endpoints/create #### Visão Geral Este endpoint cria uma nova configuração de webhook para receber notificações em tempo real sobre eventos específicos da API. O `secret` de assinatura (`whsec_...`) é retornado **apenas nesta resposta** — guarde-o imediatamente. #### Precauções - Seu endpoint deve responder a requisições HTTP `POST`. - A URL do `endpoint` deve ser pública e **HTTPS** (URLs `http://` são rejeitadas com 400). - Sua aplicação deve responder rapidamente (timeout de entrega de **30 s**); processe de forma assíncrona. - Salve o `id` (`wbe_*`) e o `secret` (`whsec_...`) retornados — o `secret` não é exibido novamente. #### Descrição A criação envolve um `name` descritivo, uma URL `endpoint` válida (HTTPS) e a lista `events[]` de tipos de evento a receber. Forneça `events[]` **ou** defina `forceActive: true` (um dos dois é obrigatório). Opcionalmente, você pode configurar cabeçalhos de autenticação enviados a cada disparo. #### Requisição ``` POST /v1/webhooks ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:create`. ##### Corpo da Requisição | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `name` | string (1–100) | Sim | Nome descritivo para o webhook | `"Webhook MyDomain"` | | `endpoint` | string (URI HTTPS) | Sim | URL que receberá as notificações | `"https://webhooks.mydomain.com/selectwin"` | | `events` | array de strings (máx. 200) | Condicional | Tipos de evento a assinar (ver [Catálogo](/docs/guide/webhook-events/event-catalog)). Obrigatório, a menos que `forceActive` seja `true` | `["transaction.approved", "transaction.failed"]` | | `forceActive` | boolean | Não | Recebe **todos** os tipos de evento (dispensa `events[]`) | `false` | | `enabled` | boolean | Não | Se o endpoint está ativo (padrão `true`) | `true` | | `headerAuthorization` | string (máx. 1000) | Não | Valor de autenticação enviado no cabeçalho `Authorization` de cada disparo (**write-only** — nunca retorna) | `"sk_live_xyz"` | | `headerAuthorizationType` | string (máx. 50) | Não | Tipo de autenticação associado (**write-only**) | `"Bearer"` | | `metadata` | objeto | Não | Metadados livres | `{"team": "ops"}` | > Valores em `events[]` são validados contra o [Catálogo de Eventos](/docs/guide/webhook-events/event-catalog). Um tipo inexistente retorna `400`. ##### Exemplo de Requisição ```json { "name": "Webhook MyDomain", "endpoint": "https://webhooks.mydomain.com/selectwin", "events": [ "transaction.approved", "transaction.pending", "transaction.failed" ], "metadata": { "team": "ops" } } ``` #### Resposta ##### Sucesso (201 Created) ```json { "id": "wbe_01hqzvabc", "name": "Webhook MyDomain", "endpoint": "https://webhooks.mydomain.com/selectwin", "enabled": true, "events": [ "transaction.approved", "transaction.pending", "transaction.failed" ], "forceActive": false, "shotsQty": 0, "failedShotsQty": 0, "lastDeliveryAt": null, "metadata": { "team": "ops" }, "createdAt": "2026-06-20T20:18:05.000Z", "updatedAt": "2026-06-20T20:18:05.000Z", "secret": "whsec_abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/webhooks/wbe_01hqzvabc", "method": "GET", "description": "Read a webhook." }, "create": { "href": "https://api.selectwin.io/v1/webhooks", "method": "POST", "description": "Create a new webhook." }, "update": { "href": "https://api.selectwin.io/v1/webhooks/wbe_01hqzvabc", "method": "PUT", "description": "Update the webhook." }, "delete": { "href": "https://api.selectwin.io/v1/webhooks/wbe_01hqzvabc", "method": "DELETE", "description": "Delete the webhook." }, "list": { "href": "https://api.selectwin.io/v1/webhooks", "method": "GET", "description": "List all webhooks." } } } ``` ##### Atributos da Resposta `secret` (`whsec_*`) só aparece **no create** (e no rotate-secret). As credenciais `headerAuthorization`/`headerAuthorizationType` são write-only e nunca retornam. | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `wbe_*` | | `name` | string | Nome descritivo | | `endpoint` | string | URL HTTPS | | `enabled` | boolean | Ativo | | `events` | array \| null | Tipos de evento assinados (`null` quando `forceActive`) | | `forceActive` | boolean | Recebe todos os tipos de evento | | `shotsQty` | integer | Total de disparos | | `failedShotsQty` | integer | Disparos com falha | | `lastDeliveryAt` | string (ISO) \| null | Última entrega | | `metadata` | objeto \| null | Metadados | | `createdAt` / `updatedAt` | string (ISO) | Timestamps | | `secret` | string | Segredo `whsec_*` — **apenas no create/rotate-secret** | | `merchant` | object | Bloco do lojista | | `_links` | object | HATEOAS | #### Erros | Código HTTP | Quando | |-------------|--------| | 400 | Validação (URL não-HTTPS, `events[]` ausente sem `forceActive`, tipo de evento inválido) | | 401 | Chave de API ausente/inválida (`SelectKey`) | | 403 | Sem o escopo `webhooks:create` | | 409 | Conflito de idempotência | | 500 | Erro interno | #### Melhores Práticas 1. **Dê nomes descritivos** aos seus webhooks para facilitar a identificação. 2. **Implemente idempotência** no seu endpoint; deduplique pelo `id` do evento. 3. **Assine a menor lista de eventos** que você precisa, em vez de `forceActive`, para evitar tráfego desnecessário. 4. **Use cabeçalhos de autenticação** (`headerAuthorization`) ou valide a assinatura HMAC para garantir a origem das requisições. 5. **Salve o `id` e o `secret`** imediatamente após a criação. 6. **Teste seu endpoint** com `POST /v1/webhooks/{webhookId}/test` antes de colocá-lo em produção. --- ## Endpoints de webhook - Listar Fonte: https://selectwin.io/docs/guide/webhook-endpoints/list #### Visão Geral Este endpoint lista os webhook endpoints configurados na sua conta, com paginação e filtros por nome, URL do endpoint, status (ativo/inativo) e intervalo de datas. #### Precauções - Use os filtros para limitar os resultados e encontrar endpoints específicos. - A resposta é paginada; utilize `offset`/`limit` para navegar entre os resultados. #### Descrição A listagem é útil para obter uma visão geral de todos os endpoints, verificar seu status (`enabled`) e gerenciar suas integrações. #### Requisição ``` GET /v1/webhooks ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:list`. ##### Parâmetros de Consulta | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `offset` | integer | Não | Posição inicial para paginação (padrão `0`) | `0` | | `limit` | integer | Não | Máximo de registros por página (padrão `20`, máximo `100`) | `20` | | `sort` | string | Não | Expressão de ordenação (aceita também `ascending`/`descending`) | `descending` | | `name` | string | Não | Filtro por nome (substring) | `Webhook` | | `endpoint` | string | Não | Filtro por URL exata do endpoint | `https://webhooks.mydomain.com/selectwin` | | `status` | string | Não | Filtro por status: `active`/`enabled` ou `inactive`/`disabled` | `active` | | `daterange` | string (date) | Não | Filtra por um único dia de criação | `2026-06-20` | | `daterangegt` / `daterangegte` | string (date-time) | Não | Criado após / a partir de | `2026-06-01T00:00:00Z` | | `daterangelt` / `daterangelte` | string (date-time) | Não | Criado antes de / até | `2026-06-30T23:59:59Z` | ##### Exemplo de Requisição ``` GET /v1/webhooks?limit=5&offset=0&status=active&sort=descending ``` #### Resposta ##### Sucesso (200 OK) ```json { "offset": 0, "limit": 20, "total": 2, "page": { "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 }, "current": 1, "total": 1 }, "hasMore": false, "data": [ { "id": "wbe_01hqzvabc", "name": "Webhook MyDomain", "endpoint": "https://webhooks.mydomain.com/selectwin", "enabled": true, "events": ["transaction.approved", "transaction.pending"], "forceActive": false, "shotsQty": 3068, "failedShotsQty": 12, "lastDeliveryAt": "2026-06-20T17:56:33.000Z", "metadata": { "team": "ops" }, "createdAt": "2026-01-26T20:18:05.000Z", "updatedAt": "2026-06-20T17:56:33.000Z", "_links": { "self": { "href": "https://api.selectwin.io/v1/webhooks/wbe_01hqzvabc", "method": "GET", "description": "Read a webhook." } } } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/webhooks", "method": "GET", "description": "List all webhooks." } } } ``` > O `secret` **nunca** aparece na listagem nem na leitura; ele só é retornado no create e no rotate-secret. ##### Atributos da Resposta ###### Envelope da lista | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` | integer | Posição inicial da consulta | | `limit` | integer | Máximo de registros retornados | | `total` | integer | Total de registros encontrados | | `page` | object | Objeto de paginação rico (ver abaixo) | | `hasMore` | boolean | Indica se há mais registros | | `data` | array | Endpoints retornados | | `merchant` | object | Bloco do lojista | | `_links` | object | HATEOAS (`self`) | ###### Objeto `page` | Atributo | Tipo | Descrição | |----------|------|-----------| | `page.offset.first` | integer | Offset da primeira página | | `page.offset.prev` | integer | Offset da página anterior | | `page.offset.next` | integer | Offset da próxima página | | `page.offset.last` | integer | Offset da última página | | `page.current` | integer | Número da página atual (1-based) | | `page.total` | integer | Total de páginas | ###### Objeto endpoint (item de `data`) | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `wbe_*` (legados `wbh_*`) | | `name` | string | Nome descritivo | | `endpoint` | string | URL HTTPS | | `enabled` | boolean | Ativo | | `events` | array \| null | Tipos de evento assinados | | `forceActive` | boolean | Recebe todos os tipos de evento | | `shotsQty` | integer | Total de disparos | | `failedShotsQty` | integer | Disparos com falha | | `lastDeliveryAt` | string (ISO) \| null | Última entrega | | `metadata` | objeto \| null | Metadados | | `createdAt` / `updatedAt` | string (ISO) | Timestamps | | `_links` | object | HATEOAS (`self`) | ##### Erros | Código HTTP | Quando | |-------------|--------| | 401 | Chave de API ausente/inválida (`SelectKey`) | | 403 | Sem o escopo `webhooks:list` | | 500 | Erro interno | #### Melhores Práticas 1. **Utilize os filtros** (`name`, `endpoint`, `status`) para encontrar endpoints específicos. 2. **Configure `limit`** adequadamente (máximo `100`) para otimizar suas requisições. 3. **Monitore o campo `lastDeliveryAt`** para identificar webhooks que não recebem eventos recentemente. 4. **Verifique `hasMore`** para determinar se há mais resultados além dos retornados. --- ## Endpoints de webhook - Atualizar Fonte: https://selectwin.io/docs/guide/webhook-endpoints/update #### Visão Geral Este endpoint atualiza as configurações de um webhook endpoint existente: nome, URL, lista de eventos, status, metadados ou cabeçalhos de autenticação. Todos os campos são opcionais (atualização parcial). #### Precauções - A atualização não interrompe entregas em andamento. - Se alterar a URL, garanta que o novo `endpoint` (HTTPS) esteja acessível. - O `secret` **não** é alterado nem retornado aqui — para rotacioná-lo, use `POST /v1/webhooks/{webhookId}/rotate-secret`. #### Descrição Útil para alterar a URL de destino, adicionar/remover eventos monitorados, ativar/desativar (`enabled`) ou atualizar credenciais de autenticação. #### Requisição ``` PUT /v1/webhooks/{webhookId} ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:update`. ##### Parâmetros de Caminho | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `webhookId` | string | Sim | ID do webhook (`wbe_*`; legados `wbh_*`) | `wbe_01hqzvabc` | ##### Corpo da Requisição Todos os campos são **opcionais** — envie apenas o que deseja alterar. | Parâmetro | Tipo | Descrição | Exemplo | |-----------|------|-----------|---------| | `name` | string (1–100) | Nome descritivo | `"Webhook MyDomain - Updated"` | | `endpoint` | string (URI HTTPS) | Nova URL de destino | `"https://webhooks.mydomain.com/selectwin"` | | `events` | array de strings (máx. 200) | Nova lista de tipos de evento (ver [Catálogo](/docs/guide/webhook-events/event-catalog)) | `["transaction.approved", "customer.created"]` | | `forceActive` | boolean | Recebe todos os tipos de evento | `true` | | `enabled` | boolean | Ativa/desativa o endpoint | `true` | | `headerAuthorization` | string (máx. 1000) | Valor de autenticação enviado a cada disparo (**write-only**) | `"sk_live_xyz"` | | `headerAuthorizationType` | string (máx. 50) | Tipo de autenticação (**write-only**) | `"Bearer"` | | `metadata` | objeto | Metadados livres | `{"updatedBy": "admin"}` | ##### Exemplo de Requisição ```json { "name": "Webhook MyDomain - Updated", "events": [ "transaction.approved", "transaction.pending", "customer.created" ], "enabled": true } ``` #### Resposta ##### Sucesso (200 OK) ```json { "id": "wbe_01hqzvabc", "name": "Webhook MyDomain - Updated", "endpoint": "https://webhooks.mydomain.com/selectwin", "enabled": true, "events": [ "transaction.approved", "transaction.pending", "customer.created" ], "forceActive": false, "shotsQty": 3068, "failedShotsQty": 12, "lastDeliveryAt": "2026-06-20T18:10:00.000Z", "metadata": { "team": "ops", "updatedBy": "admin" }, "createdAt": "2026-01-26T20:18:05.000Z", "updatedAt": "2026-06-20T18:10:05.000Z", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/webhooks/wbe_01hqzvabc", "method": "GET", "description": "Read a webhook." } } } ``` ##### Atributos da Resposta Mesma estrutura do [Create](/docs/guide/webhook-endpoints/create), **sem** o campo `secret` (que só aparece no create/rotate-secret). As credenciais `headerAuthorization`/`headerAuthorizationType` são write-only e não retornam. | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `wbe_*` | | `name` | string | Nome (atualizado) | | `endpoint` | string | URL HTTPS | | `enabled` | boolean | Ativo | | `events` | array \| null | Tipos de evento assinados | | `forceActive` | boolean | Recebe todos os tipos de evento | | `shotsQty` / `failedShotsQty` | integer | Disparos | | `lastDeliveryAt` | string (ISO) \| null | Última entrega | | `metadata` | objeto \| null | Metadados | | `createdAt` / `updatedAt` | string (ISO) | Timestamps | | `merchant` | object | Bloco do lojista | | `_links` | object | HATEOAS | #### Erros | Código HTTP | Quando | |-------------|--------| | 400 | Validação (ex.: URL não-HTTPS, tipo de evento inválido) | | 401 | Chave de API ausente/inválida (`SelectKey`) | | 403 | Sem o escopo `webhooks:update` | | 404 | Webhook endpoint não encontrado (`webhookEndpointNotFound`) | | 500 | Erro interno | #### Melhores Práticas 1. **Teste a nova URL** antes de atualizá-la no webhook. 2. **Considere o impacto** da alteração de `events[]` nas integrações que dependem dessas notificações. 3. **Use `enabled: false`** para pausar temporariamente um endpoint em vez de excluí-lo. 4. **Rotacione o segredo** periodicamente via `POST /v1/webhooks/{webhookId}/rotate-secret`. --- ## Endpoints de webhook - Excluir Fonte: https://selectwin.io/docs/guide/webhook-endpoints/delete #### Visão Geral Este endpoint remove um webhook endpoint configurado. Ao excluir, você interrompe o envio de notificações para a URL especificada. #### Precauções - A exclusão é permanente. - Certifique-se de que sistemas dependentes estejam preparados para deixar de receber notificações. - Para apenas pausar (sem excluir), use `PUT /v1/webhooks/{webhookId}` com `enabled: false`. #### Descrição Útil para interromper completamente o recebimento de notificações em um endpoint, como em mudança de infraestrutura, desativação de integrações ou problemas de segurança. #### Requisição ``` DELETE /v1/webhooks/{webhookId} ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:delete`. ##### Parâmetros de Caminho | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `webhookId` | string | Sim | ID do webhook (`wbe_*`; legados `wbh_*`) | `wbe_01hqzvabc` | ##### Exemplo de Requisição ``` DELETE /v1/webhooks/wbe_01hqzvabc ``` #### Resposta ##### Sucesso (200 OK) ```json { "id": "wbe_01hqzvabc", "resource": "webhook", "deleted": true, "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/webhooks/wbe_01hqzvabc", "method": "GET", "description": "Read a webhook." }, "list": { "href": "https://api.selectwin.io/v1/webhooks", "method": "GET", "description": "List all webhooks." } } } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | `wbe_*` excluído | | `resource` | string | `"webhook"` | | `deleted` | boolean | `true` | | `merchant` | object | Bloco do lojista | | `_links` | object | HATEOAS | ##### Erros ###### Webhook não encontrado (404 Not Found) ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "Webhook endpoint not found.", "suggestedActions": [ "Verifique se o ID do webhook está correto", "Certifique-se de que o webhook existe no sistema" ], "docUrl": "https://docs.api.selectwin.io/errors/resource", "supportUrls": [ "https://suporte.selectwin.io/ajuda" ] } } ``` ###### ID de webhook inválido (400 Bad Request) ```json { "error": { "status": "Bad Request", "statusCode": 400, "category": "validation", "message": "Validation errors occurred.", "params": [ { "id": "O formato do ID do webhook é inválido. Formato esperado: wbe_XXXXXXXXX" } ], "suggestedActions": [ "Verifique o formato do ID do webhook" ], "docUrl": "https://docs.api.selectwin.io/errors/validation", "supportUrls": [ "https://suporte.selectwin.io/ajuda" ] } } ``` ###### Erro de autenticação (401 Unauthorized) ```json { "error": { "status": "Unauthorized", "statusCode": 401, "category": "authentication", "message": "Authentication failed.", "details": "Invalid or missing API key.", "suggestedActions": [ "Verifique se a chave de API foi fornecida corretamente no cabeçalho SelectKey", "Certifique-se de que sua chave de API está ativa e tem as permissões necessárias" ], "docUrl": "https://docs.api.selectwin.io/errors/authentication", "supportUrls": [ "https://suporte.selectwin.io/ajuda" ] } } ``` ###### Acesso negado (403 Forbidden) ```json { "error": { "status": "Forbidden", "statusCode": 403, "category": "authorization", "message": "Access denied to the requested resource.", "details": "You do not have permission to delete this webhook.", "suggestedActions": [ "Verifique se você tem o escopo webhooks:delete", "Certifique-se de que o webhook pertence à sua conta" ], "docUrl": "https://docs.api.selectwin.io/errors/authorization", "supportUrls": [ "https://suporte.selectwin.io/ajuda" ] } } ``` #### Melhores Práticas 1. **Notifique os sistemas integrados** antes de excluir um webhook. 2. **Verifique o `webhookId`** cuidadosamente antes de excluir. 3. **Considere desativar temporariamente** (`enabled: false`) em vez de excluir, se houver chance de reativá-lo. 4. **Monitore a resposta** (`deleted: true`) para confirmar a exclusão. --- ## Endpoints de webhook - Testar e rotacionar o segredo Fonte: https://selectwin.io/docs/guide/webhook-endpoints/analyze #### Visão Geral Esta página cobre as duas ações operacionais sobre um webhook endpoint existente: - **Testar** (`POST /v1/webhooks/{webhookId}/test`) — envia um ping sintético (`webhook.ping`) ao endpoint para verificar a fiação da integração. - **Rotacionar o segredo** (`POST /v1/webhooks/{webhookId}/rotate-secret`) — gera um novo `secret` (`whsec_...`) de assinatura, retornado **uma única vez**. ⚠️ **Você tem 24h para migrar, sem perder entrega:** durante a janela de sobreposição as entregas vão assinadas com os **dois** segredos — o novo nos headers canônicos (`X-Selectwin-Signature`, `X-Selectwin-Signature-V1`) e o antigo em `X-Selectwin-Signature-Previous` / `X-Selectwin-Signature-V1-Previous`. Seu receptor pode continuar validando o header antigo até trocar a chave. Passada a janela, só o segredo novo é assinado. > Não existe um endpoint de "analytics" por webhook endpoint. Para auditar entregas, métricas de > sucesso/falha e diagnosticar problemas, use os [Webhook Dispatches](/docs/guide/webhook-dispatches/overview). --- #### Testar o Endpoint Dispara um evento sintético `webhook.ping` ao endpoint e cria um registro de entrega (dispatch) que você pode inspecionar nos [Webhook Dispatches](/docs/guide/webhook-dispatches/list). A resposta confirma apenas que o teste foi aceito; o resultado real aparece no dispatch. ##### Requisição ``` POST /v1/webhooks/{webhookId}/test ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:update`. Não há corpo obrigatório. ##### Parâmetros de Caminho | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `webhookId` | string | Sim | ID do webhook (`wbe_*`; legados `wbh_*`) | `wbe_01hqzvabc` | ##### Resposta — Aceito (202 Accepted) ```json { "accepted": true, "id": "wbh_01hqzvtest" } ``` | Atributo | Tipo | Descrição | |----------|------|-----------| | `accepted` | boolean | Sempre `true` quando o teste foi enfileirado/disparado | | `id` | string | ID do evento de teste gerado (`wbh_*`) — use-o para localizar o dispatch | O payload entregue ao seu servidor tem `type: "webhook.ping"` e `payload.object` contendo `{ "message": "Test ping from Selectwin", "endpointId": "wbe_..." }`. --- #### Rotacionar o Segredo Gera um novo `secret` (`whsec_...`) para o endpoint. O segredo retorna **apenas nesta resposta** — atualize-o imediatamente no seu verificador de assinatura. ##### Requisição ``` POST /v1/webhooks/{webhookId}/rotate-secret ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:update`. ##### Resposta — Sucesso (200 OK) ```json { "id": "wbe_01hqzvabc", "name": "Webhook MyDomain", "endpoint": "https://webhooks.mydomain.com/selectwin", "enabled": true, "events": ["transaction.approved", "transaction.pending"], "forceActive": false, "shotsQty": 3068, "failedShotsQty": 12, "lastDeliveryAt": "2026-06-20T17:56:33.000Z", "metadata": { "team": "ops" }, "createdAt": "2026-01-26T20:18:05.000Z", "updatedAt": "2026-06-20T18:30:00.000Z", "secret": "whsec_novosegredo123456789abcdefghijklmnopqrstuvwxyz0123", "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/webhooks/wbe_01hqzvabc", "method": "GET", "description": "Read a webhook." } } } ``` Mesmos atributos do [Create](/docs/guide/webhook-endpoints/create) — inclui `secret` (novo). As credenciais `headerAuthorization`/`headerAuthorizationType` permanecem write-only e não retornam. --- #### Erros (ambas as ações) | Código HTTP | Quando | |-------------|--------| | 401 | Chave de API ausente/inválida (`SelectKey`) | | 403 | Sem o escopo `webhooks:update` | | 404 | Webhook endpoint não encontrado (`webhookEndpointNotFound`) | | 500 | Erro interno | #### Melhores Práticas 1. **Teste antes de produção** com `POST /v1/webhooks/{webhookId}/test` e confira o resultado nos [Dispatches](/docs/guide/webhook-dispatches/list). 2. **Atualize o verificador** assim que rotacionar o segredo — durante a janela de troca, aceite tanto o segredo antigo quanto o novo até confirmar que todas as entregas usam o novo. 3. **Nunca logue o `secret`** (`whsec_...`); trate-o como uma senha. 4. **Para métricas e auditoria de entrega**, use os [Webhook Dispatches](/docs/guide/webhook-dispatches/overview). --- ## Eventos de webhook - Visão Geral Fonte: https://selectwin.io/docs/guide/webhook-events/overview #### Introdução O recurso de Webhook Events é o histórico de todas as notificações geradas para os Webhook Endpoints configurados. Ele permite monitorar a entrega de eventos, verificar o status, identificar falhas e reenviar (replay) eventos quando necessário, garantindo que os sistemas integrados permaneçam sincronizados com a plataforma. #### Conceito e Funcionalidade Cada **evento** registra uma ocorrência na sua conta (ex.: `transaction.approved`), seu tipo, o recurso afetado e o resultado da entrega. A partir do evento, a plataforma cria um ou mais **[dispatches](/docs/guide/webhook-dispatches/overview)** — uma tentativa de entrega por endpoint elegível. A listagem de eventos já traz os dispatches **aninhados** em cada item. #### Timeout e Retentativas A plataforma aguarda a resposta do seu endpoint por até **30 segundos** (a resposta completa — cabeçalho e corpo). Sem resposta `2xx` nesse período (ou em caso de erro/timeout), a tentativa é registrada como falha e o sistema agenda uma nova tentativa com backoff (até esgotar as retentativas, ~3 dias). **Endpoint lento.** A entrega da plataforma é compartilhada entre as contas, então nenhum endpoint pode segurá-la: cada endpoint tem um número pequeno de entregas simultâneas, e um endpoint que levou **5 segundos ou mais** para responder (ou estourou o prazo) passa, por alguns minutos, a receber pela **fila agendada** — os eventos continuam chegando, com alguns segundos de atraso. Responder `2xx` logo e processar depois evita isso. **Ordem.** A ordem de entrega **não é garantida**: dois eventos do mesmo recurso podem chegar fora da ordem em que aconteceram (por exemplo, um vai pela fila agendada e o seguinte não). Use o `createdAt` do envelope e o estado do recurso em `payload.object` — nunca a ordem de chegada — para decidir o que vale. #### Fluxo de Entrega de Eventos ```mermaid sequenceDiagram autonumber participant SW as Selectwin participant EP as Seu Endpoint Note over SW: Mudança de status
gera um evento SW->>EP: POST payload (X-Selectwin-Signature) alt resposta 2xx em até 30s EP-->>SW: 200 OK Note over SW: dispatch = success else timeout (>30s) ou erro EP--xSW: erro / sem resposta Note over SW: agenda nova tentativa (backoff) SW->>EP: POST payload (retry) EP-->>SW: 200 OK end ``` Eventos podem ser reenviados manualmente via `POST /v1/webhooks/events/{eventId}/resend` (veja [Reenviar Evento](/docs/guide/webhook-events/resend)). Valide sempre a assinatura `X-Selectwin-Signature` antes de processar o payload. #### Retenção O histórico de eventos e de suas tentativas de entrega ([dispatches](/docs/guide/webhook-dispatches/overview)) fica disponível por **90 dias** a partir da criação do evento. Depois disso o evento e os dispatches dele saem da listagem e não podem mais ser reenviados — guarde no seu sistema o que precisar manter por mais tempo. **O mesmo evento não volta depois da janela.** Um evento é único para sempre, não só enquanto está no histórico — um por recurso, id do recurso e tipo, ou um por **ocorrência** nos tipos que se repetem (veja [Uma vez por recurso × uma vez por ocorrência](#uma-vez-por-recurso--uma-vez-por-ocorrência)): um segundo `transaction.approved` da mesma venda `tra_…` não gera entrega nova, nem depois que o evento saiu da listagem (a Selectwin guarda a chave do evento apagado). Mesmo assim, além de deduplicar pelo `id`, torne o processamento idempotente pelo **estado do recurso** em `payload.object`: não libere de novo um pedido que o seu sistema já marcou como pago. #### Uma vez por recurso × uma vez por ocorrência Cada evento tem um `id` (`wbh_…`), que chega também no cabeçalho `X-Selectwin-Event-Id`. A Selectwin nunca entrega o mesmo fato como dois eventos; o que conta como "o mesmo fato" depende do tipo: | Regra | Tipos | Na prática | |-------|-------|------------| | **Uma vez por recurso** | Os fatos que acontecem uma vez na vida do recurso: `*.created`, `*.approved`, `*.paid`, `*.canceled`, `*.deleted` e os demais tipos de status, inclusive as mudanças de status de assinatura (`subscription.active`, `subscription.pastdue`…) e de seller (`seller.approved`, `seller.blocked`…) | Um segundo `transaction.approved` da mesma venda `tra_…` não gera evento novo, nem quando a venda volta a `approved` porque você ganhou uma disputa | | **Uma vez por ocorrência** | Todo `*.updated` (`customer.updated`, `customer.address.updated`, `card.updated`, `subscription.updated`, `seller.updated`…) | Cada alteração do recurso é um evento novo, com `id` próprio — no máximo **um por recurso a cada 60 s**: alterações do mesmo recurso no mesmo minuto chegam como UM evento, com o estado da primeira (veja **Janela e orçamento dos `*.updated`**, abaixo) | | **Uma vez por ocorrência** | `coupon.disabled` | Desativar, reativar e desativar de novo o mesmo cupom entrega dois `coupon.disabled`, cada um com `id` próprio | | **Uma vez por ocorrência** | `transaction.refunded` | Cada estorno liquidado da mesma transação é um evento novo, com `id` próprio. Pela API a venda aceita um pedido de reembolso, mas pode receber outro estorno por fora dele (uma devolução de Pix, por exemplo) — e esse também gera evento | | **Uma vez por ocorrência** | `transaction.dispute`, `transaction.chargeback`, `receivable.dispute`, `receivable.chargeback` | Disputa e chargeback podem reabrir: a disputa encerrada a seu favor volta, o chargeback revertido é cobrado de novo. Cada reabertura na mesma venda (ou no mesmo recebível) é um evento novo, com `id` próprio. A primeira disputa e o primeiro chargeback chegam com o mesmo `id` de sempre | **Janela e orçamento dos `*.updated`.** Cada `*.updated` sai no máximo **uma vez por recurso a cada 60 s**: a ocorrência é o `updatedAt` do recurso truncado ao minuto (UTC), então dez alterações do mesmo cliente no mesmo minuto chegam como UM `customer.updated`, com o estado da **primeira** — o evento é um snapshot congelado e assinado, nunca reescrito (a exceção é `subscription.updated`, cuja ocorrência é a versão do conteúdo: cada mudança da assinatura é um evento). E cada conta tem um **orçamento de `*.updated` por hora, por ambiente**: **5 000** no live e **300** no sandbox. Acima dele, os `*.updated` restantes daquela hora **não são emitidos** — a sua request continua respondendo normalmente (`2xx`); só o webhook fica de fora. Os eventos de dinheiro e de estado (`*.created`, `*.approved`, `*.paid`, `*.canceled`, `*.deleted`, `transaction.refunded`, disputa e chargeback…) não têm janela nem orçamento: saem sempre. Por isso trate um `*.updated` como o **sinal de que o recurso mudou** e, quando precisar do estado atual, leia-o pela API (`GET`) — o que vale para todo evento, porque a ordem de entrega não é garantida. Retentativas, reentregas e reenvios da **mesma** ocorrência chegam sempre com o **mesmo** `id`: deduplique por ele. Duas ocorrências do mesmo tipo no mesmo recurso chegam com `id`s **diferentes** — não descarte um `customer.updated` porque já recebeu outro do mesmo cliente, nem um `transaction.refunded` porque a venda já tinha um estorno, nem um `transaction.dispute` porque a venda já esteve em disputa. Como a ordem de entrega não é garantida, use o estado do recurso em `payload.object` (no estorno, os valores estornados da transação) para decidir o que vale. Os eventos de catálogo saem **um por recurso afetado**: uma operação que mexe em várias variantes de um produto (criar o produto com variantes, excluí-lo, a retirada do ar pela moderação) entrega o evento do produto e um `variant.*` para cada variante, cada um com `id` próprio. Veja o [Catálogo](/docs/guide/webhook-events/event-catalog#produtos). #### Estrutura do Objeto Evento (respostas da API) Itens de listagem de events (com dispatches aninhados; envelope paginado com `merchant`/`_links` no root): ```json { "id": "wbh_01hqzvabc", "type": "transaction.approved", "resource": "transaction", "resourceId": "tra_987654321", "source": "automatic", "delivered": true, "attempts": 1, "data": { "id": "tra_987654321", "status": "approved", "amount": 1500 }, "createdAt": "2026-06-20T17:56:32.000Z", "dispatches": [ { "id": "wdi_01hqzvabc", "status": "success", "...": "..." } ] } ``` | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | ID do evento (`wbh_*`) | | `type` | string | Tipo do evento (ver [Catálogo](/docs/guide/webhook-events/event-catalog)) | | `resource` | string | Recurso afetado (ex.: `transaction`) | | `resourceId` | string \| null | ID público do recurso | | `source` | string | Origem do evento (`automatic`, `api`) | | `account` | string | **Só para plataformas de marketplace**, nos eventos repassados de uma sub-conta: o `id` (`bus_…`) da sub-conta a que o evento pertence. Ausente nos eventos da própria conta. Presente também no envelope entregue à sua URL — veja [Marketplace](/docs/guide/marketplace/overview#webhooks) | | `delivered` | boolean | Se a entrega foi concluída com sucesso | | `attempts` | integer | Total de tentativas de entrega | | `data` | objeto | O objeto do recurso (mesma forma da leitura do recurso) | | `createdAt` | string (ISO) | Data de criação do evento | | `dispatches` | array | Tentativas de entrega aninhadas (ver [Dispatches](/docs/guide/webhook-dispatches/overview)) | | `merchant` / `_links` | object | Presentes na leitura individual e no envelope da lista | > O reenvio (`/resend`) responde `202 Accepted` com `{ "accepted": true, "id": "wbh_..." }`. #### Recursos Disponíveis | Recurso | Método | Caminho | Descrição | |---------|--------|---------|-----------| | [Listar Eventos](/docs/guide/webhook-events/list) | GET | `/v1/webhooks/events` | Lista eventos com paginação e filtros (dispatches aninhados) | | Recuperar Evento | GET | `/v1/webhooks/events/{eventId}` | Recupera um evento e seus dispatches | | [Reenviar Evento](/docs/guide/webhook-events/resend) | POST | `/v1/webhooks/events/{eventId}/resend` | Reentrega o evento a todos os endpoints elegíveis | > 🔒 **Antes de processar qualquer evento em produção, verifique a assinatura HMAC.** Veja > [Verificando Assinaturas de Webhook](/docs/guide/webhook-events/verifying-signatures) para o passo a passo e exemplos de código. #### Tipos de Eventos A lista completa e autoritativa está no [Catálogo de Eventos de Webhook](/docs/guide/webhook-events/event-catalog). Alguns dos principais tipos: | Categoria | Tipo de Evento | Descrição | |-----------|----------------|-----------| | Transações | `transaction.created` | Quando uma nova transação é criada | | Transações | `transaction.approved` | Quando uma transação é aprovada | | Transações | `transaction.failed` | Quando uma tentativa de transação falha | | Transações | `transaction.canceled` | Quando uma transação é cancelada | | Transações | `transaction.refunded` | Quando uma transação é estornada | | Clientes | `customer.created` | Quando um novo cliente é criado | | Clientes | `customer.updated` | Quando um cliente é atualizado | | Clientes | `customer.deleted` | Quando um cliente é excluído | | Endereços | `customer.address.created` | Quando um endereço de cliente é criado | | Cartões | `card.created` | Quando um novo cartão é registrado | | Cartões | `card.deleted` | Quando um cartão é removido | | Carteiras | `wallet.created` | Quando uma nova carteira é criada | | Carteiras | `wallet.enabled` | Quando uma carteira é ativada | | Recebíveis | `receivable.paid` | Quando um recebível é pago/liberado | | Webhooks | `webhook.created` | Quando um novo webhook endpoint é configurado | #### Casos de Uso Comuns - **Monitoramento de Integrações**: acompanhamento da saúde das integrações via webhook - **Recuperação de Eventos**: identificação e reenvio de eventos não entregues - **Auditoria**: histórico completo das notificações geradas - **Depuração**: análise de falhas de entrega via [Dispatches](/docs/guide/webhook-dispatches/overview) #### Melhores Práticas 1. **Monitoramento Regular**: verifique o status de entrega dos eventos. 2. **Reenvio Seletivo**: use o reenvio apenas para eventos que não foram entregues com sucesso. 3. **Filtragem**: ao listar, use os filtros (`type`, `status`, `resource`, `endpoint`, datas). 4. **Deduplicação**: processe cada `id` de evento (`wbh_...`) apenas uma vez — e trate o estado do recurso em `payload.object` como a verdade (veja [Retenção](#retenção)). Deduplique pelo `id`, nunca por recurso e tipo: os tipos por ocorrência trazem um `id` novo a cada alteração ou estorno. Num `*.updated`, releia o recurso (`GET`) se precisar do estado atual: ele chega no máximo uma vez por minuto por recurso, com o estado da primeira alteração do minuto, e tem orçamento por hora (veja [Uma vez por recurso × uma vez por ocorrência](#uma-vez-por-recurso--uma-vez-por-ocorrência)). 5. **Filas**: processe os eventos de forma assíncrona e resiliente. #### Integração com Outros Recursos - **Webhook Endpoints**: os eventos são entregues aos endpoints configurados. - **Webhook Dispatches**: cada tentativa de entrega é um dispatch auditável. - **Transactions / Customers / Cards / Wallets / Finance / Checkout**: geram eventos em mudanças de estado (ver [Catálogo](/docs/guide/webhook-events/event-catalog)). --- ## Eventos de webhook - Listar Fonte: https://selectwin.io/docs/guide/webhook-events/list #### Visão Geral Este endpoint lista os eventos de webhook gerados na sua conta, com paginação e filtros. Cada item traz os **dispatches aninhados** (tentativas de entrega). É a ferramenta para monitorar o histórico de eventos e diagnosticar falhas. #### Descrição A listagem oferece uma visão das notificações geradas. Você pode filtrar por tipo de evento, recurso, status de entrega, endpoint de destino e intervalo de datas. #### Requisição ``` GET /v1/webhooks/events ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:read`. ##### Parâmetros de Consulta | Parâmetro | Tipo | Descrição | Exemplo | |-----------|------|-----------|---------| | `offset` | integer | Posição inicial (padrão `0`) | `0` | | `limit` | integer | Máximo de registros (padrão `20`, máximo `100`) | `20` | | `sort` | string | Expressão de ordenação (aceita `ascending`/`descending`) | `descending` | | `type` | string | Filtra pelo tipo de evento (ver [Catálogo](/docs/guide/webhook-events/event-catalog)) | `transaction.approved` | | `resource` | string | Filtra pelo recurso afetado | `transaction` | | `resourceId` | string | Filtra pelo ID do recurso | `tra_987654321` | | `status` | string | Filtra pelo status de entrega: `delivered` ou `failed` | `failed` | | `eventId` | string | Filtra por um ID de evento específico | `wbh_01hqzvabc` | | `endpoint` | string | Filtra pela URL exata do endpoint de destino | `https://webhooks.mydomain.com/selectwin` | | `daterange` | string (date) | Filtra por um único dia de criação | `2026-06-20` | | `daterangegt` / `daterangegte` | string (date-time) | Criado após / a partir de | `2026-06-01T00:00:00Z` | | `daterangelt` / `daterangelte` | string (date-time) | Criado antes de / até | `2026-06-30T23:59:59Z` | > Os aliases legados `resourceid` e `eventid` (minúsculos) também são aceitos. #### Resposta ##### Sucesso (200 OK) Envelope paginado (com `merchant`/`_links` no root); cada item de `data` é um evento com `dispatches[]` aninhados. ```json { "offset": 0, "limit": 20, "total": 100, "page": { "offset": { "first": 0, "prev": 0, "next": 20, "last": 80 }, "current": 1, "total": 5 }, "hasMore": true, "data": [ { "id": "wbh_01hqzvabc", "type": "transaction.approved", "resource": "transaction", "resourceId": "tra_987654321", "source": "automatic", "delivered": true, "attempts": 1, "data": { "id": "tra_987654321", "amount": 1500, "status": "approved" }, "createdAt": "2026-06-20T17:56:32.000Z", "dispatches": [ { "id": "wdi_01hqzvabc", "eventId": "wbh_01hqzvabc", "endpointId": "wbe_01hqzvabc", "endpoint": "https://webhooks.mydomain.com/selectwin", "status": "success", "attempts": 1, "responseStatusCode": 200, "responseTime": 120, "nextAttemptAt": null, "lastAttemptAt": "2026-06-20T17:56:33.100Z", "successAt": "2026-06-20T17:56:33.150Z", "createdAt": "2026-06-20T17:56:33.000Z" } ] } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/webhooks/events", "method": "GET", "description": "List all webhook events." } } } ``` ##### Atributos do Envelope | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` / `limit` / `total` / `page` / `hasMore` | | Paginação padrão | | `data` | array | Eventos com `dispatches[]` aninhados | | `merchant` | object | Bloco do lojista | | `_links` | object | HATEOAS (`self`) | ##### Atributos do Evento (item de `data`) | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | ID do evento (`wbh_*`) | | `type` | string | Tipo do evento | | `resource` | string | Recurso afetado | | `resourceId` | string \| null | ID público do recurso | | `source` | string | Origem (`automatic`, `api`) | | `delivered` | boolean | Se a entrega foi concluída com sucesso | | `attempts` | integer | Total de tentativas | | `data` | objeto | Objeto do recurso no momento do evento | | `createdAt` | string (ISO) | Data de criação | | `dispatches` | array | Tentativas de entrega (ver [Dispatches](/docs/guide/webhook-dispatches/list)) | #### Exemplo de Uso ##### Consulta Básica ``` GET /v1/webhooks/events ``` ##### Filtrar por Status de Entrega ``` GET /v1/webhooks/events?status=failed ``` ##### Filtrar por Tipo de Evento ``` GET /v1/webhooks/events?type=transaction.approved ``` ##### Filtrar por Endpoint e Ordenar Decrescente ``` GET /v1/webhooks/events?endpoint=https://webhooks.mydomain.com/selectwin&sort=descending ``` #### Códigos de Erro | Código HTTP | Descrição | |-------------|-----------| | 401 | Não autorizado (`SelectKey` ausente/inválida) | | 403 | Sem o escopo `webhooks:read` | | 500 | Erro interno do servidor | --- ## Catálogo de Eventos de Webhook Fonte: https://selectwin.io/docs/guide/webhook-events/event-catalog Lista consolidada e **autoritativa** dos tipos de evento que a Selectwin pode entregar aos seus [Webhook Endpoints](/docs/guide/webhook-endpoints/overview). Ao criar/atualizar um endpoint, o campo `events[]` aceita **exatamente** os valores listados aqui — um valor fora desta lista é rejeitado com `400` na criação/atualização do endpoint. Cada entrega traz o envelope do evento e o cabeçalho `X-Selectwin-Signature` — **sempre verifique a assinatura** ([guia](/docs/guide/webhook-events/verifying-signatures)) antes de processar. #### Transações | Evento | Quando | |--------|--------| | `transaction.created` | Transação criada | | `transaction.pending` | Aguardando pagamento (boleto/PIX) ou processamento | | `transaction.failed` | Tentativa de transação falhou | | `transaction.approved` | Transação aprovada | | `transaction.canceled` | Transação cancelada | | `transaction.chargeback` | Chargeback registrado. Sai de novo se o chargeback voltar depois de revertido (veja abaixo) | | `transaction.refunded` | Transação estornada (total/parcial) | | `transaction.fraud-review` | Em análise de fraude | | `transaction.pre-authorized` | Pré-autorização aprovada (captura manual) | | `transaction.unauthorized` | Pré-autorização não autorizada | | `transaction.awaiting` | Aguardando ação/análise | | `transaction.dispute` | Transação em disputa. Sai de novo se a disputa reabrir depois de encerrada a seu favor (veja abaixo) | **Disputa e chargeback que reabrem.** Uma disputa encerrada a seu favor pode voltar, e um chargeback revertido pode ser cobrado de novo. Cada vez que isso acontece na mesma venda chega um `transaction.dispute` ou `transaction.chargeback` **novo**, com `id` próprio, e o mesmo vale para o `receivable.dispute` e o `receivable.chargeback` de cada recebível. A primeira disputa e o primeiro chargeback chegam como sempre chegaram. Já a vitória, que devolve a venda a `approved`, não gera um segundo `transaction.approved`, porque a aprovação é uma vez por venda: para saber o desfecho, leia a transação. Veja [Uma vez por recurso × uma vez por ocorrência](/docs/guide/webhook-events/overview#uma-vez-por-recurso--uma-vez-por-ocorrência). ##### Pagamento combinado (2 cartões ou cartão + Pix) Num pedido pago com **2 cartões** (`dualcredit`) ou **cartão + Pix** (`creditpix`), cada parte é uma transação com o bloco `paymentGroup` (`id`, `method`, `status`, `legIndex`, `legCount`, `isPrimary`, `totalAmount` e `transactionIds` — as transações atuais das partes do pedido, para ligar as cobranças). Uma parte só é anunciada como aprovada quando o **pedido inteiro** está pago, então não entregue nada por uma parte antes disso: - **Pedido pago:** sai `transaction.approved` de **cada parte**, uma vez, junto com `checkout.session.completed` (que traz `paymentGroupId` e `transactionIds[]`). Depois disso, cada parte é como qualquer transação (estorno, contestação). - **Parte do Pix:** sai `transaction.pending` (o QR) e, se o pedido não fechar, o fim dele (`transaction.canceled` ou `transaction.failed`). - **Dinheiro voltando** (`transaction.refunded`, `transaction.chargeback`, `transaction.dispute`): sai sempre. Exemplo: o Pix pago depois de o pedido ter falhado é estornado automaticamente e chega só como `transaction.refunded`, nunca como `transaction.approved`. - **Não saem:** a reserva do cartão (`transaction.pre-authorized`), a recusa ou o cancelamento de uma tentativa de cartão (o comprador troca de cartão na mesma página) e a aprovação de uma parte antes de o pedido estar pago. #### Clientes | Evento | Quando | |--------|--------| | `customer.created` | Cliente criado | | `customer.updated` | Cliente atualizado | | `customer.deleted` | Cliente removido | | `customer.address.created` | Endereço do cliente criado | | `customer.address.updated` | Endereço do cliente atualizado | | `customer.address.deleted` | Endereço do cliente removido | #### Cartões | Evento | Quando | |--------|--------| | `card.created` | Cartão tokenizado/registrado | | `card.updated` | Cartão atualizado | | `card.deleted` | Cartão removido | | `card.expired` | Cartão expirado | #### Assinaturas | Evento | Quando | |--------|--------| | `subscription.created` | Assinatura criada | | `subscription.pending` | Assinatura aguardando confirmação | | `subscription.active` | Assinatura ativada (em dia) | | `subscription.canceled` | Assinatura cancelada | | `subscription.pastdue` | Cobrança de ciclo falhou; em retentativa | | `subscription.unpaid` | Retentativas esgotadas sem pagamento | | `subscription.trialing` | Em período de trial | | `subscription.paused` | Assinatura pausada | | `subscription.updated` | Assinatura atualizada | #### Recebíveis | Evento | Quando | |--------|--------| | `receivable.created` | Recebível gerado | | `receivable.pending` | Aguardando liquidação | | `receivable.paid` | Recebível liberado/pago | | `receivable.canceled` | Recebível cancelado | | `receivable.chargeback` | Chargeback afeta o recebível. Sai de novo se o chargeback voltar depois de revertido | | `receivable.refunded` | Recebível reembolsado | | `receivable.dispute` | Disputa afeta o recebível. Sai de novo se a disputa reabrir depois de revertida | | `receivable.scheduled` | Recebível agendado para liberação | | `receivable.fraud-hold` | Recebível retido por análise de fraude | #### Carteiras | Evento | Quando | |--------|--------| | `wallet.created` | Carteira criada | | `wallet.deleted` | Carteira removida | | `wallet.pending` | Carteira aguardando validação | | `wallet.disabled` | Carteira desativada | | `wallet.enabled` | Carteira ativada | #### Vendedores (Sellers) Primariamente para plataformas de marketplace, que recebem estes eventos para cada sub-conta via fan-out — veja [Marketplace (sub-contas)](/docs/guide/marketplace/overview). O `payload.object` é a sub-conta (o objeto de conta), e `id` é o `bus_…` que você usa em `onBehalfOf`. | Evento | Quando | |--------|--------| | `seller.created` | Sub-conta/vendedor provisionado | | `seller.updated` | Perfil/configuração atualizados | | `seller.analyzing` | KYC submetido (status → analyzing) | | `seller.approved` | KYC aprovado | | `seller.refused` | KYC recusado | | `seller.blocked` | Conta bloqueada (status → blocked); não cobra nem saca enquanto estiver assim | | `seller.disabled` | Conta desativada (status → disabled); não cobra nem saca enquanto estiver assim | | `seller.reinstated` | Bloqueio ou desativação desfeitos (status → active, ou analyzing quando a conta volta para a análise do KYC) | | `seller.kycRequired` | A análise de KYC precisa ser refeita (status → awaiting-documents); a conta não cobra nem saca até reenviar os documentos pedidos e ser aprovada de novo | #### Saques (Withdrawals) | Evento | Quando | |--------|--------| | `withdrawal.created` | Saque criado | | `withdrawal.pending` | Saque pendente | | `withdrawal.confirmed` | Saque confirmado | | `withdrawal.canceled` | Saque cancelado | | `withdrawal.refused` | Saque recusado | | `withdrawal.processing` | Saque em processamento | | `withdrawal.analysis` | Saque em análise | #### Webhooks | Evento | Quando | |--------|--------| | `webhook.created` | Um novo webhook endpoint foi configurado | | `webhook.updated` | Um webhook endpoint foi atualizado | | `webhook.deleted` | Um webhook endpoint foi removido | | `webhook.disabled` | Um webhook foi desativado por falhas de entrega | #### Checkout | Evento | Quando | |--------|--------| | `checkout.session.created` | Sessão de checkout criada | | `checkout.session.updated` | Sessão de checkout atualizada | | `checkout.session.abandoned` | Sessão abandonada | | `checkout.session.recovered` | Sessão recuperada | | `checkout.session.reactivated` | Sessão reativada | | `checkout.session.expired` | Sessão expirada | | `checkout.session.completed` | A compra da sessão foi **paga** (cartão aprovado, Pix/boleto pago) ou a assinatura dela foi criada. Traz o objeto da sessão + `transactionId`/`subscriptionId` | #### Produtos | Evento | Quando | |--------|--------| | `product.created` | Produto criado. O objeto traz as variantes criadas junto com ele, e cada uma gera também o seu `variant.created` | | `product.updated` | Produto atualizado | | `product.deleted` | Produto removido. O objeto é `{ "id", "resource": "product", "deleted": true }`. Cada variante removida junto gera também o seu `variant.deleted` | | `variant.created` | Variante criada: acrescentada a um produto que já existe ou criada junto com o produto | | `variant.updated` | Variante atualizada, inclusive quando a moderação da Selectwin retira o produto do ar ou o devolve | | `variant.deleted` | Variante removida, sozinha ou junto com o produto. O objeto é `{ "id", "resource": "variant", "deleted": true }` | **Um evento por variante.** Uma operação que mexe em várias variantes de uma vez entrega o evento do produto **e** um `variant.*` para cada variante afetada, nunca um evento só para o lote. Criar um produto com 3 variantes entrega um `product.created` e 3 `variant.created`. Excluir o produto entrega um `product.deleted` e um `variant.deleted` por variante que saiu com ele. Quando a moderação retira o produto do ar, chegam um `product.updated` e um `variant.updated` para cada variante desligada. Quem sincroniza só variantes pode assinar só `variant.*`. Como a ordem de entrega não é garantida, um `variant.created` pode chegar antes do `product.created` do mesmo produto. Nos eventos de criação e de atualização, `payload.object` é o produto (com as variantes) ou a variante, na mesma forma da leitura. #### Cupons | Evento | Quando | |--------|--------| | `coupon.created` | Cupom criado | | `coupon.updated` | Cupom alterado, inclusive quando volta a ficar ativo | | `coupon.disabled` | Cupom desativado, por você ou pela moderação da Selectwin | Cada alteração gera **um** evento: a que desativa o cupom é `coupon.disabled`, e as demais são `coupon.updated`. `payload.object` é o cupom na mesma forma de `GET /v1/coupons/{couponId}`, com `resource: "coupon"`. O cupom que vence, esgota ou é excluído não gera evento. #### Eventos de teste | Evento | Quando | |--------|--------| | `webhook.ping` | Ping sintético gerado por `POST /v1/webhooks/{webhookId}/test` | --- #### Envelope do evento O objeto do recurso é entregue em `payload.object` (não em `data`): ```json { "id": "wbh_01hqzvabc", "type": "transaction.approved", "source": "automatic", "payload": { "object": { "...": "objeto do recurso no momento do evento" } }, "correlationId": null, "updatedAt": "2026-06-20T17:56:33.000Z", "createdAt": "2026-06-20T17:56:33.000Z" } ``` - `type` — o tipo do evento (uma das linhas acima). - `source` — origem do evento (`automatic` para eventos de plataforma; `api` para o ping de teste). - `payload.object` — o objeto do recurso (mesma forma da leitura do recurso). - `correlationId` — id de correlação da requisição originária, quando disponível. - Em fan-out de marketplace, o envelope entregue ao **parent** inclui também `account` (publicId da sub-conta). > Nota: ao **ler** um evento pela API (`GET /v1/webhooks/events`), o objeto do recurso aparece no campo > `data` da resposta da API — equivalente ao `payload.object` entregue ao seu endpoint. #### Atribuição (`utm`) nos eventos De onde veio a venda chega em **`payload.object.utm`** — no topo do objeto do recurso, irmão de `metadata`. Mesmo nome, mesmas chaves, nas duas famílias de evento: ```json { "type": "checkout.session.created", "payload": { "object": { "object": "checkout.session", "id": "sess_fc2b692056e12dc475b1", "status": "active", "utm": { "source": "instagram", "medium": "cpc", "campaign": "lancamento-agosto", "content": "criativo-v2", "term": "curso-online", "fbclid": "IwAR1x...", "gclid": "Cj0KCQ..." }, "metadata": null } } } ``` ```json { "type": "transaction.approved", "payload": { "object": { "id": "tra_68qx2h3skshvd85g", "status": "approved", "utm": { "source": "instagram", "medium": "cpc", "campaign": "lancamento-agosto" } } } } ``` | Chave | O que é | | --- | --- | | `source` | Origem (`instagram`, `google`, `newsletter`) | | `medium` | Meio (`cpc`, `organic`, `email`) | | `campaign` | Campanha | | `content` | Criativo/variação | | `term` | Termo de busca | | `gclid` · `fbclid` · `ttclid` · `msclkid` · `twclid` · `wbraid` · `gbraid` · `irclickid` · `li_fat_id` · `epik` | Identificador de clique do anunciante, como veio. **Só em `checkout.session.*`** | Três regras que valem para os dois eventos: 1. **`utm` é `null` quando não há campanha.** Compra direta não vem com um objeto de chaves nulas — vem `null`. Trate `null` como "tráfego direto". 2. **Chave ausente ≠ chave nula.** Só aparece o que realmente veio: uma campanha sem `term` não traz a chave `term`. 3. **Os nomes são curtos.** A URL do anúncio usa `utm_source`, mas o que você recebe é `source`. A conversão é feita na entrada — você pode continuar mandando `utm_source` quando for você a criar a sessão ou a cobrança. A sessão captura a atribuição **uma vez**, na primeira visita do comprador, e ela é **imutável** dali em diante — o clique que trouxe a venda não muda no meio do checkout. Quando a sessão vira cobrança, os cinco campos UTM descem para a transação sozinhos: o `utm` de `transaction.*` de uma venda de checkout é o mesmo da sessão, sem você fazer nada. ##### Cookies de rastreio (`cookies`) em `checkout.session.*` Os identificadores do navegador do comprador chegam em **`payload.object.cookies`**, irmão de `utm`, com o **nome do cookie** como chave e o valor como veio — é o que o seu CAPI/Conversions API precisa para casar um carrinho abandonado com o anúncio: ```json { "type": "checkout.session.abandoned", "payload": { "object": { "object": "checkout.session", "id": "sess_a1b2c3d4e5f6789012ab", "utm": { "source": "meta", "medium": "cpc", "campaign": "lancamento" }, "cookies": { "_fbp": "fb.1.1790000000000.1234567890", "_fbc": "fb.1.1790000000000.IwAR1x...", "_ga": "GA1.1.1234567890.1790000000", "_gcl_au": "1.1.1234567890.1790000000" } } } } ``` - **`null` quando nada foi capturado** — o comprador saiu antes de a página rodar, ou não consentiu. Não é um objeto vazio. - As chaves são as do navegador (`_fbp`, não `fbp`); cookies de GA4 com stream aparecem como `_ga_`. - A sessão os **acumula** enquanto o comprador navega; um evento posterior (`reactivated`, `expired`) pode trazer mais chaves que o `created`. - Na venda aprovada eles já chegavam em `transaction.*` como `metadata.cookies`; agora também chegam na sessão. #### Boas práticas - **Assine a menor lista de eventos** que você precisa — evita tráfego desnecessário. - **Deduplique pelo `id`** do evento (`wbh_...`); reentregas podem repetir um evento. - **Responda `2xx` rápido** (o prazo é 30 s, mas acima de 5 s o endpoint passa à fila agendada) e processe de forma assíncrona — veja [Proibição de Polling](/docs/reference/proibicao-de-polling). - A lista pode evoluir; novos tipos podem ser adicionados. Ignore com segurança tipos que você não trata. --- ## Eventos de webhook - Reenviar Fonte: https://selectwin.io/docs/guide/webhook-events/resend #### Visão Geral Este endpoint solicita o **reenvio (replay)** de um evento aos endpoints elegíveis para aquele tipo de evento. É útil quando ocorrem falhas na entrega original ou quando você precisa ressincronizar sistemas externos. #### Descrição Ao receber o `eventId`, a API localiza o evento, coleta todos os endpoints (ativos, no mesmo ambiente) inscritos naquele tipo e dispara uma nova entrega (replay forçado) para cada um. Para reentregar a **um endpoint específico**, use o [Reenviar Dispatch](/docs/guide/webhook-dispatches/redispatch). #### Requisição ``` POST /v1/webhooks/events/{eventId}/resend ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:update`. Não há corpo de requisição. ##### Parâmetros de Caminho | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `eventId` | string | Sim | ID do evento a reenviar (`wbh_*`) | `wbh_01hqzvabc` | #### Resposta ##### Aceito (202 Accepted) ```json { "accepted": true, "id": "wbh_01hqzvabc" } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `accepted` | boolean | Sempre `true` quando o reenvio foi disparado | | `id` | string | ID do evento reenviado (`wbh_*`) | #### Exemplo de Uso ``` POST /v1/webhooks/events/wbh_01hqzvabc/resend ``` #### Considerações 1. **Processamento Assíncrono**: a resposta `202` indica que o reenvio foi disparado; o resultado de cada entrega aparece nos [Dispatches](/docs/guide/webhook-dispatches/list). 2. **Idempotência**: garanta que seu endpoint processe o mesmo evento múltiplas vezes sem efeitos colaterais. Deduplique pelo `id` do evento. 3. **Replay forçado**: o reenvio dispara a entrega mesmo que já tenha havido sucesso anterior naquele endpoint. 4. **Escopo de ambiente**: o reenvio alcança apenas endpoints do mesmo ambiente (sandbox/live) da sua chave de API. #### Códigos de Erro | Código HTTP | Descrição | |-------------|-----------| | 401 | Não autorizado (`SelectKey` ausente/inválida) | | 403 | Sem o escopo `webhooks:update` | | 404 | Evento não encontrado (`webhookEventNotFound`) | | 500 | Erro interno do servidor | #### Casos de Uso Comuns 1. **Recuperação de Falhas**: reenviar eventos que falharam por problemas temporários no endpoint. 2. **Ressincronização**: garantir que sistemas externos receberam todos os eventos após uma manutenção. 3. **Diagnóstico**: identificar se as falhas de entrega são persistentes ou temporárias. --- ## Verificando Assinaturas de Webhook Fonte: https://selectwin.io/docs/guide/webhook-events/verifying-signatures > **Por que isso importa:** qualquer pessoa que descubra a URL do seu endpoint pode enviar requisições > forjadas. Antes de confiar em **qualquer** payload de webhook, verifique a assinatura criptográfica > para garantir que ele foi realmente enviado pela Selectwin e não foi alterado em trânsito. Esta é uma > etapa **obrigatória** para integrações em produção que movimentam dinheiro. #### Como funciona A cada entrega, a Selectwin calcula uma assinatura **HMAC-SHA256** sobre o **corpo bruto (raw body) do evento** usando o **segredo do seu endpoint** e envia o resultado no cabeçalho: ```http X-Selectwin-Signature: sha256= ``` - O valor após `sha256=` é o HMAC-SHA256 em **hexadecimal minúsculo**. - O segredo é o `whsec_...` retornado **uma única vez** na criação do endpoint (`POST /v1/webhooks` → campo `secret`) ou ao rotacioná-lo (`POST /v1/webhooks/{webhookId}/rotate-secret`). Guarde-o com segurança (variável de ambiente, cofre). - A mensagem assinada é **exatamente o corpo JSON recebido**. Para reproduzir a assinatura você precisa do **corpo bruto** — não re-serialize o JSON (reordenar chaves ou mudar espaçamento quebra a verificação). ##### Cabeçalhos enviados em cada entrega | Cabeçalho | Conteúdo | |-----------|----------| | `X-Selectwin-Signature` | `sha256=` — HMAC-SHA256 do corpo bruto (**cabeçalho principal a verificar**) | | `X-Selectwin-Signature-v1` | `t=,v1=` — esquema à prova de replay, HMAC-SHA256 de `.` (opcional) | | `X-Selectwin-Timestamp` | O mesmo `` (segundos) presente no `v1=` | | `X-Selectwin-Signature-Previous` | Só nas 24 h depois de uma troca do segredo: `sha256=`, calculado com o segredo **anterior** ([veja abaixo](#durante-a-troca-do-segredo)) | | `X-Selectwin-Signature-v1-Previous` | Só nas 24 h depois de uma troca do segredo: `t=,v1=`, com o segredo **anterior** | | `X-Selectwin-Event` | O tipo do evento (ex.: `transaction.approved`) | | `X-Selectwin-Event-Id` | O ID do evento (`wbh_...`) | | `X-Selectwin-Delivery` | O ID do dispatch desta tentativa (`wdi_...`) | | `User-Agent` | `Selectwin-Webhook/1.0` | | `Authorization` | Presente apenas se você configurou `headerAuthorization` no endpoint (valor cru, sem prefixo de tipo) | > O `X-Selectwin-Signature` (legado `sha256=`) é o cabeçalho **principal** e suficiente para validar a > origem. O `X-Selectwin-Signature-v1` é oferecido **adicionalmente** para receptores que queiram > proteção extra contra replay (vincula o timestamp à assinatura). ```mermaid sequenceDiagram autonumber participant SW as Selectwin participant EP as Seu Endpoint Note over SW: assina = HMAC_SHA256(rawBody, whsec_…) SW->>EP: POST evento + X-Selectwin-Signature: sha256=assina Note over EP: recalcula HMAC_SHA256(rawBody, whsec_…) alt assinaturas batem (comparação constant-time) EP-->>SW: 200 OK · processa o evento else não batem / ausente EP-->>SW: 401 · descarta o evento end ``` #### Passos da verificação 1. Leia o **corpo bruto** da requisição (bytes/string exatos, antes de qualquer parsing JSON). 2. Calcule `HMAC-SHA256(rawBody, endpointSecret)` e represente em hexadecimal. 3. Compare com o valor após `sha256=` no cabeçalho `X-Selectwin-Signature`, usando uma **comparação de tempo constante** (evita ataques de timing). Nas 24 h depois de uma troca do segredo, compare também com o `X-Selectwin-Signature-Previous` ([veja abaixo](#durante-a-troca-do-segredo)). 4. Se não baterem (ou o cabeçalho estiver ausente), responda `401` e **não** processe o evento. > ⚠️ **Use o corpo bruto.** Frameworks que fazem `JSON.parse` automático e te entregam um objeto já > alteraram a representação. Configure o parser para preservar o corpo bruto (ver exemplos abaixo). ##### Durante a troca do segredo Ao rotacionar o segredo (`POST /v1/webhooks/{webhookId}/rotate-secret`), o segredo novo volta **uma única vez**, na resposta, e o anterior continua valendo por **24 horas**. Nessa janela, cada entrega vai assinada com os dois: | Cabeçalhos | Assinados com | |------------|---------------| | `X-Selectwin-Signature` · `X-Selectwin-Signature-v1` | o segredo **novo** | | `X-Selectwin-Signature-Previous` · `X-Selectwin-Signature-v1-Previous` | o segredo **anterior**, no mesmo formato (`sha256=` e `t=,v1=`) | Assim o seu receptor não perde entrega enquanto você troca a chave: com o segredo anterior, a assinatura bate com o `-Previous`; com o novo, bate com o cabeçalho principal. Para cobrir os dois casos sem coordenar o deploy, aceite a entrega quando o HMAC calculado com o segredo que você tem bater com o `X-Selectwin-Signature` **ou** com o `X-Selectwin-Signature-Previous`. - Passadas as 24 horas, os cabeçalhos `-Previous` deixam de vir e só o segredo novo assina. - Rotacionar de novo dentro da janela encerra na hora o segredo mais antigo: o anterior passa a ser o que acabou de ser trocado, com 24 horas novas. - Um endpoint que ainda não tinha segredo não recebe os cabeçalhos `-Previous` na primeira rotação: não há segredo anterior para assinar. #### Exemplos ##### Node.js (Express) ```js const express = require('express'); const crypto = require('crypto'); const app = express(); const SECRET = process.env.SELECTWIN_WEBHOOK_SECRET; // whsec_... // IMPORTANTE: capture o corpo BRUTO (Buffer), não use express.json() aqui. app.post('/webhooks/selectwin', express.raw({ type: 'application/json' }), (req, res) => { const header = req.get('X-Selectwin-Signature') || ''; const received = header.startsWith('sha256=') ? header.slice(7) : header; const expected = crypto .createHmac('sha256', SECRET) .update(req.body) // req.body é um Buffer (corpo bruto) .digest('hex'); // Comparação de tempo constante const ok = received.length === expected.length && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected)); if (!ok) return res.status(401).send('invalid signature'); const event = JSON.parse(req.body.toString('utf8')); // ... processe event.type / event.payload.object ... res.status(200).send('ok'); }); ``` ##### Python (Flask) ```python import hmac, hashlib, os from flask import Flask, request, abort app = Flask(__name__) SECRET = os.environ["SELECTWIN_WEBHOOK_SECRET"].encode() # whsec_... @app.post("/webhooks/selectwin") def selectwin_webhook(): raw = request.get_data() # corpo bruto (bytes) header = request.headers.get("X-Selectwin-Signature", "") received = header[7:] if header.startswith("sha256=") else header expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest() if not hmac.compare_digest(received, expected): # constant-time abort(401) event = request.get_json() # ... processe event["type"] / event["payload"]["object"] ... return "ok", 200 ``` ##### PHP ```php **Não existe** um endpoint dedicado de "analytics" de dispatches. Para métricas de entrega (taxa de > sucesso/falha por período), use a [listagem](/docs/guide/webhook-dispatches/list) com filtros de `status`, `daterange*` e > `onlyErrored`. Veja [Analytics](/docs/guide/webhook-dispatches/analytics). #### Identificadores Os dispatches usam o prefixo de ID `wdi_` (ex.: `wdi_01hqzvabc`). Trate-o como string opaca. Os IDs relacionados seguem o mesmo padrão: evento `wbh_`, endpoint `wbe_`. --- ## Entregas de webhook - Listar Fonte: https://selectwin.io/docs/guide/webhook-dispatches/list #### Visão Geral Este endpoint retorna uma lista paginada das **tentativas de entrega** (dispatches) de eventos de webhook da sua conta, com filtros por status, endpoint, código HTTP de resposta e intervalo de datas. É a ferramenta principal para auditar entregas e diagnosticar falhas por endpoint. #### Descrição Cada item representa uma entrega de um evento a um endpoint específico, com o resultado da tentativa (`status`, `responseStatusCode`, `responseTime`) e os carimbos de tempo relevantes. #### Requisição ``` GET /v1/webhooks/dispatches ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:read`. ##### Parâmetros de Consulta | Parâmetro | Tipo | Descrição | Exemplo | |-----------|------|-----------|---------| | `offset` | integer | Ponto de início dos resultados (padrão `0`) | `0` | | `limit` | integer | Máximo de registros (padrão `20`, máximo `100`) | `20` | | `sort` | string | Expressão de ordenação (aceita `ascending`/`descending`) | `descending` | | `status` | string | Filtra pelo resultado: `pending`, `success`, `failed`, `retrying`, `canceled` | `success` | | `responseStatus` | integer | Filtra pelo código HTTP retornado pelo seu servidor | `200` | | `onlyErrored` | boolean | Se `true`, retorna apenas entregas com falha | `false` | | `endpoint` | string | Filtra pela URL exata do endpoint de destino | `https://webhooks.mydomain.com/selectwin` | | `daterange` | string (date) | Filtra por um único dia de criação | `2026-06-20` | | `daterangegt` / `daterangegte` | string (date-time) | Criado após / a partir de | `2026-06-01T00:00:00Z` | | `daterangelt` / `daterangelte` | string (date-time) | Criado antes de / até | `2026-06-30T23:59:59Z` | > Os aliases legados `responsestatus` e `onlyerrored` (minúsculos) também são aceitos. #### Resposta ##### Sucesso (200 OK) Envelope paginado padrão (com `merchant`/`_links` no root); cada item de `data` é um dispatch. ```json { "offset": 0, "limit": 20, "total": 50, "page": { "offset": { "first": 0, "prev": 0, "next": 20, "last": 40 }, "current": 1, "total": 3 }, "hasMore": true, "data": [ { "id": "wdi_01hqzvabc", "eventId": "wbh_01hqzvabc", "endpointId": "wbe_01hqzvabc", "endpoint": "https://webhooks.mydomain.com/selectwin", "status": "success", "attempts": 1, "responseStatusCode": 200, "responseTime": 120, "nextAttemptAt": null, "lastAttemptAt": "2026-06-20T17:56:33.000Z", "successAt": "2026-06-20T17:56:33.050Z", "createdAt": "2026-06-20T17:56:33.000Z" } ], "merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false }, "_links": { "self": { "href": "https://api.selectwin.io/v1/webhooks/dispatches", "method": "GET", "description": "List all webhook dispatches." } } } ``` ##### Atributos do Envelope | Atributo | Tipo | Descrição | |----------|------|-----------| | `offset` / `limit` / `total` / `page` / `hasMore` | | Paginação padrão | | `data` | array | Dispatches (ver abaixo) | | `merchant` | object | Bloco do lojista | | `_links` | object | HATEOAS (`self`) | ##### Atributos do Dispatch (item de `data`) | Atributo | Tipo | Descrição | |----------|------|-----------| | `id` | string | Identificador do dispatch (`wdi_`) | | `eventId` | string \| null | ID do evento associado (`wbh_`) | | `endpointId` | string \| null | ID do endpoint de destino (`wbe_`) | | `endpoint` | string | URL do endpoint de destino | | `status` | string | `pending`, `success`, `failed`, `retrying` ou `canceled` | | `attempts` | integer | Número de tentativas realizadas | | `responseStatusCode` | integer \| null | Código HTTP retornado pelo seu servidor | | `responseTime` | integer \| null | Tempo de resposta em milissegundos | | `nextAttemptAt` | string (ISO) \| null | Próxima tentativa agendada (quando `retrying`) | | `lastAttemptAt` | string (ISO) \| null | Data da última tentativa | | `successAt` | string (ISO) \| null | Data em que a entrega foi confirmada | | `createdAt` | string (ISO) | Data de criação do dispatch | #### Exemplo de Uso ##### Consulta Básica ``` GET /v1/webhooks/dispatches ``` ##### Apenas Entregas com Falha ``` GET /v1/webhooks/dispatches?onlyErrored=true ``` ##### Filtrar por Status e Endpoint ``` GET /v1/webhooks/dispatches?status=failed&endpoint=https://webhooks.mydomain.com/selectwin ``` ##### Filtrar por Período ``` GET /v1/webhooks/dispatches?daterangegte=2026-06-01T00:00:00Z&daterangelte=2026-06-30T23:59:59Z ``` #### Códigos de Erro | Código HTTP | Descrição | |-------------|-----------| | 401 | Não autorizado (`SelectKey` ausente/inválida) | | 403 | Sem o escopo `webhooks:read` | | 500 | Erro interno do servidor | --- ## Entregas de webhook - Métricas Fonte: https://selectwin.io/docs/guide/webhook-dispatches/analytics > **Nota:** não existe um endpoint dedicado de analytics de dispatches (`/v1/webhooks/dispatches/analytics` > **não** está disponível). As métricas de entrega são derivadas da [listagem de dispatches](/docs/guide/webhook-dispatches/list) > usando os filtros de `status`, `responseStatus`, `onlyErrored` e intervalo de datas (`daterange*`). #### Visão Geral Para acompanhar a saúde de entrega dos seus webhooks (taxa de sucesso/falha, latência, endpoints instáveis), consulte a [listagem de dispatches](/docs/guide/webhook-dispatches/list) filtrando por período e status, e agregue os resultados do seu lado. #### Como obter métricas de um período Use os filtros de data e status do endpoint de listagem: ``` GET /v1/webhooks/dispatches?daterangegte=2026-06-01T00:00:00Z&daterangelte=2026-06-30T23:59:59Z ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:read`. ##### Receitas úteis | Objetivo | Consulta | |----------|----------| | Total de entregas no período | listar com `daterangegte`/`daterangelte` e ler `total` | | Apenas entregas bem-sucedidas | `...&status=success` | | Apenas entregas com falha | `...&onlyErrored=true` (ou `status=failed`) | | Entregas que retornaram um código específico | `...&responseStatus=500` | | Saúde de um endpoint específico | `...&endpoint=https://webhooks.mydomain.com/selectwin` | Cada item retornado traz `status`, `attempts`, `responseStatusCode`, `responseTime`, `lastAttemptAt`, `successAt` e `createdAt` (ver [atributos do dispatch](/docs/guide/webhook-dispatches/list#atributos-do-dispatch-item-de-data)) — agregue por dia/endpoint do seu lado para construir gráficos e taxas. #### Cálculos sugeridos - **Taxa de sucesso**: `success ÷ total` por dia (campo `total` do envelope, filtrando por `status`). - **Latência**: média/percentis de `responseTime` (ms). - **Instabilidade**: média de `attempts` por dispatch — valores altos indicam endpoints que dependem de retentativas. - **Diagnóstico de falhas**: concentração de `5xx` em `responseStatusCode` indica indisponibilidade do seu servidor; `4xx` costuma indicar problema de configuração (autenticação, rota). #### Melhores Práticas 1. **Pagine** os resultados (`limit` máximo `100`) e agregue do seu lado para períodos longos. 2. **Combine filtros** (`onlyErrored=true` + `endpoint=...`) para investigar falhas de um endpoint específico. 3. **Acompanhe `responseStatusCode` e `attempts`** para distinguir falhas transitórias de persistentes. 4. **Para reprocessar** uma falha pontual, use o [Reenviar Dispatch](/docs/guide/webhook-dispatches/redispatch). #### Códigos de Erro | Código HTTP | Descrição | |-------------|-----------| | 401 | Não autorizado (`SelectKey` ausente/inválida) | | 403 | Sem o escopo `webhooks:read` | | 500 | Erro interno do servidor | --- ## Entregas de webhook - Reenviar Fonte: https://selectwin.io/docs/guide/webhook-dispatches/redispatch #### Visão Geral Este endpoint **reenvia (redeliver) uma entrega específica** ao seu endpoint de destino. Diferente do [reenvio de evento](/docs/guide/webhook-events/resend) — que reentrega o evento a **todos** os endpoints elegíveis —, aqui a reentrega é direcionada **ao endpoint exato daquele dispatch**, reaproveitando o mesmo registro de entrega. É útil para reprocessar uma falha pontual sem afetar as demais entregas do mesmo evento. #### Descrição Ao receber o `dispatchId`, a API localiza o dispatch (junto do evento e do endpoint associados), reconstrói o payload do evento e dispara uma nova tentativa de entrega para aquele endpoint específico. A resposta confirma que o reenvio foi disparado; o recebimento efetivo depende do seu servidor. #### Requisição ``` POST /v1/webhooks/dispatches/{dispatchId}/redispatch ``` Cabeçalho: `SelectKey: sk_live_...` (ou `sk_test_...`). Escopo necessário: `webhooks:update`. Não há corpo de requisição. ##### Parâmetros de Caminho | Parâmetro | Tipo | Obrigatório | Descrição | Exemplo | |-----------|------|-------------|-----------|---------| | `dispatchId` | string | Sim | ID do dispatch a reentregar (`wdi_*`) | `wdi_01hqzvabc` | ##### Exemplo de Requisição ``` POST /v1/webhooks/dispatches/wdi_01hqzvabc/redispatch ``` #### Resposta ##### Aceito (202 Accepted) ```json { "accepted": true, "id": "wdi_01hqzvabc" } ``` ##### Atributos da Resposta | Atributo | Tipo | Descrição | |----------|------|-----------| | `accepted` | boolean | Sempre `true` quando a reentrega foi disparada | | `id` | string | ID do dispatch reentregue (`wdi_*`) | #### Considerações 1. **Reentrega direcionada**: a reentrega vai apenas ao endpoint daquele dispatch, não a todos os endpoints do evento. 2. **Idempotência**: garanta que seu endpoint processe o mesmo evento múltiplas vezes sem efeitos colaterais — uma reentrega pode chegar após a entrega original. 3. **Confirmação ≠ recebimento**: a resposta `202` indica que a reentrega foi disparada; o resultado real aparece em seguida no `status`/`responseStatusCode` do dispatch (consulte a [listagem](/docs/guide/webhook-dispatches/list)). 4. **Dispatches legados não são reentregáveis**: dispatches sintéticos reconstruídos de eventos legados (ID no formato `wdi_legacy_*`) não possuem um registro real e retornam `404` ao tentar reentregar. #### Códigos de Erro | Código HTTP | `code` | Descrição | |-------------|--------|-----------| | 401 | — | Não autorizado (`SelectKey` ausente/inválida) | | 403 | — | Acesso negado (sem o escopo `webhooks:update`) | | 404 | `webhookDispatchNotFound` | Dispatch não encontrado na sua conta (ou evento/endpoint associado ausente) | | 500 | — | Erro interno do servidor | ##### Dispatch não encontrado (404 Not Found) ```json { "error": { "status": "Not Found", "statusCode": 404, "category": "resource", "message": "Webhook dispatch not found.", "docUrl": "https://docs.api.selectwin.io/errors/resource", "supportUrls": [ "https://suporte.selectwin.io/ajuda" ] } } ``` #### Casos de Uso Comuns 1. **Recuperação de falha pontual**: reentregar um dispatch `failed` após corrigir uma indisponibilidade temporária do seu servidor. 2. **Reprocessamento direcionado**: forçar a reentrega a um endpoint específico sem reenviar o evento aos demais. 3. **Diagnóstico**: confirmar se uma falha é persistente ou transitória observando o resultado da nova tentativa. --- ## Exemplo de Caso de Uso Completo: Implementação de Checkout para E-commerce Fonte: https://selectwin.io/docs/guide/use-cases/e-commerce-checkout #### Cenário Uma loja virtual precisa implementar um processo de checkout completo, permitindo ao cliente: 1. Cadastrar-se ou fazer login 2. Adicionar/selecionar endereço de entrega 3. Escolher método de pagamento (cartão, boleto ou PIX) 4. Visualizar opções de parcelamento 5. Finalizar a compra 6. Receber notificações sobre o status da transação #### Fluxo de Implementação com a API Selectwin O diagrama abaixo resume a sequência de chamadas dos passos a seguir — do cadastro do cliente à confirmação assíncrona via webhook: ```mermaid sequenceDiagram autonumber participant C as Cliente (Browser) participant L as Loja (Backend) participant API as Selectwin API participant WH as Webhook (Loja) C->>L: Inicia checkout L->>API: GET /v1/customers?email=... alt cliente não existe L->>API: POST /v1/customers API-->>L: cus_... end L->>API: POST /v1/addresses API-->>L: addr_... opt cartão de crédito L->>API: POST /v1/cards API-->>L: card_... L->>API: GET /v1/simulators/installments?amount=... API-->>L: tabela de parcelas end L->>API: POST /v1/transactions API-->>L: tra_... (201, status: pending) opt PIX ou boleto API-->>WH: POST webhook · transaction.pending (QR Code ou linha digitável) WH-->>API: 200 OK end API-->>WH: POST webhook · transaction.approved (assíncrono) WH-->>API: 200 OK Note over L,WH: O resultado vem pelo webhook (não faça polling). Um GET pontual é opcional. ``` > Todas as chamadas à API incluem o header `SelectKey: sk_live_...` (veja **Autenticação**). > Operações de escrita (ex.: `POST /v1/transactions`) devem enviar `X-Idempotency-Key`. ##### Passo 1: Cadastro/Login do Cliente ```javascript // 1. Verificar se o cliente já existe GET /v1/customers?email=cliente@exemplo.com // 2. Se não existir, criar novo cliente POST /v1/customers { "firstName": "João", "lastName": "Silva", "email": "cliente@exemplo.com", "document": { "type": "cpf", "number": "12345678909" }, "telephone": { "countryCode": "55", "areaCode": "11", "number": "999998888" } } // Resposta (salvar ID para próximos passos) { "id": "cus_123456789", "firstName": "João", "lastName": "Silva", ... } ``` ##### Passo 2: Gerenciamento de Endereço ```javascript // 1. Listar endereços existentes do cliente (o filtro é customerid, tudo minúsculo) GET /v1/addresses?customerid=cus_123456789 // 2. Se não houver endereço ou o cliente desejar cadastrar um novo POST /v1/addresses { "customerId": "cus_123456789", "street": "Avenida Paulista", "number": "1000", "complement": "Apto 123", "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR", "postcode": "01310-100", "primary": true } // Resposta (salvar ID para a transação) { "id": "addr_123456789", "ownerType": "customer", "ownerId": "cus_123456789", ... } ``` ##### Passo 3: Criação do Cartão (quando aplicável) ```javascript // Se o cliente optar por salvar o cartão para futuras compras POST /v1/cards { "customerId": "cus_123456789", "holderName": "JOAO SILVA", "numbering": "4111111111111111", "expirationMonth": 12, "expirationYear": 2030, "securityCode": "123" } // Resposta (salvar ID para a transação) { "id": "card_123456789", "holderName": "JOAO SILVA", "firstDigits": "411111", "lastDigits": "1111", "brand": "visa", ... } ``` ##### Passo 4: Opções de Parcelamento (cartão) Para mostrar as parcelas ao cliente, simule a tabela para o valor do pedido: ```javascript GET /v1/simulators/installments?amount=10000 // Resposta (resumida): uma linha por número de parcelas, até o máximo da conta { "resource": "credit", "installments": [ { "installment": 1, "installmentAmountCents": 10000, "totalAmountCents": 10000, "extraFee": false, "description": "sem juros" }, { "installment": 3, "installmentAmountCents": 3333, "totalAmountCents": 10000, "extraFee": false, "description": "sem juros" }, { "installment": 6, "installmentAmountCents": 1783, "totalAmountCents": 10700, "extraFee": true, "description": "com juros" }, ... ], ... } ``` - `installmentAmountCents` é o valor de cada parcela e `totalAmountCents`, o total que o cliente paga; `extraFee: true` marca as linhas com juros do comprador (os configurados na conta). - Na cobrança, envie o `amount` do pedido (sem juros) e o número de `installments` escolhido: a API aplica o mesmo cálculo da simulação e cobra o total daquela linha (`payment.financeInstallments`, ligado por padrão). Com juros, o `amount` da transação passa a ser o total financiado, e o `originalAmount` guarda o valor do pedido. - Pedir mais parcelas do que a tabela oferece responde `422`. ##### Passo 5: Processamento da Transação ###### Cartão de crédito ```javascript // Criação da transação com cartão de crédito em 3x POST /v1/transactions { "amount": 10000, "externalReference": "PEDIDO-123", "customer": { "id": "cus_123456789" }, "payment": { "method": "credit", "currency": "BRL", "installments": 3, "card": { "id": "card_123456789" } }, "billing": { "address": { "id": "addr_123456789" } }, "items": [ { "name": "Smartphone XYZ", "unitPrice": 10000, "quantity": 1 } ], "metadata": { "source": "website", "campaign": "summer_sale" } } // Resposta da transação (a criação é ASSÍNCRONA: nasce "pending") { "id": "tra_123456789", "amount": 10000, "status": "pending", "method": "credit", ... } ``` O desfecho chega por webhook: `transaction.approved` ou `transaction.failed`. ###### PIX ```javascript // PIX: o QR Code vale 30 minutos POST /v1/transactions { "amount": 10000, "externalReference": "PEDIDO-124", "customer": { "id": "cus_123456789" }, "payment": { "method": "pix", "currency": "BRL", "pix": { "expiresInMinutes": 30 } }, "items": [ { "name": "Smartphone XYZ", "unitPrice": 10000, "quantity": 1 } ] } // Resposta: nasce "pending", ainda sem o QR Code { "id": "tra_223456789", "amount": 10000, "status": "pending", "method": "pix", ... } ``` O QR Code chega logo depois, no webhook `transaction.pending`, e fica na leitura da transação (`GET /v1/transactions/{id}`): ```json { "type": "transaction.pending", "payload": { "object": { "id": "tra_223456789", "externalReference": "PEDIDO-124", "status": "pending", "method": "pix", "payment": { "pixQrCodeEmv": "000201010212...", "pixQrCodeUrl": "https://...", "expirationDate": "2023-06-15T15:00:45Z" } } } } ``` - Mostre ao cliente o copia e cola (`payment.pixQrCodeEmv`) e gere a imagem do QR Code a partir dele: a API não gera a imagem (`payment.pixQrCodeImage` vem sempre `null`). - Sem `pix.expiresInMinutes`, vale o prazo configurado na conta (o padrão é 30 minutos). - Pago, chega `transaction.approved`. Vencido sem pagamento, `transaction.canceled`; se o pagamento ainda chegar depois disso, a transação é aprovada (`transaction.approved`). ###### Boleto ```javascript // Boleto: vence em 3 dias; o endereço de cobrança é obrigatório POST /v1/transactions { "amount": 10000, "externalReference": "PEDIDO-125", "customer": { "id": "cus_123456789" }, "payment": { "method": "billet", "currency": "BRL", "billet": { "expiresInDays": 3 } }, "billing": { "address": { "id": "addr_123456789" } }, "items": [ { "name": "Smartphone XYZ", "unitPrice": 10000, "quantity": 1 } ] } // Resposta: nasce "pending", ainda sem a linha digitável { "id": "tra_323456789", "amount": 10000, "status": "pending", "method": "billet", ... } ``` Como no PIX, os dados do boleto chegam no webhook `transaction.pending` e ficam na leitura da transação: a linha digitável em `payment.billetBarcode`, o link do boleto em `payment.billetUrl` e o vencimento em `payment.expirationDate`. Pago, chega `transaction.approved`. O boleto fica `pending` até o vencimento e não é cancelado automaticamente quando vence: continua `pending` até ser pago (`transaction.approved`, inclusive com atraso) ou baixado (`transaction.canceled`). > **Notas de contrato:** > - `payment.method` aceita `credit`, `pix` ou `billet` (e `nupay`, nas contas habilitadas — veja > [Criar transação](/docs/guide/transactions/create)). > - Para um cartão já salvo, basta `payment.card.id`. Para cartão inline, envie `holderName`, > `expirationMonth`, `expirationYear`, `numbering` e `securityCode` dentro de `payment.card`. > - `billing.address` é necessário para **boleto** (`billet`); para crédito/PIX é opcional. > - Quando enviar `items[]`, a soma de `unitPrice × quantity` deve ser **igual** a `amount`. > - Descontos são por **código de cupom**: `"discounts": [{ "code": "SUMMER10" }]`. > - A transação nasce `pending` e o resultado chega por webhook (`transaction.approved` / > `transaction.failed`) — **não faça polling**. ##### Passo 6: Configuração de Webhook para Notificações Uma vez só, antes da primeira cobrança: ```javascript // Configurar webhook para receber notificações de status POST /v1/webhooks { "name": "Notificações de Transações", "endpoint": "https://minhalojavirtual.com/webhooks/selectwin", "events": [ "transaction.pending", "transaction.approved", "transaction.failed", "transaction.canceled", "transaction.refunded" ] } // Resposta (traz o `secret` whsec_... — guarde-o para verificar a assinatura) { "id": "wbe_123456789", "name": "Notificações de Transações", "endpoint": "https://minhalojavirtual.com/webhooks/selectwin", "secret": "whsec_...", ... } ``` O `transaction.pending` traz o QR Code do PIX e a linha digitável do boleto (passo 5). ##### Passo 7: Recebimento e Processamento de Eventos O servidor da loja virtual deverá implementar um endpoint para receber as notificações via webhook. Exemplo de evento recebido: O objeto do recurso é entregue em **`payload.object`** (mesma estrutura da leitura do recurso) — não há chave `data`/`resource` no nível raiz. ```json { "id": "wbh_123456789", "type": "transaction.approved", "source": "automatic", "payload": { "object": { "id": "tra_123456789", "externalReference": "PEDIDO-123", "amount": 10000, "status": "approved", "method": "credit", "payment": { "cardFirstDigits": "411111", "cardLastDigits": "1111", "cardBrand": "visa", "installments": 3 }, "customer": { "id": "cus_123456789", "firstName": "João", "lastName": "Silva" } } }, "createdAt": "2023-06-15T14:35:30Z" } ``` ##### Passo 8: Consulta de Status da Transação ```javascript // Para verificar o status atual de uma transação (uma consulta pontual, não polling) GET /v1/transactions/tra_123456789 // Resposta (resumida) { "id": "tra_123456789", "externalReference": "PEDIDO-123", "status": "approved", "method": "credit", "payment": { "installments": 3, "paidAt": "2023-06-15T14:31:15Z", ... }, ... } ``` ##### Passo 9: Listagem de Transações para Painel do Cliente ```javascript // Listar as transações do cliente para exibir em um painel (a mais recente primeiro) GET /v1/transactions?customerId=cus_123456789&limit=10 // Resposta (cada item é uma projeção leve: amount vem como string, e customId é o identificador gerado pela API) { "offset": 0, "limit": 10, "total": 2, "page": { "offset": { "first": 0, "prev": 0, "next": 0, "last": 0 }, "current": 1, "total": 1 }, "hasMore": false, "data": [ { "id": "tra_123456789", "customId": "E5D4C3B2A1", "customerId": "cus_123456789", "amount": "10000", "status": "approved", "method": "credit", "createdAt": "2023-06-15T14:30:45Z" }, { "id": "tra_987654321", "customId": "K7M8N9P2Q3", "customerId": "cus_123456789", "amount": "5000", "status": "approved", "method": "pix", "createdAt": "2023-06-10T11:22:33Z" } ] } ``` #### Considerações Finais Este caso de uso demonstra como utilizar os diferentes recursos da API Selectwin de forma integrada para criar uma experiência de checkout completa. Os principais aspectos implementados incluem: 1. **Gestão de Clientes**: Criação e consulta de perfis de cliente 2. **Gestão de Endereços**: Adição e seleção de endereços 3. **Pagamentos**: Suporte a cartão, PIX e boleto, com parcelamento no cartão 4. **Processamento de Transações**: Execução de pagamentos com diferentes métodos 5. **Notificações em Tempo Real**: Configuração de webhooks para receber atualizações de status 6. **Consulta de Histórico**: Listagem e verificação de transações anteriores A flexibilidade da API permite personalizar cada etapa de acordo com as necessidades específicas do negócio, oferecendo uma solução completa e robusta para processamento de pagamentos online. --- ## SDK da Selectwin para Node.js Fonte: https://selectwin.io/docs/sdk/nodejs/overview O SDK oficial da **Selectwin** para Node.js e TypeScript (`@selectwin/sdk`): pagamentos (cartão de crédito, Pix, boleto), assinaturas, carteiras, webhooks e mais. > Situação: **versão inicial, em evolução**. A base, a camada de conveniência e os namespaces tipados já > estão prontos. ```bash npm install @selectwin/sdk ``` #### Primeiros passos ```ts import { Selectwin, CardError } from '@selectwin/sdk'; const sw = new Selectwin(process.env.SELECTWIN_API_KEY!); // sk_test_… / sk_live_… // Crie uma transação Pix (valores em centavos). Atalho conciso: passe o corpo direto const tx = await sw.transactions.create({ amount: 9990, payment: { method: 'pix', currency: 'BRL' } }); // Atalhos com id + corpo / só com id: await sw.subscriptions.pause('subs_…'); await sw.subscriptions.cancel('subs_…', { /* … */ }); const one = await sw.transactions.retrieve('tra_…'); // Erros tipados: decida pela classe ou pelo error.code, nunca pela mensagem try { await sw.transactions.create({ /* … */ }); } catch (err) { if (err instanceof CardError) { console.log(err.displayMessage, err.reversible); // texto ao comprador + se pode repetir } } ``` Cada recurso é um namespace com **atalhos concisos** (`create`, `retrieve`, `update`, `list`, `delete`, além de verbos do recurso, como `sw.subscriptions.pause('subs_…')`). A API gerada inteira (todos os métodos por `operationId`) continua ao alcance pelo `.raw`: ```ts await sw.transactions.raw.createTransaction({ /* … */ }); ``` ##### Paginação Os métodos `list()` de primeiro nível paginam sozinhos. O valor devolvido serve ao `await` (a primeira página) e ao `for await` (todos os itens, de todas as páginas): ```ts // percorre todas as transações, de todas as páginas for await (const tx of sw.transactions.list({ limit: 100 })) { // … } const firstPage = await sw.transactions.list({ limit: 20 }); // { data, hasMore, offset, … } const all = await sw.customers.list().toArray(); // ou .toArray(500), com teto for await (const page of sw.transactions.list().pages()) { /* page.data */ } ``` Para os endpoints que não ganharam paginação automática (as listas de sub-recursos, por exemplo), o auxiliar de baixo nível `paginate` funciona sobre qualquer função de listagem: ```ts import { paginate } from '@selectwin/sdk'; for await (const item of paginate((p) => sw.subscriptions.raw.listSubscriptionItems({ subscriptionId: 'subs_…', ...p }), { limit: 100 })) { /* … */ } ``` ##### Webhooks O `constructEvent` confere a assinatura e devolve uma **união discriminada** sobre todo o catálogo de eventos: o `switch (event.type)` estreita o `event.payload.object` para o formato do recurso correspondente: ```ts // rawBody PRECISA ser exatamente os bytes/a string recebidos (não serialize de novo) const event = sw.webhooks.constructEvent( rawBody, req.headers['x-selectwin-signature'], process.env.SELECTWIN_WEBHOOK_SECRET!, // whsec_… ); switch (event.type) { case 'transaction.approved': event.payload.object.id; // tipado com o formato da leitura da transação break; case 'subscription.paused': event.payload.object; // tipado com o formato da leitura da assinatura break; } ``` O `event.type` completa todos os valores do catálogo (`WebhookEventType`). O próprio catálogo é exportado para validação, com o `WEBHOOK_EVENT_TYPES` (tupla somente leitura) e o `isWebhookEventType(x)`, junto com os grupos por recurso (`TransactionEventType`, `SubscriptionEventType`, …) e o mapa `WebhookEventObjectMap` (tipo → objeto). Quer afirmar você mesmo o formato do objeto? Passe um argumento de tipo: `constructEvent(rawBody, sig, secret)`. #### O que o SDK acrescenta ao cliente gerado O pacote é um **núcleo gerado** (da especificação OpenAPI v2.0.0) mais uma **camada de conveniência escrita à mão**: - **Cliente tipado**: `new Selectwin(key)` → `sw.transactions`, `sw.subscriptions`, `sw.customers`, … - **Atalhos concisos** por recurso (`.create`/`.retrieve`/`.update`/`.list`/`.delete` e verbos próprios), que chamam os métodos gerados com os tipos exatos. O CRUD comum é **achatado**, `create(body)`, `retrieve(id)`, `update(id, body)`, e os métodos mais complexos (sub-recursos com vários ids, listagens) ficam na forma de objeto. O `.raw` expõe toda a superfície gerada. - **Erros tipados** pelo status HTTP e pelo `error.code`: `CardError` (402, `displayMessage`/`reversible`), `ValidationError` (`params`), `RateLimitError` (`retryAfter`), `AuthenticationError`, `PermissionError`, `NotFoundError`, `ConflictError`, `ApiError`, `ApiConnectionError`. - **Novas tentativas automáticas** (429, 5xx e falha de rede), com backoff exponencial e respeitando o `Retry-After`. - **Idempotência**: toda operação de escrita leva um `X-Idempotency-Key` (para usar o seu, passe nos headers). - **Tempo limite** (`AbortController`). - **Paginação automática**: o `list()` de primeiro nível devolve um `AutoPager` (`await` = a primeira página, `for await` = todos os itens, `.toArray()`, `.pages()`); e há o auxiliar de baixo nível `paginate(listFn, params)`. - **Verificação de webhook e eventos tipados**: o `constructEvent` (HMAC-SHA256 do corpo bruto, em tempo constante) devolve uma união discriminada sobre o catálogo de eventos, e o `switch (event.type)` estreita o `event.payload.object`. O catálogo é gerado da especificação, então nunca fica defasado. A autenticação vai no header `SelectKey`; o ambiente (sandbox ou produção) sai do prefixo da chave (`sk_test_` / `sk_live_`). #### Arquitetura ``` src/ generated/ # saída do openapi-generator typescript-fetch — NÃO edite; vem do selectwin-sdks namespaces.ts # GERADO: wrappers tipados (gen-namespaces.mjs) — NÃO edite webhook-events.ts # GERADO: catálogo de eventos tipado + união discriminada — NÃO edite http.ts # fetch próprio (tentativas/tempo limite) + middleware (idempotência, erros) errors.ts # hierarquia de erros tipados webhooks.ts # constructEvent (verificação da assinatura) pagination.ts # paginate() + AutoPager client.ts # Selectwin: liga uma Configuration a todos os namespaces index.ts # exports públicos ``` O que vale para todas as chamadas entra **uma vez só** na `Configuration` gerada (`fetchApi` + `middleware`): todo endpoint herda, e endpoint novo funciona sozinho quando o núcleo é gerado de novo. #### Desenvolvimento ```bash npm run sync:core # traz o núcleo gerado do selectwin-sdks e refaz namespaces e eventos npm run gen:namespaces # refaz o src/namespaces.ts a partir do núcleo gerado npm run gen:webhook-events # refaz o src/webhook-events.ts (catálogo de eventos tipado) npm run typecheck # tsc --noEmit (src + test; confere também as asserções de tipo) npm test # vitest (errors, webhooks, webhook-events, pagination, http, client) npm run test:integration # suíte de integração no sandbox (veja abaixo) npm run build # tsup → ESM/CJS e d.ts em dist/ ``` O `sync:core` já roda o `gen:namespaces` e o `gen:webhook-events`. O núcleo é gerado da especificação OpenAPI no `selectwin-sdks`; a camada de conveniência em `src/` (menos os gerados `namespaces.ts` e `webhook-events.ts`) é mantida à mão. ##### Testes de integração (sandbox) O `test/integration/**` roda contra a **API de sandbox de verdade** e se protege sozinho: toda suíte é pulada (o `npm test` nunca vai à rede) a menos que haja uma chave de sandbox. Passe uma e rode a suíte à parte: ```bash SELECTWIN_SANDBOX_KEY=sk_test_... npm run test:integration # SELECTWIN_API_KEY também vale, mas só se começar com sk_test_ (nunca uma chave de produção). # SELECTWIN_BASE_URL troca o host, se preciso (sandbox ou produção sai do prefixo da chave). ``` Cobertura: um teste rápido só de leitura (autenticação, envelope das listas e paginação automática em vários recursos), o mapa de erros tipados (chave inválida → `AuthenticationError`) e uma volta completa de cliente (criar → consultar → excluir), mais a repetição com a mesma chave de idempotência. As escritas são só no sandbox e se desfazem no `afterAll`. #### Documentação Os guias completos e a referência da API ficam em [selectwin.io/docs](/docs). --- ## SDK da Selectwin para Python Fonte: https://selectwin.io/docs/sdk/python/overview O SDK oficial da **Selectwin** para Python (`selectwin`): pagamentos (cartão de crédito, Pix, boleto), assinaturas, carteiras, webhooks e mais. Síncrono **e** assíncrono, todo tipado. > Situação: **versão inicial, em evolução**. Núcleo gerado (openapi-python-client, httpx síncrono e > assíncrono) mais uma camada de conveniência escrita à mão (erros tipados, novas tentativas, > idempotência, paginação, verificação de webhook). ```bash pip install selectwin ``` #### Primeiros passos ```python import os from selectwin import Selectwin, CardError sw = Selectwin(api_key=os.environ["SELECTWIN_API_KEY"]) # sk_test_… / sk_live_… # Crie uma transação Pix (valores em centavos) tx = sw.transactions.create({"amount": 9990, "payment": {"method": "pix", "currency": "BRL"}}) # Atalhos com id / id + corpo one = sw.transactions.retrieve("tra_…") sw.subscriptions.pause("subs_…") # Erros tipados: decida pela classe ou pelo error.code, nunca pela mensagem try: sw.transactions.create({...}) except CardError as e: print(e.display_message, e.reversible) # texto ao comprador + se pode repetir ``` ##### Assíncrono ```python import asyncio from selectwin import AsyncSelectwin async def main(): async with AsyncSelectwin(api_key="sk_test_…") as sw: tx = await sw.transactions.create({"amount": 9990, "payment": {"method": "pix", "currency": "BRL"}}) async for t in sw.transactions.list(limit=100): ... asyncio.run(main()) ``` ##### Paginação Os métodos `list()` de primeiro nível devolvem um paginador que busca páginas e também é iterável: ```python # síncrono: percorre todas as transações, de todas as páginas for tx in sw.transactions.list(limit=100): ... first_page = sw.transactions.list(limit=20).first() # um objeto de página (.data, .has_more, …) some = sw.customers.list().to_list(max=500) # junta tudo, com teto opcional # assíncrono async for tx in sw.transactions.list(limit=100): ... ``` A API gerada inteira (todo `operation_id`) continua ao alcance pelo `.raw`: ```python from selectwin._core.api.transactions import create_transaction # ou, a partir do próprio recurso: sw.transactions.raw # o módulo gerado deste recurso ``` ##### Webhooks ```python # raw_body PRECISA ser exatamente os bytes/a str recebidos (não serialize de novo) event = sw.webhooks.construct_event( raw_body, request.headers["x-selectwin-signature"], os.environ["SELECTWIN_WEBHOOK_SECRET"], # whsec_… ) if event.type == "transaction.approved": obj = event.payload.object ``` O `WEBHOOK_EVENT_TYPES` (o catálogo oficial) e o `is_webhook_event_type()` são exportados para validação; o `WebhookEventType` é um `typing.Literal` com todos os tipos de evento. #### O que o SDK acrescenta ao cliente gerado O pacote é um **núcleo gerado** (openapi-python-client, da especificação OpenAPI v2.0.0) mais uma **camada de conveniência escrita à mão**: - **Clientes tipados**: `Selectwin(api_key=…)` (síncrono) e `AsyncSelectwin(api_key=…)` (gerenciador de contexto assíncrono), com `sw.transactions`, `sw.subscriptions`, `sw.customers`, … - **Atalhos concisos** por recurso (`create` / `retrieve` / `update` / `list` / `delete` e verbos próprios), que aceitam um dict ou o modelo de corpo gerado; o `.raw` expõe toda a superfície gerada. - **Erros tipados** pelo status HTTP e pelo `error.code`: `CardError` (402, `display_message`/ `reversible`), `ValidationError` (`params`), `RateLimitError` (`retry_after`), `AuthenticationError`, `PermissionDeniedError`, `NotFoundError`, `ConflictError`, `APIError`, `APIConnectionError`. - **Novas tentativas automáticas** (429, 5xx e falha de rede), com backoff exponencial e respeitando o `Retry-After`. - **Idempotência**: toda operação de escrita leva um `X-Idempotency-Key` (dá para trocar a cada chamada). - **Tempo limite** e um cliente `httpx` só, compartilhado e já autenticado, por cliente do SDK. - **Paginação automática**: iteradores síncronos (`for …`) e assíncronos (`async for …`), `.first()`, `.to_list(max=…)`, `.pages()`. - **Verificação de webhook**: `construct_event` (HMAC-SHA256 do corpo bruto, em tempo constante). A autenticação vai no header `SelectKey`; o ambiente (sandbox ou produção) sai do prefixo da chave (`sk_test_` / `sk_live_`). #### Arquitetura ``` src/selectwin/ _core/ # saída do openapi-python-client — NÃO edite; sincronizada do selectwin-sdks resources.py # GERADO: namespaces dos recursos (síncronos e assíncronos) — NÃO edite webhook_events.py # GERADO: catálogo de eventos de webhook (tipos + verificação) — NÃO edite errors.py # hierarquia de erros tipados http.py # transporte httpx (tentativas/tempo limite) + ganchos (idempotência, UA) + erros pagination.py # SyncPager / AsyncPager webhooks.py # construct_event (verificação da assinatura) client.py # Selectwin / AsyncSelectwin: ligam a autenticação e o cliente httpx próprio ``` O que vale para todas as chamadas entra **uma vez só** no cliente httpx que o núcleo gerado compartilha: todo endpoint herda, e endpoint novo funciona sozinho quando o núcleo é gerado de novo. #### Desenvolvimento ```bash pip install -e ".[dev]" python scripts/sync_core.py # traz o núcleo gerado do selectwin-sdks e refaz python scripts/gen_resources.py # refaz o src/selectwin/resources.py python scripts/gen_webhook_events.py # refaz o src/selectwin/webhook_events.py pytest # suíte de unidade pytest -m integration # suíte do sandbox (pede SELECTWIN_SANDBOX_KEY; veja tests/integration) mypy src/selectwin # confere os tipos da camada de conveniência ``` --- ## SDK da Selectwin para PHP Fonte: https://selectwin.io/docs/sdk/php/overview O SDK oficial da **Selectwin** para PHP (`selectwin/sdk`): pagamentos (cartão de crédito, Pix, boleto), assinaturas, carteiras, webhooks e mais. > Situação: **versão inicial, em evolução**. Núcleo gerado (openapi-generator, Guzzle) mais uma camada de > conveniência escrita à mão (exceções tipadas, novas tentativas, idempotência, paginação, verificação de > webhook). ```bash composer require selectwin/sdk ``` #### Primeiros passos ```php use Selectwin\SelectwinClient; use Selectwin\Exception\CardException; $sw = new SelectwinClient(getenv('SELECTWIN_API_KEY')); // sk_test_… / sk_live_… // Crie uma transação Pix (valores em centavos). Passe um array ou um modelo gerado: $tx = $sw->transactions->create([ 'amount' => 9990, 'payment' => ['method' => 'pix', 'currency' => 'BRL'], ]); // Atalhos com id / id + corpo $one = $sw->transactions->retrieve('tra_…'); $sw->subscriptions->pause('subs_…'); // Exceções tipadas: decida pela classe ou pelo getSelectwinCode(), nunca pela mensagem try { $sw->transactions->create([/* … */]); } catch (CardException $e) { echo $e->getDisplayMessage(), $e->isReversible(); // texto ao comprador + se pode repetir } ``` Todo recurso tem **atalhos concisos** (`create`, `retrieve`, `update`, `list`, `delete`, além de verbos como `$sw->subscriptions->pause('subs_…')`). A API gerada inteira continua ao alcance pelo `->raw()`: ```php $sw->transactions->raw()->createTransaction($request); ``` ##### Paginação O `list()` de primeiro nível devolve um `Paginator`: percorra todos os itens de todas as páginas, pegue a primeira página ou junte tudo num array: ```php foreach ($sw->transactions->list(['limit' => 100]) as $tx) { // todas as transações, de todas as páginas } $firstPage = $sw->transactions->list(['limit' => 20])->first(); // a página (getData(), getHasMore()) $some = $sw->customers->list()->toArray(500); // junta tudo, com teto opcional ``` ##### Webhooks ```php // $rawBody PRECISA ser exatamente os bytes recebidos (não serialize de novo) $event = $sw->constructEvent( $rawBody, $_SERVER['HTTP_X_SELECTWIN_SIGNATURE'] ?? null, getenv('SELECTWIN_WEBHOOK_SECRET'), // whsec_… ); if ($event->type === 'transaction.approved') { $object = $event->object; } ``` O `Selectwin\WebhookEvents::ALL` é o catálogo oficial; o `WebhookEvents::isValid($type)` valida um valor. O `$sw->webhooks` também cuida dos endpoints, dos eventos e das entregas. #### O que o SDK acrescenta ao cliente gerado O pacote é um **núcleo gerado** (da especificação OpenAPI v2.0.0, namespaces `Selectwin\Api` e `Selectwin\Model`) mais uma **camada de conveniência escrita à mão**: - **Cliente tipado**: `new SelectwinClient($key)` → `$sw->transactions`, `$sw->subscriptions`, … - **Atalhos concisos** por recurso, que aceitam um array ou um modelo gerado; o `->raw()` dá acesso a toda a superfície gerada. - **Exceções tipadas** pelo status HTTP e pelo `error.code`: `CardException` (402, `getDisplayMessage()`/`isReversible()`), `ValidationException` (`getParams()`), `RateLimitException` (`getRetryAfter()`), `AuthenticationException`, `PermissionDeniedException`, `NotFoundException`, `ConflictException`, `ApiErrorException`, `ApiConnectionException`. - **Novas tentativas automáticas** (429, 5xx e falha de rede), com backoff e respeitando o `Retry-After` (pilha de handlers do Guzzle). - **Idempotência**: toda operação de escrita leva um `X-Idempotency-Key`. - **Paginação**: `Paginator` (percorrer, `first()`, `toArray()`, `pages()`). - **Verificação de webhook**: `constructEvent` (HMAC-SHA256 do corpo bruto, em tempo constante). A autenticação vai no header `selectkey`; o ambiente (sandbox ou produção) sai do prefixo da chave (`sk_test_` / `sk_live_`). #### Arquitetura ``` lib/ # núcleo do openapi-generator (Selectwin\Api, \Model, Configuration…), sincronizado: NÃO edite src/ SelectwinClient.php # o cliente: liga a Configuration e o Guzzle a todos os recursos Resource/ # GERADO: wrappers dos recursos (gen_resources.php) — NÃO edite Exception/Exceptions.php # hierarquia de exceções tipadas + ErrorFactory Http/ClientFactory.php # handlers do Guzzle (tentativas) + middleware de idempotência Pagination/Paginator.php Webhook/ # WebhookSignature (constructEvent) + Event WebhookEvents.php # GERADO: catálogo de eventos ``` O `lib/` e o `src/` dividem o namespace `Selectwin\` por dois diretórios PSR-4. O que vale para todas as chamadas entra **uma vez só** no cliente Guzzle e na `Configuration`: todo endpoint herda, e endpoint novo funciona sozinho quando o núcleo é gerado de novo. #### Desenvolvimento ```bash composer install php scripts/sync_core.php # traz o núcleo gerado para lib/ (do selectwin-sdks) php scripts/gen_resources.php # refaz o src/Resource/* php scripts/gen_webhook_events.php # refaz o src/WebhookEvents.php vendor/bin/phpunit --testsuite unit SELECTWIN_SANDBOX_KEY=sk_test_... vendor/bin/phpunit --testsuite integration ``` #### Publicação (Packagist) O Packagist serve as versões direto das tags do git: nada é enviado. Configuração, uma vez só: 1. Entre em com o GitHub e clique em **Submit** com o endereço do repositório (`https://github.com/selectwin/sdk-php`). O Packagist instala um webhook, e toda tag enviada vira uma versão nova sozinha. 2. `git tag v0.1.0 && git push origin v0.1.0`. O `.github/workflows/release.yml` roda a suíte de testes a cada tag e (se os segredos `PACKAGIST_USERNAME` / `PACKAGIST_API_TOKEN` estiverem definidos) avisa o Packagist para atualizar na hora; sem eles, conta com o webhook. --- ## @selectwin/cli Fonte: https://selectwin.io/docs/sdk/cli/overview The **Selectwin** command-line interface — authenticate, call any endpoint, and manage resources from your terminal. Built on [oclif](https://oclif.io) and the official [`@selectwin/sdk`](https://www.npmjs.com/package/@selectwin/sdk). > Status: **early (`0.1.0`).** MVP = auth/profiles, the `get/post/…` escape hatch > (100% API coverage), curated commands for the hot flows, and `webhooks test`. > `listen` / `trigger` (local webhook forwarding + synthetic events) are planned > and depend on new backend streaming — see the roadmap. #### Install ```bash npm install -g @selectwin/cli selectwin --help ``` #### Authenticate Keys are stored per profile in `~/.selectwin/config.toml` (chmod 0600). The environment (sandbox/production) is decided by the key prefix — `sk_test_` vs `sk_live_`. ```bash selectwin login --api-key sk_test_… # saves to the default profile selectwin login --profile live --api-key sk_live_… selectwin whoami # validate + show the active profile selectwin config list # list profiles selectwin config use live # switch the default profile ``` Key precedence: `--api-key` flag › `$SELECTWIN_API_KEY` › active profile. #### Call the API **Escape hatch** — reach any endpoint directly: ```bash selectwin get /v1/customers?limit=3 selectwin post /v1/customers -d '{"firstName":"Ana","lastName":"Silva"}' selectwin patch /v1/customers/cus_123 -d '{"email":"ana@ex.com"}' selectwin delete /v1/customers/cus_123 ``` **Curated commands** — ergonomic wrappers over the SDK for the common flows: ```bash selectwin transactions create -d '{"amount":1000,"currency":"BRL","paymentMethod":"pix"}' selectwin transactions list --limit 10 selectwin transactions get txn_123 selectwin transactions refund txn_123 -d '{"amount":500}' selectwin customers create -d '{"firstName":"Ana","lastName":"Silva"}' selectwin customers list selectwin customers get cus_123 selectwin customers delete cus_123 selectwin webhooks endpoints list selectwin webhooks endpoints create -d '{"url":"https://ex.com/hook","events":["transaction.approved"]}' selectwin webhooks test whe_123 ``` Anything without a curated command is reachable through the escape hatch. #### Global flags `--profile` · `--api-key` · `--base-url` · `--idempotency-key` · `--json` · `-v/--verbose` - `--json` prints the raw response body (pipe-friendly for `jq`); otherwise output is a compact table (lists) or key/value view (single objects). - Mutations get an automatic `X-Idempotency-Key`; override with `--idempotency-key`. - Errors map the API `{ error: { code, message } }` envelope to a readable message and a **stable exit code** keyed on the HTTP status (401→10, 402→15, 404→12, 409→13, 422→14, 429→16, 5xx→17, connection→18). #### Development ```bash npm install npm run build # tsc → dist/ npm test # vitest (builds first via pretest) npm run typecheck # tsc --noEmit node bin/run.js --help ``` #### Roadmap 1. **MVP** (this release): login/profiles, escape hatch, curated transactions/ customers/webhooks, mapped errors, JSON/table output. 2. More curated resources (subscriptions, products, coupons, wallets, checkouts…). 3. **`listen`** (stream events → forward to `localhost`, verifying the HMAC signature) and **`trigger`** (synthetic test events) — require new node-api streaming endpoints. 4. Shell completions, `upgrade`, Homebrew/Scoop distribution. --- ## @selectwin/mcp Fonte: https://selectwin.io/docs/sdk/mcp/overview Official **Selectwin** MCP server — lets AI assistants (Claude Desktop, Cursor, Claude Code, …) **read the Selectwin docs** and **operate the API** over the Model Context Protocol. > Status: **early / work in progress** (`0.1.0`). Standalone server (Streamable HTTP + stdio). > Documentation tools are public/read-only; API access is SelectKey-authed and write-gated. #### What it exposes **Tools** - `search_docs(query, limit?)` — full-text search over the docs + API reference. - `get_llms_txt(full?)` — the docs corpus as text (`llms.txt` / `llms-full.txt`). - `list_api_reference()` — every API operation (operationId, method, path) grouped by resource. - `get_openapi()` — the OpenAPI v2.0.0 document. - `call_api({ operationId, pathParams?, query?, body?, idempotencyKey? })` — execute an API operation. Reads work with a SelectKey; **writes require `MCP_ENABLE_WRITES`** and **financial writes require `MCP_ENABLE_FINANCIAL_WRITES`**. **Resources** — `selectwin://openapi.json`, `selectwin://llms.txt`. Auth is the `selectkey` header; the environment (sandbox/production) is decided by the key prefix (`sk_test_` / `sk_live_`). #### Run ##### Remote (Streamable HTTP) ```bash npm install && npm run build PORT=3000 MCP_ENABLE_WRITES=false node dist/http.js # → Streamable HTTP endpoint at http://localhost:3000/mcp (health at /health) ``` Add it in a client that supports remote MCP connectors (e.g. Claude Desktop → *Custom Connector*): URL `https:///mcp`, and set the `selectkey` header to your API key. ##### Local (stdio) ```jsonc // Claude Desktop / Cursor MCP config { "mcpServers": { "selectwin": { "command": "npx", "args": ["-y", "@selectwin/mcp"], "env": { "SELECTWIN_API_KEY": "sk_test_…", "MCP_ENABLE_WRITES": "false" } } } } ``` ##### Inspect ```bash npx @modelcontextprotocol/inspector # point it at http://localhost:3000/mcp (Streamable HTTP) ``` #### Configuration (env) | Var | Default | Purpose | |-----|---------|---------| | `SELECTWIN_API_KEY` | — | SelectKey for stdio (HTTP takes it from the `selectkey` header) | | `SELECTWIN_API_URL` | `https://api.selectwin.io` | API base (host-locked target) | | `SELECTWIN_DOCS_URL` | `https://selectwin.io` | docs site used by the docs tools | | `MCP_ENABLE_WRITES` | `false` | allow non-GET API calls | | `MCP_ENABLE_FINANCIAL_WRITES` | `false` | allow financial/sensitive mutations — everything outside the standard write allowlist (see Safety) | | `PORT` / `MCP_PATH` | `3000` / `/mcp` | HTTP host | #### Safety - **Read-only by default.** Writes are gated behind `MCP_ENABLE_WRITES`, which alone allows ONLY a short allowlist of low-risk edits: create/update customers and addresses, create products and variants, build checkout sessions and payment links, validate a coupon, test a webhook. Everything else — money (transactions, withdrawals, refunds, captures, disputes, checkout pay), subscriptions and revenue splits, coupons, cards, wallets, webhook endpoints and secrets, product/variant edits (prices), every DELETE, and any operation added to the API later — is a financial/sensitive write behind a second flag (`MCP_ENABLE_FINANCIAL_WRITES`). Never enable it for an autonomous agent. - **API responses are marked as untrusted data.** Names, descriptions and metadata come from buyers and merchants; the `call_api` output tells the model not to follow instructions inside it. - **Host-locked.** API calls only ever go to the configured Selectwin API origin. - **The SelectKey is never logged.** An `X-Idempotency-Key` is added to every write. - No polling loops — the docs steer assistants toward webhooks. #### Architecture ``` src/ server.ts # createServer(config) — registers all tools + resources http.ts # Streamable HTTP host (stateless; SelectKey per request header) stdio.ts # stdio host (SelectKey from SELECTWIN_API_KEY) docs.ts # docs tools (fetch the public docs endpoints) api.ts # host-locked API client + write/financial gating spec.ts # vendored openapi.json access (operations, reference summary) config.ts # ServerConfig + env parsing openapi.json # vendored spec (refresh: npm run sync:spec) ``` The server is self-contained: docs tools call the public docs site; API tools drive the vendored OpenAPI spec and talk to `api.selectwin.io`. Nothing depends on the docs app internals. #### Development ```bash npm run typecheck # tsc --noEmit npm test # vitest (spec, docs, api, full protocol via in-memory transport) npm run build # tsup → dist/ (server, http, stdio) npm run sync:spec # refresh openapi.json from selectwin-docs ``` #### Roadmap - OAuth 2.1 for a hosted multi-tenant deployment (replacing SelectKey-by-header). - Write confirmation UX via MCP elicitation for financial operations. - Publish to npm. --- ## Nó Selectwin para n8n Fonte: https://selectwin.io/docs/nocode/n8n/overview O nó oficial da **Selectwin** para o [n8n](https://n8n.io): opere a API de pagamentos da Selectwin (Pix, cartão, boleto, clientes, produtos, links de pagamento, assinaturas) e **reaja aos webhooks**, sem escrever código. > Situação: **versão inicial, em evolução** (`0.1.0`). São dois nós: **Selectwin** (operações da API) e > **Selectwin Trigger** (eventos de webhook assinados). #### O que é - **Nó Selectwin**: cobre o dia a dia da API. Crie cobranças, cuide de clientes, produtos e links de pagamento, e consulte saldo, recebíveis e saques. - **Nó Selectwin Trigger**: registra um webhook na API para os eventos que você escolher, confere a assinatura de cada entrega (em tempo constante e à prova de reenvio) e entrega o evento inteiro ao seu workflow. - **Sem dependências em tempo de execução.** A autenticação é a sua SelectKey, e todo POST leva um `X-Idempotency-Key` automático. #### Instalação ##### Pela interface do n8n (nós da comunidade) 1. No n8n, vá em **Settings → Community Nodes → Install**. 2. Digite `n8n-nodes-selectwin` e confirme. ##### Manual (n8n próprio) ```bash cd ~/.n8n/nodes # a pasta de nós da comunidade do seu n8n npm install n8n-nodes-selectwin ``` Depois, reinicie o n8n. #### Credencial Crie uma credencial **Selectwin API**: | Campo | Valor | |-------|-------| | API Key | a sua SelectKey: `sk_test_…` (sandbox) ou `sk_live_…` (produção) | | Base URL | `https://api.selectwin.io` (o padrão; deixe como está) | O ambiente é decidido pelo prefixo da chave, não pelo endereço. O teste da credencial chama `GET /v1/balance`. As requisições se autenticam com o cabeçalho `selectkey`. #### O nó Selectwin | Recurso | Operações | |---------|-----------| | Transaction | Criar (Pix, boleto ou cartão salvo; cliente por ID ou na hora), consultar, listar (filtros de status, meio e cliente), estornar, capturar | | Customer | Criar, consultar, listar, atualizar, apagar | | Product | Criar, consultar, listar, atualizar, apagar | | Variant | Consultar, listar, listar por produto | | Payment Link | Criar (o `accessFullUrl` da resposta é o endereço para compartilhar), consultar, listar, atualizar, apagar | | Checkout Session | Criar (o corpo JSON vai como está), consultar, listar | | Subscription | Consultar, listar, cancelar, pausar, retomar | | Coupon | Validar, consultar, listar | | Balance | Consultar, consultar o histórico | | Receivable | Consultar, listar | | Withdrawal | Consultar, listar | As listagens paginam com `Limit` e `Offset`; a API responde no envelope `{ data[], hasMore, total, page }`. #### O Selectwin Trigger Escolha um ou mais eventos do catálogo da API (`transaction.approved`, `transaction.refunded`, `subscription.canceled`, `checkout.session.completed`, …). Ao ativar, o nó registra o webhook com `POST /v1/webhooks` e guarda o segredo devolvido; ao desativar, apaga o webhook. Cada entrega é conferida antes de o seu workflow rodar: - `x-selectwin-signature-v1: t=,v1=.", secret)>` — o preferido, à prova de reenvio (tolerância padrão de 300 s, ajustável em **Options**); - `x-selectwin-signature: sha256=` — o formato antigo, aceito como alternativa. A conferência é feita sobre os bytes recebidos, com comparação em tempo constante. Entrega inválida ou sem assinatura é recusada com `401` e nunca chega ao seu workflow. #### Chamada livre à API A credencial **Selectwin API** também vale como *tipo de credencial predefinido* no nó **HTTP Request** do próprio n8n: o caminho para chegar a qualquer endpoint da API que o nó ainda não cobre. #### Desenvolvimento ```bash npm install npm run typecheck # tsc --noEmit npm test # vitest (conferência da assinatura do webhook) npm run build # tsc + copia os ícones dos nós para dist/ ``` --- ## Referência da API: endpoints Base: https://api.selectwin.io. Autenticação: header SelectKey. ### Clientes - GET /v1/customers — Lista os clientes (https://selectwin.io/docs/reference/customers/listCustomers) - POST /v1/customers — Cria um cliente (https://selectwin.io/docs/reference/customers/createCustomer) - GET /v1/customers/{customerId} — Consulta um cliente (https://selectwin.io/docs/reference/customers/getCustomer) - PUT /v1/customers/{customerId} — Atualiza um cliente (https://selectwin.io/docs/reference/customers/updateCustomer__put) - PATCH /v1/customers/{customerId} — Atualiza um cliente (https://selectwin.io/docs/reference/customers/updateCustomer) - DELETE /v1/customers/{customerId} — Exclui um cliente (https://selectwin.io/docs/reference/customers/deleteCustomer) ### Endereços - GET /v1/addresses — Lista os endereços (https://selectwin.io/docs/reference/addresses/listAddresses) - POST /v1/addresses — Cria um endereço (https://selectwin.io/docs/reference/addresses/createAddress) - GET /v1/addresses/{addressId} — Consulta um endereço (https://selectwin.io/docs/reference/addresses/getAddress) - PUT /v1/addresses/{addressId} — Atualiza um endereço (https://selectwin.io/docs/reference/addresses/updateAddress) - PATCH /v1/addresses/{addressId} — Atualiza um endereço (https://selectwin.io/docs/reference/addresses/updateAddress__patch) - DELETE /v1/addresses/{addressId} — Exclui um endereço (https://selectwin.io/docs/reference/addresses/deleteAddress) ### Cartões - GET /v1/cards — Lista os cartões (https://selectwin.io/docs/reference/cards/listCards) - POST /v1/cards — Cria um cartão (https://selectwin.io/docs/reference/cards/createCard) - GET /v1/cards/{cardId} — Consulta um cartão (https://selectwin.io/docs/reference/cards/getCard) - DELETE /v1/cards/{cardId} — Exclui um cartão (https://selectwin.io/docs/reference/cards/deleteCard) ### Transações - GET /v1/transactions — Lista as transações (https://selectwin.io/docs/reference/transactions/listTransactions) - POST /v1/transactions — Cria uma transação (cobrança) (https://selectwin.io/docs/reference/transactions/createTransaction) - GET /v1/transactions/{transactionId} — Consulta uma transação (https://selectwin.io/docs/reference/transactions/getTransaction) - POST /v1/transactions/{transactionId}/refund — Estorna uma transação (https://selectwin.io/docs/reference/transactions/refundTransaction) - POST /v1/transactions/{transactionId}/capture — Captura uma transação pré-autorizada (https://selectwin.io/docs/reference/transactions/captureTransaction) - POST /v1/transactions/{transactionId}/dispute — Envia as provas de defesa de uma disputa (https://selectwin.io/docs/reference/transactions/submitTransactionDispute) ### Assinaturas - GET /v1/subscriptions — Lista as assinaturas (https://selectwin.io/docs/reference/subscriptions/listSubscriptions) - POST /v1/subscriptions — Cria uma assinatura (https://selectwin.io/docs/reference/subscriptions/createSubscription) - GET /v1/subscriptions/{subscriptionId} — Consulta uma assinatura (https://selectwin.io/docs/reference/subscriptions/getSubscription) - GET /v1/subscriptions/{subscriptionId}/items — Lista os itens de uma assinatura (https://selectwin.io/docs/reference/subscriptions/listSubscriptionItems) - POST /v1/subscriptions/{subscriptionId}/items — Adiciona um item (de uma variante recorrente) (https://selectwin.io/docs/reference/subscriptions/addSubscriptionItem) - POST /v1/subscriptions/{subscriptionId}/pause — Pausa uma assinatura (https://selectwin.io/docs/reference/subscriptions/pauseSubscription) - GET /v1/subscriptions/{subscriptionId}/usage — Lista os registros de uso medido (https://selectwin.io/docs/reference/subscriptions/listSubscriptionUsage) - POST /v1/subscriptions/{subscriptionId}/cancel — Cancela uma assinatura (https://selectwin.io/docs/reference/subscriptions/cancelSubscription) - GET /v1/subscriptions/{subscriptionId}/cycles — Lista os ciclos de cobrança de uma assinatura (https://selectwin.io/docs/reference/subscriptions/listSubscriptionCycles) - POST /v1/subscriptions/{subscriptionId}/cycles — Renova uma assinatura (agenda o próximo ciclo) (https://selectwin.io/docs/reference/subscriptions/renewSubscriptionCycle) - POST /v1/subscriptions/{subscriptionId}/resume — Retoma uma assinatura pausada (https://selectwin.io/docs/reference/subscriptions/resumeSubscription) - GET /v1/subscriptions/{subscriptionId}/splits — Lista os splits de uma assinatura (https://selectwin.io/docs/reference/subscriptions/listSubscriptionSplits) - POST /v1/subscriptions/{subscriptionId}/splits — Adiciona um split (empresa recebedora) (https://selectwin.io/docs/reference/subscriptions/addSubscriptionSplit) - GET /v1/subscriptions/{subscriptionId}/thresholds — Lista os limiares de cobrança (https://selectwin.io/docs/reference/subscriptions/listSubscriptionThresholds) - POST /v1/subscriptions/{subscriptionId}/thresholds — Adiciona um limiar de cobrança (https://selectwin.io/docs/reference/subscriptions/addSubscriptionThreshold) - GET /v1/subscriptions/{subscriptionId}/items/{itemId} — Consulta um item (https://selectwin.io/docs/reference/subscriptions/getSubscriptionItem) - PUT /v1/subscriptions/{subscriptionId}/items/{itemId} — Atualiza a quantidade de um item (https://selectwin.io/docs/reference/subscriptions/updateSubscriptionItem__put) - PATCH /v1/subscriptions/{subscriptionId}/items/{itemId} — Atualiza a quantidade de um item (https://selectwin.io/docs/reference/subscriptions/updateSubscriptionItem) - DELETE /v1/subscriptions/{subscriptionId}/items/{itemId} — Remove um item (https://selectwin.io/docs/reference/subscriptions/removeSubscriptionItem) - GET /v1/subscriptions/{subscriptionId}/cycles/{cycleId} — Consulta um ciclo de cobrança (https://selectwin.io/docs/reference/subscriptions/getSubscriptionCycle) - GET /v1/subscriptions/{subscriptionId}/splits/{splitId} — Consulta um split (https://selectwin.io/docs/reference/subscriptions/getSubscriptionSplit) - PUT /v1/subscriptions/{subscriptionId}/splits/{splitId} — Atualiza um split (https://selectwin.io/docs/reference/subscriptions/updateSubscriptionSplit__put) - PATCH /v1/subscriptions/{subscriptionId}/splits/{splitId} — Atualiza um split (https://selectwin.io/docs/reference/subscriptions/updateSubscriptionSplit) - DELETE /v1/subscriptions/{subscriptionId}/splits/{splitId} — Remove um split (https://selectwin.io/docs/reference/subscriptions/removeSubscriptionSplit) - POST /v1/subscriptions/{subscriptionId}/items/{itemId}/usage — Registra o uso medido de um item da assinatura (https://selectwin.io/docs/reference/subscriptions/recordSubscriptionUsage) - GET /v1/subscriptions/{subscriptionId}/cycles/{cycleId}/usage — Uso agregado + valores calculados por item de um ciclo (https://selectwin.io/docs/reference/subscriptions/getSubscriptionCycleUsage) - DELETE /v1/subscriptions/{subscriptionId}/thresholds/{thresholdId} — Remove um limiar de cobrança (https://selectwin.io/docs/reference/subscriptions/removeSubscriptionThreshold) ### Simuladores - GET /v1/simulators/installment — Simula as taxas e os totais de um número de parcelas (cartão/NuPay) (https://selectwin.io/docs/reference/simulators/simulateInstallment) - GET /v1/simulators/installments — Simula a tabela de parcelamento de um valor (https://selectwin.io/docs/reference/simulators/simulateInstallments) - GET /v1/simulators/payment-methods — Simula as taxas e os totais de um meio de pagamento (https://selectwin.io/docs/reference/simulators/simulatePaymentMethods) ### Recebíveis - GET /v1/receivables — Lista os recebíveis (https://selectwin.io/docs/reference/receivables/listReceivables) - GET /v1/receivables/schedule — Agenda de liberação por dia — recebíveis pendentes pela data prevista de liberação, garantia pela data de devolução, e os vencidos (https://selectwin.io/docs/reference/receivables/receivablesSchedule) - GET /v1/receivables/{receivableId} — Consulta um recebível (https://selectwin.io/docs/reference/receivables/getReceivable) ### Saques - GET /v1/withdrawals — Lista os saques (https://selectwin.io/docs/reference/withdrawals/listWithdrawals) - POST /v1/withdrawals — Cria um saque (https://selectwin.io/docs/reference/withdrawals/createWithdrawal) - GET /v1/withdrawals/destinations — Para onde os saques são pagos (o destino por chave Pix das vendas no Pix) (https://selectwin.io/docs/reference/withdrawals/getWithdrawalDestinations) - GET /v1/withdrawals/{withdrawalId} — Consulta um saque (https://selectwin.io/docs/reference/withdrawals/getWithdrawal) - POST /v1/withdrawals/destinations/pix-key/recheck — Reconfere o destino de saque por chave Pix depois de cadastrar a chave no banco (https://selectwin.io/docs/reference/withdrawals/recheckPixKeyDestination) ### Saldo - GET /v1/balance — Consulta o saldo da conta (https://selectwin.io/docs/reference/finance/getBalance) - GET /v1/balance/history — Lista os movimentos do saldo (https://selectwin.io/docs/reference/finance/listBalanceHistory) - GET /v1/balance/debt-settlements — Lista as quitações de dívida (o registro de cada acerto de saldo negativo) (https://selectwin.io/docs/reference/finance/listDebtSettlements) - POST /v1/balance/debt-settlements — Emite (ou devolve o já aberto) Pix para quitar um saldo negativo (https://selectwin.io/docs/reference/finance/createDebtSettlement) ### Contas bancárias - GET /v1/wallets — Lista as carteiras de recebimento (https://selectwin.io/docs/reference/wallets/listWallets) - POST /v1/wallets — Cria uma carteira de recebimento (https://selectwin.io/docs/reference/wallets/createWallet) - GET /v1/wallets/{walletId} — Consulta uma carteira de recebimento (https://selectwin.io/docs/reference/wallets/getWallet) - DELETE /v1/wallets/{walletId} — Arquiva uma carteira de recebimento (https://selectwin.io/docs/reference/wallets/deleteWallet) ### Checkout - GET /v1/checkouts/sessions — Lista as sessões de checkout (https://selectwin.io/docs/reference/checkout/listCheckoutSessions) - POST /v1/checkouts/sessions — Cria uma sessão de checkout (https://selectwin.io/docs/reference/checkout/createCheckoutSession) - GET /v1/checkouts/payment-links — Lista os links de pagamento (https://selectwin.io/docs/reference/checkout/listCheckoutPaymentLinks) - POST /v1/checkouts/payment-links — Cria um link de pagamento (https://selectwin.io/docs/reference/checkout/createCheckoutPaymentLink) - GET /v1/checkouts/sessions/{sessionId} — Consulta uma sessão de checkout (https://selectwin.io/docs/reference/checkout/getCheckoutSession) - PUT /v1/checkouts/sessions/{sessionId} — Atualiza uma sessão de checkout (https://selectwin.io/docs/reference/checkout/updateCheckoutSession) - DELETE /v1/checkouts/sessions/{sessionId} — Cancela uma sessão de checkout (https://selectwin.io/docs/reference/checkout/deleteCheckoutSession) - POST /v1/checkouts/sessions/{sessionId}/pay — Envia uma sessão de checkout para pagamento (assíncrono) (https://selectwin.io/docs/reference/checkout/submitCheckoutPayment) - GET /v1/checkouts/sessions/{sessionId}/events — Lê o rastro bruto de interação de uma sessão (evento a evento) (https://selectwin.io/docs/reference/checkout/listCheckoutSessionEvents) - GET /v1/checkouts/payment-links/{paymentLinkId} — Consulta um link de pagamento (https://selectwin.io/docs/reference/checkout/getCheckoutPaymentLink) - PUT /v1/checkouts/payment-links/{paymentLinkId} — Atualiza um link de pagamento (https://selectwin.io/docs/reference/checkout/updateCheckoutPaymentLink) - PATCH /v1/checkouts/payment-links/{paymentLinkId} — Atualiza um link de pagamento (https://selectwin.io/docs/reference/checkout/updateCheckoutPaymentLink__patch) - DELETE /v1/checkouts/payment-links/{paymentLinkId} — Exclui um link de pagamento (https://selectwin.io/docs/reference/checkout/deleteCheckoutPaymentLink) - GET /v1/checkouts/sessions/{sessionId}/behavior — Lê o resumo comportamental + o diagnóstico de intenção/abandono de uma sessão (https://selectwin.io/docs/reference/checkout/getCheckoutSessionBehavior) - POST /v1/checkouts/payment-links/{paymentLinkId}/test — Cria (ou renova) o link de teste em sandbox de um link de pagamento de produção (https://selectwin.io/docs/reference/checkout/testCheckoutPaymentLink) - POST /v1/checkouts/sessions/{sessionId}/telemetry-token — Emite a credencial de telemetria da sessão para o front-end do checkout (https://selectwin.io/docs/reference/checkout/createCheckoutTelemetryToken) ### Cupons - GET /v1/coupons — Lista os cupons (https://selectwin.io/docs/reference/coupons/listCoupons) - POST /v1/coupons — Cria um cupom (https://selectwin.io/docs/reference/coupons/createCoupon) - POST /v1/coupons/validate — Valida um cupom contra um carrinho (prévia do desconto) (https://selectwin.io/docs/reference/coupons/validateCoupon) - GET /v1/coupons/{couponId} — Consulta um cupom (https://selectwin.io/docs/reference/coupons/getCoupon) - PUT /v1/coupons/{couponId} — Atualiza um cupom (https://selectwin.io/docs/reference/coupons/updateCoupon) - PATCH /v1/coupons/{couponId} — Atualiza um cupom (https://selectwin.io/docs/reference/coupons/patchCoupon) - DELETE /v1/coupons/{couponId} — Exclui um cupom (https://selectwin.io/docs/reference/coupons/deleteCoupon) ### Status da plataforma - GET /v1/marketplace — Consulta o status de marketplace da sua conta (https://selectwin.io/docs/reference/marketplace/getMarketplaceStatus) ### Sub-contas - GET /v1/marketplace/sub-accounts — Lista as suas sub-contas (https://selectwin.io/docs/reference/marketplace-sub-accounts/listSubAccounts) - POST /v1/marketplace/sub-accounts — Cria uma sub-conta sob a sua conta (https://selectwin.io/docs/reference/marketplace-sub-accounts/createSubAccount) - GET /v1/marketplace/sub-accounts/{companyId} — Consulta uma sub-conta (https://selectwin.io/docs/reference/marketplace-sub-accounts/getSubAccount) - PATCH /v1/marketplace/sub-accounts/{companyId} — Atualiza uma sub-conta (taxa da plataforma) (https://selectwin.io/docs/reference/marketplace-sub-accounts/updateSubAccount) - POST /v1/marketplace/sub-accounts/{companyId}/suspend — Suspende uma sub-conta ativa (ela para de cobrar e de sacar) (https://selectwin.io/docs/reference/marketplace-sub-accounts/suspendSubAccount) - POST /v1/marketplace/sub-accounts/{companyId}/reinstate — Reativa uma sub-conta que a sua plataforma suspendeu (https://selectwin.io/docs/reference/marketplace-sub-accounts/reinstateSubAccount) - GET /v1/marketplace/sub-accounts/{companyId}/onboarding — Consulta o andamento do cadastro de uma sub-conta (passos, documentos, última mudança de status) (https://selectwin.io/docs/reference/marketplace-sub-accounts/getSubAccountOnboarding) - GET /v1/marketplace/sub-accounts/{companyId}/closure-request — O pedido de exclusão atual de uma sub-conta, com o que ainda o bloqueia (https://selectwin.io/docs/reference/marketplace-sub-accounts/getSubAccountClosureRequest) - POST /v1/marketplace/sub-accounts/{companyId}/closure-request — Pede a exclusão de uma sub-conta (a Selectwin analisa e executa) (https://selectwin.io/docs/reference/marketplace-sub-accounts/requestSubAccountClosure) - DELETE /v1/marketplace/sub-accounts/{companyId}/closure-request — Desiste da exclusão da sub-conta — só enquanto nada foi executado (https://selectwin.io/docs/reference/marketplace-sub-accounts/cancelSubAccountClosureRequest) ### Credenciamento (KYC) - GET /v1/marketplace/kyc/{token} — Pré-visualiza um link de credenciamento (validade + nome da sub-conta) (https://selectwin.io/docs/reference/marketplace-kyc/previewKycLink) - GET /v1/marketplace/kyc/{token}/onboarding — Consulta o estado do cadastro da sub-conta pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/viewKycOnboarding) - POST /v1/marketplace/kyc/{token}/assets/uploads — Pede um upload pré-assinado de documento pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/createKycDocumentUpload) - POST /v1/marketplace/kyc/{token}/onboarding/step-1 — Envia o passo 1 do cadastro pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/submitKycStep1) - POST /v1/marketplace/kyc/{token}/onboarding/step-2 — Envia o passo 2 do cadastro pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/submitKycStep2) - POST /v1/marketplace/kyc/{token}/onboarding/step-3 — Envia o passo 3 do cadastro (documentos) pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/submitKycStep3) - POST /v1/marketplace/kyc/{token}/onboarding/step-4 — Envia o passo 4 do cadastro pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/submitKycStep4) - POST /v1/marketplace/kyc/{token}/onboarding/step-5 — Envia o passo 5 do cadastro (conta de recebimento) pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/submitKycStep5) - POST /v1/marketplace/kyc/{token}/onboarding/step-6 — Envia o passo 6 do cadastro (conclusão) pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/submitKycStep6) - GET /v1/marketplace/sub-accounts/{companyId}/kyc-link — Consulta o status do link de credenciamento (KYC) de uma sub-conta (https://selectwin.io/docs/reference/marketplace-kyc/getSubAccountKycLink) - POST /v1/marketplace/sub-accounts/{companyId}/kyc-link — Gera (ou regenera) o link de credenciamento (KYC) de uma sub-conta (https://selectwin.io/docs/reference/marketplace-kyc/createSubAccountKycLink) - DELETE /v1/marketplace/sub-accounts/{companyId}/kyc-link — Revoga o link de credenciamento (KYC) de uma sub-conta (https://selectwin.io/docs/reference/marketplace-kyc/revokeSubAccountKycLink) - GET /v1/marketplace/kyc/{token}/onboarding/document-types — Os tipos de documento que o passo 1 pode escolher, pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/getKycDocumentTypes) - POST /v1/marketplace/kyc/{token}/onboarding/invite-code/validate — Confere um código de convite pelo link de credenciamento — não consome (https://selectwin.io/docs/reference/marketplace-kyc/validateKycInviteCode) - POST /v1/marketplace/kyc/{token}/assets/uploads/{assetId}/confirm — Confirma o upload de um documento pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/confirmKycDocumentUpload) - GET /v1/marketplace/kyc/{token}/onboarding/document-requirements — O que o passo 3 precisa pedir, pelo link de credenciamento (https://selectwin.io/docs/reference/marketplace-kyc/getKycDocumentRequirements) ### Planos de taxas - GET /v1/marketplace/plans — Lista os planos de taxas (https://selectwin.io/docs/reference/marketplace-plans/listMarketplacePlans) - POST /v1/marketplace/plans — Cria um plano de taxas (https://selectwin.io/docs/reference/marketplace-plans/createMarketplacePlan) - GET /v1/marketplace/plans/{planId} — Consulta um plano de taxas (https://selectwin.io/docs/reference/marketplace-plans/getMarketplacePlan) - PATCH /v1/marketplace/plans/{planId} — Atualiza um plano de taxas (https://selectwin.io/docs/reference/marketplace-plans/updateMarketplacePlan) - DELETE /v1/marketplace/plans/{planId} — Exclui um plano de taxas (https://selectwin.io/docs/reference/marketplace-plans/deleteMarketplacePlan) - PUT /v1/marketplace/sub-accounts/{companyId}/plan — Atribui um plano de taxas a uma sub-conta (https://selectwin.io/docs/reference/marketplace-plans/assignSubAccountPlan) ### Produtos - GET /v1/products — Lista os produtos (https://selectwin.io/docs/reference/products/listProducts) - POST /v1/products — Cria um produto (https://selectwin.io/docs/reference/products/createProduct) - GET /v1/products/{productId} — Consulta um produto (https://selectwin.io/docs/reference/products/getProduct) - PUT /v1/products/{productId} — Atualiza um produto (https://selectwin.io/docs/reference/products/updateProduct__put) - PATCH /v1/products/{productId} — Atualiza um produto (https://selectwin.io/docs/reference/products/updateProduct) - DELETE /v1/products/{productId} — Exclui um produto (https://selectwin.io/docs/reference/products/deleteProduct) ### Variantes de produto - GET /v1/variants — Lista todas as variantes de produto (https://selectwin.io/docs/reference/products-variants/listAllVariants) - GET /v1/variants/{variantId} — Consulta uma variante de produto (https://selectwin.io/docs/reference/products-variants/getVariant) - DELETE /v1/variants/{variantId} — Exclui uma variante de produto (https://selectwin.io/docs/reference/products-variants/deleteVariant) - GET /v1/products/{productId}/variants — Lista as variantes de um produto (https://selectwin.io/docs/reference/products-variants/listProductVariants) - POST /v1/products/{productId}/variants — Cria uma variante de produto (https://selectwin.io/docs/reference/products-variants/createProductVariant) - GET /v1/products/{productId}/variants/{variantId} — Consulta uma variante de produto (https://selectwin.io/docs/reference/products-variants/getProductVariant) - PUT /v1/products/{productId}/variants/{variantId} — Atualiza uma variante de produto (https://selectwin.io/docs/reference/products-variants/updateProductVariant__put) - PATCH /v1/products/{productId}/variants/{variantId} — Atualiza uma variante de produto (https://selectwin.io/docs/reference/products-variants/updateProductVariant) - DELETE /v1/products/{productId}/variants/{variantId} — Exclui uma variante de produto (https://selectwin.io/docs/reference/products-variants/deleteProductVariant) ### Webhooks - GET /v1/webhooks — Lista os endpoints de webhook (https://selectwin.io/docs/reference/webhooks/listWebhookEndpoints) - POST /v1/webhooks — Cadastra um endpoint de webhook (https://selectwin.io/docs/reference/webhooks/createWebhookEndpoint) - GET /v1/webhooks/{id} — Consulta um endpoint de webhook (https://selectwin.io/docs/reference/webhooks/getWebhookEndpoint) - PUT /v1/webhooks/{id} — Atualiza um endpoint de webhook (https://selectwin.io/docs/reference/webhooks/updateWebhookEndpoint) - DELETE /v1/webhooks/{id} — Exclui um endpoint de webhook (https://selectwin.io/docs/reference/webhooks/deleteWebhookEndpoint) - GET /v1/webhooks/events — Lista os eventos de webhook (https://selectwin.io/docs/reference/webhooks/listWebhookEvents) - POST /v1/webhooks/{id}/test — Envia um ping de teste para conferir a integração (https://selectwin.io/docs/reference/webhooks/testWebhookEndpoint) - GET /v1/webhooks/dispatches — Lista as entregas de webhook (https://selectwin.io/docs/reference/webhooks/listWebhookDispatches) - GET /v1/webhooks/events/{eventId} — Consulta um evento de webhook + as entregas dele (https://selectwin.io/docs/reference/webhooks/getWebhookEvent) - POST /v1/webhooks/{id}/rotate-secret — Rotaciona o segredo de assinatura do endpoint (https://selectwin.io/docs/reference/webhooks/rotateWebhookSecret) - POST /v1/webhooks/events/{eventId}/resend — Reenvia um evento de webhook (https://selectwin.io/docs/reference/webhooks/resendWebhookEvent) - POST /v1/webhooks/dispatches/{id}/redispatch — Reenvia uma entrega agora (https://selectwin.io/docs/reference/webhooks/redispatchWebhook) ### Utilidades - GET /v1/utils/metadata — Capacidades da API (moedas, meios de pagamento, tipos de documento) (https://selectwin.io/docs/reference/utils/utilsMetadata) - GET /v1/utils/company-types — Lista as categorias de empresa (https://selectwin.io/docs/reference/utils/utilsCompanyTypes) - GET /v1/utils/roles-of-user — Lista o catálogo de papéis de usuário (https://selectwin.io/docs/reference/utils/utilsRolesOfUser) - GET /v1/utils/types-of-refund — Lista as categorias de estorno (https://selectwin.io/docs/reference/utils/utilsTypesOfRefund) - GET /v1/utils/types-of-company — Lista as categorias de empresa (https://selectwin.io/docs/reference/utils/utilsTypesOfCompany) - GET /v1/utils/types-of-webhooks — Lista os tipos de evento de webhook disponíveis (https://selectwin.io/docs/reference/utils/utilsTypesOfWebhooks) - GET /v1/utils/card-bin-validation — Detecta a bandeira de um cartão pelo BIN (https://selectwin.io/docs/reference/utils/utilsCardBinValidation) - GET /v1/utils/postcode-validation — Valida um CEP (formato brasileiro) (https://selectwin.io/docs/reference/utils/utilsPostcodeValidation) - GET /v1/utils/rules-of-recurrencies — Regras de recorrência suportadas (assinaturas/preços) (https://selectwin.io/docs/reference/utils/utilsRulesOfRecurrencies) - GET /v1/utils/types-of-bankaccounts — Lista os bancos suportados (https://selectwin.io/docs/reference/utils/utilsTypesOfBankAccounts) - GET /v1/utils/corporate-document-check — Valida um CPF/CNPJ (dígitos verificadores) e, para um CNPJ, enriquece com o cadastro da empresa (https://selectwin.io/docs/reference/utils/utilsCorporateDocumentCheck) ### Preços - GET /v1/public/pricing — Tabela pública de preços: os planos ativos de cada produto da Selectwin (sem autenticação) (https://selectwin.io/docs/reference/pricing/getPublicPricing) ### Tokenização - GET /v1/tokenization/config — Onde e como o navegador do comprador tokeniza um cartão (endpoint da Selectwin; nenhuma credencial trafega) (https://selectwin.io/docs/reference/tokenization/getTokenizationConfig) - POST /v1/tokenization/tokens — Tokeniza um cartão digitado no navegador do comprador (devolve um token de uso único) (https://selectwin.io/docs/reference/tokenization/tokenizeBrowserCard)