Skip to main content

Production workflow

This is the canonical production path for Conduit integrations.

Use Quickstart for the first successful report, then use this page to ship the workflow safely.

Default architecture​

  1. Create a report job with webhook.url and an idempotencyKey.
  2. Persist the returned jobId with your request metadata.
  3. Verify the webhook signature against the raw request body.
  4. Acknowledge the webhook quickly, then process it asynchronously.
  5. Fetch the final report on report.completed or record the failure on report.failed.
create report job
-> persist jobId, idempotencyKey, request metadata
-> Conduit processes async work
-> verified webhook delivery (recommended)
-> fetch report by reportId or jobId
-> persist completed output or terminal failure

Webhook-first by default​

Webhooks are the default production completion path.

Use verified webhooks when you need reliable completion handling, low API overhead, and clear correlation between accepted jobs and finished reports.

Polling is acceptable when:

  • you are developing locally and do not have a public webhook endpoint yet
  • you are running controlled internal tooling or one-off backfills
  • you cannot expose an HTTPS webhook endpoint in the target environment

If you fall back to polling, keep it bounded and deliberate. Poll GET /v1/jobs/:jobId at a modest interval, or use GET /v1/jobs/:jobId/stream for controlled tooling. Do not build your main production path around aggressive polling loops.

Create jobs for retries you control​

Retry safety starts at job creation.

create-report.ts
import { Conduit } from "@mappa-ai/conduit"

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

const receipt = await conduit.reports.create({
source: { mediaId: "media_abc123" },
output: { template: "sales_playbook" },
target: { strategy: "dominant" },
webhook: { url: process.env.CONDUIT_WEBHOOK_URL! },
idempotencyKey: "report:user-42:candidate-call:v1",
requestId: "req_candidate_call_42",
})

console.info(receipt.jobId)
  • Reuse the same idempotencyKey only for the same logical create request.
  • Persist jobId, your own request correlation id, and the idempotency key together.
  • Treat idempotency as client discipline when you retry transient request failures.

Verify every delivery​

Never trust webhook JSON before signature verification.

Conduit signs each delivery with the conduit-signature header. Verification uses the exact raw request body, so do not run JSON body parsing before verification.

webhook-handler.ts
import { Conduit } from "@mappa-ai/conduit"

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

export async function handleConduitWebhook(req: Request): Promise<Response> {
const payload = await req.text()

await conduit.webhooks.verifySignature({
payload,
headers: Object.fromEntries(req.headers),
secret: process.env.CONDUIT_WEBHOOK_SECRET!,
})

const event = conduit.webhooks.parseEvent(payload)
queueWebhookWork(event)

return new Response("ok", { status: 200 })
}
  • Keep CONDUIT_WEBHOOK_SECRET server-side only.
  • Return success quickly after verification and hand off heavier work asynchronously.
  • Ignore or log unknown future event types safely instead of crashing the consumer.

For framework-specific examples, see Webhooks.

Handle retries and duplicates​

Conduit may retry failed webhook deliveries, so your consumer must be idempotent.

Use a durable dedupe key such as event.id, and make downstream writes safe to repeat.

process-webhook.ts
async function processWebhookEvent(event: { id: string; type: string; data: Record<string, unknown> }) {
const alreadyProcessed = await db.webhookEvents.has(event.id)
if (alreadyProcessed) return

await db.webhookEvents.record(event.id)

if (event.type === "report.completed") {
const reportId = String(event.data.reportId)
const report = await conduit.reports.get(reportId)
await db.reports.upsert(report.id, report)
return
}

if (event.type === "report.failed") {
await db.failedJobs.upsert(String(event.data.jobId), event.data)
}
}
  • Retries for request creation and retries for webhook delivery are separate concerns.
  • Reuse the same idempotency key for intentional create retries.
  • Make webhook processing safe if the same event arrives more than once.
  • Do not assume every non-success response produces the same retry behavior. Build the consumer so duplicates are harmless.

Failure and recovery behavior​

There are two different failure paths to plan for.

For the short symptom-based recovery path, use Troubleshooting and FAQ.

Job failed​

If processing reaches a terminal failure, Conduit emits report.failed when a webhook is configured.

Recommended handling:

  • persist jobId, error code, and error message
  • alert your operator or workflow owner
  • decide whether a new submission is appropriate instead of assuming an automatic replay
  • surface the request id when you need support help

Delivery failed​

Webhook delivery problems are not the same as job failure. A report can complete successfully even if your endpoint misses or rejects the webhook.

Recommended recovery:

  • log failed verification and non-success responses in your webhook service
  • reconcile older accepted jobs that have no stored terminal event yet
  • fetch the current job or report state before deciding whether work is actually missing
  • use GET /v1/reports/by-job/:jobId after success or reconciliation; it returns null until a completed report exists

Launch checklist​

  • Keep CONDUIT_API_KEY and CONDUIT_WEBHOOK_SECRET server-side and rotate them on your normal secret schedule.
  • Expose an HTTPS webhook endpoint that preserves the raw request body for verification.
  • Verify conduit-signature before parsing or trusting the payload.
  • Persist jobId, idempotencyKey, and your own request correlation id for every async request.
  • Return a fast success response from the webhook handler and move heavy work off the request path.
  • Make webhook processing idempotent with durable dedupe on event.id or an equivalent key.
  • Monitor terminal failures, missing completions, and webhook verification failures.
  • Use polling only as a bounded fallback, not as the default production completion strategy.
  • Test the full flow in staging with a real public endpoint or tunnel before launch.