Vacation Tracker Docs
REST API v2

Idempotency

Retry Vacation Tracker API v2 writes safely with the Idempotency-Key header.

Networks fail. A write can time out on your side after Vacation Tracker has already applied it. An Idempotency-Key lets you retry that write without creating a second leave, a second label, or a second anything.

The Idempotency-Key header

Send Idempotency-Key on any write request (POST, PUT, PATCH, DELETE). The header is optional, but we recommend it on every write.

curl -X POST https://api.vacationtracker.io/v2/leaves \
  -H "Authorization: Bearer vt_live_your-secret" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f1c7a52-3d0e-4b8e-9d6a-2c4f0b9e1a77" \
  -d '{ "userId": "...", "leaveTypeId": "...", "startDate": "2026-10-05", "endDate": "2026-10-09" }'

If the request fails or times out, send it again with the same header value and the same body.

Key format

  • 1 to 255 printable ASCII characters (no spaces or control characters).
  • Use a random UUID (version 4) for each logical write, for example 5f1c7a52-3d0e-4b8e-9d6a-2c4f0b9e1a77.
  • Create a new key for each new write. Reuse a key only to retry the same write.

A header that breaks these rules is rejected with 400 INVALID_IDEMPOTENCY_KEY and nothing is written.

Scope and retention

  • Per API key. Keys are stored per API key. Two different API keys can send the same Idempotency-Key value without affecting each other.
  • 24 hours. A key is kept for 24 hours after the first request that used it. After that, the same value counts as a new key and the write runs again.

What counts as the same request

Vacation Tracker stores a fingerprint of the first request that used a key. Two requests match when all of these are equal:

  • the HTTP method,
  • the endpoint and its path parameters (for example the leave ID in /v2/leaves/{id}/approve),
  • the JSON body (key order and whitespace do not matter),
  • the user in the X-On-Behalf-Of header, if you send one.

Other headers do not count.

Behaviour on retry

SituationResponse
First request with this keyThe write runs normally.
Same key, same request, first request finishedThe original response is returned again: same status code, same body. It carries an extra Idempotent-Replayed: true header. The write does not run again.
Same key, same request, first request still running202 Accepted with the operation of the first request. Poll it as described in Synchronous writes and operations. The write does not run again.
Same key, different request422 IDEMPOTENCY_KEY_REUSED. Nothing is written.

The original response is replayed as is

The replay returns exactly what the first request returned:

  • A 201 or 200 returns the same resource as it was at that time. Read the resource again if you need its current state.
  • A 202 returns the same operation. Its status in the replayed body may be out of date, so poll the operation URL for the current status.
  • A 4xx error (for example a leave rejected by a blackout period) is replayed too. Fix the request and send it with a new key.

Two cases are not stored, so a retry with the same key runs the write:

  • Requests rejected before the key is checked, for example INSUFFICIENT_SCOPE or ON_BEHALF_OF_NOT_ALLOWED.
  • Requests that failed with 500 INTERNAL_ERROR before the write was submitted.

If a write was submitted and then something unexpected happened, the key is kept and the response is a 202 with the operation, so a retry never submits the write twice.

Reused key with a different payload

{
  "error": {
    "code": "IDEMPOTENCY_KEY_REUSED",
    "name": "IdempotencyKeyReused",
    "description": "This Idempotency-Key was already used with a different request payload.",
    "resolution": "Use a new Idempotency-Key for a different request, or resend the original payload.",
    "requestId": "..."
  }
}

The status is 422. This check comes first, so you get it even while the first request is still running.

Concurrent retries

If you send the same request again while the first one is still running, the retry does not wait and does not run the write. It returns 202 Accepted right away:

{
  "operation": {
    "id": "pa_...",
    "status": "PENDING",
    "url": "/v2/operations/pa_..."
  }
}

In the rare case where the first request has not linked its operation yet, the retry returns 409 IDEMPOTENCY_REQUEST_IN_PROGRESS. Wait a few seconds and retry with the same key.

Errors

StatusCodeWhen
400INVALID_IDEMPOTENCY_KEYThe header is not 1 to 255 printable ASCII characters.
409IDEMPOTENCY_REQUEST_IN_PROGRESSA request with this key is still being processed.
422IDEMPOTENCY_KEY_REUSEDThe key was already used with a different request.

On this page