API reference

Chladni API reference

The Chladni API lets you read your plant structure and maintenance data, keep it in sync with your own systems, create and update work orders, and acknowledge or resolve alerts.

Resources

Telemetry and health

GET /v1/devices/{id}/telemetry returns the battery readings of a puck: battery_voltage in volts, battery_temperature in degrees Celsius and battery_percent. Points are newest first. end defaults to now and start defaults to 24 hours before end. start may be at most 90 days before end, and both timestamps must be between 2000-01-01 and one day from now. limit caps the total across all types. Filter with type, repeating it for several types.

GET /v1/devices/{id}/health returns the degradation trend the platform computes from vibration data, oldest first. Each point has a zone (stable, degrading or critical), a trafficLight (the colour the web app shows), a trendDirection and a severityIndex, which is null while the zone is stable. A status of no-data means the device has no assessments in the window.

Devices of other types return empty results.

Where the platform runs without real device data, both responses carry X-Mock-Data: true and the values are generated.

Authentication

API keys are issued per organization. Contact Chladni to get one.

Send the key as a bearer token on every request:

Authorization: Bearer <your API key>

A key belongs to one organization and carries a set of permissions, such as line:read. Each endpoint lists the permission it needs. A request with a key that lacks it returns 403.

Revoking a key can take up to 60 seconds to take effect.

Pagination

List endpoints return a page of items and a cursor:

{ "data": [ ... ], "nextCursor": "eyJ2IjoxLC..." }

Pass nextCursor as the cursor parameter to get the next page. nextCursor is null on the last page. limit sets the page size, 50 by default and 200 at most.

Treat cursors as opaque strings. A cursor is only valid with the sort it was issued for.

Sorting and filtering

sort accepts createdAt, -createdAt, updatedAt and -updatedAt. A leading - sorts newest first. The default is -createdAt.

createdAfter, createdBefore, updatedAfter and updatedBefore filter by time. After is inclusive and Before is exclusive. Timestamps are ISO 8601 with a time and an offset, for example 2026-01-31T08:00:00Z. URL-encode the value: a + in an offset must be sent as %2B.

Unknown query parameters are rejected with 400, so a misspelled filter never returns unfiltered data.

Keeping data in sync

To pull only what changed since your last run, sort by updatedAt and filter with updatedAfter:

GET /v1/lines?sort=updatedAt&updatedAfter=2026-01-31T08:00:00Z

Follow nextCursor to the end, then store the largest updatedAt you saw and use it as updatedAfter next time. Subtract a small overlap, for example one minute, from the stored value, because a change can be committed slightly after its timestamp. An item changed while you are paging can appear twice, so make your import idempotent on id.

Writing data

Write requests send a JSON body with Content-Type: application/json. Unknown fields are rejected with 400, so a misspelled field never goes unnoticed. Timestamps are ISO 8601 with an offset, for example 2026-01-31T08:00:00+01:00.

POST answers 201 with the created resource. PATCH answers 200 with the updated resource. Both return the same shape as the matching GET.

assigneeIds must be members of your organization; list them with GET /users. assetIds and alertIds must belong to your organization. A reference that does not qualify answers 400, never 404, which is reserved for the resource in the path.

Arrays in a PATCH replace the stored list. Send "dueDate": null to clear a due date. Alerts are linked to a work order only when it is created.

A work order that has been synced to SAP cannot be edited and answers 409. Alerts only move forward, so an invalid status change answers 409.

Idempotency

POST /work-orders requires an Idempotency-Key header, so a retry after a timeout cannot create a second work order. The key is 1 to 255 printable ASCII characters. Generate a new one for every create, for example a UUID. An empty header counts as missing. A missing or malformed key answers 400.

Retry with the same key and the same body to get the first response again, marked with Idempotency-Replayed: true. Keys are remembered for 24 hours. While the first attempt is still running, a retry answers 409. The same key with a different body answers 422. If a create fails with a 4xx status, the key is released, so you can retry with the same key and a corrected body.

A request that is still running is tracked for 30 seconds. If a create takes longer than that, a retry is treated as new and can create a duplicate.

If the service fails between writing the work order and storing the response, a retry can also create a duplicate. If that matters to you, look for the work order before you create it again.

Errors

Errors use standard HTTP status codes and one body shape:

{
  "error": {
    "code": "invalid_request",
    "message": "Request validation failed",
    "details": [{ "path": "limit", "message": "Too big: expected number to be <=200" }],
    "requestId": "2f1c9a52-6d0b-4a3e-9d57-0a4c3f6d1b11"
  }
}
Status Code Meaning
400 invalid_request A parameter, body field or header is missing or invalid. details names the field when the body or query is at fault.
401 unauthorized The API key is missing, invalid or revoked.
403 forbidden The API key lacks the permission this endpoint needs.
404 not_found The resource or route does not exist.
409 conflict An invalid alert status change, a work order synced to SAP, or a create with this Idempotency-Key still in progress.
422 idempotency_key_reused The Idempotency-Key was already used with a different request body.
429 rate_limited Too many requests. Wait for Retry-After seconds.
500 internal_error Something went wrong on our side.
503 service_unavailable Key validation or idempotency storage is temporarily unavailable (retry shortly), or telemetry is not available in this environment.

Every response carries an X-Request-Id header. Include it when you contact support.

Rate limits

Each organization may make 600 requests per minute by default across all its keys; the headers below carry the live value. Every authenticated response carries the budget:

Header Meaning
RateLimit-Limit Requests allowed per minute
RateLimit-Remaining Requests left in the current minute
RateLimit-Reset Seconds until the budget resets

When the budget is spent the API answers 429 with code rate_limited and a Retry-After header saying how many seconds to wait. Back off until then; retrying earlier only returns another 429.

A separate, more generous limit applies per client address before authentication. It protects the API from floods and is not part of the budget above.

Versioning

The version is part of the path. Within v1 we only add: new endpoints, new optional parameters and new response fields. Ignore fields you do not recognise. New error codes and new values of other enumerated fields may also be added, so handle an unknown code by its HTTP status.

Lines

Production lines. Assets belong to a line.

  • GET /v1/lines List lines
  • GET /v1/lines/{id} Get a line

Assets

Machines on a line that sensors monitor.

  • GET /v1/assets List assets
  • GET /v1/assets/{id} Get an asset

Devices

Sensors mounted on assets.

  • GET /v1/devices List devices
  • GET /v1/devices/{id} Get a device
  • GET /v1/devices/{id}/telemetry Get device telemetry
  • GET /v1/devices/{id}/health Get device health

Alerts

Detected faults on assets.

  • GET /v1/alerts List alerts
  • GET /v1/alerts/{id} Get an alert
  • PATCH /v1/alerts/{id} Update an alert status

Work orders

Maintenance work, linked to alerts and assets.

  • GET /v1/work-orders List work orders
  • POST /v1/work-orders Create a work order
  • GET /v1/work-orders/{id} Get a work order
  • PATCH /v1/work-orders/{id} Update a work order

Users

Members of your organization.

  • GET /v1/users List users