Locations
Locations represent physical shops, warehouses, or service branches in BenchKey. Every ticket, invoice, and inventory record is tied to a location. A tenant must have at least one active location; deleting the last one or the last active one is rejected.
The location object
Section titled “The location object”{ "object": "location", "id": 3, "name": "Downtown Shop", "address": { "line1": "400 Congress Ave", "line2": "Suite 200", "city": "Austin", "state": "TX", "postal_code": "78701", "country": "US" }, "phone": "512-555-0100", "email": "downtown@acmerepair.com", "timezone": "America/Chicago", "currency": "USD", "is_warehouse": false, "is_active": true, "sort_order": 0, "created_at": "2024-01-10T18:00:00.000Z"}| Field | Type | Description |
|---|---|---|
id | integer | Unique location ID |
name | string | Display name of the location |
address | object | Mailing address fields (line1, line2, city, state, postal_code, country) |
phone | string|null | Location phone number |
email | string|null | Location contact email |
timezone | string|null | IANA timezone identifier (e.g. America/Chicago) |
currency | string|null | ISO 4217 currency code (e.g. USD) |
is_warehouse | boolean | If true, this location is a warehouse/storage-only location (no customer-facing counter) |
is_active | boolean | If false, the location is disabled and hidden from most workflows |
sort_order | integer | Display order among locations (lower = first) |
created_at | string | ISO-8601 timestamp when the location was created |
List locations
Section titled “List locations”GET /api/v1/locationsReturns a cursor-paginated list of locations ordered by creation date descending.
Scope required: locations.read
Query parameters
Section titled “Query parameters”| Parameter | Type | Description |
|---|---|---|
is_active | boolean | Filter by active status: true or false. Omit to return all. |
limit | integer | Page size, 1–100 (default: 25) |
cursor | string | Opaque cursor from a previous response’s next_cursor |
curl "https://app.benchkey.com/api/v1/locations?is_active=true" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"Node.js
Section titled “Node.js”const res = await fetch( "https://app.benchkey.com/api/v1/locations?is_active=true", { headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" } });const { data, has_more, next_cursor } = await res.json();Response
Section titled “Response”{ "object": "list", "data": [ { "object": "location", "id": 3, "name": "Downtown Shop", "address": { "line1": "400 Congress Ave", "line2": "Suite 200", "city": "Austin", "state": "TX", "postal_code": "78701", "country": "US" }, "phone": "512-555-0100", "email": "downtown@acmerepair.com", "timezone": "America/Chicago", "currency": "USD", "is_warehouse": false, "is_active": true, "sort_order": 0, "created_at": "2024-01-10T18:00:00.000Z" } ], "has_more": false, "next_cursor": null}See Pagination for how to page through results.
Retrieve a location
Section titled “Retrieve a location”GET /api/v1/locations/:idReturns a single location by its integer ID.
Scope required: locations.read
curl "https://app.benchkey.com/api/v1/locations/3" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"Node.js
Section titled “Node.js”const res = await fetch("https://app.benchkey.com/api/v1/locations/3", { headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" },});const location = await res.json();Response
Section titled “Response”Returns the location object. Returns 404 if no location with that ID exists.
Create a location
Section titled “Create a location”POST /api/v1/locationsCreates a new location. Requires name. Adding locations may be gated on your billing plan, you will receive a 402 if a plan upgrade is required.
Scope required: locations.write
Request body
Section titled “Request body”| Field | Required | Description |
|---|---|---|
name | yes | Display name (max 160 characters) |
address | no | Address object: line1, line2, city, state, postal_code, country |
phone | no | Location phone number |
email | no | Location contact email |
timezone | no | IANA timezone identifier (e.g. America/Chicago) |
currency | no | ISO 4217 currency code (e.g. USD) |
is_warehouse | no | true to designate as a warehouse/storage location (default: false) |
is_active | no | false to create the location in a disabled state (default: true) |
sort_order | no | Display order; lower numbers appear first (default: 0) |
curl -X POST https://app.benchkey.com/api/v1/locations \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \ -H "Content-Type: application/json" \ -d '{ "name": "North Austin Branch", "address": { "line1": "9901 Burnet Rd", "city": "Austin", "state": "TX", "postal_code": "78758", "country": "US" }, "phone": "512-555-0199", "timezone": "America/Chicago", "currency": "USD" }'Node.js
Section titled “Node.js”const res = await fetch("https://app.benchkey.com/api/v1/locations", { method: "POST", headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa", "Content-Type": "application/json", }, body: JSON.stringify({ name: "North Austin Branch", address: { line1: "9901 Burnet Rd", city: "Austin", state: "TX", postal_code: "78758", country: "US", }, phone: "512-555-0199", timezone: "America/Chicago", currency: "USD", }),});const location = await res.json(); // HTTP 201Response
Section titled “Response”Returns the created location object with HTTP 201.
Update a location
Section titled “Update a location”PATCH /api/v1/locations/:idUpdates one or more fields on an existing location. Only the fields included in the request body are changed.
Scope required: locations.write
Request body
Section titled “Request body”At least one writable field must be provided:
| Field | Description |
|---|---|
name | New display name |
address | New address object (line1, line2, city, state, postal_code, country) |
phone | New phone number |
email | New contact email |
timezone | New IANA timezone identifier |
currency | New ISO 4217 currency code |
is_warehouse | Toggle warehouse designation |
is_active | Enable (true) or disable (false) the location |
sort_order | New display sort order |
curl -X PATCH "https://app.benchkey.com/api/v1/locations/3" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \ -H "Content-Type: application/json" \ -d '{ "phone": "512-555-0111", "sort_order": 1 }'Node.js
Section titled “Node.js”const res = await fetch("https://app.benchkey.com/api/v1/locations/3", { method: "PATCH", headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa", "Content-Type": "application/json", }, body: JSON.stringify({ phone: "512-555-0111", sort_order: 1 }),});const location = await res.json();Response
Section titled “Response”Returns the updated location object.
Delete a location
Section titled “Delete a location”DELETE /api/v1/locations/:idPermanently deletes a location. This will fail if the location has inventory on hand, an open register session, or if it is the only remaining location for the tenant.
Scope required: locations.write
curl -X DELETE "https://app.benchkey.com/api/v1/locations/3" \ -H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"Node.js
Section titled “Node.js”const res = await fetch("https://app.benchkey.com/api/v1/locations/3", { method: "DELETE", headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" },});const result = await res.json();// { "object": "location", "id": 3, "deleted": true }Response
Section titled “Response”{ "object": "location", "id": 3, "deleted": true}Error codes
Section titled “Error codes”| HTTP status | Code | Meaning |
|---|---|---|
400 | invalid_id | The :id is not a valid positive integer |
400 | invalid_cursor | The cursor value is malformed |
400 | invalid_param | The is_active filter is not true or false |
400 | missing_field | Required field name is absent on create |
400 | invalid_field | A typed field has the wrong type, phone, email, timezone, currency must be strings; is_warehouse, is_active must be booleans; sort_order must be a non-negative integer; address must be an object (or null); each address subfield (line1, line2, city, state, postal_code, country) must be a string or null; unrecognized address subfield keys are rejected |
400 | no_fields | PATCH body is empty |
400 | no_writable_fields | PATCH body contains no recognized writable fields |
400 | create_failed | Internal validation rejected the create input (message includes details) |
400 | update_failed | Internal validation rejected the update (message includes details) |
400 | delete_failed | Location could not be deleted, see message for details |
404 | not_found | No location with that ID exists |
422 | billing_required | Your plan does not allow adding another location |
422 | conflict | Location has inventory or an open register session and cannot be deleted |
403 | insufficient_scope | API key lacks locations.read or locations.write |
See Errors for the full error envelope format.