API 문서

Cryptanio를 애플리케이션에 연결하세요.

계약 v1 / 1.0.0 62 / 2026-10-10

목차

시작하기

API는 서버용입니다. 먼저 Account에서 프로젝트, 서비스 및 주소 풀을 설정하세요. API는 그 범위 안에서 키에 허용된 작업을 수행합니다.

  1. 계정을 확인하고 내 서비스에서 필요한 서비스를 활성화하세요.
  2. 최소 권한으로 API 키를 만들고 서버에 보관하세요.
  3. 작업을 선택하세요: 주소 모니터링, 청구서 생성 또는 이벤트 수신.

예제는 실제 데이터에서 실제 작업을 수행합니다. 객체 생성이나 삭제는 서비스 할당량을 사용할 수 있습니다. 예제에는 키를 내장하지 않으며 환경 변수에서 읽습니다.

에이전트 지침

에이전트 채팅이나 프로젝트 규칙에 이 지침을 붙여 넣으세요. 에이전트가 서비스, 가격 및 참조 문서를 파악한 뒤 REST API 또는 SDK로 애플리케이션 통합을 작성하는 데 도움이 됩니다. 에이전트는 사용자의 언어로 답할 수 있습니다.

연동 지침
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.

이 블록에는 키나 계정 데이터가 없습니다. 통합에 필요한 최소 권한의 API 키를 사용하고 애플리케이션 서버에 보관하세요. 에이전트 채팅에 키를 공유하지 마세요.

API 참조

메서드: 16

인증

서버에서 Authorization: Bearer <API_KEY>을 보내세요. Account → API 키에서 실제 데이터용 키를 생성하고 필요한 프로젝트와 작업으로 제한하세요. 현재 공개 샌드박스는 제공하지 않습니다.

키에는 읽기 전용, 특정 작업, 프로젝트나 서비스 하나, 특정 IP/CIDR 범위의 접근을 허용할 수 있습니다. 만료, 일시 중지와 취소도 접근에 영향을 줍니다. 키를 교체하려면 적절한 제한으로 새 키를 만들고 연동을 전환한 뒤 기존 키를 취소하세요. 키를 브라우저 코드, 모바일 앱이나 URL에 넣지 마세요.

범위목적
watchesMonitoring
events이벤트 및 결제
invoicesPayments 청구서
webhooks웹훅 엔드포인트
streamSSE

제한은 서로 대체하지 않고 함께 적용됩니다. 401은 키가 수락되지 않았음을 뜻합니다. 403은 권한 부족이나 허용되지 않은 IP를 뜻할 수 있습니다. 접근 불가능한 리소스는 404를 반환할 수 있습니다. 오류 및 재시도를 확인하세요.

SDK: 바로 사용할 수 있는 클라이언트

사용 언어의 파일을 다운로드해 애플리케이션 서버에 저장하고 생성자에 키를 전달하세요. 각 클라이언트는 이 참조 문서의 16개 메서드를 모두 지원합니다. 설치할 npm, PyPI, Composer 또는 crates.io 패키지는 없습니다. 다운로드한 파일 자체가 SDK입니다.

언어요구 사항파일
Node.jsNode.js 18+cryptanio.mjs
PythonPython 3.10+cryptanio.py
PHPPHP 8.1+ / cURLCryptanio.php
GoGo 1.22+cryptanio.go
RustRust 2021 / reqwest 0.12 / serde_json 1cryptanio.rs

파일은 릴리스 1.0.0 62을 공유합니다. 매니페스트에는 버전, 계약 및 각 파일의 SHA-256가 포함됩니다. 먼저 환경에 CRYPTANIO_API_KEY과 CRYPTANIO_PROJECT_ID를 설정하세요. 예시는 이벤트만 읽지만 실제 계정 데이터에 접근합니다.

Node.js

예시를 cryptanio.mjs 옆에 .mjs 파일로 저장하세요.

Node.js
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);

기존 인보이스의 결제 안내를 읽으려면 CRYPTANIO_RESOURCE_ID에 해당 ID를 설정하세요. 다음 예제는 필수 payment_route, QR용으로 반환된 payment_uri, 정확한 checkout_url을 보존합니다. 결과는 자체 인터페이스를 통해 결제자에게 전달하고 공개 로그에는 남기지 마세요. 금액은 문자열로 유지되며 없는 필드는 생략됩니다.

Node.js
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

예시를 cryptanio.py 옆에 .py 파일로 저장하세요.

Python
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)

기존 인보이스의 결제 안내를 읽으려면 CRYPTANIO_RESOURCE_ID에 해당 ID를 설정하세요. 다음 예제는 필수 payment_route, QR용으로 반환된 payment_uri, 정확한 checkout_url을 보존합니다. 결과는 자체 인터페이스를 통해 결제자에게 전달하고 공개 로그에는 남기지 마세요. 금액은 문자열로 유지되며 없는 필드는 생략됩니다.

Python
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

예시를 Cryptanio.php 옆에 .php 파일로 저장하고 cURL 확장을 활성화하세요.

PHP
<?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);

기존 인보이스의 결제 안내를 읽으려면 CRYPTANIO_RESOURCE_ID에 해당 ID를 설정하세요. 다음 예제는 필수 payment_route, QR용으로 반환된 payment_uri, 정확한 checkout_url을 보존합니다. 결과는 자체 인터페이스를 통해 결제자에게 전달하고 공개 로그에는 남기지 마세요. 금액은 문자열로 유지되며 없는 필드는 생략됩니다.

PHP
<?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

Go 모듈의 cryptanio/ 디렉터리에 파일을 배치하세요. 이 예시는 example.local/myapp 모듈을 사용합니다. 가져오기 경로를 자신의 모듈 이름으로 바꾸세요. 외부 의존성은 필요하지 않습니다.

Go
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)
}

기존 인보이스의 결제 안내를 읽으려면 CRYPTANIO_RESOURCE_ID에 해당 ID를 설정하세요. 다음 예제는 필수 payment_route, QR용으로 반환된 payment_uri, 정확한 checkout_url을 보존합니다. 결과는 자체 인터페이스를 통해 결제자에게 전달하고 공개 로그에는 남기지 마세요. 금액은 문자열로 유지되며 없는 필드는 생략됩니다.

Go
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

파일을 src/cryptanio.rs으로, 예시를 src/main.rs로 저장하세요. Cargo.toml에 아래 의존성을 추가하세요. 동기식 차단 클라이언트이므로 비동기 작업자에서 직접 실행하지 마세요.

Cargo.toml
[dependencies]
reqwest = { version = "=0.12.24", default-features = false, features = ["blocking", "json", "rustls-tls"] }
serde_json = "=1.0.145"
Rust
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(())
}

기존 인보이스의 결제 안내를 읽으려면 CRYPTANIO_RESOURCE_ID에 해당 ID를 설정하세요. 다음 예제는 필수 payment_route, QR용으로 반환된 payment_uri, 정확한 checkout_url을 보존합니다. 결과는 자체 인터페이스를 통해 결제자에게 전달하고 공개 로그에는 남기지 마세요. 금액은 문자열로 유지되며 없는 필드는 생략됩니다.

Rust
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(())
}

메서드 호출

Node.js와 PHP는 listEvents, Python과 Rust는 list_events, Go는 ListEvents를 사용합니다. 각 메서드는 참조 문서의 요청 하나에 대응합니다. 필터는 객체/맵이며 Rust에서는 쌍의 목록입니다. Go 메서드는 항상 context.Context를 첫 번째 인수로 받습니다. 알 수 없는 필터는 요청 전송 전에 거부됩니다.

Node.js / PHP 메서드HTTP
createWatchPOST /v1/watches
listWatchesGET /v1/watches
getWatchGET /v1/watches/{id}
deleteWatchDELETE /v1/watches/{id}
listEventsGET /v1/events
listPaymentsGET /v1/payments
createInvoicePOST /v1/invoices
listInvoicesGET /v1/invoices
getInvoiceGET /v1/invoices/{id}
cancelInvoicePOST /v1/invoices/{id}/cancel
createEndpointPOST /v1/endpoints
listEndpointsGET /v1/endpoints
deleteEndpointDELETE /v1/endpoints/{id}
streamEventsGET /v1/stream
getHealthGET /v1/health
listChainsGET /v1/chains

모니터링과 웹훅 생성에는 요청 본문이 필요합니다. 단일 리소스 메서드는 문자열 ID를 받습니다. createInvoice(body, idempotencyKey)에는 ASCII 문자, 숫자, 하이픈 또는 밑줄로 된 16–80자 키가 필요합니다. 재시도 시 같은 키와 본문을 사용하세요. SDK는 새 키를 생성하거나 자동 재시도하지 않습니다. 각 메서드에서 본문, 오류 및 권한을 확인하세요.

응답, 오류 및 스트리밍

JSON 응답은 필드명을 유지하며 ID, 금액 및 커서는 문자열입니다. 페이지 조회는 각 메서드 문서에 따라 수동으로 수행합니다. 오류는 HTTP 상태, code, message 및 Retry-After을 제공합니다. HTTP 응답이 없으면 상태는 0입니다(Rust는 None 사용). 리디렉션은 따르지 않습니다. 일반 JSON 응답은 8 MiB로 제한됩니다. 기본 시간 초과는 30초이며 최대 300초로 설정할 수 있습니다. Python에서는 네트워크 작업에 적용되고 다른 클라이언트에서는 스트리밍을 포함한 전체 요청에 적용됩니다. 시간 초과가 쓰기 미실행을 증명하지는 않습니다.

언어 / 오류필드
Node.js / Python: ApiErrorstatus, code, message, retry_after
PHP: CryptanioErrorstatus, apiCode, getMessage(), retryAfter
Go: *APIErrorStatus, Code, Message, RetryAfter
Rust: ApiErrorstatus: Option<u16>, code, message, retry_after

streamEvents은 재연결이나 이벤트 분석 없이 원시 SSE 바이트를 제공합니다. Node.js ReadableStream, 닫을 수 있는 Python HTTP 응답, Go io.ReadCloser 또는 Rust blocking::Response입니다. 필요 없으면 스트림을 해제하세요. PHP는 청크를 콜백에 전달하며 false를 반환하면 중단됩니다. 스트리밍은 웹훅을 대체하지 않고 내역 재생을 보장하지 않습니다. SSE 규칙을 확인하세요.

첫 모니터링

Account에서 활성 Monitoring 서비스가 있는 프로젝트와 watches 및 events 권한이 있는 키를 만드세요. 필요한 경우 계정 인증을 완료하세요. 설정에서 프로젝트 ID를 복사하고 실제로 모니터링하려는 주소만 추가하세요.

서버 환경에 CRYPTANIO_API_KEY과 CRYPTANIO_PROJECT_ID을 설정하세요. 예시 주소를 본인의 주소로 바꾸세요. 실제 모니터링을 연결하는 요청이며 트랜잭션을 시뮬레이션하지 않습니다.

Shell / cURL
#!/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

성공 응답에는 id이 포함되니 저장하세요. GET /v1/watches/{id}은 결제가 아니라 관찰 대상과 설정을 반환합니다. 발견된 활동은 watch_id 조건으로 필터링한 GET /v1/events 요청으로 확인하세요. 트랜잭션이 발견되기 전 목록이 비어 있는 것은 정상입니다. 응답, 오류와 다른 언어의 예제.

Payments 청구서

프로젝트에서 Payments를 활성화하고 Account에서 지원 주소를 풀에 추가하세요. 기본 프로젝트 선택에 의존하지 말고 project를 명시적으로 보내세요. amount는 암호자산 단위의 문자열이며 invoice_currency/invoice_amount는 주문의 견적을 별도로 설명합니다.

CRYPTANIO_PROJECT_ID은 Payments 프로젝트로, CRYPTANIO_API_KEY은 invoices 권한이 있는 키로 설정하세요. 새 주문의 CRYPTANIO_OPERATION_ID는 고유해야 하며 같은 작업을 재시도할 때는 재사용하세요. 청구서는 실제 데이터에 생성됩니다.

Shell / cURL
#!/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

키는 invoices과 Payments를 허용해야 합니다. Monitoring 예시의 키는 다른 서비스로 제한되었을 수 있습니다. Idempotency-Key은 16-80자로, ASCII 문자, 숫자, - 및 _으로 구성됩니다. 같은 키로 동일한 정규화 요청을 반복하면 201와 기존 청구서를 반환하고, 내용이 바뀌면 409 conflict를 반환합니다. 작업 키는 계정 전체에서 적용됩니다. reference은 대체 수단이 아닙니다.

구매자에게 반환된 네트워크, 자산, 주소, amount, 만료 시간을 표시하세요. payment_route이 있으면 해당 kind와 정확한 문자열 value도 전달하세요. destination_tag, memo, comment 또는 reference는 이 인보이스에 결제를 연결하는 데 필수입니다. 주문의 reference 필드는 별개입니다.

반환된 payment_uri이 있으면 결제 QR에 그대로 사용하세요. Solana Pay reference는 지갑 memo가 아닙니다. URI를 단순 주소로 바꾸거나 다시 조립하지 마세요. 지갑이 필수 식별자를 포함할 수 없으면 결제자에게 checkout_url 또는 호환 지갑을 안내하세요.

checkout_url은 새로 발행된 인보이스의 호스팅 결제 페이지입니다. 반환된 정확한 URL만 결제자에게 공유하고 ID로 만들지 마세요. 링크에서 결제 상세와 상태를 볼 수 있으므로 공개 로그에 남기지 마세요. 필드가 없으면 전체 결제 안내를 표시하세요. 브라우저 리디렉션은 결제를 확인하지 않습니다. API 또는 검증된 webhook을 확인하세요.

주문을 구분하기 위해 asked이 amount와 다를 수 있습니다. amount_raw을 임의 정밀도 정수로 사용하여 대조하세요. 기본 TTL은 60분이며 최대 10080분입니다. 인보이스 목록은 before를 받지만 next_before를 반환하지 않습니다. 마지막 항목의 ID로 계속 조회하고 빈 페이지에서 중단하세요.

Customer

필드유형필수설명
idstring예귀하의 시스템에서 사용하는 고객 식별자입니다.
maxLength: 120
Product

필드유형필수설명
idstring아니요사용자 시스템의 제품 식별자입니다.
maxLength: 120
namestring아니요제품 이름입니다.
maxLength: 200
quantityinteger아니요수량입니다.
minimum: 1; maximum: 1000000
Payer

필드유형필수설명
commentstring예결제자 의견입니다.
maxLength: 500
InvoiceMetadata

최대 20쌍, JSON 8192바이트까지 허용됩니다. 비밀 값을 포함하지 마세요.

object 최대 20쌍, JSON 8192바이트까지 허용됩니다. 비밀 값을 포함하지 마세요.

Monitoring 감시

관찰 대상은 Monitoring 프로젝트 하나에 속합니다. 같은 주소를 여러 프로젝트에서 독립적인 방향과 자산 규칙으로 사용할 수 있습니다. 주소록 목록과 Address Sources는 Account에서 연결하세요. 이 REST API는 해당 관리 명령을 제공하지 않습니다.

목록은 ID 내림차순입니다. next_before이 있으면 다음 요청에서 before로 전달하고 프로젝트와 필터를 유지하세요. limit의 기본값은 50, 최대값은 200입니다. ID는 문자열로 저장하세요. 제거된 모니터링은 활성 목록에서 빠지지만 다른 프로젝트나 주소록의 주소를 삭제하지는 않습니다.

이벤트 및 기록

/v1/events은 서비스 전체에서 키에 허용된 이벤트를 반환하고 /v1/payments은 Payments로 한정됩니다. project, address, chain, type, watch_id 및 tx_id로 필터링하세요. before는 날짜가 아닌 ID 커서입니다. from/to 필터는 없습니다. next_before으로 다음 페이지를 조회합니다.

감지된 이벤트 기록은 최대 30일 동안 조회할 수 있습니다. 모든 전달 메시지의 보관소는 아니며 notice와 webhook.test 메시지는 이 기록에서 복구가 보장되지 않습니다. REST Event는 웹훅과 별도의 스키마를 사용합니다. v1 내에서 필드가 추가될 수 있습니다.

Event

필드유형필수설명
event_idstring예불투명 식별자입니다. 문자열로 유지하세요.
minLength: 1
event_typestring예이벤트 유형입니다. 새로운 유형이 추가될 수 있습니다.
chainstring예네트워크 코드입니다.
servicestring아니요알려진 경우 서비스입니다.
watch_idstring아니요모니터링에 연결된 경우 프로젝트 모니터링 ID입니다.
minLength: 1
occurred_atstring예RFC 3339 형식의 UTC 날짜 및 시간입니다.
date-time
paymentobject | null예결제 문서이며, 결제 외 이벤트는 null입니다. GET /events는 contract/notice 본문을 반환하지 않습니다.
sandboxboolean아니요테스트 이벤트입니다. 생략하면 false입니다.
EventPayment

REST/SSE 결제 문서입니다. 필드는 이벤트와 네트워크에 따라 다르며, 정규화된 웹훅 v1 형식이 아닙니다.

필드유형필수설명
tx_idstring아니요트랜잭션 식별자입니다.
indexinteger아니요트랜잭션 내 전송 인덱스입니다.
minimum: 0
heightinteger아니요확인된 경우 블록 높이입니다.
minimum: 0
addressstring아니요관찰 대상 주소입니다.
assetstring아니요REST/SSE 자산: 웹훅 자산 객체가 아닌 문자열 식별자입니다.
amount_rawstring아니요최소 단위의 정수 금액을 문자열로 표현합니다.
pattern: ^[0-9]+$
amountstring아니요정확한 소수 금액 문자열입니다. 부동 소수점을 사용하지 마세요.
pattern: ^[0-9]+(?:\.[0-9]+)?$
decimalsinteger아니요소수 자릿수입니다.
minimum: 0
confirmationsinteger아니요확인 횟수입니다.
minimum: 0
revisioninteger아니요전송 상태 리비전입니다.
minimum: 0
directionstring아니요관찰 대상 주소를 기준으로 한 방향입니다.
in, out
simulatedboolean아니요시뮬레이션된 이벤트입니다.
lateboolean아니요뒤늦게 감지된 이체입니다.
block_hashstring아니요블록 해시입니다.
match_keystring아니요네트워크에서 사용하는 경우의 매칭 식별자입니다.
metaobject아니요
invoiceobject아니요
customerobject아니요
productobject아니요
payerobject아니요
metadataobject아니요

웹훅 이벤트

전달은 최소 한 번 방식이므로 이벤트가 여러 번 도착할 수 있습니다. 서명 확인 후 연동 내에서 event_id을 기준으로 중복을 제거하세요. 처리 기록과 비즈니스 변경을 하나의 데이터베이스 트랜잭션에 저장하세요. tx_id/address/asset 키는 서로 다른 상태 변화, 방향 및 프로젝트를 합치므로 event_id를 대신할 수 없습니다.

서명된 본문에서 event_type과 event_id을 읽으세요. X-Event-Type/X-Event-Id 헤더는 HMAC에 포함되지 않으므로 사용할 경우 본문과 비교하세요. 결제 갱신 시 project_id, direction 및 payment.revision을 고려하세요. 전송 순서를 가정하지 마세요. 알 수 없는 유형은 안전하게 무시하되 진단을 위해 보관하세요.

계열본문
payment.pending / confirmed / rolled_back입금
debit.pending / confirmed / rolled_back출금 결제
contract.approval / owner_added / owner_removed / threshold_changed / permissions_changedcontract
asset.unexpectedcontract. 해당 자산은 수락된 결제로 적립되지 않습니다
account.notice / account.notification / treasury.alarm / subscription.noticenotice.subject, notice.text
webhook.test결제 없는 제어 이벤트

schema_version: v1에는 이벤트 유형에 따라 payment, contract 또는 notice가 포함되며 webhook.test에는 없습니다. ID와 금액은 문자열입니다. amount_raw는 최소 단위, amount는 십진수 표현입니다. 네트워크와 자산은 chains로 확인하세요. 전체 객체 스키마는 OpenAPI에 있습니다.

헤더의미
X-Timestamp서명 시점의 Unix 초
X-Signaturehex HMAC-SHA256(timestamp + '.' + raw body)
X-Event-Id / X-Event-Type서명된 본문과 비교하세요.
X-Attempt1부터 시작하는 시도 번호

이벤트를 영구적으로 수락한 뒤 2xx을 반환하세요. 시도 제한 시간은 10초입니다. 네트워크 실패, 408, 425, 429 및 5xx는 최대 24시간 재시도하며, 대기 시간은 2초부터 15분까지 무작위 편차를 적용합니다. Retry-After를 준수합니다. 리디렉션은 따르지 않습니다. 다른 3xx/4xx는 전송을 종료하고 410은 대상과 대기열을 폐기합니다.

웹훅 형식 예시
{
  "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

공개 서명 웹훅 v1 본문입니다. payment.* 및 debit.*에는 payment, contract.* 및 asset.unexpected에는 contract가 포함됩니다. webhook.test에는 업무 본문이 없으며 account.notice, account.notification, treasury.alarm 및 subscription.notice에는 notice가 포함됩니다. event_id로 재전송을 중복 제거하세요.

필드유형필수설명
schema_versionstring예웹훅 본문 버전입니다.
v1
event_idstring예이벤트 ID: 전달 재시도 시 변경되지 않습니다. 상태별로 event_id 값이 다를 수 있습니다.
minLength: 1
event_typestring예서명된 본문의 유형입니다. 서명을 검증한 뒤 사용하세요. 새 유형이 추가될 수 있습니다.
occurred_atstring예RFC 3339 형식의 UTC 날짜 및 시간입니다.
date-time
project_idstring예불투명 식별자입니다. 문자열로 유지하세요.
minLength: 1
servicestring아니요해당하는 경우 프로젝트 서비스입니다.
modestring예이벤트 모드입니다.
live, test
sandboxboolean예명시적 모의 실행 플래그입니다.
paymentobject아니요
contractobject아니요
invoiceobject아니요
customerobject아니요
productobject아니요
payerobject아니요
metadataobject아니요
noticeobject아니요
WebhookPayment

필드유형필수설명
chainstring예네트워크 코드입니다.
directionstring예입금: in; 출금: out.
in, out
assetobject예
addressstring예관찰 대상 주소입니다.
tx_idstring예트랜잭션 ID입니다.
indexinteger예전송 인덱스입니다. 모든 네트워크에서 0라고 가정하지 마세요.
minimum: 0
heightinteger아니요알려진 경우 블록 높이입니다.
minimum: 0
block_hashstring아니요알려진 경우 블록 해시입니다.
match_keystring아니요해당되는 경우 네트워크 매칭 식별자입니다.
amountstring예정확한 소수 금액 문자열입니다. 부동 소수점을 사용하지 마세요.
pattern: ^[0-9]+(?:\.[0-9]+)?$
amount_rawstring예최소 단위의 정수 금액을 문자열로 표현합니다.
pattern: ^[0-9]+$
decimalsinteger예소수 자릿수입니다.
minimum: 0
confirmationsinteger예확인 횟수입니다.
minimum: 0
revisioninteger예전송 상태 리비전입니다. 트랜잭션 식별자만으로 다른 event_id 값을 합치지 마세요.
minimum: 0
lateboolean아니요뒤늦게 감지된 이체이며 청구서 상태 late와는 다릅니다.
simulatedboolean아니요시뮬레이션된 전송입니다.
valuationobject아니요
WebhookAsset

필드유형필수설명
symbolstring아니요알려진 경우 기호입니다. 기호만으로 토큰을 식별할 수 없습니다.
standardstring아니요알려진 경우 토큰 표준입니다.
contractstring아니요해당하는 경우 네트워크 내 토큰 계약입니다.
identifierstring아니요네트워크별 다른 토큰 식별자입니다.
WebhookContract

필드유형필수설명
chainstring예네트워크 코드입니다.
addressstring예관찰 대상 주소 또는 계약입니다.
tx_idstring예트랜잭션 ID입니다.
indexinteger예이벤트 인덱스입니다.
minimum: 0
heightinteger아니요블록 높이입니다.
minimum: 0
block_hashstring아니요블록 해시입니다.
kindstring아니요변경 유형입니다.
assetstring아니요알려진 경우 영향을 받은 자산 식별자입니다.
detailobject아니요
simulatedboolean아니요시뮬레이션된 이벤트입니다.
WebhookInvoice

필드유형필수설명
idstring예불투명 식별자입니다. 문자열로 유지하세요.
minLength: 1
referencestring아니요주문 참조입니다.
statusstring아니요pending은 결제 대기, seen은 감지됨, partial은 일부 결제, paid는 정산 완료입니다. underpaid/overpaid/late는 판단이 필요하며 expired는 기한 만료, canceled는 취소입니다.
pending, seen, paid, underpaid, partial, overpaid, expired, late, canceled
currencystring아니요주문 견적 통화입니다.
amountstring아니요정확한 소수 금액 문자열입니다. 부동 소수점을 사용하지 마세요.
pattern: ^[0-9]+(?:\.[0-9]+)?$
Notice

필드유형필수설명
subjectstring예고지 제목입니다.
textstring예고지 본문입니다.
Valuation

필드유형필수설명
currencystring예USD 등 평가 통화입니다.
amountstring예정확한 소수 금액 문자열입니다. 부동 소수점을 사용하지 마세요.
pattern: ^[0-9]+(?:\.[0-9]+)?$
ratestring예소수 문자열로 표현한 자산 환율입니다.
pattern: ^[0-9]+(?:\.[0-9]+)?$
rate_atstring예RFC 3339 형식의 UTC 날짜 및 시간입니다.
date-time
providerstring아니요알려진 경우 시세 출처 이름입니다.

서명 검증

JSON 분석 전에 공백과 줄바꿈을 포함한 원본 본문 바이트로 HMAC을 검증하세요. 시간 검사는 재생 공격을 제한하지만 영구적인 event_id 중복 제거를 대체하지 않습니다. 예시는 64자 16진수 서명과 서버 시간 기준 300초 이내의 타임스탬프만 허용합니다. 해당 웹훅 대상의 비밀 값을 사용하세요.

Node.js
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;
}

Node.js 예시 다운로드

Go
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
}

Go 예시 다운로드

웹훅 엔드포인트

특정 프로젝트에 포트 443의 공개 HTTPS URL을 추가하세요. URL의 자격 증명, 프래그먼트, 리디렉션과 비공개 목적지는 허용되지 않습니다. 계정당 최대 20개 엔드포인트를 사용할 수 있습니다. 각각 별도 비밀 값이 있으며 최초 생성 시에만 반환되므로 서버에 저장하세요.

같은 프로젝트와 모드에서 같은 URL을 다시 생성하면 200과 비밀 값이 제외된 기존 엔드포인트를 반환합니다. 새 엔드포인트는 201를 반환합니다. DELETE는 안전한 반복 호출을 포함하여 200과 id, deleted: true를 반환합니다. 키에는 해당 모드와 허용된 프로젝트만 표시되며 전달은 프로젝트 설정을 따릅니다.

SSE 스트림

SSE는 서버 통합과 실시간 표시용입니다. 전달이나 누락 이벤트 재생을 보장하지 않습니다. Bearer 헤더를 사용하는 서버 측 HTTP 클라이언트를 사용하세요. 표준 브라우저 EventSource는 이 헤더를 보낼 수 없습니다. 브라우저 코드나 쿼리 문자열에 API 키를 넣지 마세요.

메시지는 event: payment와 자체 StreamEvent 구조를 사용하며 webhook v1이나 REST Event와 다릅니다. 주석은 콜론으로 시작하고 하트비트는 약 20초마다 도착합니다. retry: 5000은 재연결 힌트입니다. 연결이 끊긴 후에는 events에서 조회 가능한 블록체인 이벤트 기록을 가져오세요.

StreamEvent

SSE data의 JSON이며 webhook v1과 다릅니다. 결제 외 이벤트는 payment를 생략하며 contract를 포함할 수 있습니다.

필드유형필수설명
event_idstring예불투명 식별자입니다. 문자열로 유지하세요.
minLength: 1
event_typestring예이벤트 유형입니다. 새로운 유형이 추가될 수 있습니다.
chainstring예네트워크 코드입니다.
servicestring아니요알려진 경우 서비스입니다.
watch_idstring아니요모니터링에 연결된 경우 프로젝트 모니터링 ID입니다.
minLength: 1
occurred_atstring예RFC 3339 형식의 UTC 날짜 및 시간입니다.
date-time
paymentobject | null아니요결제 문서이며, 결제 외 이벤트는 null입니다. GET /events는 contract/notice 본문을 반환하지 않습니다.
sandboxboolean아니요테스트 이벤트입니다. 생략하면 false입니다.
project_idstring아니요불투명 식별자입니다. 문자열로 유지하세요.
minLength: 1
project_namestring아니요프로젝트 이름입니다.
urlstring아니요Account 이벤트 링크입니다.
uri
contractobject아니요
modestring아니요live 또는 test입니다.
live, test

결제 및 청구서 상태

블록체인 결제 상태와 주문 상태는 다릅니다. payment.pending은 충분한 확인 전의 감지, payment.confirmed는 네트워크/프로젝트의 확인 규칙 충족, payment.rolled_back은 거래 되돌림을 의미합니다. pending, 출금 또는 모의 이벤트를 근거로 주문을 이행하지 마세요.

Invoice의미
pending결제 대기 중
partial일부 입금됨. 프로젝트의 부분 결제 정책이 적용됩니다
paid결제 수락됨
underpaid부족 입금은 판단이 필요합니다
overpaid초과 입금은 적용 정책에 따른 판단이 필요합니다
late만료 후 결제는 판단이 필요합니다
expired결제 기한 만료
canceled청구서가 취소되었습니다. 기록은 계속 조회할 수 있습니다.

needs_decision은 underpaid, overpaid 및 late에 있습니다. Account에서 해결하세요. 체인 재구성과 재포함은 새 상태 전환 및 새 payment.revision을 만들 수 있습니다. confirmed는 향후 변경이 없음을 무조건 보장하지 않습니다.

오류 및 안전한 재시도

application/json
{"error":{"code":"forbidden","message":"this API key is read-only"}}

로직에는 HTTP 상태 및 error.code를, 진단에는 message를 사용하세요. 성공한 목록에는 data가 있고 단일 객체는 직접 반환됩니다. Health는 503 응답을 포함해 별도 status/version 형식을 사용합니다. 정확한 메시지 문구에 의존하지 마세요.

HTTP작업
400JSON, 형식 및 매개변수를 수정하세요. 변경 없이 재시도하지 마세요.
401키, 만료, 일시 중지 및 취소 상태를 확인하세요.
403범위, 읽기/쓰기, 프로젝트, 서비스, IP 및 계정 인증을 확인하세요.
404ID, 키 모드 및 접근 권한을 확인하세요. 제한에 따라 리소스가 숨겨질 수 있습니다.
402Monitoring 감시 한도입니다. 요금제를 확인하세요.
409충돌, 비활성 서비스, 사용 중인 풀 또는 한도입니다. 코드를 확인하세요.
429Retry-After를 준수하고 계정의 전체 요청 속도를 줄이세요
500 / 503제한된 점진적 대기로 재시도하세요. 청구서 생성은 원래 Idempotency-Key만 사용하세요

시간 초과는 작업이 실행되지 않았다는 뜻이 아닙니다. 재시도 시 청구서의 작업 키를 바꾸지 마세요. 중복 방지 기능이 없는 메서드는 먼저 상태를 읽으세요. 모든 POST가 멱등적이라고 가정하면 안 됩니다.

네트워크 및 한도

선택한 API 호스트의 chains로 네트워크, 자산, 기능 및 확인 카탈로그를 조회하세요. 일반 카탈로그에 있다고 해서 해당 환경에서 활성화되거나 모든 작업을 지원하는 것은 아닙니다. 서비스 사용량 한도와 예산은 Account에서, 가격은 요금 페이지에서 확인하세요.

계정의 인증된 REST 한도는 유료 서비스가 없으면 초당 10회, 있으면 초당 50회입니다. 짧은 급증 허용량은 20/100입니다. 계정의 모든 키와 모드가 이 한도를 공유합니다. 초과하면 Retry-After와 함께 429을 반환합니다. 서비스 사용량 한도와 API 요청 속도 제한은 별도입니다.

서비스 요금 및 한도

API 버전

이 참조는 v1(릴리스 1.0.0 62)을 설명하며 2026-10-10에 갱신되었습니다. 새 기능을 사용하기 전에 GET /v1/health로 배포 버전을 확인하세요. 문서 버전은 배포를 확인하지 않습니다.

공개 API계약
https://api.cryptanio.com/v1

안정적인 통합에는 /v1을 사용하세요. 버전 없는 경로는 환경의 현재 안정 계약을 선택하며, 알 수 없는 /vN은 404를 반환합니다. X-API-Version은 계약을, X-Server-Version 및 health.version는 제품 릴리스를 뜻하며 다른 API 계약이 아닙니다.

OpenAPI 3.1 다운로드

API 변경 사항

릴리스 1.0.0 62에는 Node.js, Python, PHP, Go 및 Rust용 다운로드 SDK와 AI 에이전트용 로컬 MCP가 포함됩니다. 참조, Markdown 및 원시 HTTP 예시는 별도로 제공됩니다. 공개 샌드박스는 없습니다. API 동작은 변경되지 않습니다.

v1 클라이언트는 추가 응답 필드를 허용해야 합니다. 알 수 없는 이벤트 유형을 수락해도 주문 이행이나 결제 적립을 뜻하지 않습니다. 사용 전에 선택한 환경의 버전에서 변경 기능을 제공하는지 확인하세요.