Account Functions & Webhooks
Site functions belong to one site. Account functions belong to you — they can reach across all your sites (read one site’s orders, write another’s CRM) and are managed in the Automations section of your dashboard.
Inside an account function, ctx.sites gives you each of your sites’ tables, files, users, and exposed methods — so two sites that never knew about each other can work together.
Five ways they run
- Webhook — each function can get a secret URL; anything that POSTs to it runs the function with the request body as the event. Perfect for payment providers, form services, CI, or any external tool that “calls you back”. Manage them under Automations → Triggers.
- Email trigger (Mailhook) — a unique email address that runs the function with the parsed email as input. See Email Triggers (Mailhooks).
- Schedule — cron timing, same idea as site schedules but account-wide.
- Called by an automation or another function — automations can target account functions; functions can chain (
ctx.functions.run). - Called from your published app — see below.
Calling from your published app
Your app’s pages can invoke an account function with functions.invokeAccount('name', params). Because account functions are powerful (they can span sites), this is off by default — you must switch it on per function, in two steps:
- Grant the site: the function’s grants must include the site that calls it.
- Set who may call it: give the function execute access rules — for example “signed-in members”. No rules = no app access.
The signed-in member’s identity is checked against your rules automatically, and the function receives it as params._appUser. Account functions read global environment variables — your account-level secrets (ctx.env.get('NAME') or {{NAME}} in ctx.fetch). These are deliberately separate from each site's env vars: site functions see only site values, account functions see only global values — no silent fallback between them.
An account function is not a site function
It has no ctx.tables and no logged-in user, because it belongs to your account rather than to one site. Reach a site's data through ctx.sites['Site Name'].tables (granted sites only). If you're unsure what a function can see, have it return Object.keys(ctx) once — the AI can do this for you.
Calling a slow API (an AI model, a big import)? Two limits must both allow it: the request's own timeout and the function's timeout, which defaults to 10 seconds. Raise the function timeout for anything AI-related, or the call is cut off mid-flight.
Listing mailboxes: whose do you get?
ctx.email.accounts() answers “what may the person calling right now send through”. If a member is signed in you get only the mailboxes they connected — that isolation is the point, so one customer can never send as another. An empty list therefore means this member has none, not that you have none; the reply's scope tells you which it was. When your own code wants your account's shared mailboxes anyway — an admin screen, an ops function — ask for them explicitly with ctx.service.email.accounts(). It never returns a member's private mailbox.
Site function or account function?
Most logic belongs to a single site — write that as a backend function (code) or a data function (declarative). Reach for an account function only when the work spans sites or an external service needs a URL to call.
| Situation | Use |
|---|---|
| Logic for one site, called by its pages | Site function (backend or data) |
| External service needs a URL to call | Account function + webhook |
| Work spanning two or more of your sites | Account function (ctx.sites) |
Quotas & requirements
Running functions requires a paid plan. Account functions execute in the same sandboxed engine as project backend functions and draw from the same monthly execution quota and concurrency limit — there is no separate account-function allowance, so heavy cross-site automation counts against the same budget as your sites’ own functions.