Documents

Create a document and get an upload URL

Creates the document record and returns a presigned URL for the file itself.

The bytes do not travel through this API. Take uploadUrl from the response and issue a single PUT straight to it with the file as the raw request body, no authorization header, and two headers that must match what was signed exactly: Content-Type equal to the contentType you sent here, and Content-Length equal to fileSize. A mismatch on either one is rejected by storage, not by InkSeal. The URL is valid for 900 seconds (15 minutes) from the moment this call returns; after that, create a new document rather than reusing the id.

Nothing about the document is usable until the upload is handed back: call POST /documents/{documentId}/confirm-upload once the PUT succeeds. The document stays in processing with no page count until then.

The key the server signed is recorded on the document row, so confirm-upload binds to it and never trusts a key supplied by the caller. objectKey is returned for logging and support; it is not required by any later call.

Workspace content limits are workspace-wide rather than per-credential; a breach answers 429 with the code workspace_limit_exceeded, which is a different condition from request rate limiting and is not retryable on a timer.

Accepts an Idempotency-Key: a retry with the same key replays the original response rather than signing a second upload URL.

post/documents

Request

  • Base URL: https://inkseal.net/api/v1
  • URL: https://inkseal.net/api/v1/documents
  • Auth: HTTP bearer

Headers

Idempotency-Keystring

Makes this mutation retry-safe. 1 to 255 printable ASCII characters (0x20 to 0x7e), trimmed before use. The key is scoped to the workspace and the credential that sent it. Reusing it with a different method, path, or payload answers 422 idempotency_key_reuse. A completed attempt replays its stored status and body with an Idempotent-Replay: true response header; 5xx, 429, and non-JSON answers leave the key retryable. A concurrent request holding the 90 second lease answers 409 idempotency_in_progress. Records are retained 24 hours.

Request body

fileNamestring required

The file's name. Kept as the document's display name and used to derive the download name of the normalized PDF; it never becomes part of a storage path.

contentType'application/pdf' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/msword' | 'application/rtf' | 'text/rtf' | 'application/vnd.oasis.opendocument.text' | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' | 'text/plain' | 'text/markdown' | 'text/html' | 'image/png' | 'image/jpeg' | 'image/webp' required

The content type declared at creation. This is checked against the uploaded file's leading bytes at confirmation when the format has a reliable signature, so it has to be the truth.

text/html accepts .html and .htm files up to 10 MB during conversion. The file must be self-contained and is rejected without alteration at the first forbidden construct:

  • Any <script> element, including inside SVG; any on* event attribute; or a javascript: or vbscript: URL.
  • Any <iframe>, <frame>, <frameset>, <object>, <embed>, <applet>, <base>, <form>, <link>, <meta http-equiv>, <video>, <audio>, <track>, or <portal> construct.
  • Any src, href, srcset, poster, background, xlink:href, data, formaction, or ping value unless it is empty, a bare fragment, or an allowed image data URL. Relative and scheme-relative references are external.
  • Any CSS @import or @font-face, any url() outside the allowed data-image rule, or any image-set() with an external member, in either <style> or a style attribute.
  • Any data URL except image/png, image/jpeg, image/webp, or image/gif; SVG images, fonts, media, and all other data types are rejected.

Chromium independently disables JavaScript, runs offline, and aborts non-data and non-about requests. The normalized PDF follows the same 200-page limit and downstream workflow as every other source format.

fileSizeinteger required

The file's exact size in bytes. It is signed into the upload URL, so the PUT must send a matching Content-Length.

Example request

{
  "fileName": "master-services-agreement.pdf",
  "contentType": "application/pdf",
  "fileSize": 184320
}

Response

The document record was created and an upload URL was signed.

Example response

{
  "data": {
    "documentId": "V1StGXR8_Z5jdHi6B-myT",
    "uploadUrl": "https://r2.example.com/inkseal/w/wk_8f2c/2026-08-21/6f1b8a2c-2f61-4a1e-9d0f-1c0f9f6a4b77.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=...",
    "objectKey": "w/wk_8f2c/2026-08-21/6f1b8a2c-2f61-4a1e-9d0f-1c0f9f6a4b77.pdf"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.