Lelemon
  • Get started
  • Use cases
  • Guides
  • API basics
  • Webhooks
  • SDK, CLI and MCP
  • API reference
  • Changelog
  • v1 (legacy)
Configure your account
Talk to people
Run and protect
    Give an agent a jobSchedule a jobTrack usage and cap spendingRun Lelemon for your customersKeep tool data out of storageErase a contact or a messageBest practicesMoving from v1
powered by Zudoku
Run and protect

Track usage and cap conversations

You will

Set a budget of 500 conversations a month for a copilot, get an alert at 80%, and know what happens when it runs out.

You need

  • A key with usage:read, turns:read and budgets:write
  • An agent that already answers messages
  • A webhook endpoint, to receive the alert

The example is a support copilot inside your product. It may handle 500 conversations a month. At 400 you want a message in your channel. At 500 you want it to stop opening conversations, and you want your customers to reach a person.

A conversation is the unit Lelemon bills: a window per case of up to 10 agent turns or 24 hours, whichever comes first. The next turn opens another one. The model does not change the count.

Your appa copilotLelemonbudget: 500 conversationsContactPOST /v2/budgetsmonthlyLimitConversations: 500, alertPercent: 801Writes a message2Count, answerone conversation counted3Used reaches 400 conversationsstate: alert4budget.threshold_reachedonce per month and budget5Used reaches 500 conversationsstate: limit_reached6POST /v2/cases/{id}/messages7402 budget_exceedednothing saved, do not retry8Writes again9Fixed messagecase handed to a person10budget.exceededand case.escalated11
A monthly budget of 500 conversations with an alert at 80%. Each new conversation counts once. At 80% an event fires once. At the limit the agent stops opening conversations and a person takes over.
  1. Create the budget once. enforcement defaults to stop.
  2. A contact writes and a turn starts. The first turn of a case, or the turn after 10 turns or 24 hours, opens a conversation.
  3. When the turn opens a conversation, Lelemon counts it in every budget that applies. The turns that continue an open conversation count nothing. usedConversations grows.
  4. The budget passes alertPercent and its state is alert. The agent keeps working.
  5. The webhook budget.threshold_reached fires once. It fires again if you raise the limit and reach the new threshold.
  6. The limit is reached. With enforcement stop the agent does not open another conversation; the ones already open keep going. With notify it would keep working and only the events would fire.
  7. You write as a contact through the API.
  8. Nothing is saved. Stop and do not retry until the month resets or you raise the limit with PATCH /v2/budgets/{id}.
  9. A contact who writes to the agent after the cut is not left without an answer. This does not apply to internal copilots.
  10. Lelemon hands the case to a person with transfer_to_human, reason budget_exceeded, and the contact reads a fixed message.
  11. budget.exceeded fires once per month and budget, and case.escalated tells your team that a case needs a person.

See where the conversations go

GET /v2/usage counts conversations, turns, replies and tokens per day, agent, model or subaccount. It shows no prices: use it to bill your own customers or to see where the volume is. The conversations are counted from the day Lelemon started counting them; earlier days show zero.

Days count in the time zone of the organization. The range cannot pass 366 days. A platform key reads one subaccount with the Lelemon-Subaccount header, or all of them with groupBy=subaccount. That grouping also counts the usage of the platform itself: it comes as the group with subaccountId: null, and totals is the sum of every group.

Usageobject, 6 fields
  • objectenumrequired

    usage

  • fromstringrequired

    Example 2026-11-01

  • tostringrequired

    Example 2026-11-30

  • timezonestringrequired

    Example America/Santiago

  • totalsUsageTotalsrequired
    UsageTotals6 fields
    • turnsintegerrequired

      Turns the agent took.

    • messagesintegerrequired

      Replies the agent wrote.

    • inputTokensintegerrequired
    • outputTokensintegerrequired
    • conversationsintegerrequired

      Conversations opened in the range, the unit that is billed. A conversation is a window per case of up to 10 agent turns or 24 hours, whichever comes first; the next turn opens another one.

    • casesintegerrequired

      Cases (conversations) in which the agent wrote at least one reply in the range. A case counts once however many replies it has.

  • dataUsageGroup[]required
    UsageGroup10 fields
    • turnsintegerrequired

      Turns the agent took.

    • messagesintegerrequired

      Replies the agent wrote.

    • inputTokensintegerrequired
    • outputTokensintegerrequired
    • conversationsintegeroptional

      Conversations opened in this group. Absent with groupBy=model: a conversation is not split by model.

    • casesintegeroptional

      Cases in which the agent wrote a reply in this group. A case with replies on two days counts in both, so the groups can add up to more than totals.cases. Absent with groupBy=model: a case is not split by model.

    • daystringoptional

      Example 2026-11-01

    • agentIdstringoptional

      Example agt_7Hc2LmQ0x8bVw1TqS9mYp3

    • modelstringoptional

      Example anthropic/claude-sonnet-4.6

    • subaccountIdstring or nulloptional

      With groupBy=subaccount. null is the usage of the platform itself, which is not in any subaccount.

      Example sub_7Hc2LmQ0x8bVw1TqS9mYp3

Usage reports. It does not enforce. To enforce a limit, create a budget.

Read what the agent did in a turn

GET /v2/cases/{id}/turns lists the turns of a case, newest first. Each turn has the tools the agent called, with their input and output, the tokens it spent and the model that answered.

Input and output are masked by shape: emails, RUTs, phones, cards and secrets. A tool marked noPersist leaves only its status and duration. Traces are kept for 90 days. To know when a turn ends without polling, subscribe to case.turn_completed.

Turnobject, 13 fields
  • objectenumrequired

    turn

  • idstringrequired

    Example trn_7Hc2LmQ0x8bVw1TqS9mYp3

  • caseIdstringrequired

    Example cse_7Hc2LmQ0x8bVw1TqS9mYp3

  • agentIdstring or nullrequired
  • modelstring or nullrequired

    The model that answered the last call of the turn.

  • statusenumrequired

    completed: the agent finished by itself. incomplete: it stopped early and delivered a partial answer. failed: it gave no answer.

    completedincompletefailed

  • stopReasonstring or nullrequired
  • iterationsintegerrequired

    Calls to the model in the turn.

  • tokensTurnTokensrequired
    TurnTokens2 fields
    • inputintegerrequired
    • outputintegerrequired
  • durationMsintegerrequired
  • stepsTurnStep[]required
    TurnStep5 fields
    • toolstringrequired

      Name of the tool the agent called. A Lelemon system tool shows its public name (transfer_to_human, save_case_data, send_buttons...), the same one GET /v2/agents/{id}/tools lists. For a tool from an MCP server it is the server slug and the tool name.

    • statusenumrequired

      okerror

    • durationMsintegerrequired

      Milliseconds the tool took.

    • inputobjectoptional

      What the agent sent to the tool, with emails, RUTs, phones, cards and secrets masked by shape. Absent when the tool is marked noPersist: those tools leave only status and duration.

    • outputstringoptional

      What the tool answered, masked the same way and cut at 4000 characters. Absent when the tool is marked noPersist.

  • omittedStepsintegerrequired

    Tool calls that did not fit in steps (up to 50 steps and 64 KB are kept).

  • createdAtstring (date-time)required

Measure how long cases take

GET /v2/case-metrics lists the cases that changed in a range, one row each, with the times you need for a service report: when the case came in, was handed to a person, was taken and was closed, who closed it, and the seconds spent with the agent and with a person. Rows come oldest change first, so you can sync them page by page into your own reports.

  • from is required, as an ISO 8601 date and time with its zone. to is not included and defaults to now. It can be at most 31 days after from; a longer range answers 400 with param: to.
  • The range filters by updatedAt, the last change of the case. A case that changes again moves to a later position, so a sync that stores the last updatedAt it saw misses nothing.
  • Paginate with cursor and the same from and to. A cursor from another range answers 400 invalid_cursor.
  • resolvedByAgent is true when the agent closed the case without handing it off and without anyone taking it. closedBy is agent, human or system (inactivity, follow-ups exhausted), and null while the case is open.
  • waitSeconds is the time between the last handoff and the first time a person took the case. It is null when that never happened.
  • The scope is cases:read. A platform key reads one subaccount with the Lelemon-Subaccount header.
  • The dashboard shows the same numbers in Tiempos de los casos, to owners and admins.
CaseMetricsobject, 16 fields
  • objectenumrequired

    case_metrics

  • caseIdstringrequired

    Example cse_4Zq0x8bVw1TqS9mYp3RkLd

  • pipelineIdstringrequired

    Example ppl_02y0e5MANqacVbmtn1YyeJ

  • statusenumrequired

    openclosed

  • controlenumrequired

    Who has the case now. human also covers a case the agent handed off and nobody took yet.

    agenthuman

  • createdAtstring (date-time)required
  • escalatedAtstring or null (date-time)required

    The first time the agent handed the case to a person.

  • takenAtstring or null (date-time)required

    The first time a person took the case.

  • takenBystring or nullrequired

    The member who took it first. null when nobody did, or when it was taken from the phone.

    Example mbr_4Zq0x8bVw1TqS9mYp3RkLd

  • closedAtstring or null (date-time)required

    When it was closed. null while it is open.

  • closedByenumrequired

    Who closed it: the agent, a person, or the system (inactivity, follow-ups exhausted). null while it is open.

    agenthumansystemnull

  • resolvedByAgentbooleanrequired

    True when the agent closed it without handing it off and without anyone taking it.

  • waitSecondsinteger or nullrequired

    Seconds between the last handoff and the first time a person took the case. null when it was never handed off and taken.

  • agentSecondsintegerrequired

    Seconds the agent attended the case.

  • humanSecondsintegerrequired

    Seconds a person attended the case.

  • updatedAtstring (date-time)required

    The last change. It is the field the range filters by.

Create the budget

Budgetobject, 12 fields
  • objectenumrequired

    budget

  • idstringrequired

    Example bgt_7Hc2LmQ0x8bVw1TqS9mYp3

  • scopeenumrequired

    What the budget covers: the whole organization, one subaccount, one agent or one contact.

    organizationsubaccountagentcontact

  • subjectIdstring or nullrequired

    The sub_, agt_ or ctc_ id the budget covers. null for the organization.

  • monthlyLimitConversationsintegerrequired

    Monthly limit in conversations.

    Example 500

  • alertPercentintegerrequired

    Percent of the limit that fires budget.threshold_reached.

    Example 80

  • enforcementenumrequired

    stop: at the limit the agent stops and the API answers 402 budget_exceeded. notify: the agent keeps working and only the events fire.

    stopnotify

  • usedConversationsintegerrequired

    Conversations used this month.

    Example 124

  • stateenumrequired

    ok: under the alert percent. alert: past it. limit_reached: the used conversations reached the limit.

    okalertlimit_reached

  • periodBudgetPeriodrequired
    BudgetPeriod2 fields
    • startstringrequired

      First day of the month.

      Example 2026-11-01

    • resetsAtstring (date-time)required

      When the month ends and the spent amount goes back to zero.

  • createdAtstring (date-time)required
  • updatedAtstring (date-time)required

A budget covers one scope: the whole organization, one subaccount, one agent or one contact. Set subjectId for every scope but organization. A budget per contact keeps one stranger from using up the budget of everyone. Several budgets can apply to the same turn, and the strictest one stops it. You get one budget per scope and subject: a second one answers 409. Creating a budget is not idempotent, so keep the Idempotency-Key stable when you retry.

The month is counted in the time zone of the organization. A turn that opens a conversation counts one in every budget that applies. A turn that continues an open conversation counts nothing, so a budget at its limit never cuts a conversation in the middle.

React to the alert

At alertPercent the webhook budget.threshold_reached fires once per month and budget. Its data has usedConversations, monthlyLimitConversations, alertPercent and resetsAt.

The agent keeps working. Use the alert to decide: raise the limit, or let it run out.

What happens at the limit

With enforcement: "stop", the default:

  • POST /v2/cases/{id}/messages with author: contact answers 402 budget_exceeded when the message would open a new conversation. Nothing is saved. Do not retry until the month resets or you raise the limit. A case whose conversation is already open keeps answering.
  • A contact who writes to the agent gets a fixed message, and the case goes to a person (transfer_to_human, reason budget_exceeded). You get case.escalated.
  • The webhook budget.exceeded fires once per month and budget.

With enforcement: "notify" the agent never stops and only the events fire.

Read budget_exceeded for the error.

Raise the limit

Raising the limit above what is used makes a stopped agent answer again at once and re-arms the alert. The scope and the subjectId cannot change: create another budget instead. The same body twice leaves the same budget.

A key of a subaccount only reaches the budgets of its own organization. The budget its platform set for it is not visible to it and cannot be raised from there.

Delete a budget

The answer is 204. An agent that the budget was stopping answers again at once, its alerts stop and the conversations counted so far go with it; the usage report is not affected. To cap the same scope and subject again, create a new budget. Deleting it a second time answers 404.

Schedule a jobRun Lelemon for your customers
On this page
  • See where the conversations go
  • Read what the agent did in a turn
  • Measure how long cases take
  • Create the budget
  • React to the alert
  • What happens at the limit
  • Raise the limit
  • Delete a budget
curl "https://api.lelemon.cl/v2/usage?from=2026-11-01&to=2026-11-30&groupBy=agent" \ -H "Authorization: Bearer $LELEMON_API_KEY"
{ "object": "usage", "from": "2026-11-01", "to": "2026-11-30", "timezone": "America/Santiago", "totals": { "conversations": 312, "turns": 1240, "messages": 1198, "inputTokens": 3120400, "outputTokens": 211800 }, "data": [ { "agentId": "agt_7Hc2LmQ0x8bVw1TqS9mYp3", "conversations": 312, "turns": 1240, "messages": 1198, "inputTokens": 3120400, "outputTokens": 211800 } ] }
curl "https://api.lelemon.cl/v2/cases/cse_4Zq0x8bVw1TqS9mYp3RkLd/turns?limit=5" \ -H "Authorization: Bearer $LELEMON_API_KEY"
{ "object": "turn", "id": "trn_7Hc2LmQ0x8bVw1TqS9mYp3", "caseId": "cse_4Zq0x8bVw1TqS9mYp3RkLd", "status": "completed", "iterations": 2, "tokens": { "input": 2410, "output": 168 }, "durationMs": 3120, "steps": [{ "tool": "find_order", "status": "ok", "durationMs": 420 }], "omittedSteps": 0 }
curl "https://api.lelemon.cl/v2/case-metrics?from=2026-11-02T13:00:00Z&to=2026-11-02T14:00:00Z&limit=50" \ -H "Authorization: Bearer $LELEMON_API_KEY"
{ "object": "list", "data": [ { "object": "case_metrics", "caseId": "cse_4Zq0x8bVw1TqS9mYp3RkLd", "pipelineId": "ppl_Q8dV1cX2mRw0TqS9mYp3Lk", "status": "closed", "control": "human", "createdAt": "2026-11-02T12:00:00.000Z", "escalatedAt": "2026-11-02T12:10:00.000Z", "takenAt": "2026-11-02T12:20:00.000Z", "takenBy": "mbr_4Zq0x8bVw1TqS9mYp3RkLd", "closedAt": "2026-11-02T12:50:00.000Z", "closedBy": "human", "resolvedByAgent": false, "waitSeconds": 600, "agentSeconds": 600, "humanSeconds": 1800, "updatedAt": "2026-11-02T12:50:00.000Z" } ], "hasMore": false, "nextCursor": null }
curl https://api.lelemon.cl/v2/budgets \ -H "Authorization: Bearer $LELEMON_API_KEY" \ -H "Idempotency-Key: budget-copilot-2026" \ --json '{ "scope": "organization", "monthlyLimitConversations": 500, "alertPercent": 80 }'
{ "object": "budget", "id": "bgt_7Hc2LmQ0x8bVw1TqS9mYp3", "scope": "organization", "subjectId": null, "monthlyLimitConversations": 500, "alertPercent": 80, "enforcement": "stop", "usedConversations": 0, "state": "ok", "period": { "start": "2026-11-01", "resetsAt": "2026-12-01T03:00:00.000Z" } }
{ "budget": { "id": "bgt_7Hc2LmQ0x8bVw1TqS9mYp3", "scope": "organization", "subjectId": null }, "alertPercent": 80, "monthlyLimitConversations": 500, "usedConversations": 400, "resetsAt": "2026-12-01T03:00:00.000Z" }
curl -X PATCH https://api.lelemon.cl/v2/budgets/bgt_7Hc2LmQ0x8bVw1TqS9mYp3 \ -H "Authorization: Bearer $LELEMON_API_KEY" \ --json '{ "monthlyLimitConversations": 750 }'
curl -X DELETE https://api.lelemon.cl/v2/budgets/bgt_7Hc2LmQ0x8bVw1TqS9mYp3 \ -H "Authorization: Bearer $LELEMON_API_KEY"