# Run Lelemon for your customers with subaccounts

**You will:** Provision a subaccount per customer, share your tools with them, cap their conversations and follow their events from one endpoint.

**You need:**
- A platform API key with subaccounts:write, agents:write, pipelines:write, actions:write, budgets:write, webhooks:write, usage:read and the scopes you want your customers to have
- An https stock service and an MCP server of yours (local http works in a development API)
- Node.js 20 or later

You sell a service built on Lelemon. Each of your customers gets a subaccount: its own agents, pipelines, cases, numbers and keys, isolated from the rest. You keep the tools in one place and share them with every subaccount, you fix how many conversations each one may have, and a single webhook endpoint tells you what happens in all of them.

**Who it is for.** Software platforms, agencies and franchisors that run agents for many customers. **Scopes.** Listed above. A subaccount key cannot exceed the scopes of the key that creates it.

**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.

```text title="The flow"
Your platform key
  |- actions.create(sharedWithSubaccounts)      one place for the tool
  |- mcpServers.create(sharedWithSubaccounts)   one place for the server
  |- subaccounts.create (one per customer)
  |      '- Lelemon-Subaccount: sub_...  ->  agents, pipelines, cases of that customer
  |- budgets.create(scope: subaccount)          the monthly cap, enforced
  '- webhookEndpoints.create(scope: customers)  events of every subaccount, with customerId
```

The script is in [`examples/use-cases/platform-subaccounts`](https://github.com/Lelemon-studio/lelemon-app/tree/main/apps/docs/examples/use-cases/platform-subaccounts).

## 0. Set up

```ts title="Node.js"
import { Lelemon } from '@lelemon-ai/sdk';

const lelemon = new Lelemon({ apiKey: process.env.LELEMON_API_KEY });
```

## 1. Share an action with every subaccount

Create the action once with `sharedWithSubaccounts: true`. Your endpoint is yours: a variable of Lelemon (`subaccount.id`) tells it which customer is asking, and Lelemon fills it, not the model. A **floor** is the least approval any subaccount can run the action with; a subaccount can be stricter, never looser.

```bash title="cURL"
curl https://api.lelemon.cl/v2/actions \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Idempotency-Key: action-stock-1" \
  --json '{
    "name": "get_stock",
    "description": "Units in stock of a product in the franchise warehouse, by sku.",
    "audience": "customers",
    "sharedWithSubaccounts": true,
    "request": { "method": "GET", "url": "https://stock.example.com/stock" },
    "input": { "type": "object", "required": ["sku"], "properties": { "sku": { "type": "string" } } },
    "variables": [{ "name": "subaccount", "source": "lelemon", "variable": "subaccount.id" }]
  }'

curl -X PUT https://api.lelemon.cl/v2/actions/act_.../approval-floor \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  --json '{ "mode": "when", "condition": { "param": "sku", "op": "eq", "value": "RESTRICTED" }, "approvers": [{ "type": "role", "value": "owner" }] }'
```

```ts title="Node.js"
const getStock = await lelemon.actions.create({
  name: `get_stock`,
  description: 'Units in stock of a product in the franchise warehouse, by sku.',
  audience: 'customers',
  sharedWithSubaccounts: true,
  request: { method: 'GET', url: 'https://stock.example.com/stock' },
  input: { type: 'object', required: ['sku'], properties: { sku: { type: 'string' } } },
  variables: [{ name: 'subaccount', source: 'lelemon', variable: 'subaccount.id' }],
});
const floor = await lelemon.actions.approvalFloor.set(getStock.id, {
  mode: 'when',
  condition: { param: 'sku', op: 'eq', value: 'RESTRICTED' },
  approvers: [{ type: 'role', value: 'owner' }],
});
```


```text
action {"id":"act_4uQao29MeSRDwnQm98ymB0","sharedWithSubaccounts":true}
    floor {"mode":"when","condition":{"param":"sku","op":"eq","value":"RESTRICTED"},"approvers":[{"type":"role","value":"owner"}],"expiresInMinutes":1440,"automatedDecision":false}
```

## 2. Share an MCP server

Connect an MCP server of yours once. Its tools become available to every subaccount agent, with the credentials kept on your side.

```bash title="cURL"
curl https://api.lelemon.cl/v2/mcp-servers \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Idempotency-Key: mcp-franchise-1" \
  --json '{ "name": "franchise-info", "url": "https://mcp.example.com/mcp", "auth": { "type": "none" }, "sharedWithSubaccounts": true }'
```

```ts title="Node.js"
const mcp = await lelemon.mcpServers.create({
  name: `franchise-info`,
  url: 'https://mcp.example.com/mcp',
  auth: { type: 'none' },
  sharedWithSubaccounts: true,
});
```


```text
mcp server {"id":"mcp_6w5NPeUTIijdv38ds4Hy9C","status":"connected","shared":true,"tools":["mtl_64bepNDEJhXykm9RMrCmnL get_store_hours"]}
```

The server must be a public Streamable HTTP MCP server. The call asks it for its tools right away. See [Actions and MCP](/guides/actions-and-mcp#mcp-servers-your-agents-call).

## 3. Create a subaccount per customer

`externalRef` is your own id for the customer, so you can find the subaccount from your database.

```bash title="cURL"
curl https://api.lelemon.cl/v2/subaccounts \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Idempotency-Key: sub-providencia-1" \
  --json '{ "externalRef": "franchise-providencia", "name": "Franchise Providencia" }'
```

```ts title="Node.js"
const franchise = await lelemon.subaccounts.create({
  externalRef: `franchise-providencia`,
  name: 'Franchise Providencia',
});
```


```text
subaccount {"id":"sub_4E6vmnn5aCcP6tx7NLmQGF","externalRef":"franchise-mvxa5tb0-1"}
```

To give the customer direct access, `POST /v2/subaccounts/{id}/api-keys` issues a key that sees only that subaccount. See [Run Lelemon for your customers](/guides/platforms).

## 4. Provision the subaccount and use the shared tools

Any route acts on a subaccount when you send the `Lelemon-Subaccount` header; the SDK does it with `withSubaccount`. Create the agent and the pipeline of the customer with the same instructions you use for everyone. The shared tools show up in the tool list of the agent with `sharedByPlatform: true`; grant the ones the customer needs.

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

curl -X PUT https://api.lelemon.cl/v2/agents/agt_.../tools/act_... \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Lelemon-Subaccount: sub_..."
```

```ts title="Node.js"
const instructions = `You are the assistant of a franchise store. You answer questions about opening hours and stock in Chilean Spanish, in at most two sentences. Use get_stock for stock and get_store_hours for hours. If you do not know, say so and offer a person.`;

const scoped = lelemon.withSubaccount(franchise.id);
const inherited = await scoped.actions.list();
const subAgent0 = await scoped.agents.create({
  name: 'Franchise assistant',
  instructions,
  llmConfig: { model: 'anthropic/claude-sonnet-5.5', temperature: 0.2 },
  channels: ['api'],
});
const subAgentId = subAgent0.id;
const subPipeline = await scoped.pipelines.create({
  name: 'Franchise store',
  stages: [{ name: 'Open', entry: true, agentId: subAgentId }, { name: 'Closed' }],
});
const tools = await scoped.agents.tools.list(subAgentId, { query: { limit: 100 } });
const sharedTools = tools.data.filter((t) => t.sharedByPlatform);
for (const tool of sharedTools) await scoped.agents.tools.grant(subAgentId, tool.id);
const subAgent = await scoped.agents.activate(subAgentId);
```


```text
actions []
    shared tools ["mtl_64bepNDEJhXykm9RMrCmnL get_store_hours","act_4uQao29MeSRDwnQm98ymB0 get_stock_mvxa5tb0"]
    agent {"id":"agt_49tWojD6RAPxVKzppe3vjD","status":"active","ready":true}
```

The subaccount does not list shared actions in `GET /v2/actions`: it reaches them through the tools of its agent, and cannot change or delete them.

## 5. Cap the conversations of a subaccount

A budget caps the conversations per month of the organization, a subaccount, an agent or a contact. With `enforcement: "stop"`, a message that would open a conversation past the limit answers `402 budget_exceeded` and the agent stops opening conversations until the month resets. At `alertPercent` the event `budget.threshold_reached` fires once.

```bash title="cURL"
curl https://api.lelemon.cl/v2/budgets \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Idempotency-Key: budget-providencia-1" \
  --json '{ "scope": "subaccount", "subjectId": "sub_...", "monthlyLimitConversations": 250, "alertPercent": 80, "enforcement": "stop" }'
```

```ts title="Node.js"
const budget = await lelemon.budgets.create({
  scope: 'subaccount',
  subjectId: franchise.id,
  monthlyLimitConversations: 250,
  alertPercent: 80,
  enforcement: 'stop',
});
```


```text
budget {"id":"bgt_4kw1BXHwbkPSAdPDyW2EVH","scope":"subaccount","limit":250,"used":0,"state":"ok"}
```

## 6. Receive the events of every subaccount

An endpoint with `scope: "customers"` receives the events of all your subaccounts; each one carries `customerId`, the `sub_` id it belongs to. With `scope: "self"` it receives only those of the platform organization.

```bash title="cURL"
curl https://api.lelemon.cl/v2/webhook-endpoints \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Idempotency-Key: webhook-customers-1" \
  --json '{ "url": "https://example.com/lelemon", "enabledEvents": ["case.stage_entered", "message.created", "budget.threshold_reached"], "scope": "customers" }'
```

```ts title="Node.js"
const endpoint = await lelemon.webhookEndpoints.create({
  url: 'https://example.com/lelemon',
  enabledEvents: ['case.stage_entered', 'message.created', 'budget.threshold_reached'],
  scope: 'customers',
});
const person = await scoped.contacts.create({ name: 'Rosa Pérez', externalRef: `shopper-` });
const kase = await scoped.cases.create({ contactId: person.id, pipelineId: subPipeline.id });
await scoped.cases.takeControl(kase.id);
await scoped.caseMessages.create(kase.id, { author: 'team', authorName: 'Store team', text: 'Hola Rosa, tu pedido está listo para retirar.' });
```


```text
event {"type":"message.created","customerId":"sub_4E6vmnn5aCcP6tx7NLmQGF"}
```

## 7. Read the usage of all subaccounts

Bill your customers from the usage: turns, messages and tokens per day, agent, model or subaccount.

```bash title="cURL"
curl "https://api.lelemon.cl/v2/usage?groupBy=subaccount" \
  -H "Authorization: Bearer $LELEMON_API_KEY"
```

```ts title="Node.js"
const usage = await lelemon.usage.retrieve({ query: { groupBy: 'subaccount' } });
```


## Notes

- A budget goes away with `DELETE /v2/budgets/{id}`, and `PATCH /v2/budgets/{id}` changes its limit.

## What you see in the dashboard

- **Subaccounts:** one per customer, with its agents, cases and keys, and the budget with its state (`ok`, `alert`, `limit_reached`).
- **Actions and MCP servers:** the shared ones marked as shared by the platform.
- **Usage:** the consumption by subaccount for the month.

## What's next

- [Run Lelemon for your customers](/guides/platforms): direct keys, templates and limits.
- [Track usage and cap conversations](/guides/usage-and-budgets).
- [Approve agent actions](/use-cases/support-with-approvals): the floor in action.
