Get an invoice
GET/v1/invoices/{id}
Contract v1 / 1.0.0 62 / 2026-10-10
On this page
Read an invoice in a project allowed by the API key.
Key permissions: invoices / read / payments.
Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
id | path | Yes | Opaque resource ID.string minLength: 1 |
Request examples
Examples perform a real operation in your account. Set the environment variables named in the code. Keep your key private; write requests are not retried automatically.
#!/usr/bin/env bash
set -euo pipefail
: "${CRYPTANIO_RESOURCE_ID:?Set CRYPTANIO_RESOURCE_ID}"
: "${CRYPTANIO_API_KEY:?Set CRYPTANIO_API_KEY}"
urlencode() {
local LC_ALL=C text="$1" char index
for ((index=0; index<${#text}; index++)); do
char="${text:index:1}"
case "$char" in [a-zA-Z0-9.~_-]) printf '%s' "$char" ;; *) printf '%%%02X' "'$char" ;; esac
done
}
response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT
transfer_status=0
http_status="$(curl \
--silent --show-error --max-time 30 --request GET \
--header "Authorization: Bearer $CRYPTANIO_API_KEY" \
--header 'Accept: application/json' \
"https://api.cryptanio.com/v1/invoices/$(urlencode "$CRYPTANIO_RESOURCE_ID")" \
--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
// Go; standard library only. Run: go run request.go
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func env(name string) string {
value := os.Getenv(name)
if value == "" { fmt.Fprintln(os.Stderr, "Set", name); os.Exit(1) }
return value
}
func run() error {
path := "/v1/invoices/{id}"
path = strings.ReplaceAll(path, "{id}", url.PathEscape(env("CRYPTANIO_RESOURCE_ID")))
endpoint, err := url.Parse("https://api.cryptanio.com" + path)
if err != nil { return err }
query := endpoint.Query()
endpoint.RawQuery = query.Encode()
request, err := http.NewRequest("GET", endpoint.String(), nil)
if err != nil { return err }
request.Header.Set("Authorization", "Bearer " + env("CRYPTANIO_API_KEY"))
request.Header.Set("Accept", "application/json")
client := &http.Client{Timeout: 30 * time.Second, CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }}
response, err := client.Do(request)
if err != nil { return err }
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
body, err := io.ReadAll(response.Body)
if err != nil { return err }
return fmt.Errorf("HTTP %d: %s", response.StatusCode, body)
}
_, err = io.Copy(os.Stdout, response.Body)
return err
}
func main() {
if err := run(); err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
}
// Cargo.toml dependencies:
// reqwest = { version = "=0.12.24", features = ["blocking", "json"] }
// serde_json = "=1.0.145"
use std::{error::Error, io, time::Duration};
use reqwest::{blocking::Client, redirect::Policy, Method, Url};
fn env(name: &str) -> Result<String, Box<dyn Error>> {
let value = std::env::var(name)?;
if value.is_empty() { return Err(format!("Set {name}").into()); }
Ok(value)
}
fn run() -> Result<(), Box<dyn Error>> {
let mut url = Url::parse("https://api.cryptanio.com")?;
{
let mut segments = url.path_segments_mut().map_err(|_| "Invalid base URL")?;
segments.clear();
segments.push("v1");
segments.push("invoices");
segments.push(&env("CRYPTANIO_RESOURCE_ID")?);
}
let client = Client::builder().timeout(Duration::from_secs(30)).redirect(Policy::none()).build()?;
let request = client.request(Method::GET, url)
.header("Authorization", format!("Bearer {}", env("CRYPTANIO_API_KEY")?))
.header("Accept", "application/json".to_owned());
let mut response = request.send()?;
let status = response.status();
if !status.is_success() { return Err(format!("HTTP {}: {}", status.as_u16(), response.text()?).into()); }
io::copy(&mut response, &mut io::stdout())?;
Ok(())
}
fn main() {
if let Err(error) = run() { eprintln!("{error}"); std::process::exit(1); }
}
<?php
// PHP 8 with the cURL extension. Run this on your server.
function env(string $name): string {
$value = getenv($name);
if ($value === false || $value === '') throw new RuntimeException('Set ' . $name);
return $value;
}
try {
$path = '/v1/invoices/{id}';
$path = str_replace('{id}', rawurlencode(env('CRYPTANIO_RESOURCE_ID')), $path);
$query = [
];
$url = 'https://api.cryptanio.com' . $path . ($query ? '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986) : '');
$headers = [
'Authorization: ' . 'Bearer ' . env('CRYPTANIO_API_KEY'),
'Accept: ' . 'application/json',
];
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($curl);
if ($response === false) throw new RuntimeException(curl_error($curl));
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($status < 200 || $status >= 300) throw new RuntimeException('HTTP ' . $status . ': ' . $response);
echo $response;
} catch (Throwable $error) {
fwrite(STDERR, $error->getMessage() . PHP_EOL);
exit(1);
}
# Python 3; standard library only.
import json
import os
import sys
from urllib.error import HTTPError, URLError
from urllib.parse import quote, urlencode
from urllib.request import HTTPRedirectHandler, Request, build_opener
def env(name):
result = os.environ.get(name)
if not result:
raise ValueError("Set " + name)
return result
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
try:
path = "/v1/invoices/{id}"
path = path.replace("{id}", quote(env("CRYPTANIO_RESOURCE_ID"), safe=""))
query = {
}
url = "https://api.cryptanio.com" + path + ("?" + urlencode(query) if query else "")
headers = {
"Authorization": "Bearer " + env("CRYPTANIO_API_KEY"),
"Accept": "application/json",
}
data = None
request = Request(url, data=data, headers=headers, method="GET")
with build_opener(NoRedirect()).open(request, timeout=30) as response:
if not 200 <= response.status < 300:
raise ValueError("HTTP " + str(response.status))
sys.stdout.buffer.write(response.read())
except HTTPError as error:
print("HTTP " + str(error.code) + ": " + error.read().decode("utf-8", errors="replace"), file=sys.stderr)
sys.exit(1)
except (ValueError, URLError, OSError) as error:
print(str(error), file=sys.stderr)
sys.exit(1)
// Node.js 18+. Run on your server, never in a browser.
const env = name => {
const result = process.env[name];
if (!result) throw new Error(`Set ${name}`);
return result;
};
try {
let path = "/v1/invoices/{id}";
path = path.replace("{id}", encodeURIComponent(env("CRYPTANIO_RESOURCE_ID")));
const url = new URL(path, "https://api.cryptanio.com");
const headers = {
"Authorization": "Bearer " + env("CRYPTANIO_API_KEY"),
"Accept": "application/json",
};
const response = await fetch(url, {
method: "GET", headers, redirect: "manual",
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
if (response.body) {
for await (const chunk of response.body) process.stdout.write(chunk);
}
} catch (error) {
console.error(error.message);
process.exitCode = 1;
}
Responses and errors
200 Successful response.
| Field | Type | Required | Description |
|---|---|---|---|
checkout_url | string | No | Hosted Cryptanio payment page. Creation/retry of a payer-selected invoice returns an interactive capability that permits method selection until payment is detected. Fixed-method creation and every GET/list return a separate read-only link. Share either link only with the intended payer; neither grants account or API-key access. Confirm payment using the invoice API or a verified webhook, not a browser redirect.uri |
id | string | Yes | Opaque identifier; keep it as a string. minLength: 1 |
status | string | Yes | pending awaits payment; seen detected; partial partially paid; paid settled; underpaid/overpaid/late need a decision; expired deadline passed; canceled voided.pending, seen, paid, underpaid, partial, overpaid, expired, late, canceled |
project_id | string | Yes | Opaque identifier; keep it as a string. minLength: 1 |
reference | string | No | Order reference. |
customer | object | No | |
product | object | No | |
payer | object | No | |
metadata | object | No | Up to 20 pairs, at most 8192 JSON bytes. Do not include secrets. |
invoice_currency | string | No | Order quote currency. |
invoice_amount | string | No | Exact decimal amount as a string; do not use floating point. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
chain | string | Yes | Network code. |
asset | string | Yes | Payment asset. |
address | string | Yes | Payment address. |
payment_route | object | No | Required on-chain discriminator when an automatic source issues a tag, memo, TON comment or Solana Pay reference. Keep it with the receiving address; the merchant order reference is a separate field. |
payment_uri | string | No | Solana Pay transfer request with the recipient, exact amount, supported token mint and mandatory reference. Encode this URI in the payment QR instead of the plain receiving address.uri |
amount | string | string | Yes | Actual amount the buyer must send; may differ from asked to distinguish invoices. Empty until a method is selected. |
amount_raw | string | string | Yes | Integer smallest-unit amount as a string. Empty until a method is selected. |
asked | string | string | Yes | Originally requested crypto amount. Empty until a method is selected. |
decimals | integer | Yes | Asset decimal places. minimum: 0 |
tolerance | string | No | Tolerance in smallest units; unlike request.tolerance. pattern: ^[0-9]+$ |
sandbox | boolean | Yes | A test invoice does not prove a real blockchain payment. |
needs_decision | boolean | No | A decision is required for underpaid, overpaid, or late. |
paid | string | No | Received amount in smallest units. pattern: ^[0-9]+$ |
tx_id | string | No | Payment transaction identifier. |
paid_at | string | No | UTC date and time in RFC 3339.date-time |
expires_at | string | Yes | UTC date and time in RFC 3339.date-time |
created_at | string | Yes | UTC date and time in RFC 3339.date-time |
{
"id": "2100",
"status": "pending",
"project_id": "2002",
"reference": "order-42",
"chain": "ethereum",
"asset": "USDC",
"address": "0x52908400098527886E0F7030069857D2E4169EE7",
"amount": "12.5",
"amount_raw": "12500000",
"asked": "12.5",
"decimals": 6,
"sandbox": true,
"expires_at": "2026-10-06T11:00:00Z",
"created_at": "2026-10-06T10:00:00Z"
}401 No valid active API key (unauthorized).
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}403 Key disallows the operation, project, service or IP; email unconfirmed.
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}404 Object not found within the key's scope (not_found).
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}429 Too many requests (rate_limited); retry after Retry-After.
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}500 Internal error (internal).
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}503 Temporary unavailability (unavailable); retry with backoff.
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}Nested objects
Invoice
| Field | Type | Required | Description |
|---|---|---|---|
checkout_url | string | No | Hosted Cryptanio payment page. Creation/retry of a payer-selected invoice returns an interactive capability that permits method selection until payment is detected. Fixed-method creation and every GET/list return a separate read-only link. Share either link only with the intended payer; neither grants account or API-key access. Confirm payment using the invoice API or a verified webhook, not a browser redirect.uri |
id | string | Yes | Opaque identifier; keep it as a string. minLength: 1 |
status | string | Yes | pending awaits payment; seen detected; partial partially paid; paid settled; underpaid/overpaid/late need a decision; expired deadline passed; canceled voided.pending, seen, paid, underpaid, partial, overpaid, expired, late, canceled |
project_id | string | Yes | Opaque identifier; keep it as a string. minLength: 1 |
reference | string | No | Order reference. |
customer | object | No | |
product | object | No | |
payer | object | No | |
metadata | object | No | Up to 20 pairs, at most 8192 JSON bytes. Do not include secrets. |
invoice_currency | string | No | Order quote currency. |
invoice_amount | string | No | Exact decimal amount as a string; do not use floating point. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
chain | string | Yes | Network code. |
asset | string | Yes | Payment asset. |
address | string | Yes | Payment address. |
payment_route | object | No | Required on-chain discriminator when an automatic source issues a tag, memo, TON comment or Solana Pay reference. Keep it with the receiving address; the merchant order reference is a separate field. |
payment_uri | string | No | Solana Pay transfer request with the recipient, exact amount, supported token mint and mandatory reference. Encode this URI in the payment QR instead of the plain receiving address.uri |
amount | string | string | Yes | Actual amount the buyer must send; may differ from asked to distinguish invoices. Empty until a method is selected. |
amount_raw | string | string | Yes | Integer smallest-unit amount as a string. Empty until a method is selected. |
asked | string | string | Yes | Originally requested crypto amount. Empty until a method is selected. |
decimals | integer | Yes | Asset decimal places. minimum: 0 |
tolerance | string | No | Tolerance in smallest units; unlike request.tolerance. pattern: ^[0-9]+$ |
sandbox | boolean | Yes | A test invoice does not prove a real blockchain payment. |
needs_decision | boolean | No | A decision is required for underpaid, overpaid, or late. |
paid | string | No | Received amount in smallest units. pattern: ^[0-9]+$ |
tx_id | string | No | Payment transaction identifier. |
paid_at | string | No | UTC date and time in RFC 3339.date-time |
expires_at | string | Yes | UTC date and time in RFC 3339.date-time |
created_at | string | Yes | UTC date and time in RFC 3339.date-time |
ID
Opaque identifier. Its format is not guaranteed; do not convert to a number.
string Opaque identifier. Its format is not guaranteed; do not convert to a number.
InvoiceStatus
pending awaits payment; seen detected; partial partially paid; paid settled; underpaid/overpaid/late need a decision; expired deadline passed; canceled voided.
string pending awaits payment; seen detected; partial partially paid; paid settled; underpaid/overpaid/late need a decision; expired deadline passed; canceled voided.
Customer
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Customer identifier in your system. maxLength: 120 |
Product
| Field | Type | Required | Description |
|---|---|---|---|
id | string | No | Product identifier in your system. maxLength: 120 |
name | string | No | Product name. maxLength: 200 |
quantity | integer | No | Quantity. minimum: 1; maximum: 1000000 |
Payer
| Field | Type | Required | Description |
|---|---|---|---|
comment | string | Yes | Payer comment. maxLength: 500 |
InvoiceMetadata
Up to 20 pairs, at most 8192 JSON bytes. Do not include secrets.
object Up to 20 pairs, at most 8192 JSON bytes. Do not include secrets.
Decimal
string
RawAmount
string
Error
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |