Invoices
Invoices are the core of the Merchant API. Create an invoice, redirect the customer to the hosted payment page, receive webhook updates, and track the invoice status from your backend.
Create an invoice
POST /api/v1/create-invoice
Creates a new pending invoice and returns a hosted payment_link.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
amount | string | Yes | Payment amount in fiat currency, e.g. "1500.00". Maximum 2 decimal places. |
currency_code | string | Yes | ISO 4217 currency code, 3 uppercase letters, e.g. "UAH". |
customer_id | string | Yes | Unique customer identifier in your system. Maximum 128 characters. |
external_id | string | Yes | Your unique order ID. Must be unique per merchant. Maximum 128 characters. |
purpose | string | No | Payment description shown on the hosted payment page. Maximum 512 characters. |
callback_url | string | Required* | Webhook URL for status notifications. Overrides merchant default if passed. |
success_url | string | Required* | Redirect URL after successful payment. Overrides merchant default if passed. |
fail_url | string | Required* | Redirect URL after failed, expired, or canceled payment. Overrides merchant default if passed. |
callback_url, success_url, and fail_url may be omitted only if default values are already configured in merchant settings. Otherwise the API returns 422 MISSING_REQUIRED_URLS.
Example request
curl -X POST https://api.ecca-ex.com/api/v1/create-invoice \
-H "Content-Type: application/json" \
-H "X-Api-Key: your_api_key_here" \
-d '{
"amount": "1500.00",
"currency_code": "UAH",
"customer_id": "user_12345",
"external_id": "order-2026-0001",
"purpose": "Premium subscription",
"callback_url": "https://yoursite.com/webhooks/payment",
"success_url": "https://yoursite.com/payment/success",
"fail_url": "https://yoursite.com/payment/failed"
}'
Example response
{
"invoice_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"external_id": "order-2026-0001",
"amount": "1500.00",
"currency": "UAH",
"status": "pending",
"payment_link": "https://pay.app.com/a1b2c3d4-5678-90ab-cdef-1234567890ab",
"customer_id": "user_12345",
"purpose": "Premium subscription",
"callback_url": "https://yoursite.com/webhooks/payment",
"success_url": "https://yoursite.com/payment/success",
"fail_url": "https://yoursite.com/payment/failed",
"created_at": "2026-02-25T12:00:00+00:00",
"expires_at": "2026-02-25T12:20:00+00:00",
"finished_at": null
}
Unlike list/detail endpoints, POST /api/v1/create-invoice returns the invoice object directly and does not wrap it in successful / data.
Response fields
| Field | Type | Description |
|---|---|---|
invoice_id | string | System-generated invoice UUID. Use this value as {order_id} in GET /api/v1/invoices/{order_id}. |
external_id | string | Your external_id echoed back. |
amount | string | Requested fiat amount. |
currency | string | Currency code. |
status | string | Invoice status. Initially pending. |
payment_link | string | Hosted payment page URL. Redirect the customer here. |
customer_id | string | Your customer identifier. |
purpose | string | null | Payment description. |
callback_url | string | null | Effective webhook URL for this invoice. |
success_url | string | null | Effective success redirect URL. |
fail_url | string | null | Effective fail redirect URL. |
created_at | string | Invoice creation timestamp in ISO 8601 format. |
expires_at | string | Invoice expiration timestamp in ISO 8601 format. |
finished_at | string | null | Finalization timestamp. null while pending. |
Get invoice details
GET /api/v1/invoices/{order_id}
Returns the current invoice state for the authenticated merchant.
You can request the invoice using:
invoice_id— the UUID returned byPOST /api/v1/create-invoiceexternal_id— your own order identifier from invoice creation
Use whichever identifier is more convenient in your system:
invoice_id for the payment provider-side reference, or external_id for your internal order lookup.
Example request by invoice_id
curl -X GET https://api.ecca-ex.com/api/v1/invoices/a1b2c3d4-5678-90ab-cdef-1234567890ab \
-H "X-Api-Key: your_api_key_here"
Example request by external_id
curl -X GET https://api.ecca-ex.com/api/v1/invoices/order-2026-0001 \
-H "X-Api-Key: your_api_key_here"
Example response
{
"successful": true,
"data": {
"invoice_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"external_id": "order-2026-0001",
"customer_id": "user_12345",
"purpose": "Premium subscription",
"amount": "1500.00",
"currency": "UAH",
"status": "success",
"callback_url": "https://yoursite.com/webhooks/payment",
"success_url": "https://yoursite.com/payment/success",
"fail_url": "https://yoursite.com/payment/failed",
"payment_link": "https://pay.app.com/a1b2c3d4-5678-90ab-cdef-1234567890ab",
"created_at": "2026-04-26T12:00:00+00:00",
"expires_at": "2026-04-26T12:20:00+00:00",
"finished_at": "2026-04-26T12:08:31+00:00",
"method_selected": true,
"deal": {
"deal_id": "8f1b2c3d-4e5f-6789-90ab-cdef12345678",
"status": "completed",
"sub_status": null,
"payment_method_code": "monobank",
"payment_method_name": "Monobank UA",
"amount_fiat": "1537.50",
"conversion_rate": "41.50",
"merchant_usdt": "35.42",
"expires_at": "2026-04-26T12:30:00+00:00",
"finished_at": "2026-04-26T12:08:31+00:00",
"mark_paid_at": "2026-04-26T12:07:55+00:00"
}
}
}
Response basics
| Field | Type | Description |
|---|---|---|
successful | boolean | Indicates whether the request succeeded. |
data.invoice_id | string | Invoice UUID / order ID. |
data.external_id | string | Your original order ID from invoice creation. |
data.status | string | Invoice status: pending, success, fail, expired, or canceled. |
data.method_selected | boolean | true if the customer has already selected a payment method and a deal exists. |
data.deal | object | null | Deal-level details. null until the customer selects a payment method. |
Deal fields
When method_selected=true, the deal object may contain:
| Field | Type | Description |
|---|---|---|
deal_id | string | Internal deal UUID. |
status | string | Deal status such as pending, completed, canceled, failed, appeal, or expired. |
sub_status | string | null | More detailed deal sub-status. |
payment_method_code | string | null | Selected payment method code. |
payment_method_name | string | null | Human-readable payment method name. |
amount_fiat | string | null | Final fiat amount the customer had to send. |
conversion_rate | string | null | Locked USDT/fiat conversion rate at deal creation. |
merchant_usdt | string | null | USDT amount credited to merchant after successful completion. |
expires_at | string | null | Deal expiration timestamp. |
finished_at | string | null | Deal completion timestamp. |
mark_paid_at | string | null | Time when the customer marked the payment as sent. |
List invoices
GET /api/v1/invoices
Returns a paginated list of invoices for the authenticated merchant.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number. Default: 1. |
per_page | integer | No | Items per page. Default: 20, maximum: 500. |
status | string | No | Filter by invoice status: pending, success, fail, expired, canceled. |
currency | string | No | Filter by currency code, case-insensitive. |
order | string | No | Sort by created_at: desc or asc. Default: desc. |
Example request
curl -X GET "https://api.ecca-ex.com/api/v1/invoices?page=1&per_page=20&status=success¤cy=UAH&order=desc" \
-H "X-Api-Key: your_api_key_here"
Example response
{
"successful": true,
"page": 1,
"per_page": 20,
"total": 42,
"total_pages": 3,
"data": [
{
"invoice_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"external_id": "order-2026-0001",
"customer_id": "user_12345",
"purpose": "Payment for Premium subscription",
"amount": "1500.00",
"currency": "UAH",
"status": "success",
"callback_url": "https://yoursite.com/webhooks/payment",
"success_url": "https://yoursite.com/payment/success",
"fail_url": "https://yoursite.com/payment/failed",
"payment_link": "https://pay.app.com/a1b2c3d4-5678-90ab-cdef-1234567890ab",
"created_at": "2026-04-26T12:00:00+00:00",
"expires_at": "2026-04-26T12:20:00+00:00",
"finished_at": "2026-04-26T12:08:31+00:00",
"method_selected": true,
"deal": {
"deal_id": "8f1b2c3d-4e5f-6789-90ab-cdef12345678",
"status": "completed",
"sub_status": null,
"payment_method_code": "monobank",
"payment_method_name": "Monobank UA",
"amount_fiat": "1537.50",
"conversion_rate": "41.50",
"merchant_usdt": "35.42",
"expires_at": "2026-04-26T12:30:00+00:00",
"finished_at": "2026-04-26T12:08:31+00:00",
"mark_paid_at": "2026-04-26T12:07:55+00:00"
}
}
]
}
Invoice lifecycle
Invoice-level statuses:
| Status | Description | Final |
|---|---|---|
pending | Invoice created and waiting for the customer flow to complete | No |
success | Payment completed successfully | Yes |
fail | Payment flow ended with failure | Yes |
expired | Invoice expired before successful completion | Yes |
canceled | Invoice was canceled | Yes |
The top-level invoice status is different from nested deal.status. Invoice status describes the merchant-facing payment result. Deal status describes the internal payment deal state when a payment method has already been selected.
Idempotency
The external_id field acts as an idempotency key per merchant. If you try to create another invoice with the same external_id, the API returns 409 DUPLICATE_EXTERNAL_ID.
Use a stable internal order number for external_id. This prevents accidental duplicate invoice creation during retries.
Rate limiting
Invoice creation is rate-limited per customer. If a customer accumulates too many unpaid invoices within a configured time window, the API returns 403 CUSTOMER_RATE_LIMIT.
Example error
{
"successful": false,
"request_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"error": {
"code": "CUSTOMER_RATE_LIMIT",
"message": "Customer created too many unpaid invoices",
"details": {
"window_minutes": 60,
"limit": 5,
"next_allowed_at": "2026-02-26T15:30:00+00:00",
"customer_id": "user_12345"
}
}
}
Wait until next_allowed_at before creating a new invoice for that customer.
Invoice errors
| HTTP | Code | Description |
|---|---|---|
409 | DUPLICATE_EXTERNAL_ID | An invoice with this external_id already exists for the merchant. |
403 | CUSTOMER_RATE_LIMIT | Too many unpaid invoices were created for this customer. |
422 | CURRENCY_INVALID | Currency code is invalid or disabled. |
422 | PAYMENT_METHOD_NOT_FOUND | No active payment method exists for the selected currency and amount range. |
422 | AMOUNT_BELOW_MIN | Amount is lower than the minimum supported for this currency. |
422 | AMOUNT_ABOVE_MAX | Amount exceeds the maximum supported for this currency. |
422 | MISSING_REQUIRED_URLS | Required URLs are missing in both the request and merchant defaults. |
503 | DEALS_SYSTEM_DISABLED | Invoice creation is temporarily unavailable. |
404 | INVOICE_NOT_FOUND | The specified invoice was not found. |
404 | INVOICES_NOT_FOUND | No invoices matched the merchant query filters. |
Last updated today
Built with Documentation.AI