# Script resource and endpoints

A *Script* is a declarative backend endpoint that the frontend calls over HTTP (its concept and top-level structure are covered in [Script Overview](/api/reference/script.md)). This page covers the `sys` structure and body properties of the *Script* **resource**, the specification of the **HTTP endpoints** that author and run a *Script*, and *ScriptLog*, the record of a run.

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

A *Script* is a resource that carries a `version`, and it is a billable resource subject to a per-plan count limit. Unlike *Content* or *Media*, however, it **has no publish status.** Its `sys` has no publish-related properties such as `status` or `publish`; only `version` goes up with each change. Because there is no concept of publishing or unpublishing, deletion happens right away without unpublishing first.

## Resource structure {#resource-structure}

The following is the single-read response for the *Script* "t6-http". Along with `sys` (system properties), it has as body properties `name`, `definition`, and the two switches that open and close its call paths, `directCallEnabled` and `anonymousCallEnabled`.

```json
{
  "sys": {
    "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK",
    "type": "Script",
    "space": { "sys": { "id": "6jSUUAWT", "type": "Refer", "targetType": "Space" } },
    "createdBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
    "createdAt": "2026-07-15T12:35:47.575Z",
    "updatedBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
    "updatedAt": "2026-07-15T12:35:47.575Z",
    "version": 1
  },
  "name": "t6-http",
  "directCallEnabled": true,
  "anonymousCallEnabled": false,
  "definition": {
    "method": "Post",
    "statements": [
      {
        "name": "resp",
        "method": "POST",
        "url": "https://postman-echo.com/post",
        "headers": [ { "key": "Content-Type", "value": "application/json", "secret": false } ],
        "body": { "prompt": "{ /payload/prompt }" },
        "timeoutMs": 10000,
        "retry": 0,
        "type": "Http"
      },
      {
        "value": { "status": "{ /resp/status }", "prompt": "{ /resp/body/json/prompt }" },
        "isError": false,
        "statusCode": 200,
        "type": "Return"
      }
    ]
  }
}
```

Key properties:

- `sys.id`: The unique identifier of the *Script*. It goes into `{scriptId}` in the single-read, update, delete, and execute paths.
- `name`: The name of the *Script* (1-64 characters). Used in the on-screen list and for management identification.
- `definition`: The `ScriptDefinition` that declares what this *Script* does. It consists of the call method (`method`), the `statements` array, and an optional payload schema (`payloadSchema`). Its detailed structure is covered in [Definition and name](#definition-and-name) below and in [the top-level structure in Script Overview](/api/reference/script.md#script-definition).
- `directCallEnabled`: Whether this *Script* can be called directly via `/execute` (boolean, `true` when omitted). If `false`, direct calls are rejected. Other paths for running this *Script* remain as they are: a *Webhook*'s linked action (`script`) and a *Scheduler* do not go through this endpoint, so they run it as usual.
- `anonymousCallEnabled`: Whether this *Script* can be called **without authentication** via `/execute/anonymous` (boolean, `false` when omitted). Turning it on lets a third party that cannot carry a token run this *Script* through that path, and the run is performed under the **author's identity**. The conditions and save-time rules are covered in [Anonymous calls](#anonymous-call) below.

Note that `sys` has no `status`, `publish`, or `archive`. A *Script* is not a resource that gets published to a delivery path; it is a resource you author and run through the management APIs.

## System properties (sys) {#system-properties-sys}

Every *Script* carries common system properties in the `sys` object. `space`, `createdBy`, and `updatedBy` are in the `Refer` shape (`{ "sys": { "id", "type": "Refer", "targetType" } }`).

| Property | Type | Description |
|---|---|---|
| `id` | string | Unique resource identifier. |
| `type` | string | Resource kind. For a *Script* this is always `"Script"`. |
| `space` | Refer&lt;Space&gt; | The *Space* this *Script* belongs to. |
| `createdBy` | Refer&lt;User&gt; | The user who created it. |
| `createdAt` | string (date-time) | Creation time. |
| `updatedBy` | Refer&lt;User&gt; | The user who last updated it. |
| `updatedAt` | string (date-time) | Last update time. |
| `version` | integer (≥1) | Resource version. It increases by 1 with every create and update. |

The `status` (publish status) and `publish` (publish history) found in the `sys` of *Content*, *Content Type*, and *Media* are **not present** on a *Script*, because a *Script* is not published. There is no `archive` property either. As a result, a *Script*'s `version` increases purely with the number of creates and updates, without any publishing.

## Definition and name (name, definition) {#definition-and-name}

A *Script* has four body properties: `name`, `definition`, `directCallEnabled`, and `anonymousCallEnabled`.

| Property | Required | Description |
|---|:---:|---|
| `name` | Required | The name of the *Script*. 1-64 characters. |
| `definition` | Required | `ScriptDefinition`. Made up of the keys in the table below. |
| `directCallEnabled` | Optional | Whether this *Script* can be called directly via `/execute`. Boolean, `true` when omitted. If `false`, direct calls are rejected. A *Webhook*'s linked action (`script`) and a *Scheduler* do not go through this endpoint, so they run it as usual. |
| `anonymousCallEnabled` | Optional | Whether this *Script* can be called without authentication via `/execute/anonymous`. Boolean, **`false` when omitted**. See [Anonymous calls](#anonymous-call) below. `PUT` is a full replacement, so omitting it returns the value to `false`. |

Keys of `definition` (`ScriptDefinition`):

| Key | Required | Description |
|---|:---:|---|
| `method` | Required | The HTTP method used to call this *Script*. One of `Get`, `Post`, `Put`, `Patch`, `Delete`. Execution is matched against this value. |
| `statements` | Required | An ordered array of statements to run. At least 1. |
| `payloadSchema` | Optional | A JSON Schema. If set, the request payload is validated against this schema before execution. |

The kinds and fields of each statement you put in the `statements` array are covered in [Statement catalog](/api/reference/script/statements.md), and the `{ /pointer }` expressions that pass values through are covered in [Value expressions](/api/reference/script/value-expressions.md).

In the "t6-http" example above, `definition` has `method` `Post`; it calls an external API with an `Http` statement and then returns the result with a `Return` statement. A statement that makes an external call, such as `Http`, declares its own share of time, and that much is added to the time one execution gets (see [How much time one execution gets](/api/reference/script.md#execution-budget)).

## Constraints {#constraints}

| Target | Constraint |
|---|---|
| `name` | 1-64 characters, required. |
| `definition.statements` | At least 1, required. |
| External calls (`Http`, `EmailSend`) per definition | Per plan (see [Pricing](/pricing/pricing.md)). |
| Total statements per definition | Per plan (see [Pricing](/pricing/pricing.md), including nested). |
| `SetVar` per definition | Up to 10 (default, including nested). |
| `Regex.pattern` | Up to 128 characters. |
| A definition with `anonymousCallEnabled` set to `true` | A `where` may not carry `createdBy: ":self"`. See [Anonymous calls](#anonymous-call) below. |
| A *Script* another resource references | Cannot be deleted. If a *Webhook* references the *Script* as a linked action, or a *Scheduler* references the *Script* as its execution target, the deletion is rejected, and the code you get back differs by which resource holds the reference (the same goes for a *Scheduler* that is switched off; see [Errors](#errors)). |

The static constraints above are checked **at save time (create/update)**, and a violation causes the save to be rejected. The number of external calls and the total number of statements are not validation errors but **plan limits**, so the same definition is allowed on a higher plan.

At save time, permissions and resource kinds are checked as well.

- Whether the author **actually holds the resource and action permissions** those statements use (if any one is missing, the save is rejected; see [Errors](/api/reference/script/execution-and-limits.md#errors)). A statement that reads members (*ServiceUser*) is checked not through a permission map but against `SETTING_SERVICE_LOGIN` in the *SpaceRole* `settings`.
- If a statement that **mutates** a member (*ServiceUser*) is present, the save is rejected. That resource is read-only from a *Script*, so no role can save it.

The detailed rules, the time budget, and the value length limits checked while running are covered in [Execution semantics, limits, and security](/api/reference/script/execution-and-limits.md).

A *Script* is a billable resource, and **the number per Organization** is limited by plan (Free 10 / Basic 30 / Pro 100 / Enterprise unlimited). Once the limit is reached, creating a new *Script* is rejected (see [per-plan count limits](/api/reference/script/execution-and-limits.md#plan-limits)).

## Anonymous calls (anonymousCallEnabled) {#anonymous-call}

Setting `anonymousCallEnabled` to `true` also makes that *Script* runnable through a **dedicated unauthenticated path**.

```
{method} https://script.weegloo.com/v1/spaces/{spaceId}/scripts/{scriptId}/execute/anonymous
```

**Needing this is rare.** It exists for a third party that has to send us a callback but **cannot carry an Access Token because it does not support custom headers**, as with some payment providers (PG, MoR). Every caller that can carry a token uses the authenticated path (`/execute`).

- **The authenticated path is untouched.** `/execute` still requires a Bearer token and the *Script* **Execute** permission. The only thing that becomes unauthenticated is this one path, `/execute/anonymous`.
- **It takes no token.** Even if a token is sent, it is ignored and the run is always under the author's identity. To run under the caller's identity, use `/execute`.
- **Both gates must pass.** If `anonymousCallEnabled` is `false`, the call is rejected as unauthenticated access; if `directCallEnabled` is `false`, it is rejected because direct calls are blocked. The code you get back depends on which gate caught the call (see [Errors](#errors)). Because anonymous permission is examined first, a caller with no standing cannot learn anything about the *Script*'s configuration.
- From there it is the same as `/execute`: the request HTTP method must match `definition.method`, and the call consumes the *Organization*'s *Script* execution quota and is metered as usage.
- This path is on the same Script host as the authenticated execution path (`https://script.weegloo.com/v1`).

### It runs as the author {#runs-as-the-author}

Because there is no caller, the run is performed under the identity of the user who **created** the *Script* (`sys.createdBy`).

- The `createdBy` and `updatedBy` of any *Content* or *Media* the *Script* creates or updates are set to the **author** (not the anonymous caller; there is no other identity to attribute them to).
- `createdBy: ":self"` in a `where` also resolves to the author rather than the caller. Leaving an ownership filter written for an authenticated caller in place and then turning anonymous on would silently open the author's own resources, so such a definition cannot be saved in the first place (see below).

### Additional save-time checks {#anonymous-save-rules}

A *Script* whose `anonymousCallEnabled` is `true` gets one more rule.

| Rule | Code |
|---|---|
| A `where` on `ResourceFind` or `ResourceForEach` may not carry `createdBy: ":self"` | See [Errors](/api/reference/script/execution-and-limits.md#errors) |

That is because an anonymous call has no caller identity, so `:self` [resolves to the author](#runs-as-the-author). Checking it at save time keeps an ownership filter written for an authenticated caller from being silently bypassed.

### The real authentication is what the Script does for itself {#anonymous-must-verify}

There is no platform-provided authentication on this path. **Anyone who knows the URL can call it, the call consumes the *Organization*'s *Script* execution quota, and there is no separate rate limit.** So an anonymous *Script* has to verify for itself what it was sent.

- Put a [`Signature`](/api/reference/script/statements.md#signature) first to check the signature over `{ /rawPayload }`, and cut the run short with `Return` if it does not pass. A complete example is in [Verifying a webhook signature in the cookbook](/api/reference/script/cookbook.md#verify-webhook-signature).
- Checking a replay window with `/now` as well blocks re-sending an earlier request (see [`/now`](/api/reference/script/value-expressions.md#now)).
- Put only what that callback actually has to do in an anonymous *Script*. A *Script* runs with the author's permissions delegated to it, so whatever you put in is what you opened without authentication (see [Security model](/api/reference/script/execution-and-limits.md#execution-identity-and-authorization)).

## ScriptLog {#script-log}

Each time a *Script* runs, one record is left behind. That record is a *ScriptLog*. It is read-only and has no create, update, or delete endpoints. Its path is `/spaces/{spaceId}/scripts/{scriptId}/logs`, and its base URL is not the execution host but CMA's `https://cma.weegloo.com/v1`. Reading it requires the `Read` permission on that *Script*.

```json
{
  "sys": {
    "id": "3trmXRM7pLdV5Rz8kWq2NcHfJt4bYs",
    "type": "ScriptLog",
    "space": { "sys": { "id": "6jSUUAWT", "type": "Refer", "targetType": "Space" } },
    "script": { "sys": { "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK", "type": "Refer", "targetType": "Script" } },
    "trigger": { "sys": { "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK", "type": "Refer", "targetType": "Script" } },
    "requestId": "3trmXRM9wTbK4Vz7hLp2QsNdRf6cYm",
    "returned": true,
    "value": { "status": 200, "prompt": "Summer dress product description, 3 lines" },
    "success": true,
    "statusCode": 200,
    "durationMs": 195,
    "createdBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
    "createdAt": "2026-07-15T12:41:03.902Z",
    "updatedBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
    "updatedAt": "2026-07-15T12:41:03.902Z"
  }
}
```

All values are inside `sys`, and there are no body properties. A key with no value is omitted from the response.

| Property | Type | Description |
|---|---|---|
| `id` | string | The unique identifier of the record. |
| `type` | string | Always `"ScriptLog"`. |
| `space` | Refer&lt;Space&gt; | The *Space* this record belongs to. |
| `script` | Refer&lt;Script&gt; | The *Script* that ran. |
| `trigger` | Refer | What set this run off. See the explanation below. |
| `requestId` | string | The identifier of this run. It is the same value as the `requestId` in the execution response envelope. |
| `returned` | boolean | Whether a `Return` statement was reached. |
| `value` | any | The value returned by the `Return` that was reached. An object, an array, or a scalar goes in as it is. If the run failed, the reason for the failure is carried here. |
| `success` | boolean | Whether it succeeded. |
| `statusCode` | integer | The status code set by the `Return` that was reached. |
| `durationMs` | integer | How long the run took, in milliseconds. |
| `createdBy` | Refer&lt;User&gt; or Refer&lt;ServiceUser&gt; | The identity this record is attributed to. See the explanation below. |
| `createdAt` | string (date-time) | The record creation time. |
| `updatedBy` | Refer&lt;User&gt; or Refer&lt;ServiceUser&gt; | The same as `createdBy`. |
| `updatedAt` | string (date-time) | The same as `createdAt`. |

`trigger` points to what set this run off. For a direct call it is **the *Script* itself**; if it ran through a *Webhook*'s linked action it is that *Webhook*; and if a *Scheduler* ran it, it is that *Scheduler*.

`requestId` is the same value as the `requestId` in the execution response envelope. To find the record of a run from the response the caller received, use this value as the reference.

The record is written once after the run ends and never changes. **A successful run disappears after 1 hour, and a failed run after 3 days.** For values you need to keep longer than that, save them as *Content* from inside the *Script*.

`createdBy` points to the identity the run was performed under. A run called with a Weegloo User token is that user, and a run called with a member (*ServiceUser*) token is that member. For a run with no caller, the identity comes from the trigger: an anonymous run is that *Script*'s author, a run started by a *Scheduler* is **that *Scheduler*'s creator** (who may differ from the *Script*'s author), and one a *Webhook* ran is **that *Webhook*'s creator**. A *Webhook*'s [runAs](/api/reference/cma/webhook.md#run-as) only decides whose name the work inside the *Script* is done under; it does not change this log's attribution.

## Errors {#errors}

These are the codes that come back when you call or delete a *Script*. The codes that come back when you save a definition are in [the errors of Execution semantics, limits, and security](/api/reference/script/execution-and-limits.md#errors), the codes for breaking the value expression rules are in [the errors of Value expressions](/api/reference/script/value-expressions.md#errors), and the codes that are common to every resource are in [common errors](/api/reference/common/errors.md).

| Code | Condition |
|---|---|
| `WGL422066` | A *Webhook* references the *Script* you are trying to delete as its linked action (the same goes for a *Webhook* that is switched off). |
| `WGL422110` | A *Scheduler* references the *Script* you are trying to delete as its execution target (the same goes for a *Scheduler* that is switched off). |
| `WGL401001` | A *Script* whose `anonymousCallEnabled` is `false` was called through the anonymous execution path (`/execute/anonymous`). |
| `WGL422062` | A *Script* whose `directCallEnabled` is `false` was called directly through an execution path (`/execute`, `/execute/anonymous`). |
| `WGL400007` | The HTTP method of the execution request differs from that *Script*'s `definition.method`. A request body that was sent but is not a JSON object, and a body that does not satisfy the schema on a *Script* that has a `definition.payloadSchema`, are rejected with the same code as well. |
| `WGL408002` | The execution exceeded its time budget and was stopped. The execution record up to that point remains in the *ScriptLog*. |

## API {#api}

The base URL for the five endpoints below (list, read, create, update, delete) is CMA's `https://cma.weegloo.com/v1`, and a Bearer token that authenticates against CMA is required in the `Authorization` header. Update must also send the `X-Weegloo-Version` header (the current resource's `sys.version`) for optimistic concurrency control. The two *ScriptLog* reads at the very bottom use the same CMA base URL.

The base URL for the two execution endpoints is the dedicated Script host, `https://script.weegloo.com/v1`. Authenticated execution (`/execute`) accepts **both** a Bearer token of a Weegloo User identity and a Bearer token of a member (*ServiceUser*) identity, and either way the caller needs the **Execute** permission on that *Script*.

Anonymous execution (`/execute/anonymous`) is the one exception that **does not require an authentication header.** It is on the same Script host, and it is reachable only when that *Script* has turned `anonymousCallEnabled` on (see [Anonymous calls](#anonymous-call) above).

```api-endpoint
{
  "title": "List Scripts",
  "method": "GET",
  "path": "/spaces/{spaceId}/scripts",
  "description": "Reads the list of Scripts in a Space, page by page. Each item carries sys, name, and definition.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true }
  },
  "queryParameterSchema": {
    "limit": { "type": "integer", "description": "Number of items to return per page (1-100)", "default": 15 },
    "skip": { "type": "integer", "description": "Number of items to skip", "default": 0 },
    "next": { "type": "string", "description": "Next page cursor (the value obtained from links.next in the previous response)" },
    "prev": { "type": "string", "description": "Previous page cursor (the value obtained from links.prev in the previous response)" },
    "order": { "type": "string", "description": "Sort criteria. Comma-separated for multi-level sorting (e.g. sys.createdAt,sys.id)", "default": "sys.createdAt,sys.id" },
    "select": { "type": "string", "description": "Fields to include (sys.id,name) or exclude (-definition). Do not mix include and exclude." }
  },
  "additionalQueryParams": true,
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "sys": { "type": "TotalPageResponse" },
    "limit": 15,
    "totalCount": 2,
    "items": [
      {
        "sys": {
          "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK",
          "type": "Script",
          "space": { "sys": { "id": "6jSUUAWT", "type": "Refer", "targetType": "Space" } },
          "createdBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
          "createdAt": "2026-07-15T12:35:47.575Z",
          "updatedAt": "2026-07-15T12:35:47.575Z",
          "updatedBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
          "version": 1
        },
        "name": "t6-http",
        "directCallEnabled": true,
        "anonymousCallEnabled": false,
        "definition": {
          "method": "Post",
          "statements": [
            { "name": "resp", "method": "POST", "url": "https://postman-echo.com/post", "headers": [ { "key": "Content-Type", "value": "application/json", "secret": false } ], "body": { "prompt": "{ /payload/prompt }" }, "timeoutMs": 10000, "retry": 0, "type": "Http" },
            { "value": { "status": "{ /resp/status }", "prompt": "{ /resp/body/json/prompt }" }, "isError": false, "statusCode": 200, "type": "Return" }
          ]
        }
      },
      {
        "sys": {
          "id": "3trmXRM3qajH3UU7bY5yuTmUbSuoTc",
          "type": "Script",
          "space": { "sys": { "id": "6jSUUAWT", "type": "Refer", "targetType": "Space" } },
          "createdBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
          "createdAt": "2026-07-15T23:27:59.005Z",
          "updatedAt": "2026-07-15T23:28:23.108Z",
          "updatedBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
          "version": 2
        },
        "name": "order-webhook-notify",
        "directCallEnabled": true,
        "anonymousCallEnabled": false,
        "definition": {
          "method": "Post",
          "statements": [
            { "name": "notify", "method": "POST", "url": "https://hooks.example.com/orders", "headers": [ { "key": "Content-Type", "value": "application/json", "secret": false } ], "body": { "orderId": "{ /payload/orderId }", "status": "{ /payload/status }", "customerId": "{ /payload/customerId }" }, "timeoutMs": 15000, "retry": 0, "type": "Http" }
          ]
        }
      }
    ],
    "links": { "self": "/v1/spaces/6jSUUAWT/scripts" }
  }
}
```

```api-endpoint
{
  "title": "Create Script",
  "method": "POST",
  "path": "/spaces/{spaceId}/scripts",
  "description": "Creates a new Script in a Space. At save time it checks the static constraints and the author's permissions; if they pass, it returns a resource with version 1. Unlike Content, there is no separate publish step.",
  "responseStatus": 201,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "requestBodySchema": {
    "type": "object",
    "required": ["name", "definition"],
    "properties": {
      "name": { "type": "string", "minLength": 1, "maxLength": 64, "description": "Script name" },
      "directCallEnabled": { "type": "boolean", "default": true, "description": "Whether this Script can be called directly via /execute. If false, direct calls are rejected. A Webhook's linked action and a Scheduler do not go through this endpoint, so they run it as usual. Optional; true when omitted. Since PUT is a full replace, it is reset to true when omitted." },
      "anonymousCallEnabled": { "type": "boolean", "default": false, "description": "Whether this Script can be called without authentication via /execute/anonymous. When on, it runs under the author's identity. Optional; false when omitted. Since PUT is a full replace, it is reset to false when omitted. When true, a where may not carry createdBy :self." },
      "definition": {
        "type": "object",
        "required": ["method", "statements"],
        "description": "ScriptDefinition. For the kinds of statements and value expressions, see the Statement catalog and Value expressions documents.",
        "properties": {
          "method": { "type": "string", "enum": ["Get", "Post", "Put", "Patch", "Delete"], "description": "The HTTP method used to call this Script" },
          "payloadSchema": { "type": "object", "description": "Optional. If present, the request payload is validated against this JSON Schema before execution" },
          "statements": { "type": "array", "minItems": 1, "description": "The array of statements to run from top to bottom", "items": { "type": "object" } }
        }
      }
    },
    "example": {
      "name": "order-webhook-notify",
      "directCallEnabled": true,
      "anonymousCallEnabled": false,
      "definition": {
        "method": "Post",
        "statements": [
          { "type": "Http", "method": "POST", "url": "https://hooks.example.com/orders", "headers": [ { "key": "Content-Type", "value": "application/json", "secret": false } ], "body": { "orderId": "{ /payload/orderId }", "status": "{ /payload/status }" }, "timeoutMs": 10000, "name": "notify" }
        ]
      }
    }
  },
  "responseExample": {
    "sys": {
      "id": "3trmXRM3qajH3UU7bY5yuTmUbSuoTc",
      "type": "Script",
      "space": { "sys": { "id": "6jSUUAWT", "type": "Refer", "targetType": "Space" } },
      "createdBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-07-15T23:27:59.005Z",
      "updatedBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
      "updatedAt": "2026-07-15T23:27:59.005Z",
      "version": 1
    },
    "name": "order-webhook-notify",
    "directCallEnabled": true,
    "anonymousCallEnabled": false,
    "definition": {
      "method": "Post",
      "statements": [
        { "name": "notify", "method": "POST", "url": "https://hooks.example.com/orders", "headers": [ { "key": "Content-Type", "value": "application/json", "secret": false } ], "body": { "orderId": "{ /payload/orderId }", "status": "{ /payload/status }" }, "timeoutMs": 10000, "retry": 0, "type": "Http" }
      ]
    }
  }
}
```

```api-endpoint
{
  "title": "Read a single Script",
  "method": "GET",
  "path": "/spaces/{spaceId}/scripts/{scriptId}",
  "description": "Reads one Script by sys.id.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "scriptId": { "type": "string", "description": "The sys.id of the Script", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "sys": {
      "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK",
      "type": "Script",
      "space": { "sys": { "id": "6jSUUAWT", "type": "Refer", "targetType": "Space" } },
      "createdBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-07-15T12:35:47.575Z",
      "updatedBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
      "updatedAt": "2026-07-15T12:35:47.575Z",
      "version": 1
    },
    "name": "t6-http",
    "directCallEnabled": true,
    "anonymousCallEnabled": false,
    "definition": {
      "method": "Post",
      "statements": [
        { "name": "resp", "method": "POST", "url": "https://postman-echo.com/post", "headers": [ { "key": "Content-Type", "value": "application/json", "secret": false } ], "body": { "prompt": "{ /payload/prompt }" }, "timeoutMs": 10000, "retry": 0, "type": "Http" },
        { "value": { "status": "{ /resp/status }", "prompt": "{ /resp/body/json/prompt }" }, "isError": false, "statusCode": 200, "type": "Return" }
      ]
    }
  }
}
```

```api-endpoint
{
  "title": "Update Script (full replace)",
  "method": "PUT",
  "path": "/spaces/{spaceId}/scripts/{scriptId}",
  "description": "Replaces the entire Script. You must include both name and definition in the body; partial update (PATCH) that changes only part of it is not supported. On success, version goes up by 1.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "scriptId": { "type": "string", "description": "The sys.id of the Script", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" },
    "X-Weegloo-Version": { "type": "integer", "description": "The current resource's sys.version (optimistic concurrency control)" }
  },
  "requestBodySchema": {
    "type": "object",
    "required": ["name", "definition"],
    "properties": {
      "name": { "type": "string", "minLength": 1, "maxLength": 64, "description": "Script name" },
      "directCallEnabled": { "type": "boolean", "default": true, "description": "Whether this Script can be called directly via /execute. If false, direct calls are rejected. A Webhook's linked action and a Scheduler do not go through this endpoint, so they run it as usual. Optional; true when omitted. Since PUT is a full replace, it is reset to true when omitted." },
      "anonymousCallEnabled": { "type": "boolean", "default": false, "description": "Whether this Script can be called without authentication via /execute/anonymous. When on, it runs under the author's identity. Optional; false when omitted. Since PUT is a full replace, it is reset to false when omitted. When true, a where may not carry createdBy :self." },
      "definition": {
        "type": "object",
        "required": ["method", "statements"],
        "description": "The entire ScriptDefinition. This is a full replace, not a partial update.",
        "properties": {
          "method": { "type": "string", "enum": ["Get", "Post", "Put", "Patch", "Delete"] },
          "payloadSchema": { "type": "object", "description": "Optional" },
          "statements": { "type": "array", "minItems": 1, "items": { "type": "object" } }
        }
      }
    },
    "example": {
      "name": "order-webhook-notify",
      "directCallEnabled": true,
      "anonymousCallEnabled": false,
      "definition": {
        "method": "Post",
        "statements": [
          { "type": "Http", "method": "POST", "url": "https://hooks.example.com/orders", "headers": [ { "key": "Content-Type", "value": "application/json", "secret": false } ], "body": { "orderId": "{ /payload/orderId }", "status": "{ /payload/status }", "customerId": "{ /payload/customerId }" }, "timeoutMs": 15000, "name": "notify" }
        ]
      }
    }
  },
  "responseExample": {
    "sys": {
      "id": "3trmXRM3qajH3UU7bY5yuTmUbSuoTc",
      "type": "Script",
      "space": { "sys": { "id": "6jSUUAWT", "type": "Refer", "targetType": "Space" } },
      "createdBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-07-15T23:27:59.005Z",
      "updatedBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
      "updatedAt": "2026-07-15T23:28:23.108Z",
      "version": 2
    },
    "name": "order-webhook-notify",
    "directCallEnabled": true,
    "anonymousCallEnabled": false,
    "definition": {
      "method": "Post",
      "statements": [
        { "name": "notify", "method": "POST", "url": "https://hooks.example.com/orders", "headers": [ { "key": "Content-Type", "value": "application/json", "secret": false } ], "body": { "orderId": "{ /payload/orderId }", "status": "{ /payload/status }", "customerId": "{ /payload/customerId }" }, "timeoutMs": 15000, "retry": 0, "type": "Http" }
      ]
    }
  }
}
```

```api-endpoint
{
  "title": "Delete Script",
  "method": "DELETE",
  "path": "/spaces/{spaceId}/scripts/{scriptId}",
  "description": "Deletes a Script. On success it responds with 204 No Content and no response body. Because a Script is not a published resource, it is deleted right away without a prior step such as unpublishing, and deleting it frees up one slot of the billable count limit.",
  "responseStatus": 204,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "scriptId": { "type": "string", "description": "The sys.id of the Script", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  }
}
```

```api-endpoint
{
  "title": "Execute Script",
  "method": "POST",
  "path": "/spaces/{spaceId}/scripts/{scriptId}/execute",
  "description": "Executes a Script under the identity of the authenticated caller (the caller needs the Script Execute permission, and the createdBy of any resource it creates is the caller as well; a third party that cannot carry a token uses the anonymous execution endpoint below instead). This one path accepts both a Weegloo User Bearer token and a ServiceUser Bearer token. The Script's directCallEnabled must be true; if it is false, this endpoint cannot be used for a direct call (a Webhook's linked action and a Scheduler do not go through this endpoint, so they run it as usual). The request's HTTP method must match the Script's definition.method. This example executes a Script whose method is Post with POST. Execution always happens inline on this request path, and it returns 200 with the execution response envelope. There is no path that only accepts the request and hands the result over later. For the shape of the response, see the request and response section in Script Overview. The request body (payload) is optional; if the definition has a payloadSchema, it is validated before execution. The body must be a JSON object. An empty body is treated as no body, and one that is not an object (broken JSON, an array, a scalar, a literal null) is rejected without executing. Inside the script the parsed value is referenced as { /payload/... } and the raw body string as sent is referenced as { /rawPayload } (needed when computing over the bytes as sent, as in signature verification). This request's HTTP headers can be referenced as { /headers/<name> } (keys are lowercase; see Value expressions).",
  "responseStatus": 200,
  "baseUrl": "https://script.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "scriptId": { "type": "string", "description": "The sys.id of the Script", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token. Both a Weegloo User token and a ServiceUser token are accepted, and either way the Execute permission on that Script is required" }
  },
  "requestBodySchema": {
    "type": "object",
    "description": "The payload (a JSON object) to pass to the Script. From statements, the parsed value is referenced as { /payload/... } and the raw body string as { /rawPayload }. It can also be called without a body.",
    "example": { "orderId": "ORD-20260716-0007", "status": "paid" }
  },
  "responseExample": {
    "requestId": "3trmXRM3qajH3UU7bY5yuYc91kKoqv",
    "durationMs": 195,
    "statusCode": 200
  }
}
```

```api-endpoint
{
  "title": "Execute a Script anonymously (no authentication)",
  "method": "POST",
  "path": "/spaces/{spaceId}/scripts/{scriptId}/execute/anonymous",
  "description": "Executes a Script without authentication. This path exists for a third party that cannot carry an Access Token because it does not support custom headers (a payment provider's callback, for example); in every other case use /execute. Both anonymousCallEnabled and directCallEnabled must be true on the Script: calling this path for a Script that does not allow anonymous invocation, or one whose directCallEnabled is false, is rejected. No Authorization header is needed, and one that is sent is ignored. The execution identity is always the Script's author (sys.createdBy), so the createdBy of resources the Script creates is the author as well, and createdBy :self in a where resolves to the author. The response is 200 with the execution response envelope. The request HTTP method must match the Script's definition.method. There is no platform authentication on this path, so the Script must verify the request for itself (for example a Signature statement over /rawPayload).",
  "responseStatus": 200,
  "baseUrl": "https://script.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "scriptId": { "type": "string", "description": "The sys.id of the Script", "required": true }
  },
  "requestHeaderSchema": {},
  "requestBodySchema": {
    "type": "object",
    "description": "The payload to pass to the Script (a JSON object). In statements, the parsed value is referenced as { /payload/... } and the raw body string as { /rawPayload }. Signature verification must run over the raw body.",
    "example": { "id": "evt_1", "type": "charge.succeeded", "data": { "orderId": "ORD-20260824-0007" } }
  },
  "responseExample": {
    "requestId": "3trmXRM3qajH3UU7bY5yuYc91kKoqv",
    "durationMs": 42,
    "statusCode": 200,
    "return": { "ok": true }
  }
}
```

The response in the authenticated execution example above has no `return`, because the target *Script* finished without reaching a `Return` that carries a value (in which case `statusCode` defaults to 200). When a `Return` hands a value back, as in the anonymous execution example, the response carries `return` (or `error` if `Return.isError` is true). The full rules for the response are covered in [the request and response section in Script Overview](/api/reference/script.md#request-response-envelope).

```api-endpoint
{
  "title": "List ScriptLogs",
  "method": "GET",
  "path": "/spaces/{spaceId}/scripts/{scriptId}/logs",
  "description": "Reads the execution records of one Script, page by page. The Read permission on that Script is required. You can add filters yourself with queries that are not in the schema below (see the common query parameters document). The record of a successful run disappears after 1 hour, and the record of a failed run after 3 days.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "scriptId": { "type": "string", "description": "The sys.id of the Script", "required": true }
  },
  "queryParameterSchema": {
    "limit": { "type": "integer", "description": "Number of items to return per page (1-100)", "default": 15 },
    "skip": { "type": "integer", "description": "Number of items to skip", "default": 0 },
    "next": { "type": "string", "description": "Next page cursor (the value obtained from links.next in the previous response)" },
    "prev": { "type": "string", "description": "Previous page cursor (the value obtained from links.prev in the previous response)" },
    "order": { "type": "string", "description": "Sort criteria. Comma-separated for multi-level sorting", "default": "sys.createdAt,sys.id" },
    "select": { "type": "string", "description": "Fields to include (sys.id) or exclude (-sys.value). Do not mix include and exclude." },
    "include": { "type": "integer", "description": "Related-resource inclusion level. 0=default, 1=related resources, 2=nested relations, 3=full", "default": 0 }
  },
  "additionalQueryParams": true,
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "sys": { "type": "TotalPageResponse" },
    "limit": 15,
    "totalCount": 1,
    "items": [
      {
        "sys": {
          "id": "3trmXRM7pLdV5Rz8kWq2NcHfJt4bYs",
          "type": "ScriptLog",
          "space": { "sys": { "id": "6jSUUAWT", "type": "Refer", "targetType": "Space" } },
          "script": { "sys": { "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK", "type": "Refer", "targetType": "Script" } },
          "trigger": { "sys": { "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK", "type": "Refer", "targetType": "Script" } },
          "requestId": "3trmXRM9wTbK4Vz7hLp2QsNdRf6cYm",
          "returned": true,
          "value": { "status": 200, "prompt": "Summer dress product description, 3 lines" },
          "success": true,
          "statusCode": 200,
          "durationMs": 195,
          "createdBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
          "createdAt": "2026-07-15T12:41:03.902Z",
          "updatedBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
          "updatedAt": "2026-07-15T12:41:03.902Z"
        }
      }
    ],
    "links": { "self": "/v1/spaces/6jSUUAWT/scripts/3trmXRMZcTAjDnphewjj1AaxYcaxlK/logs" }
  }
}
```

```api-endpoint
{
  "title": "Read a single ScriptLog",
  "method": "GET",
  "path": "/spaces/{spaceId}/scripts/{scriptId}/logs/{scriptLogId}",
  "description": "Reads one execution record. The Read permission on that Script is required. A record past its retention period is gone, so it cannot be read.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "scriptId": { "type": "string", "description": "The sys.id of the Script", "required": true },
    "scriptLogId": { "type": "string", "description": "The sys.id of the ScriptLog", "required": true }
  },
  "queryParameterSchema": {
    "include": { "type": "integer", "description": "Related-resource inclusion level. 0=default, 1=related resources, 2=nested relations, 3=full", "default": 0 }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "sys": {
      "id": "3trmXRM7pLdV5Rz8kWq2NcHfJt4bYs",
      "type": "ScriptLog",
      "space": { "sys": { "id": "6jSUUAWT", "type": "Refer", "targetType": "Space" } },
      "script": { "sys": { "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK", "type": "Refer", "targetType": "Script" } },
      "trigger": { "sys": { "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK", "type": "Refer", "targetType": "Script" } },
      "requestId": "3trmXRM9wTbK4Vz7hLp2QsNdRf6cYm",
      "returned": true,
      "value": { "status": 200, "prompt": "Summer dress product description, 3 lines" },
      "success": true,
      "statusCode": 200,
      "durationMs": 195,
      "createdBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-07-15T12:41:03.902Z",
      "updatedBy": { "sys": { "id": "3p4tcFbQYJNvYTBJf2rYKr42xegQLJ", "type": "Refer", "targetType": "User" } },
      "updatedAt": "2026-07-15T12:41:03.902Z"
    }
  }
}
```

## Related documents {#related-documents}

- [Script Overview](/api/reference/script.md): Covers the top-level `ScriptDefinition` structure, request and response, and how much time one execution gets.
- [Statement catalog](/api/reference/script/statements.md): Covers the fields and results of each statement you put in `statements`.
- [Value expressions](/api/reference/script/value-expressions.md): Covers `{ /pointer }` references and JsonLogic operations.
- [Execution semantics, limits, and security](/api/reference/script/execution-and-limits.md): Covers static constraints, per-plan count limits, and the permission and security model.
- [SpaceRole](/api/reference/cma/space-role.md) and [ServiceUserRole](/api/reference/cma/service-user-role.md): Cover how to grant a *Script*'s action permissions (including `Execute`) to a role.
