@codespar/sdk 0.16.9 → 0.16.11

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.
@@ -2873,7 +2873,9 @@ export interface paths {
2873
2873
  };
2874
2874
  /**
2875
2875
  * List approvals held for review
2876
- * @description The organization's holds, newest first.
2876
+ * @description The holds, newest first.
2877
+ *
2878
+ * **What a key sees.** A project API key or an OAuth token reads only the holds raised under ITS OWN project, and only their status: `tool_input`, the held request itself, is not in the response. A hold raised outside any project is not a key's either. The dashboard (service auth) reads the whole organization's queue with `tool_input`, because the approver works there.
2877
2879
  *
2878
2880
  * **Without `status`, you get only the `pending` ones**, not all of them. It is the useful default for an operator queue, and the trap for anyone expecting a history: ask for `status=approved,denied` to see what has already been decided.
2879
2881
  *
@@ -2911,8 +2913,8 @@ export interface paths {
2911
2913
  matched_rule_name: string;
2912
2914
  /** @description The held call, in whatever form the lane that raised the hold writes. There is no single format: see the operation description before matching on it. */
2913
2915
  tool_name: string;
2914
- /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. */
2915
- tool_input: {
2916
+ /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. ABSENT unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
2917
+ tool_input?: {
2916
2918
  [key: string]: unknown;
2917
2919
  };
2918
2920
  /** @enum {string} */
@@ -2990,7 +2992,7 @@ export interface paths {
2990
2992
  *
2991
2993
  * **`reason` is required for both decisions.** Up to 8000 characters, and whitespace alone does not count: the audit trail of a human decision with no written reason is worth nothing later.
2992
2994
  *
2993
- * **Deciding takes a PERSON, not a key.** A bearer credential alone is refused with `bearer_token_cannot_decide`: the user token has to come with it, be verifiable, and its identity has to match the one the header asserts. That is what separates `the organization decided` from `someone with the key decided`.
2995
+ * **Deciding takes a PERSON, through the dashboard.** Only service auth (the dashboard) may call this. A project API key or an OAuth token is refused with `bearer_token_cannot_decide` before the body is read, even when it carries a user token: a key or token proves which integration is calling, never that a person looked at the hold. On the dashboard's call the approver's user token has to come with it, be verifiable, and its identity has to match the one the header asserts. That is what separates `the organization decided` from `someone with the key decided`.
2994
2996
  *
2995
2997
  * **A decision already taken is 409, and an expired one is 410.** In both cases the full approval comes in the body, so the caller can see who decided what without a second read.
2996
2998
  */
@@ -3033,8 +3035,8 @@ export interface paths {
3033
3035
  matched_rule_name: string;
3034
3036
  /** @description The held call, in whatever form the lane that raised the hold writes. There is no single format: see the operation description before matching on it. */
3035
3037
  tool_name: string;
3036
- /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. */
3037
- tool_input: {
3038
+ /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. ABSENT unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
3039
+ tool_input?: {
3038
3040
  [key: string]: unknown;
3039
3041
  };
3040
3042
  /** @enum {string} */
@@ -3077,7 +3079,7 @@ export interface paths {
3077
3079
  };
3078
3080
  };
3079
3081
  };
3080
- /** @description The caller cannot decide. `bearer_token_cannot_decide` when only a bearer credential arrived; `user_token_required`, `user_token_invalid` and `user_token_identity_mismatch` when the user token is missing, does not verify, or does not match the identity asserted in the header; `insufficient_role` when the person is below admin. Raw body. */
3082
+ /** @description The caller cannot decide. `bearer_token_cannot_decide` when the request is not the dashboard's (service auth): a project API key or an OAuth token, with or without a user token; `user_token_required`, `user_token_invalid` and `user_token_identity_mismatch` when the user token is missing, does not verify, or does not match the identity asserted in the header; `insufficient_role` when the person is below admin. Raw body. */
3081
3083
  403: {
3082
3084
  headers: {
3083
3085
  [name: string]: unknown;
@@ -3125,8 +3127,8 @@ export interface paths {
3125
3127
  matched_rule_name: string;
3126
3128
  /** @description The held call, in whatever form the lane that raised the hold writes. There is no single format: see the operation description before matching on it. */
3127
3129
  tool_name: string;
3128
- /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. */
3129
- tool_input: {
3130
+ /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. ABSENT unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
3131
+ tool_input?: {
3130
3132
  [key: string]: unknown;
3131
3133
  };
3132
3134
  /** @enum {string} */
@@ -3175,8 +3177,8 @@ export interface paths {
3175
3177
  matched_rule_name: string;
3176
3178
  /** @description The held call, in whatever form the lane that raised the hold writes. There is no single format: see the operation description before matching on it. */
3177
3179
  tool_name: string;
3178
- /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. */
3179
- tool_input: {
3180
+ /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. ABSENT unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
3181
+ tool_input?: {
3180
3182
  [key: string]: unknown;
3181
3183
  };
3182
3184
  /** @enum {string} */
@@ -3247,7 +3249,7 @@ export interface paths {
3247
3249
  * Approval queue health
3248
3250
  * @description How deep this organization's approval queue is, how long the oldest hold has been waiting, and whether the sweeps are running.
3249
3251
  *
3250
- * `pending_count` and `oldest_pending_age_seconds` are computed over the caller's own organization, and the second is null exactly when the first is 0: both come from the same `status = 'pending'` filter over the same rows.
3252
+ * `pending_count` and `oldest_pending_age_seconds` are computed over the caller's own organization for the dashboard (service auth), and over the caller's own PROJECT for an API key or an OAuth token, the same rows the list shows it. The second is null exactly when the first is 0: both come from the same `status = 'pending'` filter over the same rows.
3251
3253
  *
3252
3254
  * `last_sweep_at` IS NOT SCOPED TO THE CALLER, and cannot be. It is the newest run of the expiry and orphan sweeps for the whole deployment: the table it reads holds one row per sweep type and carries no organization column, so the value moves for reasons that have nothing to do with this organization. Read it as 'the sweeps are alive here', never as 'my queue was swept'. Null means neither sweep has ever recorded a run.
3253
3255
  *
@@ -3308,9 +3310,11 @@ export interface paths {
3308
3310
  };
3309
3311
  /**
3310
3312
  * Read one approval
3311
- * @description One held call, with the arguments recorded when it was held.
3313
+ * @description One held call. The dashboard gets it with the arguments recorded when it was held; an API key or an OAuth token gets its status only.
3314
+ *
3315
+ * SCOPE. The lookup matches the id AND the caller's organization, so an id from another organization answers 404 rather than 403: a 403 would confirm the id exists, which is what a caller sweeping ids is asking. For an API key or an OAuth token it also matches the credential's project, the way the agent-facing poll at `GET /v1/approvals/{id}/status` does: a hold raised under a sibling project, or outside any project, is the same 404. The dashboard (service auth) reads any hold in the organization.
3312
3316
  *
3313
- * SCOPE. The lookup matches the id AND the caller's organization, so an id from another organization answers 404 rather than 403: a 403 would confirm the id exists, which is what a caller sweeping ids is asking. Inside the organization this read is NOT project-scoped, and the agent-facing poll at `GET /v1/approvals/{id}/status` is: a key bound to one project reads holds raised under a sibling project here, and does not there.
3317
+ * `tool_input` is ABSENT for an API key or an OAuth token: it is the held request itself, which can carry a Pix key, a CPF/CNPJ or account data, and the approver who needs it reads it on the dashboard.
3314
3318
  *
3315
3319
  * `tool_input` IS NOT THE CALL. It is what the lane that raised the hold recorded, and two things happen to it on the way in. A hold raised on the session execute path carries `{}`, because that path hands the policy engine no tool input at all and the engine stores an empty object when it gets none. A hold raised on the proxy path carries `{ server, method, endpoint, body, params, headers }`, with the `authorization` and `cookie` request headers removed before the row is written. So an empty object here means 'this lane records nothing', not 'the agent sent nothing'.
3316
3320
  *
@@ -3350,8 +3354,8 @@ export interface paths {
3350
3354
  matched_rule_name: string;
3351
3355
  /** @description The held call, in whatever form the lane that raised the hold writes. There is no single format: see the operation description before matching on it. */
3352
3356
  tool_name: string;
3353
- /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. */
3354
- tool_input: {
3357
+ /** @description The arguments as RECORDED, which is not always the arguments as sent. Empty for a hold raised on the session execute path, and header-stripped on the proxy path. ABSENT unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
3358
+ tool_input?: {
3355
3359
  [key: string]: unknown;
3356
3360
  };
3357
3361
  /** @enum {string} */
@@ -5159,6 +5163,26 @@ export interface paths {
5159
5163
  };
5160
5164
  };
5161
5165
  };
5166
+ /** @description `ofb_not_live`: this is a LIVE project and the deployment has no real Open Finance adapter, only the stub, which serves test projects. TERMINAL for this project: use a test project. Nothing was written and no bank was contacted. */
5167
+ 422: {
5168
+ headers: {
5169
+ [name: string]: unknown;
5170
+ };
5171
+ content: {
5172
+ "application/json": {
5173
+ error: {
5174
+ /** @enum {string} */
5175
+ code: "ofb_not_live";
5176
+ message: string;
5177
+ details?: {
5178
+ [key: string]: unknown;
5179
+ };
5180
+ };
5181
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
5182
+ request_id: string | null;
5183
+ };
5184
+ };
5185
+ };
5162
5186
  /** @description `consent_disappeared`: the row was written and could not be read back. Do not retry blind; both the row and the bank-side consent may exist. */
5163
5187
  500: {
5164
5188
  headers: {
@@ -5199,7 +5223,11 @@ export interface paths {
5199
5223
  };
5200
5224
  };
5201
5225
  };
5202
- /** @description `consent_lock_timeout`: another request is issuing a consent for this consumer at this bank and the wait ended without an answer, so NOTHING WAS DECIDED. We do not know whether a sibling holds the grant, and saying so is why this is not the 409. `details.retriable` is true. No consent row was created; a bank-side consent was, and is orphaned. */
5226
+ /**
5227
+ * @description `consent_lock_timeout`: another request is issuing a consent for this consumer at this bank and the wait ended without an answer, so NOTHING WAS DECIDED. We do not know whether a sibling holds the grant, and saying so is why this is not the 409. `details.retriable` is true. No consent row was created; a bank-side consent was, and is orphaned.
5228
+ *
5229
+ * `ofb_adapter_unavailable`: the Open Finance adapter this deployment selects cannot serve (`details.reason`: `missing_credentials`, `not_implemented` or `unknown_mode`). Ours to fix, not yours; nothing was written and no bank was contacted.
5230
+ */
5203
5231
  503: {
5204
5232
  headers: {
5205
5233
  [name: string]: unknown;
@@ -5208,7 +5236,7 @@ export interface paths {
5208
5236
  "application/json": {
5209
5237
  error: {
5210
5238
  /** @enum {string} */
5211
- code: "consent_lock_timeout";
5239
+ code: "consent_lock_timeout" | "ofb_adapter_unavailable";
5212
5240
  message: string;
5213
5241
  details?: {
5214
5242
  [key: string]: unknown;
@@ -7947,7 +7975,7 @@ export interface paths {
7947
7975
  requestBody?: {
7948
7976
  content: {
7949
7977
  "application/json": {
7950
- /** @description The identity fields the provider asks for: `documentNumber` (CPF or CNPJ), full name, date of birth, address. Passed through as an object: the handler does not validate it field by field here, and whatever the provider refuses comes back in the provider's own refusal. */
7978
+ /** @description The identity fields the provider asks for: `documentNumber` (CPF or CNPJ), full name, date of birth, address. Passed through as an object: the handler does not validate it field by field here, and whatever the provider refuses comes back in the provider's own refusal. Two exceptions are checked before anything is sent: the document's check digits, and, for a natural person (CPF) in a live project, `financialDetails` = `{ declaredIncome, occupation, netWorth }` in the provider's codes. That is the titular's own declaration, and CodeSpar never fills it in for them. */
7951
7979
  buyer: {
7952
7980
  [key: string]: unknown;
7953
7981
  };
@@ -7995,7 +8023,7 @@ export interface paths {
7995
8023
  };
7996
8024
  };
7997
8025
  };
7998
- /** @description The body did not match the schema, or the document did not pass the check. BARE BODY, not the `{ error: { code, message } }` envelope: this handler writes its failure inline and there is no `request_id`. */
8026
+ /** @description The body did not match the schema, the document did not pass the check, or a natural person's onboarding in a live project came without `buyer.financialDetails` (one issue per missing code, its `path` naming it). BARE BODY, not the `{ error: { code, message } }` envelope: this handler writes its failure inline and there is no `request_id`. */
7999
8027
  400: {
8000
8028
  headers: {
8001
8029
  [name: string]: unknown;
@@ -9333,7 +9361,7 @@ export interface paths {
9333
9361
  ok: false;
9334
9362
  provider: string;
9335
9363
  /** @enum {string} */
9336
- error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable";
9364
+ error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable" | "shared_sandbox_live_project_refused";
9337
9365
  /** @description Whose sentence this is depends on the code, and the difference matters. On `provider_rejected` and `provider_unreachable` it is up to 256 characters of the PROVIDER's own response body, or the transport error. On the others it is text we wrote, naming what is missing on our side. It is absent on `redirect_not_followed`, and on the `server_unknown` and `endpoint_missing` refusals that carry nothing but the code. On `test_venue_unavailable` it names the catalog file and the field to declare, and it is the one refusal here that is about the project's ENVIRONMENT rather than its connection: the identical request from a `live` project reaches the provider. */
9338
9366
  detail?: string;
9339
9367
  /** @description The provider's HTTP status, present only when a round trip completed: on `provider_rejected`, `redirect_not_followed`, and the `provider_unreachable` that came from a response rather than from a transport failure. Its ABSENCE on a 502 is the signal that nothing was reached at all. */
@@ -9368,7 +9396,7 @@ export interface paths {
9368
9396
  ok: false;
9369
9397
  provider: string;
9370
9398
  /** @enum {string} */
9371
- error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable";
9399
+ error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable" | "shared_sandbox_live_project_refused";
9372
9400
  /** @description Whose sentence this is depends on the code, and the difference matters. On `provider_rejected` and `provider_unreachable` it is up to 256 characters of the PROVIDER's own response body, or the transport error. On the others it is text we wrote, naming what is missing on our side. It is absent on `redirect_not_followed`, and on the `server_unknown` and `endpoint_missing` refusals that carry nothing but the code. On `test_venue_unavailable` it names the catalog file and the field to declare, and it is the one refusal here that is about the project's ENVIRONMENT rather than its connection: the identical request from a `live` project reaches the provider. */
9373
9401
  detail?: string;
9374
9402
  /** @description The provider's HTTP status, present only when a round trip completed: on `provider_rejected`, `redirect_not_followed`, and the `provider_unreachable` that came from a response rather than from a transport failure. Its ABSENCE on a 502 is the signal that nothing was reached at all. */
@@ -9398,7 +9426,7 @@ export interface paths {
9398
9426
  ok: false;
9399
9427
  provider: string;
9400
9428
  /** @enum {string} */
9401
- error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable";
9429
+ error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable" | "shared_sandbox_live_project_refused";
9402
9430
  /** @description Whose sentence this is depends on the code, and the difference matters. On `provider_rejected` and `provider_unreachable` it is up to 256 characters of the PROVIDER's own response body, or the transport error. On the others it is text we wrote, naming what is missing on our side. It is absent on `redirect_not_followed`, and on the `server_unknown` and `endpoint_missing` refusals that carry nothing but the code. On `test_venue_unavailable` it names the catalog file and the field to declare, and it is the one refusal here that is about the project's ENVIRONMENT rather than its connection: the identical request from a `live` project reaches the provider. */
9403
9431
  detail?: string;
9404
9432
  /** @description The provider's HTTP status, present only when a round trip completed: on `provider_rejected`, `redirect_not_followed`, and the `provider_unreachable` that came from a response rather than from a transport failure. Its ABSENCE on a 502 is the signal that nothing was reached at all. */
@@ -9417,7 +9445,7 @@ export interface paths {
9417
9445
  };
9418
9446
  };
9419
9447
  };
9420
- /** @description `not_connected`. There is no usable credential to test: no active connection for this project and provider, or the vault returned nothing for the reference it holds. This body carries the extra `hint` / `alternative` steer. Or `connection_expired`: the project has an authorization-code OAuth connection whose access token passed its expiry, and nothing refreshes it, so the fix is to reconnect. That body carries `expires_at` and no sandbox steer. */
9448
+ /** @description `not_connected`. There is no usable credential to test: no active connection for this project and provider, or the vault returned nothing for the reference it holds. This body carries the extra `hint` / `alternative` steer. Or `connection_expired`: the project has an authorization-code OAuth connection whose access token passed its expiry, and nothing refreshes it, so the fix is to reconnect. That body carries `expires_at` and no sandbox steer. Or `shared_sandbox_live_project_refused`: the project's connection for this provider is the platform's shared SANDBOX credential, which serves test projects only, and this is a live project (or a live-mode call). Nothing was sent to the provider; the fix is to connect the project's own credential. */
9421
9449
  424: {
9422
9450
  headers: {
9423
9451
  [name: string]: unknown;
@@ -9428,7 +9456,7 @@ export interface paths {
9428
9456
  ok: false;
9429
9457
  provider: string;
9430
9458
  /** @enum {string} */
9431
- error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable";
9459
+ error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable" | "shared_sandbox_live_project_refused";
9432
9460
  /** @description Whose sentence this is depends on the code, and the difference matters. On `provider_rejected` and `provider_unreachable` it is up to 256 characters of the PROVIDER's own response body, or the transport error. On the others it is text we wrote, naming what is missing on our side. It is absent on `redirect_not_followed`, and on the `server_unknown` and `endpoint_missing` refusals that carry nothing but the code. On `test_venue_unavailable` it names the catalog file and the field to declare, and it is the one refusal here that is about the project's ENVIRONMENT rather than its connection: the identical request from a `live` project reaches the provider. */
9433
9461
  detail?: string;
9434
9462
  /** @description The provider's HTTP status, present only when a round trip completed: on `provider_rejected`, `redirect_not_followed`, and the `provider_unreachable` that came from a response rather than from a transport failure. Its ABSENCE on a 502 is the signal that nothing was reached at all. */
@@ -9458,7 +9486,7 @@ export interface paths {
9458
9486
  ok: false;
9459
9487
  provider: string;
9460
9488
  /** @enum {string} */
9461
- error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable";
9489
+ error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable" | "shared_sandbox_live_project_refused";
9462
9490
  /** @description Whose sentence this is depends on the code, and the difference matters. On `provider_rejected` and `provider_unreachable` it is up to 256 characters of the PROVIDER's own response body, or the transport error. On the others it is text we wrote, naming what is missing on our side. It is absent on `redirect_not_followed`, and on the `server_unknown` and `endpoint_missing` refusals that carry nothing but the code. On `test_venue_unavailable` it names the catalog file and the field to declare, and it is the one refusal here that is about the project's ENVIRONMENT rather than its connection: the identical request from a `live` project reaches the provider. */
9463
9491
  detail?: string;
9464
9492
  /** @description The provider's HTTP status, present only when a round trip completed: on `provider_rejected`, `redirect_not_followed`, and the `provider_unreachable` that came from a response rather than from a transport failure. Its ABSENCE on a 502 is the signal that nothing was reached at all. */
@@ -9488,7 +9516,7 @@ export interface paths {
9488
9516
  ok: false;
9489
9517
  provider: string;
9490
9518
  /** @enum {string} */
9491
- error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable";
9519
+ error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable" | "shared_sandbox_live_project_refused";
9492
9520
  /** @description Whose sentence this is depends on the code, and the difference matters. On `provider_rejected` and `provider_unreachable` it is up to 256 characters of the PROVIDER's own response body, or the transport error. On the others it is text we wrote, naming what is missing on our side. It is absent on `redirect_not_followed`, and on the `server_unknown` and `endpoint_missing` refusals that carry nothing but the code. On `test_venue_unavailable` it names the catalog file and the field to declare, and it is the one refusal here that is about the project's ENVIRONMENT rather than its connection: the identical request from a `live` project reaches the provider. */
9493
9521
  detail?: string;
9494
9522
  /** @description The provider's HTTP status, present only when a round trip completed: on `provider_rejected`, `redirect_not_followed`, and the `provider_unreachable` that came from a response rather than from a transport failure. Its ABSENCE on a 502 is the signal that nothing was reached at all. */
@@ -9518,7 +9546,7 @@ export interface paths {
9518
9546
  ok: false;
9519
9547
  provider: string;
9520
9548
  /** @enum {string} */
9521
- error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable";
9549
+ error: "server_unknown" | "verify_unsupported" | "not_connected" | "connection_expired" | "provider_rejected" | "provider_unreachable" | "redirect_not_followed" | "endpoint_missing" | "test_venue_unavailable" | "shared_sandbox_live_project_refused";
9522
9550
  /** @description Whose sentence this is depends on the code, and the difference matters. On `provider_rejected` and `provider_unreachable` it is up to 256 characters of the PROVIDER's own response body, or the transport error. On the others it is text we wrote, naming what is missing on our side. It is absent on `redirect_not_followed`, and on the `server_unknown` and `endpoint_missing` refusals that carry nothing but the code. On `test_venue_unavailable` it names the catalog file and the field to declare, and it is the one refusal here that is about the project's ENVIRONMENT rather than its connection: the identical request from a `live` project reaches the provider. */
9523
9551
  detail?: string;
9524
9552
  /** @description The provider's HTTP status, present only when a round trip completed: on `provider_rejected`, `redirect_not_followed`, and the `provider_unreachable` that came from a response rather than from a transport failure. Its ABSENCE on a 502 is the signal that nothing was reached at all. */
@@ -14866,7 +14894,7 @@ export interface paths {
14866
14894
  *
14867
14895
  * **There is no read route for the outcome.** An `attempt_id` that answered `psp_dispatch_uncertain` has no endpoint to ask about afterwards. Keep the `attempt_id` and present it again: the lifecycle is idempotent on it and answers the state that attempt stopped in, instead of firing a second one. Do NOT restart the same intent under a fresh `attempt_id`. That is how a payment gets made twice. A retry of an attempt that already holds money is not evaluated against the organization's policy rules again, so a budget or a rate limit cannot refuse the retry that finds out what happened.
14868
14896
  *
14869
- * **A settled attempt answers its recorded response.** Presenting the `attempt_id` of an attempt that already settled, with the same amount, payee, mandate, rail and quote, returns the ORIGINAL 200 body verbatim (same `transactionId`, same receipt) with `idempotent_replay: true`, and nothing is dispatched, held, sealed or published. This holds on every rail and in test mode alike, and while the organization is paused, since the answer moves no money. Presenting it with any of those parameters changed is `attempt_id_conflict`. Idempotency is opt-in: a request without an `attempt_id` is its own payment and is never replayed, so two spends under one mandate are two payments. Send an `attempt_id` to make a retry safe.
14897
+ * **A settled attempt answers its recorded response.** Presenting the `attempt_id` of an attempt that already settled, with the same amount, payee, mandate, rail, quote and approval, returns the ORIGINAL 200 body verbatim (same `transactionId`, same receipt) with `idempotent_replay: true`, and nothing is dispatched, held, sealed or published. This holds on every rail and in test mode alike, and while the organization is paused, since the answer moves no money. Presenting it with any of those parameters changed is `attempt_id_conflict`. Idempotency is opt-in: a request without an `attempt_id` is its own payment and is never replayed, so two spends under one mandate are two payments. Send an `attempt_id` to make a retry safe.
14870
14898
  */
14871
14899
  post: {
14872
14900
  parameters: {
@@ -14891,6 +14919,7 @@ export interface paths {
14891
14919
  ted?: components["schemas"]["TedDestination"];
14892
14920
  quote?: components["schemas"]["SpendQuote"];
14893
14921
  actor?: components["schemas"]["PaymentActor"];
14922
+ approval?: components["schemas"]["SpendApproval"];
14894
14923
  };
14895
14924
  };
14896
14925
  };
@@ -14904,7 +14933,7 @@ export interface paths {
14904
14933
  "application/json": components["schemas"]["SpendOutcome"];
14905
14934
  };
14906
14935
  };
14907
- /** @description The body did not match the schema (`invalid_body`), or `actor.on_behalf_of` names a consumer other than the one this mandate belongs to (`actor_consumer_mismatch`). Nothing was read, held or sent in either case. */
14936
+ /** @description The body did not match the schema (`invalid_body`), `approval` is malformed (`invalid_approval_hash`), `quote.at` is not an RFC 3339 timestamp with an offset in UTC years 0001-9999 (`invalid_receipt_timestamp`), or `actor.on_behalf_of` names a consumer other than the one this mandate belongs to (`actor_consumer_mismatch`). Nothing was read, held or sent in any of these cases. `attempt_id_required`: the body names no `attempt_id` and the deployment enforces one (`ATTEMPT_ID_REQUIRED_ENFORCE`); add a stable id and reuse it on every retry of that payment. It is checked AFTER the body is valid, so a request that is both malformed and id-less is answered `invalid_approval_hash`, `invalid_receipt_timestamp` or `invalid_body` first. */
14908
14937
  400: {
14909
14938
  headers: {
14910
14939
  [name: string]: unknown;
@@ -14913,7 +14942,7 @@ export interface paths {
14913
14942
  "application/json": {
14914
14943
  error: {
14915
14944
  /** @enum {string} */
14916
- code: "invalid_body" | "actor_consumer_mismatch";
14945
+ code: "invalid_body" | "invalid_approval_hash" | "invalid_receipt_timestamp" | "actor_consumer_mismatch" | "attempt_id_required";
14917
14946
  message: string;
14918
14947
  details?: {
14919
14948
  [key: string]: unknown;
@@ -15095,7 +15124,7 @@ export interface paths {
15095
15124
  *
15096
15125
  * **There is no read route for the outcome.** An `attempt_id` that answered `psp_dispatch_uncertain` has no endpoint to ask about afterwards. Keep the `attempt_id` and present it again: the lifecycle is idempotent on it and answers the state that attempt stopped in, instead of firing a second one. Do NOT restart the same intent under a fresh `attempt_id`. That is how a payment gets made twice. A retry of an attempt that already holds money is not evaluated against the organization's policy rules again, so a budget or a rate limit cannot refuse the retry that finds out what happened.
15097
15126
  *
15098
- * **A settled attempt answers its recorded response.** Presenting the `attempt_id` of an attempt that already settled, with the same amount, payee, mandate, rail and quote, returns the ORIGINAL 200 body verbatim (same `transactionId`, same receipt) with `idempotent_replay: true`, and nothing is dispatched, held, sealed or published. This holds on every rail and in test mode alike, and while the organization is paused, since the answer moves no money. Presenting it with any of those parameters changed is `attempt_id_conflict`. Idempotency is opt-in: a request without an `attempt_id` is its own payment and is never replayed, so two spends under one mandate are two payments. Send an `attempt_id` to make a retry safe.
15127
+ * **A settled attempt answers its recorded response.** Presenting the `attempt_id` of an attempt that already settled, with the same amount, payee, mandate, rail, quote and approval, returns the ORIGINAL 200 body verbatim (same `transactionId`, same receipt) with `idempotent_replay: true`, and nothing is dispatched, held, sealed or published. This holds on every rail and in test mode alike, and while the organization is paused, since the answer moves no money. Presenting it with any of those parameters changed is `attempt_id_conflict`. Idempotency is opt-in: a request without an `attempt_id` is its own payment and is never replayed, so two spends under one mandate are two payments. Send an `attempt_id` to make a retry safe.
15099
15128
  */
15100
15129
  post: {
15101
15130
  parameters: {
@@ -15119,6 +15148,7 @@ export interface paths {
15119
15148
  ted?: components["schemas"]["TedDestination"];
15120
15149
  quote?: components["schemas"]["SpendQuote"];
15121
15150
  actor?: components["schemas"]["PaymentActor"];
15151
+ approval?: components["schemas"]["SpendApproval"];
15122
15152
  };
15123
15153
  };
15124
15154
  };
@@ -15132,7 +15162,7 @@ export interface paths {
15132
15162
  "application/json": components["schemas"]["SpendOutcome"];
15133
15163
  };
15134
15164
  };
15135
- /** @description The body did not match the schema (`invalid_body`), or `actor.on_behalf_of` names a consumer other than the one this mandate belongs to (`actor_consumer_mismatch`). Nothing was read, held or sent in either case. */
15165
+ /** @description The body did not match the schema (`invalid_body`), `approval` is malformed (`invalid_approval_hash`), `quote.at` is not an RFC 3339 timestamp with an offset in UTC years 0001-9999 (`invalid_receipt_timestamp`), or `actor.on_behalf_of` names a consumer other than the one this mandate belongs to (`actor_consumer_mismatch`). Nothing was read, held or sent in any of these cases. `attempt_id_required`: the body names no `attempt_id` and the deployment enforces one (`ATTEMPT_ID_REQUIRED_ENFORCE`); add a stable id and reuse it on every retry of that payment. It is checked AFTER the body is valid, so a request that is both malformed and id-less is answered `invalid_approval_hash`, `invalid_receipt_timestamp` or `invalid_body` first. */
15136
15166
  400: {
15137
15167
  headers: {
15138
15168
  [name: string]: unknown;
@@ -15141,7 +15171,7 @@ export interface paths {
15141
15171
  "application/json": {
15142
15172
  error: {
15143
15173
  /** @enum {string} */
15144
- code: "invalid_body" | "actor_consumer_mismatch";
15174
+ code: "invalid_body" | "invalid_approval_hash" | "invalid_receipt_timestamp" | "actor_consumer_mismatch" | "attempt_id_required";
15145
15175
  message: string;
15146
15176
  details?: {
15147
15177
  [key: string]: unknown;
@@ -15277,7 +15307,7 @@ export interface paths {
15277
15307
  *
15278
15308
  * **There is no read route for the outcome.** An `attempt_id` that answered `psp_dispatch_uncertain` has no endpoint to ask about afterwards. Keep the `attempt_id` and present it again: the lifecycle is idempotent on it and answers the state that attempt stopped in, instead of firing a second one. Do NOT restart the same intent under a fresh `attempt_id`. That is how a payment gets made twice. A retry of an attempt that already holds money is not evaluated against the organization's policy rules again, so a budget or a rate limit cannot refuse the retry that finds out what happened.
15279
15309
  *
15280
- * **A settled attempt answers its recorded response.** Presenting the `attempt_id` of an attempt that already settled, with the same amount, payee, mandate, rail and quote, returns the ORIGINAL 200 body verbatim (same `transactionId`, same receipt) with `idempotent_replay: true`, and nothing is dispatched, held, sealed or published. This holds on every rail and in test mode alike, and while the organization is paused, since the answer moves no money. Presenting it with any of those parameters changed is `attempt_id_conflict`. Idempotency is opt-in: a request without an `attempt_id` is its own payment and is never replayed, so two spends under one mandate are two payments. Send an `attempt_id` to make a retry safe.
15310
+ * **A settled attempt answers its recorded response.** Presenting the `attempt_id` of an attempt that already settled, with the same amount, payee, mandate, rail, quote and approval, returns the ORIGINAL 200 body verbatim (same `transactionId`, same receipt) with `idempotent_replay: true`, and nothing is dispatched, held, sealed or published. This holds on every rail and in test mode alike, and while the organization is paused, since the answer moves no money. Presenting it with any of those parameters changed is `attempt_id_conflict`. Idempotency is opt-in: a request without an `attempt_id` is its own payment and is never replayed, so two spends under one mandate are two payments. Send an `attempt_id` to make a retry safe.
15281
15311
  */
15282
15312
  post: {
15283
15313
  parameters: {
@@ -15299,6 +15329,7 @@ export interface paths {
15299
15329
  ted?: components["schemas"]["TedDestination"];
15300
15330
  quote?: components["schemas"]["SpendQuote"];
15301
15331
  actor?: components["schemas"]["PaymentActor"];
15332
+ approval?: components["schemas"]["SpendApproval"];
15302
15333
  };
15303
15334
  };
15304
15335
  };
@@ -15312,7 +15343,7 @@ export interface paths {
15312
15343
  "text/event-stream": string;
15313
15344
  };
15314
15345
  };
15315
- /** @description The body did not match the schema (`invalid_body`), or `actor.on_behalf_of` names a consumer other than the one this mandate belongs to (`actor_consumer_mismatch`). Nothing was read, held or sent in either case. */
15346
+ /** @description The body did not match the schema (`invalid_body`), `approval` is malformed (`invalid_approval_hash`), `quote.at` is not an RFC 3339 timestamp with an offset in UTC years 0001-9999 (`invalid_receipt_timestamp`), or `actor.on_behalf_of` names a consumer other than the one this mandate belongs to (`actor_consumer_mismatch`). Nothing was read, held or sent in any of these cases. `attempt_id_required`: the body names no `attempt_id` and the deployment enforces one (`ATTEMPT_ID_REQUIRED_ENFORCE`); add a stable id and reuse it on every retry of that payment. It is checked AFTER the body is valid, so a request that is both malformed and id-less is answered `invalid_approval_hash`, `invalid_receipt_timestamp` or `invalid_body` first. */
15316
15347
  400: {
15317
15348
  headers: {
15318
15349
  [name: string]: unknown;
@@ -15321,7 +15352,7 @@ export interface paths {
15321
15352
  "application/json": {
15322
15353
  error: {
15323
15354
  /** @enum {string} */
15324
- code: "invalid_body" | "actor_consumer_mismatch";
15355
+ code: "invalid_body" | "invalid_approval_hash" | "invalid_receipt_timestamp" | "actor_consumer_mismatch" | "attempt_id_required";
15325
15356
  message: string;
15326
15357
  details?: {
15327
15358
  [key: string]: unknown;
@@ -18115,7 +18146,10 @@ export interface paths {
18115
18146
  nonce: string;
18116
18147
  scope: string;
18117
18148
  currency: string;
18149
+ /** @description The mandate's HMAC signature. It must not be shared with a third party. Chains of version 1-3 seal it, which is why only the tenant can recompute those; a version 4 chain seals `sig_sha256` instead. */
18118
18150
  sig: string;
18151
+ /** @description SHA-256 over the UTF-8 bytes of `sig`, lowercase hex. What a version 4 chain seals in the mandate link, so a third party recomputes it without ever seeing `sig`. */
18152
+ sig_sha256: string;
18119
18153
  };
18120
18154
  /** @description Null when the receipt carries neither a seller nor a resource. */
18121
18155
  quote: {
@@ -18127,6 +18161,15 @@ export interface paths {
18127
18161
  sig: string | null;
18128
18162
  at: string | null;
18129
18163
  } | null;
18164
+ /**
18165
+ * @description Present ONLY when the spend carried an `approval`; absent otherwise, and absent on every receipt sealed before the field existed. The value is sealed into `chain` as its own link (chain version 4), so it is covered by `receipt_sig` and `receipt_sig_ed25519`.
18166
+ *
18167
+ * It is a SEALED CLAIM by the caller, NOT a server-side approval check: the server validated its shape and nothing else. It proves this payment was sealed together with this hash, not that anybody approved anything, and not who did — the approver's identity stays in the caller's own approval artifact.
18168
+ */
18169
+ approval?: {
18170
+ items_hash: string;
18171
+ batch_hash: string | null;
18172
+ };
18130
18173
  payment: {
18131
18174
  rail: string;
18132
18175
  provider: string | null;
@@ -18161,7 +18204,20 @@ export interface paths {
18161
18204
  nfe_chave: string | null;
18162
18205
  at: string | null;
18163
18206
  } | null;
18207
+ /**
18208
+ * @description SHA-256 (64 lowercase hex) over the UTF-8 bytes of the RFC 8785 (JCS) canonical JSON of `{ "v": chain_version, "links": [...] }`. Every value a link needs is in this read, so the chain can be recomputed from it and bound to `receipt_sig_ed25519`. The links, in order, each left out when it does not apply:
18209
+ *
18210
+ * 1. mandate — always: `id`, `nonce`, `scope`, `currency`, then `sig` (v1-v3) or `sig_sha256` (v4), from `mandate`.
18211
+ * 2. quote — when `quote` is not null: `seller`, `resource`, `price_minor`, `payee`, `session_id`, `sig`, `at`, nulls included.
18212
+ * 3. approval — v4 only: `items_hash`, `batch_hash` (null when absent).
18213
+ * 4. payment — always: `rail`, `provider`, `tx_id`, `amount_minor`, `attempt_id`, `money_moved`, `at`, plus each of `amount_atomic`, `amount_authorized`, `amount_charged`, `amount_refunded`, `metering`, `sandbox` only when present and not null.
18214
+ * 5. delivery — when `delivery` is not null: `result`, `proof`, `kind`, `nfe_chave`, `at`.
18215
+ *
18216
+ * Values are copied as read; timestamps are RFC 3339 UTC with milliseconds. The same recipe is served as data in `chain_recipe` on the unauthenticated `/.well-known/codespar-receipt-keys.json`. Recomputing needs `quote.payee` unmasked, and a v1-v3 chain needs `mandate.sig`, so only the tenant can recompute those. The Ed25519 signing string does not change with the version: it covers this digest.
18217
+ */
18164
18218
  chain: string;
18219
+ /** @description The chain format `chain` was computed with: 4 when `approval` is present; else 3 when `payment.amount_authorized` is present; else 2 when `payment.amount_atomic` is not null; else 1. A receipt keeps the version it was sealed under. */
18220
+ chain_version: number;
18165
18221
  receipt_sig: string;
18166
18222
  /** @description base64url Ed25519 signature over `codespar-receipt:v1:<receipt_id>:<chain>`, made with the platform issuer key. Verifiable by anyone: fetch `/.well-known/codespar-receipt-keys.json` (no credential), take the key whose `kid` is the one below, and check the signature over that string. Null on a receipt sealed before this existed, and on one sealed while the key could not be resolved — `receipt_sig` is the HMAC seal either way and is unaffected. */
18167
18223
  receipt_sig_ed25519: string | null;
@@ -18238,7 +18294,10 @@ export interface paths {
18238
18294
  nonce: string;
18239
18295
  scope: string;
18240
18296
  currency: string;
18297
+ /** @description The mandate's HMAC signature. It must not be shared with a third party. Chains of version 1-3 seal it, which is why only the tenant can recompute those; a version 4 chain seals `sig_sha256` instead. */
18241
18298
  sig: string;
18299
+ /** @description SHA-256 over the UTF-8 bytes of `sig`, lowercase hex. What a version 4 chain seals in the mandate link, so a third party recomputes it without ever seeing `sig`. */
18300
+ sig_sha256: string;
18242
18301
  };
18243
18302
  /** @description Null when the receipt carries neither a seller nor a resource. */
18244
18303
  quote: {
@@ -18250,6 +18309,15 @@ export interface paths {
18250
18309
  sig: string | null;
18251
18310
  at: string | null;
18252
18311
  } | null;
18312
+ /**
18313
+ * @description Present ONLY when the spend carried an `approval`; absent otherwise, and absent on every receipt sealed before the field existed. The value is sealed into `chain` as its own link (chain version 4), so it is covered by `receipt_sig` and `receipt_sig_ed25519`.
18314
+ *
18315
+ * It is a SEALED CLAIM by the caller, NOT a server-side approval check: the server validated its shape and nothing else. It proves this payment was sealed together with this hash, not that anybody approved anything, and not who did — the approver's identity stays in the caller's own approval artifact.
18316
+ */
18317
+ approval?: {
18318
+ items_hash: string;
18319
+ batch_hash: string | null;
18320
+ };
18253
18321
  payment: {
18254
18322
  rail: string;
18255
18323
  provider: string | null;
@@ -18284,7 +18352,20 @@ export interface paths {
18284
18352
  nfe_chave: string | null;
18285
18353
  at: string | null;
18286
18354
  } | null;
18355
+ /**
18356
+ * @description SHA-256 (64 lowercase hex) over the UTF-8 bytes of the RFC 8785 (JCS) canonical JSON of `{ "v": chain_version, "links": [...] }`. Every value a link needs is in this read, so the chain can be recomputed from it and bound to `receipt_sig_ed25519`. The links, in order, each left out when it does not apply:
18357
+ *
18358
+ * 1. mandate — always: `id`, `nonce`, `scope`, `currency`, then `sig` (v1-v3) or `sig_sha256` (v4), from `mandate`.
18359
+ * 2. quote — when `quote` is not null: `seller`, `resource`, `price_minor`, `payee`, `session_id`, `sig`, `at`, nulls included.
18360
+ * 3. approval — v4 only: `items_hash`, `batch_hash` (null when absent).
18361
+ * 4. payment — always: `rail`, `provider`, `tx_id`, `amount_minor`, `attempt_id`, `money_moved`, `at`, plus each of `amount_atomic`, `amount_authorized`, `amount_charged`, `amount_refunded`, `metering`, `sandbox` only when present and not null.
18362
+ * 5. delivery — when `delivery` is not null: `result`, `proof`, `kind`, `nfe_chave`, `at`.
18363
+ *
18364
+ * Values are copied as read; timestamps are RFC 3339 UTC with milliseconds. The same recipe is served as data in `chain_recipe` on the unauthenticated `/.well-known/codespar-receipt-keys.json`. Recomputing needs `quote.payee` unmasked, and a v1-v3 chain needs `mandate.sig`, so only the tenant can recompute those. The Ed25519 signing string does not change with the version: it covers this digest.
18365
+ */
18287
18366
  chain: string;
18367
+ /** @description The chain format `chain` was computed with: 4 when `approval` is present; else 3 when `payment.amount_authorized` is present; else 2 when `payment.amount_atomic` is not null; else 1. A receipt keeps the version it was sealed under. */
18368
+ chain_version: number;
18288
18369
  receipt_sig: string;
18289
18370
  /** @description base64url Ed25519 signature over `codespar-receipt:v1:<receipt_id>:<chain>`, made with the platform issuer key. Verifiable by anyone: fetch `/.well-known/codespar-receipt-keys.json` (no credential), take the key whose `kid` is the one below, and check the signature over that string. Null on a receipt sealed before this existed, and on one sealed while the key could not be resolved — `receipt_sig` is the HMAC seal either way and is unaffected. */
18290
18371
  receipt_sig_ed25519: string | null;
@@ -20184,6 +20265,10 @@ export interface paths {
20184
20265
  /** @enum {string} */
20185
20266
  kind: "order_confirmation" | "nfe" | "nfse" | "tracking" | "resource" | "merchant_ref" | "pix_e2e";
20186
20267
  nfe_chave?: string;
20268
+ /**
20269
+ * Format: date-time
20270
+ * @description When the delivery happened, RFC 3339 with an offset, in UTC years 0001-9999. Sealed into the chain as that instant in UTC with milliseconds. Anything else is refused with `invalid_receipt_timestamp`.
20271
+ */
20187
20272
  at?: string;
20188
20273
  };
20189
20274
  };
@@ -20216,7 +20301,10 @@ export interface paths {
20216
20301
  nonce: string;
20217
20302
  scope: string;
20218
20303
  currency: string;
20304
+ /** @description The mandate's HMAC signature. It must not be shared with a third party. Chains of version 1-3 seal it, which is why only the tenant can recompute those; a version 4 chain seals `sig_sha256` instead. */
20219
20305
  sig: string;
20306
+ /** @description SHA-256 over the UTF-8 bytes of `sig`, lowercase hex. What a version 4 chain seals in the mandate link, so a third party recomputes it without ever seeing `sig`. */
20307
+ sig_sha256: string;
20220
20308
  };
20221
20309
  /** @description Null when the receipt carries neither a seller nor a resource. */
20222
20310
  quote: {
@@ -20228,6 +20316,15 @@ export interface paths {
20228
20316
  sig: string | null;
20229
20317
  at: string | null;
20230
20318
  } | null;
20319
+ /**
20320
+ * @description Present ONLY when the spend carried an `approval`; absent otherwise, and absent on every receipt sealed before the field existed. The value is sealed into `chain` as its own link (chain version 4), so it is covered by `receipt_sig` and `receipt_sig_ed25519`.
20321
+ *
20322
+ * It is a SEALED CLAIM by the caller, NOT a server-side approval check: the server validated its shape and nothing else. It proves this payment was sealed together with this hash, not that anybody approved anything, and not who did — the approver's identity stays in the caller's own approval artifact.
20323
+ */
20324
+ approval?: {
20325
+ items_hash: string;
20326
+ batch_hash: string | null;
20327
+ };
20231
20328
  payment: {
20232
20329
  rail: string;
20233
20330
  provider: string | null;
@@ -20262,7 +20359,20 @@ export interface paths {
20262
20359
  nfe_chave: string | null;
20263
20360
  at: string | null;
20264
20361
  } | null;
20362
+ /**
20363
+ * @description SHA-256 (64 lowercase hex) over the UTF-8 bytes of the RFC 8785 (JCS) canonical JSON of `{ "v": chain_version, "links": [...] }`. Every value a link needs is in this read, so the chain can be recomputed from it and bound to `receipt_sig_ed25519`. The links, in order, each left out when it does not apply:
20364
+ *
20365
+ * 1. mandate — always: `id`, `nonce`, `scope`, `currency`, then `sig` (v1-v3) or `sig_sha256` (v4), from `mandate`.
20366
+ * 2. quote — when `quote` is not null: `seller`, `resource`, `price_minor`, `payee`, `session_id`, `sig`, `at`, nulls included.
20367
+ * 3. approval — v4 only: `items_hash`, `batch_hash` (null when absent).
20368
+ * 4. payment — always: `rail`, `provider`, `tx_id`, `amount_minor`, `attempt_id`, `money_moved`, `at`, plus each of `amount_atomic`, `amount_authorized`, `amount_charged`, `amount_refunded`, `metering`, `sandbox` only when present and not null.
20369
+ * 5. delivery — when `delivery` is not null: `result`, `proof`, `kind`, `nfe_chave`, `at`.
20370
+ *
20371
+ * Values are copied as read; timestamps are RFC 3339 UTC with milliseconds. The same recipe is served as data in `chain_recipe` on the unauthenticated `/.well-known/codespar-receipt-keys.json`. Recomputing needs `quote.payee` unmasked, and a v1-v3 chain needs `mandate.sig`, so only the tenant can recompute those. The Ed25519 signing string does not change with the version: it covers this digest.
20372
+ */
20265
20373
  chain: string;
20374
+ /** @description The chain format `chain` was computed with: 4 when `approval` is present; else 3 when `payment.amount_authorized` is present; else 2 when `payment.amount_atomic` is not null; else 1. A receipt keeps the version it was sealed under. */
20375
+ chain_version: number;
20266
20376
  receipt_sig: string;
20267
20377
  /** @description base64url Ed25519 signature over `codespar-receipt:v1:<receipt_id>:<chain>`, made with the platform issuer key. Verifiable by anyone: fetch `/.well-known/codespar-receipt-keys.json` (no credential), take the key whose `kid` is the one below, and check the signature over that string. Null on a receipt sealed before this existed, and on one sealed while the key could not be resolved — `receipt_sig` is the HMAC seal either way and is unaffected. */
20268
20378
  receipt_sig_ed25519: string | null;
@@ -20276,7 +20386,7 @@ export interface paths {
20276
20386
  };
20277
20387
  };
20278
20388
  };
20279
- /** @description The body did not match the schema. Most often a `kind` outside the seven accepted values, or a `proof` longer than 500 characters. Zod's issues are in `details.issues`. */
20389
+ /** @description The body did not match the schema (`invalid_body`): most often a `kind` outside the seven accepted values, or a `proof` longer than 500 characters, with Zod's issues in `details.issues`. Or `at` is not an RFC 3339 timestamp with an offset in UTC years 0001-9999 (`invalid_receipt_timestamp`). Nothing was re-sealed in either case. */
20280
20390
  400: {
20281
20391
  headers: {
20282
20392
  [name: string]: unknown;
@@ -20285,7 +20395,7 @@ export interface paths {
20285
20395
  "application/json": {
20286
20396
  error: {
20287
20397
  /** @enum {string} */
20288
- code: "invalid_body";
20398
+ code: "invalid_body" | "invalid_receipt_timestamp";
20289
20399
  message: string;
20290
20400
  details?: {
20291
20401
  [key: string]: unknown;
@@ -21072,6 +21182,26 @@ export interface paths {
21072
21182
  };
21073
21183
  };
21074
21184
  };
21185
+ /** @description `ofb_not_live`: this is a LIVE project and the deployment has no real Open Finance adapter, only the stub, which serves test projects. TERMINAL for this project: use a test project. Nothing was written and no bank was contacted. */
21186
+ 422: {
21187
+ headers: {
21188
+ [name: string]: unknown;
21189
+ };
21190
+ content: {
21191
+ "application/json": {
21192
+ error: {
21193
+ /** @enum {string} */
21194
+ code: "ofb_not_live";
21195
+ message: string;
21196
+ details?: {
21197
+ [key: string]: unknown;
21198
+ };
21199
+ };
21200
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
21201
+ request_id: string | null;
21202
+ };
21203
+ };
21204
+ };
21075
21205
  /** @description `missing_bank_consent_id`: the stored consent carries no bank-side id, so there is nothing to finalise. Ours to fix, not yours; quote the request id. */
21076
21206
  500: {
21077
21207
  headers: {
@@ -21112,6 +21242,26 @@ export interface paths {
21112
21242
  };
21113
21243
  };
21114
21244
  };
21245
+ /** @description `ofb_adapter_unavailable`: the Open Finance adapter this deployment selects cannot serve (`details.reason`: `missing_credentials`, `not_implemented` or `unknown_mode`). Ours to fix, not yours; nothing was written and no bank was contacted. */
21246
+ 503: {
21247
+ headers: {
21248
+ [name: string]: unknown;
21249
+ };
21250
+ content: {
21251
+ "application/json": {
21252
+ error: {
21253
+ /** @enum {string} */
21254
+ code: "ofb_adapter_unavailable";
21255
+ message: string;
21256
+ details?: {
21257
+ [key: string]: unknown;
21258
+ };
21259
+ };
21260
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
21261
+ request_id: string | null;
21262
+ };
21263
+ };
21264
+ };
21115
21265
  };
21116
21266
  };
21117
21267
  delete?: never;
@@ -21244,6 +21394,26 @@ export interface paths {
21244
21394
  };
21245
21395
  };
21246
21396
  };
21397
+ /** @description `ofb_not_live`: this is a LIVE project and the deployment has no real Open Finance adapter, only the stub, which serves test projects. TERMINAL for this project: use a test project. Nothing was written and no bank was contacted. */
21398
+ 422: {
21399
+ headers: {
21400
+ [name: string]: unknown;
21401
+ };
21402
+ content: {
21403
+ "application/json": {
21404
+ error: {
21405
+ /** @enum {string} */
21406
+ code: "ofb_not_live";
21407
+ message: string;
21408
+ details?: {
21409
+ [key: string]: unknown;
21410
+ };
21411
+ };
21412
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
21413
+ request_id: string | null;
21414
+ };
21415
+ };
21416
+ };
21247
21417
  /** @description `consent_missing_tokens`: the consent says `authorised` but is missing something the fetch needs. TWO conditions raise it and the code does not distinguish them: no stored access-token pointer, OR no bank-side consent id. The second is not a token, so read the code as `this authorised consent is not usable` rather than as a statement about credentials. Ours to fix, not yours; quote the request id. */
21248
21418
  500: {
21249
21419
  headers: {
@@ -21284,6 +21454,26 @@ export interface paths {
21284
21454
  };
21285
21455
  };
21286
21456
  };
21457
+ /** @description `ofb_adapter_unavailable`: the Open Finance adapter this deployment selects cannot serve (`details.reason`: `missing_credentials`, `not_implemented` or `unknown_mode`). Ours to fix, not yours; nothing was written and no bank was contacted. */
21458
+ 503: {
21459
+ headers: {
21460
+ [name: string]: unknown;
21461
+ };
21462
+ content: {
21463
+ "application/json": {
21464
+ error: {
21465
+ /** @enum {string} */
21466
+ code: "ofb_adapter_unavailable";
21467
+ message: string;
21468
+ details?: {
21469
+ [key: string]: unknown;
21470
+ };
21471
+ };
21472
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
21473
+ request_id: string | null;
21474
+ };
21475
+ };
21476
+ };
21287
21477
  };
21288
21478
  };
21289
21479
  delete?: never;
@@ -22016,6 +22206,26 @@ export interface paths {
22016
22206
  };
22017
22207
  };
22018
22208
  };
22209
+ /** @description `ofb_not_live`: this is a LIVE project and the deployment has no real Open Finance adapter, only the stub, which serves test projects. TERMINAL for this project: use a test project. Nothing was written and no bank was contacted. */
22210
+ 422: {
22211
+ headers: {
22212
+ [name: string]: unknown;
22213
+ };
22214
+ content: {
22215
+ "application/json": {
22216
+ error: {
22217
+ /** @enum {string} */
22218
+ code: "ofb_not_live";
22219
+ message: string;
22220
+ details?: {
22221
+ [key: string]: unknown;
22222
+ };
22223
+ };
22224
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
22225
+ request_id: string | null;
22226
+ };
22227
+ };
22228
+ };
22019
22229
  /** @description `consent_disappeared`: the row was written and could not be read back. Do not retry blind; both the row and the bank-side consent may exist. */
22020
22230
  500: {
22021
22231
  headers: {
@@ -22056,7 +22266,11 @@ export interface paths {
22056
22266
  };
22057
22267
  };
22058
22268
  };
22059
- /** @description `consent_lock_timeout`: another request is issuing a consent for this consumer at this bank and the wait ended without an answer, so NOTHING WAS DECIDED. We do not know whether a sibling holds the grant, and saying so is why this is not the 409. `details.retriable` is true. No consent row was created; a bank-side consent was, and is orphaned. */
22269
+ /**
22270
+ * @description `consent_lock_timeout`: another request is issuing a consent for this consumer at this bank and the wait ended without an answer, so NOTHING WAS DECIDED. We do not know whether a sibling holds the grant, and saying so is why this is not the 409. `details.retriable` is true. No consent row was created; a bank-side consent was, and is orphaned.
22271
+ *
22272
+ * `ofb_adapter_unavailable`: the Open Finance adapter this deployment selects cannot serve (`details.reason`: `missing_credentials`, `not_implemented` or `unknown_mode`). Ours to fix, not yours; nothing was written and no bank was contacted.
22273
+ */
22060
22274
  503: {
22061
22275
  headers: {
22062
22276
  [name: string]: unknown;
@@ -22065,7 +22279,7 @@ export interface paths {
22065
22279
  "application/json": {
22066
22280
  error: {
22067
22281
  /** @enum {string} */
22068
- code: "consent_lock_timeout";
22282
+ code: "consent_lock_timeout" | "ofb_adapter_unavailable";
22069
22283
  message: string;
22070
22284
  details?: {
22071
22285
  [key: string]: unknown;
@@ -22224,6 +22438,26 @@ export interface paths {
22224
22438
  };
22225
22439
  };
22226
22440
  };
22441
+ /** @description `ofb_not_live`: this is a LIVE project and the deployment has no real Open Finance adapter, only the stub, which serves test projects. TERMINAL for this project: use a test project. Nothing was written and no bank was contacted. */
22442
+ 422: {
22443
+ headers: {
22444
+ [name: string]: unknown;
22445
+ };
22446
+ content: {
22447
+ "application/json": {
22448
+ error: {
22449
+ /** @enum {string} */
22450
+ code: "ofb_not_live";
22451
+ message: string;
22452
+ details?: {
22453
+ [key: string]: unknown;
22454
+ };
22455
+ };
22456
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
22457
+ request_id: string | null;
22458
+ };
22459
+ };
22460
+ };
22227
22461
  /** @description `missing_bank_consent_id`: the stored consent carries no bank-side id, so there is nothing to finalise. Ours to fix, not yours; quote the request id. */
22228
22462
  500: {
22229
22463
  headers: {
@@ -22264,6 +22498,26 @@ export interface paths {
22264
22498
  };
22265
22499
  };
22266
22500
  };
22501
+ /** @description `ofb_adapter_unavailable`: the Open Finance adapter this deployment selects cannot serve (`details.reason`: `missing_credentials`, `not_implemented` or `unknown_mode`). Ours to fix, not yours; nothing was written and no bank was contacted. */
22502
+ 503: {
22503
+ headers: {
22504
+ [name: string]: unknown;
22505
+ };
22506
+ content: {
22507
+ "application/json": {
22508
+ error: {
22509
+ /** @enum {string} */
22510
+ code: "ofb_adapter_unavailable";
22511
+ message: string;
22512
+ details?: {
22513
+ [key: string]: unknown;
22514
+ };
22515
+ };
22516
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
22517
+ request_id: string | null;
22518
+ };
22519
+ };
22520
+ };
22267
22521
  };
22268
22522
  };
22269
22523
  delete?: never;
@@ -22391,6 +22645,26 @@ export interface paths {
22391
22645
  };
22392
22646
  };
22393
22647
  };
22648
+ /** @description `ofb_not_live`: this is a LIVE project and the deployment has no real Open Finance adapter, only the stub, which serves test projects. TERMINAL for this project: use a test project. Nothing was written and no bank was contacted. */
22649
+ 422: {
22650
+ headers: {
22651
+ [name: string]: unknown;
22652
+ };
22653
+ content: {
22654
+ "application/json": {
22655
+ error: {
22656
+ /** @enum {string} */
22657
+ code: "ofb_not_live";
22658
+ message: string;
22659
+ details?: {
22660
+ [key: string]: unknown;
22661
+ };
22662
+ };
22663
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
22664
+ request_id: string | null;
22665
+ };
22666
+ };
22667
+ };
22394
22668
  /** @description `consent_missing_tokens`: the consent says `authorised` but is missing something the fetch needs. TWO conditions raise it and the code does not distinguish them: no stored access-token pointer, OR no bank-side consent id. The second is not a token, so read the code as `this authorised consent is not usable` rather than as a statement about credentials. Ours to fix, not yours; quote the request id. */
22395
22669
  500: {
22396
22670
  headers: {
@@ -22431,6 +22705,26 @@ export interface paths {
22431
22705
  };
22432
22706
  };
22433
22707
  };
22708
+ /** @description `ofb_adapter_unavailable`: the Open Finance adapter this deployment selects cannot serve (`details.reason`: `missing_credentials`, `not_implemented` or `unknown_mode`). Ours to fix, not yours; nothing was written and no bank was contacted. */
22709
+ 503: {
22710
+ headers: {
22711
+ [name: string]: unknown;
22712
+ };
22713
+ content: {
22714
+ "application/json": {
22715
+ error: {
22716
+ /** @enum {string} */
22717
+ code: "ofb_adapter_unavailable";
22718
+ message: string;
22719
+ details?: {
22720
+ [key: string]: unknown;
22721
+ };
22722
+ };
22723
+ /** @description Echoes the `X-Request-Id` header when the request carried one. */
22724
+ request_id: string | null;
22725
+ };
22726
+ };
22727
+ };
22434
22728
  };
22435
22729
  };
22436
22730
  delete?: never;
@@ -23657,6 +23951,8 @@ export interface paths {
23657
23951
  * @description DEPRECATED alias of `GET /v1/approvals` (ent#979), kept for two releases. Same handler, same scope; switch the path and nothing else changes.
23658
23952
  *
23659
23953
  * **Without `status` you get only the `pending` ones**, not all of them. Ask for `status=approved,denied` to see what has already been decided. The response is an array, with no envelope and no cursor. An `{orgId}` that is not the credential's own is 404 before any read.
23954
+ *
23955
+ * An API key or an OAuth token lists only its own project's holds, without `tool_input`; the dashboard (service auth) lists the whole organization's, with it.
23660
23956
  */
23661
23957
  get: {
23662
23958
  parameters: {
@@ -23688,8 +23984,8 @@ export interface paths {
23688
23984
  matched_rule_id: string;
23689
23985
  matched_rule_name: string;
23690
23986
  tool_name: string;
23691
- /** @description The tool arguments the rule held for review, verbatim. */
23692
- tool_input: {
23987
+ /** @description The tool arguments the rule held for review, verbatim. Absent unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
23988
+ tool_input?: {
23693
23989
  [key: string]: unknown;
23694
23990
  };
23695
23991
  /**
@@ -23764,7 +24060,7 @@ export interface paths {
23764
24060
  * @deprecated
23765
24061
  * @description DEPRECATED alias of `POST /v1/approvals/{id}/decide` (ent#979), kept for two releases. Same handler, same scope; switch the path and nothing else changes.
23766
24062
  *
23767
- * `reason` is required in both directions, up to 8000 characters, and whitespace alone is refused. Deciding requires a PERSON: a bearer credential on its own gets `bearer_token_cannot_decide`. Already decided is 409 and expired is 410, both with the full approval in the body.
24063
+ * `reason` is required in both directions, up to 8000 characters, and whitespace alone is refused. Deciding requires a PERSON, through the dashboard: only service auth may call it, and a project API key or an OAuth token gets `bearer_token_cannot_decide` before the body is read, with or without a user token. Already decided is 409 and expired is 410, both with the full approval in the body.
23768
24064
  */
23769
24065
  post: {
23770
24066
  parameters: {
@@ -23801,8 +24097,8 @@ export interface paths {
23801
24097
  matched_rule_id: string;
23802
24098
  matched_rule_name: string;
23803
24099
  tool_name: string;
23804
- /** @description The tool arguments the rule held for review, verbatim. */
23805
- tool_input: {
24100
+ /** @description The tool arguments the rule held for review, verbatim. Absent unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
24101
+ tool_input?: {
23806
24102
  [key: string]: unknown;
23807
24103
  };
23808
24104
  /**
@@ -23843,7 +24139,7 @@ export interface paths {
23843
24139
  };
23844
24140
  };
23845
24141
  };
23846
- /** @description The caller cannot decide: a bearer credential alone, a user token that is missing/invalid/mismatched, or a person below admin. Bare body. */
24142
+ /** @description The caller cannot decide: not the dashboard (a project API key or an OAuth token, with or without a user token), a user token that is missing/invalid/mismatched, or a person below admin. Bare body. */
23847
24143
  403: {
23848
24144
  headers: {
23849
24145
  [name: string]: unknown;
@@ -23887,8 +24183,8 @@ export interface paths {
23887
24183
  matched_rule_id: string;
23888
24184
  matched_rule_name: string;
23889
24185
  tool_name: string;
23890
- /** @description The tool arguments the rule held for review, verbatim. */
23891
- tool_input: {
24186
+ /** @description The tool arguments the rule held for review, verbatim. Absent unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
24187
+ tool_input?: {
23892
24188
  [key: string]: unknown;
23893
24189
  };
23894
24190
  /**
@@ -23931,8 +24227,8 @@ export interface paths {
23931
24227
  matched_rule_id: string;
23932
24228
  matched_rule_name: string;
23933
24229
  tool_name: string;
23934
- /** @description The tool arguments the rule held for review, verbatim. */
23935
- tool_input: {
24230
+ /** @description The tool arguments the rule held for review, verbatim. Absent unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
24231
+ tool_input?: {
23936
24232
  [key: string]: unknown;
23937
24233
  };
23938
24234
  /**
@@ -24002,7 +24298,7 @@ export interface paths {
24002
24298
  * @deprecated
24003
24299
  * @description DEPRECATED alias of `GET /v1/approvals/{id}` (ent#979), kept for two releases. Same handler, same `approvals:read` scope; switch the path and nothing else changes.
24004
24300
  *
24005
- * The whole row a rule parked for a human, `tool_input` included, which is what makes the decision reviewable rather than a yes or no on an id. Two selected columns are deliberately off the wire: `decision_history`, and `metadata`, where the decision path records whether the approver's identity was verified or merely asserted.
24301
+ * The whole row a rule parked for a human, `tool_input` included, which is what makes the decision reviewable rather than a yes or no on an id. That is the dashboard's view (service auth). An API key or an OAuth token reads only holds raised under its own project, and without `tool_input`; any other hold is the same 404 as one that does not exist. Two selected columns are deliberately off the wire: `decision_history`, and `metadata`, where the decision path records whether the approver's identity was verified or merely asserted.
24006
24302
  *
24007
24303
  * An approval belonging to another org is indistinguishable from one that does not exist: both are 404, and an `{orgId}` that is not the credential's own org is 404 before any read.
24008
24304
  */
@@ -24033,8 +24329,8 @@ export interface paths {
24033
24329
  matched_rule_id: string;
24034
24330
  matched_rule_name: string;
24035
24331
  tool_name: string;
24036
- /** @description The tool arguments the rule held for review, verbatim. */
24037
- tool_input: {
24332
+ /** @description The tool arguments the rule held for review, verbatim. Absent unless the caller is the dashboard (service auth): an API key or an OAuth token never receives it. */
24333
+ tool_input?: {
24038
24334
  [key: string]: unknown;
24039
24335
  };
24040
24336
  /**
@@ -24092,7 +24388,7 @@ export interface paths {
24092
24388
  * @deprecated
24093
24389
  * @description DEPRECATED alias of `GET /v1/approvals/health` (ent#979), kept for two releases. Same handler, same `approvals:read` scope.
24094
24390
  *
24095
- * The operator snapshot behind a queue alarm: how many approvals are pending for this org and how long the oldest has been waiting. `status` is a verdict on the sweeps only (`degraded` when `last_sweep_at` is null or older than 30 minutes, `status_reason` says which), never on queue depth, so alert on `oldest_pending_age_seconds` for that. Both counters are null or zero on an empty queue.
24391
+ * The operator snapshot behind a queue alarm: how many approvals are pending for this org (for an API key or an OAuth token, for its own project) and how long the oldest has been waiting. `status` is a verdict on the sweeps only (`degraded` when `last_sweep_at` is null or older than 30 minutes, `status_reason` says which), never on queue depth, so alert on `oldest_pending_age_seconds` for that. Both counters are null or zero on an empty queue.
24096
24392
  *
24097
24393
  * `last_sweep_at` is PLATFORM-WIDE, not per org: it is the last run of the expiry and orphan sweeps across the deployment. A stale value there means approvals may be sitting past their `expires_at` without being marked expired anywhere, which is a different fault from a deep queue.
24098
24394
  *
@@ -25861,7 +26157,7 @@ export interface paths {
25861
26157
  };
25862
26158
  };
25863
26159
  };
25864
- /** @description The dashboard path named no admin, or a token that did not verify. Bare `{ error, remediation }` body. */
26160
+ /** @description The dashboard path named no admin, or a token that did not verify; or the credential is an OAuth token, which cannot pause (`bearer_token_cannot_decide`: only a bearer key, as itself, or the dashboard, as a person, presses the switch). Bare `{ error, remediation }` body. */
25865
26161
  403: {
25866
26162
  headers: {
25867
26163
  [name: string]: unknown;
@@ -29920,7 +30216,7 @@ export interface paths {
29920
30216
  * Complete the review of a payable
29921
30217
  * @description A person records what they decided about a payable that is `NEEDS_REVIEW`, and the payable comes out `READY` or `REJECTED`. There is no third outcome and no partial review: a call that leaves anything blocking is refused and writes nothing, so resolve every item of `review` in one call.
29922
30218
  *
29923
- * **Who may review.** A verified human. Forward the reviewer's Clerk session token in `x-codespar-user-token` on a service-auth request; that user must be an `admin` of the organization. A project API key is refused (`bearer_token_cannot_decide`): a key proves which service is calling, never which person confirmed, and the audit event names the person.
30219
+ * **Who may review.** A verified human. Forward the reviewer's Clerk session token in `x-codespar-user-token` on a service-auth request; that user must be an `admin` of the organization. Any other credential, a project API key or an OAuth token, is refused (`bearer_token_cannot_decide`) even with a user token: a key or token proves which service is calling, never which person confirmed, and the audit event names the person.
29924
30220
  *
29925
30221
  * **The ordinary review is an empty one.** `resolutions: []` releases a payable that no check failed and that only a review trigger holds — typically `new_supplier`, a supplier this project has never paid. The person is still recorded as the author.
29926
30222
  *
@@ -30492,7 +30788,7 @@ export interface paths {
30492
30788
  };
30493
30789
  };
30494
30790
  };
30495
- /** @description The reviewer was not established. `bearer_token_cannot_decide`: a project API key cannot review. `user_token_required`: no token was forwarded. `user_token_invalid`: the token did not verify — mint a fresh one. `user_token_identity_mismatch`: `x-codespar-user` names somebody else. `insufficient_role`: that user is not an admin of the organization. */
30791
+ /** @description The reviewer was not established. `bearer_token_cannot_decide`: the credential is not service auth (a project API key or an OAuth token), which cannot review. `user_token_required`: no token was forwarded. `user_token_invalid`: the token did not verify — mint a fresh one. `user_token_identity_mismatch`: `x-codespar-user` names somebody else. `insufficient_role`: that user is not an admin of the organization. */
30496
30792
  403: {
30497
30793
  headers: {
30498
30794
  [name: string]: unknown;
@@ -33930,7 +34226,7 @@ export interface paths {
33930
34226
  };
33931
34227
  };
33932
34228
  };
33933
- /** @description Failed Dependency — no usable credential for this server. `reason` is the resolver's own outcome. */
34229
+ /** @description Failed Dependency — no usable credential for this server. `reason` is the resolver's own outcome. Or `shared_sandbox_live_project_refused`: the project's connection for this server is the platform's shared SANDBOX credential, which serves test projects only, and this is a live project or a live-mode call; `reason` is that environment. Answered whether or not `PROXY_REQUIRE_CONNECTION` is set, nothing is sent upstream, and the fix is to connect the project's own credential (p2-proxy#5). */
33934
34230
  424: {
33935
34231
  headers: {
33936
34232
  [name: string]: unknown;
@@ -33943,6 +34239,12 @@ export interface paths {
33943
34239
  reason: "server_unknown" | "not_connected" | "endpoint_missing";
33944
34240
  server: string;
33945
34241
  message: string;
34242
+ } | {
34243
+ /** @enum {string} */
34244
+ error: "shared_sandbox_live_project_refused";
34245
+ reason: string;
34246
+ server: string;
34247
+ message: string;
33946
34248
  };
33947
34249
  };
33948
34250
  };
@@ -36675,6 +36977,10 @@ export interface components {
36675
36977
  price_minor: number;
36676
36978
  payee: string;
36677
36979
  session_id?: string;
36980
+ /**
36981
+ * Format: date-time
36982
+ * @description When the offer was presented, RFC 3339 with an offset, in UTC years 0001-9999. Sealed into the receipt's chain as that instant in UTC with milliseconds. Anything else is refused with `invalid_receipt_timestamp` before anything is read, held or sent.
36983
+ */
36678
36984
  at?: string;
36679
36985
  };
36680
36986
  /** @description WHO triggered this spend, recorded on the receipt and read back from it. Optional, and absence is a real answer: a spend that sends no actor records `null`, and nothing is ever inferred from `agent_id` — that field names the agent the mandate was SIGNED for, which is an authority, not an event. A person acting through WhatsApp under an agent's mandate and the agent acting unattended are the same row without this field. */
@@ -36693,6 +36999,17 @@ export interface components {
36693
36999
  /** @description Where the person acted from: `whatsapp`, `dashboard`, `cli`. */
36694
37000
  channel?: string;
36695
37001
  };
37002
+ /**
37003
+ * @description The hash of what the caller says a person approved: `items_hash` for this spend's lines, and for a batch `batch_hash` over the whole presented list. When present it is SEALED into the receipt's signed chain as its own link (chain version 4), so it cannot be stripped or altered afterwards without breaking both `receipt_sig` and `receipt_sig_ed25519`, and the receipt reads return it.
37004
+ *
37005
+ * It is a SEALED CLAIM by the caller, NOT a server-side approval check. The server only validates the shape; it never interprets the value, never compares it with anything and never refuses a spend because of it. It proves that this payment was sealed together with this hash, so whoever holds the approval artifact can recompute the hash and hold it against the receipt. It does not prove that anybody approved anything, or who. A malformed value, or a key other than these two, is refused with `invalid_approval_hash` before anything is read, held or sent. Part of what makes two requests with one `attempt_id` the same payment: a repeat with a different `approval` is `attempt_id_conflict`.
37006
+ */
37007
+ SpendApproval: {
37008
+ /** @description A SHA-256 digest in lowercase hex, optionally prefixed `sha256:`. Sealed exactly as sent. */
37009
+ items_hash: string;
37010
+ /** @description A SHA-256 digest in lowercase hex, optionally prefixed `sha256:`. Sealed exactly as sent. */
37011
+ batch_hash?: string;
37012
+ };
36696
37013
  };
36697
37014
  responses: never;
36698
37015
  parameters: never;