Documentation
Staff
staff.get
REST
ShippingPOST /api/v1/staff/getMCP tool
Livestaff.getExposed on: REST API · Shop MCP server · Claude connector · Dash (in-app copilot) · Zapier. Part of the Staff domain.
Operating contract
One person in full: their skills, their certifications with expiry dates, the recurring weekly availability they work, the time off they have booked, and the jobs currently on their plate.
availability is the roster they normally work — weekday plus hours. Somebody with NO recurring availability on file is treated as available by every scheduling path in the product rather than as unavailable, because 'we have not written their hours down' is not the same as 'they do not work'. Say which of the two you are looking at.
time_off is the approved and pending blocks that touch the near future. A PENDING block is a request nobody has answered, and planning against it as if it were free is how somebody comes back from a day off to a booked car.
certifications carry an expires_on. A lapsed certificate is not a technicality — services can be restricted to certified installers, so an expiry silently removes somebody from the shop's capacity. staff.certifications is the read that surfaces those before they bite.
jobs is the live queue, which is the honest answer to 'is this person free'.
Refuses when no staff profile in this shop has that id, which includes somebody who has left.
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
| Field | Type | Description |
|---|---|---|
| staff_idrequired | stringuuid | The staff profile id, as returned by staff.list or jobs.staff. |
Output
| Field | Type | Description |
|---|---|---|
| id | string | — |
| name | string | — |
| bookable | boolean | — |
| role_name | string | null | — |
| login_state | string | — |
| skills | string[] | — |
| certifications | object[] | — |
| certifications[].code | string | — |
| certifications[].name | string | — |
| certifications[].issuer | string | null | — |
| certifications[].issued_on | string | null | — |
| certifications[].expires_on | string | null | — |
| availability | object[] | — |
| availability[].weekday | number | null | — |
| availability[].start_time | string | null | — |
| availability[].end_time | string | null | — |
| time_off | object[] | — |
| time_off[].starts_at | string | null | — |
| time_off[].ends_at | string | null | — |
| time_off[].note | string | null | — |
| jobs | object[] | — |
| jobs[].id | string | — |
| jobs[].title | string | null | — |
| jobs[].stage | string | — |
| jobs[].scheduled_start | string | null | — |
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/get \
-H "Authorization: Bearer $SERVICEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"staff_id":"9b2f1c6e-4a77-4d2b-9f31-0f1c9a8e5d20"}'const res = await fetch("https://www.servicevin.com/api/v1/staff/get", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SERVICEVIN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"staff_id": "9b2f1c6e-4a77-4d2b-9f31-0f1c9a8e5d20"
}),
});
// 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/get",
headers={"Authorization": f"Bearer {os.environ['SERVICEVIN_API_KEY']}"},
json={
"staff_id": "9b2f1c6e-4a77-4d2b-9f31-0f1c9a8e5d20"
},
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.get",
"arguments": {
"staff_id": "9b2f1c6e-4a77-4d2b-9f31-0f1c9a8e5d20"
}
}
}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. |