| Pattern | When to use | Endpoint family |
|---|---|---|
| Direct multipart | Small files (creative media up to ~50 MB). | POST /media, POST /ingest/transaction_summary/from_excel, [cr_api] POST events/bulk_upload |
| Presigned URL multipart | Large files (>50 MB), CSVs, datasets. | POST /storage/multipart-upload, S3 PUT, PUT /storage/multipart-upload (complete) |
| Single-part presigned | Medium files where you want to skip multipart. | POST /storage/presigned-url, S3 PUT |
Direct multipart — creative media
Reference implementation
XMLHttpRequest is the simplest path to onProgress in a browser; modern
fetch finally supports request-side progress but adoption is uneven.
Presigned URL multipart — large files
For files > ~50 MB, do a multipart upload directly to S3 with presigned URLs.1. Initiate
X-Upload-Type: <type> (e.g. audience_csv).
Response (abbreviated):
2. Upload each part to S3
3. Complete
4. Validate (optional)
5. Abort (on failure or cancel)
Single-part presigned
For medium files (a few MB) where multipart is overkill:{ url, object_key }. PUT to url directly with the file as
the body and Content-Type: application/octet-stream. The object is then
addressable via object_key.
Bulk ingest endpoints
The/ingest/* family is for data-pipeline scope. Direct multipart for the
Excel variants:
| Endpoint | Body |
|---|---|
POST /ingest/transaction_summary | JSON { eid, from, to, tickets: [...] } |
POST /ingest/transaction_summary/from_excel?eid=... | multipart, file field |
POST /ingest/events_df / POST /ingest/sales_df / POST /ingest/campaigns_df | JSON DataFrame payload |
POST /ingest/events / POST /ingest/sales / POST /ingest/campaigns | JSON array |
[cr_api] POST events/bulk_upload?event_type=... | multipart, file field |
CSV downloads
For per-package customer list downloads:Common mistakes
Forgetting the missing /v3 on /media
Forgetting the missing /v3 on /media
POST /media is on the API host without the version prefix. The
reference webapp builds a separate axios instance. Replicate or you
get a 404.Not setting Content-Type on the S3 PUT
Not setting Content-Type on the S3 PUT
Presigned PUTs to S3 require
Content-Type: application/octet-stream
by default (unless the presigned URL was created with a specific
content type). Mismatching the content type at PUT time produces a
signature mismatch.Sending the auth header to the S3 PUT
Sending the auth header to the S3 PUT
The S3 PUT URL is presigned. Do not add your Bearer token or the
partner header — they cause a signature mismatch. Hit the URL with
only
Content-Type and the body.Single-line ETag stored with quotes
Single-line ETag stored with quotes
S3 returns
ETag: "abcdef..." — strip the quotes before sending to
the complete endpoint. The reference uses
resp.headers.get("ETag").replace(/"/g, "").Not aborting on user-cancel for large uploads
Not aborting on user-cancel for large uploads
Multipart uploads can run for minutes. Wire the cancel button to
DELETE /storage/multipart-upload to free the S3 space and stop the
parts in flight.