# Subcontas Safe2Pay por tenant O super admin pode criar uma subconta na Safe2Pay, vincular uma já existente, consultar seu cadastro e atualizar dados permitidos. Cada vínculo pertence a **um tenant e um provider específicos**. A árvore de tenants controla visibilidade; ela não compartilha credenciais financeiras entre pai e filhos. Este guia descreve a API **Calebe Pay**, com origem local `http://localhost:18080`. Pelo proxy do portal Docker, use `http://localhost:18088/api`. Todas as operações deste guia exigem `Authorization: Bearer` de uma sessão `super_admin`. Não há endpoint público `/v1/subaccounts`, scope de chave para gerir subcontas ou acesso para `tenant_admin`/`viewer`. O [OpenAPI](openapi.json) contém os schemas e o [guia de integração](integration.md) explica autenticação, erros e isolamento. O SDK `CalebePay` continua dedicado à API pública de pagamentos; os exemplos administrativos usam cURL com uma credencial separada. ## Cadastro remoto e ambiente Não há simulador de cadastro de subcontas. A documentação da Safe2Pay não promete uma base cadastral isolada de sandbox para esses endpoints: a operação pode criar ou alterar um cadastro remoto mesmo quando o provider Calebe Pay está em `sandbox`. Não use dados fictícios para um cadastro remoto autorizado e não execute os exemplos como testes automáticos. `TokenSandbox` serve às transações que suportam sandbox. Boleto suporta esse ambiente; Pix Safe2Pay exige produção. O cadastro não habilita emissão real: o controle efetivo `allow_live_payments` do super admin e as proteções de servidor `LIVE_PAYMENT_SCOPE_JSON` e `ALLOW_UNSCOPED_LIVE_PAYMENTS` continuam aplicáveis aos pagamentos. O cadastro administrativo solicitado pelo super admin tem autorização e fluxo próprios. ## Preparar o provider 1. Cadastre ou atualize um provider `kind: "safe2pay"` com `is_marketplace: true` e a **chave de produção da conta matriz marketplace**. 2. Escolha `environment` na criação do provider. Esse ambiente é imutável e determina qual token da subconta será usado para transacionar. 3. Mantenha o provider desabilitado para pagamentos enquanto prepara as subcontas, se necessário. Provisionar subconta não exige `enabled: true`, mas tenant e ancestrais precisam estar ativos. 4. Crie ou vincule a subconta do tenant exato. Confirme `status: "active"`, `enabled: true` e a presença do token do ambiente. 5. Configure os meios transacionais na Safe2Pay e os `enabled_methods` e regras de roteamento da Calebe Pay. São configurações distintas. O `api_key` do **provider marketplace** é a chave de **produção da matriz**, usada para administrar suas subcontas, inclusive quando `environment: "sandbox"`. Não coloque ali a chave sandbox da matriz nem o token de um cliente. Na emissão de pagamentos, o backend usa `Token` ou `TokenSandbox` da subconta vinculada, conforme o ambiente do provider. Usar a chave de produção para gestão não altera esse ambiente nem libera pagamentos reais. Cadastros de providers existentes não são convertidos automaticamente em marketplace. `is_marketplace` é `false` por padrão e só é aceito para Safe2Pay. Enquanto houver vínculo `creating`, `unknown` ou `active`, retirar esse indicador ou substituir a chave da matriz retorna `409 marketplace_has_subaccounts`. Isso evita redirecionar vínculos existentes para outra conta. Registros que terminaram em rejeição definitiva `failed` não bloqueiam a correção da matriz. Não contorne essa proteção editando o banco. ## Criar uma subconta Persista o corpo e uma `Idempotency-Key` antes da primeira chamada. A chave aceita de 1 a 128 caracteres em `[A-Za-z0-9._:-]`. O namespace é **provider + chave**, separado da idempotência de pagamentos. Para cada provider, só pode existir um vínculo não `failed` por tenant e um vínculo por ID remoto. Tentativas definitivamente recusadas permanecem no histórico. O corpo abaixo é um **modelo**: substitua os identificadores e os dados cadastrais por informações autorizadas e verificadas. Os placeholders não devem ser enviados. Salve o resultado em um arquivo privado, por exemplo `.local/subaccount-create.json` com permissão `0600`; esse cadastro pode conter dados pessoais e bancários. ```json { "tenant_id": "ten_SUBSTITUIR", "provider_id": "prv_SUBSTITUIR", "registration": { "Name": "NOME COMPLETO OU RAZÃO SOCIAL", "Identity": "CPF_OU_CNPJ_SEM_PONTUACAO", "Email": "cadastro@example.com", "ResponsibleBirthDate": "AAAA-MM-DD", "ResponsiblePhone": "DDD_NUMERO", "Address": { "ZipCode": "CEP_8_DIGITOS", "Street": "LOGRADOURO", "Number": "NUMERO", "District": "BAIRRO" }, "IsPanelRestricted": true, "IsTransferCheckingAccountDisabled": false } } ``` Para CNPJ, acrescente `ResponsibleName` e `ResponsibleIdentity` com o CPF do responsável legal. Para CPF, a omissão desses campos assume `Name` e `Identity`. ```sh # Defina a origem, uma sessão super_admin e uma chave persistida no ambiente seguro. : "${CALEBE_API_URL:?Defina a origem da API}" : "${CALEBE_ADMIN_TOKEN:?Defina a sessão administrativa}" : "${CALEBE_SUBACCOUNT_IDEMPOTENCY_KEY:?Defina a chave persistida}" curl --silent --show-error --fail-with-body --max-time 35 \ -X POST "$CALEBE_API_URL/admin/subaccounts" \ -H "Authorization: Bearer $CALEBE_ADMIN_TOKEN" \ -H 'Content-Type: application/json' \ -H "Idempotency-Key: $CALEBE_SUBACCOUNT_IDEMPOTENCY_KEY" \ --data-binary @.local/subaccount-create.json ``` Não adicione `--retry` nem `--location`. Em timeout, preserve arquivo e chave; consulte o estado antes de agir. O exemplo não imprime a credencial e não chama a Safe2Pay diretamente. ### Campos cadastrais aceitos O objeto `registration` usa os nomes originais da Safe2Pay, com maiúsculas, sem espaços extras. Não envie `Token`, `SecretKey`, `Integration`, senha ou propriedades desconhecidas. | Campo | Regra local | | --- | --- | | `Name`, `Identity`, `Email` | Obrigatórios. Nome até 120 bytes, e-mail até 254; documento como string normalizada. CPF com 11 dígitos ou CNPJ com 14 caracteres alfanuméricos. A validação definitiva pertence ao provider. | | `CommercialName`, `WebsiteUrl` | Opcionais; nome fantasia até 120 bytes e site até 2083. | | `ResponsibleBirthDate` | Obrigatória, `YYYY-MM-DD`, não futura e no máximo 120 anos atrás. | | `ResponsibleName`, `ResponsibleIdentity` | Obrigatórios para PJ. CPF do responsável com 11 dígitos. PF assume nome/documento do titular quando omitidos. | | `ResponsiblePhone` | Opcional na criação; 10 ou 11 dígitos quando informado. Pontuação normalizada. Obrigatório na atualização. | | `TechName`, `TechIdentity`, `TechEmail` | Somente PJ, enviados juntos; CPF e e-mail válidos no formato local. `TechPhone` opcional, com 10 ou 11 dígitos após normalização. | | `Address` | Obrigatório na criação. `ZipCode` com 8 dígitos, `Street` de 1 a 150 bytes, `Number` de 1 a 15 e `District` de 1 a 60. `Complement` e `Reference` opcionais, até 150. `CityName` até 120 bytes, `StateInitials` até 2 e `CountryName` até 80, opcionais; o provider resolve a localização pelo CEP. | | `BankData` | Opcional, mas completo quando enviado: `Bank.Code`, `AccountType.Code`, `BankAgency`, `BankAccount`. Código bancário com 1 a 3 dígitos e tipo de conta com até 8 bytes. Agência numérica de 1 a 10 posições e conta de 1 a 15, diferentes de zero. Dígitos opcionais com até uma posição; `X` somente no Banco do Brasil (`001`). Preserve tudo como string. | | `IsPanelRestricted` | Padrão `true`: bloqueia acesso ao painel Safe2Pay da subconta. | | `IsTransferCheckingAccountDisabled` | Padrão `false`; `true` desabilita repasse à conta bancária cadastrada. | | `MerchantSplit` | Configuração completa de meios/serviços e taxas, quando informada. Não é split dinâmico de uma cobrança. | | `MerchantPaymentDate` | Frequência de repasse opcional, descrita abaixo. | Taxas usam `MerchantSplit: [{PaymentMethodCode, IsSubaccountTaxPayer, Taxes: [{TaxTypeName, Tax}]}]`. `TaxTypeName: "1"` significa percentual entre 0 e 100; `"2"` significa **valor em reais**, diferente de `amount` em centavos nas cobranças. `Tax` é número não negativo, limitado localmente a 1.000.000 (percentual continua limitado a 100). O array aceita até 30 métodos/serviços e cada método de uma a duas taxas. Não repita método nem tipo de taxa dentro do método. Informe somente taxas efetivamente contratadas; o provider valida sua compatibilidade. Com `IsSubaccountTaxPayer: true`, a subconta paga a tarifa Safe2Pay além da sobretaxa configurada. Com `false`, a tarifa Safe2Pay está incluída na taxa informada, que precisa cobrir o custo contratado do marketplace. Consulte as [regras oficiais de cadastro e sobretaxa](https://developers.safe2pay.com.br/docs/mkt-criar-subconta) e confirme o contrato comercial antes de definir valores. **Omitir `MerchantSplit` ao criar não habilita automaticamente Pix ou boleto.** A Safe2Pay aplica sua configuração padrão de repasse; os meios precisam ser configurados explicitamente. Não copie uma taxa arbitrária para conseguir aprovar uma chamada. Para frequência, envie `MerchantPaymentDate.PlanFrequence.Code`: `"7"` diário sem `PaymentDay`, `"6"` semanal com `PaymentDay` de 2 a 6 ou `"1"` mensal com dia de 1 a 31. ### Resposta e estados O envelope é `{ "data": { ... } }`. Guarde `data.id`, o identificador **local** `sub_...`. `external_id` é o ID remoto representado como string; pode estar nulo após resposta incerta. ```json { "data": { "id": "sub_EXEMPLO", "tenant_id": "ten_EXEMPLO", "provider_id": "prv_EXEMPLO", "external_id": "123456", "status": "active", "enabled": true, "has_production_token": true, "has_sandbox_token": true, "last_error_code": null, "last_provider_error_code": null, "last_error_message": null } } ``` O DTO também inclui nomes do tenant/provider, nome/documento/e-mail do titular, `registration` sanitizado e timestamps. Tokens, ciphertext, hash e chave de idempotência nunca são devolvidos. `has_production_token` e `has_sandbox_token` indicam credenciais locais legíveis e não mascaradas; não comprovam validade remota nem homologação de um pagamento. `last_error_code` é o código interno da Calebe Pay. `last_provider_error_code` é o identificador Safe2Pay como string de 1 a 10 dígitos, ou `null` quando ausente/inválido. `last_error_message` é uma orientação estática produzida pela Calebe Pay, ou `null`; não é o texto bruto da Safe2Pay. Os diagnósticos não contêm tokens nem dados cadastrais extraídos da resposta remota. | Resultado | Conduta | | --- | --- | | `201`, `active` | Cadastro confirmado e credencial persistida. Ainda conferir habilitação de meios e rota antes de cobrar. | | `202`, `unknown` | Resultado remoto incerto. Não concluir que o cadastro falhou, nem criar novamente com outra chave. | | Replay `202`, `creating` | Registro reservado; operação ainda em curso ou requer reconciliação. A repetição não faz novo Add. | | Replay `200` | Mesmo registro existente, inclusive `unknown` ou `failed`, sem novo envio ao provider. Header `Idempotency-Replayed: true`. | | `502 provider_rejected` | Registro local `failed` sem ID remoto; após conferir a rejeição definitiva, uma tentativa deliberada com cadastro corrigido e nova chave é permitida. O histórico anterior permanece. | ### Corrigir uma rejeição definitiva Uma tentativa `failed` sem `external_id` pode ser corrigida com um **novo POST deliberado**, corpo revisado e nova `Idempotency-Key`. O registro anterior permanece para auditoria; repetir sua chave continua retornando o resultado antigo sem chamar Add. Esse procedimento não se aplica a `creating`, `unknown`, `active`, timeout ou HTTP 5xx sem confirmação local de rejeição definitiva. Primeiro consulte o registro: incerteza continua exigindo reconciliação, nunca uma chave nova automática. ## Consultar, sincronizar e habilitar ```sh curl --silent --show-error --fail-with-body \ "$CALEBE_API_URL/admin/subaccounts?provider_id=$CALEBE_PROVIDER_ID&tenant_id=$CALEBE_TENANT_ID&limit=50&offset=0" \ -H "Authorization: Bearer $CALEBE_ADMIN_TOKEN" curl --silent --show-error --fail-with-body \ "$CALEBE_API_URL/admin/subaccounts/$CALEBE_SUBACCOUNT_ID" \ -H "Authorization: Bearer $CALEBE_ADMIN_TOKEN" curl --silent --show-error --fail-with-body --max-time 35 \ -X POST "$CALEBE_API_URL/admin/subaccounts/$CALEBE_SUBACCOUNT_ID/refresh" \ -H "Authorization: Bearer $CALEBE_ADMIN_TOKEN" curl --silent --show-error --fail-with-body \ -X PATCH "$CALEBE_API_URL/admin/subaccounts/$CALEBE_SUBACCOUNT_ID" \ -H "Authorization: Bearer $CALEBE_ADMIN_TOKEN" \ -H 'Content-Type: application/json' --data '{"enabled":false}' ``` As duas leituras locais não consultam a Safe2Pay. Os filtros `tenant_id`, `provider_id` e `status` são exatos; não incluem a árvore. Paginação usa `limit` de 1 a 100 e `offset`; `meta.count` é o tamanho da página. `refresh` consulta o `external_id` pelo marketplace, confere identidade e sincroniza dados cadastrais, preservando as credenciais já armazenadas. Nunca chama Add e não é uma operação de recuperação ou rotação de tokens. Sem ID externo, retorna `409 reconciliation_required`; use a importação comprovada, sujeita aos limites abaixo. Desabilitar é uma alteração local e não exclui o cadastro remoto. Habilitar exige `active` e token do ambiente do provider. ### Preservação e perda de tokens A Safe2Pay documenta que o token de autenticação é entregue apenas na criação da subconta; em caso de perda, é necessário solicitar outro ao suporte. A resposta de consulta pode conter um valor mascarado, que não serve para autenticar nem comprova a credencial de produção. [Documentação oficial de criação](https://developers.safe2pay.com.br/reference/marketplace-subconta-criar). A Calebe Pay preserva os segredos utilizáveis durante `refresh` e atualização cadastral; somente valores remotos completos e não mascarados podem preencher lacunas. Valores mascarados não substituem o token original e não contam como credencial utilizável. As leituras locais recalculam os indicadores `has_*_token` a partir do conteúdo cifrado; máscara ou cifra ilegível resulta em `false`. Preserve backups e a chave de criptografia; consultar repetidamente o cadastro não garante a recuperação do segredo perdido. Depois de recuperar o token ou obter sua reposição na Safe2Pay, o super admin pode salvar o segredo local pelo procedimento abaixo. Essa operação não solicita nem gera um novo token na Safe2Pay. ### Atualizar o token local pelo super admin No portal, abra **Subcontas → Atualizar token** no vínculo desejado. Confira tenant, provider, ambiente e ID remoto antes de informar a credencial completa obtida em canal seguro. A operação também está disponível em `PATCH /admin/subaccounts/{id}/credentials`, exclusivamente com sessão Bearer de `super_admin`; `{id}` é o ID **local `sub_...`**, não o número remoto da Safe2Pay. O corpo contém somente `api_key` e `expected_updated_at`, ambos obrigatórios. `api_key` substitui **apenas o token do ambiente do provider**: produção atualiza `Token`, sandbox atualiza `TokenSandbox`. O outro token e demais segredos são preservados. O token precisa ser completo, não vazio nem mascarado, sem espaços ou outros caracteres de controle, com até 8192 bytes. Copie `expected_updated_at` exatamente do `updated_at` recebido no GET local mais recente, em RFC3339 e preservando a precisão do timestamp. Campos inválidos retornam `422 validation_error`. Prepare um arquivo privado `.local/subaccount-credentials.json` com esses dois campos e envie sem imprimir seu conteúdo: ```sh curl --silent --show-error --fail-with-body \ -X PATCH "$CALEBE_API_URL/admin/subaccounts/$CALEBE_SUBACCOUNT_ID/credentials" \ -H "Authorization: Bearer $CALEBE_ADMIN_TOKEN" \ -H 'Content-Type: application/json' \ --data-binary @.local/subaccount-credentials.json ``` A resposta `200` contém o DTO da subconta, sem devolver o token. A gravação é cifrada e gera a auditoria `subaccount.credentials_updated`, sem segredos. Uma alteração concorrente retorna `409 subaccount_changed`: leia novamente o vínculo, confira o estado e só então prepare outra atualização deliberada. Não substitua automaticamente o timestamp para forçar uma gravação. Uma operação em curso retorna `409 subaccount_busy`; registros `creating`, `failed` ou sem `external_id` exigem reconciliação (`409 reconciliation_required`). Não é necessário enviar `Idempotency-Key`. Remova o arquivo temporário conforme sua política de segredos. **Salvar o token não consulta a Safe2Pay nem comprova titularidade, validade remota ou habilitação para cobrar.** Não cria subconta ou pagamento e não altera tenant, provider, ID remoto, ambiente, cadastro ou `enabled`. O administrador é responsável por selecionar a credencial daquela subconta e daquele ambiente. Apenas um registro `unknown` cujo `last_error_code` seja `subaccount_credentials_missing` pode voltar a `active` ao recuperar sua credencial; `active` permanece ativo e outros resultados incertos preservam estado e diagnósticos, exigindo reconciliação. O controle de pagamentos em produção e as regras de roteamento permanecem aplicáveis. ## Importar ou recuperar uma criação incerta Primeiro consulte os cadastros remotos disponíveis: ```sh curl --silent --show-error --fail-with-body --max-time 35 \ "$CALEBE_API_URL/admin/providers/$CALEBE_PROVIDER_ID/subaccounts/remote?limit=50&offset=0" \ -H "Authorization: Bearer $CALEBE_ADMIN_TOKEN" ``` Cada item contém somente `id`, `name`, `document` e `email`; `meta` contém `limit`, `offset`, `count` da página e `total` remoto. O `id` dessa lista é **remoto**. A consulta não importa nem vincula nada e nunca entrega tokens. Nesta listagem remota, `offset` deve ser múltiplo de `limit`: com `limit=50`, use `0`, `50`, `100` etc. Uma posição desalinhada retorna `422 validation_error`; a listagem local não tem essa restrição. Para importar, prepare em canal seguro o corpo `{tenant_id, provider_id, external_id, api_key, sandbox_api_key?}` e salve temporariamente em arquivo privado `.local/subaccount-link.json`. `external_id` é uma string numérica positiva de até 19 dígitos. `api_key` é o token de **produção da subconta**, não a credencial da matriz nem uma chave Calebe Pay. `sandbox_api_key` é obrigatório para provider sandbox. Cada token aceita no máximo 8192 bytes; enviar um valor mascarado retorna `422 validation_error`. Não inclua segredos em chat, Git, URL, logs ou captura de tela. ```sh curl --silent --show-error --fail-with-body --max-time 35 \ -X POST "$CALEBE_API_URL/admin/subaccounts/link" \ -H "Authorization: Bearer $CALEBE_ADMIN_TOKEN" \ -H 'Content-Type: application/json' \ --data-binary @.local/subaccount-link.json ``` Não há criação remota nesse endpoint. O backend consulta a subconta pela credencial da matriz e exige ID/documento e tokens comprováveis. Se os tokens retornados não permitirem a conferência exata, inclusive quando o token de produção estiver ausente ou mascarado, responde `422 subaccount_credentials_unverifiable`. Uma consulta cadastral bem-sucedida ou a presença de `TokenSandbox` não comprova o token produtivo informado; uma chave arbitrária nunca é aceita como fallback. Portanto, a importação pode ficar indisponível para uma subconta cuja consulta não exponha credenciais verificáveis, mesmo que ela exista na Safe2Pay. O link não é um mecanismo de recuperação de token perdido. Uma importação sem registro local retorna `201`. Se já existe registro do mesmo tenant/provider, o backend prioriza o vínculo não `failed`; se só houver rejeições, escolhe a tentativa `failed` mais recente. O link recupera esse registro com `200` e preserva ID, chave e hash originais, após conferir documento e credenciais. Pode reconciliar `creating`/`unknown` quando não houver operação em curso, recuperar `failed` ou confirmar um vínculo `active` da mesma identidade. ID externo conflitante retorna `409 subaccount_conflict`. Remova o arquivo temporário com os tokens após concluir o procedimento conforme sua política de segredos. ## Atualizar cadastro e taxas `PUT /admin/subaccounts/{id}/registration` aceita `{ "registration": { ... } }`. Envie `Email` e `ResponsiblePhone`, mais os campos que pretende alterar: endereço, dados bancários, telefone técnico, opções do painel/repasse, `MerchantPaymentDate` e taxas. Nome, documento e dados dos responsáveis legais não são editáveis por esse endpoint. Omitir as opções booleanas preserva a configuração existente; seus padrões de criação não são reaplicados no update. ```json { "registration": { "Email": "cadastro@example.com", "ResponsiblePhone": "11999999999" } } ``` Salve o corpo revisado em `.local/subaccount-update.json` e use: ```sh curl --silent --show-error --fail-with-body --max-time 35 \ -X PUT "$CALEBE_API_URL/admin/subaccounts/$CALEBE_SUBACCOUNT_ID/registration" \ -H "Authorization: Bearer $CALEBE_ADMIN_TOKEN" \ -H 'Content-Type: application/json' \ --data-binary @.local/subaccount-update.json ``` **Omitir `MerchantSplit` preserva taxas. Enviá-lo substitui toda a configuração.** Inclua todos os métodos e serviços que devem continuar habilitados, mesmo alterando só uma taxa. Após timeout ou `202 unknown`, faça refresh e confira o resultado antes de outra alteração; não há retry automático nem idempotência de update prometida. A atualização cadastral preserva os tokens já armazenados, inclusive quando sua confirmação exige um GET remoto. Não a utilize para tentar recuperar ou trocar credenciais. ## Pagamentos pelo vínculo A chamada pública de Pix/boleto continua igual: nunca aceita `subaccount_id`, token remoto ou `provider_id` no corpo. A chave Calebe Pay determina o tenant; o backend escolhe a regra e, em provider marketplace, exige o vínculo ativo e habilitado **daquele tenant + provider**. O filho de uma software house não usa a subconta da software house. Uma transação guarda `subaccount_id`. O refresh usa a mesma subconta original, preservando o contexto de autenticação da emissão. `null` identifica uma emissão sem vínculo de subconta. A consulta de transações de descendentes não permite emitir usando as credenciais financeiras deles. ### Subconta ativa, mas emissão retorna `subaccount_not_configured` Confira o **provider efetivamente selecionado pela regra do método** e compare seu ID com o provider do vínculo. Uma subconta ativa no provider de produção não atende automaticamente uma regra de boleto apontando para outro cadastro de provider em sandbox, mesmo que ambos usem Safe2Pay e pertençam à mesma matriz. A mensagem identifica o provider selecionado e seu ambiente. Se a cobrança deve permanecer em sandbox, **Vincular existente** pode vincular a mesma subconta remota ao provider sandbox somente quando as credenciais de produção e sandbox puderem ser verificadas. Configure a chave de produção da matriz nesse provider e siga os limites de importação acima; token mascarado não permite confirmar o vínculo. Não crie outra subconta remota para contornar essa limitação. Se a intenção é produção, configure a regra para o provider de produção que já possui o vínculo; as permissões de pagamentos reais continuam necessárias. Nunca troque o ambiente automaticamente para contornar esse erro. Essa recusa ocorre antes de reservar a transação ou chamar a Safe2Pay. Depois de corrigir o roteamento ou o vínculo, o mesmo corpo e a mesma `Idempotency-Key` podem ser reenviados. Isso não se aplica a erros de resultado incerto, que exigem reconciliação. As taxas estáticas do cadastro não implementam um endpoint de split dinâmico, saldo, saque ou liquidação na Calebe Pay. O cadastro também não é aprovação KYC nem prova de titularidade dos recebíveis; siga os estados e exigências da Safe2Pay. ## Diagnóstico 301: chave sandbox da matriz A Safe2Pay recusou uma criação com o código `301`, que indica recurso indisponível com a chave sandbox. Essa recusa não exige trocar o ambiente das cobranças para produção: **a gestão usa a chave de produção da matriz; o boleto de teste continua usando `TokenSandbox` da subconta**. 1. Consulte o registro local e confirme `status: "failed"`, `external_id: null` e `last_provider_error_code: "301"`. 2. No provider marketplace, configure a chave de produção da mesma matriz, em canal seguro. Mantenha `environment: "sandbox"` se a intenção é cobrar boleto em sandbox; preserve as proteções de pagamentos reais. 3. Quando só houver tentativas definitivamente `failed`, a correção da chave é permitida. Se houver `creating`, `unknown` ou `active`, a proteção `marketplace_has_subaccounts` continua em vigor: reconcilie o vínculo e não contorne o bloqueio no banco. 4. Depois da correção, faça uma nova tentativa deliberada com corpo revisado e nova chave de idempotência. A tentativa recusada permanece no histórico; sua chave antiga continua retornando o resultado anterior. Não aplique esse procedimento de nova criação a timeout ou estado `unknown`. Ter a mensagem do erro não elimina as regras de reconciliação. ## Erros para agentes e automações | HTTP / código | Próxima ação | | --- | --- | | `403 forbidden` | Usar sessão super admin; não ampliar scopes de uma chave de tenant. | | `400 idempotency_key_required`, `invalid_json` | Corrigir chave ou formato; campos desconhecidos e segredos em `registration` são recusados. | | `422 validation_error`, `provider_not_marketplace` | Corrigir cadastro ou configurar o provider correto. | | `422 tenant_inactive` | Ativar o tenant e os ancestrais antes de criar ou importar vínculo. | | `409 idempotency_conflict` | Recuperar o corpo original; não mudar a chave para forçar uma segunda criação. | | `409 subaccount_exists`, `subaccount_conflict` | Consultar o vínculo existente; reconciliar sem duplicar. | | `409 subaccount_busy` | Aguardar o término da operação cadastral em curso antes de refresh ou link. | | `409 subaccount_changed` | Uma alteração concorrente tornou a consulta anterior desatualizada. Ler novamente; a resposta antiga não sobrescreve o estado recente. | | `409 reconciliation_required`, `subaccount_not_active` | Consultar/refresh se houver ID remoto, ou importar com verificação. | | `422 subaccount_credentials_unverifiable`, `subaccount_identity_mismatch` | Conferir matriz, ID, documento e credenciais em canal seguro; tokens ausentes ou mascarados não comprovam o vínculo. | | `409 marketplace_has_subaccounts` | Preservar identidade da matriz dos vínculos existentes. | | `422 subaccount_not_configured` | Emissão em marketplace sem vínculo ativo e habilitado do tenant exato; configurar antes de cobrar. | | `422 subaccount_credentials_missing` | Emissão sem credencial utilizável da subconta para o ambiente; recuperar a credencial de forma segura. Refresh cadastral não recupera um token perdido. | | `502 provider_refresh_failed` | Preservar o estado local e investigar a resposta remota; não criar novamente. | | `503 reconciliation_required` | Operação exige reconciliação; não disparar novo cadastro automaticamente. | Erros de criação, consulta, listagem remota e link podem trazer `error.message` com orientação segura e o sufixo `Código Safe2Pay: `. O envelope continua `{error:{code,message,request_id}}`; não há um novo campo de código remoto nesse envelope. Para uma tentativa persistida, consulte os campos `last_provider_error_code` e `last_error_message` no DTO. Um código remoto desconhecido é preservado apenas quando passa pela validação numérica; a mensagem continua sendo uma orientação estática. A classificação pode indicar autenticação/permissão, taxas/meios, cadastro, resposta incompleta ou incerteza. Sem evidência suficiente, ela usa uma recusa genérica. Não interprete ausência de código como sucesso, não faça automações por fragmentos de `message` e não presuma acesso ao texto bruto `Error`/`Message` do provider. Guarde `error.code`, `request_id`, HTTP, IDs locais e o código remoto sanitizado para correlação com suporte. Não registre corpos completos de cadastro ou respostas brutas da Safe2Pay. Os testes locais com transporte simulado validam o contrato; não substituem homologação cadastral externa autorizada. ## Referências oficiais e mapeamento O adaptador usa os seguintes contratos oficiais, consultados em 18/09/2026: | Operação Safe2Pay | Endpoint remoto | | --- | --- | | [Criar subconta](https://developers.safe2pay.com.br/reference/marketplace-subconta-criar) | `POST https://api.safe2pay.com.br/v2/marketplace/add` | | [Consultar subconta](https://developers.safe2pay.com.br/reference/consultar-subconta) | `GET https://api.safe2pay.com.br/v2/marketplace/get?id=` | | [Atualizar subconta](https://developers.safe2pay.com.br/reference/marketplace-subconta-alterar) | `PUT https://api.safe2pay.com.br/v2/marketplace/update-1?id=` | | [Listar subcontas](https://developers.safe2pay.com.br/reference/marketplace-subconta-listar) | `GET https://api.safe2pay.com.br/v2/marketplace/list?pageNumber=1&rowsPerPage=1000` | O sufixo `update-1` é o endpoint documentado. A listagem remota também oferece `object.Identity`; não presuma esse filtro na API Calebe Pay, cujo contrato está acima. A autenticação remota usa a chave marketplace no header `X-API-KEY`. O [guia oficial de cadastro](https://developers.safe2pay.com.br/docs/mkt-criar-subconta) detalha requisitos e taxas; respostas HTTP 200 ainda exigem inspeção de `HasError` pelo adaptador. A criação pode retornar tokens na raiz de `ResponseDetail`; consulta usa `Integration` e listagem pode usar `IntegrationData`. A presença desses objetos não garante tokens completos: valores mascarados não são credenciais. Refresh e atualização cadastral preservam os segredos existentes. Esses formatos remotos são tratados internamente e não fazem parte do contrato público Calebe Pay.