Skip to content

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:

  1. Reserve - POST /api/v2/uploads/create returns a presigned PUT URL and an opaque upload_reference.
  2. Upload - PUT the file bytes to the URL before it expires. No Intuizi authentication on this request - the signature in the URL is the credential.
  3. Create - pass upload_reference to the create endpoint that matches the reserved purpose: Create Submission by Upload for poi_submission, or Create Cohort for cohort.

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

FieldTypeRequiredDescription
purposestringYesWhat the upload is for: poi_submission or cohort. Determines where the file is stored and its size cap.
filenamestringNoThe 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_lengthintegerYesThe 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_typestringNoThe 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.csv

Response

{
  "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_reference is the only value you pass onwards - the create endpoints never take the URL.
  • upload_url is valid until expires_at, and a create has to claim the reference before then too. If it expires before the PUT completes 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 PUT succeeded.
  • Reserve and upload again if the file changes - a reference always points at exactly the bytes that were PUT for it.

The Idempotency-Key header is not used on this endpoint: reserving a slot creates no resource, and an unused reservation simply expires.