Use case

Invoicing built for SaaS

Turn your subscription and usage records into numbered invoices with a hosted pay page, then let webhooks tell your app when they are paid or overdue.

SaaS invoicing usually starts in your own database: a plan, a seat count, metered usage for the month. ASHDOCS takes those numbers in one API request and returns an issued invoice with a PDF, a hosted page and, if you ask, an email to the customer.

Your app stays the source of truth for who pays what. ASHDOCS handles numbering, tax and totals, delivery, reminders and payment tracking. It works with Stripe and Razorpay, and with payments you record yourself.

POST /api/v1/invoices
curl https://www.ashdocs.com/api/v1/invoices \
  -H "X-API-Key: ash_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sub_8812-2026-10" \
  -d '{
  "customer": { "name": "Brightline Labs", "email": "billing@brightline.example" },
  "currency": "USD",
  "due_terms": "net_15",
  "reference": "sub_8812 2026-10",
  "line_items": [
    { "description": "Growth plan, October 2026", "quantity": "1", "unit_price": "49.00" },
    { "description": "Extra seats", "quantity": "3", "unit": "seats", "unit_price": "12.00" },
    { "description": "API calls over plan", "quantity": "18250", "unit": "calls", "unit_price": "0.0020" }
  ],
  "issue": true,
  "send": { "channels": [{ "type": "email" }] }
}'
Response
{
  "id": "inv_7c1e0b52a9d44f0e8b31",
  "kind": "invoice",
  "status": "issued",
  "number": "INV-0042",
  "reference": "sub_8812 2026-10",
  "line_items": [
    {
      "description": "Growth plan, October 2026",
      "quantity": "1",
      "unit": null,
      "unit_price": "49.00",
      "amount_minor": 4900,
      "amount_formatted": "$49.00"
    },
    {
      "description": "Extra seats",
      "quantity": "3",
      "unit": "seats",
      "unit_price": "12.00",
      "amount_minor": 3600,
      "amount_formatted": "$36.00"
    },
    {
      "description": "API calls over plan",
      "quantity": "18250",
      "unit": "calls",
      "unit_price": "0.0020",
      "amount_minor": 3650,
      "amount_formatted": "$36.50"
    }
  ],
  "currency": "USD",
  "due_terms": "net_15",
  "totals": {
    "subtotal_formatted": "$121.50",
    "tax_formatted": "$0.00",
    "total_minor": 12150,
    "total_formatted": "$121.50",
    "balance_minor": 12150,
    "balance_formatted": "$121.50"
  },
  "pdf_url": "https://api.ashdocs.com/api/v1/invoices/inv_7c1e0b52a9d44f0e8b31/pdf",
  "hosted_url": "https://api.ashdocs.com/i/<hosted_token>",
  "sent_at": null,
  "paid_at": null
}

Response trimmed; values illustrative. Note the usage line: 18,250 calls at $0.0020 comes back as 3650 minor units.

An ASHDOCS invoice in the clean design, with line items, tax, total and due date.
The clean design your customers receive (shown here with the canonical example invoice).

Usage lines from your own data

Quantities are decimals and unit prices take up to four decimal places, so 18,250 API calls at $0.0020 is one line item with its unit set to calls.

Recurring invoices for fixed plans

A recurring profile issues the same invoice on an RRULE schedule in your timezone and can send it each time. A profile that fails three runs in a row is paused and you get an email.

Webhooks for your billing logic

On paid plans, invoice.paid, invoice.partially_paid and invoice.overdue reach your endpoint signed with HMAC-SHA256, so your app can extend access, flag an account or open a task.

How SaaS invoicing works with ASHDOCS

At the end of each billing period, a job in your app reads the subscription table and builds one request per customer: a line for the plan, a line for extra seats, a line for metered usage. It posts the request with issue and send, stores the returned invoice id against the subscription period, and moves on.

Use an Idempotency-Key made from the subscription and the period, and a retry after a timeout returns the same invoice instead of billing twice; keys are kept for 24 hours. The reference field (up to 120 characters) and custom_fields carry your own subscription id onto the invoice, and invoices can be searched by reference later.

Plans, seats and usage on one invoice

Everything that makes up a SaaS bill fits in line items. ASHDOCS does not meter usage or store your price list, so the quantities and prices are the ones your app sends.

  • Fixed plan: quantity 1 at the plan price.
  • Seats: a whole-number quantity with unit set to seats.
  • Metered usage: a decimal quantity (calls, GB, minutes) at a unit price with up to four decimal places.
  • Discounts per line or for the whole invoice, as a percentage or a flat amount.
  • Tax per line, or from saved tax rates, with tax-exclusive or tax-inclusive pricing.
  • Any ISO 4217 currency, set per invoice.

Getting paid

The Pay button shows on the hosted invoice page when Stripe or Razorpay is connected. With Stripe, ASHDOCS creates a payment link in your own Stripe account for the amount due; with Razorpay it uses your Razorpay keys. When the provider reports the payment, the invoice is marked paid and invoice.paid fires.

Payment always starts from the hosted page: ASHDOCS creates payment links and does not charge a saved card on its own. Customers who pay by bank transfer are recorded with POST /api/v1/invoices/{invoice_id}/payments, which updates the balance in the same way.

When a payment does not arrive

If a card payment fails, nothing changes on the invoice: it stays unpaid with its Pay button, and the customer can try again from the same page. Reminders keep going on schedule (3 days before, on and 7 days after the due date by default) until the invoice is paid or voided.

Once the due date has passed with a balance left, the background job that runs every minute marks the invoice overdue soon after midnight in your account's timezone, and invoice.overdue fires. That event is the natural place for your own dunning rules, such as limiting access or emailing the account owner.

Frequently asked questions

How do SaaS companies handle invoicing?

Most bill from data they already hold: a subscription record with a plan, seats and metered usage, turned into an invoice at the end of each period and sent to the customer to pay. With ASHDOCS that step is one API call per customer per period, and on paid plans webhooks report back when each invoice is viewed, paid or overdue.

Can I bill usage-based amounts?

Yes. Send the usage as a line item with a decimal quantity, an optional unit such as calls or GB, and a unit price with up to four decimal places, and ASHDOCS computes the line amount, tax and totals. It does not meter usage itself, so the quantity comes from your own records.

Does it work without Stripe?

Yes. Invoices are created, numbered, hosted and sent without any payment provider, and the Pay button shows on the hosted invoice page when Stripe or Razorpay is connected. Without either, customers pay you directly and you record the payment with POST /api/v1/invoices/{invoice_id}/payments, which marks the invoice paid and fires invoice.paid.

How do I handle failed payments?

A failed payment does not change the invoice: it stays unpaid, the Pay button stays on the hosted page, and the customer can try again. Reminders continue until it is paid or voided, the invoice turns overdue after its due date, and on paid plans invoice.overdue lets your app act, for example by limiting access. ASHDOCS does not retry cards itself, because payment always goes through the hosted page.

Plans

Free
$0/mo
  • 100 credits per month
  • 1 seat
  • No webhooks
Starter
$15/mo
or $150/year
  • 1,500 credits per month
  • 1 seat
  • Webhooks
Growth
$49/mo
or $490/year
  • 10,000 credits per month
  • 3 seats
  • Webhooks
Pro
$99/mo
or $990/year
  • 30,000 credits per month
  • 5 seats
  • Webhooks
Scale
$249/mo
or $2,490/year
  • 100,000 credits per month
  • 10 seats
  • Webhooks

Test keys are free, never expire and never use credits; plan rate limits apply. Webhooks are on paid plans. Issuing an invoice uses 1 credit. No per-seat fees. See full pricing.