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.
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.
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.
{
"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 }
]
}
| Field | Type | Description |
|---|---|---|
timestamp | string | RFC3339 timestamp for the request |
nonce | string | Unique request identifier (UUID) |
payer_email | string | Customer email address |
payer_name | string | Customer name |
invoice_number | string | Unique invoice number |
description | string | Invoice description |
issue_date / due_date | string | RFC3339 dates |
currency | string | Currency code (USD, EUR, …) |
line_items | array | At least one line item |
| Field | Type | Description |
|---|---|---|
payer_company | string | Customer company name |
po_reference | string | Purchase order reference |
payment_terms | string | Payment terms description |
notes / footer_message | string | Additional notes / footer message |
tip_amount / discount | decimal | Invoice-level tip and discount amounts |
timezone | string | Timezone for date calculations |
redirect_url / cancel_url | string | Success / cancel redirect URLs |
billing_address | object | line1, line2, city, state, postal_code, country, phone |
recurring | object | Recurring invoice configuration (below) |
| Field | Type | Required | Description |
|---|---|---|---|
description | string | Yes | Item description |
quantity | integer | Yes | Item quantity |
unit_price | decimal | Yes | Price per unit |
tax_rate | decimal | No | Tax rate percentage |
taxable | boolean | No | Whether the item is taxable |
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.
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.
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.
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.
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | Yes | Enable recurring functionality |
recurrence_type | string | Yes | daily | weekly | monthly | yearly |
interval_count | integer | Yes | Interval between occurrences (1–365) |
start_date | string | Yes | Local time, no UTC offset |
timezone | string | Yes | IANA zone, e.g. America/Los_Angeles |
max_occurrences | integer | No | Maximum invoices to generate |
auto_send_email | boolean | No | Auto-send emails for generated invoices |
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).
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.
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.
# 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)
# 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.
100 requests per minute per API key, burst 10 requests/second; check X-RateLimit-* response headers.
Errors return { "success": false, "error": { "code", "message" } }:
| Code | Description | Solution |
|---|---|---|
MISSING_API_KEY | X-API-Key header required | Include your API key in the X-API-Key header |
MISSING_MERCHANT_ID | X-Merchant-ID header required | Include your merchant ID in the X-Merchant-ID header |
MISSING_SIGNATURE | X-Signature header required | Sign the request body and send X-Signature |
INVALID_API_KEY | Invalid API key | Verify the key is correct and belongs to your merchant account |
INVALID_SIGNATURE | Request signature invalid | Verify your merchant signing key and signature calculation |
INVALID_NONCE_TIMESTAMP | Invalid nonce or timestamp | Use RFC3339 timestamps and a unique nonce per request |
INVALID_JSON | Invalid JSON body | Check JSON syntax and required-field formats |
CONVERSION_ERROR | Failed to process invoice data | Check request format and required fields |
INVOICE_NOT_FOUND | Invoice not found | Verify the invoice ID belongs to your account |
RECURRING_PLAN_NOT_FOUND | Recurring plan not found | Verify the plan ID and its association with the invoice |
PLAN_ALREADY_CANCELED | Plan already canceled | No action — the plan is not active |
INTERNAL_ERROR | Internal server error | Contact support |
Email support@soldinow.com.