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:
- checks your key's scopes and the
X-On-Behalf-Ofheader, - checks the
Idempotency-Key, if you sent one, - submits the change,
- waits until the main part of the change reports back,
- 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 limit | Response |
|---|---|
| Created | 201 Created with { "data": <resource> } |
| Updated, or an action such as approve or cancel | 200 OK with { "data": <resource> } |
| Deleted | 204 No Content. Deleting a leave or TOIL request returns 200 OK with the deleted request. |
| Failed | The 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/..."
}
}
}| Field | Description |
|---|---|
id | Operation ID, always starts with pa_. |
status | PENDING, SUCCEEDED, FAILED, or UNKNOWN. |
action | The public action, for example leaves.approve. |
createdAt | When the write was submitted (ISO 8601). |
completedAt | When the operation reached SUCCEEDED or FAILED. Absent while PENDING or UNKNOWN. |
actingAsUserId | The user the write was made as: the key owner, or the user in X-On-Behalf-Of. |
resource | The affected resource, with type, id, and url. Absent when the resource is not known up front. |
error | Only 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 fromresource.url.FAILED: the change was rejected. Seeerror.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 sameIdempotency-Keynever applies the change twice. If a result arrives later, the status still changes toSUCCEEDEDorFAILED.
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 reachedSUCCEEDED.operation.failed: the operation reachedFAILED.
Flow
Client guidance
- Never resubmit a
202without the sameIdempotency-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-Keyon writes, so a timeout on your side is safe to retry. - Treat
202as "in progress", not as an error. - Branch on
status, and onerror.codeforFAILEDoperations. - Set your HTTP client timeout above 20 seconds (30 seconds works well), so you receive the
202instead of timing out yourself.