Retry requests safely

A timeout does not tell you whether the server processed your request — retrying blindly can create the same document or folder twice. Most create operations accept an Idempotency-Key header that makes retries safe. This guide demonstrates it with two requests.

Step 1: Send a create request with an idempotency key

Generate a unique string per logical operation — a UUID works well — and send it in the Idempotency-Key header:

POST /folders
Authorization: Bearer YOUR_API_KEY
Idempotency-Key: 7f2c9a4e-51d8-4b6f-9e3a-c8d15b7f2a40
Content-Type: application/json

{
    "name": "Quarterly reports"
}
HTTP/2 201

{
    "createdAt": "2026-05-11T09:30:00.000Z",
    "createdBy": "john.smith@example.com",
    "id": "Nc5XwR8vT2kQm7ZbJd4H",
    "name": "Quarterly reports",
    "parentFolderId": null,
    "scope": "personal",
    "trashed": false,
    "webUrl": "https://sandbox.circularo.com/home/my_files?folderId=Nc5XwR8vT2kQm7ZbJd4H"
}

The first attempt behaves as if the header was not there — the folder is created normally.

Step 2: Retry the identical request

Sending the exact same request with the same key does not create a second folder — the stored response is replayed:

POST /folders
Authorization: Bearer YOUR_API_KEY
Idempotency-Key: 7f2c9a4e-51d8-4b6f-9e3a-c8d15b7f2a40
Content-Type: application/json

{
    "name": "Quarterly reports"
}
HTTP/2 201
Idempotency-Replayed: true

{
    "createdAt": "2026-05-11T09:30:00.000Z",
    "createdBy": "john.smith@example.com",
    "id": "Nc5XwR8vT2kQm7ZbJd4H",
    "name": "Quarterly reports",
    "parentFolderId": null,
    "scope": "personal",
    "trashed": false,
    "webUrl": "https://sandbox.circularo.com/home/my_files?folderId=Nc5XwR8vT2kQm7ZbJd4H"
}

The body is identical to the first response, and the Idempotency-Replayed response header tells you it was a replay. Keys are remembered for a limited time after completion; reusing a key with a different payload is rejected with 422.

Tip

Generate the key when the logical operation starts, not per HTTP attempt, and reuse it for every retry of that operation — that is the whole trick.

Complete example

A create that survives a timeout, as a Node.js script. The key is generated once, outside the retry loop, so every attempt is the same logical operation.

JavaScript
import { randomUUID } from "node:crypto";

const BASE_URL = "https://sandbox.circularo.com/api/v1/public"; // your instance's base URL
const API_KEY = "YOUR_API_KEY";

const idempotencyKey = randomUUID();
const payload = {
    name: "Quarterly reports"
};

let folder;
for (let attempt = 1; attempt <= 3; attempt++) {
    try {
        const res = await fetch(`${BASE_URL}/folders`, {
            method: "POST",
            headers: {
                "Authorization": `Bearer ${API_KEY}`,
                "Content-Type": "application/json",
                "Idempotency-Key": idempotencyKey
            },
            body: JSON.stringify(payload)
        });
        if (res.ok) {
            folder = await res.json();
            break;
        }
        // only a server error or a rate limit can succeed on retry; error handling kept minimal for brevity
        if (res.status < 500 && res.status !== 429) throw Object.assign(new Error(`Request failed with ${res.status}`), { fatal: true });
        throw new Error(`Transient error ${res.status}`);
    } catch (error) {
        if (error.fatal || attempt === 3) throw error;
        await new Promise((resolve) => setTimeout(resolve, attempt * 1000));
    }
}

console.log(`Folder ${folder.id} exists exactly once`);

Next steps

  • Decide which failures are worth retrying at all — Handle errors.

  • Retry a document create the same way — Create a document from a file.

  • Which operations accept Idempotency-Key is documented per endpoint in the API reference.