Developers
Roldesk REST API
Read and write your workspace's field-service data over a clean, versioned REST API. Every request is authenticated with a Personal Access Token and strictly scoped to your organization.
Quickstart
Create a token under Settings → Developer (owner/admin, Professional plan), then verify it:
curl https://www.roldesk.com/api/v1/me \
-H "Authorization: Bearer rk_live_…"List your work orders:
curl "https://www.roldesk.com/api/v1/work-orders?limit=25" \
-H "Authorization: Bearer rk_live_…"Authentication
Pass your token as a Bearer credential on every request: Authorization: Bearer rk_live_…. Tokens are shown once at creation — store them securely. They can be created and revoked by an owner or admin under Settings → Developer, and are available on the Professional plan. The API is server-to-server (no cookies, no browser CORS credentials).
Scopes
Each token is granted a subset of scopes. A scope is {group}:{read|write}. A token can never exceed what your plan allows.
| Group | Covers |
|---|---|
| work-orders | Work orders and their tasks, parts, labor, and completion |
| crm | Customers (accounts), contacts, leads |
| sales | Quotes and invoices |
| contracts | Service contracts |
| assets | Equipment, parts, tools, vehicles, warehouses |
| projects | Projects and their links |
| scheduling | Appointments / calendar events |
| preventive | Preventive-maintenance schedules |
| team | Members and roles (read-only) |
| webhooks | Register and manage webhook endpoints |
Pagination
Lists are cursor-paginated. Pass limit (1–100, default 25) and starting_after (the previous response's next_cursor).
{
"data": [ { "object": "work_order", "id": "…", … } ],
"has_more": true,
"next_cursor": "eyJ…"
}Most lists also accept allow-listed filters such as status, account_id, and updated_since.
Writes, idempotency & concurrency
Creates accept an Idempotency-Key header — retrying with the same key returns the original result instead of creating a duplicate. Updates accept expected_updated_at for optimistic concurrency; a stale value returns 409.
curl -X POST https://www.roldesk.com/api/v1/customers \
-H "Authorization: Bearer rk_live_…" \
-H "Idempotency-Key: create-acme-2026-08" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "email": "ops@acme.com"}'Errors
Errors use a consistent envelope with the right HTTP status:
{ "error": { "type": "…", "code": "…", "message": "…", "param": "…" } }| 400 / 422 | Malformed request or validation error |
| 401 | Missing, invalid, revoked, or expired token |
| 403 | Token lacks the required scope, or the plan doesn't include API access |
| 404 | No such resource in your workspace |
| 409 | Optimistic-concurrency conflict (stale expected_updated_at) |
| 429 | Rate limit exceeded |
Webhooks
Instead of polling, subscribe to events and we'll POST them to your server as they happen. Add an endpoint under Settings → Developer (or via the API for Zapier-style integrations), choose the events you care about, and store the signing secret we show once.
Each delivery is a JSON POST with these headers:
Roldesk-Event: contract.renewed
Roldesk-Delivery: <delivery id>
Roldesk-Signature: t=<unix_ts>,v1=<hex hmac-sha256>The body is a thin event — fetch the full object from the REST API using its id:
{ "id": "…", "type": "contract.renewed", "created": "2026-08-10T12:00:00Z",
"data": { "object": "contract", "id": "…" } }Verify the signature before trusting a delivery (Node):
import crypto from "crypto";
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
const expected = crypto.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}Failed deliveries retry with backoff (up to 6 attempts); an endpoint that keeps failing is auto-disabled. Available events:
Zapier & no-code: use the REST Hook pattern — your Zap subscribes with POST /api/v1/webhooks (a token with webhooks:write) and unsubscribes with DELETE /api/v1/webhooks/{id}.
Endpoint reference
| Methods | Path | Scope |
|---|---|---|
| GET | /work-orders · /work-orders/{id} | work-orders:read |
| POST · PATCH · DELETE | /work-orders · /work-orders/{id} | work-orders:write |
| GET | /customers · /customers/{id} | crm:read |
| POST · PATCH · DELETE | /customers · /customers/{id} | crm:write |
| GET | /contacts · /contacts/{id} | crm:read |
| POST · PATCH · DELETE | /contacts · /contacts/{id} | crm:write |
| GET | /leads · /leads/{id} | crm:read |
| POST · PATCH · DELETE | /leads · /leads/{id} | crm:write |
| GET | /invoices · /invoices/{id} | sales:read |
| POST · PATCH · DELETE | /invoices · /invoices/{id} | sales:write |
| GET | /quotes · /quotes/{id} | sales:read |
| POST · PATCH · DELETE | /quotes · /quotes/{id} | sales:write |
| GET | /contracts · /contracts/{id} | contracts:read |
| POST · PATCH · DELETE | /contracts · /contracts/{id} | contracts:write |
| GET | /projects · /projects/{id} | projects:read |
| GET | /appointments · /appointments/{id} | scheduling:read |
| GET | /preventive-schedules · …/{id} | preventive:read |
| GET | /assets/equipment · /assets/parts · /assets/tools · /assets/vehicles · /assets/warehouses | assets:read |
| GET | /members · /members/{id} | team:read |
| GET | /roles · /roles/{id} | team:read |
| GET | /webhooks · /webhooks/{id} | webhooks:read |
| POST · DELETE | /webhooks · /webhooks/{id} | webhooks:write |
The complete machine-readable definition lives at /api/v1/openapi.json (OpenAPI 3.1) — import it into Postman, Insomnia, or your codegen tool.
New to it? Step-by-step guides live in the Help Center — getting started, webhooks, Zapier, and common recipes.