Navorina API
Read invoices and transactions, create draft invoices and get signed webhooks when money moves. Part of the Plus plan.
Start
Create a key in Settings → API. It is shown once; keep it on your server. Send it as a bearer token.
https://navorina.ai/api/v1curl https://navorina.ai/api/v1/invoices \
-H "Authorization: Bearer $NAVORINA_API_KEY"Answers are JSON. Amounts are whole minor units (cents): 120000 is 1,200.00. Dates are YYYY-MM-DD, times are UTC.
Access
Each key can do only what you chose when you created it.
| Access | Scope | Allows |
|---|---|---|
| Read invoices | invoices:read | GET /invoices, GET /invoices/{id} |
| Create draft invoices | invoices:write | POST /invoices |
| Read transactions | transactions:read | GET /transactions |
List invoices
GET /invoicesinvoices:read
Newest first. Filters: status (draft, sent, partially_paid, paid, overdue, void), limit (1 to 100, default 25), starting_after.
curl "https://navorina.ai/api/v1/invoices?status=paid&limit=10" \
-H "Authorization: Bearer $NAVORINA_API_KEY"Get an invoice
GET /invoices/{id}invoices:read
The invoice with its line items.
curl https://navorina.ai/api/v1/invoices/7c1e0b52-… \
-H "Authorization: Bearer $NAVORINA_API_KEY"Create a draft invoice
POST /invoicesinvoices:write
Saved as a draft for you to review and send in Navorina. Send total_minor, or line_items with name, quantity and unit_price_minor. An Idempotency-Key makes a retry safe.
curl https://navorina.ai/api/v1/invoices \
-H "Authorization: Bearer $NAVORINA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042" \
-d '{"number":"INV-1042","customer_name":"Acme GmbH","customer_email":"[email protected]",
"issue_date":"2026-10-21","due_date":"2026-11-04","currency_code":"EUR","total_minor":120000}'List transactions
GET /transactionstransactions:read
Newest first. Filters: from and to (YYYY-MM-DD), limit, starting_after. amount_minor is negative for money out.
curl "https://navorina.ai/api/v1/transactions?from=2026-10-01&to=2026-10-31" \
-H "Authorization: Bearer $NAVORINA_API_KEY"Pages
Lists answer {"object":"list","data":[…],"has_more":true,"next_cursor":"…"}. Pass next_cursor as starting_after to get the next page.
Errors
Every error has one shape: {"error":{"code","message","status"},"request_id"}. Quote request_id when you write to support.
| Status | Code | Means |
|---|---|---|
| 400 | INVALID_PARAMETER | A query parameter is wrong, for example limit=500. |
| 400 | INVALID_PAYLOAD | The JSON body is missing a field or has a bad value. |
| 401 | API_KEY_REQUIRED | No Authorization header. |
| 401 | INVALID_API_KEY | The key is wrong or was revoked. |
| 403 | SCOPE_REQUIRED | The key cannot do this. Create one with the right access. |
| 403 | FEATURE_NOT_INCLUDED | The workspace is not on Plus. |
| 404 | NOT_FOUND | No such object in this workspace, or no such endpoint. |
| 409 | INVOICE_NUMBER_EXISTS | An invoice with this number already exists. |
| 429 | RATE_LIMITED | Too many requests. Wait for Retry-After seconds. |
| 500 | INTERNAL_ERROR | Something failed on our side. Retry with the same Idempotency-Key. |
Limits
60 requests a minute for each key, and 12 new invoices a minute for each workspace. Over the limit the API answers 429 with Retry-After. Up to 10 keys and 5 webhook endpoints per workspace.
Webhooks
Add an https endpoint in Settings → API and choose its events. Navorina sends a POST for each event and expects a 2xx answer within 5 seconds. Redirects are not followed; private and local addresses are refused.
| Event | When |
|---|---|
invoice.created | Invoice created |
invoice.paid | Invoice paid |
invoice.payment_reported | Client says they paid |
transaction.created | Transaction added |
{
"id": "evt_3f9a1c0b7d2e4a6f8b1c5d9e",
"type": "invoice.paid",
"created": "2026-10-21T09:14:03.120Z",
"livemode": true,
"data": {
"object": {
"id": "7c1e0b52-…",
"object": "invoice",
"number": "INV-1042",
"status": "paid",
"currency": "EUR",
"total_minor": 120000,
"amount_due_minor": 0
}
}
}The object is read when the event is first sent; a retry sends the same body with the same id, so you can skip ids you already handled.
Check the signature
Each delivery has Navorina-Signature: t=<unix seconds>,v1=<hex>: an HMAC-SHA256 of t.body with the endpoint's signing secret (shown once when you add it). Also sent: Navorina-Event and Navorina-Delivery.
import crypto from "node:crypto";
// header: the Navorina-Signature header, body: the raw request body as text
function verify(header, body, secret) {
const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${body}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 || ""));
}Retries
A failed delivery is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours, then marked failed. Settings → API shows the last 50 deliveries of each endpoint, with Resend and Send test.