DOCUMENTAÇÃO PARA DESENVOLVEDORES
Documentação da API
Integre faturas, checkout e notificações de pagamento.
Resultados da busca
Sem resultados. Tente um nome de endpoint, campo ou guia.
Início rápido
Crie sua primeira fatura.
- Prepare uma loja
Ative as formas de pagamento, configure os provedores e faça backup das carteiras do projeto.
- Crie uma credencial de API
Em Configurações → Acesso à API do console, escolha leitura/gravação e atribua o projeto.
- Envie a solicitação
Use seu host de API e copie seus IDs de projeto e loja. Envie valores decimais como strings.
- Abra o checkout
Redirecione para
links.checkoutda resposta. Verifique a liquidação antes de entregar o pedido.
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new invoice.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: order-1042-attempt-1' \
--header 'Content-Type: application/json' \
--data-raw '{
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"exchange_rate_spread_percent": "0.5"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new invoice.
const body = `{
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"exchange_rate_spread_percent": "0.5"
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new invoice.
$body = <<<'JSON'
{
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"exchange_rate_spread_percent": "0.5"
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: order-1042-attempt-1", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new invoice.
headers = {
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"exchange_rate_spread_percent": "0.5"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Os exemplos usam marcadores e não enviam solicitações desta página. Ver todos os campos da fatura e o formato de resposta →
IDs de projeto e loja
Onde encontrar YOUR_PROJECT_ID e YOUR_STORE_ID.
Use os UUIDs do seu console, não os nomes de projetos ou lojas nem seus identificadores legíveis.
| Marcador | Onde encontrar | Para que é usado |
|---|---|---|
| YOUR_PROJECT_ID | Projeto → Configurações → IDs da API → ID de API do projeto → Copiar. Também aparece na aba Básico da loja. | Solicitações no nível de projeto e loja. |
| YOUR_STORE_ID | Projeto → Lojas → selecione uma loja → Básico → IDs da API → ID de API da loja → Copiar. | Criação de faturas e solicitações de formas de pagamento da loja. |
- Criar uma fatura exige os dois IDs, mesmo para a loja padrão. A loja precisa pertencer a esse projeto e a credencial de API precisa ter acesso ao projeto.
- Criação, lista, detalhe e checkout de faturas retornam invoice_id: o mesmo UUID enviado em IPN/webhooks. Use nas rotas de faturas, não o id interno nem order_id. A partir do merchant 4.0.0, o antigo campo de resposta public_id foi removido; atualize as integrações antes de atualizar o software.
- A API REST não oferece rotas para listar projetos ou lojas. Copie os IDs no console ou use as ferramentas MCP com escopo limitado list_projects e list_stores do merchant 5.0.0+.
- Loja → Básico → Domínios da loja seleciona nomes de host ativos de lojista, pagamento e API. Links de checkout retornados e novos links de notificação priorizam essa loja, depois a loja padrão e depois os padrões do sistema. Nomes retirados ou não ativados nunca são selecionados. Configure seu SDK com o host de API preferido; mudar uma preferência não redireciona outros aliases ativos.
Autenticação e escopo
Mantenha as credenciais no seu servidor e conceda só o acesso necessário.
| Host padrão | Finalidade |
|---|---|
| merchant.example.com | Console do lojista e Configurações |
| pay.example.com | Checkout do cliente |
| api.example.com | Solicitações de API do lojista |
Substitua example.com pelo seu domínio. Instalações existentes preservam seus nomes configurados; gerencie aliases em Configurações → Sistema.
Authorization: Bearer YOUR_MERCHANT_API_TOKEN| Configuração | Como funciona |
|---|---|
| Nível de acesso | Credenciais somente leitura podem listar e consultar. As de leitura/gravação também podem criar faturas e atualizar as políticas de ativos documentadas. |
| Projetos | Atribua os projetos que a credencial pode acessar. IDs de loja e fatura precisam pertencer a um projeto atribuído. |
| Restrições de IP | Opcionalmente permita endereços públicos de saída IPv4 ou IPv6 exatos em Configurações → Acesso à API. |
| Armazenamento de credenciais | Guarde tokens na configuração do seu backend. Nunca inclua uma credencial bearer em um navegador nem em um link de checkout. |
Rotas públicas de checkout usam o ID público da fatura e só expõem dados seguros para o checkout. Sessões do console e controles administrativos são separados das credenciais de API do lojista.
Ativos e carteiras
Escolha as formas de pagamento de cada loja separadamente.
- Consulte os ativos de pagamento do projeto e sua disponibilidade.
- Ative a rede nativa e configure sua carteira e provedores.
- Explore tokens candidatos e verifique o contrato ou mint antes de ativar um token.
- Selecione para a loja a lista ordenada de formas de pagamento. Novas faturas usam as seleções disponíveis.
Tokens compartilham a carteira da rede nativa. Os saldos das carteiras retornam valores atômicos exatos e valores fiduciários indicativos. Use os campos de disponibilidade retornados para determinar quais formas podem receber pagamentos.
Tokens ERC-20 verificados usam as redes EVM compatíveis; SPL verificados usam Solana. Formas de pagamento nativas estão disponíveis nas 30 redes integradas. Monero usa uma conexão externa de carteira somente leitura vinculada ao projeto.
APIs de recebimento e cobertura nativa/de tokens
| Via de pagamento | Compatibilidade | Evidência | Requisitos |
|---|---|---|---|
| Vias de pagamento nativas | compatível | Rastreamento de transações | BTC, SOL, ETH (Ethereum/Base/Arbitrum/OP), BNB, HYPE, AVAX e POL; saídas Bitcoin, transações/recibos EVM canônicos e transferências Solana analisadas fornecem evidências para as faturas. |
| Vias de tokens ERC-20 | compatível | Rastreamento de transações | Ethereum, Base, BNB Chain, HyperEVM, Avalanche, Polygon, Arbitrum e Optimism exigem verificação na blockchain; logs Transfer indexados permitem atribuir os pagamentos. |
| Vias de tokens SPL | compatível | Rastreamento de transações | Candidatos Solana exigem verificação de rede principal e mint; diferenças exatas de saldo de tokens em transações analisadas permitem atribuir os pagamentos. |
| Vias nativas UTXO adicionais | compatível | Rastreamento de transações | BCH/LTC/DOGE usam Esplora; BCH/DOGE também aceitam Bitcore, LTC/DOGE/DASH aceitam BlockCypher, Dash aceita Insight e ZEC transparente aceita zcash-explorer. Todos também aceitam blocos completos preservados de node-rpc compatível com Core. O modo direto exige 1–48 confirmações, não detecção na mempool. Zcash blindado não é compatível. |
| Vias nativas de contas indexadas | compatível | Rastreamento de transações | TRON usa tron-indexer ou node-rpc solidificado; XRP usa xrpl-jsonrpc; Stellar usa stellar-horizon ou registros preservados de node-rpc do Stellar; Cosmos Hub usa cometbft-jsonrpc; Algorand usa algorand-indexer ou node-rpc do algod; Hedera exige hedera-mirror, não um relay EVM. |
| Vias de pagamento nativas por registro | compatível | Rastreamento de transações | Aptos usa aptos-rest; Sui usa sui-graphql; NEAR usa near-jsonrpc; Kaspa usa kaspa-rest. Polkadot Asset Hub aceita substrate-rest ou node-rpc finalizado que interprete metadados; Tezos aceita tezos-tzkt ou operações completas de node-rpc do Octez. Só recebimentos nativos; faturas antigas exigem retenção do histórico. |
| Vias nativas Cardano e TON | compatível | Rastreamento de transações | Cardano exige cardano-koios e TON exige toncenter-v3. Tags XRP, IDs de memo Stellar e comentários de fatura TON são retornados como destination_tag e precisam ser enviados exatamente. |
| Integridade da liquidação | compatível | Verificação independente | Por padrão, a liquidação final exige que dois provedores independentes concordem sobre a transação/evento exatos, o valor, o bloco ou slot canônico e a finalidade. Janelas diretas e compartilhadas EVM também verificam a cobertura completa. Um administrador pode escolher expressamente um provedor confiável para uma rede; isso remove a verificação independente, não as de identidade, integridade ou finalidade. |
| Via nativa Monero | compatível | RPC de carteira somente leitura vinculada ao projeto | Uma wallet-RPC externa dedicada somente observação, atrás de um gateway HTTPS com lista de métodos permitidos, cria subendereços da conta 0. O limite configurado de daemons da rede principal (padrão 2 fontes independentes, opcionalmente 1) fornece a evidência de liquidação. O --restricted-rpc nativo é incompatível com create_address; o operador declara expressamente o backup da carteira e a ausência de chave de gasto, e nenhum material de chaves é enviado ao Wholly Crypto. |
Saldos de exchanges e a escolha de carteira ou exchange por ativo para envios estão disponíveis no console, não na API pública v1. Ver configuração de exchanges.
Ciclo de vida de uma fatura
Evidência de pagamento, liquidação e entrega de pedidos.
| Status | Significado |
|---|---|
| new | Aguardando um pagamento |
| processing | Pagamento observado; valor aceito ou finalidade pendentes |
| settled | Aceito pela política de liquidação da fatura ou manualmente |
| expired | Prazo encerrado; o monitoramento tardio pode continuar |
| invalid | O pagamento não pode ser aceito automaticamente |
| cancelled | Cancelada; só uma conciliação explícita pode reabri-la |
amount_status registros none, partial, paid ou overpaid. timing_status distingue pagamentos no prazo e atrasados. As regras da loja decidem as confirmações necessárias e a tolerância aceita para pagamentos a menor.
Use o valor da fatura invoice_id com a rota de detalhe da fatura. Uma redireção do checkout sozinha não comprova a liquidação. Revise exceções pela conciliação.
Novas tentativas seguras
Criar uma fatura exige Idempotency-Key. Após esgotar o tempo de espera, tente novamente com a mesma credencial, chave e corpo exato da solicitação. Use uma chave nova só para uma fatura nova.
Rastreamento de pagamentos EVM
A detecção compartilhada de blocos nativos e ERC-20 agrupa faturas recentes separadamente do trabalho de recuperação de histórico antigo. Cada fatura preserva seu cursor de histórico persistente. Consultas de tokens usam no máximo 100 blocos por solicitação e reduzem o intervalo se o provedor tiver limites mais rígidos. Dois provedores independentes verificam cada janela por padrão. Configurações → Conexões de redes → Detalhes permite escolher uma única fonte confiável para uma rede, sem verificação independente; as verificações de transação canônica, valor e confirmações permanecem. Detalhes da conexão distinguem atrasos do scanner, restrições de histórico e pausas por cota da saúde básica do nó. A capacidade RPC pública não é garantida.
IPN e webhooks
Receba e verifique eventos de pagamento.
IPN recebe cada evento de fatura gerado na ipn_url efetiva da fatura. Webhooks só recebem os eventos escolhidos para cada endpoint habilitado da loja. Ambos enviam por POST o mesmo retrato JSON; são independentes, então ativar ambos pode notificar seu aplicativo duas vezes.
Defina ipn_url ao criar uma fatura, ou herde o padrão da loja. IPN usa o segredo de Loja → IPN ; cada endpoint de Loja → Webhooks tem seu próprio segredo. Nenhum é sua chave de API.
Quando devo entregar um pedido?
Para processar por eventos, use event_type = invoice.settled junto com status = settled para acionar a verificação do pedido. Verifique a fatura atual e entregue cada pedido uma única vez.
status é o estado da fatura ao criar o evento. event_type indica o que aconteceu. payment.received pode levar processing ou settled; não significa um segundo pagamento nem é um sinal independente para entregar.
Quais eventos e status são enviados?
| Evento em configurações/histórico | Status no corpo | Significado |
|---|---|---|
| invoice.created | new | Fatura criada e aguardando pagamento. Também é usado quando uma reabertura controlada retorna uma fatura a new. |
| payment.received | Resulting invoice status | Um pagamento foi registrado ou o valor recebido aumentou. Normalmente processing ou settled; este evento sozinho não comprova a liquidação. |
| invoice.processing | processing | Pagamento detectado, mas o valor aceito ou a finalidade exigida ainda não foi atingido. Inclui pagamentos parciais. |
| invoice.settled | settled | Política de liquidação cumprida ou aceitação manual. Confira resolution e seu pedido antes de entregar. |
| invoice.expired | expired | O prazo de pagamento venceu. Um pagamento tardio ainda pode mudar o status enquanto o monitoramento continuar. |
| invoice.invalid | invalid | Não pode ser aceito automaticamente, a evidência de pagamento foi perdida ou um lojista rejeitou. Revise a fatura. |
| invoice.cancelled | cancelled | Fatura cancelada. Não entregue; cancelar não reembolsa um pagamento na blockchain. |
Por que Ethereum e Solana podem enviar fluxos de eventos diferentes
As confirmações chegam depois (exemplo de Ethereum)
| Sequência | event_type | status |
|---|---|---|
| 1 | invoice.created | new |
| 2 | payment.received | processing |
| 2 | invoice.processing | processing |
| 3 | invoice.settled | settled |
Já é definitivo quando detectado (exemplo de Solana)
| Sequência | event_type | status |
|---|---|---|
| 1 | invoice.created | new |
| 2 | payment.received | settled |
| 2 | invoice.settled | settled |
Isto mostra a criação de eventos, não uma ordem de entrega garantida. Qualquer fluxo pode ocorrer em outras redes conforme o momento da detecção e a política de liquidação. Não exija um evento processing antes de settled.
Entregar uma única vez: exemplo de receptor e proteção contra duplicatas
| Abordagem | Como tratar |
|---|---|
| Receptor por eventos | Preserve eventos distintos pelo event_id assinado e depois selecione invoice.settled com status = settled. Não descarte este evento porque payment.received com a mesma sequence chegou primeiro. |
| Caixa de estados de pedido do SDK | Os exemplos de receptor PHP, Python e Node fornecidos agrupam project + invoice_id + sequence. Processe o estado salvo independentemente de event_type, consulte a fatura atual e entregue uma vez se estiver settled. Não adicione um filtro exclusivo invoice.settled após agrupar. |
Uma nova tentativa preserva event_id e o corpo original. Eventos diferentes podem compartilhar sequence, mas ter valores event_id diferentes. Elimine entregas duplicadas por event_id assinado ao processar por eventos; proteja separadamente a entrega por instalação/projeto configurados + invoice_id e seu pedido. Uma liquidação posterior não pode creditar o pedido duas vezes.
HTTP receiver:
Verify raw-body signature, timestamp and configured project/store scope.
Save to a durable inbox; deduplicate the signed event_id.
Return HTTP 2xx only after persistence succeeds.
Event-based background worker:
Other events go to status/reconciliation handling, not fulfilment.
Continue here only for event_type = invoice.settled and status = settled.
Fetch the current invoice from your configured API origin.
Check settled status, project/store, order, amount, currency and review policy.
In one database transaction:
Lock the order and check the scoped invoice has not been fulfilled.
Credit/complete once and save the fulfilment record.
Queue any external fulfilment with the same business idempotency key.
SDK order-state worker:
Use the same current-invoice checks and fulfil-once transaction.
Do not filter event_type after collapsing events by invoice revision.Pseudocódigo, não um receptor pronto para usar.
Todos os status de fatura e exceções de pagamento
| Campo | Valores | Significado |
|---|---|---|
| status | new, processing, settled, expired, invalid, cancelled | Status da fatura ao criar o evento; não necessariamente o status atual na entrega. |
| amount_status | none, partial, paid, overpaid | Valor recebido, incluindo a tolerância aceita. paid não significa finalidade das confirmações. |
| timing_status | on_time, late | Se o pagamento cumpriu o prazo da fatura. |
| resolution | automatic, manually_settled, manually_invalidated | Se o resultado foi determinado pelas regras normais ou por uma aceitação/rejeição manual. |
| requires_review | false, true | Aviso de exceção, não outro status da fatura nem permissão automática para entregar ou reembolsar. |
| Situação | Tratamento |
|---|---|
| Pagamento a menor / tolerância | Com regras automáticas, partial não liquida. paid pode incluir uma diferença aceita, mas a finalidade continua necessária. Use o status da fatura, não só uma comparação de valores. |
| Pagamento a maior | overpaid pode coexistir com settled e requires_review = true. Aplique sua política para pagamentos a maior; nunca credite o pedido duas vezes nem reembolse automaticamente um endereço não verificado. |
| Pagamento tardio | expired pode mudar depois enquanto o monitoramento continuar. timing_status = late indica necessidade de revisão; não reabra nem envie automaticamente um pedido cancelado. |
| Aceitação manual | invoice.settled pode ter resolution = manually_settled sem fundos aptos na blockchain. Decida se sua integração aceita essa substituição das regras; os campos de resumo do pagamento podem ser null. |
| Reorganização / invalidação | Uma revisão mais nova pode invalidar a evidência de pagamento anterior. Consulte novamente o status atual e trate a reversão por conciliação. Não ignore só porque o pedido já esteve liquidado. |
| Zero confirmações / valor zero | A liquidação com zero confirmações pode ocorrer na detecção e envolve risco de reorganização. Uma fatura de valor zero permitida expressamente é liquidada sem pagamento. Nenhuma exige primeiro um evento payment.received. |
Use status = settled para entregar, não amount_status = paid nem uma redireção do checkout. Com zero confirmações exigidas, a liquidação pode ocorrer na detecção; isso envolve risco de reorganização.
Pagamento a menor é amount_status = partial; pagamento a maior é overpaid. paid significa que chegou o mínimo aceito, incluindo a tolerância de pagamento a menor da fatura. São estados de valor, não de fatura. late é um timing_status, não um evento separado.
Um fluxo comum é new → processing → settled, mas estados intermediários podem ser pulados. Uma fatura de valor zero permitida expressamente é liquidada sem pagamento e mantém amount_status = none. A aceitação manual é marcada como manually_settled.
As notificações são retratos imutáveis, não respostas de status ao vivo. Podem chegar atrasadas, fora de ordem ou mais de uma vez. Eventos de pagamento e status podem compartilhar uma sequence de fatura e os mesmos campos de estado, mas ter valores assinados event_id e event_type diferentes. O número de confirmações não gera uma notificação garantida por bloco.
O que você recebe
{
"invoice_id": "11111111-2222-4333-8444-555555555555",
"status": "settled",
"amount_status": "paid",
"timing_status": "on_time",
"resolution": "automatic",
"sequence": 3,
"amount": "49.9",
"currency": "EUR",
"order_id": "order-1042",
"payload_version": 2,
"event_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"event_type": "invoice.settled",
"occurred_at": "2026-09-14T12:05:00Z",
"project_id": "11111111-1111-4111-8111-111111111111",
"store_id": "22222222-2222-4222-8222-222222222222",
"description": "Annual plan",
"email": "ada@example.test",
"customer": {
"firstname": "Ada",
"lastname": "Lovelace",
"countryiso2": "GB"
},
"metadata": {
"firstname": "Ada",
"lastname": "Lovelace",
"countryiso2": "GB",
"cart_id": "cart-681"
},
"created_at": "2026-09-14T12:00:00Z",
"updated_at": "2026-09-14T12:05:00Z",
"expires_at": "2026-09-14T12:15:00Z",
"monitoring_expires_at": "2026-09-21T12:15:00Z",
"settled_at": "2026-09-14T12:05:00Z",
"paid_chain": "ethereum",
"paid_asset": "USDC",
"paid_asset_amount": "58.17342",
"paid_asset_amount_received": "58.17342",
"paid_payment_method_id": "33333333-3333-4333-8333-333333333333",
"settlement_exchange_rate": {
"rate": "1.17",
"units": "asset_per_invoice_currency",
"currency": "EUR",
"symbol": "USDC",
"observed_at": "2026-09-14T12:05:00Z",
"as_of": "2026-09-14T12:04:30Z",
"pricing_provider": "kraken",
"asset_provider": "kraken",
"pricing_fetched_at": "2026-09-14T12:04:30Z",
"asset_fetched_at": "2026-09-14T12:04:30Z",
"stale": false,
"is_fixed": false,
"reference_currency": "USD",
"uses_reference_proxy": false
},
"cancelled_at": null,
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"reason_code": "payment_confirmed",
"requires_review": false,
"links": {
"checkout": "https://pay.example.com/invoice/11111111-2222-4333-8444-555555555555",
"invoice": "https://api.example.com/v1/projects/11111111-1111-4111-8111-111111111111/invoices/11111111-2222-4333-8444-555555555555",
"payments": "https://api.example.com/v1/projects/11111111-1111-4111-8111-111111111111/invoices/11111111-2222-4333-8444-555555555555/payments"
},
"payment_info": {
"active_payment_method_id": "33333333-3333-4333-8333-333333333333",
"method_count": 1,
"methods_truncated": false,
"methods": [
{
"payment_method_id": "33333333-3333-4333-8333-333333333333",
"payment_rail": "onchain",
"chain_slug": "ethereum",
"network": "mainnet",
"caip_network_id": "eip155:1",
"asset_id": "44444444-4444-4444-8444-444444444444",
"asset_key": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"caip_asset_id": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"asset_name": "USD Coin",
"symbol": "USDC",
"asset_kind": "token",
"asset_decimals": 6,
"contract_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"token_standard": "erc20",
"destination_address": "0x1111111111111111111111111111111111111111",
"destination_tag": null,
"status": "paid",
"amounts": {
"expected_amount": "58.17342",
"expected_amount_atomic": "58173420",
"received_amount": "58.17342",
"received_amount_atomic": "58173420",
"confirmed_amount": "58.17342",
"confirmed_amount_atomic": "58173420",
"unconfirmed_amount": "0",
"unconfirmed_amount_atomic": "0",
"minimum_payment_amount": "57.591686",
"minimum_payment_amount_atomic": "57591686",
"remaining_amount": "0",
"remaining_amount_atomic": "0",
"remaining_to_full_amount": "0",
"remaining_to_full_amount_atomic": "0",
"overpaid_amount": "0",
"overpaid_amount_atomic": "0"
},
"acceptance": {
"finality_mode": "confirmations",
"required_confirmations": 2,
"observed_confirmations": 2,
"underpayment_tolerance_percent": "1"
},
"quote": {
"effective_rate": "1.1658",
"reference_rate": "1.16",
"units": "asset_per_invoice_currency",
"currency": "EUR",
"symbol": "USDC",
"exchange_rate_spread_percent": "0.5",
"quote_expires_at": "2026-09-14T12:15:00Z",
"provenance_available": true,
"rounding": "up",
"unrounded_payment_amount": "58.17342",
"rounding_adjustment": "0",
"pricing_provider": "kraken",
"asset_provider": "kraken",
"pricing_fetched_at": "2026-09-14T11:59:30Z",
"asset_fetched_at": "2026-09-14T11:59:30Z"
},
"market_rate_at_event": {
"rate": "1.17",
"units": "asset_per_invoice_currency",
"currency": "EUR",
"symbol": "USDC",
"observed_at": "2026-09-14T12:05:00Z",
"pricing_provider": "kraken",
"asset_provider": "kraken",
"pricing_fetched_at": "2026-09-14T12:04:30Z",
"asset_fetched_at": "2026-09-14T12:04:30Z",
"as_of": "2026-09-14T12:04:30Z",
"stale": false,
"is_fixed": false,
"reference_currency": "USD",
"uses_reference_proxy": false
},
"payment_count": 1,
"payments_truncated": false,
"payments": [
{
"payment_id": "55555555-5555-4555-8555-555555555555",
"payment_method_id": "33333333-3333-4333-8333-333333333333",
"transaction_id": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"payment_hash": null,
"event_index": 0,
"payment_rail": "onchain",
"chain_slug": "ethereum",
"network": "mainnet",
"asset_id": "44444444-4444-4444-8444-444444444444",
"asset_key": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"caip_asset_id": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"symbol": "USDC",
"asset_decimals": 6,
"amount": "58.17342",
"amount_atomic": "58173420",
"status": "final",
"counts_towards_received": true,
"confirmations": 2,
"block_height": 26000000,
"observed_at": "2026-09-14T12:04:30Z",
"chain_time": "2026-09-14T12:04:20Z",
"finalized_at": "2026-09-14T12:05:00Z",
"explorer_name": "Etherscan",
"explorer_url": "https://etherscan.io/tx/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
],
"links": {
"payments": "https://api.example.com/v1/projects/11111111-1111-4111-8111-111111111111/invoices/11111111-2222-4333-8444-555555555555/payments?payment_method_id=33333333-3333-4333-8333-333333333333"
}
}
]
}
}amount é o total original da fatura. payment_info descreve transferências cripto observadas, valores ainda faltantes e cotações fixadas. A versão 2 também assina o nome e o ID do evento e o escopo de projeto e loja.
Todos os campos de notificação e dados adicionais da fatura
| Campo | Tipo | Significado |
|---|---|---|
| invoice_id | UUID | UUID público da fatura, usado pela rota autenticada de detalhe da fatura |
| status | string | Status da fatura no retrato: new, processing, settled, expired, invalid, cancelled |
| amount_status | string | none, partial, paid ou overpaid; paid inclui a tolerância aceita de pagamento a menor, não a finalidade das confirmações |
| timing_status | string | on_time ou late |
| resolution | string | automatic, manually_settled ou manually_invalidated |
| sequence | integer | Revisão crescente da fatura; vários eventos podem compartilhar uma revisão. Compare sem perder precisão de inteiros |
| amount | decimal string | Total original da fatura, não valor cripto recebido; preserve a precisão decimal |
| currency | string | Moeda de amount, por exemplo, EUR para uma fatura EUR paga com USDC |
| order_id | string | null | Referência do pedido do lojista |
| payload_version | integer | 2 para eventos novos gerados em 4.1.0+; ausente em eventos antigos preservados |
| event_id | UUID | Identidade assinada do evento, sem alterações em novas tentativas e reenvios manuais |
| event_type | string | Um dos sete eventos de assinatura |
| occurred_at | timestamp | Quando este evento imutável foi criado, não a hora da entrega |
| project_id | UUID | Escopo do projeto do lojista; deve corresponder ao receptor configurado |
| store_id | UUID | Escopo da loja do lojista; deve corresponder ao receptor configurado |
| description | string | null | Descrição original da fatura |
| string | null | Email opcional do cliente na criação do evento | |
| customer | object | Campos opcionais reconhecidos de metadados de cliente; sem dados pessoais supostos nem enriquecidos |
| metadata | object | Metadados originais do lojista como existiam na criação do evento |
| created_at | timestamp | Hora de criação da fatura |
| updated_at | timestamp | Hora de atualização do status da fatura |
| expires_at | timestamp | Prazo de pagamento da fatura |
| monitoring_expires_at | timestamp | Prazo de monitoramento de pagamentos tardios |
| settled_at | timestamp | null | Hora de liquidação |
| paid_chain | string | null | 4.1.2+: slug da rede do método de liquidação comprovado, por exemplo, ethereum; null sem uma liquidação apta salva |
| paid_asset | string | null | 4.1.2+: símbolo da moeda nativa ou token, por exemplo, BTC, ETH ou USDC; rótulo visual, não identidade única do ativo |
| paid_asset_amount | decimal string | null | 5.0.1+: valor solicitado total fixado em unidades paid_asset, antes de subtrair a tolerância; salvo na liquidação |
| paid_asset_amount_received | decimal string | null | 5.0.1+: total válido recebido pelo método vencedor na liquidação, incluindo diferenças aceitas a menor ou maior; fixo, não um saldo ao vivo |
| paid_payment_method_id | UUID | null | 4.1.2+: ID da intenção que liquida; corresponde a payment_info.methods[].payment_method_id e sua rede/contrato exatos |
| settlement_exchange_rate | object | null | 4.1.2+: retrato de mercado antes da margem salvo na liquidação, com unidades, moeda, datas das fontes e indicadores de qualidade explícitos; nunca recalculado na entrega |
| cancelled_at | timestamp | null | Hora de cancelamento |
| exchange_rate_spread_percent | decimal string | Margem fixada, não a padrão atual da loja |
| underpayment_tolerance_percent | decimal string | Tolerância da fatura fixada; cada forma também informa sua tolerância efetiva |
| reason_code | string | null | Motivo de transição de estado legível por máquina |
| requires_review | boolean | Aviso de exceção de pagamento; não autoriza entregar nem reembolsar automaticamente |
| links | object | URLs de checkout, fatura autenticada e pagamentos na criação do evento. Aplicam-se as preferências de domínio de Loja → Básico, depois a loja padrão e depois o principal global; só são usados domínios ativos da função correta. Novas tentativas preservam os links assinados originais; null se não houver registro de host ativo. |
| payment_info | object | Formas realmente observadas, valores exatos, cotação fixada, retrato indicativo do mercado e observações de pagamento limitadas; veja os grupos de campos abaixo |
Resumo da liquidação: settlement_exchange_rate
| Campo | Tipo | Significado |
|---|---|---|
| rate / units / currency / symbol | strings | Unidades do ativo antes da margem por uma unidade da moeda da fatura. String decimal, não valor de pagamento nem operação executada. |
| observed_at / as_of | timestamps | Hora de captura da liquidação / data da fonte anterior. Não trate dados em cache como uma cotação ao vivo. |
| pricing_provider / asset_provider / pricing_fetched_at / asset_fetched_at | strings / timestamps | Fontes de preços fiduciários e de ativos e suas horas de consulta, salvas na liquidação. |
| stale / is_fixed / uses_reference_proxy / reference_currency | booleans / string | Os mesmos indicadores de qualidade de market_rate_at_event. Preços fixos do projeto são identificados; a moeda de referência é USD. |
| Missing snapshot or price | null | Sem cotações históricas supostas. Antes da liquidação, todos os campos de resumo são null; se só faltarem preços, os identificadores paid_* comprovados continuam disponíveis. |
Formas de pagamento: payment_info
| Campo | Tipo | Significado |
|---|---|---|
| active_payment_method_id | UUID | null | Forma observada vencedora ou selecionada. Null antes da detecção ou após invalidação; nenhuma forma padrão é presumida. |
| method_count / methods_truncated | integer / boolean | Total de formas observadas e se a lista incorporada de formas está incompleta. |
| methods[] | object[] | No máximo oito formas observadas, a ativa primeiro. Sem totais entre ativos diferentes. |
| payment_method_id / payment_rail | UUID / string | Identidade da intenção de fatura e transporte onchain ou lightning. |
| chain_slug / network / caip_network_id | string | Identidade da rede. Vincule sempre a identidade do token à sua rede. |
| asset_id / asset_key / caip_asset_id | UUID / string / nullable string | Identidade verificada do registro; símbolos sozinhos não são únicos. |
| asset_name / symbol / asset_kind | string | Nome visível do ativo, símbolo e tipo native ou token. |
| contract_address / token_standard | string | null | Contrato ou mint do token e padrão; null para ativos nativos. |
| asset_decimals | integer | Precisão atômica; Lightning BTC usa 11. |
| destination_address / destination_tag | string | null | Endereço público de recebimento e memo/tag obrigatório. O endereço é null para Lightning; nunca uma chave privada. |
| status | string | Estado da forma: pending, partial, paid, overpaid, expired ou invalid. Paid não é por si só a liquidação da fatura. |
| payment_count / payments_truncated / payments[] | integer / boolean / object[] | Total de observações e as últimas cinco ou menos. Cada observação é descrita abaixo. |
| links.payments | HTTPS URL | null | Histórico autenticado e paginado desta forma na origem da API configurada. |
Valores exatos: methods[].amounts
| Campo | Tipo | Significado |
|---|---|---|
| expected_amount | decimal string | Cotação total fixada, após a margem e o arredondamento para cima. |
| received_amount / confirmed_amount | decimal strings | Fundos válidos detectados / fundos que cumprem a política de confirmações ou finalidade desta forma. |
| unconfirmed_amount | decimal string | max(received - confirmed, 0). Não é um valor adicional para enviar. |
| minimum_payment_amount | decimal string | Limite aceito após a tolerância. Pode ser menor que a cotação total. |
| remaining_amount | decimal string | max(minimum accepted - received, 0). Fundos adicionais necessários para atingir o limite aceito, não o progresso das confirmações. |
| remaining_to_full_amount | decimal string | max(full quote - received, 0), ignorando a tolerância. |
| overpaid_amount | decimal string | max(received - full quote, 0). Não autoriza um reembolso automático. |
| Every amount's *_atomic companion | integer string | Representação exata na menor unidade. Use bibliotecas decimais ou de inteiros; nunca float nem Number do JavaScript para dinheiro. |
Política de confirmações: methods[].acceptance
| Campo | Tipo | Significado |
|---|---|---|
| finality_mode / required_confirmations | string / integer | Confirmações fixadas ou política finalized. A política do lojista permite expressamente zero confirmações; não significa finalidade universal da rede. |
| observed_confirmations | integer | null | Mínimo entre observações válidas, não só da transferência mais recente. Null para Lightning ou se não houver observações válidas. |
| underpayment_tolerance_percent | decimal string | Tolerância efetiva da forma. Lightning usa zero mesmo quando a fatura tem tolerância na blockchain diferente de zero. |
Cotações: methods[].quote e market_rate_at_event
| Campo | Tipo | Significado |
|---|---|---|
| quote.effective_rate / units / currency / symbol | strings | Cotação asset_per_invoice_currency fixada, incluindo a margem; moeda e símbolo indicam explicitamente a direção. |
| quote.exchange_rate_spread_percent / quote_expires_at | decimal string / timestamp | Margem e prazo da cotação fixados. Nunca são substituídos pelas configurações atuais da loja. |
| quote.reference_rate / unrounded_payment_amount / rounding_adjustment | decimal string | null | Referência antes da margem, valor de pagamento antes do arredondamento e ajuste para cima em unidades do ativo. |
| quote.pricing_provider / asset_provider / pricing_fetched_at / asset_fetched_at | string or timestamp | null | Fontes e datas originais de preços da moeda e do ativo. Sem chaves de API nem credenciais de provedores. |
| quote.provenance_available / rounding | boolean / string | False para faturas antigas sem retrato salvo das fontes; o arredondamento é para cima. |
| market_rate_at_event | object | null | Retrato indicativo do mercado em cache ao criar este evento. Dados ausentes permanecem null; nunca muda valores da fatura nem aguarda uma consulta de rede. |
| market_rate_at_event.rate / units / currency / symbol | strings | Cotação de mercado antes da margem, com a mesma direção explícita de quote. |
| market_rate_at_event.observed_at / as_of / pricing_fetched_at / asset_fetched_at | timestamps | Hora do retrato do evento / a mais antiga das duas fontes / hora de cada fonte. |
| market_rate_at_event.pricing_provider / asset_provider | strings | Fontes de moeda e ativo em cache, incluindo preços configurados de tokens personalizados. |
| market_rate_at_event.stale / is_fixed / uses_reference_proxy / reference_currency | booleans / string | Se o cache está desatualizado, o preço do token é fixo ou a referência USD usa uma stablecoin como substituta. A moeda de referência é USD. Desatualizado é um aviso, nunca uma cotação nova. |
Registros de transferências: methods[].payments[] e GET …/payments
| Campo | Tipo | Significado |
|---|---|---|
| payment_id / payment_method_id | UUID | Identidade da observação / identidade da intenção principal. Use payment_id para eliminar duplicatas do histórico. |
| transaction_id / payment_hash / event_index | string | null / integer | Hash na blockchain e índice de transferência/log/saída, ou hash Lightning. Lightning não tem transação nem link de explorador. |
| payment_rail / chain_slug / network / asset_id / asset_key / caip_asset_id / symbol / asset_decimals | strings / UUID / integer | Os mesmos identificadores de ativo e rede da forma que o contém. |
| amount / amount_atomic | decimal / integer strings | Valor exato desta transferência, nunca uma conversão fiduciária. |
| status / counts_towards_received | string / boolean | detected, confirming e final contam; reorged, replaced e invalid não. Preserve o histórico invalidado para conciliação. |
| confirmations / block_height | integer | null | Dados de bloco da observação; confirmações null para Lightning. |
| observed_at / chain_time / finalized_at | timestamp | null | Primeira detecção local, hora confiável da rede se disponível e hora de finalidade pela política se alcançada. |
| explorer_name / explorer_url | string | null | Referência validada a explorador público de blocos, quando compatível. |
Merchant 5.13.3 exclui transferências internas verificadas de fornecimento de gás dos totais de pagamentos de clientes, payment_info, da API de pagamentos de faturas, dos limites de reembolso e dos eventos payment.received. Seus registros de rede e tesouraria continuam disponíveis para a contabilidade das carteiras. Transferências normais e pagamentos a maior reais continuam contando. Corpos de notificação assinados existentes nunca são reescritos. Se uma liquidação histórica dependia de fundos internos em vez de fundos do cliente, a conciliação emite invoice.invalid com reason_code internal_gas_funding_excluded; revise em vez de entregar novamente.
Merchant 4.1.0 adiciona payload_version 2 sem mover nem alterar os nove campos originais. Eventos já na fila preservam o corpo original e podem não ter payload_version. event_id, event_type e IDs de projeto e loja agora estão no corpo assinado; cabeçalhos de transporte de evento e entrega continuam sem assinatura.
payment_info descreve pagamentos observados, não todas as opções de checkout oferecidas. Antes da detecção, active_payment_method_id é null e methods está vazio. Observações reorged/invalid podem permanecer em methods mesmo quando a forma ativa passa a null. Nunca some valores de ativos ou redes diferentes.
Todos os valores, inteiros atômicos, cotações e percentuais são strings. received_amount inclui fundos válidos aguardando confirmação; confirmed_amount cumpre a política de finalidade da forma. remaining_amount é max(minimum_payment_amount menos received_amount, 0); remaining_to_full_amount é max(expected_amount menos received_amount, 0). Exemplo: 100 USDC esperados, 99 recebidos e 1% de tolerância dá remaining_amount 0 e remaining_to_full_amount 1. A finalidade continua obrigatória.
quote é o cálculo fixado da fatura: unidades do ativo por uma unidade da moeda da fatura. A margem é aplicada antes de arredondar para cima. Use expected_amount_atomic para comparar o pagamento exato; uma cotação exibida sozinha pode não reproduzir o arredondamento para cima. Faturas antigas sem origem das fontes salva mostram campos de fonte/referência/arredondamento null e provenance_available false, nunca dados de hoje apresentados como uma cotação histórica.
market_rate_at_event são dados indicativos em cache antes da margem, fixados na criação do evento. Inclui datas das fontes e indicadores de dados desatualizados e referência substituta; é null se não existir um par utilizável em cache. Nenhuma consulta de preço ao vivo bloqueia uma notificação e esta observação de mercado nunca muda o valor devido. Tokens personalizados fixos são identificados como is_fixed; tokens DEX usam a fonte específica do projeto, não um token com o mesmo símbolo.
Os campos superiores paid_chain, paid_asset, paid_payment_method_id e settlement_exchange_rate (4.1.2+) identificam a forma vencedora comprovada após liquidar, não uma opção de checkout selecionada nem uma soma de formas. Antes da liquidação, após invalidação, em liquidações antigas sem retrato ou em aceitação manual sem fundos que cumpram a finalidade exigida, os campos de resumo são null. Símbolos são rótulos visuais: siga o ID da forma para a identidade exata de rede/ativo/contrato.
Merchant 5.0.1 adiciona paid_asset_amount e paid_asset_amount_received como strings decimais exatas em unidades paid_asset; payload_version continua 2. paid_asset_amount é a cotação total fixada, incluindo margem e arredondamento para cima, nunca o limite de tolerância nem um saldo restante. paid_asset_amount_received é o total de recebimentos válidos da forma vencedora na liquidação, incluindo fundos aguardando confirmação e diferenças aceitas a menor ou maior. Exemplo: 100 USDC cotados, 99 recebidos e aceitos com tolerância dá 100 e 99, não 99 e 99. Ambos são fixados com o retrato da liquidação; use payment_info.methods[].amounts para recebimentos em cada evento ou a API de pagamentos para registros atuais. São null sem um retrato apto e para retratos anteriores a 5.0.1; corpos de eventos antigos na fila não mudam. Nunca converta strings decimais exatas para ponto flutuante na contabilidade.
settlement_exchange_rate é a observação de mercado em cache antes da margem capturada na liquidação, não a cotação fixada da fatura nem uma operação executada em uma exchange. Sua estrutura corresponde a market_rate_at_event; 1.17 asset_per_invoice_currency com EUR/USDC significa 1 EUR = 1.17 USDC. Datas das fontes e indicadores de dados desatualizados/fixos/substitutos descrevem sua qualidade. Se faltar um par, a cotação fica null, mas uma forma comprovada mantém os campos paid_*. Nunca muda o valor devido nem espera uma chamada a um provedor ao vivo. Pagamentos posteriores com a mesma forma, novas tentativas e reenvios não podem substituir o retrato salvo, incluindo uma cotação null salva. Uma nova liquidação real ou uma mudança da forma que liquida captura outro retrato; observed_at identifica essa captura, enquanto settled_at pode preservar a hora da primeira liquidação. Corpos de eventos antigos não mudam.
São incluídas no máximo oito formas observadas e as cinco observações de pagamento mais recentes por forma, com contagens e indicadores de truncamento. O limite de tamanho pode reduzir ainda mais esses arrays. Uma observação de pagamento é uma transferência/log/saída UTXO, não necessariamente um hash de transação único. Use GET /v1/projects/YOUR_PROJECT_ID/invoices/{invoice_id}/payments com payment_method_id, limit e offset para obter todo o histórico atual. O detalhe da fatura preserva cada forma cotada e seu quote_details. Links de API exigem seu host e credenciais configurados; nunca encaminhe um token bearer a uma URL arbitrária fornecida por uma notificação.
Lightning usa payment_hash em vez de transaction_id; endereço de recebimento, explorador e confirmações observadas são null. Seu valor BTC exato usa 11 casas decimais (millisatoshis) e a tolerância efetiva é zero. Não inclui pré-imagem de pagamento BOLT11, chave de carteira, segredo de assinatura nem credencial do provedor. Campos de cliente/metadados pertencem só a respostas do lojista e notificações assinadas, nunca ao checkout público; não coloque credenciais nos metadados.
Receba com segurança
- Verifique o corpo original exato com o segredo correspondente antes de analisar. Loja → IPN fornece o segredo IPN, incluindo entregas a ipn_url personalizadas. Cada endpoint de Loja → Webhooks tem seu próprio segredo. Nenhum é seu token de API; rotacionar um não rotaciona os outros.
- Confira o carimbo de tempo assinado (padrão do SDK: cinco minutos em qualquer direção) e, quando presentes, compare os IDs assinados de projeto e loja com a configuração do receptor. Salve em uma fila durável antes de retornar HTTP 2xx. Para processar por evento, event_id de v2 é assinado; IDs de cabeçalho sozinhos não protegem contra repetição porque esses cabeçalhos não são assinados. Para caixas de estado de pedido, elimine duplicatas de invoice_id e sequence e compare os campos originais de status da fatura, não o corpo v2 inteiro: tipos e IDs de evento diferentes podem compartilhar uma revisão.
- Em um worker, consulte a fatura atual pela origem da API configurada, não por um link arbitrário de notificação. Confira pedido salvo, projeto/loja, valor e moeda, exija o status atual settled e aplique sua política de aceitação manual e exceções. Bloqueie o pedido e entregue uma única vez dentro de uma transação de banco de dados, independentemente da eliminação de eventos duplicados.
- Nunca aplique uma sequence antiga sobre uma mais nova. Vários eventos podem compartilhar revisão; não combine eliminação de duplicatas por revisão com um filtro exclusivo invoice.settled. Reabertura/conciliação pode mudar o status; sequence, não uma hierarquia fixa de estados, ordena as atualizações. Registre reversões para revisão em vez de entregar novamente.
Exemplos de receptor: PHP · Python · Node.js / TypeScript.
Verificação de assinatura e regras de entrega
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyWhollySignature(rawBody, header, signingSecret, toleranceSeconds = 300) {
const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
if (!match) return false;
const timestamp = Number(match[1]);
if (!Number.isSafeInteger(timestamp)) return false;
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > toleranceSeconds) return false;
// rawBody must be the exact request Buffer, before JSON parsing.
const expected = createHmac("sha256", signingSecret)
.update(String(timestamp))
.update(".")
.update(rawBody)
.digest();
const presented = Buffer.from(match[2], "hex");
return timingSafeEqual(expected, presented);
}| Regra de entrega | Detalhes |
|---|---|
| Cabeçalhos | Wholly-Signature, Wholly-Event-Id e Wholly-Delivery-Id; Content-Type é application/json. |
| Assinatura | HMAC-SHA256 sobre <unix timestamp>.<exact raw body>; formato do cabeçalho t=<timestamp>,v1=<64 lowercase hex>. |
| Sucesso | Qualquer resposta HTTP 2xx. Redirecionamentos não são seguidos; respostas não 2xx são falhas. |
| Tempos limite | 5 segundos para conectar e 10 segundos totais por solicitação. |
| Cronograma de novas tentativas | Até 8 tentativas para falhas recuperáveis: imediatamente e depois com intervalos de 10s, 1m, 5m, 15m, 1h, 6h e 24h após terminar a tentativa anterior. IPN tenta novamente automaticamente; novas tentativas automáticas de webhooks podem ser desativadas por endpoint. |
| Segurança do destino | Só HTTPS público. O DNS é revalidado e fixado para a entrega; destinos locais, privados ou reservados são rejeitados. |
| Retenção de eventos | Os dados de eventos de notificação e as entregas são preservados por 90 dias conforme a programação; detalhes retidos são removidos em lotes limitados. |
| Eliminação de duplicatas | Salve os valores assinados invoice_id e sequence no escopo do projeto configurado. Wholly-Event-Id identifica um evento; Wholly-Delivery-Id identifica um registro de entrega (novas tentativas o reutilizam; um reenvio manual cria outro). Nenhum dos cabeçalhos de ID é assinado. |
| Nomes dos eventos | A versão 2 assina event_id e event_type no corpo. Eventos antigos na fila não têm nenhum deles. Tipos diferentes de evento podem compartilhar uma sequence de fatura; concilie o status por revisão ou elimine eventos individuais duplicados por event_id assinado. |
| Rotação de segredos | A rotação não tem sobreposição nem cabeçalho de versão e muda imediatamente as assinaturas de entregas na fila, repetidas e manuais. |
| Entregas pausadas | Créditos de processamento insuficientes pausam IPN/webhooks, incluindo novas tentativas. Pagamentos recebidos continuam; as notificações na fila são retomadas após recarregar dentro do prazo de retenção dos dados. |
Assistentes de IA · MCP
Conecte um assistente à sua instalação de lojista.
Merchant 5.0.0 inclui um servidor MCP opcional no seu domínio de API configurado. Ele roda dentro da sua instalação, não por um relay compartilhado do Wholly Crypto.
- Abra Configurações → Acesso à API. Crie uma credencial dedicada, atribua só os projetos de que o assistente precisa e comece com acesso somente leitura. Em uma conta hospedada por um operador, ele ativa primeiro o serviço MCP da instalação; você só gerencia suas próprias credenciais e autorizações.
- Em Conexões de IA · MCP, ative MCP, selecione a credencial e salve o acesso MCP dela. Credenciais existentes não têm acesso MCP até que seja ativado expressamente.
- Copie a URL do servidor MCP nas configurações de servidor HTTP remoto do seu cliente. Com OAuth, entre no console do lojista, revise o nome do cliente e o endereço de retorno, escolha uma credencial e aprove. Suas proteções Basic Auth e TOTP existentes continuam se aplicando.
- Criar faturas também exige uma credencial de leitura/gravação, Ler e criar faturas na política MCP dela, o escopo OAuth mcp:invoice:create e aprovação explícita. Uma conexão OAuth nunca recebe projetos adicionados a uma credencial após a aprovação.
{
"mcpServers": {
"whollycrypto": {
"url": "https://api.example.com/mcp"
}
}
}| Ferramenta | Acesso | Finalidade |
|---|---|---|
| list_projects | Consulte os | Projetos habilitados atribuídos à conexão; paginação limit/offset. |
| list_stores | Consulte os | Lojas, IDs e status de habilitação dentro de project_id; paginação limit/offset. |
| list_payment_methods | Consulte os | Formas de redes, tokens e Lightning configuradas para project_id + store_id. |
| get_wallet_balances | Consulte os | Endereços de recebimento e saldos em cache, com campos de atualização/disponibilidade; nunca segredos de carteiras. |
| list_invoices | Consulte os | Faturas do projeto filtradas por loja, status ou busca; paginação limit/offset. |
| get_invoice | Consulte os | Detalhes completos da fatura e link de checkout usando project_id + invoice_id. |
| get_delivery_history | Consulte os | Status, tentativas e resultados HTTP de IPN/webhooks da loja. Filtros opcionais invoice_id/kind; sem segredos nem corpos de notificação. |
| convert_amount | Consulte os | Conversão de referência em cache usando from, to e um amount como string decimal; não é uma cotação de fatura. |
| create_invoice | Gravação explícita | project_id, store_id, idempotency_key e invoice (o corpo existente de criação de faturas). invoice.payment_methods filtra as formas habilitadas da loja; 5.4.0+ ignora opções inativas/não aceitas e volta aos padrões da loja se nenhuma corresponder. Filtrar só por rede seleciona todos os ativos aceitos ativos. asset_tickers limitados por rede são aceitos desde 5.3.0. Retorna a resposta normal de fatura. |
Protocolo, OAuth e segurança
Use Streamable HTTP por HTTPS. Negocie uma versão de protocolo anunciada e inclua MCP-Protocol-Version nos POSTs seguintes. Envie Content-Type: application/json e Accept: application/json, text/event-stream. As respostas são JSON finito; reconexões não precisam de um ID de sessão MCP.
OAuth usa tokens de acesso curtos (15 minutos), códigos S256 PKCE de uso único (5 minutos) e tokens de atualização rotativos (duração da conexão de 30 dias). Reutilizar um token de atualização já usado revoga essa conexão. Reconecte após expiração, rotação de credenciais, mudanças de política ou do domínio canônico da API.
A descoberta OAuth só é pública quando MCP está ativado. O parâmetro resource precisa coincidir com a URL canônica retornada pela descoberta, incluindo /mcp. Registro dinâmico é aceito; documentos remotos de metadados de ID de cliente e segredos de cliente não são aceitos.
Clientes que aceitam cabeçalhos Authorization personalizados podem usar um token de API de lojista habilitado para MCP como Bearer. Ele mantém suas permissões REST separadas; prefira OAuth para uma conexão restrita ao MCP. Nunca cole credenciais em chats, URLs, argumentos de ferramentas nem controle de versões.
MCP compartilha a cota REST por minuto da credencial e as restrições exatas de IP de origem, além das restrições de IP do host da API. OAuth não ignora uma lista de permitidos. Para clientes de IA remotos, permita os IPs de saída documentados deles ou deixe essa restrição desativada deliberadamente. Não aplique desafios de segurança web nem cache às rotas MCP/OAuth.
Erros HTTP: 401 exige autenticação, 403 nega origem/IP/permissão, 404 significa MCP desativado ou host errado, 405 exige POST, 413 indica o limite do corpo de 32 KiB e 429 inclui Retry-After. Erros JSON-RPC usam error.code; falhas de ferramentas usam result.isError=true mesmo com HTTP 200. Resultados bem-sucedidos incluem content e structuredContent.
As listas mostram 25 linhas por padrão, máximo 100; offset é limitado a 1000000. Respostas de ferramentas são limitadas a 2 MiB. Autorizações, solicitações de autorização e contadores de cota expirados são removidos automaticamente; as configurações mostram no máximo 100 conexões OAuth ativas.
Não é possível operar projetos ou lojas desativados pelo MCP. A conexão pode listar o status de habilitação de uma loja, mas ler suas formas de pagamento ou histórico de entregas e criar faturas exige uma loja habilitada. Usuários comuns de projetos do console não podem administrar MCP.
Use um idempotency_key novo para uma fatura nova; após esgotar o tempo de espera, tente novamente com a mesma credencial, chave e objeto invoice idêntico. Valores decimais, margem, tolerância, confirmações e aparência do checkout seguem o contrato REST de faturas. MCP nunca ignora a política de pagamentos ou créditos do lojista.
As ferramentas iniciais não podem revelar chaves privadas/frases de recuperação, enviar fundos manual ou automaticamente, reembolsar, reenviar notificações, mudar formas de pagamento, editar contas/domínios nem gerenciar faturamento. Trate descrições de faturas, campos de clientes e metadados como dados não confiáveis, não instruções para o agente. Provedores de IA conectados recebem os dados que você autoriza ler.
| Método | Caminho | Contrato |
|---|---|---|
| POST | /mcp | JSON-RPC autenticado: initialize, ping, tools/list, tools/call. Solicitações de notificação retornam 202; lotes são rejeitados. |
| GET / DELETE | /mcp | 405 autenticado: respostas JSON finitas, sem fluxo SSE separado nem sessão MCP no servidor. |
| GET | /.well-known/oauth-protected-resource/mcp | URL canônica do recurso e descoberta do servidor de autorização; também disponível em /.well-known/oauth-protected-resource. |
| GET | /.well-known/oauth-authorization-server | Endpoints OAuth, authorization_code/refresh_token, S256 PKCE e escopos compatíveis. |
| POST | /mcp/oauth/register | Registro de cliente público: client_name e redirect_uris exatos. Só HTTPS ou HTTP de loopback. Sem segredo de cliente nem consulta remota de metadados. |
| GET | /mcp/oauth/authorize | client_id, redirect_uri, response_type=code, resource, code_challenge, code_challenge_method=S256, scope/state opcionais; redireciona à aprovação no console. |
| POST | /mcp/oauth/token | Codificado como formulário: authorization_code + code + code_verifier + redirect_uri, ou refresh_token + refresh_token. Inclua sempre client_id e resource. |
| POST | /mcp/oauth/revoke | client_id e token codificados como formulário. Revoga a conexão do token de acesso/atualização correspondente. |
Exemplo de solicitação direta a uma ferramenta
Primeiro inicialize e negocie o protocolo pelo seu cliente MCP. Isto mostra uma solicitação posterior.
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/mcp" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'MCP-Protocol-Version: 2025-11-25' \
--header 'Accept: application/json, text/event-stream' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_invoices",
"arguments": {
"project_id": "YOUR_PROJECT_ID",
"limit": 10
}
}
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_invoices",
"arguments": {
"project_id": "YOUR_PROJECT_ID",
"limit": 10
}
}
}`;
const response = await fetch("https://api.example.com/mcp", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"MCP-Protocol-Version": "2025-11-25",
"Accept": "application/json, text/event-stream",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_invoices",
"arguments": {
"project_id": "YOUR_PROJECT_ID",
"limit": 10
}
}
}
JSON;
$ch = curl_init("https://api.example.com/mcp");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "MCP-Protocol-Version: 2025-11-25", "Accept: application/json, text/event-stream", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"MCP-Protocol-Version": "2025-11-25",
"Accept": "application/json, text/event-stream",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_invoices",
"arguments": {
"project_id": "YOUR_PROJECT_ID",
"limit": 10
}
}
}""".encode("utf-8")
request = Request("https://api.example.com/mcp",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))API do operador
Crie lojistas hospedados com chaves de servidor separadas e de escopo limitado.
Hospede vários negócios e automatize sua configuração por api.example.com/v1/operator. Disponível desde 7.4.0 só no modo operador. A API normal do lojista não muda.
- Abra Operador → Configurações → API do operador e ative (desativada por padrão). Crie uma credencial separada só com as permissões e os lojistas hospedados necessários.
- Guarde a chave wc_operator_ no seu servidor. Use o host da API, não o host do painel do operador nem uma chave de lojista.
- Salve um Idempotency-Key e o corpo exato da solicitação antes de cada POST do operador. Consulte novamente a conta após um resultado incerto; nunca substitua a chave só para tentar de novo.
- Crie um lojista com onboarding: direct e senha, ou onboarding: invitation e sem senha. Depois crie projetos/lojas e emita uma chave de lojista restrita ao projeto para a integração de checkout.
| Escopo | Acesso |
|---|---|
| merchants.read / merchants.write | Listar/ler e criar/atualizar lojistas hospedados. |
| users.read / users.write / users.security | Ler/criar/atualizar usuários; mudar senhas ou revogar sessões separadamente. Nunca cria um administrador de operador. |
| invitations.read / invitations.write | Listar/ler, criar, substituir e revogar links de convite/redefinição de uso único. Novos usuários também exigem users.write; redefinições também exigem users.security. |
| credits.read / credits.write / fees.write | Ler saldos/livro-razão; conceder ou corrigir crédito local; definir taxas futuras. Créditos iniciais diferentes de zero exigem credits.write. |
| topups.read / topups.write | Ler ou criar solicitações de checkout para créditos de lojistas hospedados. Nenhuma ação da API pode marcá-las como pagas. |
| projects.read / projects.write / reports.read | Criar projetos, lojas, aparência e configurações de pagamento de lojistas; ler faturas, saldos de carteiras e relatórios financeiros. |
| merchant_credentials.read / merchant_credentials.write | Gerenciar chaves normais de lojista com escopo limitado. Permissão poderosa: essas chaves atuam independentemente após a emissão. |
| events.read / webhooks.write / audit.read / health.read | Ler histórico do ciclo de vida; configurar notificações assinadas do ciclo de vida; ler auditoria/capacidades/saúde dos nós. |
Cadastro, créditos, permissões e novas tentativas seguras
| Tema | Regra |
|---|---|
| Credenciais | Expiração opcional e lista exata de IPv4/IPv6 permitidos; 60 solicitações/minuto por padrão, configurável entre 1–600. Cada solicitação verifica o administrador emissor e os escopos atuais. HTTP 429 inclui Retry-After. |
| Isolamento | As chaves só acessam seus lojistas hospedados atribuídos. Criar lojistas e ver relatórios de toda a instalação exige acesso a todos os lojistas. O negócio do próprio operador fica excluído. |
| Primeiro acesso | Contas diretas reconhecem que quem hospeda pode acessar suas chaves de carteiras. require_password_change adiciona uma troca de senha no primeiro acesso. Aceitar um convite exige reconhecer expressamente a custódia e depois entrar normalmente. Basic Auth e TOTP existentes continuam em vigor. |
| Convites | Links de novos usuários duram 48 horas; os de redefinir senha, uma hora. Tokens são de uso único. Reemitir revoga o link anterior. A aceitação SMTP não garante entrega na caixa de entrada; confira email_delivery. |
| Novas tentativas seguras | Cada POST do operador exige uma chave de 16–128 caracteres (letras, dígitos, -, _ ou .). A mesma chave com URL/corpo exatos retorna o resultado salvo. Bytes diferentes retornam 409. Segredos/links são omitidos na repetição; rotacione ou reemita por uma nova operação explícita quando necessário. |
| Resultados incertos | operator_request_in_progress significa que uma operação está em andamento ou foi interrompida antes de registrar o recibo. Inspecione o recurso e a auditoria; não envie uma chave nova às cegas. Recibos concluídos são compactados após 30 dias; chaves antigas continuam sem poder executar novamente. |
| Créditos e taxas | Strings decimais, no máximo seis casas decimais. starting_credit é uma concessão local única. Ajustes precisam de valor com sinal, nota e request_id, além da chave HTTP de nova tentativa. fee_bps=100 significa 1%; mudanças afetam faturas futuras. Concessões não recarregam o saldo pré-pago próprio da instalação. |
| Pausar | enabled=false desativa uma conta hospedada e revoga sessões do console. payments_paused=true interrompe novas faturas. O monitoramento de pagamentos existentes continua. A criação de projetos/lojas e a automação mantêm a política de crédito da instalação. |
| Não exposto | Sem segredos de carteiras, assinatura, envios, reembolsos, exclusão permanente, redefinição TOTP, mudanças de domínio nem configuração do servidor. Solicitações normais de faturas continuam usando uma chave de lojista e a API do lojista. |
Webhooks do ciclo de vida do operador
| Evento | Dados |
|---|---|
| merchant.created / merchant.updated | merchant_id, enabled, payments_paused, fee_bps. |
| user.created / user.updated | merchant_id, user_id, enabled. O evento de atualização cobre mudanças de email, status de habilitação e função de administrador. |
| invitation.accepted / password_reset.completed | merchant_id, user_id, invitation_id. |
| topup.settled / credit.balance_changed | merchant_id, ledger_id, kind, amount e balance. Leia a moeda de crédito do lojista ou o detalhe do livro-razão ao conciliar. |
Eventos do ciclo de vida do operador são separados de IPN de faturas e webhooks de loja. Uma assinatura pertence à credencial de operador que a criou, com até 10 endpoints por chave. Só eventos futuros correspondentes entram na fila; use GET /events para o histórico preservado.
O corpo contém event_id, event_type, merchant_id, occurred_at e data. Verifique Wholly-Signature sobre o corpo original exato usando o signing_secret mostrado uma vez do endpoint: HMAC-SHA256(secret, timestamp + '.' + raw_body), cabeçalho t=...,v1=.... Aplique uma tolerância curta de tempo.
Use o verificador genérico de assinaturas do SDK, não o analisador de notificações de faturas. Depois valide merchant_id e event_type, salve e elimine duplicatas de event_id de forma transacional e retorne 2xx só após aceitação durável. Wholly-Event-Id precisa corresponder ao corpo assinado. Não trate cabeçalhos sem assinatura como dados de negócio.
A entrega é pelo menos uma vez, pode chegar fora de ordem e é tentada até 8 vezes. Leia os recursos atuais para conciliar; occurred_at não é uma sequência monotônica. O escopo e as configurações de habilitação/expiração são verificados novamente antes da entrega. Assinaturas desativadas pausam o trabalho já na fila, mas não enfileiram novos eventos enquanto estão desativadas.
Eventos e histórico de entregas são preservados por 30 dias. A política de automação da instalação pode pausar a entrega. GET /webhooks/{id}/deliveries mostra resultado e dados imutáveis; a API pública não força a entrega de um registro expirado.
{
"event_id": "55555555-5555-4555-8555-555555555555",
"event_type": "merchant.created",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"occurred_at": "2026-10-01T12:00:00Z",
"data": {
"merchant_id": "11111111-1111-4111-8111-111111111111",
"enabled": true,
"payments_paused": false,
"fee_bps": 300
}
}Erros e limites
Trate validações, cotas e novas tentativas de forma previsível.
Confira o status HTTP e Content-Type antes de analisar uma resposta. Para um 429, aguarde pelo menos o tempo de Retry-After antes de tentar novamente.
| Limite | Detalhes |
|---|---|
| Frequência de solicitações | Cota por credencial: 120 solicitações por minuto UTC por padrão, configurável entre 1 e 6000 em Configurações → API. Todas as leituras e gravações v1 autenticadas, incluindo novas tentativas idempotentes e falhas de autorização/validação após autenticar, compartilham a cota entre domínios, projetos e processos. Credenciais inválidas, rotas do console e checkout público não consomem a cota. |
| Cabeçalhos de limite de solicitações | Respostas v1 autenticadas incluem X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset (segundos Unix no início do próximo minuto UTC). Solicitações excedentes retornam JSON 429 rate_limit_exceeded e Retry-After em segundos inteiros. Aguarde pelo menos esse tempo e adicione variação aleatória nas novas tentativas. Janelas fixas permitem rajadas nos limites de minuto; não garantem solicitações por segundo. |
| Corpo do lojista | Máximo de 32 KiB no roteador do aplicativo. A camada de entrada pode rejeitar uma solicitação grande demais antes de gerar um erro JSON. |
| Lista de faturas | limit é 50 por padrão e aceita 1–100; offset aceita 0–1,000,000. A busca aceita no máximo 100 caracteres. Os resultados vão do mais recente ao mais antigo e incluem metadados total/has_more. |
| Formas da loja | No máximo 64 seleções de ativos por loja, suficiente para as 30 redes nativas e o catálogo limitado de tokens verificados. A política do projeto, a capacidade do scanner e uma carteira de rede pronta e com backup continuam condicionando a criação de faturas. |
| Descoberta de tokens | O limite de candidatos é 50 por padrão e aceita 1–100. Os resultados da descoberta não são ativos de pagamento até passar pela verificação na blockchain. |
| Tokens registrados do projeto | No máximo 20 ativos de token persistentes por projeto. Ativos já registrados podem ser reutilizados sem consumir outra vaga. |
| Idempotência | Obrigatória para criar faturas. 1–128 caracteres ASCII visíveis sem espaços; as chaves são únicas por loja e uma repetição precisa usar a credencial original e o corpo original exato. |
| Metadados | Só objeto JSON, no máximo 4,096 bytes codificados e cinco níveis de aninhamento. |
| Notificações | URL HTTPS pública de até 2,048 bytes. Corpos de solicitações de notificação são limitados a 256 KiB; dados de eventos de fatura preservados são limitados a 64 KiB com históricos de pagamento limitados. |
| Recursos do checkout | Respostas QR SVG são privadas e no-store porque um pagamento a menor muda o restante exato. Logos PNG com versão ficam em cache público por um ano e são imutáveis. |
| Camada de entrada da API | Solicitações ao backend da API gerenciada têm um tempo de leitura de 30 segundos. Projete clientes com tempos limite explícitos menores que o tempo disponível da tarefa. |
| Falhas sem JSON | UUIDs ou parâmetros de consulta malformados, métodos errados e o limite de 32 KiB podem retornar texto do framework ou respostas vazias. Rotas /v1 desconhecidas atualmente retornam HTML do console com 404; valide status e Content-Type antes de analisar. |
Referência de erros
| HTTP | Código do erro | Significado |
|---|---|---|
| 400 | invalid_reconciliation_action | Um filtro de status de exceção, motivo, busca ou página de histórico é inválido. |
| 500 | reconciliation_unavailable | Não foi possível carregar a fila de exceções ou as evidências. Tente a leitura novamente com espera progressiva. |
| 402 | billing_required | Cada fatura nova exige uma conta de créditos vinculada e verificada e autorização vigente. Créditos pré-pagos insuficientes não bloqueiam a criação nem pagamentos recebidos: IPN, webhooks e Envio de fundos são pausados, enquanto as taxas continuam se acumulando. A criação continua bloqueada para contas suspensas, verificação de faturamento vencida/inválida, serviço de créditos inacessível ou base fiduciária de fatura não autorizada. As taxas usam o valor fiduciário original da fatura, não a cripto recebida, a margem, o pagamento a maior nem as taxas de rede. Esse valor e a conversão independente são registrados antes de criar o checkout. O monitoramento existente e a consulta de faturas continuam durante interrupções. Após recarregar, notificações na fila são retomadas dentro do prazo normal de retenção e regras de envio ativadas são retomadas. Confira Configurações → Taxas e repita a criação que falhou com o mesmo Idempotency-Key. |
| 400 | invalid_json | JSON malformado, campo desconhecido ou corpo que não corresponde à solicitação documentada. |
| 400 | idempotency_key_required | A criação da fatura omitiu Idempotency-Key. |
| 400 | invalid_idempotency_key | A chave está vazia, passa de 128 bytes, não é ASCII ou contém espaços ou um byte de controle. |
| 400 | invalid_payment_request | Um campo validado ou uma forma ativa selecionada falhou. Leia error.message e error.details.payment_methods (PaymentMethodIssue[]) para saber o bloqueio exato. SDK 2.4.0+ adiciona resumos seguros de exceções com ações e funções para problemas; SDKs de PHP antigos expõem getApiMessage(). |
| 400 | invalid_invoice_status | O status da lista não pertence aos seis status de fatura documentados. |
| 400 | invalid_callback_url | O destino IPN efetivo falhou na validação de HTTPS, endereço público, DNS ou SSRF. |
| 400 | invalid_wallet_request | Um dado de preparação de carteira/endereço é inválido. |
| 400 | invalid_token_asset | A rede do token, consulta de candidatos, identidade CoinGecko, metadados do catálogo ou entrada de contrato/mint é inválida. |
| 401 | authentication_required | O token Bearer está ausente, malformado, desativado, rotacionado ou é desconhecido. |
| 403 | source_ip_denied | A restrição de IP da credencial não inclui o endereço público exato de origem da solicitação. |
| 403 | source_ip_not_allowed | A restrição de IP de origem do host exclui este cliente. Um administrador pode gerenciar listas de permitidos de hosts ativos em Configurações → Sistema; elas se aplicam além das restrições de IP da credencial. |
| 503 | source_access_unavailable | A verificação de acesso ao host está temporariamente indisponível. Tente mais tarde; em caso de falha, as restrições bloqueiam o acesso. |
| 403 / 409 / 500 | merchant_api_access_denied | A autorização falhou: permissão/escopo de projeto pode dar 403, projeto/loja desativado pode dar 409 e uma falha do backend de autorização pode dar 500. Carteiras de recebimento do operador são reservadas ao painel do operador, não a credenciais de API de lojista nem MCP, mesmo com uma autorização explícita antiga do projeto. |
| 403 | project_access_denied | Uma nova verificação transacional na criação detectou que a credencial não tem mais acesso ao projeto. |
| 404 | invoice_not_found | Não existe uma fatura com esse ID público no projeto autorizado ou o checkout não pode expô-la. |
| 404 | payment_resource_not_found | Um projeto, loja, ativo ou carteira necessário ao preparar a fatura não existe mais. |
| 404 | token_candidate_not_found | O projeto está indisponível ou o token não está mais no catálogo de descoberta correspondente atual. |
| 409 | idempotency_conflict | A chave restrita à loja já existe e a credencial ou os bytes exatos do corpo original são diferentes. |
| 409 | store_unavailable | O projeto ou a loja está desativado ou indisponível. |
| 409 | no_ready_payment_methods | Nenhuma forma da loja está pronta. Leia error.message e error.details.payment_methods para ver chain_slug, asset_ticker e reason_code. O backup/ativação da carteira, o adaptador instalado e os preços precisam ser válidos. Desde 6.0.6, pausas do scanner, verificações de saúde com falha ou antigas e falta de quórum de provedores não bloqueiam a criação. |
| 409 | payment_method_unavailable | Uma forma selecionada ficou indisponível durante a nova verificação atômica na criação. |
| 409 | store_payment_method_not_selected | Foi solicitada uma substituição de confirmações da loja para um ativo que essa loja não tem selecionado. |
| 409 | wallet_unavailable | Uma carteira de pagamento ficou indisponível durante a nova verificação atômica na criação. |
| 409 | ipn_secret_required | Existe uma URL IPN efetiva, mas a loja não tem segredo de assinatura IPN. |
| 409 | payment_resource_not_ready | Um ativo de pagamento ou carteira exigido está desativado, sem backup, aguardando prova de ativação de conta compartilhada, esgotado ou não pronto por outro motivo. |
| 409 | account_activation_unverified | Não foi possível comprovar a ativação da conta XRP Ledger ou Stellar com o número configurado de endpoints saudáveis da rede principal (2 por padrão, 1 opcional); envie fundos à conta exata e repita a verificação. |
| 400 | invalid_monero_wallet_rpc | O endpoint HTTPS, o endereço principal exato da rede principal, o rótulo ou os dados completos de autenticação Digest/Basic/cabeçalho são inválidos. |
| 404 | monero_wallet_rpc_not_found | O vínculo de wallet-RPC Monero restrito ao projeto não existe. |
| 409 | monero_wallet_rpc_not_ready | O ativo Monero, quórum de dois daemons, vínculo imutável ou declaração explícita de backup/somente leitura não está pronto. |
| 409 | monero_wallet_rpc_unavailable | Criar faturas exige um vínculo wallet-RPC Monero do projeto ativo, verificado e declarado, com credencial válida no servidor. |
| 503 | lightning_unavailable | A única forma pronta da loja é Lightning e não foi possível verificar sua carteira ou cotação. Tente novamente com a mesma chave de idempotência. Se houver outra forma pronta na blockchain, a forma Lightning indisponível é omitida. |
| 422 | monero_wallet_rpc_verification_failed | Falhou a verificação de carteira exata, fixação HTTPS, sincronização, quórum de daemons da rede principal ou prova de rejeição de métodos do gateway. |
| 503 | monero_wallet_rpc_failed | A wallet-RPC externa somente observação não conseguiu criar e reler com segurança o subendereço da fatura; nenhum endereço alternativo é inventado. |
| 409 | token_chain_not_ready | O ativo nativo da rede está desativado, a correspondência de descoberta mudou durante a verificação ou o projeto já atingiu o máximo atual de 20 ativos de token registrados. |
| 503 | dex_price_unavailable | Provedor DEX indisponível, ocupado, limitado por cota, com resposta antiga ou dados malformados. Tente novamente após um minuto; preços fixos continuam disponíveis. |
| 422 | invalid_dex_price | Combinação de modo de preço inválida ou o pool escolhido não pode fornecer um preço apto para o contrato exato. Escolha outro pool ou preço fixo em USD. |
| 422 | token_verification_failed | Todos os nós aptos falharam na verificação de identidade da rede, código do contrato, decimais, consulta de saldo ou mint. |
| 422 | invalid_store_confirmation_policy | A substituição da loja está indisponível para este modo de finalidade, fora dos limites retornados para a rede ou solicita aceitar zero confirmações sem suporte. |
| 409 | invoice_not_payable | A fatura do checkout está em estado terminal ou seu prazo de pagamento venceu. |
| 409 | invoice_payment_method_locked | Um pagamento válido já selecionou outro ativo; continue com active_payment_method_id. |
| 409 | payment_method_not_payable | A forma selecionada está completa ou não aceita mais outro pagamento. |
| 422 | payment_qr_unavailable | A solicitação de checkout é grande demais para codificar como imagem QR SVG. |
| 503 | payment_rates_unavailable | Não há cotação recente e confiável para nenhuma forma de pagamento pronta. |
| 500 | authentication_unavailable | A autenticação Bearer não conseguiu ler ou validar com segurança sua credencial armazenada. |
| 429 | rate_limit_exceeded | Esta credencial esgotou sua cota do minuto UTC atual. Aguarde pelo menos Retry-After segundos; tente criar a fatura novamente com a mesma chave de idempotência. |
| 500 | database_error / internal_error | Falha temporária do servidor; tente novamente com segurança usando a mesma chave de idempotência. |
Visão geral da API
Escolha um endpoint para ver campos, exemplos e resposta.
Faturas
POSTCriar fatura/v1/projects/{project_id}/stores/{store_id}/invoicesGETListar faturas/v1/projects/{project_id}/invoicesGETObter fatura/v1/projects/{project_id}/invoices/{invoice_id}GETListar pagamentos de uma fatura/v1/projects/{project_id}/invoices/{invoice_id}/paymentsFormas de pagamento
GETListar ativos de pagamento do projeto/v1/projects/{project_id}/payment-assetsPUTAtualizar política de ativos do projeto/v1/projects/{project_id}/payment-assets/{asset_id}GETExplorar tokens candidatos para pagamentos/v1/projects/{project_id}/payment-token-candidatesPOSTVerificar e registrar token/v1/projects/{project_id}/payment-token-assetsGETBuscar pools DEX de tokens personalizados/v1/projects/{project_id}/payment-token-dex-poolsPOSTAdicionar ou alterar preço de token personalizado/v1/projects/{project_id}/payment-token-assets/customGETListar formas de pagamento da loja/v1/projects/{project_id}/stores/{store_id}/payment-assetsPUTSubstituir formas de pagamento da loja/v1/projects/{project_id}/stores/{store_id}/payment-assetsPUTDefinir política de confirmações de uma loja/v1/projects/{project_id}/stores/{store_id}/payment-assets/{asset_id}/confirmation-policyCarteiras
GETListar carteiras e saldos do projeto/v1/projects/{project_id}/walletsConciliação
GETListar exceções de pagamento/v1/projects/{project_id}/reconciliationGETLer evidências de conciliação/v1/projects/{project_id}/reconciliation/{invoice_id}API do operador
GETCapacidades/v1/operator/capabilitiesGETSaúde do serviço/v1/operator/healthGETListar lojistas/v1/operator/merchantsPOSTCriar lojista/v1/operator/merchantsGETObter lojista/v1/operator/merchants/{merchant_id}POSTAtualizar lojista/v1/operator/merchants/{merchant_id}GETListar usuários/v1/operator/merchants/{merchant_id}/usersPOSTCriar usuário/v1/operator/merchants/{merchant_id}/usersGETObter usuário/v1/operator/merchants/{merchant_id}/users/{user_id}POSTAtualizar usuário/v1/operator/merchants/{merchant_id}/users/{user_id}POSTDefinir senha de usuário/v1/operator/merchants/{merchant_id}/users/{user_id}/passwordPOSTRevogar sessões de usuário/v1/operator/merchants/{merchant_id}/users/{user_id}/revoke-sessionsGETListar convites/v1/operator/merchants/{merchant_id}/invitationsPOSTCriar convite/v1/operator/merchants/{merchant_id}/invitationsGETObter convite/v1/operator/invitations/{invitation_id}POSTReenviar convite/v1/operator/invitations/{invitation_id}/resendPOSTRevogar convite/v1/operator/invitations/{invitation_id}/revokeGETObter créditos/v1/operator/merchants/{merchant_id}/creditsGETListar livro-razão de créditos/v1/operator/merchants/{merchant_id}/credits/ledgerPOSTAjustar créditos/v1/operator/merchants/{merchant_id}/credits/adjustmentsGETListar recargas/v1/operator/merchants/{merchant_id}/topupsPOSTCriar recarga/v1/operator/merchants/{merchant_id}/topupsGETObter recarga/v1/operator/merchants/{merchant_id}/topups/{topup_id}GETRelatórios/v1/operator/reportsGETListar auditoria/v1/operator/auditGETListar eventos/v1/operator/eventsGETListar webhooks/v1/operator/webhooksPOSTCriar webhook/v1/operator/webhooksPOSTAtualizar webhook/v1/operator/webhooks/{webhook_id}POSTRotacionar segredo de webhook/v1/operator/webhooks/{webhook_id}/rotateGETListar entregas de webhooks/v1/operator/webhooks/{webhook_id}/deliveriesGETListar projetos/v1/operator/merchants/{merchant_id}/projectsPOSTCriar projeto/v1/operator/merchants/{merchant_id}/projectsGETObter projeto/v1/operator/merchants/{merchant_id}/projects/{project_id}POSTAtualizar projeto/v1/operator/merchants/{merchant_id}/projects/{project_id}GETListar lojas/v1/operator/merchants/{merchant_id}/projects/{project_id}/storesPOSTCriar loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/storesGETObter loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}POSTAtualizar loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}GETObter aparência da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/checkout-appearancePOSTAtualizar aparência da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/checkout-appearanceGETListar ativos de pagamento da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/payment-assetsPOSTAtualizar ativos de pagamento da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/payment-assetsGETListar webhooks da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooksPOSTCriar webhook da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooksPOSTAtualizar webhook da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooks/{webhook_id}GETListar faturas/v1/operator/merchants/{merchant_id}/projects/{project_id}/invoicesGETObter fatura/v1/operator/merchants/{merchant_id}/projects/{project_id}/invoices/{invoice_id}GETListar carteiras/v1/operator/merchants/{merchant_id}/projects/{project_id}/walletsGETListar endereços de carteiras/v1/operator/merchants/{merchant_id}/projects/{project_id}/wallets/{wallet_id}/addressesGETListar credenciais de lojista/v1/operator/merchants/{merchant_id}/api-credentialsPOSTCriar credencial de lojista/v1/operator/merchants/{merchant_id}/api-credentialsPOSTAtualizar credencial de lojista/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}POSTRotacionar credencial de lojista/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}/rotatePOSTRevogar credencial de lojista/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}/revokePOSTVerificar token de convite/v1/onboarding/invitations/checkPOSTAceitar convite ou redefinição de senha/v1/onboarding/invitations/acceptCheckout
GETEstrutura do checkout/GETPágina de checkout hospedada/invoice/{invoice_id}GETFatura segura para o checkout/checkout-api/invoices/{invoice_id}GETPrévia do checkout da loja/invoice/preview/{project_id}GETDados da prévia do checkout/checkout-api/previews/{project_id}GETImagem do checkout da loja/checkout-api/invoices/{invoice_id}/appearance-images/{kind}/{revision}/image.pngGETImagem da prévia da loja/checkout-api/previews/{project_id}/stores/{store_id}/appearance-images/{kind}/{revision}/image.pngGETLogo da prévia com versão/checkout-api/previews/{project_id}/logo/{revision}/image.pngGETImagem QR de pagamento/checkout-api/invoices/{invoice_id}/payment-methods/{intent_id}/qr.svgGETLogo do checkout com versão/checkout-api/invoices/{invoice_id}/logo/{revision}/image.pngServiço
GETDescoberta do serviço de API/GETSaúde do serviço/healthzGETCapacidades/v1/operator/capabilitiesSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige health.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/capabilities" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/capabilities", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/capabilities");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/capabilities",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"api_version": "v1",
"operator_version": "7.4.0",
"scopes": [
"health.read"
],
"all_merchants": false,
"merchant_ids": [
"11111111-1111-4111-8111-111111111111"
],
"onboarding": [
"direct",
"invitation"
],
"write_methods": [
"POST"
],
"idempotency_required": true
}GETSaúde do serviço/v1/operator/healthSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige health.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/health" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/health", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/health");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/health",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"version": "7.4.0",
"nodes": []
}GETListar lojistas/v1/operator/merchantsSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige merchants.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCriar lojista/v1/operator/merchantsLeitura e gravação
Crie atomicamente um lojista hospedado e seu primeiro administrador, diretamente com senha ou por convite.
- Exige merchants.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exige acesso global aos lojistas. Taxas personalizadas explícitas também precisam de fees.write; starting_credit diferente de zero precisa de credits.write. Cadastro por convite também precisa de invitations.write. Sem login automático, sem ignorar Basic Auth e sem crédito retroativo ao tentar novamente.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| name, email | string · required | Nome do lojista e email globalmente único do primeiro administrador. |
| onboarding | direct | invitation · required | direct exige password e não envia email de convite. invitation omite password. |
| password | string · direct only | 12–128 caracteres (no máximo 512 bytes UTF-8); nunca retornada nem enviada por email. Use require_password_change para senhas temporárias. |
| require_password_change | boolean · default false | Exige uma senha nova no primeiro acesso. Cada conta criada diretamente precisa reconhecer a custódia das carteiras hospedadas. |
| currency | fiat code · optional | Moeda da conta pré-paga; por padrão usa a moeda regional e não pode mudar depois. |
| fee_bps | integer · optional | 0–10000; 100 significa 1%. Usa o padrão do operador se omitido. Exige fees.write. |
| starting_credit | decimal string · default 0 | Concessão local exata de uma única vez. Valor diferente de zero exige credits.write. Não recarrega o saldo da instalação do operador. |
| external_id | string · optional | Referência única da integração, 1–120 caracteres. |
| default_timezone | IANA timezone · optional | Por padrão usa o fuso horário regional da instalação. |
| send_invitation_email | boolean · default false | Só convite. Exige SMTP configurado; a resposta distingue aceitação do relay de criação da conta. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Example shop",
"email": "admin@example.test",
"onboarding": "direct",
"password": "REPLACE_WITH_A_UNIQUE_TEMPORARY_PASSWORD",
"require_password_change": true,
"currency": "EUR",
"starting_credit": "0",
"external_id": "customer-1042"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Example shop",
"email": "admin@example.test",
"onboarding": "direct",
"password": "REPLACE_WITH_A_UNIQUE_TEMPORARY_PASSWORD",
"require_password_change": true,
"currency": "EUR",
"starting_credit": "0",
"external_id": "customer-1042"
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Example shop",
"email": "admin@example.test",
"onboarding": "direct",
"password": "REPLACE_WITH_A_UNIQUE_TEMPORARY_PASSWORD",
"require_password_change": true,
"currency": "EUR",
"starting_credit": "0",
"external_id": "customer-1042"
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Example shop",
"email": "admin@example.test",
"onboarding": "direct",
"password": "REPLACE_WITH_A_UNIQUE_TEMPORARY_PASSWORD",
"require_password_change": true,
"currency": "EUR",
"starting_credit": "0",
"external_id": "customer-1042"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 ou 200 application/json
{
"merchant_id": "11111111-1111-4111-8111-111111111111",
"user_id": "22222222-2222-4222-8222-222222222222",
"merchant": {
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example shop",
"currency": "EUR",
"balance": "0"
},
"onboarding": "direct",
"access_link": null,
"email_delivery": {
"status": "not_requested"
},
"custody_acceptance_required": true
}GETObter lojista/v1/operator/merchants/{merchant_id}Somente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige merchants.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example shop",
"currency": "EUR",
"balance": "10",
"fee_bps": 300,
"enabled": true,
"payments_paused": false,
"external_id": "customer-1042"
}POSTAtualizar lojista/v1/operator/merchants/{merchant_id}Leitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige merchants.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| name, enabled, payments_paused, fee_bps, external_id | optional fields | Desativar revoga sessões. payments_paused bloqueia novas faturas, não o rastreamento de pagamentos existentes. Mudar taxas exige fees.write e afeta faturas futuras; a moeda da conta não pode mudar. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"payments_paused": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"payments_paused": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"payments_paused": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"payments_paused": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example shop",
"currency": "EUR",
"balance": "10",
"fee_bps": 300,
"enabled": true,
"payments_paused": false
}GETListar usuários/v1/operator/merchants/{merchant_id}/usersSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige users.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCriar usuário/v1/operator/merchants/{merchant_id}/usersLeitura e gravação
Adicione um administrador de lojista ou um usuário restrito a projetos selecionados.
- Exige users.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| email, display_name | strings · required | O email é único na instalação. |
| onboarding, password, require_password_change, send_invitation_email | same as merchant creation | Criar convites também exige invitations.write. |
| access_level | admin | projects · default admin | admin é administrador só deste lojista, nunca da instalação ou do operador. |
| project_ids | UUID[] | Só projetos pertencentes ao lojista. Seleções obrigatórias para acesso restrito a projetos; nunca entre lojistas. |
| default_timezone | IANA timezone · optional | Padrão regional se omitido. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "staff@example.test",
"display_name": "Store team",
"onboarding": "invitation",
"access_level": "projects",
"project_ids": [
"11111111-1111-4111-8111-111111111111"
],
"send_invitation_email": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"email": "staff@example.test",
"display_name": "Store team",
"onboarding": "invitation",
"access_level": "projects",
"project_ids": [
"11111111-1111-4111-8111-111111111111"
],
"send_invitation_email": false
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"email": "staff@example.test",
"display_name": "Store team",
"onboarding": "invitation",
"access_level": "projects",
"project_ids": [
"11111111-1111-4111-8111-111111111111"
],
"send_invitation_email": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"email": "staff@example.test",
"display_name": "Store team",
"onboarding": "invitation",
"access_level": "projects",
"project_ids": [
"11111111-1111-4111-8111-111111111111"
],
"send_invitation_email": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 ou 200 application/json
{
"user": {
"id": "22222222-2222-4222-8222-222222222222",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"email": "admin@example.test",
"display_name": "Shop administrator",
"access_level": "admin",
"project_ids": [],
"enabled": true,
"password_setup_required": false,
"custody_acceptance_required": true,
"password_change_required": true
},
"user_id": "22222222-2222-4222-8222-222222222222",
"access_link": null,
"email_delivery": {
"status": "not_requested"
},
"merchant_id": "11111111-1111-4111-8111-111111111111"
}GETObter usuário/v1/operator/merchants/{merchant_id}/users/{user_id}Somente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige users.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| user_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "22222222-2222-4222-8222-222222222222",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"email": "admin@example.test",
"display_name": "Shop administrator",
"access_level": "admin",
"project_ids": [],
"enabled": true,
"password_setup_required": false,
"custody_acceptance_required": true,
"password_change_required": true
}POSTAtualizar usuário/v1/operator/merchants/{merchant_id}/users/{user_id}Leitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige users.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| user_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| email, display_name, enabled, access_level, project_ids, default_timezone | optional fields | Atualiza os campos enviados; a proteção do último administrador permanece. Senhas têm uma operação users.security separada. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"display_name": "Store manager"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"display_name": "Store manager"
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"display_name": "Store manager"
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"display_name": "Store manager"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "22222222-2222-4222-8222-222222222222",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"email": "admin@example.test",
"display_name": "Shop administrator",
"access_level": "admin",
"project_ids": [],
"enabled": true,
"password_setup_required": false,
"custody_acceptance_required": true,
"password_change_required": true
}POSTDefinir senha de usuário/v1/operator/merchants/{merchant_id}/users/{user_id}/passwordLeitura e gravação
Defina a senha de uma conta hospedada e revogue sessões. O TOTP existente é preservado.
- Exige users.security; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| user_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| password | string · required | Muda a senha e revoga sessões, preservando TOTP. Exige users.security. |
| require_password_change | boolean · default true | O usuário precisa definir sua própria senha no próximo acesso bem-sucedido. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/password" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"password": "REPLACE_WITH_A_NEW_UNIQUE_PASSWORD",
"require_password_change": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"password": "REPLACE_WITH_A_NEW_UNIQUE_PASSWORD",
"require_password_change": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/password", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"password": "REPLACE_WITH_A_NEW_UNIQUE_PASSWORD",
"require_password_change": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/password");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"password": "REPLACE_WITH_A_NEW_UNIQUE_PASSWORD",
"require_password_change": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/password",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"user_id": "11111111-1111-4111-8111-111111111111",
"sessions_revoked": true,
"totp_preserved": true
}POSTRevogar sessões de usuário/v1/operator/merchants/{merchant_id}/users/{user_id}/revoke-sessionsLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige users.security; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| user_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/revoke-sessions" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/revoke-sessions", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/revoke-sessions");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/revoke-sessions",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"user_id": "11111111-1111-4111-8111-111111111111",
"sessions_revoked": true,
"totp_preserved": true
}GETListar convites/v1/operator/merchants/{merchant_id}/invitationsSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige invitations.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCriar convite/v1/operator/merchants/{merchant_id}/invitationsLeitura e gravação
Crie ou substitua um link de convite ou redefinição de senha de uso único.
- Exige invitations.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| user_id, send_email | UUID, boolean | Emita ou substitua um link de uso único para uma conta existente. Usuários ativados recebem um link de redefinição de uma hora e exigem users.security. |
| new user fields | alternative to user_id | Use email, display_name, access_level e project_ids para criar um usuário convidado; exige users.write. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"user_id": "11111111-1111-4111-8111-111111111111",
"send_email": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"user_id": "11111111-1111-4111-8111-111111111111",
"send_email": false
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"user_id": "11111111-1111-4111-8111-111111111111",
"send_email": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"user_id": "11111111-1111-4111-8111-111111111111",
"send_email": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 ou 200 application/json
{
"user_id": "22222222-2222-4222-8222-222222222222",
"email": "admin@example.test",
"kind": "invitation",
"expires_at": "2026-10-03T12:00:00Z",
"url": "https://merchant.example.com/account-access.html#token=YOUR_PRIVATE_INVITATION_TOKEN"
}GETObter convite/v1/operator/invitations/{invitation_id}Somente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige invitations.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| invitation_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"user_id": "22222222-2222-4222-8222-222222222222",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"kind": "invitation",
"status": "pending",
"created_at": "2026-10-01T12:00:00Z",
"expires_at": "2026-10-03T12:00:00Z"
}POSTReenviar convite/v1/operator/invitations/{invitation_id}/resendLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige invitations.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| invitation_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| send_email | boolean · default false | Substitui o token anterior, nunca adiciona crédito. Retorna um link recém-gerado uma única vez. Uma conta já ativada precisa de users.security. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/resend" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"send_email": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"send_email": false
}`;
const response = await fetch("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/resend", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"send_email": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/resend");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"send_email": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/resend",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"user_id": "22222222-2222-4222-8222-222222222222",
"email": "admin@example.test",
"kind": "invitation",
"expires_at": "2026-10-03T12:00:00Z",
"url": "https://merchant.example.com/account-access.html#token=YOUR_PRIVATE_INVITATION_TOKEN"
}POSTRevogar convite/v1/operator/invitations/{invitation_id}/revokeLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige invitations.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| invitation_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/revoke" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/revoke", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/revoke");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/revoke",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"revoked": true,
"invitation_id": "44444444-4444-4444-8444-444444444444"
}GETObter créditos/v1/operator/merchants/{merchant_id}/creditsSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige credits.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example shop",
"currency": "EUR",
"balance": "10",
"fee_bps": 300,
"enabled": true,
"payments_paused": false,
"external_id": "customer-1042"
}GETListar livro-razão de créditos/v1/operator/merchants/{merchant_id}/credits/ledgerSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige credits.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page, q | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/ledger" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/ledger", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/ledger");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/ledger",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTAjustar créditos/v1/operator/merchants/{merchant_id}/credits/adjustmentsLeitura e gravação
Adicione uma concessão ou correção com motivo ao livro-razão pré-pago deste lojista.
- Exige credits.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| amount | signed decimal string · required | Concessão positiva ou correção negativa, até seis casas decimais na moeda de crédito do lojista. Não é uma transferência na blockchain. |
| note | string · required | Motivo preservado no livro-razão que só permite acréscimos. |
| request_id | UUID · required | Salve junto com o valor e o motivo, além do Idempotency-Key HTTP. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/adjustments" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"amount": "10.00",
"note": "Promotional credit",
"request_id": "11111111-1111-4111-8111-111111111111"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"amount": "10.00",
"note": "Promotional credit",
"request_id": "11111111-1111-4111-8111-111111111111"
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/adjustments", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"amount": "10.00",
"note": "Promotional credit",
"request_id": "11111111-1111-4111-8111-111111111111"
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/adjustments");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"amount": "10.00",
"note": "Promotional credit",
"request_id": "11111111-1111-4111-8111-111111111111"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/adjustments",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"balance": "25"
}GETListar recargas/v1/operator/merchants/{merchant_id}/topupsSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige topups.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCriar recarga/v1/operator/merchants/{merchant_id}/topupsLeitura e gravação
Crie um checkout para crédito pré-pago; nunca marque como pago manualmente.
- Exige topups.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| amount | decimal string · required | Pelo menos uma unidade da moeda de crédito do lojista. Exige uma loja de recebimento do operador pronta. |
| request_id | UUID · required | Preserve entre novas tentativas. Retorna a fatura existente se já foi criada. O crédito só é aplicado após observar a liquidação. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"amount": "25.00",
"request_id": "11111111-1111-4111-8111-111111111111"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"amount": "25.00",
"request_id": "11111111-1111-4111-8111-111111111111"
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"amount": "25.00",
"request_id": "11111111-1111-4111-8111-111111111111"
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"amount": "25.00",
"request_id": "11111111-1111-4111-8111-111111111111"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 ou 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"invoice_id": "33333333-3333-4333-8333-333333333333",
"checkout_url": "https://pay.example.com/invoice/33333333-3333-4333-8333-333333333333"
}GETObter recarga/v1/operator/merchants/{merchant_id}/topups/{topup_id}Somente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige topups.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| topup_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups/YOUR_TOPUP_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups/YOUR_TOPUP_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups/YOUR_TOPUP_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups/YOUR_TOPUP_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"invoice_id": "33333333-3333-4333-8333-333333333333",
"amount": "25",
"currency": "EUR",
"status": "pending",
"invoice_status": "new",
"checkout_url": "https://pay.example.com/invoice/33333333-3333-4333-8333-333333333333"
}GETRelatórios/v1/operator/reportsSomente leitura
Leia o resumo financeiro do operador. Exige acesso a todos os lojistas hospedados.
- Exige reports.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| period, start, end, currency, timezone, merchant_id | query · optional | Filtros financeiros. period usa last30 por padrão; use custom com start/end em YYYY-MM-DD. Só credenciais para todos os lojistas. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/reports" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/reports", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/reports");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/reports",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"summary": {
"fees": "10",
"costs": "3",
"margin": "7",
"credits": "25",
"pending": 0,
"missing_rates": 0
},
"merchants": [],
"filters": {
"period": "last30",
"currency": "EUR",
"timezone": "UTC"
},
"basis": "first_settlement_latest_net_fees"
}GETListar auditoria/v1/operator/auditSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige audit.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id, event_type / search | query · optional | Filtre por lojista permitido, tipo exato de evento (events) ou texto da ação (audit). Retenção de eventos: 30 dias. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/audit" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/audit", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/audit");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/audit",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}GETListar eventos/v1/operator/eventsSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige events.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id, event_type / search | query · optional | Filtre por lojista permitido, tipo exato de evento (events) ou texto da ação (audit). Retenção de eventos: 30 dias. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/events" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/events", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/events");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/events",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}GETListar webhooks/v1/operator/webhooksSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige events.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/webhooks" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/webhooks", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/webhooks");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/webhooks",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCriar webhook/v1/operator/webhooksLeitura e gravação
Assine eventos futuros do ciclo de vida do operador. Não é um webhook de pagamentos da loja.
- Exige webhooks.write + events.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| url | public HTTPS URL · required | Sem credenciais, IPs privados nem redirecionamentos. DNS/IP são verificados novamente na entrega. |
| events | string[] · required | Escolha eventos do ciclo de vida do guia do operador, não notificações de faturas. |
| merchant_ids | UUID[] · optional | Vazio significa todos os lojistas permitidos por esta credencial. Restrições de escopo vigentes são verificadas novamente. |
| enabled | boolean · default true | Endpoints pausados preservam entregas na fila; reativar retoma o trabalho preservado válido. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/webhooks" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"url": "https://shop.example.test/operator-events",
"events": [
"merchant.created",
"topup.settled"
],
"merchant_ids": [],
"enabled": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"url": "https://shop.example.test/operator-events",
"events": [
"merchant.created",
"topup.settled"
],
"merchant_ids": [],
"enabled": true
}`;
const response = await fetch("https://api.example.com/v1/operator/webhooks", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"url": "https://shop.example.test/operator-events",
"events": [
"merchant.created",
"topup.settled"
],
"merchant_ids": [],
"enabled": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/webhooks");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"url": "https://shop.example.test/operator-events",
"events": [
"merchant.created",
"topup.settled"
],
"merchant_ids": [],
"enabled": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/webhooks",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 ou 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"signing_secret": "wco_whsec_EXAMPLE_ONLY_SAVE_ONCE"
}POSTAtualizar webhook/v1/operator/webhooks/{webhook_id}Leitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige webhooks.write + events.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| webhook_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| url | public HTTPS URL · required | Sem credenciais, IPs privados nem redirecionamentos. DNS/IP são verificados novamente na entrega. |
| events | string[] · required | Escolha eventos do ciclo de vida do guia do operador, não notificações de faturas. |
| merchant_ids | UUID[] · optional | Vazio significa todos os lojistas permitidos por esta credencial. Restrições de escopo vigentes são verificadas novamente. |
| enabled | boolean · default true | Endpoints pausados preservam entregas na fila; reativar retoma o trabalho preservado válido. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"url": "https://shop.example.test/operator-events",
"events": [
"topup.settled"
],
"merchant_ids": [],
"enabled": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"url": "https://shop.example.test/operator-events",
"events": [
"topup.settled"
],
"merchant_ids": [],
"enabled": false
}`;
const response = await fetch("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"url": "https://shop.example.test/operator-events",
"events": [
"topup.settled"
],
"merchant_ids": [],
"enabled": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"url": "https://shop.example.test/operator-events",
"events": [
"topup.settled"
],
"merchant_ids": [],
"enabled": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"signing_secret": null
}POSTRotacionar segredo de webhook/v1/operator/webhooks/{webhook_id}/rotateLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige webhooks.write + events.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| webhook_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/rotate" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/rotate", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/rotate");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/rotate",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"signing_secret": "wco_whsec_EXAMPLE_ONLY_SAVE_ONCE"
}GETListar entregas de webhooks/v1/operator/webhooks/{webhook_id}/deliveriesSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige events.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| webhook_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/deliveries" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/deliveries", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/deliveries");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/deliveries",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}GETListar projetos/v1/operator/merchants/{merchant_id}/projectsSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCriar projeto/v1/operator/merchants/{merchant_id}/projectsLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| name, slug | strings · required | Nome e identificador de projeto único e estável. Cria carteiras locais com a inicialização existente do projeto; nunca transfere fundos. |
| enabled, reporting_timezone, checkout_title, checkout_description, checkout_theme, checkout_accent_color | optional | enabled usa true por padrão; recomenda-se criar pausado e configurar primeiro uma loja. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Online shop",
"slug": "online-shop",
"enabled": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Online shop",
"slug": "online-shop",
"enabled": false
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Online shop",
"slug": "online-shop",
"enabled": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Online shop",
"slug": "online-shop",
"enabled": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 ou 200 application/json
{
"id": "33333333-3333-4333-8333-333333333333",
"slug": "example-shop",
"name": "Example shop",
"enabled": false,
"reporting_currency": "EUR",
"reporting_timezone": "Europe/Berlin",
"stores": []
}GETObter projeto/v1/operator/merchants/{merchant_id}/projects/{project_id}Somente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "33333333-3333-4333-8333-333333333333",
"slug": "example-shop",
"name": "Example shop",
"enabled": false,
"reporting_currency": "EUR",
"reporting_timezone": "Europe/Berlin",
"stores": []
}POSTAtualizar projeto/v1/operator/merchants/{merchant_id}/projects/{project_id}Leitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| name, enabled, reporting_currency, reporting_timezone, checkout_title, checkout_description, checkout_theme, checkout_accent_color | optional | Atualização parcial. O identificador e o lojista proprietário não podem mudar. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"enabled": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"enabled": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"enabled": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"enabled": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "33333333-3333-4333-8333-333333333333",
"slug": "example-shop",
"name": "Example shop",
"enabled": false,
"reporting_currency": "EUR",
"reporting_timezone": "Europe/Berlin",
"stores": []
}GETListar lojas/v1/operator/merchants/{merchant_id}/projects/{project_id}/storesSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCriar loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/storesLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| name, slug | strings · required | Nome da loja e identificador estável. |
| default_currency, invoice_expiry_minutes, exchange_rate_spread_percent, underpayment_tolerance_percent | optional | Use strings decimais para percentuais. Lojas novas herdam a aparência da loja padrão do projeto. |
| enabled, is_default, allow_zero_amount_invoices, allow_underpayments, allow_overpayments, allowed_chain_slugs | optional | Configure os ativos aceitos com payment-assets; faturas de valor zero vêm desativadas por padrão. |
| ipn_enabled, default_ipn_url, default_redirect_url, default_cancel_url, redirect_automatically | optional | URLs de IPN e retorno seguem a validação de URL existente. Sem HTML/JavaScript arbitrário. |
| checkout_language, embed_enabled, allowed_embed_origins, domains | optional | Use um idioma compatível e domínios de função ativos; configure explicitamente as origens de incorporação. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Web checkout",
"slug": "web-checkout",
"default_currency": "EUR",
"enabled": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Web checkout",
"slug": "web-checkout",
"default_currency": "EUR",
"enabled": false
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Web checkout",
"slug": "web-checkout",
"default_currency": "EUR",
"enabled": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Web checkout",
"slug": "web-checkout",
"default_currency": "EUR",
"enabled": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 ou 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"project_id": "33333333-3333-4333-8333-333333333333",
"slug": "online",
"name": "Online shop",
"enabled": false,
"default_currency": "EUR",
"invoice_expiry_minutes": 60
}GETObter loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}Somente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"project_id": "33333333-3333-4333-8333-333333333333",
"slug": "online",
"name": "Online shop",
"enabled": false,
"default_currency": "EUR",
"invoice_expiry_minutes": 60
}POSTAtualizar loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}Leitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store fields | optional | As mesmas configurações modificáveis da criação da loja, exceto slug. Só mudam os campos enviados. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"enabled": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"enabled": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"enabled": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"enabled": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"project_id": "33333333-3333-4333-8333-333333333333",
"slug": "online",
"name": "Online shop",
"enabled": false,
"default_currency": "EUR",
"invoice_expiry_minutes": 60
}GETObter aparência da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/checkout-appearanceSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"revision": 1,
"settings": {
"inherit_default_store": true
},
"effective": {
"title": "Pay securely"
}
}POSTAtualizar aparência da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/checkout-appearanceLeitura e gravação
Salve um design de loja validado e protegido por revisão.
- Exige projects.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| revision | integer · required | Leia primeiro a revisão atual com GET. Uma revisão antiga falha sem sobrescrever outro editor. |
| settings | appearance object · required | Aparência do checkout validada, incluindo inherit_default_store, marca, intro/outro, tamanhos de fonte e visibilidade. Sem HTML/JavaScript arbitrário. Enviar bytes de imagens só está disponível no console. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"revision": 1,
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
}
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"revision": 1,
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
}
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"revision": 1,
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
}
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"revision": 1,
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
}
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
},
"revision": 2
}GETListar ativos de pagamento da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/payment-assetsSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": []
}POSTAtualizar ativos de pagamento da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/payment-assetsLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| assets | array · required | Substituição completa das formas na blockchain: UUID asset_id e display_order. [] limpa os ativos aceitos na blockchain. Só ativos verificados do projeto; não configura Lightning. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"assets": [
{
"asset_id": "11111111-1111-4111-8111-111111111111",
"display_order": 0
}
]
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"assets": [
{
"asset_id": "11111111-1111-4111-8111-111111111111",
"display_order": 0
}
]
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"assets": [
{
"asset_id": "11111111-1111-4111-8111-111111111111",
"display_order": 0
}
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"assets": [
{
"asset_id": "11111111-1111-4111-8111-111111111111",
"display_order": 0
}
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": []
}GETListar webhooks da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooksSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCriar webhook da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooksLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| name, url, event_types | strings / array · required | Receptor HTTPS público e nomes de eventos de fatura da documentação IPN e webhooks. |
| enabled, automatic_redelivery | booleans · default true | A criação retorna o segredo de assinatura uma única vez. São notificações de pagamento da loja, não eventos do ciclo de vida do operador. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": true,
"automatic_redelivery": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": true,
"automatic_redelivery": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": true,
"automatic_redelivery": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": true,
"automatic_redelivery": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 ou 200 application/json
{
"signing_secret": "EXAMPLE_ONLY_SAVE_ONCE",
"endpoint": {
"id": "44444444-4444-4444-8444-444444444444",
"name": "Orders",
"enabled": true
},
"secret_visible_once": true
}POSTAtualizar webhook da loja/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooks/{webhook_id}Leitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige projects.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| store_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| webhook_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| name, url, event_types | strings / array · required | Receptor HTTPS público e nomes de eventos de fatura da documentação IPN e webhooks. |
| enabled, automatic_redelivery | booleans · default true | A criação retorna o segredo de assinatura uma única vez. São notificações de pagamento da loja, não eventos do ciclo de vida do operador. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks/YOUR_WEBHOOK_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": false,
"automatic_redelivery": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": false,
"automatic_redelivery": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks/YOUR_WEBHOOK_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": false,
"automatic_redelivery": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks/YOUR_WEBHOOK_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": false,
"automatic_redelivery": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks/YOUR_WEBHOOK_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"name": "Orders",
"enabled": true
}GETListar faturas/v1/operator/merchants/{merchant_id}/projects/{project_id}/invoicesSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige reports.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| limit, offset, search, status, store_id | query · optional | Paginação e filtros de faturas, iguais aos da lista de faturas do projeto. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"pagination": {
"limit": 25,
"offset": 0,
"total": 0,
"has_more": false
}
}GETObter fatura/v1/operator/merchants/{merchant_id}/projects/{project_id}/invoices/{invoice_id}Somente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige reports.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
- invoice_id é o ID público da fatura retornado na criação e nas notificações, não o id interno.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| invoice_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices/YOUR_INVOICE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices/YOUR_INVOICE_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices/YOUR_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices/YOUR_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"invoice_id": "44444444-4444-4444-8444-444444444444",
"project_id": "33333333-3333-4333-8333-333333333333",
"amount": "25",
"currency": "EUR",
"status": "new",
"metadata": {},
"payment_intents": []
}GETListar carteiras/v1/operator/merchants/{merchant_id}/projects/{project_id}/walletsSomente leitura
Leia saldos públicos de carteiras em cache, nunca chaves privadas nem frases de recuperação.
- Exige reports.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Os saldos são observações em cache com campos de atualização, não uma garantia de saldo disponível para gastar. Envio e exportação de chaves não estão disponíveis por esta API.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}GETListar endereços de carteiras/v1/operator/merchants/{merchant_id}/projects/{project_id}/wallets/{wallet_id}/addressesSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige reports.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| project_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| wallet_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| limit, before, search, has_balance, hide_small_balances | query · optional | Limite 1–50, padrão 25. Passe next_cursor como before para a próxima página. Omita before para a página 1. has_balance=false e hide_small_balances=false incluem saldos vazios/pequenos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets/YOUR_WALLET_ID/addresses" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets/YOUR_WALLET_ID/addresses", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets/YOUR_WALLET_ID/addresses");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets/YOUR_WALLET_ID/addresses",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"wallet": {
"id": "22222222-2222-4222-8222-222222222222",
"chain_slug": "ethereum"
},
"items": [],
"total": 0,
"total_pages": 1,
"next_cursor": null,
"reporting_currency": "EUR",
"has_balance": true,
"hide_small_balances": true,
"small_balance_threshold": {
"amount": "0.20",
"currency": "EUR"
}
}GETListar credenciais de lojista/v1/operator/merchants/{merchant_id}/api-credentialsSomente leitura
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige merchant_credentials.read; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| page, search | query · optional | Páginas a partir de 1, 25 itens por página. A busca é aceita em lojistas, usuários, projetos, lojas, carteiras, credenciais e webhooks; listas nativas de eventos usam seus filtros específicos. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCriar credencial de lojista/v1/operator/merchants/{merchant_id}/api-credentialsLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige merchant_credentials.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| name | string · required | Rótulo de uma nova chave normal de lojista, não de operador. |
| access_level | read_only | read_write · default read_only | Leitura/gravação habilita o contrato existente da API do lojista. |
| project_ids | UUID[] | Só projetos pertencentes ao lojista selecionado; uma lista vazia segue a política existente de todos os projetos do lojista. |
| enabled, ip_restriction_enabled, allowed_ips, requests_per_minute | optional | Controles existentes de chaves de lojista. O segredo é retornado uma única vez; exige merchant_credentials.write. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Store integration",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"11111111-1111-4111-8111-111111111111"
]
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Store integration",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"11111111-1111-4111-8111-111111111111"
]
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Store integration",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"11111111-1111-4111-8111-111111111111"
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Store integration",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"11111111-1111-4111-8111-111111111111"
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 ou 200 application/json
{
"credential": {
"id": "22222222-2222-4222-8222-222222222222",
"name": "Checkout",
"access_level": "read_only",
"project_ids": [
"33333333-3333-4333-8333-333333333333"
],
"enabled": true
},
"token": "wc_live_EXAMPLE_ONLY_SAVE_ONCE"
}POSTAtualizar credencial de lojista/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}Leitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige merchant_credentials.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| credential_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| name, access_level, enabled, ip_restriction_enabled, allowed_ips | required fields | Envie a configuração atual completa da credencial com as mudanças. project_ids usa [] por padrão; requests_per_minute usa a cota da API do lojista. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Checkout",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"33333333-3333-4333-8333-333333333333"
]
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Checkout",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"33333333-3333-4333-8333-333333333333"
]
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Checkout",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"33333333-3333-4333-8333-333333333333"
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Checkout",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"33333333-3333-4333-8333-333333333333"
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"id": "22222222-2222-4222-8222-222222222222",
"name": "Checkout",
"access_level": "read_only",
"project_ids": [
"33333333-3333-4333-8333-333333333333"
],
"enabled": true
}POSTRotacionar credencial de lojista/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}/rotateLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige merchant_credentials.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| credential_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/rotate" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/rotate", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/rotate");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/rotate",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"credential": {
"id": "22222222-2222-4222-8222-222222222222",
"name": "Checkout",
"access_level": "read_only",
"project_ids": [
"33333333-3333-4333-8333-333333333333"
],
"enabled": true
},
"token": "wc_live_EXAMPLE_ONLY_SAVE_ONCE"
}POSTRevogar credencial de lojista/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}/revokeLeitura e gravação
Gerencie ou inspecione o recurso indicado do lojista hospedado usando uma credencial de operador separada.
- Exige merchant_credentials.write; só lojistas hospedados permitidos. Chaves de operador não acessam o espaço do negócio do proprietário.
- Salve um Idempotency-Key único e o corpo exato antes de enviar. Novas tentativas nunca repetem uma ação confirmada. Campos secretos são omitidos na repetição; se uma resposta com segredos foi perdida, inspecione o recurso criado e rotacione/reemita explicitamente. Um 409 operator_request_in_progress pode significar uma solicitação interrompida com resultado desconhecido: inspecione recurso/auditoria; não tente às cegas com uma chave nova.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obrigatório | 16–128 letras, dígitos, -, _ ou .; salva para esta operação |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| merchant_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
| credential_id | path UUID | UUID canônico do recurso em minúsculas; precisa pertencer ao escopo de lojistas da credencial. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/revoke" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/revoke", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/revoke");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/revoke",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"revoked": true
}POSTVerificar token de convite/v1/onboarding/invitations/checkPúblico
Cadastro só por token. Não aceita uma chave de operador nem entra automaticamente. O acesso ao console continua exigindo Basic Auth do site e o TOTP existente.
- Convite de 48 horas; link de redefinição de senha de uma hora. Tokens de uso único armazenados como hash. Reemitir revoga o link anterior. Aceitar preserva TOTP e revoga sessões antigas.
- Sem novas tentativas automáticas. Se o tempo se esgotar ao aceitar, confira o status do link e tente entrar; não presuma que falhou. Limitado pelo IP de origem observado. O destinatário precisa dar seu próprio reconhecimento de custódia.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| token | string · required | Segredo do fragmento da URL de convite. Nunca registre em logs. |
Solicitação
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/onboarding/invitations/check" \
--header 'Content-Type: application/json' \
--data-raw '{
"token": "YOUR_PRIVATE_INVITATION_TOKEN"
}'// Node.js 18+ · run on your server, never in browser code.
const body = `{
"token": "YOUR_PRIVATE_INVITATION_TOKEN"
}`;
const response = await fetch("https://api.example.com/v1/onboarding/invitations/check", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$body = <<<'JSON'
{
"token": "YOUR_PRIVATE_INVITATION_TOKEN"
}
JSON;
$ch = curl_init("https://api.example.com/v1/onboarding/invitations/check");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
body = """{
"token": "YOUR_PRIVATE_INVITATION_TOKEN"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/onboarding/invitations/check",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"kind": "invitation",
"email": "admin@example.test",
"merchant_name": "Example shop"
}POSTAceitar convite ou redefinição de senha/v1/onboarding/invitations/acceptPúblico
Cadastro só por token. Não aceita uma chave de operador nem entra automaticamente. O acesso ao console continua exigindo Basic Auth do site e o TOTP existente.
- Convite de 48 horas; link de redefinição de senha de uma hora. Tokens de uso único armazenados como hash. Reemitir revoga o link anterior. Aceitar preserva TOTP e revoga sessões antigas.
- Sem novas tentativas automáticas. Se o tempo se esgotar ao aceitar, confira o status do link e tente entrar; não presuma que falhou. Limitado pelo IP de origem observado. O destinatário precisa dar seu próprio reconhecimento de custódia.
- Exemplos de resposta mostram campos selecionados. Trate campos adicionais de resposta como adições compatíveis.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| token | string · required | Segredo do fragmento da URL de convite. Nunca registre em logs. |
| password | string · required | Senha nova, 12–128 caracteres (no máximo 512 bytes UTF-8). |
| custody_acknowledged | boolean | Precisa ser true ao aceitar um convite novo de carteira hospedada. |
Solicitação
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/onboarding/invitations/accept" \
--header 'Content-Type: application/json' \
--data-raw '{
"token": "YOUR_PRIVATE_INVITATION_TOKEN",
"password": "REPLACE_WITH_YOUR_OWN_UNIQUE_PASSWORD",
"custody_acknowledged": true
}'// Node.js 18+ · run on your server, never in browser code.
const body = `{
"token": "YOUR_PRIVATE_INVITATION_TOKEN",
"password": "REPLACE_WITH_YOUR_OWN_UNIQUE_PASSWORD",
"custody_acknowledged": true
}`;
const response = await fetch("https://api.example.com/v1/onboarding/invitations/accept", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$body = <<<'JSON'
{
"token": "YOUR_PRIVATE_INVITATION_TOKEN",
"password": "REPLACE_WITH_YOUR_OWN_UNIQUE_PASSWORD",
"custody_acknowledged": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/onboarding/invitations/accept");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
body = """{
"token": "YOUR_PRIVATE_INVITATION_TOKEN",
"password": "REPLACE_WITH_YOUR_OWN_UNIQUE_PASSWORD",
"custody_acknowledged": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/onboarding/invitations/accept",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"password_set": true
}GETListar exceções de pagamento/v1/projects/{project_id}/reconciliationSomente leitura
Uma fila de revisão paginada para pagamentos a menor, a maior, tardios, reorganizados ou ambíguos, entregas com falha e formas desativadas/vencidas. Casos reconhecidos por um operador são reabertos quando chegam novas evidências.
- Somente leitura, restrito ao projeto e coberto pela cota da credencial. Decisões financeiras e reembolsos continuam exclusivos do console.
- As linhas contêm id (UUID interno), invoice_id (UUID público, igual ao das notificações), informações da loja, valor/moeda fiduciária originais, invoice_status, status do caso, motivos, revisão e updated_at. Use invoice_id no endpoint de detalhe do lojista.
- A detecção automática segue a janela original de monitoramento da fatura; Rastrear novamente amplia a observação por uma hora sem habilitar o checkout. Formas liquidadas/canceladas continuam sendo monitoradas dentro dessa janela.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto atribuído a esta credencial. |
| status | query string | open (padrão), resolved ou all. |
| reason | query string | underpaid, overpaid, late, reorged, ambiguous, delivery_failed, disabled_method ou expired_method. |
| search | query string | Até 100 caracteres: ID da fatura, pedido, cliente ou loja. |
| store_id | query UUID | Filtro opcional de loja. |
| page | query integer | 1–40001. 25 casos fixos por página. |
Resposta da fila de exceções
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| data | ExceptionRow[] | sempre | Primeiro os casos atualizados mais recentemente. Use invoice_id, não o id interno, nas URLs de detalhe do lojista. |
| pagination | object | sempre | page (1–40001), per_page (25), total de linhas correspondentes, has_more. |
| counts | object | sempre | Totais open e resolved de todo o projeto, independentes dos filtros atuais. |
ExceptionRow
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id / invoice_id | UUID | sempre | ID interno do registro / UUID da fatura visível ao cliente. invoice_id corresponde aos dados das notificações. |
| store_id / store_name | UUID / string | sempre | Loja proprietária. |
| order_id / email | string | null | sempre | Referência privada do pedido do lojista e email do cliente. |
| amount / currency | decimal string / string | sempre | Valor e moeda fiduciária originais da fatura. |
| invoice_status | invoice status | sempre | Status atual do ciclo de vida do pagamento. |
| status / reasons | open|resolved / string[] | sempre | Status do caso e tipos de exceção listados no filtro reason. |
| revision / updated_at | integer / timestamp | sempre | Revisão atual e hora de atualização da revisão. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation?status=open&page=1" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation?status=open&page=1", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation?status=open&page=1");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation?status=open&page=1",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{"data":[],"pagination":{"page":1,"per_page":25,"total":0,"has_more":false},"counts":{"open":0,"resolved":0}}GETLer evidências de conciliação/v1/projects/{project_id}/reconciliation/{invoice_id}Somente leitura
Retorna fatura, caso, totais exatos por forma e valores disponíveis para reembolso, transações observadas, histórico de entregas, decisões do lojista e transferências de reembolso vinculadas. Nunca expõe chaves de assinatura nem segredos de notificação.
- case é null quando a fatura não gerou uma exceção. São retornadas as 100 observações e 50 entregas mais recentes; o histórico de decisões é paginado.
- refundable_atomic exige pelo menos uma confirmação de rede, exclui reservas de reembolso existentes e não promete fundos disponíveis para gastar na carteira. Uma cotação ao vivo também valida a disponibilidade da carteira, saldos de origem e taxas.
- Um reembolso transmitido significa enviado a um endpoint de rede, não recebimento do cliente confirmado independentemente. As taxas são adicionais e as de processamento fiduciário não são creditadas automaticamente ao reembolsar.
- Menu do projeto no console → Requer atenção oferece cancelar, aceitar, rejeitar, reabrir, revisar, notas, Rastrear novamente, nova tentativa de entrega e reembolsos em redes compatíveis. Decisões usam sessões protegidas por CSRF, um request_id único, a revisão atual do caso, nota obrigatória e confirmação explícita; tokens bearer não podem invocar essas alterações.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto atribuído. |
| invoice_id | path UUID | UUID público da fatura, não id interno. |
| page | query integer | Página de histórico de decisões, a partir de 1; 25 decisões por página. |
Resposta de conciliação
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| invoice | InvoiceDetail | sempre | Fatura completa do lojista: campos de resumo, metadados privados e payment_intents. Sem envoltório data. |
| case | object | null | sempre | Caso atual com status, motivos, revisão e carimbos de tempo; null sem exceção. Evidências internas são excluídas. |
| methods | object[] | sempre | id, wallet_id, asset_id, symbol, chain, decimals, expected_atomic, received_atomic, confirmed_atomic, refundable_atomic, address, tag, monitor_error, last_checked_at, monitoring_expires_at e spending_supported. Os valores atômicos são strings. |
| history | object[] | sempre | As 25 decisões mais recentes desta página: id, action, note, actor, result, created_at. |
| history_pagination | object | sempre | page, per_page (25), total. Só o histórico de decisões é paginado por page. |
| refunds | object[] | sempre | Os 100 reembolsos mais recentes: id, payment_intent_id, amount_atomic, destination, status, request, treasury_intent_id, transfer_status, created_at e transactions (id/status). Enviar reembolsos só está disponível no console. |
| observations | object[] | sempre | Os 100 mais recentes: payment_intent_id, transaction_id, event_index, amount, status, confirmations, observed_at, symbol, chain e disabled_at_detection. explorer_name/explorer_url são incluídos quando compatível. |
| deliveries | object[] | sempre | As 50 mais recentes: id, kind, status, attempts, response_status, error, next_attempt_at, event_type e created_at. Sem segredos de notificação. |
Resumo da fatura
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | UUID interno da fatura. Não use nas rotas de detalhe do lojista nem de checkout. |
| invoice_id | UUID | sempre | UUID público da fatura usado pelas rotas de detalhe do lojista e checkout. |
| project_id | UUID | sempre | Projeto proprietário. |
| store_id | UUID | sempre | Loja proprietária. |
| source | manual | api | sempre | Como a fatura foi criada. |
| order_id | string | null | sempre | Referência do pedido do lojista. |
| string | null | sempre | Email do cliente só para o lojista. Nunca retornado no checkout público. | |
| customer_name | string | null | sempre | Nome visível derivado dos metadados privados firstname, lastname e company. |
| customer_address | string | null | sempre | Endereço em uma linha para o lojista derivado dos metadados privados company, street, street2, zip, city, country, countryiso2 e vatid. |
| description | string | null | sempre | Descrição visível ao cliente. |
| amount | decimal string | sempre | Valor canônico da fatura. |
| currency | string | sempre | Código normalizado de moeda/ativo da fatura. |
| exchange_rate_spread_percent | decimal string | sempre | Margem da cotação fixada: o valor personalizado na criação ou o padrão da loja se omitido. Aplicada antes do arredondamento para cima; nunca muda nesta fatura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentual imutável de diferença a menor aceita, capturado na criação da fatura. |
| status | invoice status | sempre | new, processing, settled, expired, invalid ou cancelled. |
| amount_status | amount status | sempre | none, partial, paid ou overpaid. Uma fatura de valor zero permitida expressamente é liquidada com none e sem formas de pagamento. |
| timing_status | timing status | sempre | on_time ou late. |
| resolution | resolution | sempre | automatic, manually_settled ou manually_invalidated. |
| sequence | integer | sempre | Sequência monotônica do status da fatura, a partir de 1. |
| winning_payment_intent_id | UUID | null | sempre | Forma de pagamento que resolveu a fatura, quando selecionada. |
| expires_at | RFC 3339 timestamp | sempre | Prazo da cotação/pagamento. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Último limite configurado de monitoramento tardio entre as formas de pagamento. |
| settled_at | timestamp | null | sempre | Hora de liquidação quando liquidada. |
| cancelled_at | timestamp | null | sempre | Hora de cancelamento quando cancelada. |
| archived_at | timestamp | null | sempre | Hora de arquivamento quando arquivada. |
| created_at | RFC 3339 timestamp | sempre | Hora de criação. |
| updated_at | RFC 3339 timestamp | sempre | Hora da última atualização do status. |
Dados adicionais do detalhe da fatura
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| ipn_url | string | null | sempre | Destino IPN efetivo por fatura. Só na resposta ao lojista; omitido no checkout público. |
| redirect_url | string | null | sempre | URL efetiva de sucesso usada após liquidar. |
| cancel_url | string | null | sempre | URL efetiva de retorno quando o checkout termina sem pagamento bem-sucedido. |
| redirect_automatically | boolean | sempre | Se o checkout deve redirecionar automaticamente após o sucesso. |
| checkout_language | string | sempre | Tag efetiva do idioma do checkout. |
| metadata | object | sempre | Metadados do lojista. Nunca retornados no checkout público. |
| payment_intents | PaymentIntent[] | sempre | Formas de pagamento cotadas e status do monitoramento. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation/YOUR_PUBLIC_INVOICE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation/YOUR_PUBLIC_INVOICE_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation/YOUR_PUBLIC_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation/YOUR_PUBLIC_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{"invoice":{"invoice_id":"YOUR_PUBLIC_INVOICE_ID","status":"processing"},"case":{"status":"open","reasons":["underpaid"],"revision":1},"methods":[],"history":[],"history_pagination":{"page":1,"per_page":25,"total":0},"refunds":[],"observations":[],"deliveries":[]}GETDescoberta do serviço de API/Público
Resposta de entrada do host de API gerenciado que confirma a função de API pública v1. É produzida pelo proxy gerenciado, não pelo roteador Axum do lojista.
- Não precisa de token bearer.
- Só o host de API gerenciado garante esta resposta exata na raiz.
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/"// Node.js 18+ · run on your server, never in browser code.
const response = await fetch("https://api.example.com/", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://api.example.com/");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://api.example.com/",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"service": "Wholly Crypto API",
"status": "ready",
"version": "v1"
}GETSaúde do serviço/healthzPúblico
Verifica acesso ao aplicativo e um ping de dois segundos ao banco de dados. Use para monitoramento, não como substituto do status da fatura.
- Não precisa de token bearer.
- O valor da versão é a versão do pacote em execução, não a versão da rota da API.
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/healthz"// Node.js 18+ · run on your server, never in browser code.
const response = await fetch("https://api.example.com/healthz", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://api.example.com/healthz");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://api.example.com/healthz",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 saudável; 503 banco de dados indisponível
{
"status": "ok",
"database": "ok",
"version": "0.1.0"
}GETListar ativos de pagamento do projeto/v1/projects/{project_id}/payment-assetsSomente leitura
Lista ativos nativos e tokens verificados com política do projeto, disponibilidade de carteiras de rede e capacidades instaladas de rastreamento/saldos. scanner_ready é um requisito do adaptador compilado, não um resultado de quórum de endpoints ao vivo. Desde 6.0.6, a criação preserva as formas configuradas durante interrupções do scanner. A verificação de recebimento ainda precisa do limite configurado de provedores saudáveis com função exata (2 por padrão, 1 opcional).
- Um token pode estar listado globalmente e não ser selecionável se scanner_ready ou payment_supported for false.
- A matriz de capacidades do operador também exige a função exata de endpoint do scanner; um endpoint saudável que sirva uma API incompatível não conta.
- Tokens compartilham a carteira do projeto da rede nativa; não criam outra frase-semente.
- Os resumos de carteira incluídos só indicam disponibilidade e deixam saldos vazios; use GET /v1/projects/{project_id}/wallets para saldos enriquecidos.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto habilitado atribuído à credencial. |
PaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | Identificador persistente do ativo de pagamento usado pelas rotas de política de projeto e loja. |
| asset_key | string | sempre | Identidade canônica do ativo nativo ou de contrato no estilo CAIP. |
| chain_slug / network | string | sempre | Identificador de cadeia Wholly Crypto e rede configurada. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identidades canônicas da rede e do ativo. |
| asset_kind | native | token | sempre | Se a liquidação usa a moeda da rede ou um contrato/mint verificado. |
| payment_rail | string | sempre | Via de execução: utxo, evm-native, solana-native, account-native, privacy-native ou token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identidade visual e precisão exata de unidades atômicas. |
| contract_address | string | null | sempre | Contrato ERC-20 ou mint SPL canônico para tokens; null para ativos nativos. |
| coingecko_id | string | null | sempre | Identidade de descoberta/preços. Null para contratos personalizados; nunca deduza um preço de mercado pelo símbolo. Metadados CoinGecko sozinhos nunca tornam um token selecionável. |
| custom_token | boolean | sempre | Contrato personalizado verificado na blockchain com preço fixo USD ou pool DEX selecionado, restrito ao projeto. |
| icon_path | path | null | sempre | Ícone do token em cache local quando disponível. |
| token_standard | erc20 | spl-token | null | sempre | Padrão do token verificado em execução; null para ativos nativos. |
| metadata_verified_at | timestamp | null | sempre | Hora da verificação de metadados na blockchain para tokens registrados. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Condições do registro na compilação. scanner_ready significa que o scanner de pagamentos está instalado; confirmar pagamentos exige o número configurado de provedores saudáveis com função exata (2 por padrão, 1 opcional); a disponibilidade temporária do scanner não bloqueia criar faturas desde 6.0.6. balance_ready só é true para adaptadores de saldo implementados. |
| default_finality_mode | confirmations | finalized | sempre | Modelo padrão de finalidade herdado por uma nova política de projeto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Política padrão de confirmações e monitoramento. |
ProjectPaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| asset | PaymentAsset | sempre | Ativo nativo persistente ou token verificado. |
| policy | ProjectAssetPolicy | null | sempre | Política de ativação/finalidade do projeto, ou null sem configuração. Inclui custom_price_mode (fixed/dex), custom_price_usd (string decimal fixa ou null), custom_dex_pair (pool selecionado ou null) e custom_dex (dex_id, quote_symbol, price_usd atual ou null, liquidity_usd, fetched_at, last_error). Lojas deste projeto compartilham os preços personalizados. |
| wallet | WalletSummary | null | sempre | Carteira do projeto sem custódia da rede. Tokens compartilham a carteira nativa da rede. |
| wallet_readiness | readiness enum | sempre | unsupported, project_disabled, project_asset_disabled, store_disabled, store_asset_disabled, wallet_missing, wallet_pending, wallet_disabled, wallet_error, backup_required, account_activation_required, external_wallet_rpc_required ou ready. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Avaliação compartilhada da configuração de recebimento do projeto. Inclui verificações de carteira e provedores independentes de rastreamento, separadas da atualização dos saldos e do gás para envios. Null se não houver política do projeto. Moeda e cotações são verificadas ao criar uma fatura. |
WalletSummary
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id / project_id / native_asset_id | UUID | sempre | Identificadores de carteira, projeto proprietário e ativo nativo da rede. |
| chain_slug / network | string | sempre | Cadeia e rede da carteira. |
| asset_symbol / asset_name | string | sempre | Identidade visual nativa da rede. |
| status | pending | active | disabled | error | sempre | Estado operacional da carteira. |
| label | string | sempre | Rótulo do operador. |
| public_key / primary_address | string | null | sempre | Identidade pública da carteira; não expõe frase-semente nem chave privada. |
| derivation_scheme / address_format | string | null | sempre | Política e formato de endereços. |
| backup_confirmed_at | timestamp | null | sempre | Diferente de null após o operador confirmar o backup de recuperação. |
| activation_required / activation_verified_at | boolean / timestamp|null | sempre | Contas compartilhadas XRP e Stellar continuam indisponíveis até o operador enviar fundos ao endereço exibido e os provedores de rastreamento configurados verificarem essa conta exata. A prova persistente não expira; a saúde ao vivo do scanner é verificada separadamente para verificar pagamentos, não para criar faturas. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Incluído em listas de carteiras: configuração de recebimento do projeto e pré-requisitos do scanner da rede. Separado de saldos, gás de tokens e disponibilidade de envio. Outras respostas de carteira podem deixar null. |
| monero_wallet_rpc | MoneroWalletRpcBinding | null | sempre | Estado do vínculo externo wallet-RPC somente leitura do Monero, sem dados sensíveis. Inclui endpoint, modo de autenticação, endereço principal da conta 0, indicadores/alturas de prova técnica e datas de declaração do operador; credenciais, chaves e arquivos de carteira nunca são serializados. |
| last_secret_revealed_at / secret_reveal_count | timestamp|null / integer | sempre | Metadados de auditoria de revelação de segredos no console. |
| next_receive_index | integer | sempre | Próximo índice reservado de endereço derivado. |
| last_scanned_height / last_scanned_at / last_error | integer|null / timestamp|null / string|null | sempre | Status do scanner da carteira. |
| balances | WalletAssetBalance[] | sempre | Saldos em cache de cada uma das 30 vias nativas, além de ativos ERC-20 e SPL verificados. Monero exige uma wallet-RPC externa somente leitura configurada. |
| total_value_usd | decimal string | null | sempre | Soma indicativa de saldos com preço USD atual. |
| balance_status | pending | refreshing | fresh | stale | error | unknown | sempre | Atualização agregada do cache; unknown é uma alternativa defensiva e nenhum destes estados comprova a liquidação da fatura. |
| balance_checked_at | timestamp | null | sempre | Verificação de saldo bem-sucedida relevante mais antiga representada no agregado. |
| recent_payments | WalletRecentPayment[] | sempre | Até as três observações válidas detected, confirming ou final mais recentes atribuídas a esta carteira exata. |
| created_at / updated_at | RFC 3339 timestamp | sempre | Hora da criação e última atualização da carteira. |
ReceiveReadiness
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| ready | boolean | sempre | As verificações da configuração de recebimento passam. Não descreve disponibilidade de gasto, gás, atualização de saldos nem uma cotação futura garantida. |
| invoice_creatable | boolean | 6.0.6+ | A configuração permite uma forma de fatura apesar de avisos temporários do scanner. O preço da moeda é verificado na criação. Isto não verifica pagamentos: ready pode ser false enquanto invoice_creatable é true. Carteiras ausentes, políticas desativadas e adaptadores incompatíveis continuam falhando com segurança. |
| checked_at | timestamp | sempre | Hora da avaliação. Listar não faz solicitações de rede nem aloca endereços. |
| issues | PaymentMethodIssue[] | sempre | Vazio quando pronto; caso contrário, aviso de recebimento ou bloqueio de configuração. Confira invoice_creatable para distinguir avisos temporários do scanner de falhas de configuração da fatura. |
PaymentMethodIssue
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | quando conhecido | Identifica a rede e o ativo afetados. Lightning pode omitir asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled ou asset_not_accepted. |
| message / action | string | quando disponível | Explicação para o lojista e identificador de ação: chain_connections, wallets, rates, payment_methods, project_settings ou store_settings. Sem credenciais nem URLs privadas de provedores. |
| required_endpoint_role | string | null | na blockchain | Função API preferida do scanner (campo legado). Use accepted_endpoint_roles para a lista completa de compatibilidade. A saúde básica de um nó não comprova suporte ao histórico de pagamentos. |
| accepted_endpoint_roles | string[] | null | na blockchain | Dialetos de API compatíveis, não prova de histórico nem capacidade do endpoint. node-rpc direto aceita BTC/BCH/LTC/DOGE/DASH e ZEC transparente (blocos completos decodificados, 1–48 confirmações), TRX nativo solidificado, ALGO nativo via algod, XTZ via Octez, DOT finalizado do Asset Hub via metadados SCALE e XLM nativo via Stellar RPC com ID de memo da fatura. Histórico podado ou incompleto não serve. Esses adaptadores diretos não adicionam vias de tokens. APIs indexadas continuam alternativas; veja a tabela de vias abaixo. Fontes diretas/indexadas mistas verificam janelas limitadas independentemente; o padrão continua sendo dois provedores independentes, não aliases do mesmo operador. A altura básica do nó, informações de rede ORDnet e um relay EVM para uma via não EVM não são provas de recebimento. Monero ainda precisa de uma wallet-RPC somente leitura vinculada ao projeto. |
| healthy_endpoints | integer | na blockchain | Endpoints saudáveis correspondentes, não o número de provedores independentes. |
| usable_independent_providers / required_independent_providers | integer | na blockchain | Vagas de verificação utilizáveis, limitadas a duas. required_independent_providers é a configuração da rede: 2 por padrão ou 1 após escolha expressa do administrador. No modo de dois provedores, são exigidas chaves de provedor E hosts diferentes. Fontes desativadas, antigas (mais de dez minutos) ou em pausa não ocupam uma vaga. Lightning usa suas próprias regras de conexão. |
| last_checked_at | timestamp | null | na blockchain | Última verificação de saúde do endpoint correspondente, separada da hora da avaliação. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [
{
"asset": {
"id": "10000000-0000-4000-8000-000000000003",
"asset_key": "eip155:1/slip44:60",
"chain_slug": "ethereum",
"network": "mainnet",
"caip_network_id": "eip155:1",
"caip_asset_id": "eip155:1/slip44:60",
"asset_kind": "native",
"payment_rail": "evm-native",
"symbol": "ETH",
"name": "Ethereum",
"decimals": 18,
"contract_address": null,
"coingecko_id": "ethereum",
"icon_path": "/assets/coingecko/ethereum.png",
"token_standard": null,
"metadata_verified_at": null,
"payment_supported": true,
"scanner_ready": true,
"balance_ready": true,
"default_finality_mode": "confirmations",
"default_required_confirmations": 12,
"default_monitoring_minutes": 60
},
"policy": {
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 12,
"monitoring_minutes": 60,
"late_monitoring_days": 30
},
"wallet": null,
"wallet_readiness": "wallet_missing",
"receive_readiness": { "ready": false, "invoice_creatable": false, "checked_at": "2026-09-16T09:00:00Z", "issues": [{ "chain_slug": "ethereum", "asset_id": "10000000-0000-4000-8000-000000000003", "asset_ticker": "ETH", "reason_code": "wallet_missing", "message": "ethereum / ETH: Create a project wallet for this chain.", "action": "wallets" }] }
}
]
}PUTAtualizar política de ativos do projeto/v1/projects/{project_id}/payment-assets/{asset_id}Leitura e gravação
Cria ou substitui a política do projeto para um ativo persistente e retorna a lista atualizada de ativos do projeto. Desativar uma rede nativa deixa seu ativo e tokens indisponíveis para novas faturas, mas preserva políticas de tokens, carteiras e seleções da loja para retomar depois.
- O corpo substitui a política completa e rejeita campos desconhecidos.
- Ativar no projeto não seleciona por si só o ativo para nenhuma loja.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obrigatório | application/json |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto habilitado atribuído à credencial. |
| asset_id | path UUID | ID do ativo retornado pela lista de ativos do projeto ou pelo registro de tokens. |
Atualização da política de ativos do projeto
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| enabled | boolean | obrigatório | Ativa ou desativa o ativo para o projeto. A rede nativa precisa ser ativada antes de qualquer token. |
| finality_mode | confirmations | finalized | obrigatório | Política de finalidade compatível com a via do ativo. finalized exige required_confirmations=1. |
| required_confirmations | integer | obrigatório | Vias Bitcoin e EVM aceitam zero; outras vias de confirmação exigem pelo menos uma, vias só finalized exigem exatamente uma e vias EVM são limitadas a 0–48 para manter cada transferência dentro da janela de repetição de transações. |
| monitoring_minutes | integer | obrigatório | Janela de consulta de 1–10,080 minutos enquanto uma fatura está ativa. |
| late_monitoring_days | integer | obrigatório | 0–3,650 dias de monitoramento após vencer a fatura. |
PaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | Identificador persistente do ativo de pagamento usado pelas rotas de política de projeto e loja. |
| asset_key | string | sempre | Identidade canônica do ativo nativo ou de contrato no estilo CAIP. |
| chain_slug / network | string | sempre | Identificador de cadeia Wholly Crypto e rede configurada. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identidades canônicas da rede e do ativo. |
| asset_kind | native | token | sempre | Se a liquidação usa a moeda da rede ou um contrato/mint verificado. |
| payment_rail | string | sempre | Via de execução: utxo, evm-native, solana-native, account-native, privacy-native ou token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identidade visual e precisão exata de unidades atômicas. |
| contract_address | string | null | sempre | Contrato ERC-20 ou mint SPL canônico para tokens; null para ativos nativos. |
| coingecko_id | string | null | sempre | Identidade de descoberta/preços. Null para contratos personalizados; nunca deduza um preço de mercado pelo símbolo. Metadados CoinGecko sozinhos nunca tornam um token selecionável. |
| custom_token | boolean | sempre | Contrato personalizado verificado na blockchain com preço fixo USD ou pool DEX selecionado, restrito ao projeto. |
| icon_path | path | null | sempre | Ícone do token em cache local quando disponível. |
| token_standard | erc20 | spl-token | null | sempre | Padrão do token verificado em execução; null para ativos nativos. |
| metadata_verified_at | timestamp | null | sempre | Hora da verificação de metadados na blockchain para tokens registrados. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Condições do registro na compilação. scanner_ready significa que o scanner de pagamentos está instalado; confirmar pagamentos exige o número configurado de provedores saudáveis com função exata (2 por padrão, 1 opcional); a disponibilidade temporária do scanner não bloqueia criar faturas desde 6.0.6. balance_ready só é true para adaptadores de saldo implementados. |
| default_finality_mode | confirmations | finalized | sempre | Modelo padrão de finalidade herdado por uma nova política de projeto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Política padrão de confirmações e monitoramento. |
ProjectPaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| asset | PaymentAsset | sempre | Ativo nativo persistente ou token verificado. |
| policy | ProjectAssetPolicy | null | sempre | Política de ativação/finalidade do projeto, ou null sem configuração. Inclui custom_price_mode (fixed/dex), custom_price_usd (string decimal fixa ou null), custom_dex_pair (pool selecionado ou null) e custom_dex (dex_id, quote_symbol, price_usd atual ou null, liquidity_usd, fetched_at, last_error). Lojas deste projeto compartilham os preços personalizados. |
| wallet | WalletSummary | null | sempre | Carteira do projeto sem custódia da rede. Tokens compartilham a carteira nativa da rede. |
| wallet_readiness | readiness enum | sempre | unsupported, project_disabled, project_asset_disabled, store_disabled, store_asset_disabled, wallet_missing, wallet_pending, wallet_disabled, wallet_error, backup_required, account_activation_required, external_wallet_rpc_required ou ready. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Avaliação compartilhada da configuração de recebimento do projeto. Inclui verificações de carteira e provedores independentes de rastreamento, separadas da atualização dos saldos e do gás para envios. Null se não houver política do projeto. Moeda e cotações são verificadas ao criar uma fatura. |
ReceiveReadiness
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| ready | boolean | sempre | As verificações da configuração de recebimento passam. Não descreve disponibilidade de gasto, gás, atualização de saldos nem uma cotação futura garantida. |
| invoice_creatable | boolean | 6.0.6+ | A configuração permite uma forma de fatura apesar de avisos temporários do scanner. O preço da moeda é verificado na criação. Isto não verifica pagamentos: ready pode ser false enquanto invoice_creatable é true. Carteiras ausentes, políticas desativadas e adaptadores incompatíveis continuam falhando com segurança. |
| checked_at | timestamp | sempre | Hora da avaliação. Listar não faz solicitações de rede nem aloca endereços. |
| issues | PaymentMethodIssue[] | sempre | Vazio quando pronto; caso contrário, aviso de recebimento ou bloqueio de configuração. Confira invoice_creatable para distinguir avisos temporários do scanner de falhas de configuração da fatura. |
PaymentMethodIssue
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | quando conhecido | Identifica a rede e o ativo afetados. Lightning pode omitir asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled ou asset_not_accepted. |
| message / action | string | quando disponível | Explicação para o lojista e identificador de ação: chain_connections, wallets, rates, payment_methods, project_settings ou store_settings. Sem credenciais nem URLs privadas de provedores. |
| required_endpoint_role | string | null | na blockchain | Função API preferida do scanner (campo legado). Use accepted_endpoint_roles para a lista completa de compatibilidade. A saúde básica de um nó não comprova suporte ao histórico de pagamentos. |
| accepted_endpoint_roles | string[] | null | na blockchain | Dialetos de API compatíveis, não prova de histórico nem capacidade do endpoint. node-rpc direto aceita BTC/BCH/LTC/DOGE/DASH e ZEC transparente (blocos completos decodificados, 1–48 confirmações), TRX nativo solidificado, ALGO nativo via algod, XTZ via Octez, DOT finalizado do Asset Hub via metadados SCALE e XLM nativo via Stellar RPC com ID de memo da fatura. Histórico podado ou incompleto não serve. Esses adaptadores diretos não adicionam vias de tokens. APIs indexadas continuam alternativas; veja a tabela de vias abaixo. Fontes diretas/indexadas mistas verificam janelas limitadas independentemente; o padrão continua sendo dois provedores independentes, não aliases do mesmo operador. A altura básica do nó, informações de rede ORDnet e um relay EVM para uma via não EVM não são provas de recebimento. Monero ainda precisa de uma wallet-RPC somente leitura vinculada ao projeto. |
| healthy_endpoints | integer | na blockchain | Endpoints saudáveis correspondentes, não o número de provedores independentes. |
| usable_independent_providers / required_independent_providers | integer | na blockchain | Vagas de verificação utilizáveis, limitadas a duas. required_independent_providers é a configuração da rede: 2 por padrão ou 1 após escolha expressa do administrador. No modo de dois provedores, são exigidas chaves de provedor E hosts diferentes. Fontes desativadas, antigas (mais de dez minutos) ou em pausa não ocupam uma vaga. Lightning usa suas próprias regras de conexão. |
| last_checked_at | timestamp | null | na blockchain | Última verificação de saúde do endpoint correspondente, separada da hora da avaliação. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request PUT \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets/YOUR_ASSET_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 2,
"monitoring_minutes": 60,
"late_monitoring_days": 30
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 2,
"monitoring_minutes": 60,
"late_monitoring_days": 30
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets/YOUR_ASSET_ID", {
method: "PUT",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 2,
"monitoring_minutes": 60,
"late_monitoring_days": 30
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets/YOUR_ASSET_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 2,
"monitoring_minutes": 60,
"late_monitoring_days": 30
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets/YOUR_ASSET_ID",
method="PUT", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [
{ "asset": { "id": "ASSET_UUID", "symbol": "USDC", "asset_kind": "token", "scanner_ready": true }, "policy": { "enabled": true, "finality_mode": "confirmations", "required_confirmations": 2, "monitoring_minutes": 60, "late_monitoring_days": 30 }, "wallet_readiness": "ready" }
]
}GETExplorar tokens candidatos para pagamentos/v1/projects/{project_id}/payment-token-candidatesSomente leitura
Busca correspondências de contratos CoinGecko em cache local só em redes com scanner de faturas de tokens e adaptador de saldos implementados. Os resultados são candidatos de descoberta, não ativos de pagamento confiáveis.
- Adaptadores de tokens compatíveis: ERC-20 em Ethereum, Base, BNB Chain, HyperEVM, Avalanche, Polygon, Arbitrum e Optimism; SPL em Solana.
- Redes do catálogo incompatíveis são rejeitadas em vez de aparecerem selecionáveis.
- A classificação, o ícone e o preço CoinGecko são dados indicativos de descoberta.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto habilitado atribuído à credencial. |
| chain_slug | query string | Slug obrigatório de rede EVM compatível ou solana. |
| q | query string | Trecho opcional de nome, símbolo, id CoinGecko, contrato ou mint; no máximo 80 caracteres. |
| limit | query integer | Opcional 1–100; padrão 50. |
TokenCandidate
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| coingecko_id | string | sempre | Identidade de descoberta CoinGecko usada pela solicitação de registro. |
| chain_slug | string | sempre | Rede Wholly Crypto correspondente. |
| symbol / name | string | sempre | Identidade visual do catálogo. |
| contract_address | string | sempre | Contrato ou mint correspondente; é verificado na blockchain antes do registro. |
| market_cap_rank | integer | null | sempre | Classificação de descoberta, não sinal de confiança nem disponibilidade para pagamentos. |
| icon_path | path | sempre | Caminho do ícone CoinGecko em cache local. |
| current_price_usd | decimal string | null | sempre | Preço USD indicativo em cache. |
| token_standard | erc20 | spl-token | sempre | Padrão de token compatível com o adaptador da rede selecionada. |
| scanner_ready | boolean | sempre | True só para candidatos em uma via de tokens implementada nesta compilação. |
| registered_asset_id | UUID | null | sempre | Ativo persistente existente se já foi registrado. |
| project_enabled | boolean | sempre | Se o ativo registrado está habilitado para este projeto. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-candidates?chain_slug=ethereum&q=USDC&limit=50" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-candidates?chain_slug=ethereum&q=USDC&limit=50", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-candidates?chain_slug=ethereum&q=USDC&limit=50");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-candidates?chain_slug=ethereum&q=USDC&limit=50",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [
{
"coingecko_id": "usd-coin",
"chain_slug": "ethereum",
"symbol": "USDC",
"name": "USDC",
"contract_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"market_cap_rank": 7,
"icon_path": "/assets/coingecko/usd-coin.png",
"current_price_usd": "1.0001",
"token_standard": "erc20",
"scanner_ready": true,
"registered_asset_id": null,
"project_enabled": false
}
]
}POSTVerificar e registrar token/v1/projects/{project_id}/payment-token-assetsLeitura e gravação
Promove um candidato atual ao registro persistente de pagamentos só após os nós configurados verificarem a identidade da rede, identidade do contrato/mint, decimais e uma consulta de saldo utilizável. O registro nunca confia só em metadados CoinGecko e cada projeto é limitado a 20 ativos de token registrados.
- Ative o ativo nativo da rede do projeto antes de registrar seus tokens.
- Um projeto pode registrar no máximo 20 ativos de token; um candidato novo acima disso retorna token_chain_not_ready (409). Reutilizar um ativo já registrado não consome outra vaga.
- A verificação dos nós pode demorar mais que ler o catálogo; use um tempo limite explícito no cliente.
- Após registrar, selecione o ativo em cada loja que deve oferecê-lo.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obrigatório | application/json |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto habilitado atribuído à credencial. |
Corpo do registro de token
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug | string | obrigatório | ethereum, base, bnb-chain, hyperliquid, avalanche, polygon, arbitrum, optimism ou solana. |
| coingecko_id | string | obrigatório | Identidade exata do candidato retornada pela busca de tokens. Preserve sublinhados ou hífens iniciais, como _ ou -6. Não derive este ID do nome nem do símbolo do token. |
| enabled | boolean | opcional | Status da política do projeto após verificar; padrão true. |
RegisteredTokenAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| asset_id | UUID | sempre | Identificador persistente do ativo de pagamento. |
| chain_slug / coingecko_id | string | sempre | Rede verificada e identidade de descoberta/preços preservada. |
| contract_address | string | sempre | Contrato ou mint canônico verificado. |
| token_standard | erc20 | spl-token | sempre | Padrão do token verificado em execução. |
| symbol / name / decimals | string / string / integer | sempre | Identidade visual registrada e precisão exata. |
| enabled | boolean | sempre | Status inicial da política do projeto. |
| metadata_verified_at | RFC 3339 timestamp | sempre | Hora da verificação na blockchain. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"enabled": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"enabled": true
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"enabled": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"enabled": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 application/json
{
"data": {
"asset_id": "44444444-4444-4444-8444-444444444444",
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"contract_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"token_standard": "erc20",
"symbol": "USDC",
"name": "USDC",
"decimals": 6,
"enabled": true,
"metadata_verified_at": "2026-08-31T18:00:00Z"
}
}GETBuscar pools DEX de tokens personalizados/v1/projects/{project_id}/payment-token-dex-poolsSomente leitura
Encontre até 12 pools aptos por rede e contrato exato do token base via DEX Screener, ordenados por liquidez. Isto não registra nem ativa um token.
- Um array data vazio significa que nenhum pool apto foi encontrado. Só são retornados pools em que o contrato exato solicitado é o token base; preços USD do lado cotado nunca são presumidos.
- Aparecer em DEX não é uma auditoria de segurança. Liquidez mínima e atividade recente reduzem cotações inutilizáveis, mas não impedem manipulação de mercado.
- Uniswap, PancakeSwap e outros DEXs indexados são compatíveis onde o scanner de rede existente aceita tokens. O acesso à API continua restrito ao projeto e limitado por cota. Chamadas ao provedor também são serializadas e limitadas.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto atribuído. |
| chain_slug | query string | Rede de tokens EVM compatível ou solana. |
| contract_address | query string | Contrato ERC-20 ou mint SPL clássico exato. |
CustomDexPool
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| pair_address / dex_id / quote_symbol | string | sempre | Identificador exato do pool, ID da exchange (por exemplo, uniswap/pancakeswap) e símbolo pareado só visual. |
| price_usd / liquidity_usd | decimal string | sempre | Preço USD do token base solicitado e liquidez total do pool. Exige pelo menos $10,000 de liquidez e uma negociação na última hora. |
| fetched_at | RFC 3339 timestamp | sempre | Quando o servidor obteve a observação do provedor, não a data de uma negociação na blockchain. |
| url | HTTPS URL | sempre | Link validado do DEX Screener para este pool. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-dex-pools?chain_slug=ethereum&contract_address=YOUR_TOKEN_CONTRACT" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-dex-pools?chain_slug=ethereum&contract_address=YOUR_TOKEN_CONTRACT", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-dex-pools?chain_slug=ethereum&contract_address=YOUR_TOKEN_CONTRACT");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-dex-pools?chain_slug=ethereum&contract_address=YOUR_TOKEN_CONTRACT",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{"data":[{"pair_address":"0x2222222222222222222222222222222222222222","dex_id":"uniswap","quote_symbol":"WETH","price_usd":"0.25","liquidity_usd":"250000.00","fetched_at":"2026-09-09T12:00:00Z","url":"https://dexscreener.com/ethereum/0x2222222222222222222222222222222222222222"}]}POSTAdicionar ou alterar preço de token personalizado/v1/projects/{project_id}/payment-token-assets/customLeitura e gravação
Verifica um contrato personalizado com os nós de rede configurados e registra sem exigir uma entrada CoinGecko. O preço fixo USD ou pool DEX automático selecionado pertence a este projeto, não ao símbolo nem a outros projetos. Repetir a mesma identidade atualiza o preço do projeto sem mudar uma política existente de ativação/desativação.
- Após registrar, selecione asset_id no endpoint payment-assets da loja; só registrar nunca ativa uma forma da loja.
- Tokens personalizados e do catálogo compartilham o limite de 20 por projeto. O mesmo contrato em redes diferentes é um ativo de pagamento diferente.
- Contratos existentes do catálogo retornam 409: use o registro do catálogo para preservar cotações automáticas de mercado. Um símbolo personalizado nunca toma o preço de um token homônimo.
- Preços fixos são estimativas do operador. Preços automáticos DEX são observações à vista do pool escolhido via DEX Screener, não um oráculo resistente à manipulação. A margem da loja e o arredondamento para cima continuam se aplicando, com cotações fiduciárias recentes. Cotações já emitidas não mudam.
- Para modo DEX, descubra primeiro um pool e envie price_mode: dex e dex_pair_address, omitindo price_usd. Uma tarefa compartilhada em segundo plano atualiza os pools escolhidos a cada minuto. Falhas nas verificações ou preços com mais de cinco minutos excluem este token de novas cotações; não há alternativa silenciosa de preço fixo nem por símbolo.
- Só são aceitos tokens ERC-20 padrão e SPL clássicos. Token-2022/extensões e redes somente nativas são rejeitados. A verificação técnica não é uma auditoria de segurança do emissor/contrato; tokens com taxa por transferência, reajuste de saldo ou listas de bloqueio podem se comportar de forma incompatível.
- Use um tempo limite do cliente de pelo menos 60 segundos. A verificação é limitada e pode tentar nós alternativos. Dados inválidos retornam 400; falhas de verificação de rede/contrato, 422; conflitos de identidade ou limites, 409.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obrigatório | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto atribuído a esta credencial com gravação. |
Registro de token personalizado
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug | string | obrigatório | ethereum, base, bnb-chain, hyperliquid, avalanche, polygon, arbitrum, optimism ou solana. Fixo para este contrato. |
| contract_address | string | obrigatório | Contrato ERC-20 (0x e 40 caracteres hexadecimais) ou mint SPL clássico. Os nós verificam a identidade da rede e os decimais exatos; decimais e URLs RPC fornecidos pelo cliente são rejeitados. |
| name / symbol | string / string | obrigatório | Nome visível (1–80 caracteres) e símbolo (1–16 letras/dígitos/pontos/sublinhados/hífens, primeiro caractere alfanumérico). Este endpoint não pode renomear identidades existentes. |
| price_mode | fixed | dex | opcional | Por padrão fixed por compatibilidade. DEX usa um pool específico descoberto para a rede e o contrato exatos. |
| price_usd | decimal string | modo fixed | Valor USD fixo de UM token, positivo, no máximo 30 casas decimais, máximo 1000000000000000000000000. Sem expoente nem floats. Omita no modo dex. |
| dex_pair_address | string | modo dex | Endereço do pool de payment-token-dex-pools. Obrigatório no modo dex; omita no modo fixed. O servidor verifica novamente identidade, preço, liquidez e atividade do pool a cada salvamento. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets/custom" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"chain_slug": "ethereum",
"contract_address": "YOUR_VERIFIED_TOKEN_CONTRACT",
"name": "Example token",
"symbol": "EXAMPLE",
"price_usd": "0.25"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"chain_slug": "ethereum",
"contract_address": "YOUR_VERIFIED_TOKEN_CONTRACT",
"name": "Example token",
"symbol": "EXAMPLE",
"price_usd": "0.25"
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets/custom", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"chain_slug": "ethereum",
"contract_address": "YOUR_VERIFIED_TOKEN_CONTRACT",
"name": "Example token",
"symbol": "EXAMPLE",
"price_usd": "0.25"
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets/custom");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"chain_slug": "ethereum",
"contract_address": "YOUR_VERIFIED_TOKEN_CONTRACT",
"name": "Example token",
"symbol": "EXAMPLE",
"price_usd": "0.25"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets/custom",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{"data":{"asset_id":"44444444-4444-4444-8444-444444444444"}}GETListar formas de pagamento da loja/v1/projects/{project_id}/stores/{store_id}/payment-assetsSomente leitura
Lista ativos na blockchain em data e disponibilidade Lightning separada em lightning. Formas na blockchain exigem carteiras de rede prontas. Lightning usa a conexão externa de recebimento verificada escolhida na loja, independentemente da carteira Bitcoin na blockchain.
- selected é a configuração na blockchain; wallet_readiness é sua condição de elegibilidade atual.
- O membro lightning da resposta contém payment_rail: lightning, symbol: BTC, asset_decimals: 11, enabled e ready. Nunca contém credenciais de nó. Configure esta forma no console da loja; atualizar o array assets não muda Lightning.
- confirmation_policy só se aplica a formas na blockchain. Lightning liquida sem confirmações de blocos e exige o valor BOLT11 completo, sem tolerância de pagamento parcial.
- Formas nativas e de tokens de uma rede usam o mesmo destino de fatura para a carteira dessa rede.
- Os resumos de carteira incluídos só indicam disponibilidade e deixam saldos vazios; use a rota específica de carteiras do projeto para valores atuais.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto atribuído à credencial; pode estar pausado. |
| store_id | path UUID | Loja pertencente a project_id; pode estar pausada. |
PaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | Identificador persistente do ativo de pagamento usado pelas rotas de política de projeto e loja. |
| asset_key | string | sempre | Identidade canônica do ativo nativo ou de contrato no estilo CAIP. |
| chain_slug / network | string | sempre | Identificador de cadeia Wholly Crypto e rede configurada. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identidades canônicas da rede e do ativo. |
| asset_kind | native | token | sempre | Se a liquidação usa a moeda da rede ou um contrato/mint verificado. |
| payment_rail | string | sempre | Via de execução: utxo, evm-native, solana-native, account-native, privacy-native ou token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identidade visual e precisão exata de unidades atômicas. |
| contract_address | string | null | sempre | Contrato ERC-20 ou mint SPL canônico para tokens; null para ativos nativos. |
| coingecko_id | string | null | sempre | Identidade de descoberta/preços. Null para contratos personalizados; nunca deduza um preço de mercado pelo símbolo. Metadados CoinGecko sozinhos nunca tornam um token selecionável. |
| custom_token | boolean | sempre | Contrato personalizado verificado na blockchain com preço fixo USD ou pool DEX selecionado, restrito ao projeto. |
| icon_path | path | null | sempre | Ícone do token em cache local quando disponível. |
| token_standard | erc20 | spl-token | null | sempre | Padrão do token verificado em execução; null para ativos nativos. |
| metadata_verified_at | timestamp | null | sempre | Hora da verificação de metadados na blockchain para tokens registrados. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Condições do registro na compilação. scanner_ready significa que o scanner de pagamentos está instalado; confirmar pagamentos exige o número configurado de provedores saudáveis com função exata (2 por padrão, 1 opcional); a disponibilidade temporária do scanner não bloqueia criar faturas desde 6.0.6. balance_ready só é true para adaptadores de saldo implementados. |
| default_finality_mode | confirmations | finalized | sempre | Modelo padrão de finalidade herdado por uma nova política de projeto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Política padrão de confirmações e monitoramento. |
StorePaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| asset | PaymentAsset | sempre | Ativo nativo ou token verificado visível ao projeto. |
| project_policy | ProjectAssetPolicy | null | sempre | Política do projeto principal. |
| selected | boolean | sempre | Se esta forma faz parte da configuração desejada salva da loja. É oferecida quando a política do projeto, carteira, adaptador instalado e preços são válidos. Interrupções temporárias do scanner não a removem das faturas novas. |
| display_order | integer | null | sempre | Ordem no checkout da loja quando selecionada. |
| confirmation_policy | StoreConfirmationPolicy | null | sempre | Política efetiva da loja para um ativo configurado no projeto. Null se não houver política do projeto. |
| wallet | WalletSummary | null | sempre | Carteira da rede compartilhada por ativos nativos e tokens. |
| wallet_readiness | readiness enum | sempre | Só status de carteira/política; use receive_readiness para os requisitos do scanner. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Configuração compartilhada de recebimento mais aceitação da loja. Usa observações em cache; não é reserva nem garantia. A criação verifica novamente os requisitos e a cotação real da fatura. |
StoreConfirmationPolicy
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| finality_mode | confirmations | finalized | sempre | Se a liquidação usa um número configurável de blocos ou finalidade da rede. |
| project_required_confirmations | integer | sempre | Padrão atual do projeto usado por faturas futuras sem substituição da loja. |
| override_required_confirmations | integer | null | sempre | Número específico da loja, ou null para herdar o padrão do projeto. |
| effective_required_confirmations | integer | sempre | Número que faturas novas desta loja e ativo guardarão. |
| editable | boolean | sempre | False para redes finalized cuja política de finalidade não pode ser substituída. |
| minimum_required_confirmations | integer | sempre | Limite inferior inclusivo conforme a rede; 0 só é exposto em vias que aceitam na detecção. |
| maximum_required_confirmations | integer | sempre | Limite superior inclusivo conforme a rede. |
WalletSummary
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id / project_id / native_asset_id | UUID | sempre | Identificadores de carteira, projeto proprietário e ativo nativo da rede. |
| chain_slug / network | string | sempre | Cadeia e rede da carteira. |
| asset_symbol / asset_name | string | sempre | Identidade visual nativa da rede. |
| status | pending | active | disabled | error | sempre | Estado operacional da carteira. |
| label | string | sempre | Rótulo do operador. |
| public_key / primary_address | string | null | sempre | Identidade pública da carteira; não expõe frase-semente nem chave privada. |
| derivation_scheme / address_format | string | null | sempre | Política e formato de endereços. |
| backup_confirmed_at | timestamp | null | sempre | Diferente de null após o operador confirmar o backup de recuperação. |
| activation_required / activation_verified_at | boolean / timestamp|null | sempre | Contas compartilhadas XRP e Stellar continuam indisponíveis até o operador enviar fundos ao endereço exibido e os provedores de rastreamento configurados verificarem essa conta exata. A prova persistente não expira; a saúde ao vivo do scanner é verificada separadamente para verificar pagamentos, não para criar faturas. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Incluído em listas de carteiras: configuração de recebimento do projeto e pré-requisitos do scanner da rede. Separado de saldos, gás de tokens e disponibilidade de envio. Outras respostas de carteira podem deixar null. |
| monero_wallet_rpc | MoneroWalletRpcBinding | null | sempre | Estado do vínculo externo wallet-RPC somente leitura do Monero, sem dados sensíveis. Inclui endpoint, modo de autenticação, endereço principal da conta 0, indicadores/alturas de prova técnica e datas de declaração do operador; credenciais, chaves e arquivos de carteira nunca são serializados. |
| last_secret_revealed_at / secret_reveal_count | timestamp|null / integer | sempre | Metadados de auditoria de revelação de segredos no console. |
| next_receive_index | integer | sempre | Próximo índice reservado de endereço derivado. |
| last_scanned_height / last_scanned_at / last_error | integer|null / timestamp|null / string|null | sempre | Status do scanner da carteira. |
| balances | WalletAssetBalance[] | sempre | Saldos em cache de cada uma das 30 vias nativas, além de ativos ERC-20 e SPL verificados. Monero exige uma wallet-RPC externa somente leitura configurada. |
| total_value_usd | decimal string | null | sempre | Soma indicativa de saldos com preço USD atual. |
| balance_status | pending | refreshing | fresh | stale | error | unknown | sempre | Atualização agregada do cache; unknown é uma alternativa defensiva e nenhum destes estados comprova a liquidação da fatura. |
| balance_checked_at | timestamp | null | sempre | Verificação de saldo bem-sucedida relevante mais antiga representada no agregado. |
| recent_payments | WalletRecentPayment[] | sempre | Até as três observações válidas detected, confirming ou final mais recentes atribuídas a esta carteira exata. |
| created_at / updated_at | RFC 3339 timestamp | sempre | Hora da criação e última atualização da carteira. |
ReceiveReadiness
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| ready | boolean | sempre | As verificações da configuração de recebimento passam. Não descreve disponibilidade de gasto, gás, atualização de saldos nem uma cotação futura garantida. |
| invoice_creatable | boolean | 6.0.6+ | A configuração permite uma forma de fatura apesar de avisos temporários do scanner. O preço da moeda é verificado na criação. Isto não verifica pagamentos: ready pode ser false enquanto invoice_creatable é true. Carteiras ausentes, políticas desativadas e adaptadores incompatíveis continuam falhando com segurança. |
| checked_at | timestamp | sempre | Hora da avaliação. Listar não faz solicitações de rede nem aloca endereços. |
| issues | PaymentMethodIssue[] | sempre | Vazio quando pronto; caso contrário, aviso de recebimento ou bloqueio de configuração. Confira invoice_creatable para distinguir avisos temporários do scanner de falhas de configuração da fatura. |
PaymentMethodIssue
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | quando conhecido | Identifica a rede e o ativo afetados. Lightning pode omitir asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled ou asset_not_accepted. |
| message / action | string | quando disponível | Explicação para o lojista e identificador de ação: chain_connections, wallets, rates, payment_methods, project_settings ou store_settings. Sem credenciais nem URLs privadas de provedores. |
| required_endpoint_role | string | null | na blockchain | Função API preferida do scanner (campo legado). Use accepted_endpoint_roles para a lista completa de compatibilidade. A saúde básica de um nó não comprova suporte ao histórico de pagamentos. |
| accepted_endpoint_roles | string[] | null | na blockchain | Dialetos de API compatíveis, não prova de histórico nem capacidade do endpoint. node-rpc direto aceita BTC/BCH/LTC/DOGE/DASH e ZEC transparente (blocos completos decodificados, 1–48 confirmações), TRX nativo solidificado, ALGO nativo via algod, XTZ via Octez, DOT finalizado do Asset Hub via metadados SCALE e XLM nativo via Stellar RPC com ID de memo da fatura. Histórico podado ou incompleto não serve. Esses adaptadores diretos não adicionam vias de tokens. APIs indexadas continuam alternativas; veja a tabela de vias abaixo. Fontes diretas/indexadas mistas verificam janelas limitadas independentemente; o padrão continua sendo dois provedores independentes, não aliases do mesmo operador. A altura básica do nó, informações de rede ORDnet e um relay EVM para uma via não EVM não são provas de recebimento. Monero ainda precisa de uma wallet-RPC somente leitura vinculada ao projeto. |
| healthy_endpoints | integer | na blockchain | Endpoints saudáveis correspondentes, não o número de provedores independentes. |
| usable_independent_providers / required_independent_providers | integer | na blockchain | Vagas de verificação utilizáveis, limitadas a duas. required_independent_providers é a configuração da rede: 2 por padrão ou 1 após escolha expressa do administrador. No modo de dois provedores, são exigidas chaves de provedor E hosts diferentes. Fontes desativadas, antigas (mais de dez minutos) ou em pausa não ocupam uma vaga. Lightning usa suas próprias regras de conexão. |
| last_checked_at | timestamp | null | na blockchain | Última verificação de saúde do endpoint correspondente, separada da hora da avaliação. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [
{ "asset": { "id": "ASSET_UUID", "chain_slug": "ethereum", "symbol": "USDC", "asset_kind": "token", "token_standard": "erc20", "scanner_ready": true }, "project_policy": { "enabled": true, "required_confirmations": 12 }, "selected": true, "display_order": 0, "confirmation_policy": { "finality_mode": "confirmations", "project_required_confirmations": 12, "override_required_confirmations": 3, "effective_required_confirmations": 3, "editable": true, "minimum_required_confirmations": 0, "maximum_required_confirmations": 48 }, "wallet": { "id": "WALLET_UUID", "status": "active" }, "wallet_readiness": "ready" }
],
"lightning": { "payment_rail": "lightning", "symbol": "BTC", "asset_decimals": 11, "enabled": true, "ready": true }
}PUTSubstituir formas de pagamento da loja/v1/projects/{project_id}/stores/{store_id}/payment-assetsLeitura e gravação
Substitui atomicamente todo o subconjunto ordenado de ativos da loja e retorna a lista atualizada. Ativos omitidos ficam desmarcados.
- O array aceita no máximo 64 ativos e ordens visuais únicos.
- As seleções são configuração desejada salva e podem ser preparadas antes do backup da carteira ou enquanto uma rede está pausada. Criar faturas continua oferecendo só formas com política do projeto, política nativa principal, carteira e verificações de execução prontas.
- Envie um array assets vazio para configurar nenhuma forma de pagamento.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obrigatório | application/json |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto atribuído à credencial; pode estar pausado. |
| store_id | path UUID | Loja pertencente a project_id; pode estar pausada. |
Corpo de seleção de ativos de pagamento da loja
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| assets | StoreAssetSelection[] | obrigatório | Lista de substituição completa, no máximo 64 entradas. Cada entrada contém um asset_id único e display_order único entre 0 e 10,000. |
PaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | Identificador persistente do ativo de pagamento usado pelas rotas de política de projeto e loja. |
| asset_key | string | sempre | Identidade canônica do ativo nativo ou de contrato no estilo CAIP. |
| chain_slug / network | string | sempre | Identificador de cadeia Wholly Crypto e rede configurada. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identidades canônicas da rede e do ativo. |
| asset_kind | native | token | sempre | Se a liquidação usa a moeda da rede ou um contrato/mint verificado. |
| payment_rail | string | sempre | Via de execução: utxo, evm-native, solana-native, account-native, privacy-native ou token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identidade visual e precisão exata de unidades atômicas. |
| contract_address | string | null | sempre | Contrato ERC-20 ou mint SPL canônico para tokens; null para ativos nativos. |
| coingecko_id | string | null | sempre | Identidade de descoberta/preços. Null para contratos personalizados; nunca deduza um preço de mercado pelo símbolo. Metadados CoinGecko sozinhos nunca tornam um token selecionável. |
| custom_token | boolean | sempre | Contrato personalizado verificado na blockchain com preço fixo USD ou pool DEX selecionado, restrito ao projeto. |
| icon_path | path | null | sempre | Ícone do token em cache local quando disponível. |
| token_standard | erc20 | spl-token | null | sempre | Padrão do token verificado em execução; null para ativos nativos. |
| metadata_verified_at | timestamp | null | sempre | Hora da verificação de metadados na blockchain para tokens registrados. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Condições do registro na compilação. scanner_ready significa que o scanner de pagamentos está instalado; confirmar pagamentos exige o número configurado de provedores saudáveis com função exata (2 por padrão, 1 opcional); a disponibilidade temporária do scanner não bloqueia criar faturas desde 6.0.6. balance_ready só é true para adaptadores de saldo implementados. |
| default_finality_mode | confirmations | finalized | sempre | Modelo padrão de finalidade herdado por uma nova política de projeto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Política padrão de confirmações e monitoramento. |
StorePaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| asset | PaymentAsset | sempre | Ativo nativo ou token verificado visível ao projeto. |
| project_policy | ProjectAssetPolicy | null | sempre | Política do projeto principal. |
| selected | boolean | sempre | Se esta forma faz parte da configuração desejada salva da loja. É oferecida quando a política do projeto, carteira, adaptador instalado e preços são válidos. Interrupções temporárias do scanner não a removem das faturas novas. |
| display_order | integer | null | sempre | Ordem no checkout da loja quando selecionada. |
| confirmation_policy | StoreConfirmationPolicy | null | sempre | Política efetiva da loja para um ativo configurado no projeto. Null se não houver política do projeto. |
| wallet | WalletSummary | null | sempre | Carteira da rede compartilhada por ativos nativos e tokens. |
| wallet_readiness | readiness enum | sempre | Só status de carteira/política; use receive_readiness para os requisitos do scanner. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Configuração compartilhada de recebimento mais aceitação da loja. Usa observações em cache; não é reserva nem garantia. A criação verifica novamente os requisitos e a cotação real da fatura. |
StoreConfirmationPolicy
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| finality_mode | confirmations | finalized | sempre | Se a liquidação usa um número configurável de blocos ou finalidade da rede. |
| project_required_confirmations | integer | sempre | Padrão atual do projeto usado por faturas futuras sem substituição da loja. |
| override_required_confirmations | integer | null | sempre | Número específico da loja, ou null para herdar o padrão do projeto. |
| effective_required_confirmations | integer | sempre | Número que faturas novas desta loja e ativo guardarão. |
| editable | boolean | sempre | False para redes finalized cuja política de finalidade não pode ser substituída. |
| minimum_required_confirmations | integer | sempre | Limite inferior inclusivo conforme a rede; 0 só é exposto em vias que aceitam na detecção. |
| maximum_required_confirmations | integer | sempre | Limite superior inclusivo conforme a rede. |
ReceiveReadiness
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| ready | boolean | sempre | As verificações da configuração de recebimento passam. Não descreve disponibilidade de gasto, gás, atualização de saldos nem uma cotação futura garantida. |
| invoice_creatable | boolean | 6.0.6+ | A configuração permite uma forma de fatura apesar de avisos temporários do scanner. O preço da moeda é verificado na criação. Isto não verifica pagamentos: ready pode ser false enquanto invoice_creatable é true. Carteiras ausentes, políticas desativadas e adaptadores incompatíveis continuam falhando com segurança. |
| checked_at | timestamp | sempre | Hora da avaliação. Listar não faz solicitações de rede nem aloca endereços. |
| issues | PaymentMethodIssue[] | sempre | Vazio quando pronto; caso contrário, aviso de recebimento ou bloqueio de configuração. Confira invoice_creatable para distinguir avisos temporários do scanner de falhas de configuração da fatura. |
PaymentMethodIssue
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | quando conhecido | Identifica a rede e o ativo afetados. Lightning pode omitir asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled ou asset_not_accepted. |
| message / action | string | quando disponível | Explicação para o lojista e identificador de ação: chain_connections, wallets, rates, payment_methods, project_settings ou store_settings. Sem credenciais nem URLs privadas de provedores. |
| required_endpoint_role | string | null | na blockchain | Função API preferida do scanner (campo legado). Use accepted_endpoint_roles para a lista completa de compatibilidade. A saúde básica de um nó não comprova suporte ao histórico de pagamentos. |
| accepted_endpoint_roles | string[] | null | na blockchain | Dialetos de API compatíveis, não prova de histórico nem capacidade do endpoint. node-rpc direto aceita BTC/BCH/LTC/DOGE/DASH e ZEC transparente (blocos completos decodificados, 1–48 confirmações), TRX nativo solidificado, ALGO nativo via algod, XTZ via Octez, DOT finalizado do Asset Hub via metadados SCALE e XLM nativo via Stellar RPC com ID de memo da fatura. Histórico podado ou incompleto não serve. Esses adaptadores diretos não adicionam vias de tokens. APIs indexadas continuam alternativas; veja a tabela de vias abaixo. Fontes diretas/indexadas mistas verificam janelas limitadas independentemente; o padrão continua sendo dois provedores independentes, não aliases do mesmo operador. A altura básica do nó, informações de rede ORDnet e um relay EVM para uma via não EVM não são provas de recebimento. Monero ainda precisa de uma wallet-RPC somente leitura vinculada ao projeto. |
| healthy_endpoints | integer | na blockchain | Endpoints saudáveis correspondentes, não o número de provedores independentes. |
| usable_independent_providers / required_independent_providers | integer | na blockchain | Vagas de verificação utilizáveis, limitadas a duas. required_independent_providers é a configuração da rede: 2 por padrão ou 1 após escolha expressa do administrador. No modo de dois provedores, são exigidas chaves de provedor E hosts diferentes. Fontes desativadas, antigas (mais de dez minutos) ou em pausa não ocupam uma vaga. Lightning usa suas próprias regras de conexão. |
| last_checked_at | timestamp | null | na blockchain | Última verificação de saúde do endpoint correspondente, separada da hora da avaliação. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request PUT \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"assets": [
{
"asset_id": "YOUR_ASSET_ID",
"display_order": 0
}
]
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"assets": [
{
"asset_id": "YOUR_ASSET_ID",
"display_order": 0
}
]
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets", {
method: "PUT",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"assets": [
{
"asset_id": "YOUR_ASSET_ID",
"display_order": 0
}
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"assets": [
{
"asset_id": "YOUR_ASSET_ID",
"display_order": 0
}
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets",
method="PUT", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [
{ "asset": { "id": "44444444-4444-4444-8444-444444444444", "symbol": "USDC" }, "selected": true, "display_order": 0, "confirmation_policy": { "finality_mode": "confirmations", "project_required_confirmations": 12, "override_required_confirmations": null, "effective_required_confirmations": 12, "editable": true, "minimum_required_confirmations": 0, "maximum_required_confirmations": 48 }, "wallet_readiness": "ready" }
]
}PUTDefinir política de confirmações de uma loja/v1/projects/{project_id}/stores/{store_id}/payment-assets/{asset_id}/confirmation-policyLeitura e gravação
Define ou remove uma substituição de confirmações específica da loja e retorna a lista atualizada de formas de pagamento. O ativo já precisa estar selecionado para a loja. A configuração continua disponível enquanto projeto, loja, rede ou carteira estiverem pausados.
- Use {"strategy":"inherit"} para remover a substituição da loja e seguir o padrão atual do projeto em faturas futuras.
- Redes finalized retornam editable false e usam Finalidade da rede; não aceitam um número de blocos personalizado.
- Um valor de 0 significa aceitar na detecção, sem confirmação de rede nem proteção contra reorganizações. Só é aceito onde minimum_required_confirmations é 0.
- Mudanças de política só afetam faturas novas. As existentes preservam o retrato da política de confirmações de projeto/loja capturado na criação.
- Atualiza-se um ativo por vez; serialize mudanças simultâneas do mesmo ativo da loja e use a resposta atualizada como status atual.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obrigatório | application/json |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto atribuído à credencial; pode estar pausado. |
| store_id | path UUID | Loja pertencente a project_id; pode estar pausada. |
| asset_id | path UUID | Ativo de pagamento atualmente selecionado na loja a atualizar. |
Corpo da política de confirmações da loja
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| strategy | inherit | custom | obrigatório | Estratégia identificada. inherit remove a substituição da loja; custom exige required_confirmations. |
| required_confirmations | integer | somente custom | Inteiro dentro do mínimo/máximo retornado para este ativo. Campos desconhecidos ou extras são rejeitados. |
PaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | Identificador persistente do ativo de pagamento usado pelas rotas de política de projeto e loja. |
| asset_key | string | sempre | Identidade canônica do ativo nativo ou de contrato no estilo CAIP. |
| chain_slug / network | string | sempre | Identificador de cadeia Wholly Crypto e rede configurada. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identidades canônicas da rede e do ativo. |
| asset_kind | native | token | sempre | Se a liquidação usa a moeda da rede ou um contrato/mint verificado. |
| payment_rail | string | sempre | Via de execução: utxo, evm-native, solana-native, account-native, privacy-native ou token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identidade visual e precisão exata de unidades atômicas. |
| contract_address | string | null | sempre | Contrato ERC-20 ou mint SPL canônico para tokens; null para ativos nativos. |
| coingecko_id | string | null | sempre | Identidade de descoberta/preços. Null para contratos personalizados; nunca deduza um preço de mercado pelo símbolo. Metadados CoinGecko sozinhos nunca tornam um token selecionável. |
| custom_token | boolean | sempre | Contrato personalizado verificado na blockchain com preço fixo USD ou pool DEX selecionado, restrito ao projeto. |
| icon_path | path | null | sempre | Ícone do token em cache local quando disponível. |
| token_standard | erc20 | spl-token | null | sempre | Padrão do token verificado em execução; null para ativos nativos. |
| metadata_verified_at | timestamp | null | sempre | Hora da verificação de metadados na blockchain para tokens registrados. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Condições do registro na compilação. scanner_ready significa que o scanner de pagamentos está instalado; confirmar pagamentos exige o número configurado de provedores saudáveis com função exata (2 por padrão, 1 opcional); a disponibilidade temporária do scanner não bloqueia criar faturas desde 6.0.6. balance_ready só é true para adaptadores de saldo implementados. |
| default_finality_mode | confirmations | finalized | sempre | Modelo padrão de finalidade herdado por uma nova política de projeto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Política padrão de confirmações e monitoramento. |
StorePaymentAsset
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| asset | PaymentAsset | sempre | Ativo nativo ou token verificado visível ao projeto. |
| project_policy | ProjectAssetPolicy | null | sempre | Política do projeto principal. |
| selected | boolean | sempre | Se esta forma faz parte da configuração desejada salva da loja. É oferecida quando a política do projeto, carteira, adaptador instalado e preços são válidos. Interrupções temporárias do scanner não a removem das faturas novas. |
| display_order | integer | null | sempre | Ordem no checkout da loja quando selecionada. |
| confirmation_policy | StoreConfirmationPolicy | null | sempre | Política efetiva da loja para um ativo configurado no projeto. Null se não houver política do projeto. |
| wallet | WalletSummary | null | sempre | Carteira da rede compartilhada por ativos nativos e tokens. |
| wallet_readiness | readiness enum | sempre | Só status de carteira/política; use receive_readiness para os requisitos do scanner. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Configuração compartilhada de recebimento mais aceitação da loja. Usa observações em cache; não é reserva nem garantia. A criação verifica novamente os requisitos e a cotação real da fatura. |
StoreConfirmationPolicy
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| finality_mode | confirmations | finalized | sempre | Se a liquidação usa um número configurável de blocos ou finalidade da rede. |
| project_required_confirmations | integer | sempre | Padrão atual do projeto usado por faturas futuras sem substituição da loja. |
| override_required_confirmations | integer | null | sempre | Número específico da loja, ou null para herdar o padrão do projeto. |
| effective_required_confirmations | integer | sempre | Número que faturas novas desta loja e ativo guardarão. |
| editable | boolean | sempre | False para redes finalized cuja política de finalidade não pode ser substituída. |
| minimum_required_confirmations | integer | sempre | Limite inferior inclusivo conforme a rede; 0 só é exposto em vias que aceitam na detecção. |
| maximum_required_confirmations | integer | sempre | Limite superior inclusivo conforme a rede. |
ReceiveReadiness
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| ready | boolean | sempre | As verificações da configuração de recebimento passam. Não descreve disponibilidade de gasto, gás, atualização de saldos nem uma cotação futura garantida. |
| invoice_creatable | boolean | 6.0.6+ | A configuração permite uma forma de fatura apesar de avisos temporários do scanner. O preço da moeda é verificado na criação. Isto não verifica pagamentos: ready pode ser false enquanto invoice_creatable é true. Carteiras ausentes, políticas desativadas e adaptadores incompatíveis continuam falhando com segurança. |
| checked_at | timestamp | sempre | Hora da avaliação. Listar não faz solicitações de rede nem aloca endereços. |
| issues | PaymentMethodIssue[] | sempre | Vazio quando pronto; caso contrário, aviso de recebimento ou bloqueio de configuração. Confira invoice_creatable para distinguir avisos temporários do scanner de falhas de configuração da fatura. |
PaymentMethodIssue
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | quando conhecido | Identifica a rede e o ativo afetados. Lightning pode omitir asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled ou asset_not_accepted. |
| message / action | string | quando disponível | Explicação para o lojista e identificador de ação: chain_connections, wallets, rates, payment_methods, project_settings ou store_settings. Sem credenciais nem URLs privadas de provedores. |
| required_endpoint_role | string | null | na blockchain | Função API preferida do scanner (campo legado). Use accepted_endpoint_roles para a lista completa de compatibilidade. A saúde básica de um nó não comprova suporte ao histórico de pagamentos. |
| accepted_endpoint_roles | string[] | null | na blockchain | Dialetos de API compatíveis, não prova de histórico nem capacidade do endpoint. node-rpc direto aceita BTC/BCH/LTC/DOGE/DASH e ZEC transparente (blocos completos decodificados, 1–48 confirmações), TRX nativo solidificado, ALGO nativo via algod, XTZ via Octez, DOT finalizado do Asset Hub via metadados SCALE e XLM nativo via Stellar RPC com ID de memo da fatura. Histórico podado ou incompleto não serve. Esses adaptadores diretos não adicionam vias de tokens. APIs indexadas continuam alternativas; veja a tabela de vias abaixo. Fontes diretas/indexadas mistas verificam janelas limitadas independentemente; o padrão continua sendo dois provedores independentes, não aliases do mesmo operador. A altura básica do nó, informações de rede ORDnet e um relay EVM para uma via não EVM não são provas de recebimento. Monero ainda precisa de uma wallet-RPC somente leitura vinculada ao projeto. |
| healthy_endpoints | integer | na blockchain | Endpoints saudáveis correspondentes, não o número de provedores independentes. |
| usable_independent_providers / required_independent_providers | integer | na blockchain | Vagas de verificação utilizáveis, limitadas a duas. required_independent_providers é a configuração da rede: 2 por padrão ou 1 após escolha expressa do administrador. No modo de dois provedores, são exigidas chaves de provedor E hosts diferentes. Fontes desativadas, antigas (mais de dez minutos) ou em pausa não ocupam uma vaga. Lightning usa suas próprias regras de conexão. |
| last_checked_at | timestamp | null | na blockchain | Última verificação de saúde do endpoint correspondente, separada da hora da avaliação. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request PUT \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets/YOUR_ASSET_ID/confirmation-policy" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"strategy": "custom",
"required_confirmations": 0
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"strategy": "custom",
"required_confirmations": 0
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets/YOUR_ASSET_ID/confirmation-policy", {
method: "PUT",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"strategy": "custom",
"required_confirmations": 0
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets/YOUR_ASSET_ID/confirmation-policy");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"strategy": "custom",
"required_confirmations": 0
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets/YOUR_ASSET_ID/confirmation-policy",
method="PUT", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [
{
"asset": { "id": "YOUR_ASSET_ID", "chain_slug": "bitcoin", "symbol": "BTC" },
"selected": true,
"display_order": 0,
"confirmation_policy": {
"finality_mode": "confirmations",
"project_required_confirmations": 2,
"override_required_confirmations": 0,
"effective_required_confirmations": 0,
"editable": true,
"minimum_required_confirmations": 0,
"maximum_required_confirmations": 10000
},
"wallet_readiness": "ready"
}
]
}GETListar carteiras e saldos do projeto/v1/projects/{project_id}/walletsSomente leitura
Retorna metadados públicos da carteira e cada ativo registrado com leitura de saldo na cadeia e rede exatas da carteira. Cobre as 30 vias nativas; também acompanha ativos ERC-20 e SPL verificados. Ativos aparecem imediatamente, mesmo antes da primeira varredura ou se não forem aceitos para pagamentos. project_enabled informa aceitação de pagamentos; tracking_active informa separadamente a elegibilidade de atualização somente leitura. Monero exige sua wallet-RPC externa somente leitura vinculada ao projeto. Rastrear saldos no console prioriza leituras limitadas com progresso/erros por ativo; só ciclos completos atualizam totais recentes. A liquidação de faturas continua guiada por monitoramento de transações e política de confirmações, não por esses saldos em cache.
- Esta rota bearer nunca retorna frase de recuperação, chave privada, segredo criptografado nem método de gasto.
- Um ativo novo registrado da mesma rede é retornado com saldos null e status pending antes de completar a primeira varredura; nunca se informa um zero inventado.
- Desativar projeto, carteira para aceitação de pagamentos, via nativa ou ativo individual não interrompe o monitoramento de saldos somente leitura: carteiras ativas e desativadas com endereço principal continuam atualizando cada ativo registrado compatível da mesma rede. Carteiras pendentes ou com erro não são rastreadas.
- project_enabled só informa a política de aceitação de ativos do projeto e pode ser false enquanto tracking_active continua true.
- balance e balance_atomic são strings exatas; price_usd, value_usd e total_value_usd são indicativos e podem ser null. Um status de saldo recente não garante um preço de mercado recente.
- A avaliação prioriza preços CoinGecko de até duas horas. Moedas nativas e USDC/USDT canônicos verificados podem recorrer a cotações USD habilitadas de Kraken/Binance de até cinco minutos, primeiro o provedor principal. Não se presume paridade com o dólar nem se precificam tokens personalizados só pelo símbolo; preços fixos/DEX do projeto ficam separados. Cotações de faturas não mudam.
- Pendente não tem retrato completo. Atualizando preserva o último valor completo e checked_at; não significa que uma transferência na blockchain esteja pendente. Valores antigos/com erro também podem preservar valores anteriores. Nunca trate um cache indisponível como zero nem como pagamento ausente. Atualizações rotineiras EVM/Solana reutilizam endereços vazios verificados recentemente por até 30 minutos entre auditorias, enquanto verificam novamente endereços com fundos, novos ou alterados. Rastrear saldos explicitamente no console solicita uma varredura completa.
- recent_payments é limitado a três observações por carteira e exclui o histórico invalidado.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto habilitado atribuído à credencial. |
WalletSummary
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id / project_id / native_asset_id | UUID | sempre | Identificadores de carteira, projeto proprietário e ativo nativo da rede. |
| chain_slug / network | string | sempre | Cadeia e rede da carteira. |
| asset_symbol / asset_name | string | sempre | Identidade visual nativa da rede. |
| status | pending | active | disabled | error | sempre | Estado operacional da carteira. |
| label | string | sempre | Rótulo do operador. |
| public_key / primary_address | string | null | sempre | Identidade pública da carteira; não expõe frase-semente nem chave privada. |
| derivation_scheme / address_format | string | null | sempre | Política e formato de endereços. |
| backup_confirmed_at | timestamp | null | sempre | Diferente de null após o operador confirmar o backup de recuperação. |
| activation_required / activation_verified_at | boolean / timestamp|null | sempre | Contas compartilhadas XRP e Stellar continuam indisponíveis até o operador enviar fundos ao endereço exibido e os provedores de rastreamento configurados verificarem essa conta exata. A prova persistente não expira; a saúde ao vivo do scanner é verificada separadamente para verificar pagamentos, não para criar faturas. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Incluído em listas de carteiras: configuração de recebimento do projeto e pré-requisitos do scanner da rede. Separado de saldos, gás de tokens e disponibilidade de envio. Outras respostas de carteira podem deixar null. |
| monero_wallet_rpc | MoneroWalletRpcBinding | null | sempre | Estado do vínculo externo wallet-RPC somente leitura do Monero, sem dados sensíveis. Inclui endpoint, modo de autenticação, endereço principal da conta 0, indicadores/alturas de prova técnica e datas de declaração do operador; credenciais, chaves e arquivos de carteira nunca são serializados. |
| last_secret_revealed_at / secret_reveal_count | timestamp|null / integer | sempre | Metadados de auditoria de revelação de segredos no console. |
| next_receive_index | integer | sempre | Próximo índice reservado de endereço derivado. |
| last_scanned_height / last_scanned_at / last_error | integer|null / timestamp|null / string|null | sempre | Status do scanner da carteira. |
| balances | WalletAssetBalance[] | sempre | Saldos em cache de cada uma das 30 vias nativas, além de ativos ERC-20 e SPL verificados. Monero exige uma wallet-RPC externa somente leitura configurada. |
| total_value_usd | decimal string | null | sempre | Soma indicativa de saldos com preço USD atual. |
| balance_status | pending | refreshing | fresh | stale | error | unknown | sempre | Atualização agregada do cache; unknown é uma alternativa defensiva e nenhum destes estados comprova a liquidação da fatura. |
| balance_checked_at | timestamp | null | sempre | Verificação de saldo bem-sucedida relevante mais antiga representada no agregado. |
| recent_payments | WalletRecentPayment[] | sempre | Até as três observações válidas detected, confirming ou final mais recentes atribuídas a esta carteira exata. |
| created_at / updated_at | RFC 3339 timestamp | sempre | Hora da criação e última atualização da carteira. |
WalletAssetBalance
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| wallet_id / asset_id | UUID | sempre | Identidades da carteira e do ativo persistente. |
| project_enabled | boolean | sempre | Se este ativo está habilitado atualmente pela política de ativos do projeto. |
| active_store_count | integer | sempre | Número de lojas habilitadas que selecionam este ativo atualmente. É uma projeção de aceitação; o monitoramento de saldos somente leitura continua independente. |
| active_store_ids | UUID[] | sempre | Lojas habilitadas deste projeto que aceitam o ativo atualmente. Permite um filtro local exato de lojas sem outra solicitação de API. |
| tracking_active | boolean | sempre | Se esta carteira com leitura e o ativo registrado da mesma rede são aptos para atualizações de saldo em segundo plano. Controles de aceitação do projeto e da forma de pagamento não pausam o monitoramento somente leitura. |
| asset_kind | native | token | sempre | Moeda nativa ou ativo de contrato/mint verificado. |
| contract_address | string | null | sempre | Contrato ou mint do token; null para moeda nativa. |
| symbol / name / decimals | string / string / integer | sempre | Identidade visual e precisão atômica. |
| coingecko_id | string | null | sempre | Identidade de preços quando vinculada. |
| balance / balance_atomic | decimal string|null / integer string|null | sempre | Saldo visível e atômico exato do endereço principal da carteira e dos endereços de fatura emitidos. Null enquanto um valor completo estiver indisponível. |
| price_usd | decimal string | null | sempre | Preço unitário USD indicativo em cache usado para avaliação. |
| value_usd | decimal string | null | sempre | Avaliação fiduciária indicativa quando existe uma cotação atual. |
| status | pending | refreshing | fresh | stale | error | sempre | Status da varredura em cache. refreshing pode preservar um saldo completo: use checked_at para sua idade. Pendente significa que não há retrato completo. Nenhum desses estados comprova uma transferência pendente nem uma fatura liquidada. |
| checked_at | timestamp | null | sempre | Hora representada por uma varredura de saldo completa. |
| last_error | string | null | sempre | Diagnóstico seguro para o operador. |
WalletRecentPayment
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| invoice_public_id | UUID | sempre | Identidade da fatura visível ao cliente associada à observação. |
| chain_slug / symbol | string | sempre | Rede e símbolo visível da moeda nativa ou token verificado. |
| transaction_id / event_index | string / integer | sempre | Identidade canônica da transação e do evento de transferência. |
| amount | decimal string | sempre | Valor exato observado do ativo sem conversão para ponto flutuante. |
| status | detected | confirming | final | sempre | Status válido atual da observação. Observações reorganizadas, substituídas e inválidas são excluídas. |
| confirmations | integer | sempre | Último número observado de confirmações. |
| observed_at | RFC 3339 timestamp | sempre | Hora em que Wholly Crypto observou o pagamento pela primeira vez. |
ReceiveReadiness
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| ready | boolean | sempre | As verificações da configuração de recebimento passam. Não descreve disponibilidade de gasto, gás, atualização de saldos nem uma cotação futura garantida. |
| invoice_creatable | boolean | 6.0.6+ | A configuração permite uma forma de fatura apesar de avisos temporários do scanner. O preço da moeda é verificado na criação. Isto não verifica pagamentos: ready pode ser false enquanto invoice_creatable é true. Carteiras ausentes, políticas desativadas e adaptadores incompatíveis continuam falhando com segurança. |
| checked_at | timestamp | sempre | Hora da avaliação. Listar não faz solicitações de rede nem aloca endereços. |
| issues | PaymentMethodIssue[] | sempre | Vazio quando pronto; caso contrário, aviso de recebimento ou bloqueio de configuração. Confira invoice_creatable para distinguir avisos temporários do scanner de falhas de configuração da fatura. |
PaymentMethodIssue
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | quando conhecido | Identifica a rede e o ativo afetados. Lightning pode omitir asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled ou asset_not_accepted. |
| message / action | string | quando disponível | Explicação para o lojista e identificador de ação: chain_connections, wallets, rates, payment_methods, project_settings ou store_settings. Sem credenciais nem URLs privadas de provedores. |
| required_endpoint_role | string | null | na blockchain | Função API preferida do scanner (campo legado). Use accepted_endpoint_roles para a lista completa de compatibilidade. A saúde básica de um nó não comprova suporte ao histórico de pagamentos. |
| accepted_endpoint_roles | string[] | null | na blockchain | Dialetos de API compatíveis, não prova de histórico nem capacidade do endpoint. node-rpc direto aceita BTC/BCH/LTC/DOGE/DASH e ZEC transparente (blocos completos decodificados, 1–48 confirmações), TRX nativo solidificado, ALGO nativo via algod, XTZ via Octez, DOT finalizado do Asset Hub via metadados SCALE e XLM nativo via Stellar RPC com ID de memo da fatura. Histórico podado ou incompleto não serve. Esses adaptadores diretos não adicionam vias de tokens. APIs indexadas continuam alternativas; veja a tabela de vias abaixo. Fontes diretas/indexadas mistas verificam janelas limitadas independentemente; o padrão continua sendo dois provedores independentes, não aliases do mesmo operador. A altura básica do nó, informações de rede ORDnet e um relay EVM para uma via não EVM não são provas de recebimento. Monero ainda precisa de uma wallet-RPC somente leitura vinculada ao projeto. |
| healthy_endpoints | integer | na blockchain | Endpoints saudáveis correspondentes, não o número de provedores independentes. |
| usable_independent_providers / required_independent_providers | integer | na blockchain | Vagas de verificação utilizáveis, limitadas a duas. required_independent_providers é a configuração da rede: 2 por padrão ou 1 após escolha expressa do administrador. No modo de dois provedores, são exigidas chaves de provedor E hosts diferentes. Fontes desativadas, antigas (mais de dez minutos) ou em pausa não ocupam uma vaga. Lightning usa suas próprias regras de conexão. |
| last_checked_at | timestamp | null | na blockchain | Última verificação de saúde do endpoint correspondente, separada da hora da avaliação. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/wallets" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/wallets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/wallets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/wallets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [
{
"id": "55555555-5555-4555-8555-555555555555",
"project_id": "11111111-1111-4111-8111-111111111111",
"native_asset_id": "10000000-0000-4000-8000-000000000003",
"chain_slug": "ethereum",
"network": "mainnet",
"asset_symbol": "ETH",
"asset_name": "Ethereum",
"status": "active",
"label": "Primary Ethereum wallet",
"public_key": "0x…",
"primary_address": "0x…",
"derivation_scheme": "bip44",
"address_format": "eip55",
"backup_confirmed_at": "2026-08-31T17:00:00Z",
"last_secret_revealed_at": null,
"secret_reveal_count": 0,
"next_receive_index": 43,
"last_scanned_height": 23123456,
"last_scanned_at": "2026-08-31T18:05:00Z",
"last_error": null,
"balances": [
{ "wallet_id": "55555555-5555-4555-8555-555555555555", "asset_id": "10000000-0000-4000-8000-000000000003", "project_enabled": true, "tracking_active": true, "asset_kind": "native", "contract_address": null, "symbol": "ETH", "name": "Ethereum", "decimals": 18, "coingecko_id": "ethereum", "balance": "0.125", "balance_atomic": "125000000000000000", "price_usd": "4500", "value_usd": "562.50", "status": "fresh", "checked_at": "2026-08-31T18:05:00Z", "last_error": null },
{ "wallet_id": "55555555-5555-4555-8555-555555555555", "asset_id": "10000000-0000-4000-8000-000000000099", "project_enabled": false, "tracking_active": true, "asset_kind": "token", "contract_address": "0xA0b86991c6218b36c1d19d4a2e9eb0cE3606eB48", "symbol": "USDC", "name": "USDC", "decimals": 6, "coingecko_id": "usd-coin", "balance": null, "balance_atomic": null, "price_usd": "1", "value_usd": null, "status": "pending", "checked_at": null, "last_error": null }
],
"total_value_usd": "562.50",
"balance_status": "fresh",
"balance_checked_at": "2026-08-31T18:05:00Z",
"recent_payments": [
{ "invoice_public_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50", "chain_slug": "ethereum", "symbol": "USDC", "transaction_id": "0x…", "event_index": 0, "amount": "25", "status": "final", "confirmations": 12, "observed_at": "2026-08-31T18:04:00Z" }
],
"created_at": "2026-08-31T16:00:00Z",
"updated_at": "2026-08-31T18:05:00Z"
}
]
}POSTCriar fatura/v1/projects/{project_id}/stores/{store_id}/invoicesLeitura e gravação
Cria uma fatura atomicamente com destinos de carteira, cotações exatas recentes, histórico de auditoria e entradas na caixa de saída de notificações. Repetir os mesmos bytes do corpo original com a mesma credencial e Idempotency-Key retorna a fatura original.
- payment_methods filtra as formas habilitadas da loja só para esta fatura. Omitido/null preserva todas as formas; [] é inválido. Encontre a indicação chain_slug e os símbolos visíveis dos ativos em Projeto → Lojas → Formas de pagamento. A lista API payment-assets fornece chain_slug, asset.symbol e asset.id. Use {chain_slug: ethereum, asset_tickers: [USDC, USDT]} para tokens Ethereum aceitos; BTC e PEPE funcionam igual nas redes escolhidas. Símbolos não diferenciam maiúsculas, são restritos a uma rede e só são resolvidos dentro da loja. Dois contratos aceitos com o mesmo símbolo retornam 400 em vez de escolher um, mesmo se um não estiver pronto; use asset_ids nesse caso. Ativos nativos, tokens do catálogo e personalizados seguem as mesmas regras. Cada cadeia/via pode aparecer uma vez; no máximo 64 formas finais. Merchant 5.4.0+: opções desconhecidas, desativadas, de rede errada ou não aceitas são ignoradas. Se toda a seleção não tiver correspondências ativas aceitas, são usados os padrões da loja; caso contrário, só as correspondências. Uma entrada só de rede inclui todos os ativos ativos aceitos nessa blockchain. Formas ativas selecionadas precisam de carteiras válidas, adaptadores de scanner instalados e cotações confiáveis. Desde 6.0.6, scanners indisponíveis, pausas e verificações de saúde pendentes/antigas não bloqueiam criar faturas nem removem formas na blockchain configuradas. A detecção tenta novamente automaticamente; liquidar ainda exige quórum de provedores e confirmações. Monitore receive_readiness e mantenha provedores disponíveis: uma fatura pode continuar sem verificação até os scanners se recuperarem. A alocação de subendereços Monero e a geração BOLT11 Lightning ainda exigem seu serviço externo de carteira/nó. Falhas retornam error.message mais error.details.payment_methods com chain_slug, asset_ticker, reason_code e, para diagnósticos de scanner, required_endpoint_role, healthy_endpoints e required_independent_providers. TRON aceita histórico indexado ou APIs diretas compatíveis de blocos nativos solidificados; a saúde básica sozinha não comprova compatibilidade com o scanner. Falhas de preços identificam o ativo/moeda. Nada ativa um ativo não aceito nem muda a política da loja. Em versões de merchant anteriores a 5.4.0, opções explícitas desconhecidas/inativas falham. Formas de faturas existentes nunca se ampliam quando as configurações da loja mudam. Lightning precisa ser selecionado separadamente. Repetições preservam as formas originais e mudar seleções com o mesmo Idempotency-Key retorna 409.
- checkout_appearance aceita todas as configurações de apresentação listadas acima. Campos omitidos são herdados, arrays substituem e campos de mensagens aninhados são combinados; um objeto de mensagem vazio limpa esse escopo. O design e as imagens resolvidos são salvos para esta fatura sem editar a loja. Leia appearance do JSON público do checkout para inspecionar o resultado. A solicitação completa é limitada a 32 KiB e as configurações resolvidas a 20 KiB.
- Mudar checkout_appearance com o mesmo Idempotency-Key retorna 409; tente novamente com bytes originais idênticos. A aparência não muda valores, cotações, ativos aceitos, confirmações exigidas, status real nem permissões de incorporação. Sem HTML, CSS, scripts nem busca de imagens remotas.
- exchange_rate_spread_percent substitui o padrão da loja para esta fatura: omita ou envie null para herdar, ou envie "0" para desativar. Cotações de faturas existentes nunca mudam.
- A margem é aplicada antes do arredondamento para cima. As taxas continuam baseadas no valor fiduciário original da fatura, excluindo a margem.
- Envie sempre o expected_amount ou expected_amount_atomic retornado. O arredondamento é para cima, limitado pela precisão do ativo, 0.1% do valor e uma unidade menor fiduciária.
- Novas tentativas precisam preservar credencial, Idempotency-Key e bytes exatos do corpo. Mudar a margem com a mesma chave retorna 409 idempotency_conflict.
- Uma repetição exata é verificada antes de novas cotações, DNS de notificação ou preparação de endereços. O escopo da credencial e a autorização de projeto/loja continuam sendo verificados em cada solicitação.
- Uma ipn_url efetiva exige o segredo de assinatura IPN da loja. Campos desconhecidos no corpo são rejeitados.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Idempotency-Key | obrigatório | 1–128 caracteres ASCII visíveis únicos, sem espaços em branco. |
| Content-Type | recomendado | application/json. O manipulador atual do corpo original analisa JSON sem exigir o tipo de conteúdo. |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Copie o ID de API do projeto em Projeto → Configurações → IDs da API. Precisa estar atribuído à credencial; um identificador legível do projeto não é aceito. |
| store_id | path UUID | Copie o ID de API da loja em Projeto → Lojas → selecione uma loja → Básico → IDs da API. Obrigatório mesmo para a loja padrão; precisa estar habilitada e pertencer a project_id. |
Corpo de criação da fatura
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| amount | string | obrigatório | String decimal simples sem sinal nem expoente, até 48 dígitos inteiros e 30 casas decimais. Precisa ser positiva por padrão. Uma loja pode permitir faturas de valor zero em Lojas → Fatura; totais zero são liquidados sem receber fundos, alocar endereços nem taxas de processamento. |
| currency | string | null | opcional | Moeda fiduciária compatível de três letras, normalizada em maiúsculas. Omitida ou null herda a moeda de fatura da loja. A criação também exige uma cotação de conversão de faturamento disponível independentemente. |
| payment_methods | InvoicePaymentSelection[] | null | opcional | Selecione formas habilitadas da loja para esta fatura. Merchant 5.4.0+: ignora opções desconhecidas/inativas/não aceitas; se nenhuma corresponder, usa padrões da loja. Omitido/null também usa padrões da loja; [] é inválido. Nunca ativa uma forma nem muda configurações da loja. Veja o esquema de seleção abaixo. |
| order_id | string | null | opcional | Referência do pedido do lojista, 1–128 caracteres após remover espaços das pontas; caracteres de controle são rejeitados. |
| string | null | opcional | Email do cliente só para o lojista, normalizado para um endereço ASCII utilizável de no máximo 254 caracteres. Omitido ou null não salva email. | |
| description | string | null | opcional | Descrição visível ao cliente, 1–500 caracteres; quebras de linha e tabulações são permitidas. |
| expires_in_seconds | integer | null | opcional | Validade da cotação da fatura de 300 a 86,400 segundos; omitido ou null herda a política da loja. |
| exchange_rate_spread_percent | decimal string | null | opcional | Margem da cotação de 0 a 100, no máximo duas casas decimais. Omitido ou null herda o padrão da loja; "0" desativa para esta fatura. Aplicada antes do arredondamento para cima e depois fixada. Não muda o valor fiduciário da fatura nem a base da taxa de processamento. |
| underpayment_tolerance_percent | decimal string | null | opcional | Diferença a menor aceita de 0 a 99.99 com no máximo duas casas decimais. Omitido ou null herda o padrão da loja. |
| ipn_url | string | null | opcional | Notificação HTTPS pública, no máximo 2,048 bytes e sem credenciais nem fragmento. Substitui o padrão da loja; null/omitido herda. |
| redirect_url | string | null | opcional | URL HTTPS de sucesso após liquidar, no máximo 2,048 bytes e sem credenciais incluídas. Omitido ou null herda o padrão da loja e não pode removê-lo. |
| cancel_url | string | null | opcional | URL HTTPS de retorno quando o checkout termina sem pagamento bem-sucedido. Omitido ou null herda o padrão da loja e não pode removê-lo. |
| redirect_automatically | boolean | null | opcional | Omitido ou null herda a política da loja. true exige uma redirect_url efetiva. |
| language | string | null | opcional | Tag BCP 47 inglesa ou alemã como en, de ou de-DE; omitido ou null herda a política da loja. |
| checkout_appearance | CheckoutAppearanceOverride | null | opcional | Configurações parciais de apresentação para esta fatura. Omitido/null segue o design atual da loja. Um objeto, incluindo {}, fixa o design e as imagens resolvidos na criação. Veja o esquema de personalização abaixo; sem configurações financeiras, HTML, CSS, JavaScript nem URLs de imagens remotas. |
| metadata | object | null | opcional | Objeto JSON só para o lojista; omitido ou null vira {}, no máximo 4,096 bytes codificados e cinco níveis aninhados. firstname, lastname, street, street2, zip, city, country, countryiso2, company e vatid são validados, normalizados e projetados nos campos de resumo do cliente. |
InvoicePaymentSelection · escolha redes e ativos da loja
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug | string | obrigatório | Copie chain_slug em Projeto → Lojas → Formas de pagamento ou leia em GET /v1/projects/{project_id}/stores/{store_id}/payment-assets, como ethereum, base ou bitcoin. Um par cadeia/via só pode aparecer uma vez. |
| asset_ids | UUID[] | null | opcional | UUIDs asset.id na blockchain, não endereços de contrato nem IDs de formas de fatura. Use isto OU asset_tickers. Omita os dois seletores para todos os ativos ativos aceitos nesta rede. [] e IDs duplicados/nulos são inválidos. Em 5.4.0+, ignora IDs não ativos/aceitos nesta rede desta loja; uma seleção sem correspondências usa padrões da loja. |
| asset_tickers | string[] | null | opcional | Merchant 5.3.0+. Símbolos como BTC, USDC ou PEPE restritos a chain_slug e esta loja. 1–64 símbolos únicos; remove espaços e não diferencia maiúsculas, 1–40 letras/dígitos/ponto/sublinhado/hífen ASCII. Use isto OU asset_ids. Em 5.4.0+, ignora símbolos desconhecidos/inativos/não aceitos. Símbolos aceitos ambíguos ainda falham: use asset_ids. Formas ativas selecionadas precisam de carteiras e preços válidos; interrupções temporárias do scanner na blockchain não bloqueiam criar desde 6.0.6. Lightning opcionalmente aceita só BTC. |
| payment_rail | onchain | lightning | opcional | Por padrão onchain. Para escolher Bitcoin Lightning, use {chain_slug: bitcoin, payment_rail: lightning} sem asset_ids; asset_tickers pode ser opcionalmente [BTC]. Bitcoin na blockchain não inclui Lightning. A conexão Lightning da loja já precisa estar ativada e pronta. |
CheckoutAppearanceOverride · todos os campos opcionais
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| inherit_default_store | boolean | opcional | true escolhe como base o design da loja padrão do projeto; caso contrário, usa o design efetivo da loja de destino. Depois as personalizações são aplicadas e salvas independentemente; o indicador resolvido da fatura é false. |
| title | string | opcional | Título do checkout, até 120 caracteres. Vazio usa o título padrão. |
| intro / outro | string | opcional | Texto simples, até 2,000 caracteres cada. Intro aparece no topo e Outro no fim em todos os estados. Quebras de linha são preservadas; URLs seguras no texto viram links. Uma string vazia limpa. O antigo customer_message é aceito como alias de intro; não envie ambos. |
| intro_font_size / outro_font_size | integer | opcional | Pixels: 12, 14, 16, 18, 20 ou 24. Padrão 16 salvo herança diferente. |
| theme | system | light | dim | dark | opcional | Siga o dispositivo do cliente ou use um tema fixo. |
| accent_color / background_color / card_color / button_color | string | opcional | #RRGGBB. Fundo, cartão e botão podem ficar vazios para cores automáticas. O contraste do texto é automático. |
| logo_size / logo_alignment | string | opcional | small, medium ou large; left ou center. |
| images | object | opcional | Chaves logo_light, logo_dark, favicon. Omitir uma chave preserva a imagem base; null remove. Um objeto {store_id: UUID, kind?: logo_light|logo_dark|favicon} reutiliza a imagem enviada efetiva dessa loja no MESMO projeto. kind usa como padrão a chave de destino. Primeiro envie a imagem em Loja → Checkout; copie o ID de API da loja em Básico → IDs da API. Imagens ausentes ou IDs de outros projetos retornam 400. URLs externas e dados de imagens não são aceitos. |
| show_order_id / show_description / details_expanded | boolean | opcional | Mostra detalhes do ID do pedido e descrição em texto simples abaixo do título. details_expanded abre inicialmente os detalhes do ID. Só afeta a exibição, não oculta dados. |
| show_project_name / show_store_name | boolean | opcional | Merchant 5.6.0+: mostra ou oculta cada nome no cabeçalho do checkout do cliente. Ambos usam true por padrão. Também disponível em Loja → Checkout; herdado e salvo por fatura como outras configurações de aparência. Só visual, não oculta dados. |
| featured_chains | string[] | opcional | Slugs de rede ordenados, no máximo 60 valores únicos (letras minúsculas, dígitos, hífens; até 64 caracteres). [] limpa. Só reordena formas disponíveis da fatura. |
| featured_asset_ids / default_asset_id | UUID[] / UUID|null | opcional | Até 100 IDs únicos de ativos ordenados; [] limpa. O ativo padrão pode ser null. Os IDs vêm de payment-assets, não de intenções de pagamento. Nunca ativam formas; pagamentos recebidos e preferências válidas do cliente têm prioridade. |
| messages | object | opcional | Objetos en/de com strings simples waiting, confirming, paid, underpaid, expired (500 caracteres cada). Só mudam idiomas/estados enviados; {} limpa todas as mensagens, {en:{}} limpa inglês e uma string de estado vazia limpa esse estado. O idioma alternativo é inglês. Não substitui o status real. |
| support_email | string | opcional | Email ASCII, até 254 caracteres. Vazio limpa. |
| support_url / terms_url / privacy_url | string | opcional | URLs HTTPS de até 2,048 caracteres, sem credenciais. Vazio limpa. Os links abrem em uma nova janela. |
| return_button_text | string | opcional | Rótulo de até 60 caracteres. Use redirect_url/cancel_url/redirect_automatically/language de nível superior para o comportamento da fatura. |
Resumo da fatura
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | UUID interno da fatura. Não use nas rotas de detalhe do lojista nem de checkout. |
| invoice_id | UUID | sempre | UUID público da fatura usado pelas rotas de detalhe do lojista e checkout. |
| project_id | UUID | sempre | Projeto proprietário. |
| store_id | UUID | sempre | Loja proprietária. |
| source | manual | api | sempre | Como a fatura foi criada. |
| order_id | string | null | sempre | Referência do pedido do lojista. |
| string | null | sempre | Email do cliente só para o lojista. Nunca retornado no checkout público. | |
| customer_name | string | null | sempre | Nome visível derivado dos metadados privados firstname, lastname e company. |
| customer_address | string | null | sempre | Endereço em uma linha para o lojista derivado dos metadados privados company, street, street2, zip, city, country, countryiso2 e vatid. |
| description | string | null | sempre | Descrição visível ao cliente. |
| amount | decimal string | sempre | Valor canônico da fatura. |
| currency | string | sempre | Código normalizado de moeda/ativo da fatura. |
| exchange_rate_spread_percent | decimal string | sempre | Margem da cotação fixada: o valor personalizado na criação ou o padrão da loja se omitido. Aplicada antes do arredondamento para cima; nunca muda nesta fatura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentual imutável de diferença a menor aceita, capturado na criação da fatura. |
| status | invoice status | sempre | new, processing, settled, expired, invalid ou cancelled. |
| amount_status | amount status | sempre | none, partial, paid ou overpaid. Uma fatura de valor zero permitida expressamente é liquidada com none e sem formas de pagamento. |
| timing_status | timing status | sempre | on_time ou late. |
| resolution | resolution | sempre | automatic, manually_settled ou manually_invalidated. |
| sequence | integer | sempre | Sequência monotônica do status da fatura, a partir de 1. |
| winning_payment_intent_id | UUID | null | sempre | Forma de pagamento que resolveu a fatura, quando selecionada. |
| expires_at | RFC 3339 timestamp | sempre | Prazo da cotação/pagamento. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Último limite configurado de monitoramento tardio entre as formas de pagamento. |
| settled_at | timestamp | null | sempre | Hora de liquidação quando liquidada. |
| cancelled_at | timestamp | null | sempre | Hora de cancelamento quando cancelada. |
| archived_at | timestamp | null | sempre | Hora de arquivamento quando arquivada. |
| created_at | RFC 3339 timestamp | sempre | Hora de criação. |
| updated_at | RFC 3339 timestamp | sempre | Hora da última atualização do status. |
Dados adicionais do detalhe da fatura
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| ipn_url | string | null | sempre | Destino IPN efetivo por fatura. Só na resposta ao lojista; omitido no checkout público. |
| redirect_url | string | null | sempre | URL efetiva de sucesso usada após liquidar. |
| cancel_url | string | null | sempre | URL efetiva de retorno quando o checkout termina sem pagamento bem-sucedido. |
| redirect_automatically | boolean | sempre | Se o checkout deve redirecionar automaticamente após o sucesso. |
| checkout_language | string | sempre | Tag efetiva do idioma do checkout. |
| metadata | object | sempre | Metadados do lojista. Nunca retornados no checkout público. |
| payment_intents | PaymentIntent[] | sempre | Formas de pagamento cotadas e status do monitoramento. |
PaymentIntent
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | Identificador da intenção de pagamento; também usado como intent_id do QR do checkout. |
| payment_rail | onchain | lightning | sempre | Transporte da fatura. Bitcoin na blockchain e Lightning podem compartilhar asset_id; use o ID da intenção mais este campo, não só o símbolo. Difere do payment_rail do scanner do catálogo de ativos. |
| bolt11 | string | null | sempre | Solicitação de pagamento Lightning, caso contrário null. Pague esta solicitação com uma carteira Lightning; nunca envie fundos na blockchain para o hash de pagamento. |
| asset_id | UUID | sempre | Identificador configurado do ativo de pagamento. |
| asset_key | string | sempre | Chave canônica do ativo no estilo CAIP. |
| chain_slug | string | sempre | Identificador de cadeia Wholly Crypto. |
| network | string | sempre | Rede configurada, atualmente mainnet para ativos de pagamento compatíveis. |
| caip_network_id | string | sempre | Identificador canônico da rede CAIP-2. |
| caip_asset_id | string | null | sempre | Identificador canônico CAIP-19 quando registrado. |
| symbol | string | sempre | Símbolo do ativo. |
| asset_decimals | integer | sempre | Precisão em unidades atômicas. Lightning BTC usa 11 (millisatoshis), não os 8 do Bitcoin na blockchain. Cotações são em satoshis inteiros; recebimentos preservam precisão de millisatoshi. |
| status | intent status | sempre | pending, partial, paid, overpaid, expired ou invalid. |
| finality_mode | confirmations | finalized | sempre | Política de finalidade. |
| required_confirmations | integer | sempre | Confirmações exigidas quando aplicável. |
| quote_rate | decimal string | sempre | Unidades do ativo por uma unidade da moeda da fatura, incluindo a margem fixada. Por exemplo, 1.02 USDC por USD. Não é a cotação inversa. |
| quote_details | object | null | sempre | Origem fixada da cotação: reference_rate antes da margem, unrounded_payment_amount, rounding_adjustment, pricing_provider, asset_provider, pricing_fetched_at e asset_fetched_at. Null em faturas antigas; valores históricos não são inventados. |
| expected_amount | decimal string | sempre | Valor exato fixado do ativo a pagar após margem e arredondamento para cima. Desde 4.1.1, stablecoins fiduciárias reconhecidas e verificadas (como USDC, USDT, DAI, USDS, EURC) são arredondadas para cima a no máximo duas casas decimais; 1.321 vira 1.33, nunca 1.32. Este é o valor esperado mesmo com tolerância zero. Outros ativos preservam precisão adaptativa. Faturas existentes nunca são recalculadas. |
| expected_amount_atomic | integer string | sempre | Valor exato na menor unidade do ativo. |
| minimum_payment_amount | decimal string | sempre | Menor valor aceito como pago após aplicar a tolerância da fatura. |
| minimum_payment_amount_atomic | integer string | sempre | Limite aceito exato na menor unidade do ativo. |
| received_amount | decimal string | sempre | Valor observado. |
| received_amount_atomic | integer string | sempre | Valor atômico observado. |
| confirmed_amount | decimal string | sempre | Valor confirmado/final. |
| confirmed_amount_atomic | integer string | sempre | Valor atômico confirmado/final. |
| destination_address | string | sempre | Endereço de recebimento na blockchain ou hash de pagamento de 64 caracteres para Lightning. Use bolt11 para pagar Lightning; o hash não é um endereço Bitcoin. |
| destination_tag | string | null | sempre | Referência pública de pagamento obrigatória se a via usar: tag de destino XRP, ID de memo Stellar ou comentário de fatura TON. Null para vias de endereços únicos. |
| derivation_index | integer | sempre | Índice derivado reservado da carteira; só detalhe do lojista. |
| quote_expires_at | RFC 3339 timestamp | sempre | Vencimento da cotação. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Limite de monitoramento tardio desta forma. |
| next_check_at | timestamp | null | sempre | Próxima verificação programada da rede. |
| last_checked_at | timestamp | null | sempre | Última verificação da rede. |
| last_chain_height | integer | null | sempre | Última altura confiável observada pelo monitor. |
| last_anchor_hash | string | null | sempre | Última âncora/hash de bloco do monitor. |
| last_monitor_error | string | null | sempre | Diagnóstico seguro de monitoramento para operadores. |
| first_payment_at | timestamp | null | sempre | Hora do primeiro pagamento observado. |
| fully_paid_at | timestamp | null | sempre | Hora em que o mínimo aceito foi atingido pela primeira vez. |
| finalized_at | timestamp | null | sempre | Hora em que o pagamento cumpriu a política de finalidade. |
PaymentMethodIssue
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | quando conhecido | Identifica a rede e o ativo afetados. Lightning pode omitir asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled ou asset_not_accepted. |
| message / action | string | quando disponível | Explicação para o lojista e identificador de ação: chain_connections, wallets, rates, payment_methods, project_settings ou store_settings. Sem credenciais nem URLs privadas de provedores. |
| required_endpoint_role | string | null | na blockchain | Função API preferida do scanner (campo legado). Use accepted_endpoint_roles para a lista completa de compatibilidade. A saúde básica de um nó não comprova suporte ao histórico de pagamentos. |
| accepted_endpoint_roles | string[] | null | na blockchain | Dialetos de API compatíveis, não prova de histórico nem capacidade do endpoint. node-rpc direto aceita BTC/BCH/LTC/DOGE/DASH e ZEC transparente (blocos completos decodificados, 1–48 confirmações), TRX nativo solidificado, ALGO nativo via algod, XTZ via Octez, DOT finalizado do Asset Hub via metadados SCALE e XLM nativo via Stellar RPC com ID de memo da fatura. Histórico podado ou incompleto não serve. Esses adaptadores diretos não adicionam vias de tokens. APIs indexadas continuam alternativas; veja a tabela de vias abaixo. Fontes diretas/indexadas mistas verificam janelas limitadas independentemente; o padrão continua sendo dois provedores independentes, não aliases do mesmo operador. A altura básica do nó, informações de rede ORDnet e um relay EVM para uma via não EVM não são provas de recebimento. Monero ainda precisa de uma wallet-RPC somente leitura vinculada ao projeto. |
| healthy_endpoints | integer | na blockchain | Endpoints saudáveis correspondentes, não o número de provedores independentes. |
| usable_independent_providers / required_independent_providers | integer | na blockchain | Vagas de verificação utilizáveis, limitadas a duas. required_independent_providers é a configuração da rede: 2 por padrão ou 1 após escolha expressa do administrador. No modo de dois provedores, são exigidas chaves de provedor E hosts diferentes. Fontes desativadas, antigas (mais de dez minutos) ou em pausa não ocupam uma vaga. Lightning usa suas próprias regras de conexão. |
| last_checked_at | timestamp | null | na blockchain | Última verificação de saúde do endpoint correspondente, separada da hora da avaliação. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new invoice.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: order-1042-attempt-1' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_methods": [
{
"chain_slug": "bitcoin",
"asset_tickers": [
"BTC"
]
}
],
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"email": "ada@example.com",
"description": "Annual plan",
"underpayment_tolerance_percent": "1",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"language": "en",
"metadata": {
"cart_id": "cart-681",
"firstname": "Ada",
"lastname": "Lovelace",
"street": "12 Example Street",
"street2": "Suite 2",
"zip": "10115",
"city": "Berlin",
"country": "Germany",
"countryiso2": "DE",
"company": "Example GmbH",
"vatid": "DE123456789"
},
"exchange_rate_spread_percent": "0.5",
"checkout_appearance": {
"title": "Complete your order",
"intro": "Thanks for choosing our annual plan.",
"outro": "Questions? https://merchant.example/help",
"intro_font_size": 18,
"outro_font_size": 14,
"theme": "light",
"accent_color": "#1768CE",
"messages": {
"en": {
"paid": "Your order is ready."
}
}
}
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new invoice.
const body = `{
"payment_methods": [
{
"chain_slug": "bitcoin",
"asset_tickers": [
"BTC"
]
}
],
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"email": "ada@example.com",
"description": "Annual plan",
"underpayment_tolerance_percent": "1",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"language": "en",
"metadata": {
"cart_id": "cart-681",
"firstname": "Ada",
"lastname": "Lovelace",
"street": "12 Example Street",
"street2": "Suite 2",
"zip": "10115",
"city": "Berlin",
"country": "Germany",
"countryiso2": "DE",
"company": "Example GmbH",
"vatid": "DE123456789"
},
"exchange_rate_spread_percent": "0.5",
"checkout_appearance": {
"title": "Complete your order",
"intro": "Thanks for choosing our annual plan.",
"outro": "Questions? https://merchant.example/help",
"intro_font_size": 18,
"outro_font_size": 14,
"theme": "light",
"accent_color": "#1768CE",
"messages": {
"en": {
"paid": "Your order is ready."
}
}
}
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new invoice.
$body = <<<'JSON'
{
"payment_methods": [
{
"chain_slug": "bitcoin",
"asset_tickers": [
"BTC"
]
}
],
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"email": "ada@example.com",
"description": "Annual plan",
"underpayment_tolerance_percent": "1",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"language": "en",
"metadata": {
"cart_id": "cart-681",
"firstname": "Ada",
"lastname": "Lovelace",
"street": "12 Example Street",
"street2": "Suite 2",
"zip": "10115",
"city": "Berlin",
"country": "Germany",
"countryiso2": "DE",
"company": "Example GmbH",
"vatid": "DE123456789"
},
"exchange_rate_spread_percent": "0.5",
"checkout_appearance": {
"title": "Complete your order",
"intro": "Thanks for choosing our annual plan.",
"outro": "Questions? https://merchant.example/help",
"intro_font_size": 18,
"outro_font_size": 14,
"theme": "light",
"accent_color": "#1768CE",
"messages": {
"en": {
"paid": "Your order is ready."
}
}
}
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: order-1042-attempt-1", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new invoice.
headers = {
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"payment_methods": [
{
"chain_slug": "bitcoin",
"asset_tickers": [
"BTC"
]
}
],
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"email": "ada@example.com",
"description": "Annual plan",
"underpayment_tolerance_percent": "1",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"language": "en",
"metadata": {
"cart_id": "cart-681",
"firstname": "Ada",
"lastname": "Lovelace",
"street": "12 Example Street",
"street2": "Suite 2",
"zip": "10115",
"city": "Berlin",
"country": "Germany",
"countryiso2": "DE",
"company": "Example GmbH",
"vatid": "DE123456789"
},
"exchange_rate_spread_percent": "0.5",
"checkout_appearance": {
"title": "Complete your order",
"intro": "Thanks for choosing our annual plan.",
"outro": "Questions? https://merchant.example/help",
"intro_font_size": 18,
"outro_font_size": 14,
"theme": "light",
"accent_color": "#1768CE",
"messages": {
"en": {
"paid": "Your order is ready."
}
}
}
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 201 fatura nova; 200 repetição idempotente exata
{
"data": {
"id": "2f798f9f-01f2-42f0-9d10-5581d6116b4c",
"invoice_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50",
"project_id": "11111111-1111-4111-8111-111111111111",
"store_id": "22222222-2222-4222-8222-222222222222",
"source": "api",
"order_id": "order-1042",
"email": "ada@example.com",
"customer_name": "Ada Lovelace · Example GmbH",
"customer_address": "Example GmbH · 12 Example Street · Suite 2 · 10115 Berlin · Germany (DE) · VAT DE123456789",
"description": "Annual plan",
"amount": "49.9",
"currency": "USD",
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"status": "new",
"amount_status": "none",
"timing_status": "on_time",
"resolution": "automatic",
"sequence": 1,
"winning_payment_intent_id": null,
"expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"settled_at": null,
"cancelled_at": null,
"archived_at": null,
"created_at": "2026-08-31T18:00:00Z",
"updated_at": "2026-08-31T18:00:00Z",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"checkout_language": "en",
"metadata": { "cart_id": "cart-681", "firstname": "Ada", "lastname": "Lovelace", "street": "12 Example Street", "street2": "Suite 2", "zip": "10115", "city": "Berlin", "country": "Germany", "countryiso2": "DE", "company": "Example GmbH", "vatid": "DE123456789" },
"payment_intents": [
{
"id": "33333333-3333-4333-8333-333333333333",
"asset_id": "10000000-0000-4000-8000-000000000001",
"asset_key": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"chain_slug": "bitcoin",
"network": "mainnet",
"caip_network_id": "bip122:000000000019d6689c085ae165831e93",
"caip_asset_id": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"symbol": "BTC",
"asset_decimals": 8,
"status": "pending",
"finality_mode": "confirmations",
"required_confirmations": 1,
"quote_rate": "0.000009218",
"quote_details": null,
"expected_amount": "0.00046",
"expected_amount_atomic": "46000",
"minimum_payment_amount": "0.0004554",
"minimum_payment_amount_atomic": "45540",
"received_amount": "0",
"received_amount_atomic": "0",
"confirmed_amount": "0",
"confirmed_amount_atomic": "0",
"destination_address": "bc1q…example",
"destination_tag": null,
"derivation_index": 42,
"quote_expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"next_check_at": "2026-08-31T18:00:00Z",
"last_checked_at": null,
"last_chain_height": null,
"last_anchor_hash": null,
"last_monitor_error": null,
"first_payment_at": null,
"fully_paid_at": null,
"finalized_at": null
}
]
},
"links": {
"checkout": "https://pay.example.com/invoice/0a6a98db-d93d-48ee-8c3c-fd45f90c4a50"
}
}GETListar faturas/v1/projects/{project_id}/invoicesSomente leitura
Retorna uma página compacta de resumos de fatura do escopo, mais recentes primeiro, incluindo email só para o lojista e campos de cliente derivados de metadados reconhecidos. Filtros de busca, status e loja são avaliados no servidor; a resposta inclui total e has_more para paginação previsível.
- Ordenado por created_at decrescente e depois id interno decrescente.
- Os itens são objetos InvoiceSummary; email, customer_name e customer_address são só para o lojista. Consulte o detalhe para metadados originais e intenções de pagamento.
- Para a próxima página, defina offset como pagination.offset + pagination.limit só se has_more for true.
- A contagem e a página são lidas de um retrato do banco de dados com leitura repetível; gravações simultâneas aparecem em uma solicitação posterior.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto habilitado atribuído à credencial. |
| store_id | query UUID | Filtro exato opcional de loja. |
| status | query enum | Opcional: new, processing, settled, expired, invalid ou cancelled. |
| search | query string | Prefixo opcional de ID de fatura, ID de pedido ou email sem diferenciar maiúsculas; UUID exato de fatura; ou trecho na descrição e nos campos reconhecidos de cliente. Todas as chaves de metadados e valores de texto, numéricos e booleanos (incluindo objetos/arrays aninhados) também aceitam busca indexada por prefixo de palavra: cada palavra buscada precisa corresponder e pontuação é tratada como separador. Espaços nas pontas são removidos, no máximo 100 caracteres, sem controles. Correspondências de metadados não adicionam metadados originais às respostas de lista; use o detalhe da fatura para lê-los. |
| limit | query integer | Opcional 1–100; padrão 50. |
| offset | query integer | Opcional 0–1,000,000; padrão 0. |
Resumo da fatura
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | UUID interno da fatura. Não use nas rotas de detalhe do lojista nem de checkout. |
| invoice_id | UUID | sempre | UUID público da fatura usado pelas rotas de detalhe do lojista e checkout. |
| project_id | UUID | sempre | Projeto proprietário. |
| store_id | UUID | sempre | Loja proprietária. |
| source | manual | api | sempre | Como a fatura foi criada. |
| order_id | string | null | sempre | Referência do pedido do lojista. |
| string | null | sempre | Email do cliente só para o lojista. Nunca retornado no checkout público. | |
| customer_name | string | null | sempre | Nome visível derivado dos metadados privados firstname, lastname e company. |
| customer_address | string | null | sempre | Endereço em uma linha para o lojista derivado dos metadados privados company, street, street2, zip, city, country, countryiso2 e vatid. |
| description | string | null | sempre | Descrição visível ao cliente. |
| amount | decimal string | sempre | Valor canônico da fatura. |
| currency | string | sempre | Código normalizado de moeda/ativo da fatura. |
| exchange_rate_spread_percent | decimal string | sempre | Margem da cotação fixada: o valor personalizado na criação ou o padrão da loja se omitido. Aplicada antes do arredondamento para cima; nunca muda nesta fatura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentual imutável de diferença a menor aceita, capturado na criação da fatura. |
| status | invoice status | sempre | new, processing, settled, expired, invalid ou cancelled. |
| amount_status | amount status | sempre | none, partial, paid ou overpaid. Uma fatura de valor zero permitida expressamente é liquidada com none e sem formas de pagamento. |
| timing_status | timing status | sempre | on_time ou late. |
| resolution | resolution | sempre | automatic, manually_settled ou manually_invalidated. |
| sequence | integer | sempre | Sequência monotônica do status da fatura, a partir de 1. |
| winning_payment_intent_id | UUID | null | sempre | Forma de pagamento que resolveu a fatura, quando selecionada. |
| expires_at | RFC 3339 timestamp | sempre | Prazo da cotação/pagamento. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Último limite configurado de monitoramento tardio entre as formas de pagamento. |
| settled_at | timestamp | null | sempre | Hora de liquidação quando liquidada. |
| cancelled_at | timestamp | null | sempre | Hora de cancelamento quando cancelada. |
| archived_at | timestamp | null | sempre | Hora de arquivamento quando arquivada. |
| created_at | RFC 3339 timestamp | sempre | Hora de criação. |
| updated_at | RFC 3339 timestamp | sempre | Hora da última atualização do status. |
Paginação de faturas
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| limit | integer | sempre | Tamanho efetivo da página, 1–100. |
| offset | integer | sempre | Deslocamento efetivo de linhas a partir de zero, 0–1,000,000. |
| total | integer | sempre | Total de linhas que correspondem aos filtros de projeto, loja, status e busca no retrato da página. |
| has_more | boolean | sempre | True se offset mais o número de linhas retornadas for menor que total. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices?search=order-1042&status=processing&limit=50&offset=0" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices?search=order-1042&status=processing&limit=50&offset=0", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices?search=order-1042&status=processing&limit=50&offset=0");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices?search=order-1042&status=processing&limit=50&offset=0",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": [
{
"id": "2f798f9f-01f2-42f0-9d10-5581d6116b4c",
"invoice_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50",
"project_id": "11111111-1111-4111-8111-111111111111",
"store_id": "22222222-2222-4222-8222-222222222222",
"source": "api",
"order_id": "order-1042",
"email": "ada@example.com",
"customer_name": "Ada Lovelace · Example GmbH",
"customer_address": "Example GmbH · 12 Example Street · Suite 2 · 10115 Berlin · Germany (DE) · VAT DE123456789",
"description": "Annual plan",
"amount": "49.9",
"currency": "USD",
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"status": "processing",
"amount_status": "paid",
"timing_status": "on_time",
"resolution": "automatic",
"sequence": 3,
"winning_payment_intent_id": "33333333-3333-4333-8333-333333333333",
"expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"settled_at": null,
"cancelled_at": null,
"archived_at": null,
"created_at": "2026-08-31T18:00:00Z",
"updated_at": "2026-08-31T18:04:10Z"
}
],
"pagination": {
"limit": 50,
"offset": 0,
"total": 143,
"has_more": true
}
}GETObter fatura/v1/projects/{project_id}/invoices/{invoice_id}Somente leitura
Retorna o detalhe completo da fatura do lojista e a URL ativa atual do checkout. Use esta rota para consultas periódicas e conciliação.
- Uma consulta restrita ao escopo retorna deliberadamente invoice_not_found se o ID público não estiver no projeto autorizado.
- links.checkout usa Loja → Básico → Domínios da loja: o host de pagamento ativo desta loja, depois a escolha da loja padrão e depois o principal do sistema. Hosts retirados, rascunhos ou de serviço errado são ignorados. Também se aplica a respostas de criação e MCP; os links são resolvidos ao responder, incluindo repetições idempotentes. Links assinados de notificações ficam fixos na criação do evento e não são reescritos nas novas tentativas. Essas preferências só geram links; não redirecionam tráfego nem mudam restrições de IP.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto habilitado atribuído à credencial. |
| invoice_id | path UUID | O invoice_id retornado ao criar/listar, não o id interno. |
Resumo da fatura
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | UUID interno da fatura. Não use nas rotas de detalhe do lojista nem de checkout. |
| invoice_id | UUID | sempre | UUID público da fatura usado pelas rotas de detalhe do lojista e checkout. |
| project_id | UUID | sempre | Projeto proprietário. |
| store_id | UUID | sempre | Loja proprietária. |
| source | manual | api | sempre | Como a fatura foi criada. |
| order_id | string | null | sempre | Referência do pedido do lojista. |
| string | null | sempre | Email do cliente só para o lojista. Nunca retornado no checkout público. | |
| customer_name | string | null | sempre | Nome visível derivado dos metadados privados firstname, lastname e company. |
| customer_address | string | null | sempre | Endereço em uma linha para o lojista derivado dos metadados privados company, street, street2, zip, city, country, countryiso2 e vatid. |
| description | string | null | sempre | Descrição visível ao cliente. |
| amount | decimal string | sempre | Valor canônico da fatura. |
| currency | string | sempre | Código normalizado de moeda/ativo da fatura. |
| exchange_rate_spread_percent | decimal string | sempre | Margem da cotação fixada: o valor personalizado na criação ou o padrão da loja se omitido. Aplicada antes do arredondamento para cima; nunca muda nesta fatura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentual imutável de diferença a menor aceita, capturado na criação da fatura. |
| status | invoice status | sempre | new, processing, settled, expired, invalid ou cancelled. |
| amount_status | amount status | sempre | none, partial, paid ou overpaid. Uma fatura de valor zero permitida expressamente é liquidada com none e sem formas de pagamento. |
| timing_status | timing status | sempre | on_time ou late. |
| resolution | resolution | sempre | automatic, manually_settled ou manually_invalidated. |
| sequence | integer | sempre | Sequência monotônica do status da fatura, a partir de 1. |
| winning_payment_intent_id | UUID | null | sempre | Forma de pagamento que resolveu a fatura, quando selecionada. |
| expires_at | RFC 3339 timestamp | sempre | Prazo da cotação/pagamento. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Último limite configurado de monitoramento tardio entre as formas de pagamento. |
| settled_at | timestamp | null | sempre | Hora de liquidação quando liquidada. |
| cancelled_at | timestamp | null | sempre | Hora de cancelamento quando cancelada. |
| archived_at | timestamp | null | sempre | Hora de arquivamento quando arquivada. |
| created_at | RFC 3339 timestamp | sempre | Hora de criação. |
| updated_at | RFC 3339 timestamp | sempre | Hora da última atualização do status. |
Dados adicionais do detalhe da fatura
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| ipn_url | string | null | sempre | Destino IPN efetivo por fatura. Só na resposta ao lojista; omitido no checkout público. |
| redirect_url | string | null | sempre | URL efetiva de sucesso usada após liquidar. |
| cancel_url | string | null | sempre | URL efetiva de retorno quando o checkout termina sem pagamento bem-sucedido. |
| redirect_automatically | boolean | sempre | Se o checkout deve redirecionar automaticamente após o sucesso. |
| checkout_language | string | sempre | Tag efetiva do idioma do checkout. |
| metadata | object | sempre | Metadados do lojista. Nunca retornados no checkout público. |
| payment_intents | PaymentIntent[] | sempre | Formas de pagamento cotadas e status do monitoramento. |
PaymentIntent
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| id | UUID | sempre | Identificador da intenção de pagamento; também usado como intent_id do QR do checkout. |
| payment_rail | onchain | lightning | sempre | Transporte da fatura. Bitcoin na blockchain e Lightning podem compartilhar asset_id; use o ID da intenção mais este campo, não só o símbolo. Difere do payment_rail do scanner do catálogo de ativos. |
| bolt11 | string | null | sempre | Solicitação de pagamento Lightning, caso contrário null. Pague esta solicitação com uma carteira Lightning; nunca envie fundos na blockchain para o hash de pagamento. |
| asset_id | UUID | sempre | Identificador configurado do ativo de pagamento. |
| asset_key | string | sempre | Chave canônica do ativo no estilo CAIP. |
| chain_slug | string | sempre | Identificador de cadeia Wholly Crypto. |
| network | string | sempre | Rede configurada, atualmente mainnet para ativos de pagamento compatíveis. |
| caip_network_id | string | sempre | Identificador canônico da rede CAIP-2. |
| caip_asset_id | string | null | sempre | Identificador canônico CAIP-19 quando registrado. |
| symbol | string | sempre | Símbolo do ativo. |
| asset_decimals | integer | sempre | Precisão em unidades atômicas. Lightning BTC usa 11 (millisatoshis), não os 8 do Bitcoin na blockchain. Cotações são em satoshis inteiros; recebimentos preservam precisão de millisatoshi. |
| status | intent status | sempre | pending, partial, paid, overpaid, expired ou invalid. |
| finality_mode | confirmations | finalized | sempre | Política de finalidade. |
| required_confirmations | integer | sempre | Confirmações exigidas quando aplicável. |
| quote_rate | decimal string | sempre | Unidades do ativo por uma unidade da moeda da fatura, incluindo a margem fixada. Por exemplo, 1.02 USDC por USD. Não é a cotação inversa. |
| quote_details | object | null | sempre | Origem fixada da cotação: reference_rate antes da margem, unrounded_payment_amount, rounding_adjustment, pricing_provider, asset_provider, pricing_fetched_at e asset_fetched_at. Null em faturas antigas; valores históricos não são inventados. |
| expected_amount | decimal string | sempre | Valor exato fixado do ativo a pagar após margem e arredondamento para cima. Desde 4.1.1, stablecoins fiduciárias reconhecidas e verificadas (como USDC, USDT, DAI, USDS, EURC) são arredondadas para cima a no máximo duas casas decimais; 1.321 vira 1.33, nunca 1.32. Este é o valor esperado mesmo com tolerância zero. Outros ativos preservam precisão adaptativa. Faturas existentes nunca são recalculadas. |
| expected_amount_atomic | integer string | sempre | Valor exato na menor unidade do ativo. |
| minimum_payment_amount | decimal string | sempre | Menor valor aceito como pago após aplicar a tolerância da fatura. |
| minimum_payment_amount_atomic | integer string | sempre | Limite aceito exato na menor unidade do ativo. |
| received_amount | decimal string | sempre | Valor observado. |
| received_amount_atomic | integer string | sempre | Valor atômico observado. |
| confirmed_amount | decimal string | sempre | Valor confirmado/final. |
| confirmed_amount_atomic | integer string | sempre | Valor atômico confirmado/final. |
| destination_address | string | sempre | Endereço de recebimento na blockchain ou hash de pagamento de 64 caracteres para Lightning. Use bolt11 para pagar Lightning; o hash não é um endereço Bitcoin. |
| destination_tag | string | null | sempre | Referência pública de pagamento obrigatória se a via usar: tag de destino XRP, ID de memo Stellar ou comentário de fatura TON. Null para vias de endereços únicos. |
| derivation_index | integer | sempre | Índice derivado reservado da carteira; só detalhe do lojista. |
| quote_expires_at | RFC 3339 timestamp | sempre | Vencimento da cotação. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Limite de monitoramento tardio desta forma. |
| next_check_at | timestamp | null | sempre | Próxima verificação programada da rede. |
| last_checked_at | timestamp | null | sempre | Última verificação da rede. |
| last_chain_height | integer | null | sempre | Última altura confiável observada pelo monitor. |
| last_anchor_hash | string | null | sempre | Última âncora/hash de bloco do monitor. |
| last_monitor_error | string | null | sempre | Diagnóstico seguro de monitoramento para operadores. |
| first_payment_at | timestamp | null | sempre | Hora do primeiro pagamento observado. |
| fully_paid_at | timestamp | null | sempre | Hora em que o mínimo aceito foi atingido pela primeira vez. |
| finalized_at | timestamp | null | sempre | Hora em que o pagamento cumpriu a política de finalidade. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": {
"id": "2f798f9f-01f2-42f0-9d10-5581d6116b4c",
"invoice_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50",
"project_id": "11111111-1111-4111-8111-111111111111",
"store_id": "22222222-2222-4222-8222-222222222222",
"source": "api",
"order_id": "order-1042",
"email": "ada@example.com",
"customer_name": "Ada Lovelace · Example GmbH",
"customer_address": "Example GmbH · 12 Example Street · Suite 2 · 10115 Berlin · Germany (DE) · VAT DE123456789",
"description": "Annual plan",
"amount": "49.9",
"currency": "USD",
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"status": "settled",
"amount_status": "paid",
"timing_status": "on_time",
"resolution": "automatic",
"sequence": 4,
"winning_payment_intent_id": "33333333-3333-4333-8333-333333333333",
"expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"settled_at": "2026-08-31T18:05:00Z",
"cancelled_at": null,
"archived_at": null,
"created_at": "2026-08-31T18:00:00Z",
"updated_at": "2026-08-31T18:05:00Z",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"checkout_language": "en",
"metadata": { "cart_id": "cart-681", "firstname": "Ada", "lastname": "Lovelace", "street": "12 Example Street", "street2": "Suite 2", "zip": "10115", "city": "Berlin", "country": "Germany", "countryiso2": "DE", "company": "Example GmbH", "vatid": "DE123456789" },
"payment_intents": [
{
"id": "33333333-3333-4333-8333-333333333333",
"asset_id": "10000000-0000-4000-8000-000000000001",
"asset_key": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"chain_slug": "bitcoin",
"network": "mainnet",
"caip_network_id": "bip122:000000000019d6689c085ae165831e93",
"caip_asset_id": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"symbol": "BTC",
"asset_decimals": 8,
"status": "paid",
"finality_mode": "confirmations",
"required_confirmations": 1,
"quote_rate": "0.000009218",
"quote_details": null,
"expected_amount": "0.00046",
"expected_amount_atomic": "46000",
"minimum_payment_amount": "0.0004554",
"minimum_payment_amount_atomic": "45540",
"received_amount": "0.0004554",
"received_amount_atomic": "45540",
"confirmed_amount": "0.0004554",
"confirmed_amount_atomic": "45540",
"destination_address": "bc1q…example",
"destination_tag": null,
"derivation_index": 42,
"quote_expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"next_check_at": null,
"last_checked_at": "2026-08-31T18:05:00Z",
"last_chain_height": 912345,
"last_anchor_hash": "000000000000000000example",
"last_monitor_error": null,
"first_payment_at": "2026-08-31T18:03:00Z",
"fully_paid_at": "2026-08-31T18:03:00Z",
"finalized_at": "2026-08-31T18:05:00Z"
}
]
},
"links": {
"checkout": "https://pay.example.com/invoice/0a6a98db-d93d-48ee-8c3c-fd45f90c4a50"
}
}GETListar pagamentos de uma fatura/v1/projects/{project_id}/invoices/{invoice_id}/paymentsSomente leitura
Histórico atual completo de transferências, incluindo observações invalidadas. Use quando uma notificação marcar payments_truncated. É o status atual, não uma reconstrução de um evento antigo.
- Uma observação é um log de token, saída UTXO ou outra transferência de via, não necessariamente um hash de transação único. Elimine duplicatas por payment_id; transaction_id mais event_index identifica a transferência na rede.
- status é detected, confirming, final, reorged, replaced ou invalid. Só observações counts_towards_received contribuem para os valores recebidos. Nunca some valores de ativos diferentes.
- Registros Lightning usam payment_hash com transaction_id, confirmations e links de explorador null; a precisão BTC é 11 (millisatoshis). Pré-imagens, BOLT11 e segredos de carteira não são expostos.
- Ordenado por observed_at decrescente e depois payment_id decrescente. Contagem e página usam um retrato de leitura repetível; páginas posteriores podem mudar quando chegam pagamentos. Elimine duplicatas por payment_id ao paginar uma fatura ativa.
- Aplicam-se o escopo de projeto somente leitura, restrições de IP e limites por credencial existentes. Nunca siga um link de notificação com seu token a menos que a origem corresponda ao host de API configurado.
| Cabeçalho | Presença | Regra |
|---|---|---|
| Authorization | obrigatório | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | recomendado | application/json |
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | Projeto atribuído a esta credencial. |
| invoice_id | path UUID | invoice_id público retornado na criação. |
| payment_method_id | optional query UUID | Limita a uma forma de pagamento da fatura. |
| limit | query integer | 1–100; padrão 25. |
| offset | query integer | 0–1,000,000; padrão 0. |
Solicitação
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID/payments?limit=25&offset=0" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID/payments?limit=25&offset=0", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID/payments?limit=25&offset=0");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID/payments?limit=25&offset=0",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"invoice_id": "11111111-2222-4333-8444-555555555555",
"data": [{
"payment_id": "44444444-4444-4444-8444-444444444444",
"payment_method_id": "33333333-3333-4333-8333-333333333333",
"transaction_id": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"payment_hash": null,
"event_index": 12,
"payment_rail": "onchain",
"chain_slug": "ethereum",
"network": "mainnet",
"asset_id": "55555555-5555-4555-8555-555555555555",
"asset_key": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"caip_asset_id": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"symbol": "USDC",
"asset_decimals": 6,
"amount": "58.17342",
"amount_atomic": "58173420",
"status": "final",
"counts_towards_received": true,
"confirmations": 2,
"block_height": 25975377,
"observed_at": "2026-09-14T12:03:00Z",
"chain_time": "2026-09-14T12:02:48Z",
"finalized_at": "2026-09-14T12:04:00Z",
"explorer_name": "Etherscan",
"explorer_url": "https://etherscan.io/tx/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}],
"pagination": {"limit": 25, "offset": 0, "total": 1, "has_more": false}
}GETEstrutura do checkout/Público
Raiz do host de checkout gerenciado que serve o aplicativo de checkout sem selecionar uma fatura. Integrações de clientes normalmente devem usar links.checkout.
- Não precisa de token bearer.
- A entrada do checkout gerenciada permite GET/HEAD e rejeita outros métodos.
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/" \
--output 'checkout.html'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout.html", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout.html", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout.html").write_bytes(response.read())Exemplo de resposta · 200 text/html
<!doctype html>
<!-- Hosted Wholly Crypto checkout shell -->GETPágina de checkout hospedada/invoice/{invoice_id}Público
Página de checkout HTML para clientes. Busca JSON seguro para checkout no mesmo host. A incorporação em quadros é negada a menos que a loja ative e permita expressamente a origem HTTPS pai.
- Token bearer não é aceito nem necessário.
- A estrutura HTML retorna 200 mesmo sem fatura; sua solicitação JSON de checkout então recebe invoice_not_found.
- A resposta é no-store, noindex e tem CSP frame-ancestors específica da fatura.
- Projeto/loja desativado ou fatura desconhecida não expõe dados do checkout.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| invoice_id | path UUID | UUID público da fatura retornado pela API do lojista. |
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/invoice/YOUR_PUBLIC_INVOICE_ID" \
--output 'checkout.html'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/invoice/YOUR_PUBLIC_INVOICE_ID", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout.html", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/invoice/YOUR_PUBLIC_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout.html", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/invoice/YOUR_PUBLIC_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout.html").write_bytes(response.read())Exemplo de resposta · 200 text/html
<!doctype html>
<!-- Hosted Wholly Crypto checkout application -->GETFatura segura para o checkout/checkout-api/invoices/{invoice_id}Público
Retorna só campos necessários para exibir o checkout. Omite deliberadamente IDs internos, email do cliente e campos de endereço derivados, URL IPN, metadados do lojista, IDs de carteiras, caminhos de derivação e diagnósticos do monitor.
- Não precisa de token bearer.
- Cache-Control é no-store e a indexação em buscadores está desativada.
- Trate invoice_id como dado que permite acesso ao cliente; evite publicá-lo desnecessariamente.
- asset_icon_url é um recurso local da mesma origem; o checkout do cliente nunca precisa contatar CoinGecko para exibir.
- Quando destination_tag não for null, exiba e copie junto ao endereço: é uma tag de destino XRP, ID de memo Stellar ou comentário de fatura TON obrigatório e precisa ser enviado exatamente.
- Para tokens verificados, asset_kind é token, contract_address identifica o contrato ERC-20 ou mint SPL exato, token_standard identifica a via e payment_uri contém essa identidade do token.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| invoice_id | path UUID | UUID público da fatura. |
Fatura pública do checkout
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| invoice_id | UUID | sempre | UUID público da fatura. |
| order_id | string | null | sempre | Referência do pedido do lojista. |
| description | string | null | sempre | Descrição visível ao cliente. |
| amount | decimal string | sempre | Valor da fatura. |
| currency | string | sempre | Moeda da fatura. |
| exchange_rate_spread_percent | decimal string | sempre | Margem efetiva da cotação fixada na criação, incluindo personalização por fatura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentual de diferença a menor aceita para esta fatura. |
| status | invoice status | sempre | Status atual da fatura. |
| amount_status | amount status | sempre | none, partial, paid ou overpaid. Uma fatura de valor zero permitida expressamente é liquidada com none e sem formas de pagamento. |
| timing_status | timing status | sempre | on_time ou late. |
| sequence | integer | sempre | Sequência atual do status. |
| active_payment_method_id | UUID | null | sempre | A forma listada que recebeu fundos. O checkout permanece nesta forma para não continuar um pagamento a menor com um ativo incompatível. |
| payment_method_locked | boolean | sempre | True após um pagamento válido selecionar active_payment_method_id. |
| server_time | RFC 3339 timestamp | sempre | Relógio do servidor capturado para esta resposta; use com expires_at para evitar diferenças do relógio do cliente. |
| expires_at | RFC 3339 timestamp | sempre | Prazo da fatura. |
| expires_in_seconds | integer | sempre | Segundos inteiros restantes em server_time, arredondados para cima e com mínimo zero. |
| payment_open | boolean | sempre | True só se uma fatura new ou processing estiver no prazo e tiver pelo menos uma forma pagável com valor restante. |
| redirect_url | string | null | sempre | Destino de retorno do cliente após liquidação bem-sucedida. |
| cancel_url | string | null | sempre | Destino de retorno do cliente ao sair sem liquidação bem-sucedida. |
| redirect_automatically | boolean | sempre | Política de redirecionamento automático. |
| checkout_language | string | sempre | Idioma do checkout. |
| project | object | sempre | name, checkout_title, checkout_description, theme, accent_color e logo_url. |
| store | object | sempre | Nome público da loja. |
| appearance | CheckoutAppearance | sempre | Apresentação efetiva: personalização por fatura fixada se fornecida, ou design atual da loja. Nunca muda campos financeiros nem avisos de segurança. |
| payment_methods | CheckoutPaymentMethod[] | sempre | Formas de pagamento seguras para o checkout. |
CheckoutAppearance
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| inherit_default_store | boolean | sempre | True quando a loja padrão do projeto fornece esta aparência. False para lojas independentes e personalizações de fatura fixadas. |
| invoice_override | boolean | sempre | True se checkout_appearance foi fornecido na criação da fatura. Omitido/null mantém false. |
| title / intro / outro | string | sempre | Título, mensagem superior e inferior do lojista em texto simples. intro substitui customer_message; o texto antigo armazenado é preservado. Nunca interprete como marcação. |
| intro_font_size / outro_font_size | integer | sempre | Tamanhos de fonte em pixels: 12, 14, 16, 18, 20 ou 24. |
| customer_message | string | sempre | Alias de compatibilidade obsoleto de intro. Use intro em integrações novas. |
| theme | system | light | dim | dark | sempre | Preferência do dispositivo do cliente ou tema fixo. |
| accent_color / background_color / card_color / button_color | string | sempre | Cores estritas #RRGGBB. As opcionais ficam vazias para valores automáticos; o contraste do primeiro plano é calculado. |
| logo_size / logo_alignment | string | sempre | small, medium ou large; left ou center. As imagens se ajustam inteiras, sem cortes. |
| images | object | sempre | URLs opcionais logo_light, logo_dark e favicon: imagens PNG normalizadas, restritas ao escopo e da mesma origem. |
| show_order_id / show_description / details_expanded | boolean | sempre | Visibilidade do ID do pedido, descrição abaixo do título e expansão inicial do ID. O valor continua visível; são controles visuais, não ocultação de dados. |
| show_project_name / show_store_name | boolean | sempre | Merchant 5.6.0+: visibilidade dos nomes no cabeçalho. Ambos usam true por padrão. A identidade de projeto/loja continua disponível no JSON. |
| featured_chains / featured_asset_ids | array | sempre | Preferências ordenadas, aplicadas só às formas já presentes na fatura. Formas ausentes ou desativadas são ignoradas. |
| default_asset_id | UUID | null | sempre | Forma inicial sugerida. Uma preferência válida lembrada do cliente ou uma forma que já recebe fundos tem prioridade. |
| messages | object | sempre | Texto simples en/de com chaves waiting, confirming, paid, underpaid e expired. Inglês como alternativa. Complementar; nunca substitui o status real. |
| support_email / support_url / terms_url / privacy_url | string | sempre | Contato e links HTTPS opcionais, sem credenciais na URL. Links externos abrem uma nova janela. |
| return_button_text | string | sempre | Só rótulo opcional. Destinos de sucesso/cancelamento e política de redirecionamento continuam pertencendo à fatura. |
CheckoutPaymentMethod
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| payment_rail | onchain | lightning | sempre | Lightning continua sendo uma forma Bitcoin, separada de BTC na blockchain. Identifique a opção pelo ID da intenção e pela via, não só asset_id. |
| bolt11 | string | null | sempre | Solicitação Lightning assinada; null para formas na blockchain. Nunca pague depois que payable passar a false. |
| payment_hash | string | null | sempre | Hash de pagamento Lightning para conciliação, não endereço de recebimento. Null para formas na blockchain. |
| id | UUID | sempre | Identificador da intenção de pagamento. |
| asset_id | UUID | sempre | UUID do ativo usado pelas preferências de aparência; diferente do ID da intenção de pagamento desta fatura. |
| asset_key | string | sempre | Chave canônica do ativo. |
| chain_slug / chain_name | string | sempre | Nomes da rede para máquina e exibição. |
| network | string | sempre | Rede de pagamento. |
| caip_network_id | string | sempre | Identidade canônica da rede para distinguir a rede escolhida. |
| caip_asset_id | string | null | sempre | Identidade canônica exata do ativo, incluindo contrato de token ou mint verificado quando aplicável. |
| asset_name / symbol | string | sempre | Valores visuais do ativo de pagamento. |
| asset_icon_url | string | null | sempre | Ícone do ativo em cache local da mesma origem, ou null sem correspondência CoinGecko verificada. |
| asset_kind | native | token | sempre | Distingue moeda nativa de pagamento por contrato/mint. |
| contract_address | string | null | sempre | Contrato ERC-20 ou mint SPL canônico para tokens; null para moeda nativa. |
| token_standard | erc20 | spl-token | null | sempre | Implementação verificada do token, ou null para moeda nativa. |
| asset_decimals | integer | sempre | Precisão atômica: 11 para millisatoshis Lightning BTC, 8 para satoshis BTC na blockchain. |
| status | intent status | sempre | Status atual da forma de pagamento. |
| payable | boolean | sempre | True só se esta forma exata puder aceitar pagamentos agora; false para formas inativas depois que outro ativo receber fundos. |
| finality_mode / required_confirmations | string / integer | sempre | Política de finalidade. |
| expected_amount / expected_amount_atomic | decimal / integer string | sempre | Cotação total fixada em unidades visíveis e reais na blockchain. Stablecoins fiduciárias reconhecidas usam no máximo duas casas decimais na cotação, sempre para cima após a margem; outros ativos usam precisão adaptativa. Decimais reais dos tokens, fundos recebidos e restos de pagamentos parciais continuam exatos. Use os valores retornados sem alterar. |
| minimum_payment_amount / minimum_payment_amount_atomic | decimal / integer string | sempre | Limite de liquidação aceito após aplicar a tolerância de pagamento a menor. |
| received_amount / received_amount_atomic | decimal / integer string | sempre | Valor observado. |
| remaining_amount | decimal string | sempre | Valor visível exato que falta para atingir o limite aceito, com mínimo zero. |
| remaining_amount_atomic | integer string | sempre | Diferença até o limite aceito em unidades atômicas. Não é o valor solicitado: a tolerância só afeta a aceitação. |
| confirmed_amount / confirmed_amount_atomic | decimal / integer string | sempre | Valor confirmado/final. |
| destination_address / destination_tag | string / string|null | sempre | Destino na blockchain e referência opcional. Para Lightning é o hash de pagamento sem tag; pague por bolt11/payment_uri. |
| quote_expires_at | RFC 3339 timestamp | sempre | Vencimento da cotação. |
| payment_uri | string | null | sempre | Solicitação adaptada à rede: ERC-681, Solana Pay, URI nativa ou lightning:<bolt11>. Solicitações com valor usam o esperado completo menos fundos recebidos, nunca o limite de tolerância. Null quando payable é false, inclusive após aceitar uma diferença tolerada. O QR Lightning codifica a solicitação Lightning completa, não o hash do pagamento. |
| qr_url | path | null | sempre | Caminho QR SVG da mesma origem com revisão por sequência e restante exato, ou null quando payable é false. O SVG é no-store. |
| address_explorer_name / address_explorer_url | string|null | sempre | Explorador alternativo validado da rede principal onde compatível. |
| transaction_count | integer | sempre | Total de transações públicas válidas distintas observadas para esta forma. |
| transactions_truncated | boolean | sempre | True quando transaction_count supera a lista retornada de transações recentes. |
| transactions | CheckoutTransaction[] | sempre | Até as 10 transações públicas válidas mais recentes. Totais exatos recebidos continuam independentes deste limite visual. |
CheckoutTransaction
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| transaction_id | string | sempre | Identificador da transação observada. |
| status | detected | confirming | final | sempre | Status público da observação. |
| confirmations | integer | sempre | Número observado de confirmações. |
| block_height | integer | null | sempre | Altura observada de bloco/registro. |
| explorer_name | string | quando retornado | Nome fixo validado do explorador. |
| explorer_url | string | quando retornado | URL fixa validada do explorador da rede principal. |
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const response = await fetch("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID", {
method: "GET",
headers: {
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
request = Request("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": {
"invoice_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50",
"order_id": "order-1042",
"description": "Annual plan",
"amount": "49.9",
"currency": "USD",
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"status": "processing",
"amount_status": "partial",
"timing_status": "on_time",
"sequence": 3,
"active_payment_method_id": "33333333-3333-4333-8333-333333333333",
"payment_method_locked": true,
"server_time": "2026-08-31T18:10:00Z",
"expires_at": "2026-08-31T18:15:00Z",
"expires_in_seconds": 300,
"payment_open": true,
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"checkout_language": "en",
"project": {
"name": "Example project",
"checkout_title": "Complete your payment",
"checkout_description": "Send the exact amount shown.",
"theme": "system",
"accent_color": "#42e39b",
"logo_url": "/checkout-api/invoices/…/logo/…/image.png"
},
"store": { "name": "Online shop" },
"payment_methods": [
{
"id": "33333333-3333-4333-8333-333333333333",
"asset_key": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"chain_slug": "bitcoin",
"chain_name": "Bitcoin",
"network": "mainnet",
"caip_network_id": "bip122:000000000019d6689c085ae165831e93",
"caip_asset_id": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"asset_name": "Bitcoin",
"symbol": "BTC",
"asset_icon_url": "/assets/coingecko/bitcoin.png",
"asset_kind": "native",
"contract_address": null,
"token_standard": null,
"asset_decimals": 8,
"status": "partial",
"payable": true,
"finality_mode": "confirmations",
"required_confirmations": 1,
"expected_amount": "0.00046",
"expected_amount_atomic": "46000",
"minimum_payment_amount": "0.0004554",
"minimum_payment_amount_atomic": "45540",
"received_amount": "0.0002",
"received_amount_atomic": "20000",
"remaining_amount": "0.0002554",
"remaining_amount_atomic": "25540",
"confirmed_amount": "0",
"confirmed_amount_atomic": "0",
"destination_address": "bc1q…example",
"destination_tag": null,
"quote_expires_at": "2026-08-31T18:15:00Z",
"payment_uri": "bitcoin:bc1q…example?amount=0.00026",
"qr_url": "/checkout-api/invoices/…/payment-methods/…/qr.svg?sequence=3&amount_atomic=26000",
"address_explorer_name": "mempool.space",
"address_explorer_url": "https://mempool.space/address/…",
"transaction_count": 0,
"transactions_truncated": false,
"transactions": []
}
]
}
}GETPrévia do checkout da loja/invoice/preview/{project_id}Público
Exibe a aparência salva da loja com valor ilustrativo e metadados reais de ativos aceitos. Alterne entre exemplos waiting, confirming, paid, underpaid e expired sem criar pagamentos.
- A prévia é só de marca e nunca deve ser enviada a um cliente como solicitação de pagamento.
- Sem endereço de recebimento, QR pagável, ação de carteira, redirecionamento nem consulta periódica de pagamentos. Exemplos não mudam o status real de faturas.
- A resposta é no-store, noindex e não pode ser incorporada em quadros.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | UUID do projeto copiado para o link da prévia pelo console autenticado. |
| store_id | query UUID, optional | Loja deste projeto. Omita para usar a primeira loja/padrão. |
| state | query string, optional | waiting, confirming, paid, underpaid ou expired. Ilustração só no navegador. |
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/invoice/preview/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID&state=confirming" \
--output 'checkout-preview.html'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/invoice/preview/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID&state=confirming", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout-preview.html", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/invoice/preview/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID&state=confirming");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout-preview.html", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/invoice/preview/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID&state=confirming",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout-preview.html").write_bytes(response.read())Exemplo de resposta · 200 text/html
<!doctype html>
<!-- Hosted branding preview; no invoice is created -->GETDados da prévia do checkout/checkout-api/previews/{project_id}Público
Retorna a aparência efetiva da loja e metadados seguros de ativos aceitos. payment_methods continua vazio; preview_methods não contém endereços de pagamento, cotações nem dados privados de carteira.
- Token bearer não é aceito nem necessário.
- Não retorna fatura, destino, carteira, transação, IPN, webhook nem metadados de lojista.
- Use o console autenticado para obter o link correto da prévia no domínio de pagamento.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | UUID do projeto no link da prévia do console. |
| store_id | query UUID, optional | Precisa pertencer a este projeto; IDs não correspondentes retornam 404. Campos de consulta desconhecidos são rejeitados. |
CheckoutAppearance
| Campo | Tipo | Presença | Descrição |
|---|---|---|---|
| inherit_default_store | boolean | sempre | True quando a loja padrão do projeto fornece esta aparência. False para lojas independentes e personalizações de fatura fixadas. |
| invoice_override | boolean | sempre | True se checkout_appearance foi fornecido na criação da fatura. Omitido/null mantém false. |
| title / intro / outro | string | sempre | Título, mensagem superior e inferior do lojista em texto simples. intro substitui customer_message; o texto antigo armazenado é preservado. Nunca interprete como marcação. |
| intro_font_size / outro_font_size | integer | sempre | Tamanhos de fonte em pixels: 12, 14, 16, 18, 20 ou 24. |
| customer_message | string | sempre | Alias de compatibilidade obsoleto de intro. Use intro em integrações novas. |
| theme | system | light | dim | dark | sempre | Preferência do dispositivo do cliente ou tema fixo. |
| accent_color / background_color / card_color / button_color | string | sempre | Cores estritas #RRGGBB. As opcionais ficam vazias para valores automáticos; o contraste do primeiro plano é calculado. |
| logo_size / logo_alignment | string | sempre | small, medium ou large; left ou center. As imagens se ajustam inteiras, sem cortes. |
| images | object | sempre | URLs opcionais logo_light, logo_dark e favicon: imagens PNG normalizadas, restritas ao escopo e da mesma origem. |
| show_order_id / show_description / details_expanded | boolean | sempre | Visibilidade do ID do pedido, descrição abaixo do título e expansão inicial do ID. O valor continua visível; são controles visuais, não ocultação de dados. |
| show_project_name / show_store_name | boolean | sempre | Merchant 5.6.0+: visibilidade dos nomes no cabeçalho. Ambos usam true por padrão. A identidade de projeto/loja continua disponível no JSON. |
| featured_chains / featured_asset_ids | array | sempre | Preferências ordenadas, aplicadas só às formas já presentes na fatura. Formas ausentes ou desativadas são ignoradas. |
| default_asset_id | UUID | null | sempre | Forma inicial sugerida. Uma preferência válida lembrada do cliente ou uma forma que já recebe fundos tem prioridade. |
| messages | object | sempre | Texto simples en/de com chaves waiting, confirming, paid, underpaid e expired. Inglês como alternativa. Complementar; nunca substitui o status real. |
| support_email / support_url / terms_url / privacy_url | string | sempre | Contato e links HTTPS opcionais, sem credenciais na URL. Links externos abrem uma nova janela. |
| return_button_text | string | sempre | Só rótulo opcional. Destinos de sucesso/cancelamento e política de redirecionamento continuam pertencendo à fatura. |
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const response = await fetch("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID", {
method: "GET",
headers: {
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
request = Request("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Exemplo de resposta · 200 application/json
{
"data": {
"preview": true,
"invoice_id": "YOUR_PROJECT_ID",
"amount": "100.00",
"currency": "USD",
"project": {
"name": "Example project",
"checkout_title": "Complete your payment",
"checkout_description": "Choose a network and send the exact amount shown.",
"theme": "system",
"accent_color": "#42e39b",
"logo_url": "/checkout-api/previews/…/logo/…/image.png"
},
"appearance": {"inherit_default_store": true, "theme": "system", "accent_color": "#42E39B", "images": {}},
"preview_methods": [],
"payment_methods": []
}
}GETImagem do checkout da loja/checkout-api/invoices/{invoice_id}/appearance-images/{kind}/{revision}/image.pngPúblico
Retorna um logo ou favicon normalizado da loja pertencente a esta fatura. Use as URLs appearance.images dos dados do checkout.
- Use appearance.images do JSON do checkout. Imagens de fatura fixadas continuam funcionando depois que a loja de origem substituir ou remover uma imagem enviada. Revisões removidas expressamente, de outra fatura, tipo errado ou desconhecidas retornam 404; um retrato nunca recorre a uma imagem atual da loja.
- Sem personalização da fatura, é usada a imagem efetiva atual da loja e revisões substituídas/removidas retornam 404. Só PNG, nosniff e cache privado.
- Envios de imagens da loja aceitam PNG, JPEG ou WebP limitados no console autenticado; nunca SVG, HTML nem URLs remotas de imagens.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| invoice_id | path UUID | UUID público da fatura. |
| kind | path enum | logo_light, logo_dark ou favicon. |
| revision | path UUID | Revisão atual da imagem. |
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png" \
--output 'store-logo.png'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("store-logo.png", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("store-logo.png", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("store-logo.png").write_bytes(response.read())Exemplo de resposta · 200 image/png
(binary PNG response)GETImagem da prévia da loja/checkout-api/previews/{project_id}/stores/{store_id}/appearance-images/{kind}/{revision}/image.pngPúblico
Retorna uma imagem normalizada da prévia só para projeto, loja, tipo e revisão atual correspondentes.
- Use appearance.images dos dados da prévia. IDs desconhecidos ou não correspondentes retornam 404. Informações de carteiras e pagamentos não são expostas.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | UUID do projeto. |
| store_id | path UUID | Loja pertencente ao projeto. |
| kind | path enum | logo_light, logo_dark ou favicon. |
| revision | path UUID | Revisão atual da imagem. |
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png" \
--output 'store-preview-logo.png'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("store-preview-logo.png", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("store-preview-logo.png", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("store-preview-logo.png").write_bytes(response.read())Exemplo de resposta · 200 image/png
(binary PNG response)GETLogo da prévia com versão/checkout-api/previews/{project_id}/logo/{revision}/image.pngPúblico
Retorna o logo normalizado do projeto só se projeto e revisão do logo segura para cache corresponderem. Use project.logo_url da prévia em vez de construir esta URL.
- Projetos desconhecidos e revisões antigas de logo retornam invoice_not_found sem revelar qual componente faltava.
- A imagem com revisão correta é imutável e pode ser armazenada em cache.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| project_id | path UUID | UUID do projeto. |
| revision | path UUID | Revisão atual do logo do checkout retornada em project.logo_url. |
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/logo/YOUR_LOGO_REVISION/image.png" \
--output 'checkout-preview-logo.png'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/logo/YOUR_LOGO_REVISION/image.png", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout-preview-logo.png", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/logo/YOUR_LOGO_REVISION/image.png");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout-preview-logo.png", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/logo/YOUR_LOGO_REVISION/image.png",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout-preview-logo.png").write_bytes(response.read())Exemplo de resposta · 200 image/png
(binary PNG response)GETImagem QR de pagamento/checkout-api/invoices/{invoice_id}/payment-methods/{intent_id}/qr.svgPúblico
Gera um QR SVG de 512×512 com os dados de pagamento exatos adaptados à rede para uma forma da fatura.
- Não precisa de token bearer.
- Use qr_url com revisão de sequência e restante retornada pelo JSON do checkout; o SVG é privado e no-store.
- Após um pagamento parcial, solicita o restante exato e continua fixado nesse ativo.
- Retorna 409 após vencimento, conclusão ou se outra forma estiver ativa; retorna payment_qr_unavailable (422) se a solicitação for grande demais para codificar.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| invoice_id | path UUID | UUID público da fatura. |
| intent_id | path UUID | ID da forma de pagamento no JSON do checkout. |
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/payment-methods/YOUR_INTENT_ID/qr.svg" \
--output 'payment-qr.svg'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/payment-methods/YOUR_INTENT_ID/qr.svg", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("payment-qr.svg", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/payment-methods/YOUR_INTENT_ID/qr.svg");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("payment-qr.svg", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/payment-methods/YOUR_INTENT_ID/qr.svg",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("payment-qr.svg").write_bytes(response.read())Exemplo de resposta · 200 image/svg+xml
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">…</svg>GETLogo do checkout com versão/checkout-api/invoices/{invoice_id}/logo/{revision}/image.pngPúblico
Retorna o logo normalizado do checkout do projeto só se fatura e revisão atual do logo corresponderem. Prefira project.logo_url do JSON do checkout em vez de construir esta rota.
- Não precisa de token bearer.
- A duração do cache público é de um ano com immutable porque a revisão identifica o estado pelo conteúdo.
- Revisões desconhecidas/não correspondentes retornam invoice_not_found.
| Parâmetro | Tipo / localização | Regra |
|---|---|---|
| invoice_id | path UUID | UUID público da fatura. |
| revision | path UUID | Revisão atual do logo do checkout incluída em project.logo_url. |
Solicitação
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/logo/YOUR_LOGO_REVISION/image.png" \
--output 'checkout-logo.png'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/logo/YOUR_LOGO_REVISION/image.png", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout-logo.png", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/logo/YOUR_LOGO_REVISION/image.png");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout-logo.png", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/logo/YOUR_LOGO_REVISION/image.png",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout-logo.png").write_bytes(response.read())Exemplo de resposta · 200 image/png
(binary PNG response)Referência para Wholly Crypto 7.5.5. Para sua versão instalada, abra Configurações → Acesso à API → Documentação no console. Ver versões.