Insights
Endpoints for creating an insight from a single document. For a walkthrough with runnable code, see Importing insights into Marvin.
| Endpoint | Scope |
|---|---|
GET /api/v1/developer/import/insights | insight:write |
POST /api/v1/developer/import/insights/initialize | insight:write |
POST {upload_url} (S3, not Marvin) | — |
POST /api/v1/developer/import/insights/{insight_key}/complete | insight:write |
GET /api/v1/developer/import/insights/{insight_key}/status | insight: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/insights/initialize #
Creates an insight upload and returns a presigned S3 URL.
curl -s -X POST "$BASE_URL/api/v1/developer/import/insights/initialize" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"project_id": 42,
"filename": "q1-research-summary.pdf"
}' | python3 -m json.tool
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
project_id | integer | Yes | ID from the list projects response |
filename | string | Yes | Original filename, used to determine the document type. Extension must be one of pdf, ppt, pptx, doc, docx, xls, xlsx, txt |
should_publish is specified on complete, not here.
Response (201):
{
"insight_key": "e5f6a7b8-...",
"upload_url": "https://s3.amazonaws.com/bucket/",
"upload_fields": {
"key": "developer-uploads/10/e5f6a7b8-.../doc.pdf",
"Content-Type": "application/pdf",
"x-amz-credential": "...",
"policy": "...",
"x-amz-signature": "..."
},
"links": {
"complete": "/api/v1/developer/import/insights/e5f6a7b8-.../complete",
"status": "/api/v1/developer/import/insights/e5f6a7b8-.../status"
}
}
There is no idempotency key on this endpoint — retrying initialize creates a new insight each time.
POST {upload_url} (S3) #
Upload the document directly to S3 via the presigned POST from initialize. All upload_fields must be sent as form fields before the file. The document is capped at 500 MB.
curl -X POST "https://s3.amazonaws.com/bucket/" \
-F "key=developer-uploads/10/e5f6a7b8-.../doc.pdf" \
-F "Content-Type=application/pdf" \
-F "x-amz-credential=..." \
-F "policy=..." \
-F "x-amz-signature=..." \
-F "file=@q1-research-summary.pdf"
Presigned URLs expire after 1 hour. There is no retry endpoint for insights — call initialize again for a fresh URL, which creates a new insight.
POST /api/v1/developer/import/insights/{insight_key}/complete #
Signals Marvin that the S3 upload finished and starts document processing.
curl -s -X POST "$BASE_URL/api/v1/developer/import/insights/$INSIGHT_KEY/complete" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"should_publish": false
}' | python3 -m json.tool
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
should_publish | boolean | No | Publish the insight immediately once processing finishes (default: false, insight stays a private draft) |
The document type and S3 key are derived from the filename you gave at initialize time — you don't resend it here. This avoids a mismatch if a different filename were passed to complete than was used to compute the upload's S3 key.
Response (202):
{
"insight_key": "e5f6a7b8-...",
"status": "processing"
}
If the document hasn't finished uploading to S3 yet, this returns 400:
{
"error": "File not found in S3. Upload the file before calling complete."
}
Idempotency: Calling this endpoint again once the insight has reached completed or failed returns 200 with the current status instead of re-triggering processing. A duplicate call that races the original request while it's still processing (processing_status stays pending for the whole duration) is also caught — the row is locked for the duration of the request, so the duplicate reliably sees the in-progress document and returns 200 with "message": "Upload is already processing; see status endpoint." instead of enqueueing a second job.
GET /api/v1/developer/import/insights/{insight_key}/status #
Returns the current processing status for the insight upload. Poll until processing_status is completed or failed.
curl -s "$BASE_URL/api/v1/developer/import/insights/$INSIGHT_KEY/status" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Response:
{
"insight_key": "e5f6a7b8-...",
"name": "q1-research-summary.pdf",
"processing_status": "completed",
"insight_state": 1,
"insight_type": "pdf",
"thumbnail_image": "https://s3.amazonaws.com/bucket/insights/thumbnail/....png",
"created_at": "2026-07-10T00:00:00Z"
}
processing_status values:
| Status | Meaning |
|---|---|
pending | Document not yet processed — awaiting complete or processing is in progress |
completed | Conversion finished — insight available in Marvin |
failed | Processing failed |
insight_state values:
| Value | Meaning |
|---|---|
0 | Published — visible to the team |
1 | Private draft — visible only to the creator |
2 | Shared draft |
An insight starts as a private draft (1) at initialize time; once processing completes it becomes 0 (published) if should_publish was true, or stays 1 otherwise.
insight_type values: pdf, ppt, sheet, doc, marvin_doc, other — derived from the uploaded file's extension.
GET /api/v1/developer/import/insights #
Lists insights imported through this API, scoped to projects accessible to your API key, newest first.
curl -s "$BASE_URL/api/v1/developer/import/insights?limit=50&offset=0" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Response:
{
"insights": [
{
"insight_key": "e5f6a7b8-...",
"name": "q1-research-summary.pdf",
"project_id": 42,
"processing_status": "completed",
"created_at": "2026-07-10T00:00:00Z"
}
],
"count": 1,
"next": null,
"previous": null
}
processing_status uses the same values as poll for status. Requires insight:write scope (the same scope used by initialize/complete/status — there is no separate read-only scope for insights in this API). Rate limited to 20 requests/minute per API key. Paginated with limit/offset query params (default and max limit is 50).