Documentation
Staff
staff.role_matrix
REST
ShippingPOST /api/v1/staff/role_matrixMCP tool
Livestaff.role_matrixExposed on: REST API · Shop MCP server · Claude connector · Dash (in-app copilot) · Zapier. Part of the Staff domain.
Operating contract
EVERY ROLE IN THE SHOP WITH ITS ACCESS RESOLVED — not the permission keys it was granted, but what those keys actually open, and at what level.
READ THIS BEFORE ANSWERING 'WHY CAN'T SO-AND-SO SEE X'. A grant name is not an answer; writable and read_only are. A role holding a section READ-ONLY opens every page in it and cannot submit a single form, and that is by far the most common cause of 'the button does nothing'.
THE TWO LEVELS ARE THE WHOLE POINT. writable is where the role can ACT. read_only is where it can look. Reporting them together as 'has access' is the mistake this read exists to prevent.
is_manager: true MEANS FULL ADMIN — every section, every admin-only area, no exceptions. A shop asking to 'give someone the quotes section' who already has this is asking for nothing.
read_only_login: true is the honest headline for an auditor, an insurer or an outside bookkeeper: this login changes nothing. A role with NO grants at all is not read-only — nothing is not read-only — and it reads false here.
member_count is how many people hold the role, unaccepted invites included.
data names the sensitive data classes the role can see. That is the field to check before handing anybody a login that will sit in front of customer contact details or pay rates.
CHANGING A ROLE IS NOT A CAPABILITY. Widening one grants access to every person holding it, retroactively and silently, and it is the single edit that turns a read-only login in this product into a full one.
Every role is returned, so nothing is truncated.
Who may call it
- Permission
shop.manageThe caller must hold shop.manage at the ACT level. A read-only dashboard grant on the same section is refused.- Plan
- Every planNo plan gate. Available on every Service VIN plan.
- Retries
naturalNaturally idempotent — running it twice leaves the same world as running it once. A retrying integration needs no key.- Rate class
readCounted against the read budget — the widest of the four.
Input
This capability takes no arguments.
Output
| Field | Type | Description |
|---|---|---|
| items | object[] | — |
| items[].id | string | — |
| items[].key | string | — |
| items[].name | string | — |
| items[].built_in | boolean | — |
| items[].member_count | number | — |
| items[].is_manager | boolean | — |
| items[].read_only_login | boolean | — |
| items[].permission_keys | string[] | — |
| items[].writable | string[] | — |
| items[].read_only | string[] | — |
| items[].data_classes | string[] | — |
| total | number | — |
Examples
Built from this capability's own schema — required fields and the ones carrying a default, and nothing invented. Paste one and it validates.
export SERVICEVIN_API_KEY=svk_live_…
curl -X POST https://www.servicevin.com/api/v1/staff/role_matrix \
-H "Authorization: Bearer $SERVICEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const res = await fetch("https://www.servicevin.com/api/v1/staff/role_matrix", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SERVICEVIN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
// Success and failure are both envelopes. Switch on error.code, never
// on error.message — the codes are stable, the messages are for people.
const payload = await res.json();
if (!res.ok) throw new Error(payload.error.code);
const data = payload.data;import os, requests
res = requests.post(
"https://www.servicevin.com/api/v1/staff/role_matrix",
headers={"Authorization": f"Bearer {os.environ['SERVICEVIN_API_KEY']}"},
json={},
timeout=30,
)
payload = res.json()
if not res.ok:
raise RuntimeError(payload["error"]["code"])
data = payload["data"]{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "staff.role_matrix",
"arguments": {}
}
}Refusals
The four gates run in this order on every surface, and the order is not arbitrary — see Authentication.
| Status | Code | When |
|---|---|---|
| 404 | not_found | The id is unknown, or the feature is not enabled for this account. Deliberately the same answer for both. |
| 403 | forbidden | This login does not hold shop.manage. |
| 422 | validation_error | An argument was wrong. The message names the field. |
| 429 | rate_limited | Too many read calls. Back off and retry. |
| 500 | internal_error | Something failed on our side. Nothing was changed. |