Files
Endpoints for the standalone file import flow — one file becomes one Marvin interview or document. For a walkthrough with runnable code, see Importing files to Marvin.
| Endpoint | Scope |
|---|---|
GET /api/v1/developer/import/files | file:write |
POST /api/v1/developer/import/files/initialize | file:write |
POST {upload_url} (S3, not Marvin) | — |
POST /api/v1/developer/import/files/{wav_key}/complete | file:write |
GET /api/v1/developer/import/files/{wav_key}/status | file:write |
You'll also need GET /import/projects (project:read) to resolve a project_id. Marvin endpoints here are rate limited to 20 requests/minute per API key.
POST /api/v1/developer/import/files/initialize #
Creates a wav record and returns a wav_key plus a presigned S3 upload URL.
curl -s -X POST "$BASE_URL/api/v1/developer/import/files/initialize" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"project_id": 42,
"file_name": "customer-interview.mp4"
}' | python3 -m json.tool
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
project_id | integer | Yes | ID from the list projects response |
file_name | string | Yes | Original filename including extension (max 500 chars). Extension determines media type and content type. |
Response (201):
{
"wav_key": "a1b2c3d4-...",
"upload_url": "https://s3.amazonaws.com/bucket/",
"upload_fields": {
"key": "developer-uploads/10/a1b2c3d4-.../media.mp4",
"Content-Type": "video/mp4",
"x-amz-credential": "...",
"policy": "...",
"x-amz-signature": "..."
},
"links": {
"complete": "/api/v1/developer/import/files/a1b2c3d4-.../complete",
"status": "/api/v1/developer/import/files/a1b2c3d4-.../status"
}
}
Unsupported extensions return 400. See Supported file types.
POST {upload_url} (S3) #
Upload directly to S3 via the presigned POST from initialize. All upload_fields must be sent as form fields before the file.
curl -X POST "https://s3.amazonaws.com/bucket/" \
-F "key=developer-uploads/10/a1b2c3d4-.../media.mp4" \
-F "Content-Type=video/mp4" \
-F "x-amz-credential=..." \
-F "policy=..." \
-F "x-amz-signature=..." \
-F "file=@customer-interview.mp4"
Size limits (enforced by the presigned policy):
| Type | Max size |
|---|---|
| Audio / video | 10 GB |
| Documents / images | 500 MB |
Presigned URLs expire after 1 hour. To get a fresh URL, call initialize again — this creates a new wav_key, as there is no retry endpoint for files.
POST /api/v1/developer/import/files/{wav_key}/complete #
Marvin checks that the object exists in S3, sets the file URL on the wav, and kicks off processing (transcription for audio/video, extraction for documents, etc.).
export WAV_KEY="a1b2c3d4-..."
curl -s -X POST "$BASE_URL/api/v1/developer/import/files/$WAV_KEY/complete" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Response (200):
{
"wav_key": "a1b2c3d4-...",
"status": "processing"
}
If the file is missing from S3:
{
"error": "File not found in S3. Upload the file before calling complete."
}
Idempotency: Calling complete again when the file is already linked returns 200 with "status": "processing".
GET /api/v1/developer/import/files/{wav_key}/status #
Returns the current processing status for the uploaded file. Poll until status is completed or error.
curl -s "$BASE_URL/api/v1/developer/import/files/$WAV_KEY/status" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Response:
{
"wav_key": "a1b2c3d4-...",
"name": "customer-interview.mp4",
"status": "completed",
"file_url": "https://s3.amazonaws.com/bucket/developer-uploads/10/a1b2c3d4-.../media.mp4",
"created_at": "2026-07-14T08:30:00+00:00"
}
Status values:
| Status | Meaning |
|---|---|
pending_upload | Initialize succeeded; file not yet uploaded / not completed |
processing | File linked; transcription or document processing in progress |
completed | Ready to use in Marvin |
error | Processing failed (error_flag on the file) |
recording | (Rare for this API) Active recording session |
Typical audio/video transcription latency depends on duration and queue load.
GET /api/v1/developer/import/files #
Lists files imported through this API, scoped to projects accessible to your API key, newest first.
curl -s "$BASE_URL/api/v1/developer/import/files?limit=50&offset=0" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Response:
{
"files": [
{
"wav_key": "a1b2c3d4-...",
"name": "customer-interview.mp4",
"project_id": 42,
"status": "completed",
"created_at": "2026-07-14T08:30:00+00:00"
}
],
"count": 1,
"next": null,
"previous": null
}
This also includes surveys imported via the CSVs API — a survey is itself backed by a wav record, so it shows up here too (wav_key is the survey's underlying wav, not the upload_key/survey_key from the surveys endpoint). status uses the same values as poll for status, including pending_upload for files whose upload never completed (surveys are never pending_upload — their status reflects whether column mapping is complete).
Requires file:write scope (the same scope used by initialize/complete/status — there is no separate read-only scope for files in this API).
Pagination: results are paginated with limit/offset query params (default and max limit is 50). count is the total number of matching files; next/previous are full URLs to the adjacent page, or null when there isn't one.
Supported file types #
Extensions accepted by file_name (case-insensitive):
| Category | Extensions |
|---|---|
| Video | mp4, mov, avi, webm, mkv, m4v, wmv, mpeg |
| Audio | mp3, m4a, wav, flac, ogg, aac, aiff, aif, amr |
| Documents | pdf, ppt, pptx, doc, docx, xls, xlsx, txt |
| Images | png, jpg, jpeg, gif, bmp, tiff, webp |
Errors #
| HTTP | When |
|---|---|
400 | Invalid/missing body, unsupported extension, or file missing in S3 on complete |
403 | Missing required scope, or project creation disabled for the team |
404 | Project not accessible / not found, or unknown wav_key |
429 | Rate limit exceeded (20 req/min per API key on these endpoints) |
See Troubleshooting for fixes.