Documentação da API
Ligue o Cryptanio à sua aplicação.
Contrato v1 / 1.0.0 62 / 2026-10-10
Conteúdo
Começar
A API destina-se ao servidor. Configure primeiro projetos, serviços e conjuntos de endereços no Account; a API executa neles operações autorizadas pela chave.
- Verifique a conta e ative o serviço necessário em Os meus serviços.
- Crie uma chave API com permissões mínimas e mantenha-a no servidor.
- Escolha a tarefa: monitorizar um endereço, criar uma fatura ou receber eventos.
Os exemplos executam operações reais em dados live. Criar ou eliminar objetos pode consumir quotas do serviço. As chaves nunca são incorporadas nos exemplos; são lidas de variáveis de ambiente.
Instruções para o seu agente
Cole esta instrução no chat do agente ou regras do projeto. Ajuda-o a conhecer serviços, preços e referência e depois escrever uma integração pela API REST ou SDK. O agente pode responder no seu idioma.
Learn how to integrate a server-side application with Cryptanio's public REST API before proposing or writing integration code.
Start here:
- Website: https://cryptanio.com/
- Documentation index: https://cryptanio.com/llms.txt
- Product, services and pricing: https://cryptanio.com/cryptanio.md
- API integration index: https://cryptanio.com/docs/llms.txt
- API guide: https://cryptanio.com/docs.md
- Exact API v1 contract: https://cryptanio.com/openapi.json
- FAQ: https://cryptanio.com/faq/index.md
- SDK setup and examples: https://cryptanio.com/docs.html#sdk
Read the relevant method reference before using it. Use the public API at https://api.cryptanio.com/v1. Do not assume a public sandbox exists.
Use HTTP requests or the documented SDK for the application's language. Match request fields, response schemas and errors to OpenAPI; do not invent endpoints or SDK methods.
Check the actual enabled networks, service, project and API key permissions. Product documentation is not evidence of a live payment, balance or deployment.
Keep API keys in the user's server environment or secret store. Never ask the user to paste a key into chat or put it in a URL, browser bundle or log.
Default to reading. Create, change or delete objects only when the user authorizes the specific task. Explain quota or billing consequences before a write.
Follow the documented confirmation, webhook verification, pagination and retry rules. Reuse an invoice's original Idempotency-Key and request when retrying.
Show the returned chain, asset, address, amount and expiry. Preserve payment_route.kind and its exact string value: destination_tag, memo, comment or reference is mandatory when present. The merchant order reference is a separate field.
For a payment QR use the returned payment_uri when present. A Solana Pay reference is not a wallet memo; do not rebuild its URI or replace it with a plain address. Offer the exact returned checkout_url only to the payer; never construct a checkout URL from an invoice ID. Neither a checkout redirect nor a QR scan confirms payment.
Distinguish pending, confirmed, rolled-back and simulated data. If a fact is unavailable, say so instead of guessing.
Answer the user's questions in their language. If a necessary product decision is not documented, ask instead of inventing it.Este bloco não contém chaves nem dados de conta. Use uma chave API com permissões mínimas para a integração e mantenha-a no servidor da aplicação. Não partilhe a chave no chat do agente.
Referência da API
Métodos: 16
Monitorizações Monitoring
Eventos
Pagamentos
Faturas
Webhook
Estado da API
Autenticação
Envie Authorization: Bearer <API_KEY> do servidor. Crie uma chave para dados live em Account → Chaves API, limitada aos projetos e operações necessários. Não é oferecida atualmente uma sandbox pública.
Uma chave pode permitir apenas leitura, operações selecionadas, um projeto ou serviço e intervalos IP/CIDR específicos. A validade, suspensão e revogação também afetam o acesso. Para rodar a chave, crie uma substituta com restrições adequadas, altere a integração e revogue a antiga. Nunca incorpore chaves em código do navegador, aplicações móveis ou URLs.
| Âmbito | Finalidade |
|---|---|
watches | Monitorização |
events | Eventos e pagamentos |
invoices | Faturas Payments |
webhooks | Endpoints webhook |
stream | SSE |
Restrições aplicam-se em conjunto, não em substituição. 401 indica chave não aceite; 403 pode indicar permissões insuficientes ou IP não permitido. Um recurso inacessível pode devolver 404. Consulte erros e repetições.
SDK: clientes prontos a usar
Transfira o ficheiro da sua linguagem, guarde-o no servidor da aplicação e passe a chave ao construtor. Cada cliente cobre todos os 16 métodos desta referência. Não há pacote npm, PyPI, Composer ou crates.io para instalar: os ficheiros transferidos são o SDK.
| Idioma | Requisitos | Ficheiro |
|---|---|---|
| Node.js | Node.js 18+ | cryptanio.mjs |
| Python | Python 3.10+ | cryptanio.py |
| PHP | PHP 8.1+ / cURL | Cryptanio.php |
| Go | Go 1.22+ | cryptanio.go |
| Rust | Rust 2021 / reqwest 0.12 / serde_json 1 | cryptanio.rs |
Os ficheiros partilham a versão 1.0.0 62. O manifesto contém versão, contrato e SHA-256 de cada ficheiro. Defina primeiro CRYPTANIO_API_KEY e CRYPTANIO_PROJECT_ID no ambiente. Os exemplos só leem eventos, mas acedem a dados live da conta.
Node.js
Guarde o exemplo como .mjs junto de cryptanio.mjs.
import { Cryptanio } from './cryptanio.mjs';
const key = process.env.CRYPTANIO_API_KEY;
const project = process.env.CRYPTANIO_PROJECT_ID;
if (!key || !project) throw new Error('Set API key and project ID');
const api = new Cryptanio(key);
const page = await api.listEvents({ project, limit: 25 });
console.log(page);Para consultar as instruções de pagamento de uma fatura existente, defina o ID em CRYPTANIO_RESOURCE_ID. O exemplo preserva o payment_route obrigatório, o payment_uri retornado para o QR e o checkout_url exato. Envie o resultado ao pagador pela sua interface; não o publique em registros públicos. Os valores continuam como strings e os campos ausentes são omitidos.
import { Cryptanio } from './cryptanio.mjs';
const key = process.env.CRYPTANIO_API_KEY;
const invoiceID = process.env.CRYPTANIO_RESOURCE_ID;
if (!key || !invoiceID) throw new Error('Set API key and invoice ID');
const api = new Cryptanio(key);
const invoice = await api.getInvoice(invoiceID);
const payment = {
chain: invoice.chain, asset: invoice.asset, address: invoice.address,
amount: invoice.amount, expires_at: invoice.expires_at,
payment_route: invoice.payment_route,
payment_uri: invoice.payment_uri, checkout_url: invoice.checkout_url,
};
console.log(JSON.stringify(payment, null, 2));Python
Guarde o exemplo como .py junto de cryptanio.py.
import os
from cryptanio import Cryptanio
api = Cryptanio(os.environ['CRYPTANIO_API_KEY'])
page = api.list_events({
'project': os.environ['CRYPTANIO_PROJECT_ID'], 'limit': 25
})
print(page)Para consultar as instruções de pagamento de uma fatura existente, defina o ID em CRYPTANIO_RESOURCE_ID. O exemplo preserva o payment_route obrigatório, o payment_uri retornado para o QR e o checkout_url exato. Envie o resultado ao pagador pela sua interface; não o publique em registros públicos. Os valores continuam como strings e os campos ausentes são omitidos.
import json
import os
from cryptanio import Cryptanio
api = Cryptanio(os.environ['CRYPTANIO_API_KEY'])
invoice = api.get_invoice(os.environ['CRYPTANIO_RESOURCE_ID'])
payment = {field: invoice[field] for field in (
'chain', 'asset', 'address', 'amount', 'expires_at',
'payment_route', 'payment_uri', 'checkout_url'
) if field in invoice}
print(json.dumps(payment, ensure_ascii=False))PHP
Guarde o exemplo como .php junto de Cryptanio.php; ative a extensão cURL.
<?php
require __DIR__ . '/Cryptanio.php';
$key = getenv('CRYPTANIO_API_KEY');
$project = getenv('CRYPTANIO_PROJECT_ID');
if (!$key || !$project) throw new RuntimeException('Set API key and project ID');
$api = new Cryptanio($key);
$page = $api->listEvents(['project' => $project, 'limit' => 25]);
echo json_encode($page, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);Para consultar as instruções de pagamento de uma fatura existente, defina o ID em CRYPTANIO_RESOURCE_ID. O exemplo preserva o payment_route obrigatório, o payment_uri retornado para o QR e o checkout_url exato. Envie o resultado ao pagador pela sua interface; não o publique em registros públicos. Os valores continuam como strings e os campos ausentes são omitidos.
<?php
require __DIR__ . '/Cryptanio.php';
$key = getenv('CRYPTANIO_API_KEY');
$invoiceID = getenv('CRYPTANIO_RESOURCE_ID');
if (!$key || !$invoiceID) throw new RuntimeException('Set API key and invoice ID');
$api = new Cryptanio($key);
$invoice = $api->getInvoice($invoiceID);
$payment = array_intersect_key($invoice, array_fill_keys([
'chain', 'asset', 'address', 'amount', 'expires_at',
'payment_route', 'payment_uri', 'checkout_url'
], true));
echo json_encode($payment, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);Go
Coloque o ficheiro num diretório cryptanio/ do módulo Go. O exemplo usa o módulo example.local/myapp; substitua o caminho de importação pelo seu módulo. Não exige dependências externas.
package main
import (
"context"
"fmt"
"os"
"example.local/myapp/cryptanio"
)
func main() {
key, project := os.Getenv("CRYPTANIO_API_KEY"), os.Getenv("CRYPTANIO_PROJECT_ID")
if key == "" || project == "" { panic("Set API key and project ID") }
api, err := cryptanio.New(key, "", 0)
if err != nil { panic(err) }
page, err := api.ListEvents(context.Background(), map[string]any{
"project": project, "limit": 25,
})
if err != nil { panic(err) }
fmt.Println(page)
}Para consultar as instruções de pagamento de uma fatura existente, defina o ID em CRYPTANIO_RESOURCE_ID. O exemplo preserva o payment_route obrigatório, o payment_uri retornado para o QR e o checkout_url exato. Envie o resultado ao pagador pela sua interface; não o publique em registros públicos. Os valores continuam como strings e os campos ausentes são omitidos.
package main
import (
"context"
"encoding/json"
"os"
"example.local/myapp/cryptanio"
)
func main() {
key, invoiceID := os.Getenv("CRYPTANIO_API_KEY"), os.Getenv("CRYPTANIO_RESOURCE_ID")
if key == "" || invoiceID == "" { panic("Set API key and invoice ID") }
api, err := cryptanio.New(key, "", 0)
if err != nil { panic(err) }
invoice, err := api.GetInvoice(context.Background(), invoiceID)
if err != nil { panic(err) }
payment := make(map[string]any)
for _, field := range []string{
"chain", "asset", "address", "amount", "expires_at",
"payment_route", "payment_uri", "checkout_url",
} {
if value, ok := invoice[field]; ok { payment[field] = value }
}
if err := json.NewEncoder(os.Stdout).Encode(payment); err != nil { panic(err) }
}Rust
Guarde o ficheiro como src/cryptanio.rs e o exemplo como src/main.rs. Adicione as dependências abaixo a Cargo.toml. Cliente síncrono bloqueante: não o execute diretamente num worker assíncrono.
[dependencies]
reqwest = { version = "=0.12.24", default-features = false, features = ["blocking", "json", "rustls-tls"] }
serde_json = "=1.0.145"mod cryptanio;
use cryptanio::Cryptanio;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let api = Cryptanio::new(&std::env::var("CRYPTANIO_API_KEY")?)?;
let project = std::env::var("CRYPTANIO_PROJECT_ID")?;
let page = api.list_events(&[("project", &project), ("limit", "25")])?;
println!("{page}");
Ok(())
}Para consultar as instruções de pagamento de uma fatura existente, defina o ID em CRYPTANIO_RESOURCE_ID. O exemplo preserva o payment_route obrigatório, o payment_uri retornado para o QR e o checkout_url exato. Envie o resultado ao pagador pela sua interface; não o publique em registros públicos. Os valores continuam como strings e os campos ausentes são omitidos.
mod cryptanio;
use cryptanio::Cryptanio;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let api = Cryptanio::new(&std::env::var("CRYPTANIO_API_KEY")?)?;
let invoice = api.get_invoice(&std::env::var("CRYPTANIO_RESOURCE_ID")?)?;
let mut payment = serde_json::Map::new();
for field in ["chain", "asset", "address", "amount", "expires_at",
"payment_route", "payment_uri", "checkout_url"] {
if let Some(value) = invoice.get(field) {
payment.insert(field.to_owned(), value.clone());
}
}
println!("{}", serde_json::Value::Object(payment));
Ok(())
}Chamar métodos
Node.js e PHP usam listEvents, Python e Rust list_events, Go ListEvents. Cada método corresponde a um pedido na referência. Filtros são objeto/mapa, ou lista de pares em Rust. Métodos Go recebem sempre context.Context primeiro. Filtros desconhecidos são rejeitados antes do envio.
| Método Node.js / PHP | HTTP |
|---|---|
createWatch | POST /v1/watches |
listWatches | GET /v1/watches |
getWatch | GET /v1/watches/{id} |
deleteWatch | DELETE /v1/watches/{id} |
listEvents | GET /v1/events |
listPayments | GET /v1/payments |
createInvoice | POST /v1/invoices |
listInvoices | GET /v1/invoices |
getInvoice | GET /v1/invoices/{id} |
cancelInvoice | POST /v1/invoices/{id}/cancel |
createEndpoint | POST /v1/endpoints |
listEndpoints | GET /v1/endpoints |
deleteEndpoint | DELETE /v1/endpoints/{id} |
streamEvents | GET /v1/stream |
getHealth | GET /v1/health |
listChains | GET /v1/chains |
Criar monitorizações e webhooks exige corpo do pedido. Métodos de um recurso recebem o ID em cadeia. createInvoice(body, idempotencyKey) exige chave de 16–80 caracteres ASCII: letras, dígitos, hífenes ou sublinhados. Reutilize chave e corpo nas repetições; SDK não gera nova chave nem repete automaticamente. Consulte corpos, erros e permissões em cada método.
Respostas, erros e streaming
Respostas JSON mantêm nomes dos campos; IDs, montantes e cursores continuam cadeias. Paginação manual conforme cada método. Erros expõem estado HTTP, code, message e Retry-After. Sem resposta HTTP, status é 0 (Rust usa None). Não segue redirecionamentos. Respostas JSON normais limitadas a 8 MiB. Tempo limite predefinido de 30 segundos, configurável até 300. Em Python aplica-se a uma operação de rede; nos restantes clientes, ao pedido completo, incluindo streaming. Exceder o tempo limite não prova que a escrita não foi executada.
| Linguagem / erro | Campos |
|---|---|
| Node.js / Python: ApiError | status, code, message, retry_after |
| PHP: CryptanioError | status, apiCode, getMessage(), retryAfter |
| Go: *APIError | Status, Code, Message, RetryAfter |
| Rust: ApiError | status: Option<u16>, code, message, retry_after |
streamEvents expõe bytes SSE originais sem reconexão ou análise de eventos: Node.js ReadableStream, resposta HTTP Python fechável, Go io.ReadCloser ou Rust blocking::Response. Liberte o fluxo quando desnecessário. PHP passa blocos a callback; devolva false para parar. Streaming não substitui webhooks nem garante repetição de histórico; consulte regras SSE.
A sua primeira monitorização
No Account, crie um projeto com Monitoring ativo e uma chave com permissões watches e events. Conclua a verificação da conta quando exigida. Copie o ID das definições; adicione apenas um endereço que queira efetivamente monitorizar.
Defina CRYPTANIO_API_KEY e CRYPTANIO_PROJECT_ID no ambiente do servidor. Substitua o endereço de exemplo pelo seu. Este pedido associa uma monitorização real; não simula uma transação.
#!/usr/bin/env bash
set -euo pipefail
: "${CRYPTANIO_API_KEY:?Set CRYPTANIO_API_KEY}"
: "${CRYPTANIO_PROJECT_ID:?Set CRYPTANIO_PROJECT_ID}"
json_string() {
local text="$1" char code index output='"'
for ((index=0; index<${#text}; index++)); do
char="${text:index:1}"
case "$char" in
'"'|'\') output+="\\$char" ;;
*) printf -v code '%d' "'$char"
if ((code < 32)); then printf -v char '\\u%04x' "$code"; fi
output+="$char" ;;
esac
done
printf '%s"' "$output"
}
response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT
transfer_status=0
http_status="$(curl \
--silent --show-error --max-time 30 --request POST \
--header "Authorization: Bearer $CRYPTANIO_API_KEY" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-binary '{"project":'"$(json_string "$CRYPTANIO_PROJECT_ID")"',"chain":"ethereum","address":"0x52908400098527886E0F7030069857D2E4169EE7","asset":"native","label":"Treasury"}' \
"https://api.cryptanio.com/v1/watches" \
--output "$response_file" --write-out '%{http_code}')" || transfer_status=$?
if [[ "$http_status" == 2?? ]]; then
cat "$response_file"
exit "$transfer_status"
fi
printf 'HTTP %s\n' "$http_status" >&2
cat "$response_file" >&2
exit 1
Uma resposta bem-sucedida contém id: guarde-o. GET /v1/watches/{id} devolve a monitorização e as definições, não pagamentos. Para consultar atividade detetada, solicite GET /v1/events com o filtro watch_id. É normal a lista estar vazia antes de se detetar uma transação. Resposta, erros e exemplos noutras linguagens.
Faturas Payments
Ative Payments no projeto e adicione endereços suportados ao respetivo conjunto no Account. Envie project explicitamente, em vez de depender do projeto predefinido. amount é uma cadeia em unidades do criptoativo; invoice_currency/invoice_amount descrevem separadamente o preço cotado da encomenda.
Defina CRYPTANIO_PROJECT_ID como o projeto Payments e CRYPTANIO_API_KEY como chave com permissão invoices. CRYPTANIO_OPERATION_ID deve ser único para nova encomenda; reutilize-o ao repetir a mesma operação. A fatura é criada em dados live.
#!/usr/bin/env bash
set -euo pipefail
: "${CRYPTANIO_API_KEY:?Set CRYPTANIO_API_KEY}"
: "${CRYPTANIO_OPERATION_ID:?Set CRYPTANIO_OPERATION_ID}"
: "${CRYPTANIO_PROJECT_ID:?Set CRYPTANIO_PROJECT_ID}"
json_string() {
local text="$1" char code index output='"'
for ((index=0; index<${#text}; index++)); do
char="${text:index:1}"
case "$char" in
'"'|'\') output+="\\$char" ;;
*) printf -v code '%d' "'$char"
if ((code < 32)); then printf -v char '\\u%04x' "$code"; fi
output+="$char" ;;
esac
done
printf '%s"' "$output"
}
response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT
transfer_status=0
http_status="$(curl \
--silent --show-error --max-time 30 --request POST \
--header "Authorization: Bearer $CRYPTANIO_API_KEY" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: $CRYPTANIO_OPERATION_ID" \
--data-binary '{"project":'"$(json_string "$CRYPTANIO_PROJECT_ID")"',"invoice_currency":"USD","invoice_amount":"12.50","reference":"order-42"}' \
"https://api.cryptanio.com/v1/invoices" \
--output "$response_file" --write-out '%{http_code}')" || transfer_status=$?
if [[ "$http_status" == 2?? ]]; then
cat "$response_file"
exit "$transfer_status"
fi
printf 'HTTP %s\n' "$http_status" >&2
cat "$response_file" >&2
exit 1
A chave deve permitir invoices e Payments; a chave do exemplo Monitoring pode estar restrita a outro serviço. Idempotency-Key contém 16-80 letras ASCII, dígitos, - e _. Repetir o mesmo pedido normalizado com a mesma chave devolve a fatura existente com 201; conteúdo alterado devolve 409 conflict. Chaves de operação são comuns à conta. reference não as substitui.
Mostre ao comprador a rede, o ativo, o endereço, amount e o prazo retornados. Quando houver payment_route, envie seu kind e o valor exato de value como string: destination_tag, memo, comment ou reference é obrigatório para atribuir o pagamento a esta fatura. O campo reference do pedido é separado.
Use o payment_uri retornado no QR de pagamento quando estiver presente. Uma referência Solana Pay não é um memo da carteira: não substitua a URI por um endereço simples nem a reconstrua. Se a carteira não puder incluir o identificador obrigatório, ofereça ao pagador checkout_url ou uma carteira compatível.
checkout_url é a página de pagamento hospedada para novas faturas. Compartilhe apenas a URL exata retornada com o pagador; não a construa a partir do ID. O link revela os detalhes e o status do pagamento, portanto não o publique em registros públicos. Se não existir, mostre as instruções completas. Um redirecionamento do navegador não confirma o pagamento: verifique a API ou um webhook validado.
asked pode diferir de amount para distinguir pedidos. Faça a conciliação usando amount_raw como inteiro de precisão arbitrária. O TTL padrão é de 60 minutos, com máximo de 10080 minutos. As listas de faturas aceitam before, mas não retornam next_before: continue com o ID do último item e pare ao receber uma página vazia.
Customer
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | string | Sim | Identificador do cliente no seu sistema. maxLength: 120 |
Product
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | string | Não | Identificador do produto no seu sistema. maxLength: 120 |
name | string | Não | Nome do produto. maxLength: 200 |
quantity | integer | Não | Quantidade. minimum: 1; maximum: 1000000 |
Payer
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
comment | string | Sim | Comentário do pagador. maxLength: 500 |
InvoiceMetadata
Até 20 pares, no máximo 8192 bytes JSON. Não inclua segredos.
object Até 20 pares, no máximo 8192 bytes JSON. Não inclua segredos.
Monitorizações Monitoring
Uma monitorização pertence a um projeto Monitoring. O mesmo endereço pode existir em vários projetos com regras de direção e ativos independentes. Associe listas do livro de endereços e Address Sources no Account; esta API REST não disponibiliza os seus comandos de gestão.
Lista por ID descendente. Se houver next_before, passe-o como before no pedido seguinte mantendo projeto e filtros. limit tem predefinição 50, máximo 200. Guarde IDs como cadeias. Monitorizações removidas saem da lista ativa; o endereço permanece noutros projetos e no livro de endereços.
Eventos e histórico
/v1/events devolve eventos disponíveis à chave entre serviços; /v1/payments limita-se a Payments. Filtre por project, address, chain, type, watch_id e tx_id. before é cursor ID, não data; não há filtro from/to. next_before permite obter a página seguinte.
O histórico de eventos detetados está disponível até 30 dias. Não é um arquivo de todas as mensagens entregues: não se garante a recuperação de notice e webhook.test por esta via. REST Event tem um esquema próprio, separado dos webhooks; podem ser adicionados campos em v1.
Event
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
event_id | string | Sim | Identificador opaco; mantenha-o como cadeia. minLength: 1 |
event_type | string | Sim | Tipo de evento; podem ser adicionados novos tipos. |
chain | string | Sim | Código da rede. |
service | string | Não | Serviço, se conhecido. |
watch_id | string | Não | ID de monitorização do projeto, quando associado. minLength: 1 |
occurred_at | string | Sim | Data e hora UTC em RFC 3339.date-time |
payment | object | null | Sim | Documento de pagamento, ou null para eventos sem pagamento. GET /events não devolve conteúdos contract/notice. |
sandbox | boolean | Não | Evento de teste; omissão significa false. |
EventPayment
Documento de pagamento REST/SSE. Campos dependem do evento e rede; não é o formato normalizado webhook v1.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
tx_id | string | Não | Identificador da transação. |
index | integer | Não | Índice da transferência na transação. minimum: 0 |
height | integer | Não | Altura do bloco, quando conhecida. minimum: 0 |
address | string | Não | Endereço observado. |
asset | string | Não | Ativo REST/SSE: identificador de texto, não o objeto de ativo webhook. |
amount_raw | string | Não | Montante inteiro nas unidades mínimas como cadeia. pattern: ^[0-9]+$ |
amount | string | Não | Montante decimal exato como cadeia; não use vírgula flutuante. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
decimals | integer | Não | Casas decimais. minimum: 0 |
confirmations | integer | Não | Confirmações. minimum: 0 |
revision | integer | Não | Revisão do estado da transferência. minimum: 0 |
direction | string | Não | Direção em relação ao endereço observado.in, out |
simulated | boolean | Não | Evento simulado. |
late | boolean | Não | Transferência detetada tardiamente. |
block_hash | string | Não | Hash do bloco. |
match_key | string | Não | Identificador de correspondência, quando usado pela rede. |
meta | object | Não | |
invoice | object | Não | |
customer | object | Não | |
product | object | Não | |
payer | object | Não | |
metadata | object | Não |
Eventos webhook
A entrega ocorre pelo menos uma vez: um evento pode chegar várias vezes. Após verificar a assinatura, elimine duplicados por event_id na integração. Grave o processamento e a alteração de negócio numa única transação de base de dados. Uma chave tx_id/address/asset não substitui event_id: junta transições, direções e projetos diferentes.
Leia event_type e event_id do corpo assinado. Cabeçalhos X-Event-Type/X-Event-Id não estão no HMAC; compare-os com o corpo se os usar. Considere project_id, direction e payment.revision ao atualizar pagamentos. Não pressuponha ordem de entrega; ignore tipos desconhecidos com segurança e conserve-os para diagnóstico.
| Família | Corpo |
|---|---|
| payment.pending / confirmed / rolled_back | Pagamento recebido |
| debit.pending / confirmed / rolled_back | Pagamento de saída |
| contract.approval / owner_added / owner_removed / threshold_changed / permissions_changed | contract |
| asset.unexpected | contract; o ativo não é creditado como pagamento aceite |
| account.notice / account.notification / treasury.alarm / subscription.notice | notice.subject, notice.text |
| webhook.test | Evento de controlo sem pagamento |
schema_version: v1 contém payment, contract ou notice conforme o tipo; webhook.test não contém nenhum. IDs e montantes são cadeias. amount_raw contém unidades mínimas; amount é representação decimal. Verifique redes e ativos via chains. Esquemas completos em OpenAPI.
| Cabeçalho | Significado |
|---|---|
| X-Timestamp | Segundos Unix no momento da assinatura |
| X-Signature | hex HMAC-SHA256(timestamp + '.' + raw body) |
| X-Event-Id / X-Event-Type | Comparar com o corpo assinado |
| X-Attempt | Número da tentativa a partir de 1 |
Devolva 2xx após aceitar duravelmente o evento. Tempo limite da tentativa: 10 segundos. Falhas de rede, 408, 425, 429 e 5xx repetem até 24 horas, com atrasos de 2 segundos a 15 minutos e variação aleatória; respeita Retry-After. Não segue redirecionamentos. Outros 3xx/4xx terminam a entrega; 410 desativa o destino e a sua fila pendente.
{
"schema_version": "v1",
"event_id": "2300",
"event_type": "payment.confirmed",
"occurred_at": "2026-10-06T10:00:00Z",
"project_id": "1002",
"service": "monitoring",
"mode": "test",
"sandbox": true,
"payment": {
"chain": "ethereum",
"direction": "in",
"asset": {
"symbol": "ETH"
},
"address": "0x52908400098527886E0F7030069857D2E4169EE7",
"tx_id": "simulated_2300",
"index": 0,
"amount": "1.000000000000000000",
"amount_raw": "1000000000000000000",
"decimals": 18,
"confirmations": 0,
"revision": 1,
"simulated": true
}
}Webhook
Corpo público assinado webhook v1. payment.* e debit.* contêm payment; contract.* e asset.unexpected contêm contract; webhook.test não tem conteúdo de negócio; account.notice, account.notification, treasury.alarm e subscription.notice contêm notice. Elimine duplicados de entrega por event_id.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
schema_version | string | Sim | Versão do conteúdo webhook.v1 |
event_id | string | Sim | ID do evento: inalterado nas repetições de entrega. Estados diferentes podem ter valores event_id diferentes. minLength: 1 |
event_type | string | Sim | Tipo no corpo assinado; use após verificar a assinatura. Podem surgir novos tipos. |
occurred_at | string | Sim | Data e hora UTC em RFC 3339.date-time |
project_id | string | Sim | Identificador opaco; mantenha-o como cadeia. minLength: 1 |
service | string | Não | Serviço do projeto, quando aplicável. |
mode | string | Sim | Modo do evento.live, test |
sandbox | boolean | Sim | Indicador explícito de simulação. |
payment | object | Não | |
contract | object | Não | |
invoice | object | Não | |
customer | object | Não | |
product | object | Não | |
payer | object | Não | |
metadata | object | Não | |
notice | object | Não |
WebhookPayment
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
chain | string | Sim | Código da rede. |
direction | string | Sim | Pagamento recebido: in; débito de saída: out.in, out |
asset | object | Sim | |
address | string | Sim | Endereço observado. |
tx_id | string | Sim | ID da transação. |
index | integer | Sim | Índice da transferência; não pressuponha 0 em todas as redes. minimum: 0 |
height | integer | Não | Altura do bloco, se conhecida. minimum: 0 |
block_hash | string | Não | Hash do bloco, se conhecido. |
match_key | string | Não | Identificador de correspondência da rede, quando aplicável. |
amount | string | Sim | Montante decimal exato como cadeia; não use vírgula flutuante. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
amount_raw | string | Sim | Montante inteiro nas unidades mínimas como cadeia. pattern: ^[0-9]+$ |
decimals | integer | Sim | Casas decimais. minimum: 0 |
confirmations | integer | Sim | Confirmações. minimum: 0 |
revision | integer | Sim | Revisão do estado da transferência. Não junte valores event_id distintos pela identidade da transação. minimum: 0 |
late | boolean | Não | Transferência detetada tardiamente; não equivale ao estado de fatura late. |
simulated | boolean | Não | Transferência simulada. |
valuation | object | Não |
WebhookAsset
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
symbol | string | Não | Símbolo, se conhecido; não basta para determinar a identidade do token. |
standard | string | Não | Norma do token, se conhecida. |
contract | string | Não | Contrato do token na sua rede, quando aplicável. |
identifier | string | Não | Outro identificador de token específico da rede. |
WebhookContract
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
chain | string | Sim | Código da rede. |
address | string | Sim | Endereço ou contrato observado. |
tx_id | string | Sim | ID da transação. |
index | integer | Sim | Índice do evento. minimum: 0 |
height | integer | Não | Altura do bloco. minimum: 0 |
block_hash | string | Não | Hash do bloco. |
kind | string | Não | Tipo de alteração. |
asset | string | Não | Identificador do ativo afetado, se conhecido. |
detail | object | Não | |
simulated | boolean | Não | Evento simulado. |
WebhookInvoice
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | string | Sim | Identificador opaco; mantenha-o como cadeia. minLength: 1 |
reference | string | Não | Referência da encomenda. |
status | string | Não | pending aguarda pagamento; seen detetado; partial parcialmente pago; paid liquidado; underpaid/overpaid/late exigem decisão; expired prazo terminado; canceled anulado.pending, seen, paid, underpaid, partial, overpaid, expired, late, canceled |
currency | string | Não | Moeda da cotação da encomenda. |
amount | string | Não | Montante decimal exato como cadeia; não use vírgula flutuante. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
Notice
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
subject | string | Sim | Assunto do aviso. |
text | string | Sim | Texto do aviso. |
Valuation
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
currency | string | Sim | Moeda de avaliação, como USD. |
amount | string | Sim | Montante decimal exato como cadeia; não use vírgula flutuante. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
rate | string | Sim | Taxa de câmbio do ativo como cadeia decimal. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
rate_at | string | Sim | Data e hora UTC em RFC 3339.date-time |
provider | string | Não | Nome da fonte de cotação, se conhecido. |
Verificar assinaturas
Verifique HMAC nos bytes originais do corpo antes de analisar JSON, incluindo espaços e quebras de linha. A verificação temporal limita repetição, mas não substitui deduplicação persistente de event_id. Exemplos aceitam apenas assinaturas hex de 64 caracteres e timestamps a 300 segundos da hora do servidor. Use o segredo do destino webhook específico.
import { createHmac, timingSafeEqual } from "node:crypto";
import { TextDecoder } from "node:util";
// rawBody is the original HTTP request Buffer, before JSON.parse or serialization.
export function verifyWebhook(secret, timestamp, rawBody, signature, nowSeconds = Math.floor(Date.now() / 1000)) {
if (typeof secret !== "string" || secret.length === 0 || !Buffer.isBuffer(rawBody) || rawBody.length === 0 ||
typeof timestamp !== "string" || !/^(0|[1-9][0-9]*)$/.test(timestamp) ||
typeof signature !== "string" || !/^[0-9a-f]{64}$/i.test(signature) ||
!Number.isSafeInteger(nowSeconds) || nowSeconds < 0) return false;
const signedAt = Number(timestamp);
if (!Number.isSafeInteger(signedAt) || Math.abs(nowSeconds - signedAt) > 300) return false;
const expected = createHmac("sha256", secret).update(timestamp).update(".").update(rawBody).digest();
return timingSafeEqual(expected, Buffer.from(signature, "hex"));
}
// headers is Node.js request.headers (lowercase names). Optional X-Event-Id/Type
// headers are not signed separately; compare them with the verified body.
export function readSignedWebhook(secret, timestamp, rawBody, signature, headers = {}, nowSeconds) {
if (!verifyWebhook(secret, timestamp, rawBody, signature, nowSeconds) ||
headers === null || typeof headers !== "object" || Array.isArray(headers)) return null;
let event;
try {
event = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(rawBody));
} catch {
return null;
}
if (event === null || typeof event !== "object" || Array.isArray(event) || event.schema_version !== "v1" ||
typeof event.event_id !== "string" || event.event_id.length === 0 ||
typeof event.event_type !== "string" || event.event_type.length === 0) return null;
for (const [header, field] of [["x-event-id", "event_id"], ["x-event-type", "event_type"]]) {
if (headers[header] !== undefined && headers[header] !== event[field]) return null;
}
return event;
}
package webhook
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"strconv"
"time"
"unicode/utf8"
)
// VerifyWebhook checks the original HTTP body bytes before parsing JSON.
// Optional nowSeconds is used only for deterministic tests.
func VerifyWebhook(secret, timestamp string, rawBody []byte, signature string, nowSeconds ...int64) bool {
if secret == "" || len(rawBody) == 0 || len(signature) != 64 || len(nowSeconds) > 1 {
return false
}
signedAt, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil || signedAt < 0 || strconv.FormatInt(signedAt, 10) != timestamp {
return false
}
now := time.Now().Unix()
if len(nowSeconds) == 1 {
now = nowSeconds[0]
}
if now < 0 || now-signedAt < -300 || now-signedAt > 300 {
return false
}
provided, err := hex.DecodeString(signature)
if err != nil || len(provided) != sha256.Size {
return false
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(timestamp))
mac.Write([]byte("."))
mac.Write(rawBody)
return hmac.Equal(mac.Sum(nil), provided)
}
// WebhookEvent contains only routing fields. Parse other fields from rawBody
// after ReadSignedWebhook succeeds; unknown fields are allowed.
type WebhookEvent struct {
SchemaVersion string `json:"schema_version"`
EventID string `json:"event_id"`
EventType string `json:"event_type"`
}
// ReadSignedWebhook extracts event_id/type only from the verified body.
// X-Event-Id/Type are not signed separately; nonempty values must match the body.
func ReadSignedWebhook(secret, timestamp string, rawBody []byte, signature, eventIDHeader, eventTypeHeader string,
nowSeconds ...int64) (*WebhookEvent, error) {
if !VerifyWebhook(secret, timestamp, rawBody, signature, nowSeconds...) {
return nil, errors.New("invalid webhook signature or timestamp")
}
var event WebhookEvent
if !utf8.Valid(rawBody) || json.Unmarshal(rawBody, &event) != nil || event.SchemaVersion != "v1" ||
event.EventID == "" || event.EventType == "" {
return nil, errors.New("invalid webhook body")
}
if eventIDHeader != "" && eventIDHeader != event.EventID || eventTypeHeader != "" && eventTypeHeader != event.EventType {
return nil, errors.New("webhook headers do not match the signed body")
}
return &event, nil
}
Endpoints webhook
Adicione uma URL HTTPS pública na porta 443 para um projeto específico. Não são permitidos credenciais na URL, fragmentos, redirecionamentos ou destinos não públicos. São permitidos até 20 endpoints por conta. Cada um tem um segredo próprio, devolvido apenas na criação inicial; guarde-o no servidor.
Criar novamente a mesma URL no mesmo projeto e modo devolve 200 e o endpoint existente sem o segredo. Um novo endpoint devolve 201. DELETE devolve 200 com id e deleted: true, incluindo uma repetição segura. Uma chave só vê o seu modo e projetos permitidos; a entrega segue as definições do projeto.
Fluxo SSE
SSE serve para integração no servidor e visualização em direto; não garante entrega nem repetição de eventos perdidos. Use cliente HTTP no servidor com cabeçalho Bearer. EventSource padrão do navegador não o envia; não coloque a chave API em código do navegador ou query string.
As mensagens usam event: payment e formato StreamEvent próprio, não webhook v1 ou REST Event. Comentários começam por dois pontos; heartbeats chegam a cada cerca de 20 segundos. retry: 5000 sugere reconexão. Após desligar, obtenha o histórico blockchain disponível através de events.
StreamEvent
JSON em data SSE; difere de webhook v1. Eventos sem pagamento omitem payment e podem incluir contract.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
event_id | string | Sim | Identificador opaco; mantenha-o como cadeia. minLength: 1 |
event_type | string | Sim | Tipo de evento; podem ser adicionados novos tipos. |
chain | string | Sim | Código da rede. |
service | string | Não | Serviço, se conhecido. |
watch_id | string | Não | ID de monitorização do projeto, quando associado. minLength: 1 |
occurred_at | string | Sim | Data e hora UTC em RFC 3339.date-time |
payment | object | null | Não | Documento de pagamento, ou null para eventos sem pagamento. GET /events não devolve conteúdos contract/notice. |
sandbox | boolean | Não | Evento de teste; omissão significa false. |
project_id | string | Não | Identificador opaco; mantenha-o como cadeia. minLength: 1 |
project_name | string | Não | Nome do projeto. |
url | string | Não | Ligação ao evento no Account.uri |
contract | object | Não | |
mode | string | Não | live ou test.live, test |
Estados de pagamentos e faturas
O estado do pagamento na blockchain difere do estado da encomenda. payment.pending indica deteção antes de confirmações suficientes; payment.confirmed indica que foi atingida a regra de confirmação da rede/projeto; payment.rolled_back comunica uma reversão. Não satisfaça encomendas com pending, débitos de saída ou eventos simulados.
| Fatura | Significado |
|---|---|
| pending | A aguardar pagamento |
| partial | Parte recebida; sujeita à política de pagamentos parciais do projeto |
| paid | Pagamento aceite |
| underpaid | Pagamento insuficiente exige decisão |
| overpaid | Pagamento excessivo exige decisão segundo a política aplicável |
| late | Pagamento após o prazo exige decisão |
| expired | Prazo de pagamento terminado |
| canceled | Fatura cancelada; o registo continua consultável |
needs_decision está presente em underpaid, overpaid e late. Resolva no Account. Reorganização e reinclusão podem criar novas transições e novo payment.revision; confirmed não garante incondicionalmente ausência de alterações futuras.
Erros e repetições seguras
{"error":{"code":"forbidden","message":"this API key is read-only"}}Use estado HTTP e error.code na lógica, message para diagnóstico. Listas bem-sucedidas têm data; um objeto é devolvido diretamente. Health usa esquema status/version próprio, inclusive em 503. Não dependa da redação exata da mensagem.
| HTTP | Ação |
|---|---|
| 400 | Corrija JSON, formatos e parâmetros; não repita sem alterações |
| 401 | Verifique chave, validade, suspensão e revogação |
| 403 | Verifique âmbitos, leitura/escrita, projeto, serviço, IP e verificação da conta |
| 404 | Verifique ID, modo da chave e acesso; as restrições podem ocultar o recurso |
| 402 | Limite de monitorizações Monitoring; verifique o plano |
| 409 | Conflito, serviço inativo, conjunto ocupado ou limite; consulte code |
| 429 | Respeite Retry-After; reduza a taxa conjunta de pedidos da conta |
| 500 / 503 | Repita com espera progressiva limitada; criação de fatura apenas com a Idempotency-Key original |
Um tempo limite excedido não significa que a operação não foi executada. Não altere a chave de operação da fatura numa repetição. Em métodos sem proteção contra duplicados, consulte primeiro o estado; nem todo POST é idempotente.
Redes e limites
Use chains no host API selecionado para catálogo de redes, ativos, capacidades e confirmações. Constarem do catálogo geral não garante que uma rede esteja ativa nesse ambiente ou suporte todas as operações. Verifique quotas e orçamento no Account; preços na página Preços.
Quota REST autenticada da conta: 10 pedidos/s sem serviço pago, 50 com serviço pago; capacidade de pico breve 20/100. Todas as chaves e modos da conta partilham a quota. Exceder devolve 429 com Retry-After. Quotas de serviço e limites de taxa API são restrições distintas.
Versões da API
Esta referência descreve v1 na versão 1.0.0 62; atualizada em 2026-10-10. Antes de usar novas capacidades, verifique a versão implantada via GET /v1/health. A versão da documentação não confirma implantação.
| API pública | Contrato |
|---|---|
https://api.cryptanio.com | /v1 |
Use /v1 para integrações estáveis. Um caminho sem versão escolhe o contrato estável atual do ambiente; /vN desconhecido devolve 404. X-API-Version identifica o contrato; X-Server-Version e health.version a versão do produto, não um contrato API diferente.
Alterações da API
A versão 1.0.0 62 inclui SDKs transferíveis para Node.js, Python, PHP, Go e Rust e MCP local para agentes de IA. Referência, Markdown e exemplos HTTP diretos continuam disponíveis separadamente. Não há sandbox pública. O comportamento API mantém-se.
Em v1, clientes devem tolerar campos adicionais de resposta. Aceitar um tipo desconhecido não significa satisfazer encomenda ou creditar pagamento. Verifique disponibilidade das alterações na versão do ambiente escolhido antes de usar.