Create and Track Invoice
This guide walks through the full payment lifecycle: creating an invoice, sending the payment url to a customer, and confirming payment
Payment Flow
Section titled “Payment Flow”- Create an invoice: your backend calls
/invoice/createand receives apay_url. - Send the link: redirect the customer or embed the URL in your UI.
- Customer pays: they choose a cryptocurrency and network, then send the exact amount.
- Confirm payment: check
is_paidvia/invoice/infoor receive a webhook notification.
Step 1: Create an Invoice
Section titled “Step 1: Create an Invoice”See the endpoint for the full list of parameters
curl --request POST \ --url https://api.cru.cash/v1/invoice/create \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "amount": "100", "currency": "USD", "lifetime": 30, "webhook": "https://example.com/webhook", "payload": "{\"order_id\": \"A-1042\"}", "ui": { "description": "Payment for order #1042", "redirect_url": "https://example.com/payment-success" }}'async function createInvoice() { const response = await fetch("https://api.cru.cash/v1/invoice/create", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ amount: "100", currency: "USD", lifetime: 30, webhook: "https://example.com/webhook", payload: JSON.stringify({ order_id: "A-1042" }), ui: { description: "Payment for order #1042", redirect_url: "https://example.com/payment-success", }, }), });
const data = await response.json();
if (!response.ok) { throw new Error( `HTTP ${response.status}: ${data?.error?.code} - ${data?.error?.message}` ); }
const { invoice, pay_url } = data;
console.log("Pay URL:", pay_url);
return { invoiceId: invoice.id, pay_url };}interface CreateInvoiceResponse { invoice: { id: string; expires_at: number; }; pay_url: string; test_mode: boolean;}
async function createInvoice(): Promise<CreateInvoiceResponse> { const response = await fetch("https://api.cru.cash/v1/invoice/create", { method: "POST", headers: { Authorization: `Bearer YOUR_API_KEY`, "Content-Type": "application/json", }, body: JSON.stringify({ amount: "100", currency: "USD", lifetime: 30, webhook: "https://example.com/webhook", payload: JSON.stringify({ order_id: "A-1042" }), ui: { description: "Payment for order #1042", redirect_url: "https://example.com/payment-success", }, }), });
const result = await response.json(); if (!response.ok) { throw new Error( `HTTP ${response.status}: ${result?.error?.code} - ${result?.error?.message}` ); } return result;}import jsonimport timeimport requests
def create_invoice() -> dict: response = requests.post( "https://api.cru.cash/v1/invoice/create", headers={ "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, json={ "amount": "100", "currency": "USD", "lifetime": 30, "webhook": "https://example.com/webhook", "payload": json.dumps({"order_id": "A-1042"}), "ui": { "description": "Payment for order #1042", "redirect_url": "https://example.com/payment-success", }, }, )
result = response.json() if not response.ok: error = result.get("error", {}) raise Exception( f"HTTP {response.status_code}: {error.get('code')} - {error.get('message')}" )
return resultfunc doPost(url string, body map[string]any) (map[string]any, error) { b, _ := json.Marshal(body)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(b)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer YOUR_API_KEY") req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close()
var result map[string]any json.NewDecoder(resp.Body).Decode(&result)
if resp.StatusCode >= 400 { errObj, _ := result["error"].(map[string]any) return nil, fmt.Errorf("HTTP %d: %v - %v", resp.StatusCode, errObj["code"], errObj["message"]) }
return result, nil}
func createInvoice() (map[string]any, error) { return doPost("https://api.cru.cash/v1/invoice/create", map[string]any{ "amount": "100", "currency": "USD", "lifetime": 30, "webhook": "https://example.com/webhook", "payload": `{"order_id":"A-1042"}`, "ui": map[string]any{ "description": "Payment for order #1042", "redirect_url": "https://example.com/payment-success", }, })}Response example
{ "invoice": { "id": "179a4662-a6e0-4129-aeba-04c4c2a8f074", "expires_at": 1773689470 }, "pay_url": "https://pay.cru.cash/invoice/179a4662-a6e0-4129-aeba-04c4c2a8f074", "test_mode": false}Send the pay_url to the customer so they can open the payment page.
Step 2: Track Invoice Status
Section titled “Step 2: Track Invoice Status”Once you have an invoice id, poll /invoice/info or wait for a webhook. See the endpoint for all response fields.
curl --request POST \ --url https://api.cru.cash/v1/invoice/info \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "invoice_id": "YOUR_INVOICE_ID"}'async function getInvoice(invoiceId) { const response = await fetch("https://api.cru.cash/v1/invoice/info", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ invoice_id: invoiceId }), });
const data = await response.json();
if (!response.ok) { throw new Error( `HTTP ${response.status}: ${data?.error?.code} - ${data?.error?.message}` ); }
return data;}
// Poll until paidasync function waitForPayment(invoiceId, intervalMs = 5000) { while (true) { const invoice = await getInvoice(invoiceId);
if (invoice.is_paid) { console.log("Payment confirmed"); return invoice; }
if (invoice.pay_status === "end") { throw new Error("Invoice expired"); }
await new Promise((resolve) => setTimeout(resolve, intervalMs)); }}
// Usageconst invoice = await createInvoice();await waitForPayment(invoice.invoiceId);interface GetInvoiceRequest { invoice_id: string;}
interface InvoiceInfo { id: string; fiat_amount: string; fiat_currency: string; crypto_amount: string; network_fee: string; crypto: string; network: string; pay_status: string; processing_status: string; is_paid: boolean; created_at: string; is_testing: boolean; payload: string | null;}
async function getInvoice(data: GetInvoiceRequest): Promise<InvoiceInfo> { const response = await fetch("https://api.cru.cash/v1/invoice/info", { method: "POST", headers: { Authorization: `Bearer YOUR_API_KEY`, "Content-Type": "application/json", }, body: JSON.stringify(data), });
const result = await response.json(); if (!response.ok) { throw new Error( `HTTP ${response.status}: ${result?.error?.code} - ${result?.error?.message}` ); } return result;}
// Poll until paidasync function waitForPayment( invoiceId: string, intervalMs = 5000): Promise<InvoiceInfo> { while (true) { const invoice = await getInvoice({ invoice_id: invoiceId }); if (invoice.is_paid) { return invoice; } if (invoice.pay_status === "end") { throw new Error("Invoice expired"); } await new Promise<void>((resolve) => setTimeout(resolve, intervalMs)); }}
// Usage(async () => { const { invoice, pay_url } = await createInvoice();
console.log("Pay URL:", pay_url);
const paidInvoice = await waitForPayment(invoice.id); console.log("Payment confirmed:", paidInvoice.id);})();def get_invoice(invoice_id: str) -> dict: response = requests.post( "https://api.cru.cash/v1/invoice/info", headers={ "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, json={"invoice_id": invoice_id}, )
result = response.json() if not response.ok: error = result.get("error", {}) raise Exception( f"HTTP {response.status_code}: {error.get('code')} - {error.get('message')}" )
return result
# Poll until paiddef wait_for_payment(invoice_id: str, interval_ms: int = 5000) -> dict: while True: invoice = get_invoice(invoice_id) if invoice["is_paid"]: return invoice if invoice["pay_status"] == "end": raise Exception("Invoice expired") time.sleep(interval_ms / 1000)
# Usageif __name__ == "__main__": result = create_invoice() print("Pay URL:", result["pay_url"])
paid_invoice = wait_for_payment(result["invoice"]["id"]) print("Payment confirmed:", paid_invoice["id"])func getInvoice(invoiceID string) (map[string]any, error) { return doPost("https://api.cru.cash/v1/invoice/info", map[string]any{ "invoice_id": invoiceID, })}
// Poll until paidfunc waitForPayment(invoiceID string, intervalMs int) (map[string]any, error) { for { invoice, err := getInvoice(invoiceID) if err != nil { return nil, err }
if invoice["is_paid"] == true { return invoice, nil } if invoice["pay_status"] == "end" { return nil, fmt.Errorf("invoice expired") }
time.Sleep(time.Duration(intervalMs) * time.Millisecond) }}
// Usagefunc main() { result, err := createInvoice() if err != nil { panic(err) }
invoice := result["invoice"].(map[string]any) fmt.Println("Pay URL:", result["pay_url"])
paidInvoice, err := waitForPayment(invoice["id"].(string), 5000) if err != nil { panic(err) }
fmt.Println("Payment confirmed:", paidInvoice["id"])}Response example
{ "id": "179a4662-a6e0-4129-aeba-04c4c2a8f074", "fiat_amount": "30", "fiat_currency": "USD", "crypto_amount": "0", "network_fee": "0", "crypto": "NONE", "network": "NONE", "pay_status": "not_paid", "processing_status": "pending", "is_paid": false, "created_at": "2026-03-16T19:01:11Z", "is_testing": false, "payload": ""}Full Example
Section titled “Full Example”A complete example. create an invoice and poll until it’s paid
RESPONSE=$(curl --silent --request POST \ --url https://api.cru.cash/v1/invoice/create \ --header 'Authorization: Bearer YOUR_API_KEY' \ --header 'Content-Type: application/json' \ --data '{ "amount": "100", "currency": "USD", "lifetime": 30, "webhook": "https://example.com/webhook", "payload": "{\"order_id\": \"A-1042\"}", "ui": { "description": "Payment for order #1042", "redirect_url": "https://example.com/payment-success" } }')
INVOICE_ID=$(echo $RESPONSE | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)PAY_URL=$(echo $RESPONSE | grep -o '"pay_url":"[^"]*"' | cut -d'"' -f4)
echo "Pay URL: $PAY_URL"
while true; do INFO=$(curl --silent --request POST \ --url https://api.cru.cash/v1/invoice/info \ --header 'Authorization: Bearer YOUR_API_KEY' \ --header 'Content-Type: application/json' \ --data "{\"invoice_id\": \"$INVOICE_ID\"}")
IS_PAID=$(echo $INFO | grep -o '"is_paid":[^,}]*' | cut -d':' -f2) PAY_STATUS=$(echo $INFO | grep -o '"pay_status":"[^"]*"' | cut -d'"' -f4)
if [ "$IS_PAID" = "true" ]; then echo "Payment confirmed" break fi
if [ "$PAY_STATUS" = "end" ]; then echo "Invoice expired" exit 1 fi
sleep 5doneasync function createInvoice() { const response = await fetch("https://api.cru.cash/v1/invoice/create", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ amount: "100", currency: "USD", lifetime: 30, webhook: "https://example.com/webhook", payload: JSON.stringify({ order_id: "A-1042" }), ui: { description: "Payment for order #1042", redirect_url: "https://example.com/payment-success", }, }), });
const data = await response.json(); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${data?.error?.code} - ${data?.error?.message}`); } return data;}
async function getInvoice(invoiceId) { const response = await fetch("https://api.cru.cash/v1/invoice/info", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ invoice_id: invoiceId }), });
const data = await response.json(); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${data?.error?.code} - ${data?.error?.message}`); } return data;}
async function waitForPayment(invoiceId, intervalMs = 5000) { while (true) { const invoice = await getInvoice(invoiceId); if (invoice.is_paid) { return invoice; } if (invoice.pay_status === "end") { throw new Error("Invoice expired"); } await new Promise((resolve) => setTimeout(resolve, intervalMs)); }}
// Usage(async () => { const { invoice, pay_url } = await createInvoice(); console.log("Pay URL:", pay_url);
const paid = await waitForPayment(invoice.id); console.log("Payment confirmed:", paid.id);})();interface CreateInvoiceResponse { invoice: { id: string; expires_at: number }; pay_url: string; test_mode: boolean;}
interface InvoiceInfo { id: string; fiat_amount: string; fiat_currency: string; crypto_amount: string; network_fee: string; crypto: string; network: string; pay_status: string; processing_status: string; is_paid: boolean; created_at: string; is_testing: boolean; payload: string | null;}
async function createInvoice(): Promise<CreateInvoiceResponse> { const response = await fetch("https://api.cru.cash/v1/invoice/create", { method: "POST", headers: { Authorization: `Bearer YOUR_API_KEY`, "Content-Type": "application/json", }, body: JSON.stringify({ amount: "100", currency: "USD", lifetime: 30, webhook: "https://example.com/webhook", payload: JSON.stringify({ order_id: "A-1042" }), ui: { description: "Payment for order #1042", redirect_url: "https://example.com/payment-success", }, }), });
const result = await response.json(); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${result?.error?.code} - ${result?.error?.message}`); } return result;}
async function getInvoice(invoiceId: string): Promise<InvoiceInfo> { const response = await fetch("https://api.cru.cash/v1/invoice/info", { method: "POST", headers: { Authorization: `Bearer YOUR_API_KEY`, "Content-Type": "application/json", }, body: JSON.stringify({ invoice_id: invoiceId }), });
const result = await response.json(); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${result?.error?.code} - ${result?.error?.message}`); } return result;}
async function waitForPayment(invoiceId: string, intervalMs = 5000): Promise<InvoiceInfo> { while (true) { const invoice = await getInvoice(invoiceId); if (invoice.is_paid) return invoice; if (invoice.pay_status === "end") throw new Error("Invoice expired"); await new Promise<void>((resolve) => setTimeout(resolve, intervalMs)); }}
// Usage(async () => { const { invoice, pay_url } = await createInvoice(); console.log("Pay URL:", pay_url);
const paid = await waitForPayment(invoice.id); console.log("Payment confirmed:", paid.id);})();import jsonimport timeimport requests
def create_invoice() -> dict: response = requests.post( "https://api.cru.cash/v1/invoice/create", headers={ "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, json={ "amount": "100", "currency": "USD", "lifetime": 30, "webhook": "https://example.com/webhook", "payload": json.dumps({"order_id": "A-1042"}), "ui": { "description": "Payment for order #1042", "redirect_url": "https://example.com/payment-success", }, }, ) result = response.json() if not response.ok: error = result.get("error", {}) raise Exception(f"HTTP {response.status_code}: {error.get('code')} - {error.get('message')}") return result
def get_invoice(invoice_id: str) -> dict: response = requests.post( "https://api.cru.cash/v1/invoice/info", headers={ "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, json={"invoice_id": invoice_id}, ) result = response.json() if not response.ok: error = result.get("error", {}) raise Exception(f"HTTP {response.status_code}: {error.get('code')} - {error.get('message')}") return result
def wait_for_payment(invoice_id: str, interval_ms: int = 5000) -> dict: while True: invoice = get_invoice(invoice_id) if invoice["is_paid"]: return invoice if invoice["pay_status"] == "end": raise Exception("Invoice expired") time.sleep(interval_ms / 1000)
if __name__ == "__main__": result = create_invoice() print("Pay URL:", result["pay_url"])
paid = wait_for_payment(result["invoice"]["id"]) print("Payment confirmed:", paid["id"])package main
import ( "bytes" "encoding/json" "fmt" "net/http" "time")
func doPost(url string, body map[string]any) (map[string]any, error) { b, _ := json.Marshal(body) req, err := http.NewRequest("POST", url, bytes.NewBuffer(b)) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer YOUR_API_KEY") req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close()
var result map[string]any json.NewDecoder(resp.Body).Decode(&result)
if resp.StatusCode >= 400 { errObj, _ := result["error"].(map[string]any) return nil, fmt.Errorf("HTTP %d: %v - %v", resp.StatusCode, errObj["code"], errObj["message"]) } return result, nil}
func createInvoice() (map[string]any, error) { return doPost("https://api.cru.cash/v1/invoice/create", map[string]any{ "amount": "100", "currency": "USD", "lifetime": 30, "webhook": "https://example.com/webhook", "payload": `{"order_id":"A-1042"}`, "ui": map[string]any{ "description": "Payment for order #1042", "redirect_url": "https://example.com/payment-success", }, })}
func getInvoice(invoiceID string) (map[string]any, error) { return doPost("https://api.cru.cash/v1/invoice/info", map[string]any{ "invoice_id": invoiceID, })}
func waitForPayment(invoiceID string, intervalMs int) (map[string]any, error) { for { invoice, err := getInvoice(invoiceID) if err != nil { return nil, err } if invoice["is_paid"] == true { return invoice, nil } if invoice["pay_status"] == "end" { return nil, fmt.Errorf("invoice expired") } time.Sleep(time.Duration(intervalMs) * time.Millisecond) }}
func main() { result, err := createInvoice() if err != nil { panic(err) }
invoice := result["invoice"].(map[string]any) fmt.Println("Pay URL:", result["pay_url"])
paid, err := waitForPayment(invoice["id"].(string), 5000) if err != nil { panic(err) } fmt.Println("Payment confirmed:", paid["id"])}Related Endpoints
Section titled “Related Endpoints”| Endpoint | Description |
|---|---|
/invoice/create | Create a new invoice and get a payment URL |
/invoice/info | Get invoice details and payment status |