Invoices let you request crypto payments to a specific address for a specific token amount. Vilna tracks incoming transactions in real time, detects partial payments, overpayments, and confirms payments once they reach the required block depth. Each invoice has a public URL you can share with payers - no authentication required on their end. To see how invoices fit into a broader payment flow, check the payment monitoring use case.
Vilna watches the blockchain and tells you when an invoice is paid. It never holds the funds or controls the receiving wallet: the address is yours - one you provide, or derive from your own extended public key - and the payer sends to it directly. See the Non-custodial Service Notice.
You need a Vilna API key with api:invoice:read and api:invoice:write permissions. See Authentication for details.
An invoice moves through the following statuses:
| Status | Description |
|---|---|
pending | Waiting for payment. No transactions received yet. |
underpaid | Partial payment received. The amount is below the requested total. |
paid | Full amount received. Waiting for block confirmations. |
confirmed | Payment confirmed with sufficient block confirmations. |
overpaid | Received more than the requested amount. |
cancelled | Cancelled by the merchant. |
expired | The invoice passed its deadline without receiving full payment. |
Status transitions:
| From | To | Trigger |
|---|---|---|
pending | paid | Full payment received |
pending | underpaid | Partial payment received |
pending | overpaid | More than required amount received |
pending | cancelled | Cancelled by merchant |
pending | expired | Deadline passed |
underpaid | paid | Remaining amount received |
underpaid | overpaid | More than remaining amount received |
underpaid | cancelled | Cancelled by merchant |
underpaid | expired | Deadline passed |
paid | confirmed | Block confirmations reached |
overpaid | confirmed | Block confirmations reached |
confirmed, cancelled, and expired are terminal - no further transitions.
To create an invoice, specify the deposit address, token (as a CAIP-19 asset identifier), amount in base units, and an expiration time. The address must belong to your project and must not already have an active invoice.
curl -X POST "https://api.vilna.io/v1/invoices" \
-H "X-Api-Key: ${VILNA_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
"asset_gid": "eip155:56/bep20:0x55d398326f99059fF775485246999027B3197955",
"amount": "42500000",
"expires_at": "2026-06-15T18:00:00Z",
"metadata": {
"order_id": "ORD-20260615-1042",
"customer": "john@example.com"
}
}'Response (abbreviated):
{
"item": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
"asset_gid": "eip155:56/bep20:0x55d398326f99059fF775485246999027B3197955",
"amount": "42500000",
"received_amount": "0",
"status": "pending",
"expires_at": "2026-06-15T18:00:00Z",
"metadata": {
"order_id": "ORD-20260615-1042",
"customer": "john@example.com"
},
"created_at": "2026-06-15T10:30:00Z",
"updated_at": "2026-06-15T10:30:00Z",
"status_logs": [
{
"status": "pending",
"comment": null,
"changed_at": "2026-06-15T10:30:00Z"
}
],
"transactions": []
},
"references": {
"tokens": {},
"blockchains": {}
}
}Key fields:
amount- the requested payment in base units (smallest token denomination). For USDT on BSC with 6 decimals,"42500000"equals 42.5 USDT.metadata- optional key-value pairs for linking invoices to your internal records.expires_at- the payment deadline. After this time, the invoice moves toexpiredif full payment has not arrived.
An address can have only one active invoice at a time. Attempting to create a second invoice for the same address returns a 409 Conflict error.
Every invoice has a public endpoint that requires no authentication. The invoice UUID acts as the access token - anyone with the link can view the payment details.
curl "https://api.vilna.io/v1/public/invoices/550e8400-e29b-41d4-a716-446655440000"The public response includes the address, amount, status, and transaction history - everything a payer needs. It excludes merchant-only fields like metadata.
Use this endpoint to build custom payment pages, or direct payers to your hosted payment UI.
Retrieve full invoice details, including the status log and detected transactions.
curl "https://api.vilna.io/v1/invoices/550e8400-e29b-41d4-a716-446655440000" \
-H "X-Api-Key: ${VILNA_API_KEY}"Once a payment arrives, the response includes transaction details:
{
"item": {
"status": "paid",
"received_amount": "42500000",
"transactions": [
{
"hash": "0xabc123def456789012345678901234567890123456789012345678901234abcd",
"block_number": 12345678,
"is_success": true,
"detected_at": "2026-06-15T12:05:00Z",
"confirmed_at": null,
"deposits": [
{
"asset_gid": "eip155:56/bep20:0x55d398326f99059fF775485246999027B3197955",
"amount": "42500000",
"is_matched": true
}
]
}
],
"status_logs": [
{ "status": "pending", "comment": null, "changed_at": "2026-06-15T10:30:00Z" },
{ "status": "paid", "comment": null, "changed_at": "2026-06-15T12:05:00Z" }
]
}
}Each transaction includes:
is_success- whether the transaction succeeded on-chain. Failed transactions appear in the list but do not count toward payment.confirmed_at-nulluntil the transaction reaches the required number of block confirmations.deposits- individual token movements within the transaction. Theis_matchedflag indicates whether the token matches the one the invoice expects.
Vilna tracks every deposit and updates the invoice status automatically:
- Partial payment: The invoice moves to
underpaid. Thereceived_amountfield reflects the running total. Additional payments accumulate until the full amount is reached. - Full payment: The invoice moves to
paid, then toconfirmedonce block confirmations are sufficient. - Overpayment: If the total received exceeds the requested amount, the status moves to
overpaid. Handle refunds in your application logic.
You can monitor these transitions by polling the invoice endpoint or by setting up webhook notifications for invoice status changes.
Retrieve all invoices with optional status filtering and pagination.
# List all pending and underpaid invoices
curl "https://api.vilna.io/v1/invoices?status=pending&status=underpaid&limit=20&page=1" \
-H "X-Api-Key: ${VILNA_API_KEY}"The list endpoint returns InvoiceSummary objects (without status_logs and transactions). Use GET /invoices/{invoice_id} for full details on a specific invoice.
Cancel an active invoice to free the address for new invoices. Only invoices in pending or underpaid status can be cancelled. You can include an optional comment (up to 64 characters) explaining the reason.
curl -X POST "https://api.vilna.io/v1/invoices/550e8400-e29b-41d4-a716-446655440000/actions/cancel" \
-H "X-Api-Key: ${VILNA_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"comment": "Customer requested cancellation"
}'A successful cancellation returns 204 No Content. The operation is idempotent - cancelling an already-cancelled invoice returns the same response.
The comment field is optional. You can send an empty body or omit the request body entirely.
Events & Monitoring
Set up webhooks to receive real-time invoice status updates instead of polling
Core Concepts
Learn about CAIP identifiers, base unit amounts, and the response envelope pattern
Integration Patterns
Deposit detection, payment monitoring, and other common integration scenarios
Platform API reference
Full endpoint catalog including invoice endpoints with request and response schemas