Conventions
Things the Worksible API does the same way for every endpoint. Read this once and you can predict the shape of any new endpoint we ship without opening the reference.
JSON shape
- camelCase keys in both requests and responses.
organizationId, notorganization_id. - No envelope on resource responses.
GET /v1/companies/mereturns the org directly, not{ data: { ... } }. - List envelopes always look the same:
{ items, page, perPage, total }. - Errors always carry an
errorstring. - Unknown fields are silently ignoredon writes. We add fields freely; if you sent something we don't understand, you get no warning. Validate on your end if it matters.
Identifiers
IDs are opaque strings. Don't parse them, don't assume any format. Most of the public surface uses prefixed UUIDs (e.g. prj_2025_8f3a), but a few core entities (users, sessions, organizations) use unprefixed strings inherited from the auth layer. Treat all of them as opaque.
createdAt field, which we always return.Money
Every monetary amount in the API is an integer in the smallest currency unit (cents for EUR/USD, agorot for ILS, etc.) named with a Cents suffix. Currencies are always sent alongside as ISO 4217.
{
"subtotalCents": 125000,
"taxAmountCents": 26250,
"totalCents": 151250,
"currency": "EUR"
}
// Display: €1,512.50{ "amountCents": 12.5 } is a 422 error. Convert in your client.Timestamps
Every timestamp field is ISO 8601 with a timezone, in UTC by default. Examples: 2026-05-03T08:14:22.000Z. Date-only fields (e.g. issueDate on invoices) are YYYY-MM-DD.
On filters, we accept the full ISO 8601 form or any prefix that maps unambiguously (2026-05 means the entire month).
Pagination
List endpoints accept page (1-indexed, default 1) and perPage (default 25, max 100). The response always includes total so the client can compute the page count up front.
curl "https://api.worksible.com/v1/companies/me/projects?page=2&perPage=50" \
-H "X-API-Key: $WORKSIBLE_API_KEY"{
"items": [ /* up to 50 entries */ ],
"page": 2,
"perPage": 50,
"total": 287
}payments, invoices, timesheets) in 2026 Q3. The cursor field will be additive: existing page/perPage clients keep working unchanged.Sorting
Endpoints that support sorting accept the sort query parameter. The default is always documented per endpoint and is usually -createdAt (newest first).
# Ascending
GET /v1/companies/me/projects?sort=createdAt
# Descending (default)
GET /v1/companies/me/projects?sort=-createdAt
# Multi-key (status first, then date)
GET /v1/companies/me/projects?sort=status,-createdAtFiltering
Filters use plain query parameters; we don't use any filter[field]=value notation. Each endpoint documents the fields it filters on. Filter values use these operators:
- Exact:
?status=active - Multiple values (OR):
?status=active,paused - Range:
?createdAt[gte]=2026-01-01&createdAt[lt]=2026-04-01 - Substring search: a dedicated
searchparameter runs case-insensitive matching on the natural “name” field of the resource.
Field selection
Most endpoints accept fields as a comma-separated list to slim the payload. Useful when you're paginating millions of rows and only need the id + a couple of columns.
GET /v1/companies/me/projects?fields=id,title,status,createdAtExpanding relations
Foreign keys are exposed as ids by default. Pass expand to inline the related resource. Multiple values comma-separated.
GET /v1/companies/me/invoices/inv_123?expand=recipient,payment{
"id": "inv_123",
"totalCents": 151250,
"recipientOrganizationId": "org_2025_acme",
"recipient": {
"id": "org_2025_acme",
"name": "Acme Corp",
"vatId": "ESB12345678"
},
"paymentId": "pay_889",
"payment": {
"id": "pay_889",
"status": "succeeded",
"rail": "stripe_card"
}
}Request IDs
Every response carries an X-Request-Id header (server-assigned UUID). Include it when you contact support — we can use it to find the exact log line in seconds rather than hours.
Locale
Localised content (notification copy, default email subjects, etc.) follows the Accept-Language header. We support es (default), en, fr, it, pt, de, zh, and ar. Unknown locales fall back to en.