How it works
The shape of a Call Lens integration, before you touch an endpoint.
Call Lens transcribes and analyses sales calls. An integration is a loop with four moves, and it is the same loop every time.
- 1POST /uploads
ask where to write
- 2PUT the bytes
direct to storage
- 3POST /calls
submit with the ticket
- 4call.completed
signed webhook to you
You push; we push back
There is no polling step. Everything Call Lens produces for a call arrives on one signed
webhook, once, when processing finishes. If you find yourself writing a GET to check whether a
call is ready, the integration has gone wrong somewhere earlier.
There is a way to read analysed data back on demand — the MCP server, which AI clients connect to — but it is a separate surface with its own credential, and it is not part of this loop. Nothing in an integration should wait on it.
That has a consequence worth designing for up front: your webhook endpoint is not optional plumbing, it is the only way you receive results. Register it before you submit anything.
The audio never passes through the API
POST /uploads hands back a URL and a ticket. You send the bytes straight to that URL, and only
the ticket goes to POST /calls. Call Lens never proxies the recording.
Two things follow. Apply the upload.headers from the presign response to your PUT verbatim —
the map is empty against some storage backends and required by others, so skipping it works in
development and fails in production with a signature mismatch. And size the upload step for your
own network, not for the API's rate limit.
Accepted is not analysed
POST /calls returns 201 the moment the call is queued. It is an acknowledgement that Call Lens
has taken responsibility for the recording, not a result. The analysis arrives later, on the
webhook.
200 instead of 201 means you have submitted this call before and Call Lens returned the original
rather than making a duplicate. See idempotency.
What you are responsible for
| You | Call Lens |
|---|---|
| Storing the signing secret returned once at registration | Delivering call.completed, with retries |
| Verifying the signature before parsing a payload | Signing every delivery |
Sending provider_call_id and idempotency_key | Echoing them back so you can join the result |
| Responding 2xx quickly, then working asynchronously | Treating any non-2xx as a failed delivery |
Next: the quickstart walks the whole loop with real requests.