/uploads/capabilityScope documents:intakeReturns a short-lived browser/agent-direct R2 PUT URL. Document bytes never pass through the application server. `requestedBytes` is checked against the deployment's per-source ceiling here, before any byte is stored: above it the answer is 413 SOURCE_EXCEEDS_PROCESSING_CEILING carrying `maxBytes`, `maxPages` and a sentence you can show a customer. Send `x-tavonel-source-idempotency-key` (a sha256 hex digest of the source event) to make the document id deterministic, so a retried intake converges on one document rather than two.
curl -sS -X POST https://tavonel.com/api/v1/uploads/capability \
-H "Authorization: Bearer $TAVONEL_API_KEY" \
-H "content-type: application/json" \
-d '{ "originalFilename": "<originalFilename>", "declaredMimeType": "application/pdf", "requestedBytes": 1, "estimatedPages": 1 }'import os
import requests
body = {
"originalFilename": "<originalFilename>",
"declaredMimeType": "application/pdf",
"requestedBytes": 1,
"estimatedPages": 1
}
response = requests.request(
"POST",
"https://tavonel.com/api/v1/uploads/capability",
headers={"Authorization": "Bearer " + os.environ["TAVONEL_API_KEY"]},
json=body,
timeout=30,
)
response.raise_for_status()
print(response.json())const response = await fetch("https://tavonel.com/api/v1/uploads/capability", {
method: "POST",
headers: { authorization: `Bearer ${process.env.TAVONEL_API_KEY}`, "content-type": "application/json" },
body: JSON.stringify({ "originalFilename": "<originalFilename>", "declaredMimeType": "application/pdf", "requestedBytes": 1, "estimatedPages": 1 }),
});
if (!response.ok) throw new Error(`${response.status} ${(await response.json()).code}`);
console.log(await response.json());{
"originalFilename": "<originalFilename>",
"declaredMimeType": "application/pdf",
"requestedBytes": 1,
"estimatedPages": 1
}| Status | Response |
|---|---|
200 | A qualified direct upload capability and the immutable document id the bytes will be registered under. PUT the bytes to url with the headers in headers and nothing else; the URL is the credential and it is short-lived. |
400 | UNQUALIFIED_INPUT — A required field was missing or was not of the declared type. UNQUALIFIED_MIME — The declared MIME type is not in the capability manifest. FILENAME_MIME_MISMATCH — The extension and the declared MIME type disagree. FILE_NAME_INVALID — The filename carried a path separator or a character the store refuses. SOURCE_IDEMPOTENCY_KEY_INVALID — x-tavonel-source-idempotency-key was present and was not 64 hex characters. |
401 | AUTH_REQUIRED — No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns. API_KEY_INVALID — The bearer token was well-formed but did not match a stored key. API_KEY_EXPIRED — The key matched and its expiry has passed. API_KEY_REVOKED — The key matched and was revoked. A rotation revokes the key it replaces. |
402 | STUDIO_SUBSCRIPTION_REQUIRED — The operation needs the higher plan tier — activation, rollback and retrieval-index rebuild are the three. TRIAL_ARCHIVE_NOT_INCLUDED — ZIP upload is not included in the free evaluation. TRIAL_FILE_LIMIT_EXCEEDED — The free evaluation's file count is spent. GPU_CREDITS_REQUIRED — The workspace has no processing balance left to reserve against. |
409 | INTAKE_IDEMPOTENCY_CONFLICT — The same source idempotency key was already used for different bytes. COMPUTE_IDEMPOTENCY_CONFLICT — The processing reservation ledger already holds a different reservation under this request's idempotency key. |
413 | SOURCE_EXCEEDS_PROCESSING_CEILING — requestedBytes is above what every processor in the chain can read. The body carries maxBytes, maxPages and a limit sentence. This is the refusal the 5 MB per-source ceiling produces. TRIAL_FILE_TOO_LARGE — Above the free-evaluation per-file bound, which is lower than the deployment ceiling. |
429 | API_RATE_LIMITED — The key has used its per-minute allowance for this scope. The window is a fixed clock minute per key and scope; the allowances are on /docs/billing-and-limits. INTAKE_RATE_LIMITED — Too many upload capabilities were requested in the window. INTAKE_DAILY_QUOTA_EXCEEDED — The workspace's daily intake quota is spent. |
503 | SIGNER_NOT_CONFIGURED — The upload URL signer is not configured, so no capability can be issued. INTAKE_DISABLED — Intake is closed today. API_RATE_LIMIT_UNAVAILABLE — The allowance could not be read, so the request was refused rather than run unbounded. |