Call Lens

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.mp3

Apply 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.

On this page