Statement カタログ

statements 配列の各要素が 1 つの文(statement)です。このドキュメントでは 25 種の文のフィールド、動作、結果を整理します。すべての値の箇所は 値式 の規則(参照、リテラル、JsonLogic、ロケールマップ)に従います(例外は RegexpatternCachekey の 2 つです。RegexCache 参照)。

Statement 概要

分類type一行要約
リソース書き込みResourceCreateContent/Media を生成(任意で公開)
ResourceUpdateContent/Media フィールドの全体置換(渡さなかった field・locale は削除)
ResourcePatchContent/Media フィールドの部分マージ(指定した field・locale のみ。リテラル null は削除)
ResourceDelete削除(DraftArchived のみ。Published の場合はまず unpublish)
ResourcePublish / ResourceUnpublish公開 / 公開取り消し
ResourceArchive / ResourceUnarchiveアーカイブ / アーカイブ解除
リソース読み取りResourceReadid で単一件取得
ResourceFindフィルターで最初にマッチした単一件(なければ null)
ResourceForEachフィルターにマッチするリソースを内部的に巡回し、項目ごとに onEach を実行
ResourceCountフィルターにマッチする件数だけを数える(項目は読み取らない)
外部Http外部 HTTP 呼び出し({ status, body })
EmailSend登録済みの EmailAccount でメール 1 通を送信
変数SetVarscript-scoped 変数の宣言/更新
キャッシュCacheその Script だけの短命なキャッシュを読み取り/書き込み/削除
値のパースParseJsonJSON テキストを値(オブジェクト・配列・スカラー)にパースしてバインド
署名とテキストSignature受け取った署名コードが秘密鍵で作ったコードと同じかを検証(Boolean)
Hash鍵なしのダイジェスト計算(文字列)
Regex正規表現の適用。一致の有無(Boolean)またはキャプチャグループ(配列)
制御フローIf条件分岐
Loop繰り返し(foreach / while / counted)
Parallelブランチの同時実行
Return結果の返却と早期終了
Try例外処理(catch/finally)

id で対象を指定しない Content の文は、扱う Content Type を必ず書きます。 ResourceFindResourceForEachResourceCount は、resource"Content" なら contentType必須です。Space 全体を横断する Content の照会はありません。ResourceCreate も作成する Content Type を書きます。MediaSpace 全体で 1 組のため範囲を切らず、id で対象を指定する文(ResourceReadResourceUpdateResourcePatchResourceDelete と公開・アーカイブの文)は target があるため範囲が不要です。

循環呼び出しは最大 3 回まで。 上のリソース書き込み文(ResourceCreateResourceUpdateResourcePublish など)で propagateEvents を有効にすると(デフォルトは無効)、変更イベントが発生し、そのイベントが Webhook を通じて再び Script を実行することがあります。このように続く連鎖(Script → イベント → Webhook → Script → …)は最大 3 回までしか続かず、それを超えると自動的に打ち切られて無限循環を防ぎます。

共通フィールド

{ "type": "<StatementType>", "name": "<任意、script 内で一意>", /* ...タイプ別フィールド... */ }
  • type: 判別子です。上表の値のいずれかです(必須)。
  • name: 任意です。付けると結果が /<name> としてコンテキストにバインドされ、以降の statement が { /<name>/... } で参照します。結果を使わない場合は省略します。
  • バインディング名の規則: name はコンテキストルートにそのまま載るキーなので、保存時に検証されます。使えるのは英数字と _- だけで(JSON Pointer のキーとして使える必要があるため、それ以外の文字や空の名前は拒否されます)、予約ルート(payloadrawPayloadheadersvarserrornow)と同じであってはならず、1 つの Script 内で一意でなければなりません。違反した場合(形式違反・予約語の使用・重複)は保存が拒否されます。

エンティティ参照の形式

contentTypetarget などのエンティティ参照は { "sys": { "id": <値式> } } という 1 つの形式に統一します。sys.id だけが必要で、対象タイプは resource から推論します(sys.typesys.targetType は省略)。

  • contentType.sys.id は通常リテラルです(例: "ct_post")。
  • target.sys.id は通常 { /ptr } 値式です(実行時に resolve。例: { /payload/sys/id })。

resource

リソース系の文は対象の種類を resource: "Content" | "ContentType" | "Media" | "ServiceUser" で指定します。

Content Type を受け取るのは ResourceCount だけです。他の文に書くと保存が拒否されます。様式そのものを作ったり直したりする作業は、Script ではなく CMA の担当です。

ServiceUser(製品にサインアップした会員)は読み取り専用です。読み取りの 3 文(ResourceReadResourceFindResourceForEach)だけがこの値を受け取り、書き込み文に書くと保存が拒否されます(エラー 参照)。規則は 会員ディレクトリの読み取り で扱います。

リソース書き込み

すべての書き込み文は propagateEvents(デフォルト false)を持ちます。true にすると、その書き込みが変更イベントを発生させ、Webhook のような後続の動作が実行されます。デフォルトは発生させません(サイレントなシステム書き込み)。

ResourceCreate

Content または Media を作成します。ContentMediafields モデルを共有し、値はロケールマップです。

フィールド対象説明
resource共通"Content" または "Media"(必須)
contentTypeContent作成する Content Type({ sys: { id } })。Content の場合は必須
fields共通フィールドマップ { "<field>": { "<locale>": 値 } }。populate するフィールドごとにデフォルトロケールのバケットが必須。Content のキーは Content Type 定義に従い、Media のキーは固定(titledescriptionfile)
locale共通(便宜)指定すると fields の各値を { <locale>: 値 } に自動ラップ
publish共通書き込み後に公開(CDA/ACDA へ露出)。デフォルト true
  • Mediafile: fields.file.{locale} の値はインジェスト指示 { "source": <値式>, "encoding": "url"|"base64" } です(どちらも必須)。ファイルを含む書き込みでは、エンジンがインジェストを実行します(url ならダウンロード、base64 ならデコードしたうえでアップロードして処理します)。このインジェストは宣言する時間がないため 30 秒の基本予算から出ていき(時間予算)、外部呼び出しの上限には数えられません。ファイルのない(fileless)Media も作成できます。publish:true でファイルがない、または処理が未完了の場合は公開段階でエラーになり、publish:false の場合はそのまま Draft です。
  • 結果(name バインディング): 作成されたリソースです。{ /<name>/sys/id }{ /<name>/fields/<field>/<locale> }
// Content
{ "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
  "fields": { "title": { "en-US": "{ /payload/fields/title }" } }, "publish": true, "name": "post" }
 
// Media. file はインジェスト指示
{ "type": "ResourceCreate", "resource": "Media",
  "fields": {
    "title": { "en-US": "{ /payload/fields/prompt }" },
    "file":  { "en-US": { "source": "{ /gen/body/data/0/url }", "encoding": "url" } }
  }, "name": "img" }

ResourceUpdate

対象の ContentMedia のフィールドを全体置換します(PUT)。fields に渡したものがそのまま新しいフィールドになり、ここにない field と locale は消去されます。一部だけ変更するには ResourcePatch を使います。

フィールド説明
resource"Content" または "Media"
target対象({ sys: { id } }、必須)。id は通常 { /ptr }
fields書き込むフィールド全体。値はロケールマップ全体置換なので、ここにない field と locale は削除されます。Media file はインジェスト指示(上記 ResourceCreate 参照)。列挙したファイルは常に再インジェストし、渡さなかった locale のファイルは削除
locale(便宜)fields を自動ラップ
version(任意)値式(Int)。楽観的ロック。指定すると対象の現在の sys.version一致する場合のみ更新し、不一致の場合はバージョン競合エラーで abort します(Try で catch 可能)。省略時は検査なし(last-write-wins)
publish更新後に republish。デフォルト true

Media で metadata だけを変更しようと Update を使うと file が抜けてファイルがすべて削除されます(全体置換なので)。部分的な変更には必ず ResourcePatch を使います。

{ "type": "ResourceUpdate", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
  "fields": { "title": { "en-US": "Hello", "ko-KR": "안녕" }, "status": { "en-US": "published" } } }

ResourcePatch

対象の Content または Media のフィールドを部分マージします(PATCH)。fields に渡したフィールド(およびその中のロケール)のみを上書きし、言及しなかったフィールドとロケールはそのまま維持します。値の形式、localeversionpublishResourceUpdate と同じです。

フィールド説明
resource"Content" または "Media"
target対象({ sys: { id } }、必須)。id は通常 { /ptr }
fields上書きするフィールド。値はロケールマップ指定したフィールドとロケールバケットのみを更新(残りは維持)。値がリテラル null の場合はその (field, locale) を削除Media file はインジェスト指示(上記 ResourceCreate 参照)
locale(便宜)fields を自動ラップ
version(任意)ResourceUpdate と同じ(楽観的ロック)
publish更新後に republish。デフォルト true
  • 特定のロケールやファイルの削除: 値にリテラル null を指定します。例: "title": { "fr-FR": null }(fr-FR のタイトルを削除)、"file": { "en-US": null }(en-US のファイルを削除)。値式が実行時に null と評価されるのは削除ではなくエラーです(リテラル null のみ削除)。
  • Media file にインジェスト指示を与えると、そのロケールのファイルを置き換えます。ファイルを与えなければ維持します。
// viewCount(en-US) だけ +1。title やその他のロケールなど残りはそのまま維持
{ "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
  "fields": { "viewCount": { "en-US": { "$+": [ "{ /payload/fields/viewCount }", 1 ] } } } }

ResourceDelete

対象を削除します。DraftArchived 状態のみ削除できます。Published や Changed の場合は拒否されるため、まず ResourceUnpublish する必要があります。Media はファイルを処理している最中であれば削除が拒否されます。auto-unpublish はしません(CMA/ACMA で同じ)。

フィールド説明
resource"Content" または "Media"
target対象({ sys: { id } }、必須)
{ "type": "ResourceDelete", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }

ResourcePublish, ResourceUnpublish, ResourceArchive, ResourceUnarchive

対象の公開状態とアーカイブ状態を独立して制御します。4 つともフィールドは同じです。各操作の status 前提条件は CMA/ACMA と同じです。ResourcePublishArchived からは実行できず、ファイルの処理が終わっている必要があります。ResourceUnpublishPublishedChanged からのみ、ResourceArchiveDraft からのみ、ResourceUnarchiveArchived からのみ実行できます。

フィールド説明
resource"Content" または "Media"
target対象({ sys: { id } }、必須)
version(任意)値式(Int)。楽観的ロック。指定すると現在の sys.version と一致する場合のみ実行
{ "type": "ResourcePublish",   "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }
{ "type": "ResourceUnpublish", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }
{ "type": "ResourceArchive",   "resource": "Media",   "target": { "sys": { "id": "{ /m/sys/id }" } } }

リソース読み取り

ResourceReadResourceFind はリソースを読み取って値にバインドし、ResourceCount は件数だけを数えます。3 つとも状態を変更しません(propagateEvents はありません)。ResourceForEach も照会自体は読み取りですが、onEach にリソース書き込み文を含めると項目ごとにその書き込みが実行され、状態が変わります。

4 つの文(ResourceReadResourceFindResourceForEachResourceCount)はいずれも from(デフォルト Current)で、どの保存版を読み取るかを指定します。Current はコンテンツスタジオに表示される最新の下書き(CMA/ACMA が読む値)で、Published公開スナップショット(CDA/ACDA が配信する、最後に公開した時点の値)です(ServiceUser は公開されないため Current のみを受け取ります。会員ディレクトリの読み取り 参照)。

ResourceFindResourceForEachResourceCount はこれに加えて advanced(デフォルト true)で 高度な検索(Advanced Search) の有効と無効を切り替えます。書かなければ有効です。 Content 専用のため、MediaServiceUser の読み取りでは無視されます。有効にすると whereregexnearwithin 演算子とテキスト全文検索(全文検索が有効な LongText フィールドでは、eq が値を含む項目まで部分・類似マッチで見つけます)、そして fields.* での並べ替えを使えます。無効にするとこの 3 つの演算子は拒否され、テキストの eq は完全一致になり、prefix と比較・リスト演算子は高度な検索に関係なく動作します。作成または変更したばかりの項目は高度な検索への反映に少し(約 1 秒)かかるため、その直後の高度な検索では拾えないことがあります。デフォルトが有効なので、この遅延は advancedfalse にしない限りすべての照会に当てはまります。書き込んだばかりの項目をすぐに読み取る必要がある場合は、id で ResourceRead(基本の保存版で、反映の遅延はありません)を使うか、書き込みが返した sys.id で取得します。

wherecreatedBy: ":self" は「今呼び出したユーザーが作成したものだけ」を意味します。ただし匿名呼び出しを許可した Script(anonymousCallEnabled)では使えません。その場合 :self が呼び出し元ではなく作成者に解決され、作成者のリソースが黙って開いてしまうため、そうした定義は保存が拒否されます(匿名呼び出し 参照)。

whereorder では、コンテンツのフィールドは fields.<field> と書きます(名前だけでは認識されません)。fields.<field> には Space の既定ロケールが自動的に適用されるため、ロケールを直接付けません。下の例の fields.statusfields.slug がそのまま既定ロケールの照会です。特定(非既定)のロケールだけを照会する場合にのみ、fields.<field>.<locale>(例: fields.title.ko-KR)で明示します。sys.*(sys.createdAt など)と createdBy(:self)は fields. を付けずにそのまま書きます。詳しい規則は 値式の where と order のロケール にあります。

会員ディレクトリの読み取り (ServiceUser)

ResourceReadResourceFindResourceForEachresource"ServiceUser" を受け取り、その Space の会員ディレクトリを読み取ります(ResourceCount は受け取りません。下の ResourceCount 参照)。注文の持ち主が誰かを確認したり、メールアドレスで会員を探してその sys.id を次の文に渡す流れで使います。以下の規則は 3 文に共通です。

  • 読み取りだけができます。 ResourceCreateResourceUpdateResourcePatchResourceDelete と公開・アーカイブの文は "ServiceUser" を受け取らず、そうした定義は保存の時点で拒否されます。権限を足して開けるものではなく、Script から会員を変更する道がそもそもないため、権限エラーではなく書き方を誤った文として拒否されます。
  • 作成者に会員ディレクトリの権限がなければ保存できません。 ContentMedia のように権限マップで検査するのではなく、作成者の SpaceRole settingsSETTING_SERVICE_LOGIN(または SETTING_ALL)があるかを見ます。会員ディレクトリは他のすべての経路でも Space 設定が管掌するリソースだからです。なければ保存が拒否されます(セキュリティモデル 参照)。
  • fromCurrent のみを受け取ります。会員は公開されるリソースではないため、Published を渡すと実行が失敗します。
  • contentTypeadvanced は無視されます。 会員ディレクトリは Content Type で分かれず(Space 全体で 1 組)、高度な検索も Content 専用です。
  • wheresys.email は完全一致系の演算子のみを受け取ります(eqneinnin)。会員のアドレスは暗号化して保存されるため、順序比較や prefix には意味がありません。それ以外の演算子を渡すと、黙って 0 件を返すのではなく実行が失敗します。
  • 結果は ServiceUser リソースそのものです。{ /<name>/sys/id }{ /<name>/nickname } のように参照します。構造は ServiceUser リファレンス で扱います。見つけた会員にメールを送るときはアドレスを取り出さず、EmailSendtoServiceUser にその sys.id を渡します(エンジンが送信の直前にアドレスを resolve するため、会員のアドレスが Script の変数空間に入りません)。
// メールアドレスで会員 1 名を探す。なければ null
{ "type": "ResourceFind", "resource": "ServiceUser",
  "where": { "sys.email": { "eq": "{ /payload/fields/email }" } }, "name": "member" }

ResourceRead

id で単一件取得します(get-by-id)。結果はリソース全体を名前にバインドします。

フィールド説明
resource"Content""Media""ServiceUser"
target対象({ sys: { id } })。id は値式
from(任意)Current(デフォルト、最新の下書き)または Published(公開スナップショット)。ServiceUserCurrent のみ
  • 結果: リソースそのものがバインドされます。この文に name を付けていれば { /<name>/sys/id }{ /<name>/fields/<field>/<locale> } で直接参照します(下の例の "name": "order" なら { /order/sys/id })。一覧ではないので配列インデックスを経由しません。
  • 対象がなければエラーです。Try で囲んで処理できます。
{ "type": "ResourceRead", "resource": "Content",
  "target": { "sys": { "id": "{ /payload/fields/orderId }" } }, "name": "order" }

ResourceFind

フィルターで最初にマッチした単一件を読み取ります。なければ null です。ユニークな業務キー(slug、email、sku)で 1 件を探すときに使います。

フィールド説明
resource"Content""Media""ServiceUser"
contentType検索範囲の Content Type({ sys: { id } })。Content のとき必須MediaServiceUser では無視
whereフィルター({ "<field>": { "<op>": <値> } })。使える演算子は 演算子一覧 に従います(regexnearwithinadvanced が必要)。createdBy: ":self" をサポート。ServiceUsersys.emaileqneinnin のみ(会員ディレクトリの読み取り)
order複数マッチ時に「最初」を決定するソート(例: "-sys.createdAt")
from(任意)Current(デフォルト、最新の下書き)または Published(公開スナップショット)。ServiceUserCurrent のみ
advanced(任意)高度な検索(Advanced Search)で実行。Content 専用(MediaServiceUser は無視)。デフォルト true。上の リソース読み取り の説明を参照
  • 結果: 最初にマッチしたリソースを name にバインドします。{ /<name>/fields/<field>/<locale> } で直接参照します。なければ null なので、{ "==": [ "{ /<name> }", null ] } で存在の有無を分岐します(find-then-upsert の典型)。
{ "type": "ResourceFind", "resource": "Content", "contentType": { "sys": { "id": "ct_article" } },
  "where": { "fields.slug": { "eq": "{ /payload/fields/slug }" } }, "name": "found" }

ResourceForEach

フィルターにマッチするリソースを内部的に巡回し、項目ごとに onEach を実行します。値として使うコレクションを作るのではなく、各項目に対して作業を実行するための文です。draft の一括公開、条件にマッチする Content の一括修正、各項目を外部へ送信・同期するといった反復作業に使います。1 件だけ読むときは ResourceRead(id)や ResourceFind(フィルター)を使います。

フィールド説明
resource"Content""Media""ServiceUser"(必須)
contentType巡回範囲の Content Type({ sys: { id } })。Content のとき必須MediaServiceUser では無視
whereフィルター({ "<field>": { "<op>": <値> } })。意味は ResourceFindwhere と同じです(ServiceUsersys.email の制約も同じです)。使える演算子は 演算子一覧 に従います(regexnearwithinadvanced が必要)。createdBy: ":self" をサポート
orderソート(例: "sys.createdAt,sys.id")。なければプラットフォームの既定順
fromCurrent(デフォルト、最新の下書き)または Published(公開スナップショット)。ServiceUserCurrent のみ
advanced高度な検索(Advanced Search)で巡回。Content 専用(MediaServiceUser は無視)。デフォルト true。上の リソース読み取り の説明を参照
limit(任意、1 以上) 総処理件数の上限(ページサイズではありません)。なければプラットフォームの上限(10,000 件)まで巡回
name(任意) 現在の項目をバインドする名前。繰り返しごとに新しくバインドされ、onEach 内で { /<name> } で参照します(Loopname と同じ寿命。巡回が終わった後も最後の項目がバインドされたまま残ります)。項目を参照しない場合は省略します
onEach各項目ごとに実行する子 statement 配列(必須)
  • コレクションをバインドしません(map ではなく foreach)。 { items, next } も cursor もありません。巡回の結果を値として受け取るのではなく、項目ごとに onEach を回します。一覧が必要なら SetVar で自分で集めます。件数だけが必要なら ResourceCount を使います。
  • limit がなくても無限巡回ではありません。 なければプラットフォームの上限(10,000 件)まで回り、マッチが残ったままその上限に達すると失敗します(手をつけていない項目を残したまま成功と報告しないためです)。逆に 宣言した limit に到達するのは意図した停止なので正常終了です。上限を超える limit は保存時に拒否されます。
  • cursor がありません。 完走すれば成功で、途中で止まれば(実時間・クォータ超過、onEach の未処理の失敗)失敗であり、どの項目でなぜ失敗したかをエラーが指し示します。再開は作成者が自分のデータで表現します(where を「未処理」にして onEach の最後で完了を記録すれば、再実行で残ったものから続きます)。
  • 時間予算では掛け算として計上されます。 この文が宣言する時間は、onEach が宣言した時間に処理する項目数(limit、なければ 10,000)を掛けた値です(時間予算)。子を所有する複合 statement なので、それ自体は 外部呼び出しの leaf 予算 には数えず、onEach 内の外部呼び出し文が予算に数えられます。
  • onEach には他の statement と同様に外部呼び出し(HttpEmailSend)や Media ファイルのインジェストを含められます(Loopbody と同じです)。リソースのクエリ結果を項目ごとに一度ずつ処理することが、この文の存在理由です。
// draft 状態の投稿をすべて見つけて項目ごとに公開
{ "type": "ResourceForEach", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
  "where": { "fields.status": { "eq": "draft" } }, "order": "sys.createdAt,sys.id",
  "from": "Current", "advanced": false, "name": "post",
  "onEach": [
    { "type": "ResourcePublish", "resource": "Content", "target": { "sys": { "id": "{ /post/sys/id }" } } }
  ] }

ResourceCount

フィルターにマッチする件数だけを数えます。項目を読み取ってこないため、一覧ではなく個数が必要なときに使います。残っている在庫を確認したり、同じ値がすでにあるかを判定したり、限度を超えたかを検査する場面です。

フィールド説明
resource"Content" または "ContentType"(必須)。MediaServiceUser は数えられず、そう書くと保存が拒否されます
contentType数える範囲の Content Type({ sys: { id } })。Content のとき必須Content Type を数えるときは無視されます(Space 全体で 1 組)
whereフィルター。意味は ResourceFindwhere と同じです。マッチした項目をすべて数えます
from(任意)Current(デフォルト、最新の下書き)または Published(公開スナップショット)
advanced(任意)高度な検索(Advanced Search)で実行。Content 専用(Content Type を数えるときは無視)。デフォルト true。上の リソース読み取り の説明を参照
name(任意)件数をバインドする名前
  • 結果: マッチした件数を name にバインドします。{ /<name> } で参照して、比較や分岐に使います。
  • 項目は返しません。 項目が必要なら ResourceFind(最初にマッチした単一件)や ResourceForEach(項目ごとに実行)を使います。
  • 件数を得るために ResourceForEach で巡回して数えないでください。 巡回は時間予算を項目数の分だけ掛けて確保し(時間予算)、マッチが残ったままプラットフォームの上限に達すると失敗します。数えるだけでよいなら、この文が一度で終わらせます。
  • orderlimit はありません。数えるのに順序は必要なく、マッチしたものはすべて数えるからです。
// この投稿に付いたコメントが何件あるかを数える
{ "type": "ResourceCount", "resource": "Content", "contentType": { "sys": { "id": "ct_comment" } },
  "where": { "fields.postId": { "eq": "{ /payload/sys/id }" } }, "name": "commentCount" }

外部

Http

外部 HTTP を呼び出します。外部呼び出しなので、プランごとの 外部呼び出しの上限 に数えられ、時間予算 には timeoutMs(なければ 30 秒)× (1 + retry)として計上されます。

フィールド説明
method"GET", "POST", "PUT", "PATCH", "DELETE"
url対象 URL(値式。{ /ptr } を挿入可能)
headers[{ "key", "value", "secret"? }]value は値式。secret:true のヘッダーは CMA(管理者)専用として扱われ、エンドユーザーには公開されず、送信直前にのみ復号されます。Content-Type をここに入れると、body がその形式でシリアライズされます(下記)
bodyリクエスト body(値式または JSON)。どの形式で送られるかは Content-Type ヘッダーが決めます
timeoutMsこの呼び出しのタイムアウト(ms)
retry応答 status が 400 以上の場合に再試行する回数。デフォルト 0、上限は 2
ignoreStatusCode(再試行を終えた)最終 status が 400 以上のとき、この呼び出しを失敗とみなすか。false(デフォルト)の場合は失敗として扱われTry/catch の対象になります。true の場合は失敗とみなさず、{ status, body } をそのままバインドします(呼び出し元が status で直接分岐)
responseTypeレスポンス本文を何として受け取るか。"Json"(デフォルト)はオブジェクト・配列にパースし、"Text" は文字列として受け取ります
  • 結果: { status, body } です。この文に name を付けていれば { /<name>/status }{ /<name>/body/... }body の形は responseType が決めます。
  • responseType は成功レスポンスにのみ適用されます。 status が 400 以上のレスポンスの本文は、宣言した値に関わらず診断用としてバインドされます(JSON ならパースされた値、そうでなければ文字列)。
  • "Json" なのに本文が JSON でない場合、この呼び出しは失敗します(Try/catch の対象)。JSON を返さない API は "Text" で受け取り、値として扱う必要があれば ParseJson でパースします。
  • "Text" はレスポンスの Content-Type の charset でデコードし、charset がなければ UTF-8 とみなします。本文が空の場合、body はどちらも null です。
  • 応答サイズの上限: 応答本文は最大 10MiB です。これを超えると、この呼び出しは例外で失敗し、他のランタイム失敗と同様に Try/catch で処理できます(サイズによる失敗なので ignoreStatusCode では無視されません)。
{ "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, "retry": 1,
  "responseType": "Json", "name": "resp" }

body がどの形式で送られるか

headers に入れた Content-Typebody のシリアライズ形式を決めます。比較では大文字と小文字の違いや ;charset=… のようなパラメーターを無視し、前の部分だけを見ます。ヘッダーがない場合や値が空の場合は application/json で送ります。このヘッダーは body があるときにだけ付くので、body がなければ書いておいたヘッダーがそのまま送られます。同じキーを複数回入れると、最初の値だけが使われて 1 つにまとめられます。

宣言した形式に収められない body は、収められる形式に訂正して送ります。 ヘッダーの宣言と実際の body が食い違うことはありません。

宣言した値のまま送られる組み合わせです。

宣言した Content-Typebody の形送られる body
application/json何であれJSON
application/x-www-form-urlencodedオブジェクト・配列order[id]=A-2481&order[amount]=34000
text/plainスカラー値そのまま
その他(text/xml など)何であれJSON

宣言した形式には収められないため訂正される組み合わせです。

宣言した Content-Typebody の形実際に送られる Content-Type送られる body
application/x-www-form-urlencodedスカラーtext/plain;charset=UTF-8値そのまま
text/plainオブジェクト・配列application/jsonJSON

この 2 行は、噛み合わない組み合わせのときにリクエストがどう送られるかを明らかにしたものであり、意図した形式を得る方法ではありません。body を値式で組み立てると、実行時点の payload によってスカラーになることがあり、そのときこの訂正はエラーなしに行われます。受け取る側が形式を問題にする場合は、body の形と Content-Type のどちらかを意図に合わせて修正してください。

form-urlencoded はオブジェクトをブラケットキーに、配列をインデックスに展開します。

body展開されるキーと値
{ "order": { "id": "A-2481", "amount": 34000 } }order[id]=A-2481&order[amount]=34000
{ "tags": ["outerwear", "winter"] }tags[0]=outerwear&tags[1]=winter
{ "items": [{ "sku": "TUMBLER-500" }] }items[0][sku]=TUMBLER-500
{ "memo": null }memo=

キーと値は UTF-8 でパーセントエンコードされて送られます。上の表は、キーの構造を示すためにデコードした形です。値に &+ が含まれていても、ペアの区切り文字や空白と誤解されることなくそのまま伝わります。

ネストをブラケットキーに展開する表記は広く使われている慣例であり、形式そのものの規格ではありません。 受け取る側が order[id] をネストしたオブジェクトに復元するかを確認し、復元しない場合はフラットなキーで body を構成してください。

{ "type": "Http", "method": "POST", "url": "https://api.example.com/oauth/token",
  "headers": [ { "key": "Content-Type", "value": "application/x-www-form-urlencoded" } ],
  "body": { "grant_type": "client_credentials", "client_id": "{ /vars/clientId }" },
  "name": "token" }

EmailSend

登録済みの EmailAccount を通じて メール 1 通を送ります。受け取るフィールドは SMTP/MIME にそのままマッピングされるものだけです。テンプレート id、予約送信、プロバイダー固有の拡張はありません(そうした機能が必要なら Http で該当メールサービスの API を直接呼び出します)。送信者(送信元アドレス)はここで定めず、account が指す EmailAccount から来ます。

フィールド説明
account送信に使う EmailAccount 参照({ sys: { id } }、必須)。通常はリテラル id です。値式で渡すと送信時点で resolve されるため、保存時には検査できません
to受信者アドレス(値式)。totoServiceUser のうち ちょうど 1 つだけを使います
toServiceUser受信者を ServiceUser 参照で指定({ sys: { id } }。その sys.id は値式が可能)。エンジンが 送信の直前にアドレスを resolve するため、メンバーのアドレスが Script の変数空間に入りません
ccCc 受信アドレスの配列(値式)
bccBcc 受信アドレスの配列(値式)
subject件名(値式、必須)
body本文(値式、必須)。常に text/html で送信されるため、プレーンテキストではなくマークアップを書きます(改行は空白に、< はタグとして解釈されます)。補間される値式の結果は HTML エスケープ されます
replyTo(任意)Reply-To ヘッダー(値式)。送信者と異なることがあります(例: no-reply で送りつつ返信はサポートアドレスへ)
timeoutMs(任意、1 以上)この送信のタイムアウト(ms)。なければプラットフォームのデフォルト値、上限を超える値は保存時に拒否
  • 受信者の合計は最大 50 名です。to(1 名)、ccbcc をすべて合わせて数えます(SMTP のエンベロープには cc/bcc の区別がなく、すべて受信者として送られるため合計で数えます)。超えると保存・実行で拒否されます。多くの人に送るには ResourceForEach + EmailSend項目ごとに 1 通ずつ送ります。
  • 結果をバインドしません。 成功は「プロバイダーがメールを受け付けた」ことだけなので返す値がなく、name を受け取りません。再試行もしません(メールは非冪等であり、曖昧に失敗した後に再試行すると重複送信になります。そのため Httpretry に従いません)。失敗は throw され、Trycatch で処理します。
  • 外部呼び出しです。 プランごとの 外部呼び出しの上限 にカウントされ、時間予算 には timeoutMs(なければ 10 秒)1 回分として計上されます(再試行しないため、Http のように回数を掛けません)。ResourceForEachonEach 内で使えます(多件送信の標準形)。
{ "type": "EmailSend", "account": { "sys": { "id": "eml_orders" } },
  "to": "{ /order/fields/email/en-US }",
  "subject": "注文受付完了 (注文番号 { /order/sys/id })",
  "body": "<p>ご注文を受け付けました。配送が始まりましたら再度お知らせします。</p>",
  "replyTo": "support@my-shop.example" }

変数

SetVar

script-scoped な可変変数を宣言または更新します。{ /vars/<var> } で参照します(JsonLogic には変数宣言がないため statement として提供します)。

フィールド説明
var変数名。{ /vars/<var> } で参照
value値式。自分自身を参照して累積できます
{ "type": "SetVar", "var": "total", "value": 0 }
{ "type": "SetVar", "var": "total", "value": { "$+": [ "{ /vars/total }", "{ /row/qty }" ] } }   // 累積
{ "type": "SetVar", "var": "ids",   "value": { "$merge": [ "{ /vars/ids }", [ "{ /row/sys/id }" ] ] } }  // 配列の収集

キャッシュ

Cache

その Script だけの短命なキャッシュを読み書きします。外部呼び出しの結果のように、毎回取り直すのが惜しい値を数秒のあいだ持っておき、次の呼び出しで再利用するための箇所です。外部呼び出しではないので、1 定義あたりの外部呼び出し数には含まれず、時間予算に宣言する時間もありません。

フィールド説明
action"Set"(書き込み)、"Get"(読み取り)、"Delete"(削除)のいずれか(必須)
keyキャッシュキー(Cache Key)(必須)。値式ではなくリテラルです(下記参照)。最大 128 文字で、超えると保存が拒否されます
value保存する値(Set 専用)です
ttlキャッシュが生きている時間(Set 専用、秒)。1 から 30 のあいだで、省略すると 5 です
defaultValueキャッシュされたデータがないときに Get がバインドする値(Get 専用)。省略すると null です
name結果を入れる名前。Get必須です(読み取った値の行き先がなければ、読み取る理由がありません)。Set は保存した値を、Delete は削除を実行したかどうかをバインドし、どちらも任意です
  • その演算に該当するフィールドだけを書きます。 Getttl を書いたり、SetdefaultValue を書いたりすると保存が拒否されます。
  • ないことと期限切れは区別されません。 どちらも defaultValue がバインドされます。null を保存しておいた場合も同じです。
  • 保存の範囲はその Script 1 つです。 同じ Space の他の Script は、同じキャッシュキーを使っても互いのデータを見られません。その Script を修正または削除すると、その Script のデータはすべて消えます。
  • key はリテラルです。 リクエストから来たキャッシュキーでデータを選ばせると、呼び出し元が何を読むかを決めることになり、会員ごとに 1 つずつ入れておいた Script が、ある会員の値を別の会員に渡してしまいます。そのため key の中に { /pointer } があると、値に置き換えられることも文字どおり使われることもありません。保存そのものが拒否されます。
  • 繰り返しの中には置けません。 LoopResourceForEach のブロックの中に Cache があると保存が拒否されます。下記の個数の上限が、繰り返しの中では何の制限にもならないからです。繰り返しごとに 1 つずつデータを書くことになるため、定義に書かれた文の数と実際に使われるキャッシュキーの数が食い違います。
  • 1 つの定義に 5 個まで入れられます(ネスト含む、演算に関係なく合算)。超えると保存が拒否されます。
  • 保存する値は 10,240 バイト(10KiB)までです。 超えるとその文が失敗します(status 422)。他のランタイム失敗と同じなので、Try/catch で局所的に処理できます。
// 為替レートを 30 秒のあいだ再利用します。
{ "type": "Cache", "action": "Get", "name": "cached", "key": "rates" }
 
// 持っている値があれば、外部呼び出しなしでそのまま返します
{ "type": "If", "condition": { "!!": [ "{ /cached }" ] },
  "then": [ { "type": "Return", "value": "{ /cached }" } ] }
 
{ "type": "Http", "name": "fetched", "method": "GET", "url": "https://api.example.com/rates" }
{ "type": "Cache", "action": "Set", "key": "rates", "value": "{ /fetched/body }", "ttl": 30 }
{ "type": "Return", "value": "{ /fetched/body }" }
 
// 持っていた値を期限切れの前に捨てます
{ "type": "Cache", "action": "Delete", "key": "rates" }

値のパース

ParseJson

JSON テキストを、それが表す値にパースして名前にバインドします。HttpresponseType: "Text" で受け取った本文、payload で渡された JSON 文字列、フィールドに文字列として保存しておいた JSON を扱うときに使います。外部呼び出しではないので外部呼び出しの上限には数えられず、時間予算に宣言する時間もありません。

フィールド説明
nameパース結果を入れる名前(必須)。他の文では任意ですが、ここでは必須です。結果をバインドする以外に何もしないため、名前がないと何の効果もない文になります
valueパースする JSON テキスト(値式、必須)。{ /resp/body } のように前の段階の値を指すか、JSON テキストをリテラルでそのまま書きます(リテラル内の {{ ポインター } テンプレートとして解釈されません)
  • 結果: パースされた値そのものです。オブジェクトはオブジェクト、配列は配列、42"a" のような単一の値もそのままパースされます。以降は { /<name>/... } で内部を指します。
  • すでにパースされた値が来た場合はそのままバインドします。 value が文字列でない値に resolve された場合、パースするテキストではないと判断してその値をそのまま入れます。
  • パースしたテキスト内の { /pointer } は再度解釈しません。 外部から受け取った文字列が { /payload/... } のような表現を含んでいても、値に置き換えられず文字列として残ります。
  • null は 2 つの場合を区別します。 パースするテキストが null の一語であれば正常で、結果も null です。一方 value が指す場所が空で値そのものがない場合は、パースするものがないため失敗です。
  • 失敗: value が値なしに resolve された場合や空白だけの場合、そしてテキストが JSON でない場合です。他のランタイム失敗と同じく Try/catch で処理し、エラーメッセージにパースしようとしたテキストが載ります。
  • 定義あたりの statement 数には 1 つとして数えますが、外部呼び出しの上限や SetVar の上限とは無関係です。
// 1) JSON を返さない API: Text で受け取ってパース
{ "type": "Http", "method": "GET", "url": "https://api.partner.example/v1/quote",
  "responseType": "Text", "name": "resp" },
{ "type": "ParseJson", "name": "quote", "value": "{ /resp/body }" },
 
// 2) payload で渡された JSON 文字列をパース
{ "type": "ParseJson", "name": "spec", "value": "{ /payload/fields/specJson }" }

署名の検証とテキスト処理

決済代行会社が Webhook で送ってきた署名を確認し、その署名が包まれて届いた文字列を解きほどく文です。3 つとも外部呼び出しではなく計算なので、外部呼び出しの上限には数えられず、時間予算に宣言する時間もありません。また、データの箇所を持たないため $ プレフィックスの規則 とは無関係です。3 つを組み合わせた完結した例は クックブックの Webhook 署名検証 にあります。

3 つの文はいずれも resolve された値の長さに上限があります。式の長さではなく、その式が指した値の長さであり({ /rawPayload } の 16 文字が数十 KB を指します)、超えると実行が失敗するので Try で処理できます。数値は 値の長さの上限 にまとめてあります。

Signature

受け取った署名コードが secret で作ったコードと同じかを確認し、その答えを Boolean 値としてバインドします。決済代行会社(PG・MoR)が Webhook で送ってくる署名は、この文で検証します。

フィールド説明
name検証結果を入れる名前(必須)。{ /<name> }true または false です。検証しておいて結果を使わないのは検証していないのと同じなので、省略できません
algorithmコードを作るハッシュ(必須)。SHA1SHA256SHA384SHA512
secret相手と共有した秘密鍵(値式、必須)
secretEncodingsecret をどの表記で書いたか。Utf8(デフォルト、テキストの鍵)・HexBase64。hex や base64 で発行された鍵をテキストのまま置くと別の鍵になり、それらしいコードは作られるものの永遠に一致しません
valueコードを計算するメッセージ(値式、必須)。相手が署名したバイトと文字どおり同じでなければならないため、通常は { /rawPayload } か、プロバイダーがヘッダーに一緒に載せて送ってきたタイムスタンプをその前に付けたものです
expected呼び出し元が送ってきたコード(値式、必須)。例: { /headers/x-signature }
  • 結果: Boolean です。以降は If の条件に { /<name> } をそのまま書きます。
  • value はパースされた /payload ではなく /rawPayload で書きます。 パースした payload を再び文字列に戻すと、空白・数値表記・エスケープが正規化され、相手が署名したバイトには戻りません(コンテキストルート)。
  • 出力の表記を指定するフィールドはありません。 algorithm がコードのバイト長を固定し、同じ長さの hex と base64 は文字列の長さが重ならないため、相手がどちらで送ってきたかを教えてもらわなくてもエンジンがバイトを復元します。hex の大文字・小文字、base64 と base64url(パディングの有無を含む)も同じ理由で区別しません。
  • 失敗と false は、その値を誰が与えるかで分かれます。
    • expected がない場合やコードが一致しない場合は、結果が false になるだけで失敗ではありません。ヘッダーの欠落とコードの不一致を別々に知らせると、どちらが違っているのかを送信元に教えることになるからです。
    • value が空なら空のメッセージで計算します。空の本文も署名の対象です。
    • secret がない場合や secretEncoding が宣言した表記になっていない場合は失敗です。3 つのうち作成者自身の入力はこれだけです。失敗メッセージに secretvalue は載りません。
  • value の上限は 65,536 文字です(resolve された値が基準)。実際のプロバイダーが送ってくる Webhook 本文の大きさに合わせた値です。
  • 比較は値が同じかどうかを constant-time で判定します。先頭の何バイトが一致したかが応答時間から漏れません。
  • secret は暗号化して保存されません。 Http ヘッダーの secret: true(暗号化して保存し、送信の直前に復号)と違って定義に書いたまま残るため、その Script を読める役割には値が見えます。会員(ServiceUser)は Script の定義を読めません(オーサリングと照会は CMA 専用)。
// 本文全体に署名するプロバイダー
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
  "secret": "whsec_9f2c1b7ae4", "value": "{ /rawPayload }",
  "expected": "{ /headers/x-webhook-signature }" }
 
// 鍵を base64 で発行するプロバイダー
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
  "secret": "aGVsbG8td2VlZ2xvbw==", "secretEncoding": "Base64",
  "value": "{ /rawPayload }", "expected": "{ /headers/webhook-signature }" }

Hash

value をダイジェストして、encoding が定めた表記の文字列としてバインドします。HMAC ではなく「いくつかのフィールドと秘密鍵をつなげて SHA256 を計算する」署名スキームを再現するときに使います。

フィールド説明
nameダイジェストを入れる名前(必須)
algorithmMD5SHA1SHA256SHA384SHA512(必須)。MD5 はそれを要求する以前のスキームを再現するためのもので、新しく作る署名で選ぶ値ではありません
valueダイジェストするメッセージ(値式、必須)
encoding結果の表記。Hex(デフォルト)・HexUpperBase64Base64Url
  • secret フィールドはありません。 スキームごとに鍵が前・後・中間と分かれるため、鍵を value の中に直接書くほうがすべての位置を表現できます。
  • 結果: 文字列です。相手が送ってきたコードと比較するときは { "==": [ "{ /<name> }", "{ /headers/... }" ] } と書きます。この比較は Signature の constant-time 比較とは違い、通常の等価比較です。
  • value が値なしに resolve された場合や空白だけの場合は失敗です(作成者自身の値式だからです)。
  • value の上限は 128 文字です。つなげたフィールドをいくつか入れる箇所なので Signature よりはるかに狭いです。Webhook の本文全体を対象に計算する必要がある場合は Signature を使います。
// SHA256(注文番号 + 金額 + merchantKey) を大文字 hex で
{ "type": "Hash", "name": "expectedSign", "algorithm": "SHA256", "encoding": "HexUpper",
  "value": "{ /payload/orderId }{ /payload/amount }9f2c1b7ae4" }

Regex

patternvalue に適用して、mode が求めたものをバインドします。値式には文字列を切り出す手段がないため(つなげる cat と、含むかを見る in だけです)、t=…,v1=… のように 1 つのヘッダーに複数の値が包まれて届くものを解くときにこの文を使います。

フィールド説明
name結果を入れる名前(必須)。Capture は要素を { /<name>/1 } で指します
mode"Match" は一致の有無を Boolean で、"Capture" は最初のマッチを配列でバインド(必須)
pattern正規表現(必須)。値式ではなくリテラルです(下記参照)。フラグは (?i) のようにパターンの中に書きます。最大 128 文字で、超えると保存が拒否されます
valueパターンを適用するテキスト(値式、必須)。resolve された値が 10,240 文字(10KiB)を超えると実行が失敗します
  • 結果: MatchBooleanCapture は配列または null です。配列はインデックス 0 がマッチ全体で、1 からがキャプチャグループであり、参加しなかったグループは null です(空文字列ではありません。それはマッチしたということです)。パターンが現れなければ Capture は空の配列ではなく null です。
  • どちらのモードも「パターンがどこかに現れるか」を問います。 テキスト全体がパターンと同じでなければならない場合は ^…$ で固定します。Match で検査してから Capture で取り出す 2 つの文が互いに違う答えを出さないよう、問いを同じにしてあります。
  • pattern はこのエンジンで値式ではない 2 つのフィールドのうちの 1 つです(もう 1 つは Cachekey です)。リクエストから来たパターンをそのまま実行すると、呼び出し元が実行される式を選べることになり、正規表現のバックトラッキングがそれをサービス拒否の手段にします。そのため、パターンの中の { /pointer } も値に置き換えられず、文字どおりパターンの一部になります。
  • パターンは実行が始まるときに定義全体で 1 回コンパイルされます。LoopResourceForEach の中にあっても繰り返しごとに再コンパイルされることはなく、使えないパターンは最初の文が何かをする前に失敗します(Try で処理可能)。
// "t=1492774577,v1=<64 文字の hex>" を解いて { /sig/1 } = タイムスタンプ、{ /sig/2 } = コード
{ "type": "Regex", "name": "sig", "mode": "Capture",
  "pattern": "^t=(\\d+),v1=([0-9a-f]{64})$", "value": "{ /headers/x-provider-signature }" }
 
// 形式だけを検査
{ "type": "Regex", "name": "isOrderId", "mode": "Match",
  "pattern": "^ORD-\\d{8}-\\d{4}$", "value": "{ /payload/orderId }" }

制御フロー

If

条件分岐です。condition は JsonLogic で、真偽は 真偽判定 の規則に従います。

フィールド説明
conditionJsonLogic(boolean として評価)
then真の場合に実行する Statement 配列
else(任意)偽の場合に実行する Statement 配列
{ "type": "If",
  "condition": { "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] },
  "then": [ { "type": "Return", "value": { "ok": false, "reason": "insufficient credit" } } ],
  "else": [ /* ... */ ] }

Loop

繰り返しです。モードを 1 つ選びます。over(foreach)、while(条件)、for(カウント)です。どのモードでもエンジンが繰り返しの上限を強制します(無限ループの防止)。上限は maxIterations で宣言し、書かなければプラットフォームの上限が適用されます。body 内には外部呼び出し(HttpEmailSend)や Media ファイルのインジェストも含められ、外部呼び出し文は実行時に繰り返しごとに実際に呼び出されます。1 つの定義あたりの外部呼び出しの最大数の制限は、そのまま適用されます。

時間予算では掛け算として計上されます。 この文が宣言する時間は、body が宣言した時間に maxIterations(なければ 10,000)を掛けた値です(時間予算)。body に外部呼び出しがなければ宣言時間は 0 なので、30 秒の基本予算が実質的な上限です。

フィールド説明
overforeach: 配列に resolve される値式
while条件: JsonLogic(真である間繰り返す)
forカウント: { "from", "to", "step"? }from から to まで含むstep はデフォルト 1
maxIterations最大繰り返し回数(任意)。書かなければプラットフォームの上限 10,000 が適用され、それより大きい値は保存時に拒否されます
name(任意)現在の項目(foreach)やインデックス(while・for)をバインドする名前({ /<name> })
body繰り返し本体の Statement 配列
// foreach
{ "type": "Loop", "over": "{ /payload/fields/items }", "name": "item", "maxIterations": 100,
  "body": [ { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_item" } },
             "fields": { "name": { "en-US": "{ /item/name }" } } } ] }
 
// while
{ "type": "Loop", "while": "{ /vars/hasMore }", "maxIterations": 1000, "body": [ /* ... */ ] }
 
// counted (1..10 step 2)
{ "type": "Loop", "for": { "from": 1, "to": 10, "step": 2 }, "name": "i", "maxIterations": 100, "body": [ /* ... */ ] }

Parallel

ブランチを同時に実行し、join してから進みます。ブランチ間の参照は不可です(依存がある場合は順次に配置します)。

フィールド説明
branchesStatement[][]。各要素が 1 つのブランチ(文の配列)
{ "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" } ]
] }

Return

一般的なプログラミングの return ですScript の結果を呼び出し元に返し、その地点で正常終了します。

フィールド説明
value(任意)返す値式
isErrorデフォルト falsetrue の場合は value が応答の error として返ります(そうでなければ return)
statusCode応答ステータスコード。デフォルト 200
  • Return に到達しなければ返り値はありません。結果を返すには value を明示します。
  • 例外や throw ではなく正常終了なので catch の対象ではありません(Try の中でも Script 全体を終了しますが finally は実行します)。
  • guard もこの文で表現します。IfthenReturn を置くと、条件に反したときに値を返し、以降の文を実行しません。これは Return の複数ある使い方のうちの 1 つです。
{ "type": "Return", "value": { "orderId": "{ /order/sys/id }", "status": "paid" }, "statusCode": 201 }
{ "type": "Return", "value": { "reason": "payment failed" }, "isError": true, "statusCode": 402 }

Try

例外処理です。

フィールド説明
body試行する Statement 配列
catch(任意)body 失敗時に実行。/error{ message } を露出(どの文で失敗したかは入りません)
finally(任意)成功・失敗に関わらず常に実行
  • catch が処理すれば Script は中断されません。catch のない失敗のみが Script を中断させます(補償の試行を含む)。
  • 何が「失敗」なのか、補償(compensation)の限界は 実行セマンティクス、制約、セキュリティ で扱います。
{ "type": "Try",
  "body":    [ { "type": "Http", "method": "POST", "url": "https://primary.api/gen", "name": "resp" },
               { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_result" } },
                 "fields": { "text": { "en-US": "{ /resp/body/text }" } } } ],
  "catch":   [ { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_result" } },
                 "fields": { "text": { "en-US": "Generation failed" }, "error": { "en-US": "{ /error/message }" } } } ],
  "finally": [ /* 常に実行 */ ] }