Lelemon
  • Get started
  • Use cases
  • Guides
  • API basics
  • Webhooks
  • SDK, CLI and MCP
  • API reference
  • Changelog
  • v1 (legacy)
WhatsApp sales agentMeeting schedulingSupport with approvalsInternal copilot with a data connectorPlatform with subaccountsBackground runs
powered by Zudoku
Use cases

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.

Your platformone keyLelemonSubaccountsub_...POST /v2/subaccountsexternalRef: restaurant-10421sub_...2GET /v2/agentsLelemon-Subaccount: sub_...3Isolated organizationits own agents, pipelines, numbers4sharedWithSubaccounts: trueone action, every customer5Shared action callLelemon-Subaccount-Id: sub_...6Events with customerIdwebhook endpoint scope: customers7
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.
Code
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.

0. Set up

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

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

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

3. Create a subaccount per customer

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

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

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.

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

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

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

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: direct keys, templates and limits.
  • Track usage and cap conversations.
  • Approve agent actions: the floor in action.
Internal copilot with a data connectorBackground runs
On this page
  • 0. Set up
  • 1. Share an action with every subaccount
  • 2. Share an MCP server
  • 3. Create a subaccount per customer
  • 4. Provision the subaccount and use the shared tools
  • 5. Cap the conversations of a subaccount
  • 6. Receive the events of every subaccount
  • 7. Read the usage of all subaccounts
  • Notes
  • What you see in the dashboard
  • What's next
TypeScript
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" }] }'
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 }'
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" }'
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_..."
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" }'
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" }'
curl "https://api.lelemon.cl/v2/usage?groupBy=subaccount" \ -H "Authorization: Bearer $LELEMON_API_KEY"