Skip to content

Invoice Lifecycle

Invora issues tax invoices, credit notes, and debit notes through one ergonomic surface. You provide the business essentials (buyer, lines, taxes, dates) and the platform computes every total, renders a regulation-compliant document, generates the QR code, and submits to the tax authority when you freeze.

Three services share an identical shape and the same six operations:

Document Service Base path Use it to
Invoice SimpleInvoiceService /api/v1/simple/invoices Bill a customer
Credit note SimpleCreditNoteService /api/v1/simple/credit-notes Reduce a previously issued invoice (refund, return, post-sale discount)
Debit note SimpleDebitNoteService /api/v1/simple/debit-notes Increase a previously issued invoice (extra charge, under-billing)

The examples below use invoices. Credit and debit notes are identical with the path segment swapped.

Two states: Draft and Frozen

An invoice carries a single frozen flag:

State frozen Editable? Meaning
Draft false Yes Work in progress. Edit with Update, remove with Delete.
Frozen true No Finalized. The regulation pipeline has run; QR code and signed artifact are available. Immutable.

There is no separate "validated" step and no deletion after freeze. To adjust a frozen invoice you never edit it: issue a credit note to lower the amount owed or a debit note to raise it.

Operations

RPC HTTP Notes
Create POST /api/v1/simple/invoices Draft by default; set freezeImmediately to finalize in one call
Get GET /api/v1/simple/invoices/{key} Full document with computed totals and any authority results
Update PUT /api/v1/simple/invoices/{key} Draft only; requires concurrencyStamp
Delete POST /api/v1/simple/invoices/delete Draft only; body { "keys": ["..."] }
Freeze POST /api/v1/simple/invoices/{key}/freeze Requires concurrencyStamp; submits to the authority
List POST /api/v1/simple/invoices/list Filter, sort, cursor pagination; returns summaries

Money values: DecimalValue

Every monetary amount and rate (unitPrice, taxRate, all calculations.*) uses an exact decimal type so there are no floating-point rounding errors:

{ "units": "100", "nanos": 0 }            // 100.00
{ "units": "1000", "nanos": 500000000 }   // 1000.50
{ "units": "15", "nanos": 0 }             // 15 (e.g. a 15% tax rate)

The value is units + nanos / 1_000_000_000. units is sent as a string (a 64-bit integer in JSON), nanos as a number in the range 0 to 999,999,999 with the same sign as units.

Create and freeze in one call

Set freezeImmediately: true to create and submit in a single round-trip. Send only quantities, unit prices, and tax rates; the platform computes the rest.

POST /api/v1/simple/invoices
{
  "freezeImmediately": true,
  "changes": {
    "issueAt": "2026-06-11T10:30:00Z",
    "supplyDate": { "year": 2026, "month": 6, "day": 11 },
    "currencyCode": "SAR",
    "buyer": {
      "inline": {
        "legalName": "Acme Trading Co.",
        "vatRegistrationNumber": "310122393500003",
        "address": {
          "streetName": "King Fahd Road",
          "buildingNumber": "1234",
          "citySubdivisionName": "Al Olaya",
          "cityName": "Riyadh",
          "postalZone": "12345",
          "countryCode": "SA"
        }
      }
    },
    "lines": [
      {
        "description": "Consulting services",
        "quantity": { "value": { "units": "10", "nanos": 0 }, "unitCode": "HUR" },
        "unitPrice": { "units": "100", "nanos": 0 },
        "tax": { "taxRate": { "units": "15", "nanos": 0 } }
      }
    ]
  }
}

The response returns the full SimpleInvoice with a computed calculations block (here: lineExtensionTotal 1000.00, taxTotal 150.00, payableAmount 1150.00). Because you froze it, the response also carries the regulation submission outcome and the QR code.

To create a draft instead, omit freezeImmediately (or set it false), then call Freeze later.

Buyer: stored party or inline

buyer is a choice between a stored party you created earlier and inline details:

"buyer": { "partyKey": "party_01H..." }
"buyer": { "inline": { "legalName": "Acme Trading Co.", "address": { "..." } } }

Manage stored parties through the Parties service (/api/v2/parties) and reference them by partyKey.

B2B vs B2C and regulation submission

The buyer's VAT status drives how the document is treated. When you freeze, active regulations run automatically:

Buyer Document Saudi Arabia (ZATCA)
Has vatRegistrationNumber B2B tax invoice Cleared before it is valid (blocking)
No VAT number Simplified (B2C) invoice Reported after issuance (async)

For simplified (B2C) invoices above the regulatory threshold, Saudi Arabia requires at least one additionalIds entry on the inline buyer (national ID, commercial registration, passport, etc.). Other regulations (Egypt ETA, Peppol) follow their own submission model; see the regulation guides.

After freezing, the response includes the QR code, the signed artifact reference, and the authority status.

Edit a draft

PUT /api/v1/simple/invoices/{key}
{
  "concurrencyStamp": "<from your last Get/Create/Update>",
  "changes": { "..." }
}

concurrencyStamp is returned by every read; passing it back lets the platform reject conflicting concurrent edits. Frozen invoices reject Update.

Credit and debit notes

Credit and debit notes use the same content shape and the same six operations at /api/v1/simple/credit-notes and /api/v1/simple/debit-notes. Use a credit note to lower the amount owed (refund, return, post-sale discount) and a debit note to raise it (additional charge, under-billing). Like invoices, they compute their own totals and submit to the tax authority on freeze.

List with filters

POST /api/v1/simple/invoices/list
{
  "filter": {
    "textSearch": "Acme",
    "part": { "onlyFrozen": true }
  },
  "sort": { "rules": [ { "issueAt": "SORT_DIRECTION_DESCENDING" } ] },
  "pagination": { "limit": "20" }
}

List returns lightweight SimpleInvoiceSummary items (no line detail) plus totalCount and, when more pages remain, nextPageCursor. Pass that token back as pagination.cursor to fetch the next page; call Get for a full document.

Filter part is a single condition built from one of: key, issueAt, dueDate, branchId, buyerPartyKey, currencyCode, onlyFrozen, onlyDrafts. Combine several with the nested and, or, and not parts. Sort rules accept createdAt, issueAt, dueDate, or payableAmount, each with SORT_DIRECTION_ASCENDING or SORT_DIRECTION_DESCENDING.

Supporting invoicing APIs

You will use these tenant services alongside invoices:

Service Base path Purpose
Parties /api/v2/parties Stored customers/suppliers referenced by buyer.partyKey
Branches /api/v2/branches Multi-branch issuing with distinct tax registrations (branchId)
Settings /api/v2/settings Tenant invoicing configuration
Code lists / items /api/v2/code-lists, /api/v2/code-items Lookup values (tax categories, unit codes, etc.)
Exchange rates /api/v2/exchange-rates FX rates for multi-currency tax reporting
Tax reports /api/v2/tax-reports Periodic tax filings
PDF /api/v2/pdf Branded PDF rendering
Regulations /api/v2/regulations, /api/v1/regulations/zatca Enrollment, status, and ZATCA onboarding