Quickstart
From an API key to a verified call.completed event.
This walks the whole loop. It assumes you have an API key with both the ingest and
webhooks:manage scopes.
1. Register a webhook endpoint
The signing secret is returned once. There is no second read, by this route or any other. Store it before you make another request.
curl -X POST https://api.calllens.io/api/v1/ingest/webhooks \
-H "Authorization: Bearer clk_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://your-app.example.com/hooks/calllens", "description": "Production"}'The response carries secret, the addresses the URL resolved to, and a disclosure about what the
payload contains. Read the webhooks overview before going to production.
2. Check your endpoint is reachable
curl -X POST https://api.calllens.io/api/v1/ingest/webhooks/{id}/test \
-H "Authorization: Bearer clk_..."This sends a real webhook.test event to your URL and reports delivered, response_code, and
duration_ms. It does not retry — a probe that retried would tell you "eventually reachable",
which is not the question you asked.
3. Upload the recording
Ask for somewhere to write. The response carries a PUT URL and a ticket.
curl -X POST https://api.calllens.io/api/v1/ingest/uploads \
-H "Authorization: Bearer clk_..." \
-H "Content-Type: application/json" \
-d '{"content_type": "audio/mpeg", "bytes": 4210233}'Send the bytes straight to that URL. They never pass through the Call Lens API.
curl -X PUT "$URL" -H "Content-Type: audio/mpeg" --data-binary @call.mp3Apply the upload.headers from the response to the PUT verbatim. The map is empty against
some storage backends and required by others, so skipping it works in development and then fails
with a signature mismatch in production.
4. Submit the call
curl -X POST https://api.calllens.io/api/v1/ingest/calls \
-H "Authorization: Bearer clk_..." \
-H "Content-Type: application/json" \
-d '{
"mode": "upload",
"upload_ticket": "eyJpdiI6...",
"started_at": "2026-08-05T09:14:00Z",
"ended_at": "2026-08-05T09:21:33Z",
"direction": "outbound",
"provider": "your-dialler",
"provider_call_id": "CALL-88213",
"idempotency_key": "CALL-88213"
}'201 means accepted. 200 means you had already submitted this call — see
idempotency. Full field reference:
uploading audio and
submitting a call.
If your key has webhooks enabled, provider_call_id and idempotency_key are required. They
are the only way to join the resulting event to a record in your own system. The requirement is
enforced per key, but delivery is decided per organization — so send both fields on every key you
ingest through if your organization has any active webhook endpoint. See
correlation identifiers.
5. Handle the event
When processing finishes, Call Lens POSTs a signed call.completed event to your endpoint.
Verify the signature before parsing the body — see
verifying signatures for working code.
Acknowledge with any 2xx. Anything else is treated as a failed delivery and retried.