Skip to content

Finalize a draft invoice

The invoices.finalize operation: POST /api/v1/invoices/{id}/finalize in the REST API and the invoices_finalize tool in the MCP server.

POST/api/v1/invoices/{id}/finalize

Issues a draft: it gets the next gap-free number of the brand's series, a fresh tax quote and its PDF, and can't change any more (spec §16.15, §18.2).

  1. Lines in the §16.3 order: licensed items, metered usage, late usage, prorations, then invoice items.
  2. Tax is quoted now with the customer's stored location evidence and tax IDs; a tax ID last checked more than 30 days ago is checked again with its register first. Proration lines are taxed at the rates of their change date. A sale Ownrecord can't tax with confidence is refused with a tax_blocked error and nothing changes.
  3. The customer's credit balance in the invoice's currency is applied after tax (creditApplied); the rest is amountDue. When the balance covers everything the invoice is paid at once, else open.
  4. The ledger books the receivable, the revenue (deferred for subscription periods) and the tax; the sale counts toward the registration thresholds on its tax date. For send_invoice the due date is fixed.

A draft that totals zero (or less) isn't issued and takes no number: invoice_total_zero (spec §16.2). Emits invoice.issued and invoice.finalized (and invoice.paid when the credit balance paid it); the PDF is rendered right after. Dry-run it to see the number, the tax and the credit it would apply.

At a glance

  • REST: POST /api/v1/invoices/{id}/finalize
  • MCP tool: invoices_finalize
  • Operation ID: invoices.finalize
  • Scope: Brand
  • Permission: invoices:write
  • Risk tier: write
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key required
  • Modes: Test and live
  • Authentication: API key with the invoices:write scope
  • Since: M5

Request

Path parameters

FieldTypeDescription
idinv_… IDThe invoice ID (inv_…).

Body

A JSON object (Content-Type: application/json):

FieldTypeRequiredDescription
expectedVersioninteger (at least 1)NoThe version you read; a newer stored version fails with version_conflict.

Response

200 OK with a JSON object:

FieldTypeDescription
idinv_… ID
object"invoice"
livemodeboolean
entityIdent_… ID
brandIdbrd_… ID
numberstring or nullThe invoice number: gap-free per legal entity, mode, brand series and year (§18.2). Null for a draft: the number is taken at finalization.
numberingobject or null
customerIdcus_… ID or null
subscriptionIdsub_… ID or null
collectionMethodone of charge_automatically, send_invoice
kindone of invoice, receipt or nullinvoice (business customers) or receipt (the receipt-invoice consumers get); null for a draft.
statusone of 9 values, e.g. draft, pending_registration, issued (all in the OpenAPI document)draft → open (finalized) → paid, void or uncollectible. Checkout invoices are issued (paid at once), or pending_registration: a full invoice charged before the tax registration exists; the receipt went out and the formal invoice follows (§18.1).
languageone of en, de, fr, hr, it, es or null
issuedAtdate-time or null
issueDatestring or nullThe issue date in the seller's time zone.
taxPointdate-time or null
taxPointDatestring or nullThe date of supply in the seller's time zone.
daysUntilDueinteger or nullsend_invoice: days from the issue date to the due date.
dueDatestring or nullsend_invoice: when payment is due (fixed at finalization).
sellerobject or null
buyerobject or null
currencystring
linesarray of object
lines[].idstringThe tax quote's line ID.
lines[].descriptionstring
lines[].productIdprod_… ID or null
lines[].priceIdprice_… ID or null
lines[].quantityinteger
lines[].taxCategoryIdone of 9 values, e.g. electronically_supplied_service, saas, ebook (all in the OpenAPI document)
lines[].taxBehaviorone of inclusive, exclusive
lines[].unitAmountmoneyThe net price of one unit (may be finer than the minor unit).
lines[].netmoney
lines[].taxobject
lines[].tax.ratePpmstringParts per million as a string (19% = "190000").
lines[].tax.amountmoney
lines[].tax.jurisdictionIdstring or null
lines[].tax.regimeIdstring or null
lines[].tax.decisionone of charge, reverse_charge, exempt, not_taxable, out_of_scope, not_collected, blockedWhat happened to the tax: charge, reverse_charge, exempt, not_taxable, out_of_scope, not_collected.
lines[].tax.rateKindone of standard, reduced, super_reduced, parking, zero, exempt, not_taxable or null
lines[].tax.rateIdstring or null
lines[].tax.pendingRegistrationboolean
lines[].grossmoney
lines[].kindone of licensed, metered, late_usage, proration, invoice_item or nullWhere the line came from (§16.3 order); null on checkout invoices.
lines[].periodobject or null
lines[].subscriptionItemIdstring or null
lines[].invoiceItemIdii_… ID or null
lines[].discountmoney or nullWhat discounts took off the line before tax.
lines[].meteredQuantitystring or nullA metered line's usage (its quantity is 1).
totalsobject or null
taxCurrencyTotalsobject or nullThe tax in the seller's tax currency when the invoice is in another one (§18.3).
creditAppliedmoney or nullTaken from the customer's credit balance at finalization (§16.11).
amountDuemoney or nullWhat the customer pays: the gross total less the credit applied. Null on checkout invoices.
notesarray of object
notes[].codeone of reverse_charge, exempt, out_of_scope, registration_pending, oss_scheme
notes[].textstringThe note as printed.
fieldCheckobject or nullWhat the knowledge base's invoicing rules ask for that the invoice lacks (missing), and requirements Ownrecord can't check yet (unchecked).
pendingRegistrationbooleanA full invoice with a line charged before the registration exists (§18.1).
paymentIdsarray of pay_… ID
creditNoteIdsarray of cn_… ID
ledgerTxnIdsarray of txn_… ID
quoteIdtq_… ID or nullThe tax quote the invoice shows (null for a draft).
quoteIdsarray of tq_… IDEvery quote of the invoice: lines taxed at another date (prorations) have their own quote.
pdfobject or nullNull for a draft: the PDF is rendered at finalization.
finalizedAtdate-time or null
paidAtdate-time or null
paymentobject or nullHow the invoice was paid: bank_transfer (marked paid), processor (a card or debit payment) or credit_balance.
voidedAtdate-time or null
markedUncollectibleAtdate-time or null
emailedAtdate-time or null
sentAtdate-time or nullWhen invoices.send last emailed the invoice.
draftobject or nullA draft's lines before tax (tax is quoted at finalization); null once finalized.
metadataobject
versionintegerPass it as expectedVersion to refuse a change if the invoice changed. Rendering the PDF and emailing the invoice change it too.
createdAtdate-time
updatedAtdate-time

Examples

A consulting invoice to a business in Austria, part paid from the credit balance

Reverse charged; €200 of the customer's credit balance applied, €1,000 due in 14 days.

HTTP
POST /api/v1/invoices/inv_01m4tx5nj8emwbyf7y9vcf47yk/finalize
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "expectedVersion": 1
}

Response (200):

JSON
{
  "id": "inv_01m4tx5nj8emwbyf7y9vcf47yk",
  "object": "invoice",
  "livemode": false,
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "number": "GEN-2026-000131",
  "numbering": {
    "series": "GEN",
    "year": 2026,
    "seq": "131"
  },
  "customerId": "cus_01m4tx5nj8emwbyf7y9vcf47ym",
  "subscriptionId": null,
  "collectionMethod": "send_invoice",
  "kind": "invoice",
  "status": "open",
  "language": "en",
  "issuedAt": "2026-10-20T09:00:00.000Z",
  "issueDate": "2026-10-20",
  "taxPoint": "2026-10-20T09:00:00.000Z",
  "taxPointDate": "2026-10-20",
  "daysUntilDue": 14,
  "dueDate": "2026-11-03",
  "seller": {
    "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
    "legalName": "Acme Digital d.o.o.",
    "tradingName": null,
    "address": {
      "line1": "Ilica 1",
      "line2": null,
      "city": "Zagreb",
      "postalCode": "10000",
      "region": null,
      "country": "HR"
    },
    "companyRegistryId": "080000001",
    "taxIds": [
      {
        "type": "eu_vat",
        "value": "HR12345678901",
        "regimeId": null
      }
    ],
    "brand": {
      "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
      "name": "Genno",
      "primaryColor": "#0E6B5C",
      "accentColor": null,
      "supportEmail": "help@genno.example",
      "supportUrl": null
    },
    "footerText": null
  },
  "buyer": {
    "type": "b2b",
    "customerId": "cus_01m4tx5nj8emwbyf7y9vcf47ym",
    "name": "Alpen Apps GmbH",
    "email": "billing@alpen.example",
    "address": null,
    "country": "AT",
    "taxId": {
      "type": "eu_vat",
      "value": "ATU00000000",
      "country": "AT"
    }
  },
  "currency": "EUR",
  "lines": [
    {
      "id": "1",
      "description": "Implementation workshop (days)",
      "productId": "prod_01m4tx5nj8emwbyf7y9vcf47yr",
      "priceId": null,
      "quantity": 2,
      "taxCategoryId": "consulting_remote",
      "taxBehavior": "exclusive",
      "unitAmount": {
        "amount": "600000000",
        "currency": "EUR"
      },
      "net": {
        "amount": "1200000000",
        "currency": "EUR"
      },
      "tax": {
        "ratePpm": "0",
        "amount": {
          "amount": "0",
          "currency": "EUR"
        },
        "jurisdictionId": "AT",
        "regimeId": "EU_OSS_UNION",
        "decision": "reverse_charge",
        "rateKind": null,
        "rateId": null,
        "pendingRegistration": false
      },
      "gross": {
        "amount": "1200000000",
        "currency": "EUR"
      },
      "kind": "invoice_item",
      "period": null,
      "subscriptionItemId": null,
      "invoiceItemId": "ii_01m4tx5nj8emwbyf7y9vcf47ys",
      "discount": null,
      "meteredQuantity": null
    }
  ],
  "totals": {
    "net": {
      "amount": "1200000000",
      "currency": "EUR"
    },
    "tax": {
      "amount": "0",
      "currency": "EUR"
    },
    "gross": {
      "amount": "1200000000",
      "currency": "EUR"
    },
    "taxByRate": [
      {
        "ratePpm": "0",
        "jurisdictionId": "AT",
        "regimeId": "EU_OSS_UNION",
        "decision": "reverse_charge",
        "net": {
          "amount": "1200000000",
          "currency": "EUR"
        },
        "tax": {
          "amount": "0",
          "currency": "EUR"
        }
      }
    ]
  },
  "taxCurrencyTotals": null,
  "creditApplied": {
    "amount": "200000000",
    "currency": "EUR"
  },
  "amountDue": {
    "amount": "1000000000",
    "currency": "EUR"
  },
  "notes": [
    {
      "code": "reverse_charge",
      "text": "Prijenos porezne obveze (reverse charge) – čl. 17. st. 1. Zakona o PDV-u"
    }
  ],
  "fieldCheck": {
    "invoicingRuleIds": [],
    "missing": ["buyer_address"],
    "unchecked": []
  },
  "pendingRegistration": false,
  "paymentIds": [],
  "creditNoteIds": [],
  "ledgerTxnIds": ["txn_01m4tx5nj8emwbyf7y9vcf47yn"],
  "quoteId": "tq_01m4tx5nj8emwbyf7y9vcf47xr",
  "quoteIds": ["tq_01m4tx5nj8emwbyf7y9vcf47xr"],
  "pdf": {
    "status": "pending",
    "fileId": null,
    "sha256": null,
    "size": null,
    "templateVersion": null,
    "renderedAt": null,
    "url": "/api/v1/invoices/inv_01m4tx5nj8emwbyf7y9vcf47yk/pdf"
  },
  "finalizedAt": "2026-10-20T09:00:00.000Z",
  "paidAt": null,
  "payment": null,
  "voidedAt": null,
  "markedUncollectibleAt": null,
  "emailedAt": null,
  "sentAt": null,
  "draft": null,
  "metadata": {
    "contract": "ENT-2026-07"
  },
  "version": 2,
  "createdAt": "2026-10-20T08:30:00.000Z",
  "updatedAt": "2026-10-20T09:00:00.000Z"
}

Errors

CodeHTTP statusWhenWhat to do
validation_failed400id is malformed, or the draft's due date is before the issue date.Fix the fields listed in details.issues and send the request again.
resource_not_found404This brand has no such invoice in this mode (invoices belong to one brand and one mode).Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
invoice_status_invalid409The invoice isn't a draft (it was finalized or voided).Check the invoice's status (details.status) and what it still allows (details.allowed_actions). Drafts are edited and finalized; open invoices are sent, paid, voided or marked uncollectible; paid and void invoices don't change (corrections are credit notes).
invoice_total_zero409The draft totals zero, or less (credits exceed charges): zero invoices aren't issued and take no number.Add an item with an amount, or void the draft. Zero invoices aren't issued (spec §16.2): no number is taken for them.
invoice_lines_invalid400A line can't be invoiced (param names it): e.g. more than 100 lines, or revenue booked at once without a product.Describe every line of the quote exactly once, with a description of 1 to 500 characters and a whole-number quantity from 1 to 1,000,000. param names the line.
customer_deleted400The draft's customer was deleted.Create a new customer for this buyer. Deleted customers stay readable for the records they belong to, but can't be changed.
registration_required422The fresh tax quote is blocked: the seller isn't registered where the sale is taxed (details has what to do).Register the entity for the regime named in details.regime (see its prerequisites and deadline), or arm the jurisdiction once the prerequisites are done. The customer can join the waitlist meanwhile.
evidence_conflict422The customer's stored evidence doesn't confirm one place to tax the sale: e.g. a consumer, or a business without a valid VAT ID, in the EU with only its billing address (add a VAT ID, or a checkout's evidence).Collect another piece of location evidence (billing address, card country, or the customer's confirmation of their country) and quote again.
unsupported_tax_category422A product's tax category has no place-of-supply rule there.Sell it only where it is supported (e.g. domestically), or change the product's tax category if it was set wrongly.
rate_unavailable422There's no usable tax rate for a line on its tax date.Sell to this jurisdiction later, once its rate is in the knowledge base and reviewed. In test mode, check that the jurisdiction is covered.
jurisdiction_blocked422The customer's jurisdiction can't be sold to yet.This jurisdiction isn't covered by usable tax knowledge for this seller. Contact support if you need to sell there.
tax_id_unavailable422A tax ID couldn't be checked and the brand doesn't sell until it is.Try again in a few minutes. The tax-ID register (e.g. VIES) is unavailable, and this brand blocks business sales until the ID is verified.
fx_rate_unavailable404A threshold in another currency needs ECB rates that aren't loaded.Check the currency (the ECB publishes about 30) and the date. Rates are fetched every day after 16:00 CET; for a date with no rate yet, try again after the next publication.
invoice_number_format_invalid400The legal entity's number format can't make unique numbers; nothing was numbered.Change the entity's invoice number format (Invoice settings) to a template with {series}, {seq} and, when the sequence restarts every year, {year}, e.g. {series}-{year}-{seq:06}.
version_conflict409The invoice changed since expectedVersion, or at the same moment; read it again and retry.Fetch the object again and retry the change.
scope_required400The request names no brand (an org or entity key without Ownrecord-Brand).Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (details.required_scope).
permission_denied403You can't change this brand's invoices (invoices:write: finance, owners and admins).Ask an organization owner or admin for a role or scope that allows this action.
livemode_not_allowed403You have no live-mode access, so you can't use live invoices.Use test mode, or credentials for the target's mode. Developers need live access granted by an organization owner or admin.

It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.