Skip to content

Backend function reference

What a backend function receives and can reach: the event, ctx.secrets, ctx.fetch, ctx.entity, ctx.email, ctx.ai and ctx.log, return shapes, visibility and running as the app.

Last updated September 17, 2026

On this page

A backend function is the body of an async JavaScript function. event and ctx are already in scope; whatever you return goes back to the caller.

event

FieldMeaning
event.<anything>What the caller sent: the payload from fn.invoke, the API body, or the webhook delivery
event.userThe signed-in user who called it, when there is one
event.ipThe caller's address
event.requestFor webhooks: raw_body (the exact bytes the sender signed) and headers (lower-cased)
event.api_keyThe API key used, when called over the API

ctx

MemberDoes
ctx.secrets.get(NAME)A stored secret the function declared
await ctx.fetch(url, init)An outbound request to a host the function declared
ctx.entity("Name")The app's tables, with the caller's permissions (or the app's, when running as the app)
await ctx.email.send({ to, subject, body })Send email from the app
await ctx.ai.invoke({ prompt, ... })Call AI, same options as the page SDK
ctx.log(...)A line in the run's log

Return shapes from ctx.entity

CallResolves to
.list(options){ ok, total, records }
.filter(conditions)An array of rows
.get(id){ ok, record }
.create(data){ ok, id, record }
.update(id, data){ ok, record }
.archive(id){ ok }

Settings on a function

SettingValues
Visibilitypublic, users (default), admin, internal
Egress hostsThe outside hostnames it may call. Enforced
Secret namesThe stored secrets it may read. Enforced
Runs asthe caller (default) or the app. Running as the app reads and writes every table regardless of who called
EnabledA new function starts off; the builder switches it on once tested

A complete example

JavaScript
// stripe_payments: a webhook handler. Visibility public, secret STRIPE_WEBHOOK_SECRET declared.
const secret = ctx.secrets.get("STRIPE_WEBHOOK_SECRET");
const sig = event.request.headers["stripe-signature"];
if (!verifyStripeSignature(event.request.raw_body, sig, secret)) {
  return { ok: false, error: "bad_signature" };
}
const evt = JSON.parse(event.request.raw_body);
const seen = await ctx.entity("StripeEvent").filter({ stripe_id: evt.id });
if (seen.length) return { ok: true, duplicate: true };
await ctx.entity("StripeEvent").create({ stripe_id: evt.id, type: evt.type });
if (evt.type === "payment_intent.succeeded") {
  const orderId = evt.data.object.metadata.order_id;
  await ctx.entity("Order").update(orderId, { status: "paid" });
  ctx.log("order paid", orderId);
}
return { ok: true };

Calling one

  • From a page: await fn.invoke("name", payload).
  • From your server: POST /api/functions/<name> with a secret key. See Calling backend functions.
  • From a schedule or a data change: an automation.
  • From another company's server: a webhook at /api/webhooks/<name>.
Was this page helpful?