This page lists the formats the API uses on the wire. Send values back in the same forms: the API checks them strictly and does not guess.
A worklog row from GET /worklogs shows most of them at once:
{
"id": 9021,
"date": "2026-06-15",
"minutes": 90,
"description": "Sprint review with Acme",
"work_item_id": 812,
"is_overtime": false,
"created_at": "2026-06-15T07:30:00Z",
"updated_at": "2026-06-15T07:30:00Z"
// 15 more fields: project_id, project, budget_id, budget, task_id, task, work_item, user_id, user, is_not_billable, exceeded_budget, reviewed_by, invoice_id, sales_invoice_row_id, links
}
Field names
Every key is snake_case.
- A relation usually comes as a foreign key named
{relation}_id, next to the relation itself:sprint_idandsprint. Three do not: a Drive item'sdrive_folderusesfolder_id, a Drive folder'sparent_folderusesparent_id, and a contact'sroleusescontact_role_id. - A counter is an integer named
{relation}_count, such ascomments_count. - Booleans are JSON
trueandfalse. Most are namedis_…, but not all:auto_renewandactiveare booleans too. Go by the type in the schema, not by the name.
Calendar dates
A calendar date is YYYY-MM-DD, such as "2026-06-15". It has no time and no time zone. A worklog's date and a work item's due_date are calendar dates.
A time of day on its own, as in a user's working_hours, is HH:MM, such as "08:30", also without a zone.
Date-times
A date-time is UTC, to the second, with a Z: "2026-06-15T07:30:00Z". Responses always use this form.
Send date-times in exactly this form too, in request bodies and in filters. An offset such as +02:00 or fractions of a second are refused:
| Where | What you get |
|---|---|
| Request body | 422 validation-failed, code date_format |
| Filter | 400 invalid-query-parameter, code invalid_format |
Convert to UTC before you send. A meeting at 09:30 in Amsterdam on 15 June 2026 is 2026-06-15T07:30:00Z, because Amsterdam is two hours ahead of UTC in summer.
To show a date-time in a person's own zone, read the timezone field on their user record, such as Europe/Amsterdam. When it is null, the organisation's default zone applies.
Durations
A duration is a whole number of minutes, as an integer. One and a half hours is 90.
The field name does not always say so. minutes and logged_minutes do, but a work item's estimation and a budget's budget are minutes as well. The field description in the schema states the unit.
One exception: on a sales invoice row that bills budget time, quantity can hold hours, as a decimal string like any amount. The row's billable_minutes holds that time in minutes.
Money, percentages and coordinates
| Kind | Form | Example |
|---|---|---|
| Money | A decimal string with two decimals | "1250.00" |
| Percentage | A whole number from 0 to 100 | 21 means 21%, not 0.21 |
| Coordinates in a response | A decimal string with up to seven decimals | "52.3675734" |
| Coordinates in a request body | A JSON number | 52.3675734 |
Money is a string so that no rounding happens on the way. Send amounts as strings with at most two decimals. There is no currency field.
A sales invoice row, for example:
{
"quantity": "2.50",
"unit_price": "95.00",
"vat_rate": 21,
"vat_amount": "49.88",
"total_amount": "237.50",
"billable_minutes": 150
// 18 more fields: id, sales_invoice_id, sales_invoice, subscription_item_id, budget_id, exact_item_id, exact_vat_code, exact_cost_center, exact_gl_account, name, description, hourly_rate, is_aggregated_price, sort_order, created_at, updated_at, deleted_at, links
}
Identifiers
Every id is an integer, unique among records of the same resource in your organisation: project 12 and work item 12 are different records.
A work item also has an issue_key, a string such as "ACM-12". Every work item path accepts either form, so /work-items/ACM-12 and /work-items/812 are the same record. Request bodies and filters take the numeric id, such as work_item_id on a worklog. The one exception is after_work_item_id on the move action, which takes either. Store the numeric id as your key: an issue_key can be null.
Absent, null and empty
These three mean different things:
| What you see | Meaning |
|---|---|
| The field is absent | You did not ask for it. Included relations, on-demand fields and detail-only fields appear only when you request them. |
null | There is no value, such as end_date on a project with no planned end. A few on-demand fields are also null when your access does not cover them; their description says so. |
[] | The list exists and is empty. |
See Views and includes for how to request what is absent.
Enum values
Enum values are sent and returned exactly as the schema lists them. They are not always snake_case: a project's status can be on-hold, and on_hold is refused.
Most enums can gain values in a later release. The OpenAPI document marks each of them with x-extensible-enum: true, in 200 places. Treat a value you do not recognise as "unknown, show it as is", never as an error.
A value that is not in the list is 422 with code in in a request body, and 400 with code invalid_format in a filter.
Rich text
| Field | Format |
|---|---|
Work item description, comment body, note content, and similar fields | HTML from the plnnrly editor, sanitised before it is returned |
Drive document content | GitHub-Flavored Markdown |
Drive document content_html | The same document as sanitised HTML, on request |
Markdown cannot hold text colours, highlights, alignment or @mentions, so writing a document back as Markdown drops them. Read content_html first when a document may contain them. The drive document operation explains the details.
Links back to plnnrly
Projects and work items carry a web_url: an absolute URL that opens the record in plnnrly, on your organisation's own address. It is stable, so you can store it and show it as a link. It is read-only.
Language
Data comes back exactly as people entered it. Nothing is translated.
Problem documents and warnings are always in English, whatever language a user has chosen in plnnrly. Their type, code and enum values are identifiers: switch on those, not on the English text.
Related
- How the data fits together: which resource holds what.
- Filtering and sorting: the same formats in filter values.
- Errors and retries: what a refused value looks like.