Invoice API

Create invoices programmatically — one-time or recurring — with automatic email delivery, hosted payment pages, and payment webhooks. All monetary math is done server-side in exact decimal arithmetic.

Credentials & authentication

From your merchant dashboard: Settings → Developer Keys. You receive an API Key (authentication), a Merchant Signing Key (request signing), and a Soldi Now Signing Key (webhook verification). Every request sends:

Content-Type: application/json
X-API-Key: YOUR_API_KEY
X-Merchant-ID: YOUR_MERCHANT_ID
X-Signature: BASE64_HMAC_SHA256_OF_REQUEST_BODY

Sign the raw request body with your merchant signing key (HMAC-SHA256, Base64). Include a unique nonce and an RFC3339 timestamp in each body for replay protection.

Endpoints

POST https://web.soldinow.com/api/v1/create-invoice
GET  https://web.soldinow.com/api/v1/get-invoices
POST https://web.soldinow.com/api/v1/cancel-recurring-plan

Staging: replace host with web-staging.soldinow.com.

Create an invoice

{
  "timestamp": "2026-07-16T01:17:30Z",
  "nonce": "UNIQUE_RANDOM_HEX",
  "payer_email": "customer@example.com",
  "payer_name": "John Doe",
  "invoice_number": "INV-001",
  "description": "Monthly service invoice",
  "issue_date": "2026-07-16T01:17:30Z",
  "due_date": "2026-07-23T01:17:30Z",
  "currency": "USD",
  "line_items": [
    { "description": "Monthly subscription", "quantity": 1,
      "unit_price": "99.00", "tax_rate": "8.25", "taxable": true }
  ]
}

Required fields

FieldTypeDescription
timestampstringRFC3339 timestamp for the request
noncestringUnique request identifier (UUID)
payer_emailstringCustomer email address
payer_namestringCustomer name
invoice_numberstringUnique invoice number
descriptionstringInvoice description
issue_date / due_datestringRFC3339 dates
currencystringCurrency code (USD, EUR, …)
line_itemsarrayAt least one line item

Optional fields

FieldTypeDescription
payer_companystringCustomer company name
po_referencestringPurchase order reference
payment_termsstringPayment terms description
notes / footer_messagestringAdditional notes / footer message
tip_amount / discountdecimalInvoice-level tip and discount amounts
timezonestringTimezone for date calculations
redirect_url / cancel_urlstringSuccess / cancel redirect URLs
billing_addressobjectline1, line2, city, state, postal_code, country, phone
recurringobjectRecurring invoice configuration (below)

Line item fields

FieldTypeRequiredDescription
descriptionstringYesItem description
quantityintegerYesItem quantity
unit_pricedecimalYesPrice per unit
tax_ratedecimalNoTax rate percentage
taxablebooleanNoWhether the item is taxable

Totals are calculated for you

Provide quantity, unit_price, tax_rate, taxable per line; the API computes subtotal (sum of quantity × unit_price), tax (sum of taxable lines’ tax), and final total (subtotal + tax + tip − discount). Worked example with mixed tax rates:

"line_items": [
  { "description": "Consulting Services", "quantity": 2, "unit_price": "100.00", "tax_rate": "8.25", "taxable": true },
  { "description": "Software License",    "quantity": 1, "unit_price": "50.00",  "tax_rate": "0.00", "taxable": false },
  { "description": "Setup Fee",           "quantity": 1, "unit_price": "25.00",  "tax_rate": "8.25", "taxable": true }
]

All money math uses exact decimal arithmetic — no floating-point errors, consistent rounding for currency.

Response format

Non-recurring invoice:

{
  "success": true,
  "data": {
    "invoice_id": "a3e7ddf6-...",
    "invoice_number": "INV-001",
    "status": "sent",
    "payment_url": "https://web.soldinow.com/payinvoice/a3e7ddf6-...",
    "view_url": "https://web.soldinow.com/view/invoice/a3e7ddf6-...",
    "created_at": "2026-07-16T01:17:31Z",
    "total_amount": "107.17",
    "currency": "USD"
  },
  "message": "Invoice created successfully"
}

Recurring template: same shape with status: "template" and no payment_url/view_url.

Email behavior

Non-recurring: email sent immediately after creation; status sent; payment URLs in the response. Recurring: no immediate email for the template; status template; scheduled generation creates and emails each occurrence at its run time.

Recurring invoices

Add a recurring object to create a template (status: "template", no immediate email); Soldi Now generates and emails invoices on schedule:

"recurring": {
  "enabled": true,
  "recurrence_type": "monthly",     // daily | weekly | monthly | yearly
  "interval_count": 1,              // 1-365
  "start_date": "2026-08-01T09:00:00",  // LOCAL time, no offset
  "timezone": "America/Los_Angeles",    // IANA zone (required)
  "max_occurrences": 12,            // optional
  "auto_send_email": true
}

Timezone rule: give start_date as local time without a UTC offset and set the IANA timezone field — the schedule is derived from the pair.

Recurring fields

FieldTypeRequiredDescription
enabledbooleanYesEnable recurring functionality
recurrence_typestringYesdaily | weekly | monthly | yearly
interval_countintegerYesInterval between occurrences (1–365)
start_datestringYesLocal time, no UTC offset
timezonestringYesIANA zone, e.g. America/Los_Angeles
max_occurrencesintegerNoMaximum invoices to generate
auto_send_emailbooleanNoAuto-send emails for generated invoices

Cancel a recurring plan

POST /api/v1/cancel-recurring-plan
{
  "invoice_id": "273c3e81-...",
  "recurring_plan_id": "9fe71bd0-...",
  "timestamp": "2026-07-16T17:30:00Z",
  "nonce": "a1b2c3d4e5f67890"
}

Response: { "success": true, "data": { "invoice_id", "recurring_plan_id", "status": "canceled", "canceled_at" } }. Both IDs must exist, belong to your merchant, and match each other. Cancellation takes immediate effect and stops future generation; already-generated invoices are unaffected. Errors: MISSING_INVOICE_ID / MISSING_RECURRING_PLAN_ID (400), INVOICE_NOT_FOUND / RECURRING_PLAN_NOT_FOUND (404), INVALID_RECURRING_PLAN (400, plan doesn’t match invoice), PLAN_ALREADY_CANCELED (400).

List invoices

GET /api/v1/get-invoices?status=sent&limit=10&offset=0

Filter by status (draft = created not sent, sent, paid, partial_paid, template = recurring template, overdue); paginate with limit (1–100, default 50) and offset.

{
  "success": true,
  "data": {
    "invoices": [
      {
        "id": "b58b8543-...",
        "merchant_id": "...",
        "payer_email": "customer@example.com",
        "payer_name": "Jane Customer",
        "invoice_number": "INV-1759802777",
        "description": "Multiple Line Items Invoice",
        "currency": "USD",
        "issue_date": "2026-07-16T02:06:17Z",
        "due_date": "2026-07-17T02:06:17Z",
        "subtotal": "275",
        "tax_amount": "18.56",
        "tip_amount": "0",
        "discount": "0",
        "total": "293.56",
        "status": "sent",
        "created_at": "2026-07-16T02:06:17Z",
        "updated_at": "2026-07-16T02:06:17Z",
        "line_items": [
          { "id": "5c20e2b3-...", "description": "Setup Fee", "quantity": 1,
            "unit_price": "25", "tax_rate": "8.25", "taxable": true, "total": "27.06" }
        ],
        "recurring_plan": {
          "id": "9fe71bd0-...",
          "recurrence_type": "daily",
          "interval_count": 1,
          "start_date": "2026-07-16T00:00:00Z",
          "max_occurrences": 3,
          "occurrences_generated": 1,
          "next_run_at": "2026-07-17T07:00:00Z",
          "status": "active",
          "timezone": "America/Los_Angeles",
          "auto_send_email": true
        }
      }
    ],
    "total_count": 18,
    "limit": 50,
    "offset": 0
  },
  "message": "Invoices retrieved successfully"
}

Recurring plan status values: active, paused, canceled, completed.

Payment webhook

When an invoice is paid, Soldi Now POSTs to your registered webhook URL:

{
  "event": "invoice.payment.completed",
  "external_transaction_id": "txn_...",
  "amount": "107.17",
  "merchant_id": "your-merchant-id",
  "metadata": {
    "invoice_id": "inv_...",
    "invoice_number": "INV-001",
    "payment_method": "credit_card",
    "payment_status": "paid",
    "payer_email": "customer@example.com",
    "fees": {
      "soldinow_fee": "2.14",
      "stripe_fee": "3.41",
      "total_fees": "5.55"
    },
    "recurring": {
      "plan_id": "plan_...", "occurrence_number": 1,
      "max_occurrences": 12, "occurrences_remaining": 11,
      "plan_status": "active", "next_run_at": "2026-09-01T16:00:00Z",
      "description": "Monthly recurring invoice (1 of 12)"
    }
  },
  "timestamp": "2026-07-16T01:17:31Z"
}

Verify the X-Webhook-Signature header: HMAC-SHA256 of the raw body with your Soldi Now Signing Key, compared in constant time.

Request signing example

# Sign the raw JSON request body with your merchant signing key
signature=$(echo -n "$request_body" | openssl dgst -sha256 -hmac "$merchant_signing_key" -binary | base64)

Example requests

# Create an invoice
curl -X POST https://web.soldinow.com/api/v1/create-invoice \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Merchant-ID: YOUR_MERCHANT_ID" \
  -H "X-Signature: YOUR_SIGNATURE" \
  -d '{
    "timestamp": "2026-07-16T01:17:30Z",
    "nonce": "UNIQUE_RANDOM_HEX",
    "payer_email": "customer@example.com",
    "payer_name": "Test Customer",
    "invoice_number": "INV-001",
    "description": "Test invoice",
    "issue_date": "2026-07-16T01:17:30Z",
    "due_date": "2026-07-23T01:17:30Z",
    "currency": "USD",
    "line_items": [
      { "description": "Service", "quantity": 1, "unit_price": "10.00" }
    ]
  }'

# List sent invoices with pagination
curl -X GET "https://web.soldinow.com/api/v1/get-invoices?status=sent&limit=10&offset=0" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Merchant-ID: YOUR_MERCHANT_ID" \
  -H "X-Signature: YOUR_SIGNATURE"

# List recurring templates only
curl -X GET "https://web.soldinow.com/api/v1/get-invoices?status=template" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Merchant-ID: YOUR_MERCHANT_ID" \
  -H "X-Signature: YOUR_SIGNATURE"

# Cancel a recurring plan
curl -X POST "https://web.soldinow.com/api/v1/cancel-recurring-plan" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Merchant-ID: YOUR_MERCHANT_ID" \
  -H "X-Signature: YOUR_SIGNATURE" \
  -d '{ "invoice_id": "...", "recurring_plan_id": "...",
        "timestamp": "2026-07-16T17:30:00Z", "nonce": "a1b2c3d4e5f67890" }'

Test against staging first: replace the host with web-staging.soldinow.com and use your staging keys.

Rate limits

100 requests per minute per API key, burst 10 requests/second; check X-RateLimit-* response headers.

Errors

Errors return { "success": false, "error": { "code", "message" } }:

CodeDescriptionSolution
MISSING_API_KEYX-API-Key header requiredInclude your API key in the X-API-Key header
MISSING_MERCHANT_IDX-Merchant-ID header requiredInclude your merchant ID in the X-Merchant-ID header
MISSING_SIGNATUREX-Signature header requiredSign the request body and send X-Signature
INVALID_API_KEYInvalid API keyVerify the key is correct and belongs to your merchant account
INVALID_SIGNATURERequest signature invalidVerify your merchant signing key and signature calculation
INVALID_NONCE_TIMESTAMPInvalid nonce or timestampUse RFC3339 timestamps and a unique nonce per request
INVALID_JSONInvalid JSON bodyCheck JSON syntax and required-field formats
CONVERSION_ERRORFailed to process invoice dataCheck request format and required fields
INVOICE_NOT_FOUNDInvoice not foundVerify the invoice ID belongs to your account
RECURRING_PLAN_NOT_FOUNDRecurring plan not foundVerify the plan ID and its association with the invoice
PLAN_ALREADY_CANCELEDPlan already canceledNo action — the plan is not active
INTERNAL_ERRORInternal server errorContact support

Support

Email support@soldinow.com.