Cookbook (Worked Examples)

Last updated: July 17, 2026

Each scenario is shown as a complete ScriptDefinition. For the syntax they build on, see the Statement Catalog and Value Expressions; for execution and constraints, see Execution Semantics, Constraints, and Security. In every example the write fields value is a locale map ({ "<locale>": value }), and the example locale is standardized on en-US. Examples that make an external call (Http) or ingest a Media file have executionMode set to "Async".

Table of Contents

Basic CRUD

1. Create and publish Content

{ "method": "Post", "executionMode": "Sync",
  "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 } ] }

2. Update with a computed value (view count +1)

{ "method": "Post", "executionMode": "Sync",
  "statements": [
    { "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
      "fields": { "viewCount": { "en-US": { "+": [ "{ /payload/fields/viewCount }", 1 ] } } } } ] }

3. Read-only GET: my order list

{ "method": "Get", "executionMode": "Sync",
  "statements": [
    { "type": "ResourcePageRead", "resource": "Content", "contentType": { "sys": { "id": "ct_order" } },
      "where": { "createdBy": ":self" }, "order": "-sys.createdAt", "limit": 20, "name": "orders" },
    { "type": "Return", "value": { "orders": "{ /orders/items }", "next": "{ /orders/next }" } } ] }

Script is used not only for writing but also as a read BFF endpoint. With createdBy: ":self" it fetches "only mine" and returns the result as-is.

4. Read one, guard, then approve

{ "method": "Post", "executionMode": "Sync",
  "statements": [
    { "type": "ResourceRead", "resource": "Content", "target": { "sys": { "id": "{ /payload/fields/orderId }" } }, "name": "order" },
    { "type": "If", "condition": { "!=": [ "{ /order/fields/status/en-US }", "pending" ] },
      "then": [ { "type": "Return", "value": { "reason": "not pending" }, "isError": true, "statusCode": 409 } ] },
    { "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /order/sys/id }" } },
      "fields": { "status": { "en-US": "approved" } }, "publish": true },
    { "type": "Return", "value": { "ok": true } } ] }

Binding a single item to a name with ResourceRead lets you reference it directly as { /order/fields/... } (no items/0 needed). If it does not exist, the read errors out (you can wrap it in Try).

Lookup and upsert

5. slug upsert (find-then-upsert)

{ "method": "Post", "executionMode": "Sync",
  "statements": [
    { "type": "ResourceFind", "resource": "Content", "contentType": { "sys": { "id": "ct_article" } },
      "where": { "fields.slug": { "eq": "{ /payload/fields/slug }" } }, "name": "found" },
    { "type": "If", "condition": { "!!": "{ /found/sys/id }" },
      "then": [
        { "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /found/sys/id }" } },
          "fields": { "body": { "en-US": "{ /payload/fields/body }" } } },
        { "type": "Return", "value": { "id": "{ /found/sys/id }", "op": "updated" } } ],
      "else": [
        { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_article" } },
          "fields": { "slug": { "en-US": "{ /payload/fields/slug }" }, "body": { "en-US": "{ /payload/fields/body }" } }, "name": "created" },
        { "type": "Return", "value": { "id": "{ /created/sys/id }", "op": "created" }, "statusCode": 201 } ] } ] }

ResourceFind binds the first match directly (or null if there is none), and { "!!": "{ /found/sys/id }" } branches on whether it exists.

6. Dynamic field key and dynamic locale patch

{ "method": "Patch", "executionMode": "Sync",
  "statements": [
    { "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
      "fields": { "{ /payload/fields/fieldKey }": { "{ /payload/fields/locale }": "{ /payload/fields/value }" } } } ] }

Both the field key and the locale bucket key are { /ptr } references. Use this when you want to place a translation into a specific locale bucket.

External API

7. Credit guard, charge up front (CAS), LLM call, refund on failure (flagship example)

{ "method": "Post", "executionMode": "Async",
  "statements": [
    { "type": "ResourceFind", "resource": "Content", "contentType": { "sys": { "id": "ct_wallet" } },
      "where": { "createdBy": ":self" }, "name": "wallet" },
    { "type": "If", "condition": { "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] },
      "then": [ { "type": "Return", "value": { "ok": false, "reason": "insufficient credit" }, "isError": true, "statusCode": 402 } ] },
    { "type": "Try",
      "body": [
        { "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /wallet/sys/id }" } },
          "version": "{ /wallet/sys/version }",
          "fields": { "balance": { "en-US": { "-": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] } } },
          "name": "charged" } ],
      "catch": [ { "type": "Return", "value": { "ok": false, "reason": "version conflict, retry" }, "isError": true, "statusCode": 409 } ] },
    { "type": "Try",
      "body": [
        { "type": "Http", "method": "POST", "url": "https://api.llm.com/v1/gen",
          "headers": [ { "key": "Authorization", "value": "Bearer sk-...", "secret": true } ],
          "body": { "prompt": "{ /payload/fields/prompt }" }, "timeoutMs": 15000, "name": "resp" },
        { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_result" } },
          "fields": { "text": { "en-US": "{ /resp/body/choices/0/message/content }" } }, "name": "out" },
        { "type": "Return", "value": { "ok": true, "id": "{ /out/sys/id }", "remaining": "{ /charged/fields/balance/en-US }" } } ],
      "catch": [
        { "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /wallet/sys/id }" } },
          "fields": { "balance": { "en-US": { "+": [ "{ /charged/fields/balance/en-US }", "{ /payload/fields/cost }" ] } } } },
        { "type": "Return", "value": { "ok": false, "reason": "generation failed, refunded" }, "isError": true, "statusCode": 502 } ] } ] }

First a guard checks whether the balance is sufficient, then it deducts before the external call. The deduction takes an optimistic lock (CAS) on the wallet's sys.version. If another execution changed the wallet between reading the balance and deducting, it aborts on a version mismatch and catch returns 409. Because no external call has been made, concurrent requests are not double-charged. Only after the deduction is committed does it call the LLM, and if that call fails, catch adds the deducted amount (cost) back to issue a refund (compensation), then returns 502. The order is to commit the charge before the irreversible external call and to compensate only on failure. The secret key goes in a secret:true header. For the limits of compensation, see No transactions and compensation in Execution Semantics.

8. Turn an image (URL) into Media and attach it to Content

{ "method": "Post", "executionMode": "Async",
  "statements": [
    { "type": "Http", "method": "POST", "url": "https://api.img.com/gen",
      "headers": [ { "key": "x-api-key", "value": "...", "secret": true } ],
      "body": { "prompt": "{ /payload/fields/prompt }" }, "name": "gen" },
    { "type": "ResourceCreate", "resource": "Media",
      "fields": { "title": { "en-US": "{ /payload/fields/prompt }" },
                  "file":  { "en-US": { "source": "{ /gen/body/data/0/url }", "encoding": "url" } } }, "name": "img" },
    { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_artwork" } },
      "fields": { "prompt": { "en-US": "{ /payload/fields/prompt }" }, "image": { "en-US": "{ /img/sys/id }" } } } ] }

Create the Media with a name, and ResourceCreate (Content) places { /img/sys/id } into the reference field. Media uses the same fields model as Content. file is the ingest directive { source, encoding }. Because it is a file ingest, it is Async.

9. base64 image to Media

{ "method": "Post", "executionMode": "Async",
  "statements": [
    { "type": "Http", "method": "POST", "url": "https://api.img.com/gen",
      "headers": [ { "key": "x-api-key", "value": "...", "secret": true } ],
      "body": { "prompt": "{ /payload/fields/prompt }" }, "name": "gen" },
    { "type": "ResourceCreate", "resource": "Media",
      "fields": { "file": { "en-US": { "source": "{ /gen/body/data/0/b64_json }", "encoding": "base64" } } } } ] }

10. Conditional publish or delete after moderation

{ "method": "Post", "executionMode": "Async",
  "statements": [
    { "type": "Http", "method": "POST", "url": "https://api.mod.com/check",
      "headers": [ { "key": "x-api-key", "value": "...", "secret": true } ],
      "body": { "text": "{ /payload/fields/body }" }, "name": "mod" },
    { "type": "If", "condition": { "==": [ "{ /mod/body/flagged }", true ] },
      "then": [ { "type": "ResourceDelete",  "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } } ],
      "else": [ { "type": "ResourcePublish", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } } ] } ] }

11. try/catch: fallback on external failure

{ "method": "Post", "executionMode": "Async",
  "statements": [
    { "type": "Try",
      "body": [
        { "type": "Http", "method": "POST", "url": "https://primary.api/gen",
          "headers": [ { "key": "x-api-key", "value": "...", "secret": true } ],
          "body": { "prompt": "{ /payload/fields/prompt }" }, "timeoutMs": 8000, "name": "resp" },
        { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_result" } },
          "fields": { "text": { "en-US": "{ /resp/body/text }" }, "source": { "en-US": "primary" } } } ],
      "catch": [
        { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_result" } },
          "fields": { "text": { "en-US": "Generation failed" }, "error": { "en-US": "{ /error/message }" }, "source": { "en-US": "fallback" } } } ] } ] }

Parallel

12. Merge two parallel external calls into Content

{ "method": "Post", "executionMode": "Async",
  "statements": [
    { "type": "Parallel", "branches": [
      [ { "type": "Http", "method": "GET", "url": "https://api.a.com/x", "name": "a" } ],
      [ { "type": "Http", "method": "GET", "url": "https://api.b.com/y", "name": "b" } ] ] },
    { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_merged" } },
      "fields": { "left": { "en-US": "{ /a/body/value }" }, "right": { "en-US": "{ /b/body/value }" } } } ] }

13. Signup review: parallel scores, then an and-based decision

{ "method": "Post", "executionMode": "Async",
  "statements": [
    { "type": "Parallel", "branches": [
      [ { "type": "Http", "method": "POST", "url": "https://api.fraud.com/score",
          "headers": [ { "key": "x-api-key", "value": "...", "secret": true } ],
          "body": { "email": "{ /payload/fields/email }" }, "name": "fraud" } ],
      [ { "type": "Http", "method": "GET", "url": "https://api.credit.com/v1/{ /payload/fields/userId }/score",
          "headers": [ { "key": "x-api-key", "value": "...", "secret": true } ], "name": "credit" } ] ] },
    { "type": "If",
      "condition": { "and": [ { "<": [ "{ /fraud/body/risk }", 0.5 ] }, { ">=": [ "{ /credit/body/score }", 700 ] } ] },
      "then": [
        { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_account" } },
          "fields": { "email": { "en-US": "{ /payload/fields/email }" }, "status": { "en-US": "approved" } } },
        { "type": "Return", "value": { "decision": "approved" }, "statusCode": 201 } ],
      "else": [ { "type": "Return", "value": { "decision": "manual-review" }, "statusCode": 202 } ] } ] }

You can also insert { /ptr } into the URL path. Branch results are referenced after the join. There are two external calls (at most three).

Loops and aggregation

14. N Content items from an array input (Loop over)

{ "method": "Post", "executionMode": "Sync",
  "statements": [
    { "type": "Loop", "over": "{ /payload/fields/items }", "as": "item", "maxIterations": 100,
      "body": [
        { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_item" } },
          "fields": { "name": { "en-US": "{ /item/name }" }, "qty": { "en-US": "{ /item/qty }" } } } ] } ] }

15. Counted loop (for): seed slots

{ "method": "Post", "executionMode": "Sync",
  "statements": [
    { "type": "Loop", "for": { "from": 1, "to": 5 }, "as": "i", "maxIterations": 100,
      "body": [
        { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_slot" } },
          "fields": { "index": { "en-US": "{ /i }" }, "status": { "en-US": "open" } } } ] } ] }

for covers from to to, inclusive (integer literals, step defaults to 1). as binds the current counter to { /i }.

16. Cascade delete (PageRead, Loop, Delete)

{ "method": "Delete", "executionMode": "Sync",
  "statements": [
    { "type": "ResourcePageRead", "resource": "Content", "contentType": { "sys": { "id": "ct_comment" } },
      "where": { "fields.postId": { "eq": "{ /payload/sys/id }" } }, "limit": 100, "name": "comments" },
    { "type": "Loop", "over": "{ /comments/items }", "as": "c", "maxIterations": 100,
      "body": [ { "type": "ResourceDelete", "resource": "Content", "target": { "sys": { "id": "{ /c/sys/id }" } } } ] },
    { "type": "ResourceDelete", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } } ] }

For more than 100, handle it with 18. Paginate through everything.

17. Loop accumulation: SetVar sum

{ "method": "Post", "executionMode": "Sync",
  "statements": [
    { "type": "SetVar", "var": "total", "value": 0 },
    { "type": "Loop", "over": "{ /payload/fields/items }", "as": "row", "maxIterations": 100,
      "body": [ { "type": "SetVar", "var": "total", "value": { "+": [ "{ /vars/total }", "{ /row/qty }" ] } } ] },
    { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_summary" } },
      "fields": { "totalQty": { "en-US": "{ /vars/total }" } } } ] }

18. Paginate through everything

{ "method": "Post", "executionMode": "Sync",
  "statements": [
    { "type": "SetVar", "var": "cursor",  "value": null },
    { "type": "SetVar", "var": "hasMore", "value": true },
    { "type": "Loop", "while": "{ /vars/hasMore }", "maxIterations": 1000,
      "body": [
        { "type": "ResourcePageRead", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
          "where": { "fields.status": { "eq": "draft" } }, "limit": 100, "cursor": "{ /vars/cursor }", "name": "page" },
        { "type": "Loop", "over": "{ /page/items }", "as": "p", "maxIterations": 100,
          "body": [ { "type": "ResourcePublish", "resource": "Content", "target": { "sys": { "id": "{ /p/sys/id }" } } } ] },
        { "type": "SetVar", "var": "cursor",  "value": "{ /page/next }" },
        { "type": "SetVar", "var": "hasMore", "value": { "!!": "{ /page/next }" } } ] } ] }

cursor, while, and SetVar walk through every page. Since external calls are forbidden inside a Loop body, only resource operations are used here.

19. Batch-collect ids from an email list (merge)

{ "method": "Post", "executionMode": "Sync",
  "statements": [
    { "type": "SetVar", "var": "ids",     "value": [] },
    { "type": "SetVar", "var": "missing", "value": [] },
    { "type": "Loop", "over": "{ /payload/fields/emails }", "as": "email", "maxIterations": 100,
      "body": [
        { "type": "ResourceFind", "resource": "Content", "contentType": { "sys": { "id": "ct_account" } },
          "where": { "fields.email": { "eq": "{ /email }" } }, "name": "acc" },
        { "type": "If", "condition": { "!!": "{ /acc/sys/id }" },
          "then": [ { "type": "SetVar", "var": "ids",     "value": { "merge": [ "{ /vars/ids }",     [ "{ /acc/sys/id }" ] ] } } ],
          "else": [ { "type": "SetVar", "var": "missing", "value": { "merge": [ "{ /vars/missing }", [ "{ /email }" ] ] } } ] } ] },
    { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_campaign" } },
      "fields": { "recipients": { "en-US": "{ /vars/ids }" }, "unresolved": { "en-US": "{ /vars/missing }" } } } ] }

A read (ResourceFind) is not an external call, so it is allowed inside a Loop body. Presence and absence are each accumulated with merge.

Saga and concurrency

20. Payment saga (Try/catch/finally)

{ "method": "Post", "executionMode": "Async",
  "statements": [
    { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_order" } },
      "fields": { "sku": { "en-US": "{ /payload/fields/sku }" }, "status": { "en-US": "reserved" } },
      "publish": false, "name": "order" },
    { "type": "Try",
      "body": [
        { "type": "Http", "method": "POST", "url": "https://api.pay.com/charge",
          "headers": [ { "key": "Authorization", "value": "Bearer sk-...", "secret": true } ],
          "body": { "amount": "{ /payload/fields/amount }", "ref": "{ /order/sys/id }" }, "timeoutMs": 10000, "name": "pay" },
        { "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /order/sys/id }" } },
          "fields": { "status": { "en-US": "paid" }, "txId": { "en-US": "{ /pay/body/transactionId }" } }, "publish": true },
        { "type": "Return", "value": { "orderId": "{ /order/sys/id }", "status": "paid" }, "statusCode": 201 } ],
      "catch": [
        { "type": "ResourceDelete", "resource": "Content", "target": { "sys": { "id": "{ /order/sys/id }" } } },
        { "type": "Return", "value": { "reason": "payment failed", "detail": "{ /error/message }" }, "isError": true, "statusCode": 402 } ],
      "finally": [
        { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_paylog" } },
          "fields": { "orderRef": { "en-US": "{ /order/sys/id }" }, "amount": { "en-US": "{ /payload/fields/amount }" } } } ] } ] }

After reserving (draft): on payment success it confirms, publishes, and returns 201; on failure catch deletes the reservation (compensation) and returns 402; finally always logs. Delete-based compensation produces a new sys.id, so it falls within the limitation where references break (see No transactions and compensation in Execution Semantics).

21. Optimistic-lock CAS

{ "method": "Post", "executionMode": "Sync",
  "statements": [
    { "type": "ResourcePageRead", "resource": "Content", "contentType": { "sys": { "id": "ct_stock" } },
      "where": { "fields.sku": { "eq": "{ /payload/fields/sku }" } }, "limit": 1, "name": "stock" },
    { "type": "If", "condition": { "<": [ "{ /stock/items/0/fields/qty/en-US }", "{ /payload/fields/amount }" ] },
      "then": [ { "type": "Return", "value": { "reason": "out of stock" }, "isError": true, "statusCode": 409 } ] },
    { "type": "Try",
      "body": [
        { "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /stock/items/0/sys/id }" } },
          "version": "{ /stock/items/0/sys/version }",
          "fields": { "qty": { "en-US": { "-": [ "{ /stock/items/0/fields/qty/en-US }", "{ /payload/fields/amount }" ] } } } },
        { "type": "Return", "value": { "ok": true } } ],
      "catch": [
        { "type": "Return", "value": { "reason": "version conflict, retry" }, "isError": true, "statusCode": 409 } ] } ] }

Read the stock to secure a fresh sys.version, then deduct with that version (version). If another execution changed the value between the read and the write, it aborts on a version mismatch and catch returns 409. The out-of-stock guard sits outside Try (a normal early return).