> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Execution contexts

> Where your code runs, what the current end user, channel, and environment resolve to in each place, and the runtime constraints every context shares

An execution context is one of the places Lua runs your code: a tool during a conversation, a webhook when an external system calls, a job on its schedule, and so on. The same platform objects are available in all of them, but what "the current end user" and "the current channel" mean differs, and so does the time you get. Knowing which context you are in explains most surprises.

## What each context sees

Every context is a fresh run of your compiled bundle with the platform objects injected as globals: `User`, `Data`, `Products`, `Baskets`, `Orders`, `Jobs`, `Workflows`, `AI`, `Agents`, `Integrations`, `Voice`, `Channels`, `Team`, `Templates`, `CDN`, `Lua`, and `env`, plus `fetch`, `console`, and `process.env`. The imports from `'lua-cli'` exist for types and for `lua test`; at run time they resolve to the injected objects.

| Context                                               | `User.get()` with no argument                              | `Lua.request.channel`                                             | Time budget                         |
| ----------------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------- |
| Tool `execute`                                        | The end user in the conversation                           | The live channel (`pop` for the web widget, `whatsapp`, `dev`, …) | 180 s (300 s when run by a trigger) |
| Tool or skill `condition`                             | The end user                                               | The live channel                                                  | 30 s                                |
| Preprocessor, postprocessor                           | The end user (also passed as the first argument)           | The live channel                                                  | 180 s                               |
| Model resolver                                        | The end user                                               | The live channel                                                  | 5 s                                 |
| Webhook `execute`                                     | Nobody; pass a `userId`                                    | `unknown`                                                         | 180 s                               |
| Trigger `verify`, `filter`, `transform`, `tool.input` | Nobody                                                     | `unknown`                                                         | 15 s for all slots together         |
| Job `execute`                                         | Nobody, unless the job was created from an end user's tool | `unknown`                                                         | 1–600 s, default 300 s              |
| Workflow code step                                    | From the run, when it has one                              | From the run, else `unknown`                                      | The step's timeout, up to 600 s     |
| Device trigger handler                                | Nobody                                                     | `unknown`                                                         | 180 s                               |

In a conversation context (tools, conditions, processors, the model resolver) `User.get()` returns the end user who sent the message, and `Lua.request.channel` is where they sent it from, so a tool can branch on channel or read the end user's stored profile.

```ts src/skills/tools/GetGreetingTool.ts theme={null}
import { LuaTool, User, Lua } from 'lua-cli';
import { z } from 'zod';

export default class GetGreetingTool implements LuaTool {
  name = 'get_greeting';
  description = 'Greet the current user by name, adapted to the channel.';
  inputSchema = z.object({});

  async execute() {
    const user = await User.get();
    const name = user?.data?.name ?? 'there';
    return Lua.request.channel === 'whatsapp' ? `Hi ${name}` : `Hello ${name}, how can I help today?`;
  }
}
```

Outside a conversation there is no end user to return. In a [webhook](/concepts/webhooks), a [trigger](/concepts/triggers) slot, a [job](/concepts/jobs), or a device trigger handler, call `User.get(userId)` with an ID you stored earlier, or `User.get({ email })` / `User.get({ phone })`, which return `null` when nobody matches. A bare `User.get()` there fails as a platform error, `User.Inbox.push` refuses with `Inbox.push requires a user context`, and `job.user()` on a job that wasn't created from a user's tool throws `User API not initialized`. `Lua.request.channel` in those contexts is the string `unknown`.

No context exposes a thread ID to your code. `User.getChatHistory()` returns the end user's history with the agent as a whole, and `Agents.invoke` returns the thread it used.

## Runtime constraints

Each invocation runs in a fresh, isolated VM context that is discarded afterwards, so nothing at module scope survives: no in-memory caches, counters, or connection pools between calls. Persist state with `Data` or on the `User` record.

`Jobs.create` sends the job's `execute` function to the server as source text. Variables it closes over are not sent with it; pass what the job needs in `metadata` and read `job.metadata` inside.

A webhook's `secret` must be a string literal or a constant the compiler can read; `lua compile` fails on a runtime expression rather than deploy an unsigned webhook.

`console.log`, `console.info`, `console.warn`, and `console.error` are captured into `lua logs`; other console methods are not provided. Output isn't scrubbed for secrets, so don't log credentials. A tool result larger than 300,000 characters reaches the model as a truncated preview.

Your code can call any public host with `fetch`. Private-network and cloud-metadata addresses are not a supported destination, and Node modules such as `child_process`, `worker_threads`, and `vm` are not supported in the sandbox.

## A context and a channel

A channel is where the end user is; a context is where your code is. The same tool runs in the same context whether the message came from WhatsApp or the web widget, and reads `Lua.request.channel` to tell them apart. A webhook, by contrast, has no channel because no end user started it, so the value is `unknown` however the message it eventually sends goes out.

## When it matters

* Sending a message from a webhook or job: look the end user up by ID first, then `user.send(…)` or `Channels.send(…)`.
* Behavior that differs by channel: read `Lua.request.channel` in a tool, a processor, or the model resolver, not in a webhook.
* Work that needs more than a tool's budget: return quickly and hand off to `Jobs.create` or `Workflows.start`.
* A tool that must be hidden for some end users: a `condition`, which runs in the conversation context and can read the end user.

## Limits

* Time budgets are in the table; a job's `timeout` must be an integer from 1 to 600 seconds. A tool that hits its budget is not retried; the call fails. A job attempt that hits its `timeout` counts as failed and is retried when the job has `retry`.
* A job is refused before `execute` runs when loading its bundle and preparing its context took more than 128 MB of heap: `Memory limit exceeded before execution`. Nothing `execute` allocates counts, so the lever on your side is bundle size: trim top-level imports. The check applies to jobs only, and the refused attempt is retried like any failed one.
* Tool results are capped at 300,000 characters; captured log lines at 256 KB per field.
* Trigger slots share a single 15-second budget, so keep signature checks and filtering cheap.

## Next steps

<Columns cols={2}>
  <Card title="SDK reference" href="/reference/sdk/overview">Every platform object and where it is available.</Card>
  <Card title="User reference" href="/reference/sdk/user">`get`, lookups by email or phone, `send`, and `Inbox`.</Card>
  <Card title="Identify users" href="/build/identify-users">Carry a user ID from a conversation into a webhook or job.</Card>
  <Card title="Logs and debugging" href="/ship/logs-and-debugging">Read what each context logged.</Card>
</Columns>
