# Build on SetLuca: the developer docs

A real REST API with OpenAPI docs, scoped API keys, an MCP server for agents, and signed webhooks.

## REST API

- [Interactive API reference](https://api.setluca.com/docs): the full surface, rendered from the live OpenAPI spec.
- [openapi.json](https://api.setluca.com/openapi.json): raw spec for codegen.

### Get an API key

1. Sign in and open **Settings -> Developer API keys**.
2. Create a key and pick its scopes. Keys are workspace-scoped and shown once.
3. Send it as `x-api-key` (or `authorization: Bearer`):

```
curl https://api.setluca.com/api/leads \
  -H "x-api-key: luca_..."
```

## What the API can do

213 operations across these scopes, summarized from the live [capabilities endpoint](https://api.setluca.com/capabilities):

- `analytics:read`
- `bookings:read`
- `bookings:write`
- `broadcasts:read`
- `broadcasts:write`
- `cadences:write`
- `call_events:read`
- `call_events:write`
- `campaigns:read`
- `campaigns:write`
- `channels:read`
- `channels:write`
- `coach:read`
- `conversations:read`
- `conversations:write`
- `insights:write`
- `integrations:read`
- `integrations:write`
- `knowledge:read`
- `knowledge:write`
- `leads:read`
- `leads:write`
- `learning:read`
- `learning:write`
- `memory:read`
- `memory:write`
- `reports:read`
- `review_queue:read`
- `review_queue:write`
- `safety:read`
- `usage:read`
- `voice:read`
- `webhooks:read`
- `webhooks:write`

### Capability and sensitivity, two more dials

Every key also sets a capability tier and a sensitivity tier. Capability runs read, draft, queue ops (send, enroll, replay), then full, and each tier is a bundle of the scopes above. Sensitivity is separate: a redacted key sees metadata and summaries with no lead message text, and a full-content key sees the verbatim text.

A key you mint for an assistant can start at read and redacted, the narrowest corner of both dials. You widen either one later, from the same settings page, once the assistant earns more trust. A key used for the local MCP server carries the same two settings.

## Versioning and deprecation

This is version 1. Every response carries `Luca-API-Version: 1`, and you can send the same header on a request to pin the major version you built against. A request naming another major version is refused with 400 `unsupported_api_version` rather than served by something that does not match.

Inside a major version we only add: new endpoints, new optional request fields, new response properties. Treat unknown response properties as forgiving. Anything that would break a correct client ships as a new major version on a new path prefix, and version 1 keeps serving.

A retiring endpoint says so on itself. `Deprecation` (RFC 9745) carries the date it became deprecated and still works; `Sunset` (RFC 8594) carries the date it stops answering. Both are HTTP dates, and the operation is marked `deprecated: true` in the spec. Alert on them.

## Rate limits

120 requests per workspace per minute, per endpoint, on a sliding window, shared by every key the workspace holds.

Pace off the response headers rather than counting locally:

- `RateLimit-Policy`: the quota in effect, as `"default";q=120;w=60`. On every response under `/api`, including a 401.
- `RateLimit`: what is left, as `"default";r=<remaining>;t=<seconds to reset>`.
- `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`: the same numbers in the older spelling most SDKs read.
- `Retry-After`: on a 429 only. Seconds to wait.

The first two follow the IETF RateLimit header fields draft. Reset and `Retry-After` count the seconds left in the current window, not a whole window.

## MCP server (for AI agents)

SetLuca ships an official MCP server: `@setluca/mcp` on npm, hosted at `https://mcp.setluca.com/mcp`, listed in the [MCP registry](https://registry.modelcontextprotocol.io) as `io.github.setluca/luca-mcp`. The guided setup lives at [/connect](/connect).

## Webhooks

Subscribe to workspace events with signed deliveries, replays, and a signature-verification guide. See the [webhooks section of the API reference](https://api.setluca.com/docs).

## What an integration can never do

The guardrails hold for the REST API and MCP alike, because every surface maps onto the same policy-gated routes. No integration can:

- Send a DM without a permission the owner granted. A draft written through the API or MCP waits in the review queue, even on a channel set to auto-send. Approving it, or sending directly, takes the queue operations capability, which a key or connection has only if the owner grants it.
- Change whether a channel auto-sends without full access. That switch takes the full capability, which a key or connection has only if the owner grants it.
- Launch a broadcast that skipped approval. The API launches only an approved broadcast and checks its safety limits again at launch.
- Reach a workspace it wasn't scoped to. A key belongs to one workspace and dies the moment it's revoked.

## Zapier & Make

Native Zapier and Make apps are in progress and not available today. They are planned to sit on the same public API and signed webhooks above. Until they ship, the REST API and webhooks cover the same ground. Questions? Email [support@setluca.com](mailto:support@setluca.com).

## FAQ

### How do I authenticate?

With a developer API key, sent as an x-api-key header. Keys are created in your SetLuca settings under Developer API keys, scoped to your workspace, and shown once. Give each key only the scopes the integration needs. Rotate or revoke one at any time and calls using it stop immediately.

### Can the API send a DM directly?

Only with permission. A draft written through the API or MCP waits in the review queue, whatever the channel's auto-send setting. Approving it, or sending a message directly, takes the queue operations capability, which a key or connection has only if the owner grants it. Over MCP, every send also refuses to run without an explicit confirm.

### How do I verify a webhook came from SetLuca?

Each delivery carries an HMAC-SHA256 signature with a v1= prefix. It's computed over the timestamp and the raw request body joined by a period, with a base64url digest. Verify against the raw bytes before parsing, and reject anything you can't match.

### What happens when my endpoint is down?

Deliveries retry on a backoff. Every attempt is listed in the delivery log, so you can inspect the response and replay it once you're back. A 410 Gone is taken as permanent: the delivery dead-letters and the subscription is disabled instead of retrying forever.

### Is there an OpenAPI spec I can generate a client from?

Yes. The spec is published and kept in sync with the routes themselves, so any generator you already use will produce a typed client. The MCP server is generated from the same source, which is why the two never drift.

### What can an agent do over MCP?

Everything the public API exposes, as tools, plus an operations resource and a planning prompt. It's the same trust boundary as the REST API: public routes only, scoped to the key you connect with. Hosted or local over stdio, the key's scopes, capability tier, and sensitivity tier decide what the agent can see and do.

### What does a scope control?

One verb on one resource. A key with leads:read can list leads and ask why one qualified; it takes leads:write to create or change them. A key gets exactly the scopes you tick, and a call outside them is refused outright instead of half-completing.

### Is there an official SDK?

No, on purpose. The OpenAPI spec is generated from the routes themselves, so a client you generate in your own language stays more current than an SDK we'd be hand-maintaining two releases behind. Any generator you already use will produce a typed client, and agents can use the MCP server instead.

### How will I know when the API changes?

Every response carries a `Luca-API-Version: 1` header to pin against. Version 1 only ever adds: anything that would break a correct client ships as a new major version, and version 1 keeps serving. A retiring endpoint sends `Deprecation` and `Sunset` headers, so running integrations notice on their own. For the rest, diff the spec or the capabilities endpoint.

### Do you have a sandbox or test workspace?

Use a webhook subscription's test delivery to see the exact payload shape before you write a handler. The event listing endpoint replays real events of a given type, and the delivery log lists every attempt so you can inspect and replay it. For anything more involved, email support@setluca.com and we'll set you up.

### Are there rate limits?

Yes. A workspace gets 120 requests a minute per endpoint on a sliding window, shared by every key it holds. Pace off the headers: `RateLimit-Policy` states the quota, `RateLimit` states what's left, and a 429 carries `Retry-After`. The budget suits normal integration traffic, not bulk export; for something heavier, tell us and we'll make the limits fit.

The SetLuca API is operated by SetLuca, LLC.

Questions? Email [support@setluca.com](mailto:support@setluca.com).