Services
Services in BenchKey are labor / service catalog entries, the standardized list of repair types your shop performs (e.g. “Screen Replacement”, “Battery Swap”, “Diagnostic”). Each service has a default labor price and optional auto-match phrases that BenchKey uses to suggest the right category when a technician types a ticket description.
The service object
Section titled “The service object”{ "object": "service", "id": 7, "name": "Screen Replacement", "labor_price": { "amount_cents": 4500, "amount": "45.00", "currency": "USD" }, "match_phrases": ["screen", "lcd", "display", "cracked glass"], "is_default": false, "sort_order": 2, "created_at": "2024-01-10T18:30:00.000Z", "updated_at": "2025-09-04T11:15:22.000Z"}| Field | Type | Description |
|---|---|---|
id | integer | Unique service ID |
name | string | Display name of the service / labor category |
labor_price | Money|null | Default labor charge (amount_cents, amount, currency) |
match_phrases | array | Phrases used to auto-match this service when creating tickets |
is_default | boolean | Whether this is the fallback service when no phrases match |
sort_order | integer | Display order in the UI (ascending) |
created_at | string | ISO-8601 timestamp |
updated_at | string | ISO-8601 timestamp |
List services
Section titled “List services”GET /api/v1/servicesReturns a cursor-paginated list of service catalog entries ordered by sort_order ascending, then id ascending.
Scope required: services.read
Query parameters
Section titled “Query parameters”| Parameter | Type | Description |
|---|---|---|
q | string | Search by name (partial match), max 255 chars |
is_default | boolean | Filter by default status: true/1 returns only the default category, false/0 returns only non-default categories. Omit to return all. Any other value returns 400 invalid_query |
limit | integer | Page size, 1–100 (default: 20) |
cursor | string | Opaque cursor from a previous response’s next_cursor |
curl "https://app.benchkey.com/api/v1/services?limit=20&q=screen" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"Node.js
Section titled “Node.js”const res = await fetch( "https://app.benchkey.com/api/v1/services?limit=20&q=screen", { headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" } });const { data, has_more, next_cursor } = await res.json();Response
Section titled “Response”{ "object": "list", "data": [ { "object": "service", "id": 7, "name": "Screen Replacement", "labor_price": { "amount_cents": 4500, "amount": "45.00", "currency": "USD" }, "match_phrases": ["screen", "lcd", "display", "cracked glass"], "is_default": false, "sort_order": 2, "created_at": "2024-01-10T18:30:00.000Z", "updated_at": "2025-09-04T11:15:22.000Z" } ], "has_more": false, "next_cursor": null}See Pagination for how to page through results.
Retrieve a service
Section titled “Retrieve a service”GET /api/v1/services/:idReturns a single service catalog entry by its integer id.
Scope required: services.read
curl "https://app.benchkey.com/api/v1/services/7" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"Node.js
Section titled “Node.js”const res = await fetch( "https://app.benchkey.com/api/v1/services/7", { headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" } });const service = await res.json();Response
Section titled “Response”Returns the service object. Returns 404 if no service with that ID exists.
Create a service
Section titled “Create a service”POST /api/v1/servicesCreates a new labor / service catalog entry.
Scope required: services.write
Request body
Section titled “Request body”| Field | Required | Description |
|---|---|---|
name | yes | Service name, max 80 characters |
labor_cents | no | Default labor charge in cents (integer, 0–10,000,000) |
match_phrases | no | Array of strings to auto-match against ticket descriptions (max 50 items; each item max 200 characters, non-empty) |
is_default | no | Set true to mark as the fallback category |
sort_order | no | Integer display order (lower = shown first) |
curl -X POST https://app.benchkey.com/api/v1/services \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \ -H "Content-Type: application/json" \ -d '{ "name": "Battery Replacement", "labor_cents": 2500, "match_phrases": ["battery", "won'\''t charge", "dead battery"], "is_default": false, "sort_order": 3 }'Node.js
Section titled “Node.js”const res = await fetch("https://app.benchkey.com/api/v1/services", { method: "POST", headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa", "Content-Type": "application/json", }, body: JSON.stringify({ name: "Battery Replacement", labor_cents: 2500, match_phrases: ["battery", "won't charge", "dead battery"], is_default: false, sort_order: 3, }),});const service = await res.json(); // HTTP 201Response
Section titled “Response”Returns the created service object with HTTP 201.
Update a service
Section titled “Update a service”PATCH /api/v1/services/:idUpdates one or more fields on an existing service. Only fields provided in the request body are changed; omitted fields keep their current values.
Scope required: services.write
Request body
Section titled “Request body”At least one field must be included:
| Field | Description |
|---|---|
name | New service name (max 80 characters) |
labor_cents | New default labor charge in cents (integer, 0–10,000,000) |
match_phrases | Replacement array of auto-match phrases, replaces the full list (max 50 items; each item max 200 characters, non-empty) |
is_default | true to make this the default category |
sort_order | New display order integer |
curl -X PATCH "https://app.benchkey.com/api/v1/services/7" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \ -H "Content-Type: application/json" \ -d '{ "labor_cents": 5000 }'Node.js
Section titled “Node.js”const res = await fetch("https://app.benchkey.com/api/v1/services/7", { method: "PATCH", headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa", "Content-Type": "application/json", }, body: JSON.stringify({ labor_cents: 5000 }),});const service = await res.json();Response
Section titled “Response”Returns the updated service object.
Delete a service
Section titled “Delete a service”DELETE /api/v1/services/:idPermanently deletes a service catalog entry. This cannot be undone. Existing tickets that referenced this service are not affected.
Scope required: services.write
curl -X DELETE "https://app.benchkey.com/api/v1/services/7" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"Node.js
Section titled “Node.js”const res = await fetch("https://app.benchkey.com/api/v1/services/7", { method: "DELETE", headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" },});const result = await res.json();// { "object": "service", "id": 7, "deleted": true }Response
Section titled “Response”{ "object": "service", "id": 7, "deleted": true}Error codes
Section titled “Error codes”| HTTP status | Code | Meaning |
|---|---|---|
400 | invalid_query | A list filter parameter was supplied as an array or object instead of a single scalar value, or is_default is not true/false/1/0 |
400 | invalid_id | The :id is not a valid positive integer |
400 | invalid_field | name is absent, empty, over 80 characters, or has leading/trailing whitespace; labor_cents is not an integer in 0–10,000,000; is_default is not a boolean; sort_order is not a safe integer; q exceeds 255 characters; a match_phrases item is not a string, is empty, exceeds 200 characters, or has leading/trailing whitespace; match_phrases has more than 50 items; any writable field is explicitly null |
400 | nothing_to_update | PATCH body contains none of the recognized updatable fields (name, labor_cents, match_phrases, is_default, sort_order) |
422 | create_failed | The internal service could not be created |
422 | update_failed | The internal service could not be updated |
422 | delete_failed | The internal service could not be deleted |
404 | not_found | No service with that ID exists |
403 | insufficient_scope | API key lacks services.read or services.write |
See Errors for the full error envelope format.