> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.jelliu.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.jelliu.co/_mcp/server.

# Integrations

Jelliu agents act on other systems through tools. A voice or chat agent can look up a customer in your CRM, book on your calendar or run an automation while the conversation is still going. Those tools come from three places:

| Source                          | What it is                                                                                                            | How you set it up           | Which agents get it                                                                                    |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ |
| **Connected apps**              | CRMs, calendars, mailboxes and other business apps from Jelliu's app catalogue, connected with the app's own sign-in. | Dashboard, **Integrations** | Every agent in the workspace, narrowed per agent with the [agent settings](#control-access-per-agent). |
| **Hosted automation providers** | Zapier MCP, Make and Alegra's MCP: presets that need only a connection token.                                         | API or dashboard            | Attached to every agent when you create the server.                                                    |
| **Custom MCP servers**          | Any [Model Context Protocol](https://modelcontextprotocol.io) server you run.                                         | API or dashboard            | Only the agents you assign it to.                                                                      |

Information also flows the other way, from Jelliu to your systems, through [webhooks](/webhooks) and [Zapier triggers](/platform/zapier).

## How it works

```mermaid
flowchart LR
    subgraph Workspace
      A1[Agent: Sales]
      A2[Agent: Support]
    end
    CA[Connected apps<br />CRM, calendar, email] -->|every agent, unless restricted| A1
    CA --> A2
    HP[Hosted provider<br />Zapier MCP / Make / Alegra] -->|attached on create| A1
    HP --> A2
    CM[Custom MCP server] -->|POST /api/agents/id/mcp-servers| A1
    A1 -->|tool call during the conversation| CA
    A1 --> HP
    A1 --> CM
    A1 -.->|webhook events| WH[Your webhook endpoint]
```

Tools are resolved when the agent runs, on voice and on text channels alike. A tool call acts immediately with the credential of the connection, so the permissions of the connected account are the outer limit of what an agent can do there.

## Connected apps

Connect apps in the dashboard under **Integrations**. Each app is authorized with its own sign-in or API key, and the connection belongs to the workspace.

Once an app is connected, its tools are made available to every agent in the workspace. Connecting an app is the request for your agents to use it; there is no separate step per agent. To narrow that, use [per-agent access](#control-access-per-agent).

### Connection status

The Integrations screen shows each connection's state:

| Status      | Meaning                                                                                                         |
| ----------- | --------------------------------------------------------------------------------------------------------------- |
| `initiated` | The connection was started and is waiting for the person to finish authorizing it.                              |
| `active`    | Authorized. Its tools are available to agents.                                                                  |
| `failed`    | Authorization did not complete.                                                                                 |
| `expired`   | The app rejected the stored credential, for example because access was revoked on the app's side. Reconnect it. |
| `disabled`  | Turned off in Jelliu. The grant may still exist in the app.                                                     |

Reconnecting an app that is already connected, to refresh an expired grant, does not count as a new app against your plan.

### Writing interactions back to your CRM

When a connected CRM holds the contact, Jelliu writes each finished interaction (a call or a text conversation) back to it. The result is reported to your [webhooks](/webhooks) as `crm_sync.completed` or `crm_sync.failed`:

```json
{
  "event": "crm_sync.completed",
  "timestamp": "2026-09-14T15:43:11.024Z",
  "data": {
    "channel": "voice",
    "source": { "kind": "call", "callId": "5b0d2f7e-9a41-4c3e-8f0a-2c6d1e7b9a10" },
    "callId": "5b0d2f7e-9a41-4c3e-8f0a-2c6d1e7b9a10",
    "conversationId": null,
    "agentId": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
    "campaignId": "0f3c8b52-6d1a-4e2f-9b7c-4a5e6d7f8a90",
    "contactId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    "results": [
      { "provider": "hubspot", "success": true, "externalId": "18273645", "error": null }
    ]
  }
}
```

A write that fails temporarily is retried before it is reported, so you do not receive `crm_sync.failed` followed by `crm_sync.completed` for the same interaction. A write that was already made by an earlier attempt counts as a success. To build your own writeback instead, see [CRM sync with webhooks](/recipes/crm-sync-with-webhooks).

### Importing contacts from a CRM

Filling a campaign's call list from a connected CRM is done in the dashboard, from the campaign's contact import dialog. To load contacts through the API, use the campaign contacts endpoints described in [Contacts](/resources/contacts).

### Control access per agent

Four fields on [`PATCH /api/agents/{agentId}`](/resources/agents) decide what an agent may do with the workspace's connected apps and MCP servers. Apps are named by their lowercase catalogue slug, such as `hubspot` or `gmail`.

**`allowedToolkits`** `string[] | null`

Which connected apps the agent may use. `null` (the default) means **every** app the workspace connects, including apps connected later. A list means exactly those apps. An empty list `[]` means **none**: the agent gets no app tools at all. Up to 300 slugs.

---

**`toolkitAccess`** `object | null`

Read or write, per app: `{ "hubspot": "read", "gmail": "read_write" }`. An app that is not listed, or `null`, keeps `read_write`. `read` restricts the agent to reading in that app.

---

**`disabledTools`** `object | null`

Individual tools to switch off inside allowed apps, keyed by app: `{ "hubspot": ["HUBSPOT_DELETE_DEAL"] }`. `null` and `{}` both mean nothing is disabled.

---

**`mcpEnabled`** `boolean`

Whether the agent may carry MCP servers and app tools at all. Setting it to `false` also **detaches every server already attached**, in the same request. Setting it back to `true` attaches nothing; assign servers again explicitly.

---

**`cURL`**

```bash title="cURL"
curl -sS -X PATCH "https://api.jelliu.co/api/agents/7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4" \
  -H "Authorization: Bearer $JELLIU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "allowedToolkits": ["hubspot", "gmail"],
    "toolkitAccess": { "gmail": "read" },
    "disabledTools": { "hubspot": ["HUBSPOT_DELETE_DEAL"] }
  }'
```

**`Node.js`**

```javascript title="Node.js"
const agentId = '7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4';
const res = await fetch(`https://api.jelliu.co/api/agents/${agentId}`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.JELLIU_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    allowedToolkits: ['hubspot', 'gmail'],
    toolkitAccess: { gmail: 'read' },
    disabledTools: { hubspot: ['HUBSPOT_DELETE_DEAL'] },
  }),
});
console.log(res.status);
```

**`Python`**

```python title="Python"
import os
import requests

agent_id = "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4"
res = requests.patch(
    f"https://api.jelliu.co/api/agents/{agent_id}",
    headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
    json={
        "allowedToolkits": ["hubspot", "gmail"],
        "toolkitAccess": {"gmail": "read"},
        "disabledTools": {"hubspot": ["HUBSPOT_DELETE_DEAL"]},
    },
    timeout=30,
)
print(res.status_code)
```

A `write` key is enough. Slugs are not checked against the catalogue: naming an app the workspace has not connected is harmless, because an agent only ever receives apps that are actually connected. The new tool surface is applied to the agent in the background after the update returns.

Agents that talk to anonymous people, such as a public website widget, turn whatever a visitor says into tool calls made with your business's credentials. Give those agents `allowedToolkits: []` or `mcpEnabled: false` unless they genuinely need a tool, and prefer `read` access.

## Custom MCP servers

Register an MCP server once per workspace with `/api/mcp-servers`, then assign it to the agents that should use it.

| Method   | Path                                              | Required    |
| -------- | ------------------------------------------------- | ----------- |
| `GET`    | `/api/mcp-servers`                                | `read` key  |
| `GET`    | `/api/mcp-servers/providers`                      | `read` key  |
| `GET`    | `/api/mcp-servers/{mcpServerId}`                  | `read` key  |
| `POST`   | `/api/mcp-servers`                                | `write` key |
| `PATCH`  | `/api/mcp-servers/{mcpServerId}`                  | `write` key |
| `DELETE` | `/api/mcp-servers/{mcpServerId}`                  | `write` key |
| `GET`    | `/api/agents/{agentId}/mcp-servers`               | `read` key  |
| `POST`   | `/api/agents/{agentId}/mcp-servers`               | `write` key |
| `DELETE` | `/api/agents/{agentId}/mcp-servers/{mcpServerId}` | `write` key |

### The MCP server object

| Field                      | Type                                          | Description                                                                 |
| -------------------------- | --------------------------------------------- | --------------------------------------------------------------------------- |
| `id`                       | uuid                                          | Server id.                                                                  |
| `tenant_id`                | uuid                                          | Your workspace.                                                             |
| `name`                     | string                                        | Display name, up to 200 characters.                                         |
| `description`              | string or null                                | Sent to the agent as the server description. Explain when to use the tools. |
| `server_url`               | string                                        | The MCP endpoint.                                                           |
| `transport`                | `SSE`, `STREAMABLE_HTTP` or null              | How the voice engine connects. `null` leaves the engine's default.          |
| `approval_mode`            | `always_ask`, `fine_grained` or `no_approval` | Whether the agent must get approval before calling tools.                   |
| `toolOverrides`            | object                                        | Per-tool decisions in force, `auto_approved` or `requires_approval`.        |
| `provider`                 | `zapier`, `make`, `alegra` or null            | Derived from the URL's host on every read. `null` for a server you run.     |
| `agent_id`                 | uuid or null                                  | Set only on rows the platform manages for one agent.                        |
| `created_at`, `updated_at` | ISO 8601                                      | Timestamps.                                                                 |

The list endpoints (`GET /api/mcp-servers` and the per-agent list) return the same fields without `toolOverrides`. **The secret token and custom headers are never returned** by any endpoint.

### Create a server

**`name`** `string` — required

Up to 200 characters. Optional for a [hosted provider](#hosted-automation-providers), which defaults to the provider's name.

---

**`serverUrl`** `string` — required

The MCP endpoint, up to 2000 characters. It must resolve to a public address: private, internal and loopback URLs are rejected with `400 VALIDATION_FAILED`, including a hostname that only resolves to a private address when Jelliu looks it up (`La URL del servidor no está permitida: debe ser una dirección pública, no una red privada o interna.`). The same check runs when `serverUrl` changes on `PATCH`. Optional for Zapier and Alegra, whose endpoint is fixed.

---

**`secretToken`** `string`

Sent to your server as `Authorization: Bearer ...`. Up to 2000 characters. Stored encrypted.

---

**`customHeaders`** `object`

Extra headers, as name/value strings. Up to 20 headers; names up to 200 characters, values up to 2000. Stored encrypted.

---

**`transport`** `string`

`STREAMABLE_HTTP` or `SSE`. Set it to what your server actually speaks.

---

**`approvalMode`** `string` — default: always\_ask

`always_ask`, `fine_grained` or `no_approval`. Hosted providers default to `no_approval`.

---

**`toolOverrides`** `object`

Per-tool decisions, `{ "tool_name": "auto_approved" | "requires_approval" }`. Only valid with `approvalMode: "fine_grained"`. Up to 50 per request.

---

**`description`** `string`

Up to 2000 characters.

---

**`provider`** `string`

`zapier`, `make` or `alegra` to use a [hosted provider preset](#hosted-automation-providers).

---

**`cURL`**

```bash title="cURL"
curl -sS -X POST "https://api.jelliu.co/api/mcp-servers" \
  -H "Authorization: Bearer $JELLIU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Inventory",
    "description": "Stock levels and delivery dates. Use it before promising availability.",
    "serverUrl": "https://mcp.example.com/mcp",
    "secretToken": "your-server-token",
    "transport": "STREAMABLE_HTTP",
    "approvalMode": "no_approval"
  }'
```

**`Node.js`**

```javascript title="Node.js"
const res = await fetch('https://api.jelliu.co/api/mcp-servers', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JELLIU_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Inventory',
    description: 'Stock levels and delivery dates. Use it before promising availability.',
    serverUrl: 'https://mcp.example.com/mcp',
    secretToken: process.env.INVENTORY_MCP_TOKEN,
    transport: 'STREAMABLE_HTTP',
    approvalMode: 'no_approval',
  }),
});
const { data } = await res.json();
console.log(res.status, data.id);
```

**`Python`**

```python title="Python"
import os
import requests

res = requests.post(
    "https://api.jelliu.co/api/mcp-servers",
    headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
    json={
        "name": "Inventory",
        "description": "Stock levels and delivery dates. Use it before promising availability.",
        "serverUrl": "https://mcp.example.com/mcp",
        "secretToken": os.environ["INVENTORY_MCP_TOKEN"],
        "transport": "STREAMABLE_HTTP",
        "approvalMode": "no_approval",
    },
    timeout=60,
)
print(res.status_code, res.json()["data"]["id"])
```

Response `201`:

```json
{
  "data": {
    "id": "e2b7c4d1-6f3a-4b8e-9c0d-1a2b3c4d5e6f",
    "tenant_id": "9f0fafb4-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
    "agent_id": null,
    "name": "Inventory",
    "description": "Stock levels and delivery dates. Use it before promising availability.",
    "server_url": "https://mcp.example.com/mcp",
    "approval_mode": "no_approval",
    "transport": "STREAMABLE_HTTP",
    "created_at": "2026-09-14T16:00:00.000Z",
    "updated_at": "2026-09-14T16:00:00.000Z",
    "toolOverrides": {},
    "provider": null
  }
}
```

Creating a server registers it with the voice engine before anything is saved. If registration fails, nothing is created and the API answers `502` with code `MCP_SYNC_FAILED`.

When you send `toolOverrides` and some of them could not be applied, the server is still created, the response status is `207`, and a `toolOverridesSync` object (`synced`, `removed`, `failed`, `failures`) sits next to `data`. Only the decisions that were applied are stored.

**Match the transport.** A server registered with the wrong transport looks healthy and never answers a tool call. If yours only speaks streamable HTTP, send `"transport": "STREAMABLE_HTTP"`. You can fix it later with `PATCH`, which re-registers the server and keeps its id and assignments.

### Approval modes and per-tool decisions

| `approvalMode` | Behaviour                                                                            |
| -------------- | ------------------------------------------------------------------------------------ |
| `always_ask`   | The agent asks for approval before every tool call. The default for servers you run. |
| `fine_grained` | Each tool follows its entry in `toolOverrides`.                                      |
| `no_approval`  | Every tool runs without asking. The default for hosted providers.                    |

There is no per-tool off switch. `toolOverrides` accepts `auto_approved` and `requires_approval`; sending `disabled` is refused with `400`:

`The voice engine has no per-tool off switch (only auto_approved / requires_approval). To stop an agent using create_invoice, detach this MCP server from the agent instead.`

Per-tool decisions with any mode other than `fine_grained` are also refused, because they would have no effect:

`Per-tool decisions only apply when the server's approval mode is 'fine_grained'; this one is 'always_ask'. Set approvalMode to fine_grained in the same request, or drop the per-tool decisions.`

To stop an agent using a server's tools, [remove the server from the agent](#assign-servers-to-agents) or delete the server.

### Update and delete

`PATCH /api/mcp-servers/{mcpServerId}` accepts `name`, `description`, `serverUrl`, `secretToken`, `customHeaders`, `approvalMode`, `toolOverrides` and `transport`. Send at least one field, otherwise the response is `400` with `At least one field must be provided`. Changing `serverUrl` or `transport` re-registers the server with the voice engine; the id and agent assignments are kept.

`DELETE /api/mcp-servers/{mcpServerId}` first detaches the server from every agent, then deletes it, and answers `204`. If one agent cannot be detached, the delete is refused with `502 MCP_SYNC_FAILED` and the server stays in place, so no agent is left pointing at a server that no longer exists. Retry after a moment.

### Assign servers to agents

A custom server does nothing until you assign it to an agent.

**`cURL`**

```bash title="cURL"
curl -sS -X POST "https://api.jelliu.co/api/agents/7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4/mcp-servers" \
  -H "Authorization: Bearer $JELLIU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "mcpServerId": "e2b7c4d1-6f3a-4b8e-9c0d-1a2b3c4d5e6f" }'
```

**`Node.js`**

```javascript title="Node.js"
const agentId = '7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4';
const res = await fetch(`https://api.jelliu.co/api/agents/${agentId}/mcp-servers`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JELLIU_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ mcpServerId: 'e2b7c4d1-6f3a-4b8e-9c0d-1a2b3c4d5e6f' }),
});
console.log(res.status, await res.json());
```

**`Python`**

```python title="Python"
import os
import requests

agent_id = "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4"
res = requests.post(
    f"https://api.jelliu.co/api/agents/{agent_id}/mcp-servers",
    headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
    json={"mcpServerId": "e2b7c4d1-6f3a-4b8e-9c0d-1a2b3c4d5e6f"},
    timeout=60,
)
print(res.status_code, res.json())
```

Response `201`. This endpoint answers with a message rather than a `data` object:

```json
{ "message": "MCP server assigned to agent" }
```

The assignment is pushed to the voice engine in the same request. If that fails, the assignment is rolled back and the error is returned, so a `201` means the agent really has the server.

* `GET /api/agents/{agentId}/mcp-servers` lists the servers on an agent, in the list shape above. It can include Jelliu's own tool gateway (`mcp.jelliu.co`), which the platform manages.
* `DELETE /api/agents/{agentId}/mcp-servers/{mcpServerId}` removes one server from the agent and answers `204`.

## Hosted automation providers

Zapier MCP, Make and Alegra's MCP have fixed connection facts, so Jelliu offers them as presets. You supply the token; Jelliu sets the transport and approval mode, and **attaches the server to every agent** as soon as it is created.

`GET /api/mcp-servers/providers` returns the catalogue:

| `provider` | Endpoint                                                                                          | Credential                                                        | Notes                                                                                            |
| ---------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `zapier`   | Fixed: `https://mcp.zapier.com/api/v1/connect`                                                    | `secretToken`: the connection token from `https://mcp.zapier.com` | Your agents act in the apps you connected to Zapier. Each tool call runs in your Zapier account. |
| `make`     | **Required** `serverUrl`: Make hosts per region, for example `https://eu1.make.com/mcp/stateless` | `secretToken`: an MCP token from Make                             | Exposes the scenarios you published as on-demand tools.                                          |
| `alegra`   | Fixed: `https://mcp.alegra.com/mcp`                                                               | `secretToken`: `usuario:token`, sent as HTTP Basic                | Your Alegra accounting, including writes.                                                        |

All three use `STREAMABLE_HTTP`; sending another transport is refused. A token is required:

`Zapier needs the connection token from https://mcp.zapier.com`

**`cURL`**

```bash title="cURL"
curl -sS -X POST "https://api.jelliu.co/api/mcp-servers" \
  -H "Authorization: Bearer $JELLIU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "provider": "zapier", "secretToken": "'"$ZAPIER_MCP_TOKEN"'" }'
```

**`Node.js`**

```javascript title="Node.js"
const res = await fetch('https://api.jelliu.co/api/mcp-servers', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JELLIU_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ provider: 'zapier', secretToken: process.env.ZAPIER_MCP_TOKEN }),
});
const { data } = await res.json();
console.log(res.status, data.provider, data.server_url);
```

**`Python`**

```python title="Python"
import os
import requests

res = requests.post(
    "https://api.jelliu.co/api/mcp-servers",
    headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
    json={"provider": "zapier", "secretToken": os.environ["ZAPIER_MCP_TOKEN"]},
    timeout=60,
)
data = res.json()["data"]
print(res.status_code, data["provider"], data["server_url"])
```

If you paste a provider URL that carries the token (Zapier's `?token=` variant, or Make's `/u/TOKEN/` path), Jelliu moves the token out of the URL into the encrypted secret, so it is never stored or displayed as part of the address. The same provider rules apply when you register a provider URL without setting `provider`.

When you later `PATCH` a provider server, clearing its token is refused (`Zapier needs its connection token; rotate it instead of clearing it`). Send the new token instead.

## Errors

| Status | Code                   | When                                                                                                                                                                                                                          |
| ------ | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `VALIDATION_FAILED`    | Invalid body or id; a private or internal URL, or a hostname that resolves to one; a missing name, URL or provider token; `disabled` or non-`fine_grained` tool overrides; more than 50 overrides in one request.             |
| `403`  | `BILLING_ERROR`        | The agent already has as many MCP servers as the plan allows, for example `MCP servers per agent limit reached (1 on the starter plan). Upgrade your plan in Settings → Plan for more.`                                       |
| `403`  | `FORBIDDEN`            | Assigning to an agent whose tools are disabled: `Este agente tiene las herramientas MCP desactivadas; actívalas en el agente antes de asignar un servidor`. Or assigning a server the platform manages for a different agent. |
| `404`  | `AGENT_NOT_FOUND`      | The agent does not exist in your workspace.                                                                                                                                                                                   |
| `404`  | `MCP_SERVER_NOT_FOUND` | The server does not exist in your workspace, or is managed by the platform.                                                                                                                                                   |
| `409`  | `MCP_ALREADY_ASSIGNED` | `MCP server is already assigned to this agent`.                                                                                                                                                                               |
| `409`  | `AGENT_PROVISIONING`   | `Agent is still being configured — try assigning MCP servers in a few seconds`. Retry after `Retry-After`.                                                                                                                    |
| `502`  | `MCP_SYNC_FAILED`      | The voice engine rejected or did not answer a registration, update, assignment or delete.                                                                                                                                     |

See [Errors](/errors) for the envelope and validation details.

## Limits

**MCP servers per agent**, not counting Jelliu's own tool gateway:

| Plan       | Servers per agent | Connected apps per workspace |
| ---------- | ----------------- | ---------------------------- |
| Starter    | 1                 | 2                            |
| Growth     | 3                 | 5                            |
| Business   | 10                | 10                           |
| Enterprise | Unlimited         | Unlimited                    |

Re-assigning a server that is already on the agent never counts against the cap.

**Rate limits.** The MCP server and assignment endpoints use the general per-workspace limit (see [Rate limits](/rate-limits)). `PATCH /api/agents/{agentId}` shares the 10-per-minute budget for configuration changes.

The `/api/integrations` endpoints in the API reference predate the app catalogue. CRMs such as HubSpot, Salesforce or Pipedrive are no longer connected through them: creating one there saves an integration in `error` status with `last_error` set to, for example, `hubspot ya no se conecta por aquí: conéctalo desde el catálogo de aplicaciones.` Connect CRMs from **Integrations** in the dashboard.

Those endpoints never return secret material. `credentials` is always `***`; OAuth tokens and the inbound webhook secret are not included. Retrieve, create and update return `hasCredentials` and `hasWebhookSecret` instead, which say whether each is configured.

## Related

#### [Webhooks](/webhooks)

Receive `call.completed`, `crm_sync.completed` and other events.

#### [Zapier](/platform/zapier)

Start Zaps from Jelliu events.

#### [Agents](/resources/agents)

The agent object and every setting on `PATCH /api/agents/{agentId}`.

#### [MCP server](/mcp)

The opposite direction: operate Jelliu itself from an AI assistant.