Read your Entirova data from your own systems: reporting tools, dashboards, exports and scripts.

The API is read only for now, and webhooks tell your system when something happens. Creating records through the API is being built. Both are included on the Scale plan and above.

Make a key

  1. An Owner or Admin opens Settings Integrations & API, selects the API card, then New key.
  2. Name it after what will use it, and tick what it may read.
  3. Copy the key. It is shown once: Entirova keeps only a fingerprint of it, so nobody can show it to you again.

A key belongs to one organisation. The page lists each key with what it can read and when it was last used. Revoke stops a key working straight away. Making and revoking keys are recorded in the Audit trail.

Treat a key like a password. Anyone who has it can read what you allowed. Keep it on your server, never in a web page, an app people download, or a shared file. If it leaks, revoke it and make a new one.

Make a call

Every address starts with your Entirova site followed by /api/v1. Send the key in the Authorization header:

curl https://YOUR-CIRRO-SITE/api/v1/invoices?limit=50 \
      -H "Authorization: Bearer cir_live_…"
AddressWhat you get
GET /api/v1What the API offers. No key needed.
GET /api/v1/<resource>A page of records.
GET /api/v1/<resource>/<id>One record.

What you can read

ResourcePermission on the keyNotes
customersCustomersBank details and pictures are never included. Each has code (the customer ID) and, where given, firstName, lastName and company.
suppliersSuppliersBank details are never included.
productsProducts and servicesWith prices, costs and stock on hand.
invoicesInvoicesWith their lines and payments.
billsBillsWith their lines and payments.
accountsChart of accountsThe id is the account code.
journalsJournalsEach with its debit and credit lines.

Records come back as Entirova stores them, with an id and an updatedAt time. A resource can only be read while its app is installed: for example, bills needs the Bills app.

Pages

A list comes back a page at a time, 50 records unless you ask for another size with limit (1 to 200):

{ "data": [ … ], "next": "inv_8f2k…" }

To get the following page, send next back as after: /api/v1/invoices?after=inv_8f2k…. When next is null you have everything.

Limits and errors

Each key may make 120 calls a minute. Every answer says how many are left in the x-ratelimit-remaining header.

StatusCodeMeaning
401invalidThe key is missing, wrong or revoked.
403scopeThe key isn't allowed to read that resource.
403app_not_installedThe app for that resource isn't installed.
403planThe organisation's plan doesn't include the API.
404not_foundNo such resource or record.
405methodOnly GET works in this release.
429rate_limitedToo many calls. Wait the number of seconds in retry-after.

An error always looks like { "error": { "code": "…", "message": "…" } }.

Webhooks

A webhook is an address on your own system that Entirova calls when something happens, so you don't have to keep asking.

  1. An Owner or Admin opens Settings Integrations & API, selects the Webhooks card, then New webhook.
  2. Enter a public https:// address and tick the events you want.
  3. Copy the signing secret. It is shown once.
  4. Select Send test to get a ping call and check your side works.
EventWhenWhat it carries
invoice.createdAn invoice is first saved, draft or not.The invoice
invoice.sentAn invoice leaves draft.The invoice
invoice.voidedAn invoice is voided.The invoice
payment.receivedA customer payment is recorded on an invoice.The invoice and the payment
bill.createdA bill is first saved.The bill
bill.approvedA bill is approved.The bill
payment.madeA payment to a supplier is recorded on a bill.The bill and the payment
customer.createdA customer is added.The customer, without bank details
supplier.createdA supplier is added.The supplier, without bank details

There is no "invoice paid in full" event. payment.received carries the invoice with all its payments, so your system can tell.

What a call looks like

Entirova sends a POST with JSON, usually within a minute of the event:

{ "id": "5c1e…", "event": "invoice.sent", "created": "2026-10-05T10:00:00Z", "attempt": 1,
      "data": { "id": "inv_8f2k…", "number": "INV-1042", … } }

Answer with any 2xx status within 10 seconds. Do the slow work after you've answered.

Check it came from Entirova

Every call has a Cirro-Signature header: t=<time>,v1=<signature>. The signature is the HMAC-SHA256, in hex, of the time, a full stop and the body exactly as received, made with your signing secret. Work it out yourself and compare:

const crypto = require('crypto');
    function fromCirro(rawBody, header, secret) {
      const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || '');
      if (!m || Math.abs(Date.now() / 1000 - m[1]) > 300) return false;      // older than 5 minutes
      const mine = crypto.createHmac('sha256', secret).update(m[1] + '.' + rawBody).digest('hex');
      return crypto.timingSafeEqual(Buffer.from(mine), Buffer.from(m[2]));
    }

Always check the signature. Without it, anyone who learns your address could send you made-up events. Use the body exactly as it arrived, before any JSON parsing.

Retries

  • If your address doesn't answer with a 2xx, Entirova tries again after 1, 5 and 30 minutes, then 2 and 12 hours, then gives up on that call.
  • A retry has the same id (also in the Cirro-Delivery header) and a higher attempt. Use the id to ignore a call you've already handled.
  • Calls can arrive out of order. Go by created, not by arrival.
  • After 30 failures in a row the webhook is switched off, and the page says why. Send test switches it on again.
  • Redirects aren't followed, and the address must be a public name: not an IP address or a local one.