# Script

*Script* is a declarative backend endpoint that a frontend calls over HTTP. Rather than writing server code, you declare "what to do" as JSON and the WEEGLOO engine runs it for you. The goal is to replace, with a single *Script*, the typical backend plumbing that supports a frontend (a BFF, Backend-for-Frontend): authentication, guards, chained CRUD, external API calls, and value shaping.

This document set is the reference for *Script* syntax. The details of each part of the syntax are split across the pages in [Documents in this group](#documents-in-this-group) below.

Creating and managing a *Script* (create, read, update, delete) happens on **CMA** (`https://cma.weegloo.com/v1`). Execution is handled by the execution path on the dedicated Script host (`https://script.weegloo.com/v1`). That one execution path accepts **both** a Weegloo User token and the token of a member who has signed up for the product (a *ServiceUser*). **ACMA** has no *Script* API, and neither do the read-only delivery APIs (CDA, ACDA).

## Mental model {#mental-model}

- **One *Script* is one HTTP endpoint.** The call method (`method`) determines which *Script* runs.
- **The body is a `statements` array.** They run sequentially, top to bottom. This is the same as the body of a function in ordinary programming.
- **It is declaration, not code.** You do not embed arbitrary code (FaaS); you compose predefined statement types. It is designed less for hand-authoring by a person and more for generation by an AI agent over MCP.
- **Values flow through JSON Pointer templates.** You reference the result of a previous step, the input payload, or a variable with `{ /pointer }` and pass it to the next step. When you need a condition or a calculation, you use JsonLogic operators. The full rules are covered in [Value Expressions](/api/reference/script/value-expressions.md).

## Top-level structure (ScriptDefinition) {#script-definition}

A single *Script* is defined by the following `ScriptDefinition` structure.

```jsonc
{
  "method": "Post",               // Get | Post | Put | Patch | Delete. The HTTP method matched on call (required)
  "payloadSchema": { /* ... */ }, // (optional) JSON Schema. If present, validates the request payload before execution
  "statements": [ /* Statement[]. Executed from top to bottom (required, at least 1) */ ]
}
```

| Field | Required | Description |
|---|:---:|---|
| `method` | Required | The HTTP method used to call this *Script*. Calls are matched by this value. |
| `payloadSchema` | Optional | A JSON Schema. If specified, the request body (payload) is validated against this schema **before** execution, and if validation fails the request is rejected without being executed. |
| `statements` | Required | An ordered array of statements to run. At least one. |

The payload accepts a JSON object only. The call body is accessed through the `/payload` context root (`{ /payload/... }`), and when you need the raw body string before parsing you access it through `/rawPayload` (for cases such as signature verification, where the computation runs over the bytes as sent). The call's request HTTP headers are referenced through the `/headers` root (`{ /headers/... }`, with lowercase keys). The time at which execution started is in the `/now` root. The full set of context roots is covered in [Value Expressions](/api/reference/script/value-expressions.md#context-roots).

## Request and response {#request-response-envelope}

In the end, a *Script* returns the value of its `Return` statement to the caller. The response has the following shape.

```jsonc
{
  "requestId": "…",     // Execution identifier
  "durationMs": 1234,   // Execution time (ms)
  "statusCode": 200,    // The statusCode of the Return that was reached (default 200)
  "return": <value>     // Only when Return.isError is false. If the value is null, ""
  // "error": <value>   // When Return.isError is true, or when the execution failed (in which case "return" is absent). If the value is null, ""
}
```

- `requestId` is the identifier of this execution. The same value goes into `sys.requestId` on the *ScriptLog* that the execution leaves behind, so it is the key to finding this execution in the logs.
- `return` and `error` never appear together. The `isError` of the `Return` statement decides which one it is.
- If the *Script* ends **without reaching** a `Return` statement, `return` and `error` are **both absent** and `statusCode` is the default (200).
- **If the execution fails, `error` is filled in even without a `Return`.** When the failure comes from the caller's side, such as an invalid payload, and no `Try` catches it, `error` carries the reason for the failure and `statusCode` becomes the code that corresponds to that failure (4xx for an invalid payload, `502` when an external call or a mail send fails). In practice this is the error response you meet most often. An execution that goes past its time budget answers with `408` instead of the envelope.
- If a value is `null`, that field is emitted as an empty string `""`.

You control the response body and the status code with the `Return` statement's `value`, `isError`, and `statusCode`. For details, see [Return in the Statement Catalog](/api/reference/script/statements.md#return).

## How much time one execution gets {#execution-budget}

A *Script* runs inline, on the path that handles the call request. There is no flow that hands the work to the background or returns an acknowledgement first, and the response body of the call is the execution result. There is no polling path for collecting the result later either.

The time one execution gets is decided by a single expression: `min(30s + the sum of the times the statements declare, 180s)`.

- The base budget is 30 seconds. The time each statement declares is added on top of that.
- **A statement that declares nothing counts as 0 seconds.** The time such a statement actually spends comes out of the 30-second base budget.
- If the sum goes past 180 seconds, the save is not rejected. Instead, **the budget is cut off at 180 seconds.**

Here is the gist of the declaration rule for each statement.

| Statement | Time it declares |
|---|---|
| `Http` | (`timeoutMs`, or 30s if absent) × (1 + `retry`) |
| `EmailSend` | `timeoutMs`, or 10s if absent |
| `Loop` | The sum of the body statements × (`maxIterations`, or 10,000 if absent) |
| `ResourceForEach` | The sum of the `onEach` statements × (`limit`, or 10,000 if absent) |
| `If` | The larger of the `then` side and the `else` side |
| `Parallel` | The largest of the branches |

- **Iteration multiplies.** `Loop` and `ResourceForEach` multiply the time the body (`onEach`) declares by the iteration ceiling.
- An iteration with no external calls has a body that declares 0, so the 30-second base budget is the real limit.

The detailed per-statement rules and the plan limits are covered in [Execution Semantics, Constraints, and Security](/api/reference/script/execution-and-limits.md).

## Minimal example {#minimal-example}

It creates a post *Content* from the title and body in the request payload, publishes it right away, and then returns the `sys.id` it created.

```jsonc
{
  "method": "Post",
  "statements": [
    { "type": "ResourceCreate", "resource": "Content",
      "contentType": { "sys": { "id": "ct_post" } },
      "fields": {
        "title": { "en-US": "{ /payload/fields/title }" },
        "body":  { "en-US": "{ /payload/fields/body }" }
      },
      "publish": true,
      "name": "post" },

    { "type": "Return", "value": { "id": "{ /post/sys/id }" }, "statusCode": 201 }
  ]
}
```

- `ResourceCreate` creates the *Content* and binds the result to the name `post`.
- `Return` returns `{ "id": <new Content id> }` with `201`.
- Why a *Content*'s `fields` values are locale maps (`{ "en-US": ... }`) is covered in [Locale maps in Value Expressions](/api/reference/script/value-expressions.md#locale-value-map).

You'll find more varied scenarios in the [Cookbook](/api/reference/script/cookbook.md).

## Documents in this group {#documents-in-this-group}

- [Value Expressions](/api/reference/script/value-expressions.md): Covers `{ /pointer }` references, literals, JsonLogic operations and conditions, context roots, and locale maps. This is the heart of the syntax.
- [Statement Catalog](/api/reference/script/statements.md): Covers the fields and results of the 25 statement types (resource CRUD and reads, `Http`, `EmailSend`, `SetVar`, `Cache`, `ParseJson`, `Signature`, `Hash`, `Regex`, `If`, `Loop`, `Parallel`, `Try`, `Return`).
- [Execution Semantics, Constraints, and Security](/api/reference/script/execution-and-limits.md): Covers execution order, guards, compensation, optimistic locking, errors, static constraints and plan limits, and the security model.
- [Cookbook](/api/reference/script/cookbook.md): Covers complete examples such as upsert, a credit guard, an LLM proxy, pagination, parallel execution, a payment saga, and webhook signature verification.
- [Script Resource and Endpoints](/api/reference/script/endpoints.md): Covers the `sys` structure of the `Script` resource, the specification of the HTTP endpoints for authoring and execution (`/execute`), and *ScriptLog*, the execution log.

If this is your first time, we recommend reading from this page onward in the order [Value Expressions](/api/reference/script/value-expressions.md), then [Statement Catalog](/api/reference/script/statements.md). The [Cookbook](/api/reference/script/cookbook.md) is also worth skimming all the way through.
