> ## Documentation Index
> Fetch the complete documentation index at: https://api.lawtte.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API keys and webhooks

> Create Studio API keys and receive signed call events without contacting support.

## Create an API key

Open your Studio agent’s **Settings → API keys**. Enter a descriptive name, choose
**Read call history** or **Read history and place calls**, and select an expiry of
30, 90, or 365 days. Copy the secret immediately: it is displayed only once.
Keys belong to the selected Studio, not every agent you own. Studio owners and
platform admins can manage them.

Keep keys in your backend’s environment variables or secret manager. Never put
one in browser code, a mobile app, a URL, or source control. At most 10 active keys
are allowed per Studio. Key creation requires an enabled Studio calling setup;
listing and revocation remain available when calling is paused.

### Authenticate an MCP client

Connect to `https://www.lawtte.ai/api/mcp` using the HTTP header:

```http theme={null}
Authorization: Bearer lawtte_sk_YOUR_KEY
```

Supported tools are `list_calls`, `get_call`, `get_transcript`, and `place_call`.
Read-only keys cannot place calls. Call-enabled keys include read access, and
all keys share the Studio’s existing billing and calling limits. Use
`place_call` with `dry_run: true` to validate without dialing.

These self-service keys authenticate the MCP surface. They do not establish
access to the `/api/v2` REST endpoint catalog.

### Rotate or revoke

Create a replacement key, install it in your integration, verify it works, then
revoke the old key. Revocation and expiry block subsequent requests. Lost secrets
cannot be recovered. The key list displays a masked suffix, permissions, expiry,
and last use. Existing legacy keys retain their previous permissions.

## Configure a webhook

Open **Settings → Webhooks**, enter your receiver’s public HTTPS URL, select events,
and save. Each Studio supports one endpoint, covering its Retell receptionist
and outbound calls. IPv4 is required, only port 443 is supported, and private
network destinations and redirects are rejected. LiveKit preview sessions do not
currently emit these events.

Choose any of `call_started`, `call_ended`, and `call_analyzed`. A call that fails
before connecting may have no start event. Analysis can arrive after the end event.

Copy the separate signing secret when it appears. API-key revocation does not
change webhook signing. Saving an existing endpoint creates a new signing secret;
update your receiver accordingly. Pending deliveries to the previous configuration
are cancelled. Removing the endpoint also cancels pending deliveries.

Use **Send test event** for a synthetic `webhook.test` payload. Refresh recent
deliveries to see pending, delivered, failed, or cancelled status and response codes.
A connection failure is shown separately from an HTTP response.

### Payload and signature

```json theme={null}
{
  "id": "event-uuid",
  "event": "call_analyzed",
  "created_at": "2026-10-01T12:00:00.000Z",
  "call": {
    "call_id": "provider-call-id",
    "transcript": "...",
    "call_analysis": { "call_summary": "..." }
  }
}
```

Fields in `call` depend on the event. Provider credentials and internal call
metadata are excluded. Treat transcripts and recordings as confidential data.

Lawtte sends these headers:

* `X-Lawtte-Id`: the stable event ID, also used in the payload.
* `X-Lawtte-Timestamp`: Unix seconds for the current delivery attempt.
* `X-Lawtte-Signature`: `v1=` followed by a hexadecimal HMAC-SHA256 digest of
  `id.timestamp.rawBody`, keyed by your `whsec_...` signing secret.

Verify against the exact raw request body before parsing JSON. Reject timestamps
outside a five-minute window, compare signatures in constant time, and deduplicate
by event ID. Retries use the same event ID and payload with a fresh timestamp/signature.

```ts theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyLawtte(raw: string, headers: Headers, secret: string) {
  const id = headers.get("x-lawtte-id");
  const timestamp = headers.get("x-lawtte-timestamp");
  const signature = headers.get("x-lawtte-signature");
  if (!id || !timestamp || !/^\d+$/.test(timestamp) ||
      Math.abs(Date.now() / 1000 - Number(timestamp)) > 300 ||
      !signature || !/^v1=[a-f0-9]{64}$/.test(signature)) return false;
  const expected = createHmac("sha256", secret)
    .update(`${id}.${timestamp}.${raw}`).digest();
  return timingSafeEqual(expected, Buffer.from(signature.slice(3), "hex"));
}
```

Return any 2xx response within 10 seconds and process the event asynchronously.
Failures receive up to three retries, scheduled after 1, 5, and 30 minutes.
Delivery runs every minute, so timings are approximate. Events may arrive out of
order or more than once. History keeps the most recent 30 deliveries for seven days.


## Related topics

- [Authentication](/authentication.md)
- [Quickstart](/quickstart.md)
- [Protocol reference](/mcp/server.md)
- [Universal webhook handler for all firms](/api-reference/webhooks/universal-webhook-handler-for-all-firms.md)
- [Call webhooks](/guides/call-webhooks.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.