eva crm Public API

Invoices

See a contract's invoices, payments, credit notes and refunds, and raise new invoices.

The CRM keeps invoices, payments (receipts), credit notes and refunds in one ledger per contract. The API shows the ledger with each entry's kind, and lets you raise invoices and record payments.

GET Invoice types

GET/v1/invoices/types

What type means on the write calls: your organisation's invoice types by kind, and the payment methods. No parameters. Cached for five minutes.

Responses
200
`types` and `paymentMethods`.
Example request
curl https://api.evacrm.co.uk/v1/invoices/types \
  -H "Authorization: Bearer sk_..."
Example response · 200
{
  "types": [
    { "id": "…", "name": "Deposit Invoice", "kind": "invoice", "description": null, "numberPrefix": "INV", "deposit": true, "lineItems": false },
    { "id": "…", "name": "Balance Invoice", "kind": "invoice", "description": null, "numberPrefix": "BAL", "deposit": false, "lineItems": false },
    { "id": "…", "name": "Receipt", "kind": "payment", "description": null, "numberPrefix": "REC", "deposit": false, "lineItems": false },
    { "id": "…", "name": "Credit Note", "kind": "creditNote", "description": null, "numberPrefix": "CRN", "deposit": false, "lineItems": false }
  ],
  "paymentMethods": [ { "id": "…", "name": "BACS" }, { "id": "…", "name": "Card" } ]
}

GET List a contract's ledger

GET/v1/contracts/{id}/invoices

Every entry on the contract, oldest first.

Path parameters
idstring · uuidRequired

The contract's id.

Query parameters
kindstring

Only entries of that kind. All by default.

Valid values: invoice, payment, creditNote, refund
Responses
200
`data`, the entries in the ledger shape below.
404
No such contract in your organisation.
422
A `kind` that is not one of the four.
Example request
curl "https://api.evacrm.co.uk/v1/contracts/6c99094a-6f2e-4c7d-9b1e-2b4a8f3c1d05/invoices?kind=invoice" \
  -H "Authorization: Bearer sk_..."
Example response · 200
{
  "object": "list",
  "data": [
    {
      "id": "…",
      "number": "INV000189",
      "kind": "invoice",
      "type": { "id": "…", "name": "Deposit Invoice" },
      "status": "paid",
      "description": "Deposit",
      "dateRaised": "2026-09-01T00:00:00.000+01:00",
      "receivedDate": null,
      "dueDate": "2026-09-08T00:00:00.000+01:00",
      "amounts": { "net": 416.67, "vat": 83.33, "vatRate": 20, "gross": 500, "balance": 0, "allocated": 500 },
      "paymentMethod": null,
      "contract": { "id": "…", "code": "CON000561" },
      "allocations": [
        { "id": "…", "amount": 500, "with": { "id": "…", "number": "REC000191", "kind": "payment" }, "createdAt": "…" }
      ],
      "items": [],
      "exported": false,
      "exportedAt": null,
      "createdAt": "…",
      "updatedAt": "…"
    }
  ]
}
  • status is the CRM's: not_raised, draft, raised, matched, paid_part, paid, cancelled.
  • amounts.balance is what is still outstanding on an invoice, or still unallocated on a payment. allocated is the difference from gross.
  • allocations name the other side of each match: the payment on an invoice, the invoice on a payment.
  • number is the CRM's printed number and can be used anywhere an invoice id is accepted.

GET Get an entry

GET/v1/invoices/{id}

One entry of any kind, by id or by number.

Numbers come from a sequence and are unique in practice, but the CRM does not enforce it: an import or a manual edit can repeat one. If a number matches more than one entry the API answers 409 with the candidate ids rather than guessing, and the same request by id resolves it. The same rule applies wherever a number is accepted in place of an id.

Path parameters
idstring · id or numberRequired

The entry's id, or its number such as INV000189.

Responses
200
The entry, in the ledger shape.
404
Nothing with that id or number in your organisation.
409
More than one entry carries that number. `candidates` lists their ids.
Example request
curl https://api.evacrm.co.uk/v1/invoices/INV000189 \
  -H "Authorization: Bearer sk_..."
Example response · 200
{
  "id": "e3f4a5b6-c7d8-4e9f-0a1b-2c3d4e5f6071",
  "number": "INV000189",
  "kind": "invoice",
  "type": {
    "id": "…",
    "name": "Deposit Invoice"
  },
  "status": "paid",
  "description": "Deposit",
  "dateRaised": "2026-09-01T00:00:00.000+01:00",
  "receivedDate": null,
  "dueDate": "2026-09-08T00:00:00.000+01:00",
  "amounts": {
    "net": 416.67,
    "vat": 83.33,
    "vatRate": 20,
    "gross": 500,
    "balance": 0,
    "allocated": 500
  },
  "paymentMethod": null,
  "contract": {
    "id": "6c99094a-6f2e-4c7d-9b1e-2b4a8f3c1d05",
    "code": "CON000561"
  },
  "allocations": [
    {
      "id": "…",
      "amount": 500,
      "with": {
        "id": "3a1f0c92-7d4e-4b8a-9c21-5e6f7a8b9c0d",
        "number": "REC000191",
        "kind": "payment"
      },
      "createdAt": "2026-09-02T10:14:03.512+01:00"
    }
  ],
  "items": [],
  "exported": false,
  "exportedAt": null,
  "createdAt": "2026-09-01T09:00:00.000+01:00",
  "updatedAt": "2026-09-02T10:14:03.512+01:00"
}

GET Download a PDF

GET/v1/invoices/{id}/pdf

A link to the entry as a PDF, produced the way the CRM's own download button produces it: the organisation's invoice, credit note or receipt template, the invoice type's template override where one is set, and the reverse-charge template when the tax band calls for it. Works for payments too, which come out as receipts.

The link points at the CRM's PDF service, needs no key, and is good for 30 minutes. Fetch the file straight away rather than storing the link; ask again for a fresh one.

Path parameters
idstring · id or numberRequired

The entry's id, or its number such as INV000189.

Responses
200
`url` to download from, the `fileName` the CRM would give it, and `expiresAt`.
404
No such entry, or the organisation has no PDF template for entries of this kind.
409
The number matches more than one entry; use an id.
502
The CRM did not answer; repeat the request.
Example request
curl https://api.evacrm.co.uk/v1/invoices/INV000189/pdf \
  -H "Authorization: Bearer sk_..."

# then fetch the file
curl -L -o INV000189.pdf "https://pdf-api.crummy.io/gen/3f9c…?action=download"
Example response · 200
{
  "url": "https://pdf-api.crummy.io/gen/3f9c2a7e1b4d8f60a5c3e9b1d2f4a6c8?action=download",
  "fileName": "Invoice INV000189.pdf",
  "expiresAt": "2026-09-03T10:45:12.000+01:00"
}

POST Create an invoice

POST/v1/contracts/{id}/invoices

Not available yet. The route is reserved and answers 501. The CRM creates a contract's deposit and balance invoices when a lead converts; raise those with raise an invoice. Creating further invoices through the API will be announced in the changelog.

Responses
501
Always, with a `hint` pointing at raise.
404
No such contract in your organisation.
Example response · 501
{
  "error": "Creating invoices is not available yet",
  "hint": "The CRM creates a contract's invoices on conversion; raise one with POST /v1/invoices/{id}/raise"
}

POST Raise an invoice

POST/v1/invoices/{id}/raise

Raises an invoice the CRM has already prepared. When a lead converts, the CRM creates the contract's deposit and balance invoices with status not_raised, no number and the amounts from the sale; raising one gives it the next number, raised status and its balance, and updates the contract's summary. The amount is the CRM's and cannot be set here. No field is required: an empty body raises it today, due on receipt.

Path parameters
idstring · id or numberRequired

The invoice's id or number.

Request bodyapplication/json
dateRaisedstring · date or date-time

Cannot be earlier than the day the invoice was created; backdating is a staff permission the API does not carry.

Default: today
dueDatestring · date or date-time

Left out, the invoice is due on receipt: the same day it is raised. Not with dueDays.

Default: dateRaised
dueDaysinteger · 0 to 365

Days after dateRaised. Not with dueDate.

Responses
200
The invoice, raised.
404
No such entry.
409
The number matches more than one entry; use an id.
422
Already raised, a payment or refund, exported, or allocated: the same guards that lock the CRM's own edit form. A backdated `dateRaised`, or an amount or description in the body.
502
The CRM did not answer. Nothing changed; repeat the request.
Example request
curl -X POST https://api.evacrm.co.uk/v1/invoices/BAL000193/raise \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "dateRaised": "2026-09-02", "dueDays": 14 }'
Example response · 200
{
  "id": "f1a2b3c4-d5e6-4f70-8a9b-0c1d2e3f4a5b",
  "number": "BAL000202",
  "kind": "invoice",
  "type": {
    "id": "…",
    "name": "Balance Invoice"
  },
  "status": "raised",
  "description": "Balance",
  "dateRaised": "2026-09-03T00:00:00.000+01:00",
  "receivedDate": null,
  "dueDate": "2026-09-17T00:00:00.000+01:00",
  "amounts": {
    "net": 3175,
    "vat": 635,
    "vatRate": 20,
    "gross": 3810,
    "balance": 3810,
    "allocated": 0
  },
  "paymentMethod": null,
  "contract": {
    "id": "6c99094a-6f2e-4c7d-9b1e-2b4a8f3c1d05",
    "code": "CON000561"
  },
  "allocations": [],
  "items": [],
  "exported": false,
  "exportedAt": null,
  "createdAt": "2026-09-01T09:00:00.000+01:00",
  "updatedAt": "2026-09-03T09:12:41.020+01:00"
}

POST Unraise an invoice

POST/v1/invoices/{id}/unraise

Returns a raised invoice to not_raised: the number is cleared (the next raise gets a fresh one), the balance goes to zero and the contract's summary is recalculated. No body.

Path parameters
idstring · id or numberRequired

The invoice's id or number.

Responses
200
The invoice, back to `not_raised`.
404
No such entry.
422
The invoice has allocations or has been exported. Remove the allocation in the CRM first.
502
The CRM did not answer. Nothing changed; repeat the request.
Example request
curl -X POST https://api.evacrm.co.uk/v1/invoices/BAL000193/unraise \
  -H "Authorization: Bearer sk_..."
Example response · 200
{
  "id": "f1a2b3c4-d5e6-4f70-8a9b-0c1d2e3f4a5b",
  "number": null,
  "kind": "invoice",
  "type": {
    "id": "…",
    "name": "Balance Invoice"
  },
  "status": "not_raised",
  "description": "Balance",
  "dateRaised": null,
  "receivedDate": null,
  "dueDate": null,
  "amounts": {
    "net": 3175,
    "vat": 635,
    "vatRate": 20,
    "gross": 3810,
    "balance": 0,
    "allocated": 0
  },
  "paymentMethod": null,
  "contract": {
    "id": "6c99094a-6f2e-4c7d-9b1e-2b4a8f3c1d05",
    "code": "CON000561"
  },
  "allocations": [],
  "items": [],
  "exported": false,
  "exportedAt": null,
  "createdAt": "2026-09-01T09:00:00.000+01:00",
  "updatedAt": "2026-09-03T09:15:02.311+01:00"
}

DEL Delete an entry

DEL/v1/invoices/{id}

Deletes an entry of any kind that has no allocations and has not been exported, and recalculates the contract.

Path parameters
idstring · id or numberRequired

The entry's id or number.

Responses
200
Deleted: the entry's `id` and `number` with `deleted: true`.
404
No such entry.
422
The entry has allocations or has been exported.
502
The CRM did not answer. Nothing changed; repeat the request.
Example request
curl -X DELETE https://api.evacrm.co.uk/v1/invoices/BAL000193 \
  -H "Authorization: Bearer sk_..."
Example response · 200
{ "deleted": true, "id": "…", "number": "BAL000193" }

On this page