Uploads
Upload a file to Intuizi without owning any cloud storage. The upload flow is a three-step primitive shared by POI submissions and Cohorts:
- Reserve -
POST /api/v2/uploads/createreturns a presignedPUTURL and an opaqueupload_reference. - Upload -
PUTthe file bytes to the URL before it expires. No Intuizi authentication on this request - the signature in the URL is the credential. - Create - pass
upload_referenceto the create endpoint that matches the reserved purpose: Create Submission by Upload forpoi_submission, or Create Cohort forcohort.
The presigned URL is a one-key, short-lived write credential: it can write
exactly one object, cannot read or list anything, and expires 15 minutes after
it is issued. The upload_reference is one-shot - once a create has used
it, it cannot be used again. A cohort create refused by the build budget or the
data-scan limit, a file over the size cap, and a POI file that fails validation
all use it up. A cohort create that fails with a generic error leaves it
usable until expires_at, so send the create again. So does a create rejected
with No file has been uploaded for this reference yet., which has two
causes. If the PUT had not finished, finish it and send the create again. If
a poi_submission file has a header row but no data rows, the create fails
the same way every time, so add the rows and upload the file again for a new
reference. A reference must also be claimed before expires_at: a create
after that is rejected even if the PUT succeeded. An uploaded file that is
never claimed is removed automatically within 24 hours.
Create an Upload
POST /api/v2/uploads/create
Reserves an upload slot and returns the presigned PUT URL plus the
upload_reference the create endpoints accept.
Auth: bearer token + Accept: application/json + Content-Type: application/json. Rate limit: write bucket (30 requests/min).
Body
| Field | Type | Required | Description |
|---|---|---|---|
purpose | string | Yes | What the upload is for: poi_submission or cohort. Determines where the file is stored and its size cap. |
filename | string | No | The original filename, used to name the stored object. A poi_submission filename must end in .csv or .txt. For a cohort upload the name also decides how the file is imported: a name ending .csv, .gz, or .parquet (case-sensitive) imports as one file, and any other name as a folder. Left out, the object is named upload.csv. |
content_length | integer | Yes | The exact byte size of the file you will PUT. Must not exceed the purpose’s cap - 50 MB for poi_submission, 1 GB for cohort (the cap is echoed back as max_content_length). |
content_type | string | No | The MIME type your PUT will send. Defaults to text/csv. |
curl -X POST "https://console.intuizi.com/api/v2/uploads/create" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"purpose": "cohort",
"filename": "devices.csv",
"content_length": 1048576
}'
# Then PUT the file to the returned upload_url with the returned headers:
curl -X PUT "<UPLOAD_URL>" \
-H "Content-Type: text/csv" \
--data-binary @devices.csvResponse
{
"status": "success",
"code": 201,
"message": "Resource created successfully.",
"data": [
{
"upload_reference": "upl_01k0p3v9example",
"upload_url": "https://s3.us-west-2.amazonaws.com/...?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=...",
"method": "PUT",
"headers": { "Content-Type": "text/csv" },
"expires_at": "2026-07-21 14:35:00",
"max_content_length": 1073741824
}
]
}upload_referenceis the only value you pass onwards - the create endpoints never take the URL.upload_urlis valid untilexpires_at, and a create has to claim the reference before then too. If it expires before thePUTcompletes or before the create, reserve a new upload. References are cheap.- A file larger than the reserved cap is rejected when the reference is
claimed, so the size gate holds even if the
PUTsucceeded. - Reserve and upload again if the file changes - a reference always points at
exactly the bytes that were
PUTfor it.
The Idempotency-Key header is not used on this endpoint: reserving a slot
creates no resource, and an unused reservation simply expires.