Create and publish invoices with a hub key
Create an invoice on a case, add its line items, publish it to the client, and mark it paid, void or another status from your billing system.
In this article8 sections
This page covers creating, publishing and updating a case's invoices with a hub key. Paths are under https://prd-api-app.caseactive.com/v1/<hub id>/<case id>.
Calls
| Call | What it does |
|---|---|
POST /invoices | Creates an invoice, unpublished |
PUT /invoices/<invoice_id>/transactions | Sets its line items, tax and discount |
PUT /invoices/<invoice_id> | Changes it, before publishing |
DELETE /invoices/<invoice_id> | Deletes it, before publishing |
PUT /invoices/<invoice_id>/publish | Publishes it, which sends it to its recipients |
PUT /invoices/<invoice_id>/status | Marks it paid, void, processing, uncollectible or open |
GET /invoices?filter=all | Lists the case's invoices |
GET /invoices/<invoice_id> | Reads one invoice |
An invoice goes out in three steps: create it, add its lines, then publish it. Until it's published, its recipients don't see it and nobody is told. Once it's published, only its status can change.
Creating an invoice
| Field | What it does | Required | Default |
|---|---|---|---|
name | The invoice's title. Letters, numbers, spaces and & ' - _ ( ) . /; no commas, colons or #. | Yes | — |
duration | {"due_date": "2026-10-15T17:00:00-04:00"}, with its time zone offset. Invoices don't repeat: a repeat rule is refused. | Yes | — |
status | Always "open" | Yes | — |
participants_ids | Who it's billed to: user IDs of people on the case, from GET /team | No, but needed to publish | Nobody |
description | What it's for | No | None |
invoice_number, po_number | Your invoice and purchase order numbers, in letters, numbers and dashes | No | None |
host_ref_id | Your system's reference for it, in letters, numbers and dashes. No two invoices in your hub can share one, and it can't be changed once set. | No | None |
terms, notes | Payment terms and a note, shown on the invoice | No | None |
notifications | Reminders: {"names": ["days", "hours"], "values": [-1440, -60]}, each value in minutes before the due date | No | None |
instant_notify | false to send no request at publishing, only the reminders | No | true |
curl -X POST https://prd-api-app.caseactive.com/v1/ca-7e6d5c4b3a2f10/cc-1a2b3c4d5e6f70/invoices \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "September legal services", "duration": {"due_date": "2026-10-15T17:00:00-04:00"}, "status": "open", "participants_ids": ["cu-4f1c2a9e8b7d60"], "invoice_number": "INV-1042", "host_ref_id": "acct-1042", "terms": "Net 15"}'
It answers 201 with the invoice_id, such as invoice::20261002143000_ab12cd34ef5678. Use it in paths as it is.
Line items
PUT /invoices/<invoice_id>/transactions sets every line, the tax and the discount at once:
{
"transactions": {
"transactions": [
{"transaction_type": "charge", "description": "Consultation (hours)", "value": 35000, "quantity": 2},
{"transaction_type": "charge", "description": "Court filing fee", "value": 30000}
],
"discount": 0.1,
"tax": 0.07
}
}
| Field | What it does | Required | Default |
|---|---|---|---|
transaction_type | charge, or credit to take an amount off | Yes | — |
description | What the line is for | Yes | — |
value | The price of one, in whole cents: 35000 is $350.00 | Yes | — |
quantity | How many, a whole number from 1 | No | 1 |
currency | usd, the only currency | No | usd |
discount, tax | Rates between 0 and 1: 0.1 is 10% | No | 0 |
- Each call replaces all the lines and both rates. A rate you leave out goes back to 0.
- The subtotal is the charges less the credits, never below 0. The discount comes off the subtotal, then the tax is added to what's left. Every amount is rounded to whole cents.
- The lines above make a subtotal of $1,000.00, a discount of $100.00, tax of $63.00 and a total of $963.00.
Changing or deleting it before publishing
PUT /invoices/<invoice_id> takes the same fields as creating it, but status, and changes only the ones you send. Send "" to clear description, invoice_number, po_number, terms or notes, and {} to clear notifications. DELETE /invoices/<invoice_id> deletes it. Neither works once it's published: void it instead.
Publishing
PUT /invoices/<invoice_id>/publish takes no body and answers 200.
- Each recipient is emailed a request to pay with a link to the invoice, or texted if they have no confirmed email, at once, unless
instant_notifyisfalse. It appears on their To DoTo Do The list on a case of things waiting for you, such as forms to fill in, documents to sign or events to confirm. and in their portal notifications. - Reminders go out at each time in
notificationsstill ahead. Nothing is sent at the due date, and the invoice can still be paid after it. - The staff on the case are copied on the request and its reminders.
Your hub doesn't need Stripe to publish. To be paid online, it must be connected and switched on, and the total must be $1.00 or more; otherwise the client can view the invoice but not pay it online. See Connect Stripe to collect invoice payments.
Status
PUT /invoices/<invoice_id>/status takes status and, optionally, status_reason, a note kept with it.
status | Allowed from | What it does |
|---|---|---|
paid | open, processing, uncollectible | Marks it paid by hand. Final. The client gets no receipt. |
void | open, processing, uncollectible | Cancels it. Final. |
processing | open, uncollectible | A payment is on its way: reminders stop and it leaves the client's To Do, but it can still be paid |
uncollectible | open, processing | Not expected to be paid: as processing |
open | processing, uncollectible | Back on the client's To Do. If it isn't due yet they're asked for it again at once, unless instant_notify is false, and reminders still ahead go out |
When it's paid, online or by hand, or voided, the staff on the case are told, and completed_on is set. An invoice paid online reads paid with payment_gateway stripe; one marked paid here reads manual.
A key can't pay an invoice, or refund one. Refunds are made in the Admin Panel; see Refund a paid invoice.
Reading invoices
GET /invoices needs filter: all lists every invoice on the case, unpublished ones included, newest first, and self those the Virtual Assistant created. paid and unpaid list the caller's own, so they're always empty for a key. limit (default 20) and start_key page as every list does.
Each invoice holds:
| Field | What it holds |
|---|---|
invoice_id, name, description | Its ID, title and description |
participants_ids | Who it's billed to |
status, status_reason | open, paid, void, processing or uncollectible, and the note sent with it |
published | 1 once published |
due_date, completed_on | When it's due, and when it was paid or voided |
subtotal, discount_amount, tax_amount, total | Amounts in whole cents |
discount, tax | The rates, between 0 and 1 |
transactions | Its lines |
payment_gateway, refunded | How it was paid, and any refund |
invoice_number, po_number, host_ref_id, terms, notes | Your numbers and notes |
To read invoices across every case, use a manage key; see Read invoices with a manage key.
If a call is refused
| Status | message | Why |
|---|---|---|
| 400 | SchemaFieldsException | A field is missing or invalid, such as a status other than open, a value that isn't whole cents, a rate of 7 rather than 0.07, or a recipient who isn't on the case; errors names it |
| 400 | ReferenceIdExistsException | Another invoice in your hub has that host_ref_id; reason holds its invoice_id |
| 403 | PublishAlreadyPublishedException | It's already published, so it can't be published again, changed or deleted |
| 403 | PublishTransactionsMissingException | It has no line items |
| 403 | Its own name | Its due date has passed or it has nobody to send it to, so it can't be published; or it can't take that status |
| 403 | BotNotAllowedException | A key tried to pay it, or to record a payment attempt on it |
| 403 | CaseClosedException | The case is closed |
| 404 | ItemNotFoundException | The invoice doesn't exist |
For the error body and the rest of the codes, see Handle API errors and status codes.