Skip to main content

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​

  1. Authenticate with an API key.
  2. Upload media and wait for it to finish processing.
  3. Create a report job.
  4. Complete async work with webhooks or job polling.
  5. Fetch the finished report.
  6. 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.
  • PENDING or PROCESSING: 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.url is the recommended completion path for production.
  • idempotencyKey is optional but strongly recommended when your client may retry.
  • The raw REST request expects media.mediaId; SDK-only conveniences like source.url and source.path are not part of this HTTP surface.

4. Complete the async work​

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 surfaceUse nowNotes
POST /v1/analyze/filePOST /v1/files -> GET /v1/files/:mediaId -> POST /v1/reports/jobsThis is the direct REST replacement for file uploads.
POST /v1/analyze/urlFetch the remote file in your own backend, then POST /v1/files, or switch to an SDK that supports URL ingestionRaw REST does not accept a remote media URL directly today.
mode: "callback-url" and callback_urlwebhook.url inside POST /v1/reports/jobsVerify signatures before processing deliveries.
Old sync waiting on analyze routeswebhook.url for production, or GET /v1/jobs/:jobId plus GET /v1/reports/by-job/:jobId as fallbackThe 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