Documentation
Errors, retries and limits
The error shape, idempotency, rate limits and upload limits.
Errors
Every error is { "error": { "kind", "message", "requestId", "retryable" } }. kind is one of invalid, unauthenticated, forbidden, not_found, conflict, rate_limited, dependency, timeout. Quote requestId when you ask for help.
| Status | Usually means |
|---|---|
| 400 | A field is not valid; message says which and why |
| 401 | The key is missing, wrong, revoked or lacks the scope (one answer for all four) |
| 403 | The workspace's plan or a setting does not allow this |
| 404 | No such document, template or seat |
| 409 | The request is fine but the state is wrong (for example, voiding a finished document) |
| 429 | Over the rate limit; wait the Retry-After seconds |
| 5xx | Our side; safe to retry with the same Idempotency-Key |
Idempotency
Requests that create a document or a template (send a document, make a template from HTML, make a template from a file) need an Idempotency-Key header of 8 to 200 characters. Repeating a request with the same key and the same body returns the first answer instead of doing it again. Reusing a key with a different body is an error. Other requests, such as a reminder, a void or an embedded session, do not need one. The SDKs make a key for you and reuse it on every retry.
Rate limits
Each key has its own per-minute budget; over it you get 429 with a Retry-After header. See API limits. Sign-in, public signing, download and verification pages also have a per-address limit shared by every server of the install.
What counts as a document
A document is a sent document. A package of several documents sent together counts once. Drafts, signers, reminders, resends and test-mode documents never count.
The count is per calendar month in UTC and resets on the 1st.
- Free: 15 documents a month. At the limit, sending is blocked with a
403whose message says the plan allows 15 documents a month and to upgrade to Pro. Nothing is ever billed for going over: there are no overage bills. - Pro: no monthly document limit.
Email limits
Each workspace can send 25 emails a day on Free and 500 a day on Pro. Signing requests, reminders and completed copies sent to signers count. Alerts to the workspace owner, sign-in emails and the usage warning emails do not count and are never blocked. A self-hosted install has no limit. The day is a UTC day and the count resets at midnight UTC. When the limit is reached the send is refused with a plain message.
If you send from your own connected mailbox or your own verified domain, the From address uses your provider's sending. That option is in Settings → Email; see Email setup.
API limits
Each API key may make 600 requests per minute. Over that, the API answers 429 with a Retry-After header (in seconds). Nothing is lost if you wait and retry. Retried POST requests with the same Idempotency-Key never send twice. There are no per-call or per-API-document fees on any plan.
Fair use on Pro
Pro is unlimited for one organisation's own documents. If your use becomes far above normal (many thousands of documents a month) we will contact you first and agree a plan. We do not bill extra charges automatically.
Usage and alerts
The Billing and Reports pages show documents sent this month and emails sent today. We email the workspace owner once when 80% and once when 100% of the Free monthly document limit is used (the 12th and the 15th document), and once a day when 80% and 100% of the daily email limit is used (Free: the 20th and the 25th email; Pro: the 400th and the 500th).
GET /api/v1/usage returns the same numbers. It needs an API key with documents:read.
curl https://app.docustay.app/api/v1/usage -H "Authorization: Bearer $DOCUSTAY_KEY"
{
"plan": "free",
"period": { "start": "2026-10-01T00:00:00Z", "end": "2026-11-01T00:00:00Z" },
"documents": { "used": 4, "limit": 15 },
"emails": { "usedToday": 6, "limitPerDay": 25, "resetsAt": "2026-10-07T00:00:00Z" }
}
Pagination
Lists take limit (1 to 200) and cursor. The answer carries page.nextCursor (null on the last page).
Upload limits
Files uploaded for signing are limited to 25 MB on Free and 100 MB on Pro.
A retry loop that is safe
Always send the same Idempotency-Key on every retry of one logical request, and wait for Retry-After on a 429:
async function sendWithRetry(body) {
const key = crypto.randomUUID();
for (let attempt = 0; attempt < 5; attempt += 1) {
const r = await fetch("https://docustay.app/api/v1/documents/send", {
method: "POST", body: JSON.stringify(body),
headers: { Authorization: `Bearer ${process.env.DOCUSTAY_KEY}`, "Idempotency-Key": key, "Content-Type": "application/json" },
});
if (r.status !== 429 && r.status < 500) return r;
await new Promise((ok) => setTimeout(ok, (Number(r.headers.get("retry-after")) || 2 ** attempt) * 1000));
}
throw new Error("still failing after five tries");
}
Reading an error
{ "error": { "kind": "invalid", "message": "Parties: signer_2 is not a seat on this template.", "requestId": "req_123", "retryable": false } }
retryable: false means sending the same thing again will fail the same way: fix the request. retryable: true means try again with the same key.