NORMAN DEVELOPERS
Invoice and product catalog API
Sync products, extract invoice proposals from contracts, retrieve PDF and XML files, match payments and manage existing recurring series.
INVOICING WORKFLOWS
Connect your catalog, contracts and invoices
Sync your product and service catalog
Catalog reads use read_invoices; creation, updates and archiving use write_invoices. Save the returned publicId in your ERP, then update the same record with PATCH. Prices use minor currency units.
curl --fail-with-body -X POST "https://api.norman.finance/api/v1/companies/$NORMAN_COMPANY_ID/products/" \
-H "Authorization: Bearer $NORMAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Consulting hour",
"type": "SERVICES",
"price": 15000,
"vatRate": 19,
"sku": "CONSULTING-HOUR"
}'Search with GET /companies/{company_pk}/products/?search=CONSULTING. The default list contains active products. Use status=archived or status=all for archived entries. DELETE archives a product; PATCH with {"status": "active"} restores it. Existing invoice lines keep their recorded values.
Turn a contract into an invoice proposal
Use write_invoices, write_documents and read_clients. Upload a contract up to 10 MB, or pass an existing contract as attachmentId. Norman stores the source document and returns a draft, clientData and reviewNotes.
curl --fail-with-body -X POST "https://api.norman.finance/api/v1/companies/$NORMAN_COMPANY_ID/invoices/contract-draft/" \
-H "Authorization: Bearer $NORMAN_API_KEY" \
-F "file=@signed-contract.pdf"Review the recipient, dates, VAT and notes before saving. Proposal line rates use major currency units; convert them to minor units for invoice creation. The proposal is not an invoice-create request body, and this operation does not create or email an invoice. Use the invoice draft recipe after mapping and reviewing the fields.
Retrieve invoice files and reconcile payments
With read_invoices, request a suggested number, an existing PDF download link or e-invoice XML. A suggested number is not reserved. XML generation validates the invoice and returns the file directly; it does not submit anything to a tax authority.
curl --fail-with-body "https://api.norman.finance/api/v1/companies/$NORMAN_COMPANY_ID/invoices/next-invoice-number/" \
-H "Authorization: Bearer $NORMAN_API_KEY"
curl --fail-with-body "https://api.norman.finance/api/v1/companies/$NORMAN_COMPANY_ID/invoices/replace_with_invoice_uuid/pdf/" \
-H "Authorization: Bearer $NORMAN_API_KEY"
curl --fail-with-body "https://api.norman.finance/api/v1/companies/$NORMAN_COMPANY_ID/invoices/replace_with_invoice_uuid/xml/" \
-H "Authorization: Bearer $NORMAN_API_KEY" \
-o invoice.xmlFind payment candidates with GET /invoices/{id}/suggest-transactions/below the company path using read_invoices and read_transactions. After review, link a payment with write_invoices and write_transactions.
curl --fail-with-body -X POST "https://api.norman.finance/api/v1/companies/$NORMAN_COMPANY_ID/invoices/replace_with_invoice_uuid/link-transaction/" \
-H "Authorization: Bearer $NORMAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transaction": "replace_with_transaction_uuid"
}'Follow up on overdue invoices and recurring schedules
POST /invoices/{id}/send-on-overdue/ sends a reminder with write_invoices. Email verification, plan eligibility and overdue-state checks still apply. The operation sends an email; avoid blind retries after a timeout. Read a client’s payment statistics with GET /clients/{id}/payment-stats/ and both read_clients and read_invoices.
A recurring series starts from an invoice, which becomes its first invoice. POST /invoices/{id}/make-recurring/ with write_invoices takes the rule: interval (week or month), intervalCount, the date of the next invoice as startsOn, an end as endsAfter or endsOn, and mode (draft, issue or send). The same rule can go in recurring when you create the invoice.
curl --fail-with-body -X POST "https://api.norman.finance/api/v1/companies/$NORMAN_COMPANY_ID/invoices/replace_with_invoice_uuid/make-recurring/" \
-H "Authorization: Bearer $NORMAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"interval": "month",
"intervalCount": 1,
"startsOn": "2026-11-01",
"endsAfter": 6,
"mode": "draft"
}'GET /recurring-invoices/{id}/ returns the status, the next date and the next scheduled invoices with read_invoices. With write_invoices, an empty POST to pause/, resume/, skip/, create-now/ or end/ below the series path controls it. Invoices already made stay as they are. The schedule fields of the earlier recurring API, such as frequencyType and startsFromDate, are refused with 400.