skapi-js 2.2.6-beta.1 → 2.2.6-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/skapi.d.mts CHANGED
@@ -691,8 +691,9 @@ type Subscription = {
691
691
  */
692
692
  type TicketConditionOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
693
693
  /**
694
- * One row of a ticket condition list: `headers`, `data`, `params` or `user` of a ticket's
695
- * condition, or `headers`, `data` or `user` of a req action's response condition.
694
+ * One row of a ticket condition list: `headers`, `body`, `params` or `user` of a ticket's
695
+ * condition, the `response` rows of a cond action, or `headers`, `data` or `user` of the
696
+ * response condition Skapi's own rows carry.
696
697
  *
697
698
  * How a list decides:
698
699
  * - Rows on the same key are alternatives: the key passes when any one of them matches.
@@ -701,9 +702,14 @@ type TicketConditionOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne
701
702
  * - A field missing from the request is a mismatch for its key, '!=' included, not an error.
702
703
  * The only rows that pass on a missing field are `= undefined` and `!= null`.
703
704
  * - Every row is read, in order. For each key the FIRST matching row wins: its `setValueWhenMatch`
704
- * applies and its `placeholder` captures, and later rows on that key are skipped. A row that
705
- * does not match captures nothing, so several rows on one key, each with its own
706
- * `setValueWhenMatch`, followed by a catch-all row, work as a lookup table.
705
+ * replaces the value (in the data itself where the field exists) and its `placeholder` captures
706
+ * the result. The later match rows on that key are skipped without being evaluated, so nothing
707
+ * is re-checked against the replaced value; a later row on another key whose path runs through
708
+ * the replaced field reads the replacement. A row that does not match captures nothing, so
709
+ * several rows on one key, each with its own `setValueWhenMatch`, followed by a catch-all row,
710
+ * work as a lookup table. At the ticket's condition keys and values are literal: `${...}` inside
711
+ * a row is plain text. In a cond action the key is literal but `value` and `setValueWhenMatch`
712
+ * may hold `${response...}` and `${placeholder[NAME]}`, resolved before the comparison.
707
713
  * - A capture-only row (a `placeholder` and no `operator`) never counts toward passing. It
708
714
  * captures whenever its field exists.
709
715
  * - Header names ignore case, and header values are always strings. Query string values are
@@ -711,7 +717,7 @@ type TicketConditionOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne
711
717
  * `?code=LAUNCH24` the string "LAUNCH24". '=' is strict, so it compares the parsed value: a
712
718
  * `params` row needs `value: 2`, not "2", to match `?qty=2`.
713
719
  *
714
- * null and undefined (`data` and `params` rows only, in a response condition too):
720
+ * null and undefined (`body` and `params` rows only, in a response condition too):
715
721
  * - `= null` passes when the field is present and null. `!= null` passes otherwise, a missing field included.
716
722
  * - `= undefined` passes when the field is missing. `!= undefined` passes when it is present, null included.
717
723
  * - '>', '>=', '<' and '<=' never pass with null or undefined.
@@ -721,9 +727,9 @@ type TicketConditionOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne
721
727
  type TicketConditionRow = {
722
728
  /**
723
729
  * Where the row looks, relative to its own list and written without `${ }`:
724
- * - `data` rows: a path into the request body. "id" is the body's `id`, "order[id]" is `order.id`.
730
+ * - `body` rows: a path into the request body. "id" is the body's `id`, "order[id]" is `order.id`.
725
731
  * - `params` rows: a path into the query string, read the same way.
726
- * - `data` rows of a req action's response condition: a path into the response body ("status" is the response's `status`).
732
+ * - `response` rows of a cond action: a path into the enclosing action's answer ("body[status]" is a response's body.status, "record_id" a posted record's id); "" is the whole answer as text.
727
733
  * - `headers` rows: the header name, matched case-insensitively.
728
734
  * - `user` rows: the consumer attribute name.
729
735
  *
@@ -735,13 +741,13 @@ type TicketConditionRow = {
735
741
  /**
736
742
  * A literal, never templated. A list is read as described in TicketConditionOperator.
737
743
  * null, a list containing null, and undefined (an `operator` with no `value` key) are for
738
- * `data` and `params` rows only.
744
+ * `body` and `params` rows only.
739
745
  */
740
746
  value?: string | number | boolean | null | undefined | Array<string | number | boolean | null>;
741
- /** `data` and `params` rows only. When this row is the first matching row of its key, the value at `key` in the request data is replaced by this before anything else reads it. null replaces nothing. */
747
+ /** `body` and `params` rows only. When this row is the first matching row of its key, the value at `key` in the request data is replaced by this before anything else reads it. null replaces nothing. */
742
748
  setValueWhenMatch?: any;
743
749
  /**
744
- * `data` and `params` rows only. Remembers the value at `key` under this name, and the
750
+ * `body` and `params` rows only. Remembers the value at `key` under this name, and the
745
751
  * actions read it as `${placeholder[NAME]}`. A match row captures only when it is the first
746
752
  * matching row of its key. A field that is missing captures nothing. Must match
747
753
  * ^[A-Za-z_][A-Za-z0-9_]*$.
@@ -827,7 +833,8 @@ type TicketCondition = {
827
833
  /** Rows against the request headers. See TicketConditionRow. Every row compares with a value: null, or no `value`, is refused. */
828
834
  headers?: TicketConditionRow[];
829
835
  /** Rows against the request body. Refused when `method` is 'GET': a GET request has no body. */
830
- data?: TicketConditionRow[];
836
+ /** Rows against the request body ("data" before 2026-10-09; registration still accepts that name and stores "body"). */
837
+ body?: TicketConditionRow[];
831
838
  /** Rows against the query string, read on GET and on POST. */
832
839
  params?: TicketConditionRow[];
833
840
  /**
@@ -845,13 +852,15 @@ type TicketCondition = {
845
852
  record_access?: string;
846
853
  };
847
854
  /**
848
- * What a `req` action checks on the response before its nested actions run: `headers` and
849
- * `data` rows work as in the ticket's condition (captures and `setValueWhenMatch` included).
850
- * `headers` rows read the response headers and `data` row keys are paths into the parsed
851
- * response body. `user` and `record_access` still check the consumer, so they only work for
852
- * signed requests. A response that is not 2xx fails before this is checked.
855
+ * Skapi's own billing rows only (the super master registers them): the response check a `req`
856
+ * carried before 2026-10-09. Anyone else checks a response with a cond action in the req's
857
+ * Then chain. `headers` rows read the response headers and `data` row keys are paths into the
858
+ * parsed body (they keep that name here: the ticket's own body rows are `body`); `user` and
859
+ * `record_access` check the consumer.
853
860
  */
854
- type TicketResponseCondition = Pick<TicketCondition, 'headers' | 'data' | 'user' | 'record_access'>;
861
+ type TicketResponseCondition = Pick<TicketCondition, 'headers' | 'user' | 'record_access'> & {
862
+ data?: TicketConditionRow[];
863
+ };
855
864
  /**
856
865
  * One step of a ticket's action chain. Actions run in order. When an action fails its `err`
857
866
  * chain runs and the consumption stops. Nothing is rolled back.
@@ -864,14 +873,14 @@ type TicketResponseCondition = Pick<TicketCondition, 'headers' | 'data' | 'user'
864
873
  *
865
874
  * | Reference | Reads |
866
875
  * |---|---|
867
- * | `${data}`, `${data[key]}` | The incoming request body (a text body too), or one key of it: `${data[id]}` is the body's `id`, `${data[order][id]}` its `order.id`. Always the incoming body, also in nested actions. |
876
+ * | `${body}`, `${body[key]}` | The incoming request body (a text body too), or one key of it: `${body[id]}` is the body's `id`, `${body[order][id]}` its `order.id`. Always the incoming body, also in nested actions. |
868
877
  * | `${params}`, `${params[key]}` | The incoming query string, or one key of it. |
869
878
  * | `${headers[name]}` | An incoming header, name case-insensitive. Authorization and Cookie read "<redacted>". |
870
879
  * | `${placeholder[NAME]}` | A value a condition row captured with `placeholder`. |
871
880
  * | `${user}`, `${user[key]}` | The signed-in user's attributes. Signed requests only. |
872
881
  * | `${ip}`, `${user_agent}`, `${method}` | The caller's IP address, User-Agent and HTTP method. |
873
882
  * | `${record_access}` | The record ID the condition's `record_access` names. Signed requests only. |
874
- * | `${response}`, `${response[key]}` | The answer of the enclosing action, in its Then chain (`actions`) and the `err` chains inside it: the record a pstr posted, the parsed body of a req's response, the SUCCESS text of acsg and acsr. |
883
+ * | `${response}`, `${response[key]}` | The answer of the enclosing action, in its Then chain (`actions`) and the `err` chains inside it: the record a pstr posted, the response of a req ({ status, headers, body }), the SUCCESS text of acsg, acsr, mail and nlsd. |
875
884
  * | `${error}`, `${error[key]}` | In an `err` chain, the failure: `code`, `message`, `detail`, `action` (the act) and `path`. |
876
885
  * | `${ticket}`, `${ticket[key]}` | This consumption: `id`, `service`, `owner`, `consume_id`, `timestamp` and `hash`. |
877
886
  * | `${CLIENT_SECRET}` | Reserved. See `secretName` on the req action. |
@@ -879,19 +888,21 @@ type TicketResponseCondition = Pick<TicketCondition, 'headers' | 'data' | 'user'
879
888
  * Registration refuses anything else inside `${ }` (an unknown root such as `${id}`, keys under
880
889
  * a root that takes none such as `${ip[x]}`, `${placeholder}` or `${headers}` without a key,
881
890
  * broken brackets) with a message listing these forms, `${result}` (gone: an answer is read as
882
- * `${response}` in the action's Then chain, or captured there by a Condition action), and a reference written
891
+ * `${response}` in the action's Then chain, or captured there by a Check answer action), and a reference written
883
892
  * where it can never resolve (`${response}` outside a Then chain, `${error}` outside an `err`
884
893
  * chain, `${record_access}` when the condition names no record). When the action runs, a
885
894
  * reference that does not resolve fails it before it does anything: PATH_NOT_FOUND,
886
895
  * PLACEHOLDER_MISSING for a placeholder that was never captured, or AUTH_REQUIRED for `${user}`
887
896
  * on a request that is not signed in. Its `err` chain runs and the consumption stops.
888
897
  *
889
- * Every action but resp and cond answers something (the record, the response body, a SUCCESS
898
+ * Every action but resp and cond answers something (the record, the response as { status, headers, body }, a SUCCESS
890
899
  * text) and may carry a Then chain (`actions`, reading the answer as `${response}`). A Condition
891
900
  * action in that chain checks the answer: its `response` rows read it whole (key "") or by key,
892
901
  * whatever its shape. `retry: true` on acsg, acsr, pstr, req, mail and nlsd tries a failure again,
893
902
  * up to 3 more times, 1, 2 and 4 seconds apart, while the time budget holds them; never for a
894
- * failure inside Then.
903
+ * failure inside Then, nor for a refusal waiting cannot change (a bad address, a missing template
904
+ * or newsletter, the month's e-mail sends used up). An e-mail or newsletter already queued is
905
+ * never sent twice: bookkeeping that fails afterwards is a `warning` on the log row, not a failure.
895
906
  */
896
907
  type TicketAction = {
897
908
  /** Set the access group of a user. Answers the SUCCESS text the grant returns. */
@@ -947,11 +958,11 @@ type TicketAction = {
947
958
  err?: TicketAction[];
948
959
  retry?: boolean;
949
960
  } | {
950
- /** HTTP request with its own response condition and nested chain. Answers the parsed response body. */
961
+ /** HTTP request with a nested chain on its response. Answers the whole response: { status, headers, body }. */
951
962
  act: 'req';
952
963
  exe: {
953
964
  /**
954
- * http:// or https:// with a hostname, such as "https://api.example.com/orders/${data[id]}".
965
+ * http:// or https:// with a hostname, such as "https://api.example.com/orders/${body[id]}".
955
966
  * The scheme is always written out, so a URL that is one whole reference is refused.
956
967
  * No IP literal, no userinfo, never under the api domain. Redirects are not followed.
957
968
  * A value put into the URL with `${...}` is percent-encoded, so it cannot add path
@@ -1012,12 +1023,14 @@ type TicketAction = {
1012
1023
  };
1013
1024
  /** Legacy. Registration moves these rows to the end of `condition.data`. */
1014
1025
  match?: TicketConditionRow[];
1015
- /** Skapi's own billing rows only. A response check is a Condition action in `actions`, whose `response` rows read the answer. */
1026
+ /** Skapi's own billing rows only. A response check is a Check answer action (cond) in `actions`, whose `response` rows read the answer. */
1016
1027
  condition?: TicketResponseCondition;
1017
1028
  /**
1018
- * Nested chain, run when the response passes `condition`. It reads the response body
1019
- * as `${response}` and `${response[key]}`, while `${data}` is still the incoming
1020
- * request body. Any action can nest, including another req with its own `secretName`.
1029
+ * Nested chain, run on an answer below 300. It reads the response as `${response}`:
1030
+ * `${response[status]}`, `${response[headers][content-type]}` (header names in
1031
+ * lowercase) and `${response[body][key]}`, while `${body}` is still the incoming
1032
+ * request body. A cond in it checks the response. Any action can nest, including
1033
+ * another req with its own `secretName`.
1021
1034
  */
1022
1035
  actions?: TicketAction[];
1023
1036
  };
@@ -1034,10 +1047,10 @@ type TicketAction = {
1034
1047
  exe: {
1035
1048
  /** The template's ID, as the Custom tab and the upload reply show it. Literal; it must be stored when the ticket is registered. */
1036
1049
  template: string;
1037
- /** The recipient: one address, or a reference that gives one, such as "${user[email]}" or "${data[email]}". */
1050
+ /** The recipient: one address, or a reference that gives one, such as "${user[email]}" or "${body[email]}". */
1038
1051
  to: string;
1039
1052
  /**
1040
- * Values for the placeholders the template carries, by name: { "order": "${data[order][id]}" } fills
1053
+ * Values for the placeholders the template carries, by name: { "order": "${body[order][id]}" } fills
1041
1054
  * ${order}. Templated. "service_name" and "email" (the recipient) are filled by default and may be
1042
1055
  * overwritten here. Up to 30.
1043
1056
  */
@@ -1089,29 +1102,22 @@ type TicketAction = {
1089
1102
  err?: TicketAction[];
1090
1103
  } | {
1091
1104
  /**
1092
- * Condition: rows inside a chain, under the rules of the ticket's own condition. A part
1093
- * that fails fails the chain here with CONDITION_FAILED (the action's `err` chain runs).
1094
- * Answers nothing. `response` rows only inside a Then chain, `error` rows only inside an
1095
- * `err` chain, `placeholder` rows name placeholders (and may compare with null).
1105
+ * Check answer: rows on the answer of the action whose Then chain this is, under the
1106
+ * rules of the ticket's own condition rows. A key no row matches fails the chain here with
1107
+ * CONDITION_FAILED (the enclosing action's `err` chain runs). Answers nothing. Inside a
1108
+ * Then chain only.
1096
1109
  */
1097
1110
  act: 'cond';
1098
1111
  exe: {
1099
- data?: TicketConditionRow[];
1100
- params?: TicketConditionRow[];
1101
- headers?: TicketCondition['headers'];
1102
- ip?: TicketCondition['ip'];
1103
- user_agent?: TicketCondition['user_agent'];
1104
- user?: TicketCondition['user'];
1105
- record_access?: string;
1106
- placeholder?: {
1107
- key: string;
1108
- operator: TicketConditionOperator;
1109
- value: any;
1110
- }[];
1111
- /** Rows on the enclosing action's answer, inside a Then chain only: a key reads a field, "" the whole answer as text. */
1112
- response?: TicketConditionRow[];
1113
- /** Rows on the failure, inside an err chain only: code, message, detail, action, path, or "" for the whole error as text. */
1114
- error?: TicketConditionRow[];
1112
+ /**
1113
+ * Rows on the answer of the action whose Then chain this Condition is in, whatever its shape: a key is
1114
+ * a path in the answer ("record_id" on a posted record, "body[status]" or "headers[content-type]" on a
1115
+ * response), "" the whole answer as text. `value` and `setValueWhenMatch` may hold `${response...}` and
1116
+ * `${placeholder[NAME]}`, resolved right before the comparison; any other reference is refused. A row's
1117
+ * placeholder captures the replaced value when the row has one. A key no row matches fails the chain
1118
+ * here with CONDITION_FAILED. Inside a Then chain only; answers nothing.
1119
+ */
1120
+ response: TicketConditionRow[];
1115
1121
  };
1116
1122
  err?: TicketAction[];
1117
1123
  };
@@ -1153,6 +1159,15 @@ type Ticket = {
1153
1159
  * https://docs.skapi.com/deprecated/deprecated.html#tickets-saved-before-this-release
1154
1160
  */
1155
1161
  legacy?: boolean;
1162
+ /**
1163
+ * The rules the ticket was saved under. 2: an HTTP request's answer is the whole response
1164
+ * ({ status, headers, body }). Absent on a ticket saved before 2026-10-09: its req actions
1165
+ * keep answering the body alone, as it was written to read them, until it is registered
1166
+ * again (the dashboard moves such references under [body] when the ticket is opened).
1167
+ * Returned to the project owner. See
1168
+ * https://docs.skapi.com/deprecated/deprecated.html#a-request-s-answer-was-its-body
1169
+ */
1170
+ rules_version?: number;
1156
1171
  };
1157
1172
  type TicketErrorCode = 'INVALID_SERVICE' | 'SERVICE_DISABLED' | 'TICKET_NOT_FOUND' | 'TICKET_EXPIRED' | 'TICKET_EXHAUSTED' | 'USER_LIMIT_REACHED' | 'ISSUER_CANNOT_CONSUME' | 'AUTH_REQUIRED' | 'METHOD_NOT_ALLOWED' | 'CONDITION_FAILED' | 'PATH_NOT_FOUND' | 'PLACEHOLDER_MISSING' | 'REQUEST_FAILED' | 'TIMEOUT' | 'ACTION_FAILED' | 'ACTION_FORBIDDEN' | 'QUOTA_EXCEEDED' | 'ALREADY_RESPONDED' | 'INTERNAL_ERROR';
1158
1173
  /**
package/dist/skapi.d.ts CHANGED
@@ -691,8 +691,9 @@ type Subscription = {
691
691
  */
692
692
  type TicketConditionOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
693
693
  /**
694
- * One row of a ticket condition list: `headers`, `data`, `params` or `user` of a ticket's
695
- * condition, or `headers`, `data` or `user` of a req action's response condition.
694
+ * One row of a ticket condition list: `headers`, `body`, `params` or `user` of a ticket's
695
+ * condition, the `response` rows of a cond action, or `headers`, `data` or `user` of the
696
+ * response condition Skapi's own rows carry.
696
697
  *
697
698
  * How a list decides:
698
699
  * - Rows on the same key are alternatives: the key passes when any one of them matches.
@@ -701,9 +702,14 @@ type TicketConditionOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne
701
702
  * - A field missing from the request is a mismatch for its key, '!=' included, not an error.
702
703
  * The only rows that pass on a missing field are `= undefined` and `!= null`.
703
704
  * - Every row is read, in order. For each key the FIRST matching row wins: its `setValueWhenMatch`
704
- * applies and its `placeholder` captures, and later rows on that key are skipped. A row that
705
- * does not match captures nothing, so several rows on one key, each with its own
706
- * `setValueWhenMatch`, followed by a catch-all row, work as a lookup table.
705
+ * replaces the value (in the data itself where the field exists) and its `placeholder` captures
706
+ * the result. The later match rows on that key are skipped without being evaluated, so nothing
707
+ * is re-checked against the replaced value; a later row on another key whose path runs through
708
+ * the replaced field reads the replacement. A row that does not match captures nothing, so
709
+ * several rows on one key, each with its own `setValueWhenMatch`, followed by a catch-all row,
710
+ * work as a lookup table. At the ticket's condition keys and values are literal: `${...}` inside
711
+ * a row is plain text. In a cond action the key is literal but `value` and `setValueWhenMatch`
712
+ * may hold `${response...}` and `${placeholder[NAME]}`, resolved before the comparison.
707
713
  * - A capture-only row (a `placeholder` and no `operator`) never counts toward passing. It
708
714
  * captures whenever its field exists.
709
715
  * - Header names ignore case, and header values are always strings. Query string values are
@@ -711,7 +717,7 @@ type TicketConditionOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne
711
717
  * `?code=LAUNCH24` the string "LAUNCH24". '=' is strict, so it compares the parsed value: a
712
718
  * `params` row needs `value: 2`, not "2", to match `?qty=2`.
713
719
  *
714
- * null and undefined (`data` and `params` rows only, in a response condition too):
720
+ * null and undefined (`body` and `params` rows only, in a response condition too):
715
721
  * - `= null` passes when the field is present and null. `!= null` passes otherwise, a missing field included.
716
722
  * - `= undefined` passes when the field is missing. `!= undefined` passes when it is present, null included.
717
723
  * - '>', '>=', '<' and '<=' never pass with null or undefined.
@@ -721,9 +727,9 @@ type TicketConditionOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne
721
727
  type TicketConditionRow = {
722
728
  /**
723
729
  * Where the row looks, relative to its own list and written without `${ }`:
724
- * - `data` rows: a path into the request body. "id" is the body's `id`, "order[id]" is `order.id`.
730
+ * - `body` rows: a path into the request body. "id" is the body's `id`, "order[id]" is `order.id`.
725
731
  * - `params` rows: a path into the query string, read the same way.
726
- * - `data` rows of a req action's response condition: a path into the response body ("status" is the response's `status`).
732
+ * - `response` rows of a cond action: a path into the enclosing action's answer ("body[status]" is a response's body.status, "record_id" a posted record's id); "" is the whole answer as text.
727
733
  * - `headers` rows: the header name, matched case-insensitively.
728
734
  * - `user` rows: the consumer attribute name.
729
735
  *
@@ -735,13 +741,13 @@ type TicketConditionRow = {
735
741
  /**
736
742
  * A literal, never templated. A list is read as described in TicketConditionOperator.
737
743
  * null, a list containing null, and undefined (an `operator` with no `value` key) are for
738
- * `data` and `params` rows only.
744
+ * `body` and `params` rows only.
739
745
  */
740
746
  value?: string | number | boolean | null | undefined | Array<string | number | boolean | null>;
741
- /** `data` and `params` rows only. When this row is the first matching row of its key, the value at `key` in the request data is replaced by this before anything else reads it. null replaces nothing. */
747
+ /** `body` and `params` rows only. When this row is the first matching row of its key, the value at `key` in the request data is replaced by this before anything else reads it. null replaces nothing. */
742
748
  setValueWhenMatch?: any;
743
749
  /**
744
- * `data` and `params` rows only. Remembers the value at `key` under this name, and the
750
+ * `body` and `params` rows only. Remembers the value at `key` under this name, and the
745
751
  * actions read it as `${placeholder[NAME]}`. A match row captures only when it is the first
746
752
  * matching row of its key. A field that is missing captures nothing. Must match
747
753
  * ^[A-Za-z_][A-Za-z0-9_]*$.
@@ -827,7 +833,8 @@ type TicketCondition = {
827
833
  /** Rows against the request headers. See TicketConditionRow. Every row compares with a value: null, or no `value`, is refused. */
828
834
  headers?: TicketConditionRow[];
829
835
  /** Rows against the request body. Refused when `method` is 'GET': a GET request has no body. */
830
- data?: TicketConditionRow[];
836
+ /** Rows against the request body ("data" before 2026-10-09; registration still accepts that name and stores "body"). */
837
+ body?: TicketConditionRow[];
831
838
  /** Rows against the query string, read on GET and on POST. */
832
839
  params?: TicketConditionRow[];
833
840
  /**
@@ -845,13 +852,15 @@ type TicketCondition = {
845
852
  record_access?: string;
846
853
  };
847
854
  /**
848
- * What a `req` action checks on the response before its nested actions run: `headers` and
849
- * `data` rows work as in the ticket's condition (captures and `setValueWhenMatch` included).
850
- * `headers` rows read the response headers and `data` row keys are paths into the parsed
851
- * response body. `user` and `record_access` still check the consumer, so they only work for
852
- * signed requests. A response that is not 2xx fails before this is checked.
855
+ * Skapi's own billing rows only (the super master registers them): the response check a `req`
856
+ * carried before 2026-10-09. Anyone else checks a response with a cond action in the req's
857
+ * Then chain. `headers` rows read the response headers and `data` row keys are paths into the
858
+ * parsed body (they keep that name here: the ticket's own body rows are `body`); `user` and
859
+ * `record_access` check the consumer.
853
860
  */
854
- type TicketResponseCondition = Pick<TicketCondition, 'headers' | 'data' | 'user' | 'record_access'>;
861
+ type TicketResponseCondition = Pick<TicketCondition, 'headers' | 'user' | 'record_access'> & {
862
+ data?: TicketConditionRow[];
863
+ };
855
864
  /**
856
865
  * One step of a ticket's action chain. Actions run in order. When an action fails its `err`
857
866
  * chain runs and the consumption stops. Nothing is rolled back.
@@ -864,14 +873,14 @@ type TicketResponseCondition = Pick<TicketCondition, 'headers' | 'data' | 'user'
864
873
  *
865
874
  * | Reference | Reads |
866
875
  * |---|---|
867
- * | `${data}`, `${data[key]}` | The incoming request body (a text body too), or one key of it: `${data[id]}` is the body's `id`, `${data[order][id]}` its `order.id`. Always the incoming body, also in nested actions. |
876
+ * | `${body}`, `${body[key]}` | The incoming request body (a text body too), or one key of it: `${body[id]}` is the body's `id`, `${body[order][id]}` its `order.id`. Always the incoming body, also in nested actions. |
868
877
  * | `${params}`, `${params[key]}` | The incoming query string, or one key of it. |
869
878
  * | `${headers[name]}` | An incoming header, name case-insensitive. Authorization and Cookie read "<redacted>". |
870
879
  * | `${placeholder[NAME]}` | A value a condition row captured with `placeholder`. |
871
880
  * | `${user}`, `${user[key]}` | The signed-in user's attributes. Signed requests only. |
872
881
  * | `${ip}`, `${user_agent}`, `${method}` | The caller's IP address, User-Agent and HTTP method. |
873
882
  * | `${record_access}` | The record ID the condition's `record_access` names. Signed requests only. |
874
- * | `${response}`, `${response[key]}` | The answer of the enclosing action, in its Then chain (`actions`) and the `err` chains inside it: the record a pstr posted, the parsed body of a req's response, the SUCCESS text of acsg and acsr. |
883
+ * | `${response}`, `${response[key]}` | The answer of the enclosing action, in its Then chain (`actions`) and the `err` chains inside it: the record a pstr posted, the response of a req ({ status, headers, body }), the SUCCESS text of acsg, acsr, mail and nlsd. |
875
884
  * | `${error}`, `${error[key]}` | In an `err` chain, the failure: `code`, `message`, `detail`, `action` (the act) and `path`. |
876
885
  * | `${ticket}`, `${ticket[key]}` | This consumption: `id`, `service`, `owner`, `consume_id`, `timestamp` and `hash`. |
877
886
  * | `${CLIENT_SECRET}` | Reserved. See `secretName` on the req action. |
@@ -879,19 +888,21 @@ type TicketResponseCondition = Pick<TicketCondition, 'headers' | 'data' | 'user'
879
888
  * Registration refuses anything else inside `${ }` (an unknown root such as `${id}`, keys under
880
889
  * a root that takes none such as `${ip[x]}`, `${placeholder}` or `${headers}` without a key,
881
890
  * broken brackets) with a message listing these forms, `${result}` (gone: an answer is read as
882
- * `${response}` in the action's Then chain, or captured there by a Condition action), and a reference written
891
+ * `${response}` in the action's Then chain, or captured there by a Check answer action), and a reference written
883
892
  * where it can never resolve (`${response}` outside a Then chain, `${error}` outside an `err`
884
893
  * chain, `${record_access}` when the condition names no record). When the action runs, a
885
894
  * reference that does not resolve fails it before it does anything: PATH_NOT_FOUND,
886
895
  * PLACEHOLDER_MISSING for a placeholder that was never captured, or AUTH_REQUIRED for `${user}`
887
896
  * on a request that is not signed in. Its `err` chain runs and the consumption stops.
888
897
  *
889
- * Every action but resp and cond answers something (the record, the response body, a SUCCESS
898
+ * Every action but resp and cond answers something (the record, the response as { status, headers, body }, a SUCCESS
890
899
  * text) and may carry a Then chain (`actions`, reading the answer as `${response}`). A Condition
891
900
  * action in that chain checks the answer: its `response` rows read it whole (key "") or by key,
892
901
  * whatever its shape. `retry: true` on acsg, acsr, pstr, req, mail and nlsd tries a failure again,
893
902
  * up to 3 more times, 1, 2 and 4 seconds apart, while the time budget holds them; never for a
894
- * failure inside Then.
903
+ * failure inside Then, nor for a refusal waiting cannot change (a bad address, a missing template
904
+ * or newsletter, the month's e-mail sends used up). An e-mail or newsletter already queued is
905
+ * never sent twice: bookkeeping that fails afterwards is a `warning` on the log row, not a failure.
895
906
  */
896
907
  type TicketAction = {
897
908
  /** Set the access group of a user. Answers the SUCCESS text the grant returns. */
@@ -947,11 +958,11 @@ type TicketAction = {
947
958
  err?: TicketAction[];
948
959
  retry?: boolean;
949
960
  } | {
950
- /** HTTP request with its own response condition and nested chain. Answers the parsed response body. */
961
+ /** HTTP request with a nested chain on its response. Answers the whole response: { status, headers, body }. */
951
962
  act: 'req';
952
963
  exe: {
953
964
  /**
954
- * http:// or https:// with a hostname, such as "https://api.example.com/orders/${data[id]}".
965
+ * http:// or https:// with a hostname, such as "https://api.example.com/orders/${body[id]}".
955
966
  * The scheme is always written out, so a URL that is one whole reference is refused.
956
967
  * No IP literal, no userinfo, never under the api domain. Redirects are not followed.
957
968
  * A value put into the URL with `${...}` is percent-encoded, so it cannot add path
@@ -1012,12 +1023,14 @@ type TicketAction = {
1012
1023
  };
1013
1024
  /** Legacy. Registration moves these rows to the end of `condition.data`. */
1014
1025
  match?: TicketConditionRow[];
1015
- /** Skapi's own billing rows only. A response check is a Condition action in `actions`, whose `response` rows read the answer. */
1026
+ /** Skapi's own billing rows only. A response check is a Check answer action (cond) in `actions`, whose `response` rows read the answer. */
1016
1027
  condition?: TicketResponseCondition;
1017
1028
  /**
1018
- * Nested chain, run when the response passes `condition`. It reads the response body
1019
- * as `${response}` and `${response[key]}`, while `${data}` is still the incoming
1020
- * request body. Any action can nest, including another req with its own `secretName`.
1029
+ * Nested chain, run on an answer below 300. It reads the response as `${response}`:
1030
+ * `${response[status]}`, `${response[headers][content-type]}` (header names in
1031
+ * lowercase) and `${response[body][key]}`, while `${body}` is still the incoming
1032
+ * request body. A cond in it checks the response. Any action can nest, including
1033
+ * another req with its own `secretName`.
1021
1034
  */
1022
1035
  actions?: TicketAction[];
1023
1036
  };
@@ -1034,10 +1047,10 @@ type TicketAction = {
1034
1047
  exe: {
1035
1048
  /** The template's ID, as the Custom tab and the upload reply show it. Literal; it must be stored when the ticket is registered. */
1036
1049
  template: string;
1037
- /** The recipient: one address, or a reference that gives one, such as "${user[email]}" or "${data[email]}". */
1050
+ /** The recipient: one address, or a reference that gives one, such as "${user[email]}" or "${body[email]}". */
1038
1051
  to: string;
1039
1052
  /**
1040
- * Values for the placeholders the template carries, by name: { "order": "${data[order][id]}" } fills
1053
+ * Values for the placeholders the template carries, by name: { "order": "${body[order][id]}" } fills
1041
1054
  * ${order}. Templated. "service_name" and "email" (the recipient) are filled by default and may be
1042
1055
  * overwritten here. Up to 30.
1043
1056
  */
@@ -1089,29 +1102,22 @@ type TicketAction = {
1089
1102
  err?: TicketAction[];
1090
1103
  } | {
1091
1104
  /**
1092
- * Condition: rows inside a chain, under the rules of the ticket's own condition. A part
1093
- * that fails fails the chain here with CONDITION_FAILED (the action's `err` chain runs).
1094
- * Answers nothing. `response` rows only inside a Then chain, `error` rows only inside an
1095
- * `err` chain, `placeholder` rows name placeholders (and may compare with null).
1105
+ * Check answer: rows on the answer of the action whose Then chain this is, under the
1106
+ * rules of the ticket's own condition rows. A key no row matches fails the chain here with
1107
+ * CONDITION_FAILED (the enclosing action's `err` chain runs). Answers nothing. Inside a
1108
+ * Then chain only.
1096
1109
  */
1097
1110
  act: 'cond';
1098
1111
  exe: {
1099
- data?: TicketConditionRow[];
1100
- params?: TicketConditionRow[];
1101
- headers?: TicketCondition['headers'];
1102
- ip?: TicketCondition['ip'];
1103
- user_agent?: TicketCondition['user_agent'];
1104
- user?: TicketCondition['user'];
1105
- record_access?: string;
1106
- placeholder?: {
1107
- key: string;
1108
- operator: TicketConditionOperator;
1109
- value: any;
1110
- }[];
1111
- /** Rows on the enclosing action's answer, inside a Then chain only: a key reads a field, "" the whole answer as text. */
1112
- response?: TicketConditionRow[];
1113
- /** Rows on the failure, inside an err chain only: code, message, detail, action, path, or "" for the whole error as text. */
1114
- error?: TicketConditionRow[];
1112
+ /**
1113
+ * Rows on the answer of the action whose Then chain this Condition is in, whatever its shape: a key is
1114
+ * a path in the answer ("record_id" on a posted record, "body[status]" or "headers[content-type]" on a
1115
+ * response), "" the whole answer as text. `value` and `setValueWhenMatch` may hold `${response...}` and
1116
+ * `${placeholder[NAME]}`, resolved right before the comparison; any other reference is refused. A row's
1117
+ * placeholder captures the replaced value when the row has one. A key no row matches fails the chain
1118
+ * here with CONDITION_FAILED. Inside a Then chain only; answers nothing.
1119
+ */
1120
+ response: TicketConditionRow[];
1115
1121
  };
1116
1122
  err?: TicketAction[];
1117
1123
  };
@@ -1153,6 +1159,15 @@ type Ticket = {
1153
1159
  * https://docs.skapi.com/deprecated/deprecated.html#tickets-saved-before-this-release
1154
1160
  */
1155
1161
  legacy?: boolean;
1162
+ /**
1163
+ * The rules the ticket was saved under. 2: an HTTP request's answer is the whole response
1164
+ * ({ status, headers, body }). Absent on a ticket saved before 2026-10-09: its req actions
1165
+ * keep answering the body alone, as it was written to read them, until it is registered
1166
+ * again (the dashboard moves such references under [body] when the ticket is opened).
1167
+ * Returned to the project owner. See
1168
+ * https://docs.skapi.com/deprecated/deprecated.html#a-request-s-answer-was-its-body
1169
+ */
1170
+ rules_version?: number;
1156
1171
  };
1157
1172
  type TicketErrorCode = 'INVALID_SERVICE' | 'SERVICE_DISABLED' | 'TICKET_NOT_FOUND' | 'TICKET_EXPIRED' | 'TICKET_EXHAUSTED' | 'USER_LIMIT_REACHED' | 'ISSUER_CANNOT_CONSUME' | 'AUTH_REQUIRED' | 'METHOD_NOT_ALLOWED' | 'CONDITION_FAILED' | 'PATH_NOT_FOUND' | 'PLACEHOLDER_MISSING' | 'REQUEST_FAILED' | 'TIMEOUT' | 'ACTION_FAILED' | 'ACTION_FORBIDDEN' | 'QUOTA_EXCEEDED' | 'ALREADY_RESPONDED' | 'INTERNAL_ERROR';
1158
1173
  /**