Developers
A REST API for the same clients, invoices, quotes, receipts and expenses you manage in InvoiceCast. On the Business plan, connect your own tools with an API key.
Every endpoint lives under one base URL. Use the domain you sign in on:
https://invoicecast.com/api/v1
Content-Type: application/json (or multipart/form-data for file uploads).{"amount": 15000, "currency": "CAD", "formatted": "$150.00"}, where amount is an integer in minor units (cents). Compute on amount and never parse formatted. When you send money, use decimal major units, e.g. 150.00.YYYY-MM-DD. Timestamps are RFC 3339 in UTC.Send a credential as a bearer token on every request, with an X-IC-Client header naming your app and its version:
Authorization: Bearer ick_live_…
X-IC-Client: my-app/1.0
Account owners and admins create keys in Settings → API keys. Each key has a name, an optional expiry date and a list of abilities, such as invoices:read or clients:write, so an integration can do only what it needs. The key is shown once, when you create it. Store it like a password. Revoking a key takes effect on its next request.
A key acts for the account. It can read and write the account's data within its abilities, but it cannot change account settings or manage users, billing or other keys.
List your clients:
curl https://invoicecast.com/api/v1/clients \
-H "Authorization: Bearer $INVOICECAST_API_KEY" \
-H "X-IC-Client: my-app/1.0"
Create a client. Every POST and PATCH that changes your data needs an Idempotency-Key: a UUID you generate and reuse if you retry the same request.
curl -X POST https://invoicecast.com/api/v1/clients \
-H "Authorization: Bearer $INVOICECAST_API_KEY" \
-H "X-IC-Client: my-app/1.0" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name": "Northwind Ltd", "email": "[email protected]"}'
Create a draft invoice for that client, then send it:
curl -X POST https://invoicecast.com/api/v1/invoices \
-H "Authorization: Bearer $INVOICECAST_API_KEY" \
-H "X-IC-Client: my-app/1.0" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"client_uuid": "<client uuid>",
"issue_date": "2026-10-25",
"currency": "CAD",
"items": [{"description": "Consulting", "quantity": 2, "unit_price": 150.00}]
}'
curl -X POST https://invoicecast.com/api/v1/invoices/<invoice uuid>/send \
-H "Authorization: Bearer $INVOICECAST_API_KEY" \
-H "X-IC-Client: my-app/1.0" \
-H "Idempotency-Key: $(uuidgen)"
Every request carries an X-IC-Client header naming your app and its version, such as acme-sync/3.1: at most 100 printable ASCII characters. Without it you get 400 client_header_required. We use it to find your requests when you contact us and to warn you before a change affects your version, and it is recorded in the account's audit log next to the key that made each change. Only the OpenAPI document and the signed PDF download links are exempt.
Lists return up to one page of results with meta.next_cursor and meta.has_more. Pass the cursor back as ?cursor= to get the next page. Page numbers (?page=) are not supported and return a 400.
Every list also returns meta.server_time. Store it, and next time send it as ?updated_since= to receive only what changed, including deleted records, which come back with deleted_at set.
Replaying a POST or PATCH with the same Idempotency-Key and body within 24 hours returns the original response instead of acting twice. Reusing a key with a different body is a 422.
A PATCH is partial: send only the fields you change, and the rest stay as they are. Single records carry an ETag. Send it back as If-Match on a PATCH: if the record changed in the meantime you get 409 stale with the current version, instead of overwriting someone else's edit. If-None-Match on a GET returns 304 when nothing changed.
If your account has more than one business profile, choose which one issues a document with the X-IC-Business-Profile header (a business profile UUID). Without it, an API key uses the account's default business.
Version 1 only grows: we add endpoints and fields, but we never rename, remove or change the meaning of existing ones. Ignore fields you do not recognise. A breaking change would ship as /api/v2.
Limits are per key, per minute. When you exceed one you get 429 rate_limited with a Retry-After header in seconds.
| Credential | Reads | Writes |
|---|---|---|
| API key | 120 | 30 |
PDF downloads are limited to 20 per minute.
Every error has the same shape. code is stable and safe to branch on. message is translated and safe to show to a person. Quote request_id if you contact us.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid.",
"details": {"errors": {"name": ["The name field is required."]}},
"request_id": "b3b918fb-0b34-4018-9e85-54b770f8980d"
}
}
| Status | Codes | Meaning |
|---|---|---|
| 400 |
bad_request
client_header_required
client_header_invalid
idempotency_key_required
idempotency_key_invalid
|
The request is malformed: a missing or invalid X-IC-Client or Idempotency-Key, a bad filter value or a page number. |
| 401 |
unauthenticated
invalid_credentials
|
The credential is missing, wrong, expired or revoked. |
| 403 |
forbidden
insufficient_scope
upgrade_required
limit_reached
|
Not allowed: the key lacks the ability, the plan does not include the feature, or a plan limit is reached. |
| 404 |
not_found
|
The record does not exist, or belongs to another account. |
| 409 |
stale
not_editable
idempotency_key_in_progress
|
The record changed since you read it, the document can no longer be edited, or the same Idempotency-Key is still being processed. |
| 422 |
validation_failed
idempotency_key_reused
|
A field failed validation (details under error.details.errors), or an Idempotency-Key was reused with a different body. |
| 429 |
rate_limited
|
Too many requests. Wait for Retry-After seconds. |
The full list of endpoints, fields, abilities and plan requirements is published as an OpenAPI 3.1 document, generated from the same code that serves the API. Load it into Postman, Insomnia or a client generator:
https://invoicecast.com/api/v1/openapi.json
Something missing or unclear? Contact us and include the request_id of any request in question.