REST API · Pro plan

REST API Reference

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.

Authentication

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"

Base URL

https://www.getruntight.com/api/v1

List work orders

GET/api/v1/work-orders

Returns 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 equipment
  • assigned_to — only work orders assigned to this person
  • completed_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 sync
  • created_after — only work orders created at or after this date, in the same format
  • 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 total
curl "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
}

Create a work order

POST/api/v1/work-orders

Create a new work order. The title is required; everything else is optional.

Body parameters

  • title (required) — human-readable title
  • description — longer description
  • priority — low, medium, high, or critical (default: medium)
  • wo_type — reactive, corrective, or preventive (default: reactive)
  • asset_id — must belong to your tenant
  • assigned_to — must be an active user in your tenant
  • due_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"
  }
}

Get one work order

GET/api/v1/work-orders/:id

Returns 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" }
  }
}

Update, complete, hold or skip a work order

PATCH/api/v1/work-orders/:id

Change 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_hold
  • skip_reason — required with skipped
  • completed_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 minutes
  • note — text added to the end of the work order's notes
  • assigned_to — an active user in your workspace, or null to unassign. The new assignee gets the usual assignment email
  • priority, 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 instead
  • revision — 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 retries
curl -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.

List equipment / assets

GET/api/v1/assets

Returns 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 total
curl "https://www.getruntight.com/api/v1/assets?asset_tag=U-12" \
  -H "Authorization: Bearer $RUNTIGHT_API_KEY"

Create equipment

POST/api/v1/assets

Add 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.

Get one piece of equipment

GET/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"
      }
    ]
  }
}

Update equipment

PATCH/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 blank
  • location, 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 critical
  • active — 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" }'

Record a meter reading

POST/api/v1/assets/{id}/meter-readings

Send 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 used
  • recorded_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 characters
curl -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

  • • Retries are safe. Sending the same reading with the same recorded_at again changes nothing and returns 200 with "duplicate": true, so you can re-run a sync without creating double entries.
  • • Send readings oldest first, one at a time per meter. A reading timed at or before the meter's latest one returns 409. Different meters can be sent in parallel.
  • • Several meters? Leaving out meter_id returns 400 with the list of that equipment's meters to choose from.
  • • Lower than before? Without a reading_reason it returns 422, so a typo can't silently wind a meter back.

iCalendar feed

GET/api/v1/calendar.ics

Subscribe 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:

  • • Google Calendar → Settings → Add calendar → From URL
  • • Outlook → Add calendar → Subscribe from web
  • • Apple Calendar → File → New Calendar Subscription

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.

Errors

  • 401 Unauthorized — missing or invalid API key
  • 403 Forbidden — your tenant is on Free plan (API is Pro-only), or creating or restoring equipment would exceed your plan's limit
  • 400 Bad Request — validation error (missing title, invalid asset_id, unknown field, etc.); the message names the field
  • 404 Not Found — no work order, equipment or meter with that id in your workspace
  • 409 Conflict — the asset tag is already used by other equipment, the meter already has a reading at or after 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)
  • 422 Unprocessable — a meter reading lower than the current one was sent without a reading_reason
  • 429 Too Many Requests — rate limit exceeded; wait for the Retry-After seconds and try again
  • 503 Service Unavailable — we could not reach your data. A read is safe to retry. A write may or may not have saved: read the record again before retrying, or, for a work order update, retry with the revision you last read
  • 500 Internal Server Error — unexpected — please email support

Rate limits

The 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.

Other ways to integrate

  • • Outbound webhooks — get notified when events happen
  • • CSV import and export (available in the app)