Skip to main content
Technical Preview

Overview

Limble CMMS is a cloud maintenance system for work orders, preventive maintenance (PMs), work requests, assets, parts and purchasing. This connector lets the broker open a work order when an alarm fires, feed run hours, cycle counts and sensor values into asset fields that drive meter-based and threshold PMs, and log machine events on the asset, so maintenance sees what the plant sees without manual entry. The template has two connectors:
  • LimbleCmmsApi (LimbleCmmsRestApi.lot), a client connector on https://api.limblecmms.com with 31 events: 13 writes and 18 reads. It has no DELETE events.
  • LimbleCmmsWebhookReceiver (cmms_limble-cmms_webhook.lot), an optional server connector on port 9010 that receives Limble webhooks and publishes them on MQTT.
Limble serves a machine-readable OpenAPI 3.1 spec without authentication at https://api.limblecmms.com/openapi.json, and publishes the same reference as Postman docs. The events were picked by hand from it (the API has more than 200 operations), and every method and path was checked against it.

Events

Replies arrive on the same topic with /response appended.

Prerequisites

  • A Limble account and API keys. A Super Administrator creates them under Settings > Configuration > API Settings > Generate API Keys. The client secret is shown only once.
  • The API host of your account’s region. The connector uses the default host; for other regions change BASE_ADDRESS:
  • Outbound HTTPS from the broker to that host.
  • For webhooks only: a public HTTPS URL that forwards to the broker’s port 9010.

Authentication

The API keys act for the whole Limble account. Store base64("<client id>:<client secret>") in the Hub secret LIMBLE_BASIC_AUTH, read it with GET SECRET, and never put the keys or their base64 in a connector file or a retained topic.
Limble uses HTTP Basic authentication with the API Client ID and Client Secret. Every event sends Authorization: Basic {value.json.basic_auth}, so each payload carries the base64 string in basic_auth:
GetMe (GET /v2/me) returns customerName and customerPlan. Use it to check the keys. Answers seen from the real API without valid keys: no Authorization header returns 401 {"error":401,"success":false,"msg":"No Credentials Provided"}; wrong keys return 403 {"error":403,"success":false,"msg":"Invalid credentials."}.

Payload conventions

  • basic_auth: base64 of client_id:client_secret.
  • id: the numeric Limble ID in the path: taskID, assetID, valueID (an asset field value, not the asset), webhookID or workRequestSubmissionID.
  • query: the whole query string for list events, already URL-encoded, or "" for none. For example status=0&type=2,4&locations=205&limit=100&page=1 for open work orders.
  • body: the JSON request body, sent unchanged on POST, PATCH and PUT events.
All dates are Unix time in seconds. Lists come back as JSON arrays. Creates answer 201 with the new ID (for example {"taskID": 8261}); PATCH and PUT answer an empty 200. Task types are 1 PM, 2 unplanned work order, 4 planned work order and 6 work request. The status filter on ListTasks is 0 (incomplete) or 1 (complete); custom statuses such as “In Progress” filter with statusIDs. Create a work order from an alarm (priority must be one of the priorityLevel values from ListPriorities):
Close it (closing needs assignment and assignmentType):

Meter readings

Limble keeps meters as asset fields (Number fields such as “Hours Ran”). PMs set to “Every X number of units” or to a threshold on that field create tasks when a new value arrives. To push a reading:
  1. Find the field’s valueID with ListAssetFields (query: assets=<assetID>). If the asset does not have the field yet, attach it with AttachAssetField.
  2. Write the value with UpdateAssetFieldValue, using the valueID as id. The value must match the field type (a number for a Number field). Each write adds an entry to the field history (ListAssetFieldHistory).
To write 2 to 100 values in one call, use BatchUpdateAssetFields (Text, Date, Number and Currency fields only, 20 calls per hour). It returns {"success": false, "updates": [...]} with a reason per row if any row fails, so check the body as well as the HTTP status.

Webhook receiver

Deploy cmms_limble-cmms_webhook.lot only if you want Limble to push changes to the broker. It opens HTTP port 9010. Each webhook in Limble has one type (asset, assetField, location, part, po, poItem, task or instruction). Register one per type with CreateWebhook (or in Settings > Configuration > API Settings), using the public URL of the matching path:
Limble posts a short JSON body such as {"taskID": 8203, "status": "COMPLETE", "category": "task", "user": "..."}. Read the full object back with ListTasks (query: tasks=8203) or the matching list event. Limble needs at least one active API key for webhooks to work. Your endpoint must answer within 10 seconds; Limble retries after 10 minutes, 1, 2 and 4 hours and then disables the webhook. Send UpdateWebhook with {"enabled": 1} to turn it back on.
Limble signs each delivery with signature, timestamp and token headers: signature is HMAC-SHA256 of timestamp + token, keyed with the hex SHA-256 of the account’s oldest API key. The receiver connector does not check the signature and does not authenticate callers. Put a reverse proxy with a public TLS certificate in front of port 9010 and verify the signature there, or at least use a hard-to-guess path.

Connector

Webhook receiver (optional):

Example actions

These actions check the keys every hour, open a work order when a compressor alarm is raised, push run hours into a meter field, log machine state changes on the asset, and poll open work orders.

Notes

  • The catalog hint PATCH assets/{id}/fields does not exist in the Limble API. Meter values are written per field value with PATCH /v2/assets/fields/{valueID}; PUT /v2/assets/{assetID}/fields only attaches a field to an asset.
  • UpdateTask cannot set statusID to 1 (Open) or 2 (Complete); use status 0 / 1 for that. Its priority accepts 0 to 6 (6 is “On Hold”).
  • Limble’s reference also lists name, locationID, due, type and templateID as query parameters of POST /v2/tasks. Its description and example send them in a JSON body, which is what the connector does.
  • Pagination uses limit + page (the reply has Next-Page / Last-Page headers) or orderBy + cursor, never both. A limit above the endpoint maximum returns 422. Limble plans to deprecate cursor paging on tasks and assets.
  • Rate limits per account: GET 4000 per hour (360 per minute), PATCH 2000 per hour, POST and PUT 1000 per hour, batch requests 20 per hour. Every reply carries X-RateLimit-* headers; over the limit you get 429. Poll lists every few minutes, not per message.
  • api.limblecmms.com sits behind Cloudflare, which rejects some client signatures (the Python urllib default user agent gets 403 error code: 1010). Requests with no user agent, or a normal one, pass.
  • Writes start Limble’s own follow-ups: assignment notifications, meter and threshold PMs, workflow automations and webhooks.
  • Errors come back on the response topic as {"error": <code>, "success": false, "msg": "..."}: 400 validation, 401 / 403 auth, 404 unknown ID, 409 conflict, 422 limit too high, 429 rate limit.

Not covered

Deletes, images and files, creating assets, locations, parts, vendors, users, roles and teams, task instructions and invoices, purchase orders, bills, budgets and general ledgers, suggested field definitions and dropdown options, regions, tags, units of measure, and user capacity. All are in the Limble API and can be added to a fork.

Status

Technical preview, blocked. The template passes 369 of 369 smoke checks: static checks of both connectors against Limble’s OpenAPI spec and Postman docs, every event against a local mock of the Limble API v2, negative controls, and calls to the real Limble hosts without valid keys. The live /openapi.json lists every packaged method and path, the regional hosts answer, and every packaged call with fake keys was refused at the auth check (403 Invalid credentials.). Limble checks keys before it connectors a request, so those calls cannot prove a path exists on their own. No call has run yet with real Limble API keys. To finish testing, use a Limble account (ideally a test location) with API keys: set LIMBLE_CLIENT_ID and LIMBLE_CLIENT_SECRET (or LIMBLE_BASIC_AUTH), LIMBLE_HOST for a non-default region, LIMBLE_TEST_LOCATION, and optionally LIMBLE_TEST_USER and LIMBLE_TEST_VALUE_ID, then run LIMBLE_ALLOW_WRITE=1 python3 smoke_limble_cmms.py. It reads every list, creates a work order called “Coreflux smoke test (safe to delete)”, comments on it, reads it back, closes it, and writes one asset field value. Then deploy the connector on a broker and do an MQTT round trip.
Last modified on October 21, 2026