From 8178af076395e7224abcd78a9c4a2e633dca2621 Mon Sep 17 00:00:00 2001 From: "ls-bolt[bot]" <264508440+ls-bolt[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 01:12:44 +0000 Subject: [PATCH] Require substatus and reason on a card status change A card that changes state without a recorded cause leaves no way to tell later why it was frozen or closed, or on whose word. Both fields are now required whenever status is supplied: substatus names the cause in the issuer's vocabulary and is forwarded to the issuer; reason is the sentence a later reader actually needs. Co-Authored-By: benwgold --- mintlify/openapi.yaml | 33 +++++++++++++++++-- openapi.yaml | 33 +++++++++++++++++-- .../schemas/cards/CardUpdateRequest.yaml | 31 ++++++++++++++++- openapi/paths/cards/cards_{id}.yaml | 13 ++++++-- 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 79c620c16..2ef39c30f 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -8812,10 +8812,14 @@ paths: summary: Freeze an active card value: status: FROZEN + substatus: SUSPICIOUS_ACTIVITY + reason: Unrecognised charges reported by the cardholder. unfreeze: summary: Unfreeze a frozen card value: status: ACTIVE + substatus: ISSUER_REQUEST + reason: Review closed with no action. updateFundingSource: summary: Replace the card's bound funding source value: @@ -8836,11 +8840,15 @@ paths: summary: Freeze the card and replace its funding source in one call value: status: FROZEN + substatus: SUSPICIOUS_ACTIVITY + reason: Unrecognised charges reported by the cardholder. fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 close: summary: Permanently close the card value: status: CLOSED + substatus: END_USER_REQUEST + reason: Cardholder asked us to close the card. responses: '200': description: Card updated. Returns the updated card. @@ -8849,7 +8857,7 @@ paths: schema: $ref: '#/components/schemas/Card' '400': - description: Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency, with `INVALID_INPUT` when `fundingSource` is combined with any `status` change on a card program where the card issuer makes authorization decisions, and for general invalid parameters. + description: Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency, with `INVALID_INPUT` when `fundingSource` is combined with any `status` change on a card program where the card issuer makes authorization decisions, with `INVALID_INPUT` when `status` is supplied without `substatus` or without a non-empty `reason`, and for general invalid parameters. content: application/json: schema: @@ -27467,7 +27475,7 @@ components: example: 20 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay` must be supplied. `status` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. + description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. properties: status: type: string @@ -27477,6 +27485,27 @@ components: - CLOSED description: Target status for the card. Permitted transitions are `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN + substatus: + type: string + enum: + - LOST + - COMPROMISED + - DAMAGED + - END_USER_REQUEST + - ISSUER_REQUEST + - NOT_ACTIVE + - SUSPICIOUS_ACTIVITY + - INTERNAL_REVIEW + - EXPIRED + - UNDELIVERABLE + - OTHER + description: Why the card is moving, in the card issuer's vocabulary. Required whenever `status` is supplied, and forwarded to the issuer. Pick `OTHER` when none of the named values fit and say why in `reason`. + example: SUSPICIOUS_ACTIVITY + reason: + type: string + minLength: 1 + description: A short sentence naming why the card is moving. Required whenever `status` is supplied, and recorded against the card so a later reader can tell why it changed. + example: Cardholder reported the card stolen. fundingSource: type: string description: 'Replaces the card''s funding source. Must belong to the customer and be denominated in the card''s currency. Cannot be supplied alongside `status: CLOSED`. To stop a card from spending, set `status: FROZEN` instead.' diff --git a/openapi.yaml b/openapi.yaml index 79c620c16..2ef39c30f 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -8812,10 +8812,14 @@ paths: summary: Freeze an active card value: status: FROZEN + substatus: SUSPICIOUS_ACTIVITY + reason: Unrecognised charges reported by the cardholder. unfreeze: summary: Unfreeze a frozen card value: status: ACTIVE + substatus: ISSUER_REQUEST + reason: Review closed with no action. updateFundingSource: summary: Replace the card's bound funding source value: @@ -8836,11 +8840,15 @@ paths: summary: Freeze the card and replace its funding source in one call value: status: FROZEN + substatus: SUSPICIOUS_ACTIVITY + reason: Unrecognised charges reported by the cardholder. fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 close: summary: Permanently close the card value: status: CLOSED + substatus: END_USER_REQUEST + reason: Cardholder asked us to close the card. responses: '200': description: Card updated. Returns the updated card. @@ -8849,7 +8857,7 @@ paths: schema: $ref: '#/components/schemas/Card' '400': - description: Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency, with `INVALID_INPUT` when `fundingSource` is combined with any `status` change on a card program where the card issuer makes authorization decisions, and for general invalid parameters. + description: Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency, with `INVALID_INPUT` when `fundingSource` is combined with any `status` change on a card program where the card issuer makes authorization decisions, with `INVALID_INPUT` when `status` is supplied without `substatus` or without a non-empty `reason`, and for general invalid parameters. content: application/json: schema: @@ -27467,7 +27475,7 @@ components: example: 20 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay` must be supplied. `status` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. + description: Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay` must be supplied. Supplying `status` also requires `substatus` and `reason`, so every card state change carries why it happened. `status` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. properties: status: type: string @@ -27477,6 +27485,27 @@ components: - CLOSED description: Target status for the card. Permitted transitions are `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN + substatus: + type: string + enum: + - LOST + - COMPROMISED + - DAMAGED + - END_USER_REQUEST + - ISSUER_REQUEST + - NOT_ACTIVE + - SUSPICIOUS_ACTIVITY + - INTERNAL_REVIEW + - EXPIRED + - UNDELIVERABLE + - OTHER + description: Why the card is moving, in the card issuer's vocabulary. Required whenever `status` is supplied, and forwarded to the issuer. Pick `OTHER` when none of the named values fit and say why in `reason`. + example: SUSPICIOUS_ACTIVITY + reason: + type: string + minLength: 1 + description: A short sentence naming why the card is moving. Required whenever `status` is supplied, and recorded against the card so a later reader can tell why it changed. + example: Cardholder reported the card stolen. fundingSource: type: string description: 'Replaces the card''s funding source. Must belong to the customer and be denominated in the card''s currency. Cannot be supplied alongside `status: CLOSED`. To stop a card from spending, set `status: FROZEN` instead.' diff --git a/openapi/components/schemas/cards/CardUpdateRequest.yaml b/openapi/components/schemas/cards/CardUpdateRequest.yaml index edd512186..72ade0699 100644 --- a/openapi/components/schemas/cards/CardUpdateRequest.yaml +++ b/openapi/components/schemas/cards/CardUpdateRequest.yaml @@ -2,7 +2,9 @@ type: object description: >- Update request for `PATCH /cards/{id}`. At least one of `status`, `fundingSource`, `maxSpendPerTransaction`, `maxSpendPerDay`, or - `maxTransactionsPerDay` must be supplied. `status` + `maxTransactionsPerDay` must be supplied. Supplying `status` also requires + `substatus` and `reason`, so every card state change carries why it + happened. `status` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and irreversible and @@ -21,6 +23,33 @@ properties: terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again. example: FROZEN + substatus: + type: string + enum: + - LOST + - COMPROMISED + - DAMAGED + - END_USER_REQUEST + - ISSUER_REQUEST + - NOT_ACTIVE + - SUSPICIOUS_ACTIVITY + - INTERNAL_REVIEW + - EXPIRED + - UNDELIVERABLE + - OTHER + description: >- + Why the card is moving, in the card issuer's vocabulary. Required + whenever `status` is supplied, and forwarded to the issuer. Pick + `OTHER` when none of the named values fit and say why in `reason`. + example: SUSPICIOUS_ACTIVITY + reason: + type: string + minLength: 1 + description: >- + A short sentence naming why the card is moving. Required whenever + `status` is supplied, and recorded against the card so a later reader + can tell why it changed. + example: Cardholder reported the card stolen. fundingSource: type: string description: >- diff --git a/openapi/paths/cards/cards_{id}.yaml b/openapi/paths/cards/cards_{id}.yaml index b62bc3fec..12798ee6f 100644 --- a/openapi/paths/cards/cards_{id}.yaml +++ b/openapi/paths/cards/cards_{id}.yaml @@ -157,10 +157,14 @@ patch: summary: Freeze an active card value: status: FROZEN + substatus: SUSPICIOUS_ACTIVITY + reason: Unrecognised charges reported by the cardholder. unfreeze: summary: Unfreeze a frozen card value: status: ACTIVE + substatus: ISSUER_REQUEST + reason: Review closed with no action. updateFundingSource: summary: Replace the card's bound funding source value: @@ -181,11 +185,15 @@ patch: summary: Freeze the card and replace its funding source in one call value: status: FROZEN + substatus: SUSPICIOUS_ACTIVITY + reason: Unrecognised charges reported by the cardholder. fundingSource: InternalAccount:019542f5-b3e7-1d02-0000-000000000003 close: summary: Permanently close the card value: status: CLOSED + substatus: END_USER_REQUEST + reason: Cardholder asked us to close the card. responses: '200': description: Card updated. Returns the updated card. @@ -199,8 +207,9 @@ patch: supplied funding source does not belong to the cardholder or is not denominated in the card's currency, with `INVALID_INPUT` when `fundingSource` is combined with any `status` change on a card program - where the card issuer makes authorization decisions, and for general - invalid parameters. + where the card issuer makes authorization decisions, with + `INVALID_INPUT` when `status` is supplied without `substatus` or + without a non-empty `reason`, and for general invalid parameters. content: application/json: schema: