REST API Access Guide

Last verified: October 11, 2026

1. What's included

PRO workspaces can issue a one-time API access key to call ShiftScheduler's built-in REST endpoints directly. The key authenticates requests to the same server-side routes that power the web application, so you always receive normalized JSON responses.

Only workspace admins can manage integration keys. The organization must have at least one paid license to generate or use a REST API key; a free trial alone is insufficient. Keys have admin-equivalent access within their workspace.

2. Generating the API access key

  1. Sign in and open Settings → API access for REST clients.
  2. Click Generate new key.
  3. Copy the one-time value that appears in the dialog. The plaintext key is never shown again.
  4. Store it securely (we recommend a secrets manager). You can revoke or regenerate the key at any time from the same screen.

Only one API access key can exist per workspace. Regenerating a key immediately invalidates the previous value.

3. Authentication

All API requests must include the key in the Authorization header using the Bearer scheme:

curl https://shiftscheduler.ai/api/persons \
  -H "Authorization: Bearer <YOUR_API_ACCESS_KEY>"

For server-to-server requests, send the bearer key without browser session cookies. A valid browser session takes precedence over the key and uses that session’s permissions. Requests with a JSON body require Content-Type: application/json. Empty-body requests do not require it.

Authentication failures return 401. A valid key without paid access returns 403. Permissions and seat limits can also return 403; conflicts return 409 and invalid input usually returns 400. Error responses contain an error string. A paid license does not bypass organization-wide seat limits.

  • The key encodes your workspace ID, so every request is scoped to your workspace automatically.
  • API keys have workspace-wide access and are intended only for server-to-server use. Never expose a key in browser or mobile-app code.
  • Requests are not currently subject to a per-key rate limit, so apply appropriate rate limiting in your integration.

Getting locations

Use the REST API access key—not an MCP key or webhook secret. The response is a JSON object whose locations property is the array of locations; it is not a bare array.

curl https://shiftscheduler.ai/api/locations \
  -H "Authorization: Bearer <YOUR_API_ACCESS_KEY>" \
  -H "Accept: application/json"
{
  "locations": [
    {
      "id": "66f01234567890abcdef1234",
      "title": "Ward A",
      "sortOrder": 1,
      "weeklyTemplate": []
    }
  ]
}

Collection reads use matching envelopes: persons, locations , or shifts. A missing, invalid, revoked, or regenerated key returns 401 with a JSON error message.

4. Supported endpoints

The following endpoints accept bearer authentication. Payloads use camelCase fields, and responses always return the normalized ShiftScheduler shape.

| Method | Endpoint | Notes |
| --- | --- | --- |
| GET, POST | `/api/persons` | List or create people. |
| GET, PUT, DELETE | `/api/persons/{id}` | Read, partially update, or delete a person and dependent records. |
| POST | `/api/persons/reorder` | Reorder with orderedIds; unavailable with alphabetical ordering. |
| POST | `/api/persons/{id}/access` | Manage access with action: invite, cancel-invite, or revoke. Invitations send email. |
| GET, POST | `/api/locations` | List or create locations. |
| GET, PUT, DELETE | `/api/locations/{id}` | Read, update, or delete a location and its shifts. |
| POST | `/api/locations/reorder` | Reorder with orderedIds; unavailable with alphabetical ordering. |
| GET, POST | `/api/shift-templates` | List or create workspace-wide templates. |
| GET, PUT, DELETE | `/api/shift-templates/{id}` | Read, update, or delete a template; references can prevent deletion (409). |
| POST | `/api/shift-templates/{id}` | Preview template update impact without saving. |
| GET, PUT, DELETE | `/api/location-day-schedules` | GET: locationIds/locationId, from, to. PUT: locationId, date, slots. DELETE: locationId and date query parameters. |
| GET, POST, DELETE | `/api/shifts` | GET: locationId, personId, date, from, to. POST: create. DELETE: JSON body with id. |
| PUT | `/api/shifts/{id}` | Partially update a shift. |
| GET, DELETE | `/api/schedule-draft` | Read the shared draft; includeChanges=true adds safe change descriptors. DELETE discards the entire draft. |
| POST | `/api/schedule-draft/publish` | Publish all pending changes atomically. |
| GET, PUT | `/api/person-preferences` | GET: personIds/personId, from, to. PUT: personId, date, preference; empty preference clears it. |
| GET, POST | `/api/timeoff/requests` | GET: status (requested by default; archived includes approved/rejected). POST: personId, date, typeId; hours come from the template. |
| PUT | `/api/timeoff/requests/{id}` | Update note/date or action: approve/reject. Template and timing are immutable. |
| GET | `/api/timeoff/allowances` | Read visible allowance summaries. |
| GET, POST | `/api/timeoff/grants` | GET requires personId. POST: personId, typeId, hours, optional note. |
| DELETE | `/api/timeoff/grants/{id}` | Void a grant; the audit entry is retained until its person is deleted. |
| GET | `/api/overview` | Overview with optional start/end dates. |
| GET, PUT | `/api/workspace/ai-settings` | Read AI rules; PUT replaces rules or performs the supported rule action. |
| PATCH | `/api/workspace/rules/reset-interpretation` | Reset a rule interpretation with ruleId. |

Collection response fields vary: persons, locations, shifts, schedules, preferences (with quotas), data (templates and time-off requests), grants (with summary), or allowances. They are not bare arrays. Creation and update responses also vary; inspect the endpoint’s documented envelope.

Only POST /api/shifts, DELETE /api/shifts, PUT /api/location-day-schedules, PUT /api/person-preferences, and POST /api/timeoff/requests accept an { items: [...] }.

Batch results preserve input order and include successCount, failureCount, mutationCount and per-item results. An accepted batch returns HTTP 200 even if some or all items fail. Check each result’s ok and status fields; do not treat HTTP 200 alone as success.

Time Off responses include createdAt, the daily request order, and approval or rejection audit references. Queue order spans pending, approved, and rejected requests for the same calendar day.

If Draft Schedule is enabled for the workspace, API shift reads and regular-shift writes use the shared working schedule. Writes do not notify members or emit shift webhooks until the draft is published. Publishing applies every pending change together; deleting the draft discards it in full. Time Off operations remain immediate, and API keys have admin-equivalent permission to publish or discard.

To review the shared draft without exposing raw shift snapshots, call GET /api/schedule-draft?includeChanges=true. The optional changes array contains the operation type, document ID, label, and localized history descriptor parameters for each pending shift, ordered by schedule date.

Filtering shifts

GET /api/shifts accepts optional locationId, personId, from , and to query parameters to refine the list. Provide a single date to fetch everything on/after or on/before that day, or pass both to limit results to a window. When no filters are provided, you receive every shift in the workspace.

5. Creating data

Write endpoints accept the same payloads the web app uses internally. The examples below demonstrate how to create each entity with typical fields.

Resource changes to people, locations or templates invalidate AI rule interpretations. Deleting people or locations cascades to dependent schedule data. Template edits may require propagation options; preview their impact first. Use a test workspace when exploring write operations.

Create a person

curl -X POST https://shiftscheduler.ai/api/persons \
  -H "Authorization: Bearer <YOUR_API_ACCESS_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Jordan Weaver",
        "invited": false
      }'

Set invited=true to trigger an email invitation. The server sanitises any membership payload before persisting.

To add someone without inviting them yet, omit invited (or set it to false) and skip the membership block—the person will be created without any access assigned.

Membership settings require invited=true and a valid email. The optional membership.role may be admin, manager , or member; it defaults to member. Supplying membership settings without requesting an invitation returns a validation error.

Responses include the normalised membership snapshot (status, invite email, and timestamps) so you can confirm what was persisted.

Create a location

curl -X POST https://shiftscheduler.ai/api/locations \
  -H "Authorization: Bearer <YOUR_API_ACCESS_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
        "title": "Ward A",
        "weeklyTemplate": []
      }'

Create a shift

curl -X POST https://shiftscheduler.ai/api/shifts \
  -H "Authorization: Bearer <YOUR_API_ACCESS_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
        "location": "LOCATION_ID",
        "person": "PERSON_ID",
        "date": "2026-10-20",
        "startTime": "09:00",
        "endTime": "17:00",
        "note": "Internal workshop prep and handoff"
      }'