Skip to main content

Document uploads

Audience​

Integrators uploading evidence documents to a project.

What you'll accomplish​

Upload a file via the presigned flow, finalize the source, and know where to poll for extraction status.

Project documents are sources (kind: document). Uploading triggers background extraction. The complete response includes sourcePid for polling.

Requires projects:write. See Authentication.

Presigned upload​

File bytes go directly to object storage. This is the supported path for integrators and large files.

POST /projects/:projectPid/documents/upload-init
→ uploadUrl + storage key
PUT uploadUrl (no Authorization header)
POST /projects/:projectPid/documents/upload-complete
→ sourcePid

Step-by-step​

1. Init — presigned PUT URL (valid five minutes):

curl -sS -X POST 'https://api.air.thalus.ai/projects/<projectPid>/documents/upload-init' \
-H 'Authorization: Bearer <rawKey>' \
-H 'Content-Type: application/json' \
-d '{"filename":"evidence.pdf","contentType":"application/pdf"}'

2. Upload — PUT raw file bytes to uploadUrl with only Content-Type matching the init payload (e.g. application/pdf). No AIR Authorization header. Do not send x-amz-checksum-* or x-amz-sdk-checksum-algorithm headers — the presigned URL signs host (and content-type via the PutObject command) only; echoing checksum query params as headers yields 403 HeadersNotSigned.

3. Complete — start extraction:

curl -sS -X POST 'https://api.air.thalus.ai/projects/<projectPid>/documents/upload-complete' \
-H 'Authorization: Bearer <rawKey>' \
-H 'Content-Type: application/json' \
-d '{
"s3Key": "<storage key from init>",
"filename": "evidence.pdf",
"contentType": "application/pdf"
}'

Poll GET /projects/:projectPid/documents until status is no longer processing — Async jobs.

Limits and supported types​

LimitValue
Max file size50 MiB per file
Max artifacts per assessment5
Presigned URL TTL5 minutes

Supported extensions: .pdf, .docx, .pptx, .xlsx, .txt, .md, .csv, .json.

Very large files may time out during extraction even when upload succeeds.

Common failures​

SymptomLikely causeFix
403 on initMissing projects:writeAdd scope or use integrations:full
404 on initWrong projectPid or wrong domainGET /domains/&lt;domainPid&gt;/projects
PUT fails or 403 from storageWrong Content-Type, expired URL, or corrupt bodyRe-run init within five minutes; match Content-Type
PUT 403 HeadersNotSigned for checksum headersClient sent x-amz-checksum-* / SDK checksum headersOmit checksum headers; PUT body + Content-Type only
409 on completeStorage key already used or upload never finishedFresh init → PUT → complete
Source error after completeUnsupported or corrupt fileCheck lastError on document row
Stuck in processingLarge file or parser timeoutWait with backoff; check lastError; retry regenerate if needed

After upload​

ActionRoute
List documentsGET /projects/:projectPid/documents
List artifactsGET /projects/:projectPid/artifacts
Download originalGET /projects/:projectPid/documents/:sourcePid/download
Re-run extractionPOST /projects/:projectPid/documents/:sourcePid/regenerate
ArchivePOST /projects/:projectPid/documents/:sourcePid/archive

Deleting a document archives derived artifacts used as assessment evidence.