Direct API workflow
Use this path when you want raw HTTP control or you are integrating from a language without a shipped SDK yet.
If you can use an SDK, start with Quickstart first. The SDK path stays the simplest production integration. This page is the first-class non-SDK path.
What this path covers​
- Authenticate with an API key.
- Upload media and wait for it to finish processing.
- Create a report job.
- Complete async work with webhooks or job polling.
- Fetch the finished report.
- Migrate off removed
/v1/analyze/*routes.
Base URL and auth​
Use Mappa-Api-Key on every request.
export CONDUIT_API_KEY="your_api_key"
curl https://api.mappa.ai/v1/health/ping \
-H "Mappa-Api-Key: $CONDUIT_API_KEY"
Workflow overview​
POST /v1/files
-> GET /v1/files/:mediaId until processingStatus === COMPLETED
-> POST /v1/reports/jobs
-> webhook delivery (recommended) or GET /v1/jobs/:jobId polling
-> GET /v1/reports/by-job/:jobId
1. Upload media​
Upload local media with multipart form data.
curl -X POST https://api.mappa.ai/v1/files \
-H "Mappa-Api-Key: $CONDUIT_API_KEY" \
-F "file=@./candidate-call.mp3" \
-F "label=candidate-call"
Important response fields:
{
"mediaId": "media_abc123",
"label": "candidate-call",
"contentType": "audio/mpeg",
"source": "MANUAL_UPLOAD",
"createdAt": "2026-03-23T12:00:00.000Z",
"createdByApiKeyId": "key_abc123",
"createdByUserId": "user_abc123",
"workspaceId": "ws_abc123"
}
Keep the returned mediaId. You need it for the report job request.
2. Wait for media processing to complete​
POST /v1/files accepts the upload immediately, but the media may still be processing. Poll the file until processingStatus becomes COMPLETED.
curl https://api.mappa.ai/v1/files/media_abc123 \
-H "Mappa-Api-Key: $CONDUIT_API_KEY"
{
"mediaId": "media_abc123",
"label": "candidate-call",
"processingStatus": "COMPLETED",
"hasReports": false,
"contentType": "audio/mpeg",
"createdAt": "2026-03-23T12:00:00.000Z",
"lastUsedAt": null,
"retention": {
"locked": false,
"daysRemaining": 180,
"expiresAt": "2026-09-19T12:00:00.000Z"
},
"source": "MANUAL_UPLOAD",
"createdByApiKeyId": "key_abc123",
"createdByUserId": "user_abc123",
"workspaceId": "ws_abc123"
}
COMPLETED: safe to create the report job.FAILED: stop and inspect the upload or media format before retrying.PENDINGorPROCESSING: keep waiting.
3. Create the report job​
Once the file is ready, create a report job with the mediaId.
curl -X POST https://api.mappa.ai/v1/reports/jobs \
-H "Mappa-Api-Key: $CONDUIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"media": { "mediaId": "media_abc123" },
"output": { "template": "sales_playbook" },
"target": { "strategy": "dominant" },
"webhook": { "url": "https://your-app.com/webhooks/conduit" },
"idempotencyKey": "report:candidate-call:v1"
}'
{
"jobId": "job_abc123",
"status": "queued",
"stage": "queued",
"estimatedWaitSec": 95
}
Notes:
webhook.urlis the recommended completion path for production.idempotencyKeyis optional but strongly recommended when your client may retry.- The raw REST request expects
media.mediaId; SDK-only conveniences likesource.urlandsource.pathare not part of this HTTP surface.
4. Complete the async work​
Recommended: webhook delivery​
If you include webhook.url, Conduit will notify your server when the job completes or fails.
Successful completion looks like this:
{
"id": "evt_abc123",
"type": "report.completed",
"createdAt": "2026-03-23T12:05:00.000Z",
"timestamp": "2026-03-23T12:05:00.000Z",
"data": {
"jobId": "job_abc123",
"reportId": "report_abc123",
"status": "succeeded"
}
}
Failed completion looks like this:
{
"id": "evt_def456",
"type": "report.failed",
"createdAt": "2026-03-23T12:05:00.000Z",
"timestamp": "2026-03-23T12:05:00.000Z",
"data": {
"jobId": "job_abc123",
"status": "failed",
"error": {
"code": "target_not_found",
"message": "Could not identify target speaker"
}
}
}
Verify webhook signatures before trusting the payload. See Webhooks for signature verification and handler examples.
For the canonical production path, recovery guidance, and launch checklist, continue with Production workflow.
Fallback: poll the job​
If you cannot expose a webhook endpoint, poll the job until it reaches a terminal state.
curl https://api.mappa.ai/v1/jobs/job_abc123 \
-H "Mappa-Api-Key: $CONDUIT_API_KEY"
Running job example:
{
"id": "job_abc123",
"type": "report.generate",
"status": "running",
"stage": "rendering",
"progress": 0.82,
"createdAt": "2026-03-23T12:01:00.000Z",
"updatedAt": "2026-03-23T12:02:10.000Z"
}
Terminal success example:
{
"id": "job_abc123",
"type": "report.generate",
"status": "succeeded",
"stage": "finalizing",
"reportId": "report_abc123",
"createdAt": "2026-03-23T12:01:00.000Z",
"updatedAt": "2026-03-23T12:02:30.000Z"
}
Terminal failure example:
{
"id": "job_abc123",
"type": "report.generate",
"status": "failed",
"error": {
"code": "target_not_found",
"message": "Could not identify target speaker",
"retryable": false
},
"createdAt": "2026-03-23T12:01:00.000Z",
"updatedAt": "2026-03-23T12:02:30.000Z"
}
For controlled tooling, you can also stream lifecycle updates from Jobs with GET /v1/jobs/:jobId/stream.
5. Fetch the completed report​
After webhook delivery or a terminal succeeded job status, fetch the report by job id.
curl https://api.mappa.ai/v1/reports/by-job/job_abc123 \
-H "Mappa-Api-Key: $CONDUIT_API_KEY"
{
"id": "report_abc123",
"jobId": "job_abc123",
"createdAt": "2026-03-23T12:02:30.000Z",
"media": {
"mediaId": "media_abc123",
"url": "https://storage.mappa.ai/media/media_abc123"
},
"entity": {
"id": "entity_abc123",
"label": "candidate"
},
"output": {
"template": "sales_playbook"
},
"markdown": "# Candidate report\n\n..."
}
GET /v1/reports/by-job/:jobId returns null until the job reaches terminal success. You can also fetch directly by reportId with GET /v1/reports/:reportId.
One important shape difference: the raw REST response exposes markdown at the top level. The SDKs normalize that into report.output.markdown.
Migration from removed surfaces​
Legacy /v1/analyze/* routes now return 410 Gone with code: "gone".
Example response:
{
"error": {
"code": "gone",
"message": "The /v1/analyze endpoints are gone. Migrate to POST /v1/files, then POST /v1/reports/jobs. For async workflows, use webhook on /v1/reports/jobs or poll GET /v1/jobs/:jobId."
}
}
Use this mapping for current integrations:
| Old surface | Use now | Notes |
|---|---|---|
POST /v1/analyze/file | POST /v1/files -> GET /v1/files/:mediaId -> POST /v1/reports/jobs | This is the direct REST replacement for file uploads. |
POST /v1/analyze/url | Fetch the remote file in your own backend, then POST /v1/files, or switch to an SDK that supports URL ingestion | Raw REST does not accept a remote media URL directly today. |
mode: "callback-url" and callback_url | webhook.url inside POST /v1/reports/jobs | Verify signatures before processing deliveries. |
| Old sync waiting on analyze routes | webhook.url for production, or GET /v1/jobs/:jobId plus GET /v1/reports/by-job/:jobId as fallback | The current public report flow is async. |
If you still have older examples or internal notes referencing /v1/analyze/*, replace them with this upload -> create job -> complete async work flow.
Next steps​
- Production workflow for webhook-first launch guidance and when polling is still acceptable
- API reference for endpoint-by-endpoint schemas
- Files for upload and retention details
- Reports for report payload schemas
- Jobs for polling, streaming, and cancellation
- Webhooks for signature verification and production handlers
- Error codes for
410 gone, validation, rate-limit, and billing failures