Uploading documents

Documents are attached to a client, then picked up when you create a submission. There are two ways to upload — pick whichever fits your stack.

Accepted formats & size limits

Both upload paths accept the same file types:

Kind Extensions
PDF .pdf
Images .jpg .jpeg .png .bmp .heic .heif .gif .webp .tif .tiff
Office / spreadsheets .xls .xlsx .csv .doc .docx .odt .txt .rtf .ppt .pptx
Email .eml .msg
Archive .zip (we expand it)

Size limits: presigned upload (Option A) — 1 GB per file; direct multipart upload (Option B) — 10 MB per request. Anything over a path's limit is rejected (a too-large multipart request returns 413 pointing you to the presigned flow). There's no fixed cap on the number of documents per client or per prepare call.

You upload the bytes directly to storage; they never pass through the API, so large or many files don't tie up your request or ours. Three steps:

  1. PreparePOST /v1/clients/{id}/documents/prepare

    { "documents": [
        { "filename": "W2.pdf", "content_type": "application/pdf" },
        { "filename": "1099.pdf", "content_type": "application/pdf" }
    ] }
    

    Returns an upload_targets[] array, each with a document_id, an upload_url, and a set of fields.

    Optional integrity check (content_md5). Add a base64-encoded 128-bit MD5 of a file's bytes to its prepare entry and we pin it into the upload target, so storage rejects the upload if the bytes don't match — catching truncated or corrupted transfers. The returned fields then include a Content-MD5 you must submit with the upload.

    { "documents": [ { "filename": "W2.pdf", "content_type": "application/pdf", "content_md5": "XUFAKrxLKna5cZ2REBfFkg==" } ] }
    

    It's optional and per-document — omit it to skip the check. A malformed digest is rejected with 400 invalid_content_md5.

  2. Upload each file to its upload_url as multipart/form-data — include every key/value in fields, then the file part last. A success is HTTP 204.

    Upload URL lifetime & CORS. Each upload_url is valid for up to 7 days; after that, request a fresh prepare. The presigned POST is designed for server-side upload — uploading directly from a browser needs cross-origin (CORS) permission on the storage bucket, which isn't enabled by default. Contact us if you need browser-direct upload.

  3. FinalizePOST /v1/clients/{id}/documents/finalize with the document_ids you uploaded:

    { "document_ids": [456, 457] }
    

    Finalize verifies the objects landed and marks them ready. Anything that didn't upload comes back in failed_document_ids; just retry those. (The older failed_document_indices is still returned but is deprecated.)

A document isn't attached to a submission until it's finalized. If you try to create a submission while a prepared document is still unfinalized, the request is rejected with 409 documents_still_uploading — finalize (or retry) all prepared documents first. An abandoned upload (a prepare that never completed) won't block the client forever: once it's about 1 hour old, the next time you create a submission it's automatically swept aside. You don't have to wait, though — list the client's documents, find the one still in state uploading, and DELETE it to clear the block immediately. (Finalizing a never-uploaded document does not clear it — delete it.)

Option B — Direct multipart upload

For thin clients that can't do the presigned dance, send the files straight to us in one request. The bytes stream through our API, so this path is capped at 10 MB per request — meant for a handful of small documents. For larger or bulk uploads, use the presigned exchange (Option A); an over-limit request is rejected with 413 and a pointer to /prepare.

curl -s -H "Authorization: Bearer $MAGNETIC_API_KEY" \
  -F "[email protected]" -F "[email protected]" \
  $BASE/v1/clients/123/documents

Returns the created documents (each with its id) and — like prepare/finalize — a failed_document_ids and a deprecated failed_document_indices. No finalize step is needed. On this path a failed file never got created, so it has no id: failed_document_ids is always empty here and the failed entries appear in failed_document_indices (or as fewer documents than files sent). See Deprecations.

Which should I use?

Presigned (A) Multipart (B)
Bytes flow Browser/server → storage directly Through our API
Best for Large/many files, server-to-server Simple clients, small files (≤ 10 MB/request)
Round trips prepare → upload → finalize one

After uploading

Uploaded documents sit on the client until you create a submission, which picks up every finalized document not already attached to one (see which documents get attached) and starts processing. Upload everything first, then submit.

results matching ""

    No results matching ""