Webhooks
Webhooks are the recommended production completion path for Python integrations.
Create jobs with a webhook target​
receipt = conduit.reports.create(
source={"url": "https://storage.example.com/call.wav"},
output={"template": "general_report"},
target={"strategy": "dominant"},
webhook={
"url": "https://your-app.com/webhooks/conduit",
"headers": {"x-tenant-id": "tenant_123"},
},
)
print(receipt.job_id)
Verify before parsing​
Always verify the exact raw request body before calling parse_event(...).
import os
from fastapi import FastAPI, HTTPException, Request
from conduit import Conduit, WebhookVerificationError
app = FastAPI()
conduit = Conduit(api_key=os.environ["CONDUIT_API_KEY"])
@app.post("/webhooks/conduit")
async def conduit_webhook(request: Request) -> dict[str, bool]:
raw_payload = await request.body()
headers = dict(request.headers)
try:
conduit.webhooks.verify_signature(
raw_payload,
headers,
secret=os.environ["CONDUIT_WEBHOOK_SECRET"],
)
except WebhookVerificationError as error:
raise HTTPException(status_code=401, detail=error.code) from error
event = conduit.webhooks.parse_event(raw_payload)
if event.type == "report.completed":
report = conduit.reports.get(event.data["reportId"])
print(report.output.markdown)
if event.type == "matching.completed":
matching = conduit.matching.get(event.data["matchingId"])
print(matching.output.markdown)
return {"received": True}
Delivery rules​
- Verify first, parse second.
- Pass the raw bytes or raw text body exactly as received.
- Header lookup is case-insensitive and reads
conduit-signature. - Deduplicate on
event.idbecause delivery is at least once. - Return a
2xxresponse quickly, then do heavier work asynchronously if needed.
Event types you will handle most often​
report.completedreport.failedmatching.completedmatching.failed
Local fallback when webhooks are unavailable​
report = receipt.handle.wait(timeout_ms=300_000)
print(report.id)
Use handle.wait() only when you cannot consume webhooks, such as local development or a small backoffice script.