| Concern | Behaviour |
|---|---|
| Identifiers | Integers, assigned by the server, unique per table. Never reused after a delete. Not guessable in any useful sense, but also not a secret, authorization is the control, not obscurity. |
| Timestamps | created_at is a UTC datetime serialised as a string, with microseconds: "2026-09-04 15:22:07.114820". Note the space, not a T: it is not ISO 8601 and a strict parser will reject it. |
| Dates | test_date and similar are plain YYYY-MM-DD strings, stored verbatim. No timezone conversion is applied. The date on the report is the date you sent. |
| Ordering | Projects and records are returned newest first. Assemblies and equipment follow sort_order, then insertion. |
| Pagination | There is none. List endpoints return the full collection. This is honest rather than aspirational: a project has tens of assemblies, not thousands. Do not build a client that depends on a page parameter appearing. |
| Nulls | Optional strings come back as "", not null. Optional relations and dates come back as null. last_tested and last_status are null on a device that has never been tested. |
| Unknown fields | Ignored on input. Do not send extra keys expecting them to persist, outside form_data, they will not. |
| Idempotency | There are no idempotency keys. GET and DELETE are naturally safe to repeat; a repeated POST creates a second row. On a timeout during a create, list and check before retrying. |
Parse
created_at deliberately
The space-separated form will fail new Date() in some engines and datetime.fromisoformat() on older Pythons. Normalise the separator, or parse with an explicit format, rather than assuming it round-trips.