Create, update and complete work orders, keep your equipment list in sync, and send in meter readings programmatically. Ideal for custom integrations, spreadsheet and form syncs, IoT sensor triggers, or building your own middleware.
Every request requires an Authorization: Bearer YOUR_API_KEY header. Generate a key under Settings → Workspace → REST API Key in your RunTight dashboard (admin role, Pro plan required). Keys are automatically disabled if your tenant is downgraded to Free.
The examples below read the key from a RUNTIGHT_API_KEY environment variable, so it stays out of your scripts and shell history.
export RUNTIGHT_API_KEY="paste-your-key-here" curl https://www.getruntight.com/api/v1/work-orders \ -H "Authorization: Bearer $RUNTIGHT_API_KEY"
https://www.getruntight.com/api/v1
/api/v1/work-ordersReturns a paginated list of work orders in your tenant, most recent first.
Query parameters
status — filter by status (open, in_progress, on_hold, completed, skipped)asset_id — only work orders for this piece of equipmentassigned_to — only work orders assigned to this personcompleted_after — only work orders completed at or after this date (2026-10-01) or date and time with a timezone (2026-10-01T07:00:00-05:00). Use it to pick up what was finished since your last synccreated_after — only work orders created at or after this date, in the same formatlimit — max items to return (default 50, max 200)offset — pagination offset (default 0); an offset past the end of the list returns an empty data array with the list's totalcurl "https://www.getruntight.com/api/v1/work-orders?status=open&limit=25" \ -H "Authorization: Bearer $RUNTIGHT_API_KEY"
Response
{
"data": [
{
"id": "uuid",
"title": "Weekly hydraulic press inspection",
"description": "Check hoses, pressure, leaks",
"wo_type": "preventive",
"status": "open",
"priority": "high",
"due_date": "2026-04-15",
"assigned_to": "user-uuid",
"asset_id": "asset-uuid",
"started_at": null,
"completed_at": null,
"created_at": "2026-04-11T18:22:33Z"
}
],
"total": 142,
"limit": 25,
"offset": 0
}/api/v1/work-ordersCreate a new work order. The title is required; everything else is optional.
Body parameters
title (required) — human-readable titledescription — longer descriptionpriority — low, medium, high, or critical (default: medium)wo_type — reactive, corrective, or preventive (default: reactive)asset_id — must belong to your tenantassigned_to — must be an active user in your tenantdue_date — ISO date string (default: today)curl -X POST https://www.getruntight.com/api/v1/work-orders \
-H "Authorization: Bearer $RUNTIGHT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Bearing replacement on CNC #1",
"priority": "high",
"wo_type": "corrective",
"asset_id": "asset-uuid-here",
"due_date": "2026-04-15"
}'Response (201 Created)
{
"data": {
"id": "new-uuid",
"title": "Bearing replacement on CNC #1",
"status": "open",
"priority": "high",
"wo_type": "corrective",
"due_date": "2026-04-15",
"created_at": "2026-04-11T18:45:12Z"
}
}/api/v1/work-orders/:idReturns one work order with its equipment, checklist, notes, time spent, and any hold or skip reason.
curl https://www.getruntight.com/api/v1/work-orders/wo-uuid \ -H "Authorization: Bearer $RUNTIGHT_API_KEY"
Response
{
"data": {
"id": "wo-uuid",
"revision": 4,
"title": "Weekly hydraulic press inspection",
"description": "Check hoses, pressure, leaks",
"wo_type": "preventive",
"status": "completed",
"priority": "high",
"due_date": "2026-10-08",
"assigned_to": "user-uuid",
"asset_id": "asset-uuid",
"template_id": "pm-schedule-uuid",
"started_at": "2026-10-08T13:05:00Z",
"completed_at": "2026-10-08T14:10:00Z",
"completed_by": null,
"time_spent_minutes": 65,
"notes": "Replaced the return hose",
"hold_reason": null,
"skip_reason": null,
"approval_status": null,
"checklist_responses": [{ "step": "Check hoses", "checked": true, "required": true }],
"created_at": "2026-10-01T06:00:00Z",
"asset": { "id": "asset-uuid", "name": "Hydraulic press #2", "asset_tag": "HP-02", "location": "Bay 3" }
}
}/api/v1/work-orders/:idChange only the fields you send. Status changes follow the same rules as the app: a completed work order can't be reopened, one on hold has to be resumed (in_progress) before it can be completed, work waiting for approval can't be started, and a work order with required checklist steps or a meter reading still to record has to be completed in the app. On a work order that is already on hold, skipped or completed, sending that same status with a new reason or with time_spent_minutes updates it; completed_at can't be changed once a job is done. Each change shows in the work order's history, and completing one fires the wo.completed webhook and updates its PM schedule, just as in the app.
Body parameters (all optional; send at least one)
status — in_progress, on_hold, completed, skipped, or open (to reopen a skipped work order)hold_reason — required with on_holdskip_reason — required with skippedcompleted_at — with completed: when the work was done, as a date and time with a timezone, within the last 90 days (default: now)time_spent_minutes — with completed: whole minutesnote — text added to the end of the work order's notesassigned_to — an active user in your workspace, or null to unassign. The new assignee gets the usual assignment emailpriority, due_date (YYYY-MM-DD), title, description. A PM work order can't be moved onto or past its schedule's next due date; skip it insteadrevision — the revision from your last read. When you send it, the change only applies if nobody has changed the work order since, so retrying after a timeout or a 503 can't add your note twice. Recommended for any integration that retriescurl -X PATCH https://www.getruntight.com/api/v1/work-orders/wo-uuid \
-H "Authorization: Bearer $RUNTIGHT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "completed",
"completed_at": "2026-10-08T16:30:00-05:00",
"time_spent_minutes": 45,
"note": "Replaced the coolant filter",
"revision": 4
}'The response is the updated work order, in the same shape as Get one work order.
/api/v1/assetsReturns a paginated list of active equipment in your workspace, sorted by name.
Query parameters
asset_tag — return only the equipment with this asset tag (handy when your own list is keyed by unit number)limit — max items to return (default 50, max 200)offset — pagination offset (default 0); an offset past the end of the list returns an empty data array with the list's totalcurl "https://www.getruntight.com/api/v1/assets?asset_tag=U-12" \ -H "Authorization: Bearer $RUNTIGHT_API_KEY"
/api/v1/assetsAdd a piece of equipment. name is required; location, asset_tag, category and criticality (low, medium, high, critical) are optional. Text fields must be strings. Returns 201 with the new equipment.
asset_tag can be up to 128 characters. Spaces, accents and symbols are fine, but it can't contain line breaks, tabs, invisible characters or other control characters, and it can't start with http:// or https:// (its QR code would open as a web link). That way the tag can always be scanned from the QR label RunTight prints for it. A tag that breaks these rules is rejected with a 400 that names the field. Tags must also be unique in your workspace, compared exactly as written (U-12 and u-12 are different tags); a tag another piece of equipment already uses gets a 409.
/api/v1/assets/{id}Returns the equipment along with its active meters. Use the meter ids when recording readings. Archived equipment is returned too, with active: false.
{
"data": {
"id": "asset-uuid",
"name": "Unit 12 Skid Steer",
"location": "Yard B",
"asset_tag": "U-12",
"category": "Heavy equipment",
"criticality": "high",
"active": true,
"custom_fields": {},
"created_at": "2026-04-11T18:22:33Z",
"meters": [
{
"id": "meter-uuid",
"name": "Engine hours",
"unit": "hours",
"current_reading": 1234.5,
"last_read_at": "2026-09-23T12:30:00Z"
}
]
}
}/api/v1/assets/{id}Change any of the fields below; fields you leave out stay as they are. Returns the updated equipment.
Body parameters (all optional, at least one)
name — cannot be blanklocation, asset_tag, category — text, or null to clear. Asset tags follow the same rules as when creating equipment: up to 128 characters, no control characters, not starting with http:// or https://, and unique in your workspace.criticality — low, medium, high, or criticalactive — false archives the equipment, like Decommission in the app: its active PM schedules and meters are turned off too, and the response reports how many under deactivated. true restores the equipment only (counts toward your plan's equipment limit); turn its schedules and meters back on in the app.Any other field is rejected with a 400 that names it, so a typo never looks like a successful update.
curl -X PATCH https://www.getruntight.com/api/v1/assets/asset-uuid \
-H "Authorization: Bearer $RUNTIGHT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "location": "Yard B", "criticality": "high" }'/api/v1/assets/{id}/meter-readingsSend an hour-meter, odometer or cycle count for a piece of equipment. The same rules apply as entering a reading in the app, and meter-based PM schedules use it on the next daily scheduling run.
Body parameters
reading (required) — a non-negative number (a plain decimal string such as "1234.5" also works)meter_id — required only when the equipment has more than one meter; otherwise its meter is usedrecorded_at — when the reading was taken, as ISO 8601 with a timezone (e.g. 2026-09-24T07:30:00-05:00). Defaults to now. Cannot be in the future.reading_reason — required when the reading is lower than the current one: meter_reset, meter_replaced, equipment_replaced, correcting_previous_reading, counter_rollover, or other (with a note)note — up to 2,000 characterscurl -X POST https://www.getruntight.com/api/v1/assets/asset-uuid/meter-readings \
-H "Authorization: Bearer $RUNTIGHT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "reading": 1242.0, "recorded_at": "2026-09-24T07:30:00-05:00" }'Response (201 Created)
{
"data": {
"duplicate": false,
"reading": {
"id": "reading-uuid",
"meter_id": "meter-uuid",
"reading": 1242.0,
"recorded_at": "2026-09-24T12:30:00+00:00",
"reading_reason": null,
"note": null
},
"meter": {
"id": "meter-uuid",
"name": "Engine hours",
"unit": "hours",
"current_reading": 1242.0,
"last_read_at": "2026-09-24T12:30:00+00:00"
}
}
}Syncing safely
recorded_at again changes nothing and returns 200 with "duplicate": true, so you can re-run a sync without creating double entries.meter_id returns 400 with the list of that equipment's meters to choose from.reading_reason it returns 422, so a typo can't silently wind a meter back./api/v1/calendar.icsSubscribe to an auto-updating feed of the next 90 days of open/in-progress work orders from Google Calendar, Apple Calendar, Outlook, or any iCal-compatible client.
The calendar feed is free on every plan — no API key needed. Turn it on under Settings → Calendar Subscription and copy the token-based link:
https://www.getruntight.com/api/v1/calendar.ics?token=YOUR_CALENDAR_TOKEN
Pro subscribers can also authenticate the feed with their REST API key (?key=YOUR_API_KEY) — calendar clients can't set an Authorization header, so both go in the query string.
Paste the full URL into:
Each event is an all-day entry on the due_date, with priority in the title, equipment name, and a deep link back to the work order.
recorded_at, or the work order can't make that change right now (the message says why, for example that it is waiting for approval or changed while you were updating it)reading_reasonRetry-After seconds and try againrevision you last readThe work-order endpoints allow 60 requests per minute per workspace. Creating equipment, updating equipment and recording meter readings share a separate budget of 120 per minute; beyond either limit, requests return 429 with a Retry-After header. If you need higher-volume programmatic access, email support@getruntight.com and we'll work with you.