A simple JSON API with least-privilege tokens. Connect accounting, CRM, scheduling, payments or messaging apps to Utilisense, then list your integration in the marketplace.
Each Utilisense customer runs their own server. Your integration talks to that customer's server at https://<their-domain>/ext/v1/, using an API token the customer creates and gives to you. Nothing you build is installed on their server, and they can revoke your access at any moment.
/platform/api-tokens/.Send the token as a bearer token on every request. Cookies are ignored, so requests must come from your server — never put a token in browser code.
curl https://app.example.com/ext/v1/ \
-H "Authorization: Bearer usk_xxxxxxxxxx_..."
The root endpoint returns the API version and what the token can do:
{"api_version": "1", "product": "Utilisense",
"token": {"name": "Acme Billing", "scopes": ["tickets:read"], "expires_at": "2027-01-31T12:00:00+00:00"}}
Tokens are stored hashed, so a lost token can't be recovered — the customer simply creates a new one. Always use HTTPS.
| Scope | Allows |
|---|---|
tickets:read | Read tickets View tickets, their status, schedule, contact and service address. |
tickets:write | Create & update tickets Open new tickets and add notes to existing tickets. |
inventory:read | Read inventory View inventory items and stock levels. |
A scope only works while the matching module is enabled on the customer's server; otherwise the API returns 404 module_disabled.
/ext/v1/ticketstickets:readList tickets, oldest first. Optional query parameters: status (open or closed), updated_since (ISO-8601 timestamp), limit (1–100, default 50) and after (see paging).
{"data": [{"id": 412, "number": "TCKT-000412", "subject": "No internet",
"description": "...", "job_type": "trouble", "job_status": "not_started", "state": "new",
"priority": "1", "severity": "no_service", "contact_name": "Jane Doe", "phone": "...",
"service_address": "...", "scheduled_date": null, "time_slot": "", "external_id": "",
"created_at": "...", "updated_at": "...", "closed_at": null}],
"next_after": null}
/ext/v1/tickets/<id>tickets:readOne ticket, in the same shape, wrapped in {"data": ...}.
/ext/v1/ticketstickets:readtickets:writeCreate a ticket. JSON body: subject (required), description, job_type (trouble, install or other), severity (non_severe, severe or no_service), priority (0–3), contact_name, phone, service_address, external_id (your own reference). Returns 201.
curl -X POST https://app.example.com/ext/v1/tickets \
-H "Authorization: Bearer usk_..." -H "Content-Type: application/json" \
-d '{"subject": "Customer reports outage", "severity": "no_service", "external_id": "CRM-8812"}'
/ext/v1/tickets/<id>/notestickets:writeAdd a note to a ticket: {"note": "Customer called back; issue resolved."}. Notes are stamped with the time and your token's name.
/ext/v1/inventoryinventory:readActive inventory items with id, name, sku, category, unit, warehouse_qty, vehicle_qty, total_qty and low_stock.
List endpoints return up to limit rows ordered by id. When more remain, next_after holds the last id — pass it back as ?after= to get the next page. A null value means you've reached the end. To sync changes, store the time you started and pass it as updated_since next run.
Errors return JSON: {"error": "code", "message": "Human-readable detail"}.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A parameter or JSON field is missing or invalid. |
| 401 | unauthorized | Missing, wrong, expired or revoked token. |
| 403 | insufficient_scope | The token doesn't have the permission this call needs. |
| 404 | not_found / module_disabled | No such record, or the feature is turned off on that server. |
| 405 | method_not_allowed | Wrong HTTP method for the endpoint. |
| 429 | rate_limited | More than 120 requests a minute per token, or too many failed sign-ins from your IP. |
Request bodies are limited to 64 KB. Responses are never cached. Back off and retry on 429 and 5xx.
Every listing gets free access to the review sandbox: a private Utilisense server with sample customers, tickets and inventory at https://sandbox.utilsense.com/ext/v1/. Open My integrations, choose Test in sandbox and create a key. It carries exactly the permissions your listing requests and lasts 14 days.
Your recent calls, with their requests, responses and status codes, are shown on the same page. Our reviewers test your integration in the same sandbox, so make sure its main workflow runs there cleanly before you submit.
Create a free community account, then submit your integration with a description, website and the permissions it needs. Our team tests every submission, and every later edit, in the review sandbox before it's published. Listing is free.
Reviewers check that the listing matches what the integration does, that every requested permission is actually used (and nothing more is attempted), that errors and rate limits are handled gracefully, and that there is a clear privacy statement and support contact.
Guidelines: request the fewest permissions possible, explain clearly what you do with customer data, keep tokens encrypted at rest, and provide a way for customers to get support.