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.
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.
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-Keyis documented per endpoint in the API reference.