# Run Lelemon for your customers

This guide is for platforms such as a POS, a CRM or a booking app. Your organization is the platform. Each of your customers is a subaccount: a full organization with its own agents, pipelines, cases, numbers and keys, isolated from your other subaccounts. Your platform pays for all of them.


**You will:** Create a subaccount per customer, act on it with your own key, connect its WhatsApp number and receive its events.

**You need:**
- A key with subaccounts:write
- Your own customer id to use as externalRef

**A platform and its subaccounts**

One key, many customers. The Lelemon-Subaccount header picks the organization a call acts on, and a shared tool tells your backend which customer is asking.

1. When your customer signs up, create the subaccount with your own id as externalRef and an Idempotency-Key.
2. Lelemon answers with the sub_ id. A second subaccount with the same externalRef is a 409 external_ref_conflict.
3. Any /v2 route acts on the subaccount when you add the header. The key does not change, only the organization.
4. A subaccount is a full organization, isolated from your other subaccounts. One that is not yours answers 404.
5. You create a tool once in your organization and share it. Each subaccount grants it to its own agents.
6. When an agent of a subaccount uses the shared tool, your backend receives a call signed with your secret and Lelemon-Subaccount-Id, so it knows which customer is asking.
7. One webhook endpoint with scope customers receives the events of all subaccounts. Each carries customerId, the sub_ id it belongs to.

## 1. Create the subaccount when your customer signs up

Send your own id as `externalRef` so you never need a lookup table. `fromTemplates` copies draft agents and pipelines: agent ids come from `GET /v2/agent-templates` (Lelemon's templates and the ones you saved from the dashboard; leave `agents` out when it comes back empty) and pipeline ids from `GET /v2/pipelines`.

```bash title="cURL"
curl https://api.lelemon.cl/v2/subaccounts \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Idempotency-Key: create-sub-1042" \
  --json '{ "name": "Picá Don Lucho", "externalRef": "restaurant-1042" }'
```

```ts title="Node.js"
const sub = await lelemon.subaccounts.create(
  { name: 'Picá Don Lucho', externalRef: 'restaurant-1042' },
  { idempotencyKey: 'create-sub-1042' },
);
```


A subaccount is this object:

**Subaccount**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | enum | yes | `subaccount` |
| `id` | string | yes |  |
| `externalRef` | string | yes |  |
| `name` | string | yes |  |
| `createdAt` | string | yes |  |
| `updatedAt` | string | yes |  |
| `created` | SubaccountCreatedResources | no | Only on create: what fromTemplates made. |

Store the `sub_...` id next to your customer. If you lose it, `GET /v2/subaccounts?externalRef=restaurant-1042` finds it.

`externalRef` is unique among your subaccounts: a second subaccount with the same value answers `409 external_ref_conflict`. Use your customer's stable id, never an email, which can change.

The key you use needs `subaccounts:write`.

## 2. Act on it with your own key

Add the `Lelemon-Subaccount` header to any `/v2` route. The key does not change, only the organization it acts on.

```bash title="cURL"
curl https://api.lelemon.cl/v2/agents \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Lelemon-Subaccount: sub_..."
```

```ts title="Node.js"
const agents = await lelemon.agents.list({ subaccount: sub.id });
const scoped = lelemon.withSubaccount(sub.id);
```


A subaccount that is not yours, or was deleted, answers `404`, the same as one that never existed. `/v2/subaccounts` itself rejects the header with `400`.

## 3. Connect their WhatsApp number

Create a connection link and show `setupUrl` as a button in your product. Your customer confirms the number in Meta and never opens Lelemon.

```bash title="cURL"
curl https://api.lelemon.cl/v2/whatsapp/connections \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Lelemon-Subaccount: sub_..." \
  -H "Idempotency-Key: connect-1042" \
  --json '{ "phone": "+56912345678", "agentId": "agt_...", "connectionType": "coexistence" }'
```


Use `coexistence` when your customer keeps using the WhatsApp Business app on that number, and `dedicated` for a number only the agent uses. When the link completes you receive `phone_number.connected`. A number belongs to the subaccount that connected it, and the agent that answers on it is routed to the entry stage of one of its pipelines.

A subaccount can have several pending links at once, one per phone, so you can connect every branch in parallel. Asking again for a phone that already has a pending link answers `409 resource_in_use`. When a number is linked, Lelemon matches it to the pending link by the phone you asked for. A number that matches none of them is detached and ignored. Name each number with `PATCH /v2/whatsapp/phone-numbers/{id}` and `{ "label": "Branch Centro" }`. Business hours and time zone belong to the organization, not to a number: the route does not set them.

## 4. One webhook endpoint for every subaccount

Create an endpoint with `"scope": "customers"` to receive the events of all your subaccounts. Each event carries `customerId`, the `sub_...` id it belongs to.

```bash title="cURL"
curl https://api.lelemon.cl/v2/webhook-endpoints \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  --json '{ "url": "https://example.com/lelemon", "enabledEvents": ["*"], "scope": "customers" }'
```


An endpoint with `"scope": "self"` receives the events of the platform organization only.

## Tools inside each subaccount

Each customer can have its own agent with tools that only reach that customer's systems.

- **Your key creates them.** `POST /v2/actions` and `POST /v2/mcp-servers` with `Lelemon-Subaccount` create the tool inside the subaccount. The site of the tool must be one of the `allowedActionOrigins` of your key.
- **Its key creates them.** A subaccount key can hold `actions:write`. Give it sites with `allowedActionOrigins` when you create it. They must be sites of your own key. If you leave them out, the key gets the same sites as yours.
- **Isolation.** An action or an MCP server belongs to one subaccount. Another subaccount, and you without the header, do not see it, change it or delete it.

```bash title="cURL"
curl https://api.lelemon.cl/v2/actions \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Lelemon-Subaccount: sub_..." \
  --json '{
    "name": "find_order",
    "description": "Finds the order of the customer in the store.",
    "request": { "method": "POST", "url": "https://erp.example.com/lelemon/find-order" },
    "input": { "type": "object", "properties": { "orderNumber": { "type": "string" } } }
  }'
```


### Share a tool with all your subaccounts

Create the tool once in your organization with `"sharedWithSubaccounts": true`, or turn it on later with `PATCH /v2/actions/{id}`. Then:

- `GET /v2/agents/{id}/tools` for an agent of a subaccount lists it with `sharedByPlatform: true`, and `PUT /v2/agents/{id}/tools/{toolId}` grants it.
- The call to your system is signed with your signing secret and carries `Lelemon-Subaccount-Id`, so your backend knows which customer is asking. Verify it with `verifyRequest` of the SDK.
- A subaccount cannot read, change or delete it, and cannot create a tool with the same name. You cannot share a tool whose name a subaccount already uses.
- Turning sharing off, or deleting the action, removes it from the agents of your subaccounts.
- Only a platform can share. A subaccount that sends `sharedWithSubaccounts` answers `422`.

### Share an MCP server with all your subaccounts

An MCP server works the same way. Connect it in your organization with `"sharedWithSubaccounts": true` in `POST /v2/mcp-servers`, or turn it on later with `PATCH /v2/mcp-servers/{id}`. Then:

- `GET /v2/mcp-servers` in a subaccount lists it with `sharedByPlatform: true`, and `GET /v2/agents/{id}/tools` lists its tools with `kind: "mcp_tool"` and `sharedByPlatform: true`. `PUT /v2/agents/{id}/tools/{toolId}` grants them.
- The calls go out to your server with your credentials. When you turned on the run identity of the server, each call carries the signed context of the subaccount that made it, so your server knows which customer is asking.
- A subaccount can read it and grant its tools, but cannot change it, discover its tools again or delete it. It cannot connect a server whose name starts like a shared one, and you cannot share a server whose name starts like one a subaccount already has.
- Turning sharing off, or deleting the server, removes its tools from the agents of your subaccounts.
- Only a platform can share. A subaccount that sends `sharedWithSubaccounts` answers `422`.

## Keys inside a subaccount

`POST /v2/subaccounts/{id}/api-keys` issues a key that sees only that subaccount. Its scopes cannot exceed those of the key that created it and cannot include `subaccounts:*`. A subaccount key cannot use `Lelemon-Subaccount` or manage subaccounts. The response holds the secret, so it is never replayed from the idempotency cache.

With `actions:write`, the key also takes `allowedActionOrigins`, a subset of the sites of your own key. A site you do not have answers `403` with `param: allowedActionOrigins`, and sites without `actions:write` answer `400`.

## When a customer leaves

`DELETE /v2/subaccounts/{id}` closes the subaccount.
