Documentation
Staff
staff.pay_periods
REST
ShippingPOST /api/v1/staff/pay_periodsMCP tool
Livestaff.pay_periodsExposed on: REST API · Shop MCP server · Claude connector · Dash (in-app copilot) · Zapier. Part of the Staff domain.
Operating contract
The shop's payroll periods, newest first, with what each one totals and how it splits per person.
status is open, closed or paid. Only an OPEN period can still take lines; a closed one is frozen, and it is what a payroll run was built from.
is_earliest_open marks the oldest period still open. It is the one every late entry falls into and the only one a close may sweep — periods close in order, so a stuck earliest-open period blocks every later close, and that is usually the reason a shop's payroll board looks wrong.
late_total is money that landed in a period AFTER the work it relates to. It is not an error, but it explains why a period's total does not match the work done inside its dates.
cadence is how often the shop pays, and it is what turns dates into periods. A shop that changed cadence keeps its stored old periods rather than having history re-cut under it.
READ ONLY. Closing a period freezes what everybody is paid, and reopening one is restricted to the newest closed period even for a person.
TRUNCATION: truncated: true means the ledger scan hit its cap and the totals on this response are INCOMPLETE. Do not quote a payroll figure from a truncated read.
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
expensiveCounted against the expensive budget — a model call, a document render or a fan-out scan.
Partial answers
truncated is how many matching records are not in the response, and 0 is a real answer meaning you have all of them. There is no cursor here — narrow the filter instead.
Do not summarise from a truncated response
Input
This capability takes no arguments.
Output
| Field | Type | Description |
|---|---|---|
| cadence | string | — |
| currency | string | — |
| truncated | boolean | — |
| periods | object[] | — |
| periods[].id | string | null | — |
| periods[].label | string | — |
| periods[].status | string | — |
| periods[].is_earliest_open | boolean | — |
| periods[].is_current | boolean | — |
| periods[].total | string | — |
| periods[].line_count | number | — |
| periods[].late_total | string | — |
| periods[].late_count | number | — |
| periods[].workers | object[] | — |
| periods[].workers[].staff_id | string | — |
| periods[].workers[].name | string | — |
| periods[].workers[].total | string | — |
| periods[].workers[].line_count | 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/pay_periods \
-H "Authorization: Bearer $SERVICEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const res = await fetch("https://www.servicevin.com/api/v1/staff/pay_periods", {
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/pay_periods",
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.pay_periods",
"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 expensive calls. Back off and retry. |
| 500 | internal_error | Something failed on our side. Nothing was changed. |