Skip to content

Invoices

Invoices are the core checkout object. Each invoice represents a payment request with a USD amount, one or more payment options, and a payment timing model.

The invoice model uses paymentOptions[] as the source of truth. The hosted page and merchant views render one active rail at a time. Invoice creation includes the payment options that are available at build time. If one configured payment method is temporarily unavailable, the invoice is still created with the remaining successful payment options. If no payment options can be built, creation fails.

Invoice creation is also subject to billing enforcement. FinCobra may return 403 if the account is blocked because an internal billing invoice remained unpaid past the grace window.

Create an invoice

POST /api/checkout/invoices

Request body:

FieldTypeRequiredDescription
amountUsdnumberYesAmount in USD (minimum 0.01)
paymentTimingobjectNoOverride the config default timing for this invoice. Omit it to use defaultPaymentTiming from checkout config.
productNamestringNoProduct or service description (max 200 chars)
issuedBystringNoOrganization issuing the invoice. Defaults to config invoiceIssuerName when omitted. Max 120 chars.
billTostringNoCustomer or company name shown on the hosted invoice. Max 120 chars.
merchantReferencestringNoMerchant order or internal reference (max 200 chars)
customerIdstringNoYour internal customer identifier
customerEmailstringNoCustomer email for reconciliation and export
redirectUrlstringNoOverride the config-level redirect URL for this invoice
metadataobjectNoArbitrary key-value data attached to the invoice. If you use metadata.notes for the internal merchant note, it must be a string up to 140 characters.

Example:

bash
curl -X POST \
  -H "X-Api-Key: fc_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "amountUsd": 49.99,
    "paymentTiming": {
      "mode": "immediate",
      "expiresAfterMinutes": 20
    },
    "productName": "Pro Plan - Monthly",
    "issuedBy": "Acme, Inc.",
    "billTo": "Example Name",
    "merchantReference": "order_123",
    "customerEmail": "buyer@example.com",
    "metadata": {
      "orderId": "order_123",
      "notes": "VIP renewal"
    }
  }' \
  https://fincobra.com/api/checkout/invoices

Response (HTTP 201):

json
{
  "id": "a1b2c3d4-...",
  "amountUsd": 49.99,
  "status": "pending",
  "confirmations": 0,
  "productName": "Pro Plan - Monthly",
  "issuedBy": "Acme, Inc.",
  "billTo": "Example Name",
  "merchantReference": "order_123",
  "customerId": null,
  "customerEmail": "buyer@example.com",
  "redirectUrl": "https://yoursite.com/thank-you",
  "metadata": {
    "orderId": "order_123",
    "notes": "VIP renewal"
  },
  "paymentSummary": {
    "paymentState": "unpaid",
    "receivedAmount": 0,
    "remainingAmount": 49.99,
    "overpaidAmount": 0
  },
  "selectedPaymentOptionId": "payment-option-1",
  "paymentOptions": [
    {
      "id": "payment-option-1",
      "railType": "wallet_contract",
      "assetCode": "USDT",
      "network": "ethereum",
      "quotedAmount": 49.99,
      "quoteRate": 1,
      "destinationAddress": null,
      "paymentUri": null,
      "qrCode": null,
      "isDefault": true,
      "optionPayload": {
        "railType": "wallet_contract",
        "chainId": 1,
        "contractAddress": "0x...",
        "tokenContract": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
        "recipient": "0x1111111111111111111111111111111111111111",
        "decimals": 6,
        "requiredConfirmations": 14,
        "merchantIdHash": "0x...",
        "invoiceIdHash": "0x...",
        "paymentIdHash": "0x...",
        "paymentAmountAtomic": "49990000",
        "paymentCall": {
          "merchantId": "0x...",
          "invoiceId": "0x...",
          "paymentId": "0x...",
          "token": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
          "recipient": "0x1111111111111111111111111111111111111111",
          "amount": "49990000"
        }
      }
    }
  ],
  "paymentTiming": {
    "mode": "immediate",
    "expiresAfterMinutes": 20,
    "payableUntilAt": "2025-01-15T10:50:00.000Z"
  },
  "paidAt": null,
  "confirmedAt": null,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "lastPaymentObservedAt": null
}

Address-transfer rails include paymentUri and qrCode. Wallet-contract rails instead include their executable payload in optionPayload.

Payment timing shapes

Immediate checkout invoice:

json
{
  "paymentTiming": {
    "mode": "immediate",
    "expiresAfterMinutes": 20
  }
}

Due-date invoice:

json
{
  "paymentTiming": {
    "mode": "due_date",
    "dueAfterDays": 7,
    "gracePeriodDays": 14
  }
}

If you use due-date timing, the invoice remains payable until the final payable deadline (dueAt + gracePeriodDays). Checkout keeps the invoice open until that deadline and then transitions it to expired.

List invoices

GET /api/checkout/invoices

Query parameters:

ParamTypeDefaultDescription
statusstringFilter by status: pending, paid, confirmed, expired, cancelled, underpaid
searchstringFree-text search across invoice ID, address, tx hash, product name, and reference fields
merchantReferencestringExact match on merchant reference
customerIdstringExact match on customer ID
customerEmailstringExact match on customer email
exceptionStatusstringFilter open vs resolved exceptions: open, resolved
exceptionTypestringFilter exception type: late_payment, underpaid, overpaid, duplicate_payment
transactionHashstringExact match on any observed transaction hash
invoiceIduuidExact match on invoice ID
limitinteger50Max results (1–200)
offsetinteger0Pagination offset

Export invoices as CSV

GET /api/checkout/invoices/export

Uses the same filters as the invoice list endpoint and returns a CSV download.

Get invoice detail

GET /api/checkout/invoices/:id

Returns full invoice data including QR code and the resolved payment timing.

The response includes:

  • paymentSummary
  • paymentTiming
  • selectedPaymentOptionId
  • paymentOptions[]
  • payments
  • merchant and customer reference fields

Get invoice payment events

GET /api/checkout/invoices/:id/payments

Returns the normalized payment-event rows observed for this invoice.

Get invoice status

GET /api/checkout/invoices/:id/status

Lightweight endpoint for polling. Returns only status-related fields:

json
{
  "status": "paid",
  "confirmations": 0,
  "paymentSummary": {
    "paymentState": "exact",
    "receivedAmount": 49.99,
    "remainingAmount": 0,
    "overpaidAmount": 0
  },
  "exceptionType": null,
  "exceptionStatus": null,
  "resolutionAction": null,
  "resolutionNote": null,
  "resolvedAt": null,
  "resolvedBy": null,
  "paymentTiming": {
    "mode": "immediate",
    "expiresAfterMinutes": 20,
    "payableUntilAt": "2025-01-15T10:50:00.000Z"
  },
  "lastPaymentObservedAt": "2025-01-15T10:36:00.000Z",
  "payments": [
    {
      "id": "payment-1",
      "invoiceId": "a1b2c3d4-...",
      "assetCode": "USDT",
      "network": "ethereum",
      "destinationAddress": "0x...",
      "transactionHash": "abc123...",
      "amountReceived": 49.99,
      "observedTotalAmount": 49.99,
      "confirmations": 1,
      "observedAt": "2025-01-15T10:35:00.000Z",
      "confirmedAt": "2025-01-15T10:42:00.000Z"
    }
  ]
}

Get invoice stats

GET /api/checkout/invoices/stats

Returns aggregate counts across all your invoices.

Resolve an invoice exception

POST /api/checkout/invoices/:id/exception/resolve

Use this after reviewing an open exception.

Request body:

FieldTypeRequiredDescription
resolutionActionstringYesOne of accept_late_payment, accept_overpayment, write_off_underpayment, mark_duplicate_payment_reviewed, other
resolutionNotestringNoOptional operator note

Response:

json
{
  "ok": true,
  "invoiceId": "a1b2c3d4-..."
}

Invoice status lifecycle

pending ──→ paid ──→ confirmed

   ├──→ expired      (no payment received before the final payable deadline)

   └──→ underpaid    (partial payment received, then the final payable deadline elapsed)