developers

API and webhooks

One RPC endpoint exposes every operation in the product, authenticated by a scoped org key. Outbound webhooks are signed and retried. Reference for both.

8 min readUpdated

The shape of the API

There is no separate API surface with its own semantics. Every operation in the product is a named tool, and the API dispatches any of them:

POST /api/rpc/<tool_name>
Authorization: Bearer <integration key>
Content-Type: application/json

{ "orgId": "…", "input": { … } }

The response is a structured result — ok: true with data, or ok: false with a machine-readable error.code and a human message. The HTTP status is derived from that code; branch on the code, not the status.

The important property: this path runs the same validation, the same permission check, the same plan and seat gates, the same audit write and the same realtime broadcast as the dashboard. It is not a parallel implementation that can drift. Our own mobile app has no other backend than this.

Integration keys

Mint keys from Settings. A key belongs to the organization, not to you, so it survives your leaving. It is shown once at creation and stored only as a hash — losing it means minting a new one.

Two independent limits apply to every key:

  • Scopes — the explicit list of tools it may call. Default deny: a key with no scopes can call nothing, and an unknown tool name is rejected when the key is minted rather than silently accepted. Name the two operations your integration needs, not a wildcard.
  • Role ceiling — the maximum role it may act as, defaulting to Employee. Raise it only if the scoped tools require it.

Both apply at once. A key scoped to two read tools with an Owner ceiling is precisely bounded; an Owner-ceiling key scoped to everything is a root credential in a config file.

Keys carry a visible prefix (schd_ plus a few characters) so you can identify one in a log without holding the secret. Revoking is immediate.

Batching reads

POST /api/rpc/batch

{ "orgId": "…", "calls": [ { "tool": "…", "input": { … } }, … ] }

Up to 20 read-only calls in one round-trip, sharing a single resolved context so the role lookup happens once. This is how a mobile screen loads everything it needs at once.

Mutating tools are rejected here on purpose — batched writes muddy audit ordering and make partial failure ambiguous. Send those individually.

Outbound webhooks

Register an endpoint and subscribe it to the events you want. Six exist, and they are deliberately coarse — a subscriber wants "a shift changed", not nine verbs. The detail is in the payload.

EventFires when
shift.changedA shift is created, moved, edited or deleted.
week.publishedA week is published or unpublished.
timeoff.decidedA time-off request is raised, decided or cancelled.
swap.changedA swap moves state.
open_shift.changedAn open shift is posted, claimed, assigned or cancelled.
member.changedSomeone is invited, updated, or changes status.

Events are opt-in on our side too: an internal change has to be deliberately mapped to an event before it can ever leave. Billing, settings, audit reads and internal bookkeeping do not stream anywhere, which is the correct default when the failure mode is "we accidentally sent your payroll rates to a customer's automation".

Verifying a webhook

Every delivery carries:

X-Weekwright-Signature: t=<unix seconds>,v1=<hex hmac>

The signature is HMAC-SHA256 over <timestamp>.<raw body>, keyed with the endpoint's signing secret (shown once, prefixed whsec_).

To verify:

  1. Read t and v1 from the header.
  2. Reject if t is more than 5 minutes from now. The timestamp is inside the signed material precisely so it is a replay defence — a verifier that skips this step can be replayed forever with a valid signature.
  3. Recompute the HMAC over t + "." + rawBody using the raw body, before any JSON parsing or reserialisation.
  4. Compare in constant time.

Delivery is retried on failure with backoff, so your endpoint must be idempotent. Each delivery carries an id — deduplicate on it.

Practical notes

  • Rate limiting applies per key. Back off on 429 rather than retrying immediately.
  • Everything is audited. API calls appear in the audit log marked as having arrived through a key, so an integration's behaviour is inspectable after the fact.
  • Compliance rules still apply. A shift created through the API is checked exactly like one created by hand. There is no bypass, and asking for one is asking for a rota that breaks your own policy.
  • UUIDs are standard hyphenated form.

Common questions

Is there an OpenAPI spec?
Not published yet. Every tool's input is a validated schema, and an invalid input comes back with a precise, field-level error — which is a workable substitute while you are wiring an integration up.
Can I use the API on the free plan?
Yes, subject to the same plan gates as everything else: a tool that needs a paid plan returns upgrade_required whether a person or a key called it.
How do I connect Zapier or Make?
Both speak generic webhooks and HTTP requests, which is all this needs — subscribe an endpoint for the trigger side, and call the RPC endpoint with a scoped key for the action side.

Related

Try Weekwright free for up to 20 employees.

No credit card required.