Vacation Tracker Docs
REST API v2

Synchronous writes and operations

How Vacation Tracker API v2 writes wait for their result, and how to follow writes that take longer.

Writes in Vacation Tracker are processed in the background. API v2 hides that for you in most cases: a write request waits until the main part of the change has been applied, then returns the result.

Synchronous writes

When you create, update, delete, or act on a resource, the API:

  1. checks your key's scopes and the X-On-Behalf-Of header,
  2. checks the Idempotency-Key, if you sent one,
  3. submits the change,
  4. waits until the main part of the change reports back,
  5. reads the resource back and returns it.

"Main part" means the step that decides whether the change succeeded, for example saving the leave. Follow-up work, such as calendar sync or Slack and email notifications, is not waited for.

Result within the wait limitResponse
Created201 Created with { "data": <resource> }
Updated, or an action such as approve or cancel200 OK with { "data": <resource> }
Deleted204 No Content. Deleting a leave or TOIL request returns 200 OK with the deleted request.
FailedThe error status from the error catalog, for example 422 LEAVE_BLACKOUT_PERIOD, with the { "error": ... } body

The returned resource is read right after the change is applied, so it can be slightly stale. If you need follow-up fields, read the resource again a moment later.

Accepted writes (202)

If the change has not reported back after the wait limit, the API stops waiting and returns 202 Accepted:

{
  "operation": {
    "id": "pa_3f6e2a1c-8b4d-4c1e-9a7f-5d2b0c6e8f91",
    "status": "PENDING",
    "url": "/v2/operations/pa_3f6e2a1c-8b4d-4c1e-9a7f-5d2b0c6e8f91"
  }
}

The wait limit is 20 seconds, counted from when the request arrives. You also get a 202 when the change succeeded but the resource could not be read back yet; in that case status is SUCCEEDED.

A 202 does not mean the change failed. It is still running, and it will finish on its own.

Polling an operation

curl https://api.vacationtracker.io/v2/operations/pa_3f6e2a1c-8b4d-4c1e-9a7f-5d2b0c6e8f91 \
  -H "Authorization: Bearer vt_live_your-secret"
{
  "data": {
    "id": "pa_3f6e2a1c-8b4d-4c1e-9a7f-5d2b0c6e8f91",
    "status": "SUCCEEDED",
    "action": "leaves.request",
    "createdAt": "2026-09-24T09:15:02.114Z",
    "completedAt": "2026-09-24T09:15:24.870Z",
    "actingAsUserId": "...",
    "resource": {
      "type": "leaves",
      "id": "...",
      "url": "/v2/leaves/..."
    }
  }
}
FieldDescription
idOperation ID, always starts with pa_.
statusPENDING, SUCCEEDED, FAILED, or UNKNOWN.
actionThe public action, for example leaves.approve.
createdAtWhen the write was submitted (ISO 8601).
completedAtWhen the operation reached SUCCEEDED or FAILED. Absent while PENDING or UNKNOWN.
actingAsUserIdThe user the write was made as: the key owner, or the user in X-On-Behalf-Of.
resourceThe affected resource, with type, id, and url. Absent when the resource is not known up front.
errorOnly when FAILED: code, name, description, and optional details, as in the error catalog.

Statuses

  • PENDING: the change is still running. Poll again.
  • SUCCEEDED: the main part of the change was applied. Read the resource from resource.url.
  • FAILED: the change was rejected. See error.code.
  • UNKNOWN: no result arrived within 15 minutes of the write. The change may or may not have been applied, so read the resource to check before you retry. A retry with the same Idempotency-Key never applies the change twice. If a result arrives later, the status still changes to SUCCEEDED or FAILED.

Poll every few seconds, and back off if the operation stays PENDING. Stop polling at UNKNOWN. Polling counts against your rate limit.

Who can read an operation

Only the API key that made the write can read its operation. No extra scope is needed. For any other key, and for operations older than the retention period, the API returns 404 RESOURCE_NOT_FOUND.

Retention

Operations are kept for 90 days.

Webhooks instead of polling

If you prefer push over polling, subscribe a webhook to these events:

  • operation.completed: the operation reached SUCCEEDED.
  • operation.failed: the operation reached FAILED.

Flow

Client guidance

  • Never resubmit a 202 without the same Idempotency-Key. The change is already running. A new request without the key, or with a new key, can apply it twice. If you retry, use the same key and the same body, and you get the same operation back.
  • Always send an Idempotency-Key on writes, so a timeout on your side is safe to retry.
  • Treat 202 as "in progress", not as an error.
  • Branch on status, and on error.code for FAILED operations.
  • Set your HTTP client timeout above 20 seconds (30 seconds works well), so you receive the 202 instead of timing out yourself.

On this page