Skip to main content

Async processing

Use this recipe when you want the canonical async flow: create a job, persist the receipt metadata, and finish on a verified webhook.

Start a webhook-first job

import { Conduit } from "@mappa-ai/conduit"

const conduit = new Conduit({ apiKey: process.env.CONDUIT_API_KEY! })

async function startReport(mediaId: string) {
const receipt = await conduit.reports.create({
source: { mediaId },
output: { template: "sales_playbook" },
target: { strategy: "dominant" },
webhook: { url: process.env.CONDUIT_WEBHOOK_URL! },
idempotencyKey: `report:${mediaId}:sales-playbook:v1`,
})

return {
jobId: receipt.jobId,
stage: receipt.stage,
status: receipt.status,
}
}
  • Persist jobId with your own request metadata before returning control to the caller.
  • Finish the job on a verified report.completed or report.failed webhook, not on a long polling loop.
  • Reuse the same idempotencyKey only when you are retrying the same logical create request.

Local fallback for development or controlled scripts

Use receipt helpers only when you do not have a public webhook endpoint yet.

const receipt = await conduit.reports.create({
source: { url: "https://example.com/recording.mp3" },
output: { template: "general_report" },
target: { strategy: "dominant" },
})

for await (const event of receipt.handle?.stream({ timeoutMs: 300_000 }) ?? []) {
if (event.type === "stage") console.info(event.stage, event.progress)
if (event.type === "terminal") break
}

const report = await receipt.handle?.wait({ timeoutMs: 300_000 })
console.info(report?.id)
  • handle.stream() and handle.wait() are useful for local development, tests, and small backoffice tooling.
  • Production integrations should prefer verified webhooks for completion.

Next steps