From ce1a21834b86199facf6f1b0b75da7fc5c1bde04 Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Tue, 8 Sep 2026 20:09:22 -0700 Subject: [PATCH] feat(cards): add UTC daily transaction count limit contract Adds `maxTransactionsPerDay` to card create, card update, card responses, and platform `cardConfigs`, mirroring `maxSpendPerDay`: a card-specific and a platform-level cap on the number of transactions a card may authorize during one UTC calendar day. Grid enforces the lower of the two values without replacing either. On update, omitting the field leaves the card's cap unchanged, null clears it, and a positive int32 sets it. Each approved authorization counts once for the UTC day it was authorized. Refunds, reversals, and authorization expiries do not restore capacity during the day. Also updates the path descriptions, request and webhook examples, and the two mintlify card snippets. Sparkcore persistence and enforcement follow in webdev. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01YUkUFAQ1ccTXALipsg5x23 --- mintlify/openapi.yaml | 56 +++++++++++++++++-- .../snippets/cards/freezing-and-closing.mdx | 19 ++++++- mintlify/snippets/cards/issuing-cards.mdx | 4 +- openapi.yaml | 56 +++++++++++++++++-- openapi/components/schemas/cards/Card.yaml | 19 +++++++ .../schemas/cards/CardCreateRequest.yaml | 15 +++++ .../schemas/cards/CardUpdateRequest.yaml | 26 +++++++-- .../components/schemas/config/CardConfig.yaml | 18 ++++++ .../config/PlatformConfigUpdateRequest.yaml | 2 +- openapi/paths/cards/cards.yaml | 9 ++- openapi/paths/cards/cards_{id}.yaml | 16 +++++- openapi/paths/platform/config.yaml | 5 +- .../webhooks/card-funding-source-change.yaml | 1 + openapi/webhooks/card-state-change.yaml | 3 + 14 files changed, 222 insertions(+), 27 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 95485e3d3..4fdca01c1 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -92,7 +92,7 @@ paths: $ref: '#/components/schemas/Error500' patch: summary: Update platform configuration - description: Update platform configuration settings. `cardConfigs` can establish platform-level per-transaction and UTC-calendar-day card caps. Grid enforces the lower of each platform cap and its corresponding card-specific value without replacing the card-specific value. Daily limits reset at 00:00 UTC. + description: Update platform configuration settings. `cardConfigs` can establish platform-level caps on a single card transaction, on spend during one UTC calendar day, and on the number of transactions during one UTC calendar day. Grid enforces the lower of each platform cap and its corresponding card-specific value without replacing the card-specific value. Daily limits reset at 00:00 UTC. operationId: updatePlatformConfig tags: - Platform Configuration @@ -142,6 +142,7 @@ paths: cardConfigs: maxSpendPerTransaction: 10000 maxSpendPerDay: 50000 + maxTransactionsPerDay: 50 responses: '200': description: Configuration updated successfully @@ -8552,7 +8553,7 @@ paths: description: | Issue a new card for a cardholder. Every card must be bound to at least one funding source at create time. The cardholder must have KYC status `APPROVED` before a card can be issued; otherwise the request is rejected with `CARDHOLDER_KYC_NOT_APPROVED`. - Optional `maxSpendPerTransaction` and `maxSpendPerDay` values set the card-specific caps on one transaction and one UTC calendar day. The limits are enforced by Grid for card programs where Grid makes the authorization decision, whether the card is funded by an Embedded Wallet account or custodial fiat. If the platform config sets the corresponding `cardConfigs` value, Grid enforces the lower of the card and platform caps. All values use the smallest unit of the card's currency. + Optional `maxSpendPerTransaction`, `maxSpendPerDay`, and `maxTransactionsPerDay` values set the card-specific caps on one transaction, on spend during one UTC calendar day, and on the number of transactions during one UTC calendar day. The limits are enforced by Grid for card programs where Grid makes the authorization decision, whether the card is funded by an Embedded Wallet account or custodial fiat. If the platform config sets the corresponding `cardConfigs` value, Grid enforces the lower of the card and platform caps. Amounts use the smallest unit of the card's currency. If any funding source is an Embedded Wallet internal account, the cardholder must authorize Grid to sign Spark token transactions for that card funding source by completing the delegated-key creation flow with `POST /auth/delegated-keys`. Until an active delegated key exists for that funding source, Authorization Decisioning cannot use it to fund card transactions. @@ -8579,6 +8580,7 @@ paths: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: 5000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 responses: '201': description: Card created successfully. Newly-created cards start in `PROCESSING` while the issuer provisions them. Cards funded by an Embedded Wallet internal account also require an active delegated key for that funding source before Authorization Decisioning can use it. @@ -8750,12 +8752,13 @@ paths: patch: summary: Update a card description: | - Update a card's `state`, bound `fundingSources`, and / or `maxSpendPerTransaction`, or `maxSpendPerDay`. At least one field must be supplied. + Update a card's `state`, bound `fundingSources`, and / or `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. At least one field must be supplied. - `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Any other transition returns `409 INVALID_STATE_TRANSITION`. - `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least one source. `fundingSources` cannot be supplied alongside `state: CLOSED`. - `maxSpendPerTransaction`, when supplied, replaces the card-specific per-transaction cap. Supply a positive integer in the smallest unit of the card's currency to set it or null to clear it. If the platform config sets `cardConfigs.maxSpendPerTransaction`, Grid enforces the lower of the card and platform values. Limits are supported only for card programs where Grid makes the authorization decision. `maxSpendPerTransaction` cannot be supplied alongside `state: CLOSED`. - `maxSpendPerDay`, when supplied, replaces the card-specific cap on cumulative new spend during one UTC calendar day. Supply a positive integer in the smallest unit of the card's currency to set it or null to clear it. If the platform config sets `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. `maxSpendPerDay` cannot be supplied alongside `state: CLOSED`. + - `maxTransactionsPerDay`, when supplied, replaces the card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Supply a positive integer to set it or null to clear it. If the platform config sets `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. `maxTransactionsPerDay` cannot be supplied alongside `state: CLOSED`. This endpoint is authenticated by the platform credential alone and returns `200` directly. It deliberately does not use Grid's 202 → signed-retry pattern: that pattern signs with the session key of a credential on the owning internal account, so it models actions taken *by* the end user on their own credentials or funds. Freezing or closing a card is routinely an action taken *about* a user and without them present - fraud response, offboarding, an ops-driven freeze - and requiring the cardholder's signature would make exactly those cases impossible. Operations that expose sensitive card data (`POST /cards/{id}/reveal`, 3DS password retrieval) are SCA-railed instead, because there the cardholder is the party being served. @@ -8793,15 +8796,17 @@ paths: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 setSpendingLimit: - summary: Set the card's per-transaction spending limit + summary: Set the card's spending and transaction limits value: maxSpendPerTransaction: 10000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 clearSpendingLimit: summary: Remove the card's spending limits value: maxSpendPerTransaction: null maxSpendPerDay: null + maxTransactionsPerDay: null freezeAndUpdateSources: summary: Freeze the card and replace its funding sources in one call value: @@ -11532,6 +11537,7 @@ webhooks: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: 5000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 currency: USD processorRef: card_b81c2a4f issuerRef: lead_card_7a1b9c3d @@ -11553,6 +11559,7 @@ webhooks: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: null maxSpendPerDay: null + maxTransactionsPerDay: null currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:12:00Z' @@ -11576,6 +11583,7 @@ webhooks: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: 5000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-09T09:00:00Z' @@ -11652,6 +11660,7 @@ webhooks: - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 maxSpendPerTransaction: null maxSpendPerDay: null + maxTransactionsPerDay: null currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:30:00Z' @@ -12298,6 +12307,15 @@ components: - type: 'null' description: Platform-level cap on cumulative new spend during one UTC calendar day for every card whose authorization decisions are made by Grid. The value is interpreted in the smallest unit of each card's currency. Grid enforces the lower of this cap and the card's configured `maxSpendPerDay`; null means no platform-level daily cap. The window resets at 00:00 UTC. Refunds, reversals, and authorization expiries do not restore capacity during the day. The cap applies to existing cards and cards issued later. Provider-decided card programs are unaffected. example: 50000 + maxTransactionsPerDay: + anyOf: + - type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + - type: 'null' + description: Platform-level cap on the number of transactions each card may authorize during one UTC calendar day, for every card whose authorization decisions are made by Grid. Grid enforces the lower of this cap and the card's configured `maxTransactionsPerDay`; null means no platform-level daily transaction cap. The window resets at 00:00 UTC. Each approved authorization counts once; refunds, reversals, and authorization expiries do not restore capacity during the day. The cap applies to existing cards and cards issued later. Provider-decided card programs are unaffected. + example: 50 FeeType: type: string enum: @@ -12592,7 +12610,7 @@ components: to subsequent delivery attempts. cardConfigs: $ref: '#/components/schemas/CardConfig' - description: Update platform-level card settings. Fields omitted from the nested object are left unchanged. For either spending limit, supply null to clear the platform cap or a positive integer to set it. Omit this field at the top level to leave all card settings unchanged. + description: Update platform-level card settings. Fields omitted from the nested object are left unchanged. For any card limit, supply null to clear the platform cap or a positive integer to set it. Omit this field at the top level to leave all card settings unchanged. feeConfigs: type: array items: @@ -25975,6 +25993,7 @@ components: - fundingSources - maxSpendPerTransaction - maxSpendPerDay + - maxTransactionsPerDay - createdAt - updatedAt properties: @@ -26042,6 +26061,15 @@ components: - type: 'null' description: Card-specific cap on cumulative new spend during one UTC calendar day, in the smallest unit of the card's `currency`. The window resets at 00:00 UTC. Null means the card has no card-specific daily cap. When the platform config also supplies `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the two values without replacing this configured value. Refunds, reversals, and authorization expiries do not restore capacity during the day. Spend exactly equal to the effective limit is allowed. example: 25000 + maxTransactionsPerDay: + anyOf: + - type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + - type: 'null' + description: Card-specific cap on the number of transactions the card may authorize during one UTC calendar day. The window resets at 00:00 UTC. Null means the card has no card-specific daily transaction cap. When the platform config also supplies `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the two values without replacing this configured value. Each approved authorization counts once for the day it was authorized; refunds, reversals, and authorization expiries do not restore capacity during the day. A transaction that brings the day's count exactly to the effective limit is allowed. + example: 20 currency: type: string description: Currency the card transacts in (ISO 4217 for fiat, tickers for crypto). Derived from the funding sources at issue time — all funding sources bound to a card must be denominated in the same card-eligible currency. @@ -26132,9 +26160,16 @@ components: maximum: 9007199254740991 description: Optional card-specific cap on cumulative new spend during one UTC calendar day, in the smallest unit of the card currency derived from its funding sources. Omit this field for no card-specific daily cap. When the platform config also supplies `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the two values. The window resets at 00:00 UTC, and refunds, reversals, and authorization expiries do not restore capacity during the day. Supported only for card programs whose authorization decisions are made by Grid. Spend exactly equal to the effective limit is allowed. example: 25000 + maxTransactionsPerDay: + type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + description: Optional card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Omit this field for no card-specific daily transaction cap. When the platform config also supplies `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the two values. The window resets at 00:00 UTC. Each approved authorization counts once; refunds, reversals, and authorization expiries do not restore capacity during the day. Supported only for card programs whose authorization decisions are made by Grid. + example: 20 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `state`, `fundingSources`, `maxSpendPerTransaction`, or `maxSpendPerDay` must be supplied. `state` 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 `fundingSources`, `maxSpendPerTransaction`, or `maxSpendPerDay`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. + description: Update request for `PATCH /cards/{id}`. At least one of `state`, `fundingSources`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay` must be supplied. `state` 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 `fundingSources`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. properties: state: type: string @@ -26171,6 +26206,15 @@ components: - type: 'null' description: 'Replacement card-specific UTC-calendar-day cap, in the smallest unit of the card''s currency. Omit this field to leave the current cap unchanged, supply null to clear it, or supply a positive integer to set it. When the platform config also supplies `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the two values. Refunds, reversals, and authorization expiries do not restore capacity during the day. Supported only for card programs whose authorization decisions are made by Grid. Cannot be supplied alongside `state: CLOSED`.' example: 25000 + maxTransactionsPerDay: + anyOf: + - type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + - type: 'null' + description: 'Replacement card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Omit this field to leave the current cap unchanged, supply null to clear it, or supply a positive integer to set it. When the platform config also supplies `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the two values. Refunds, reversals, and authorization expiries do not restore capacity during the day. Supported only for card programs whose authorization decisions are made by Grid. Cannot be supplied alongside `state: CLOSED`.' + example: 20 CardRevealResponse: type: object required: diff --git a/mintlify/snippets/cards/freezing-and-closing.mdx b/mintlify/snippets/cards/freezing-and-closing.mdx index d898bb26b..8b6ce4387 100644 --- a/mintlify/snippets/cards/freezing-and-closing.mdx +++ b/mintlify/snippets/cards/freezing-and-closing.mdx @@ -4,8 +4,9 @@ Freeze, close, and other card updates use a single authenticated `PATCH /cards/{id}` covers freeze / unfreeze (`state`), funding source updates (`fundingSources`), per-transaction spending limits -(`maxSpendPerTransaction`), and UTC-calendar-day spending limits -(`maxSpendPerDay`). See +(`maxSpendPerTransaction`), UTC-calendar-day spending limits +(`maxSpendPerDay`), and UTC-calendar-day transaction limits +(`maxTransactionsPerDay`). See [Funding sources](/cards/card-management/funding-sources) for the funding-source-only flow. @@ -104,6 +105,20 @@ curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ -d '{ "maxSpendPerDay": 25000 }' ``` +## Updating the daily transaction limit + +Set `maxTransactionsPerDay` to a positive integer to cap how many transactions +the card may authorize per UTC calendar day, or set it to `null` to clear the +card-specific cap. Each approved authorization counts once; refunds, reversals, +and authorization expiries do not restore capacity during the same day. + +```bash +curl -X PATCH "$GRID_BASE_URL/cards/Card:019542f5-b3e7-1d02-0000-000000000010" \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{ "maxTransactionsPerDay": 20 }' +``` + ## Sandbox behavior In Sandbox the state changes are instant — no issuer round-trip is diff --git a/mintlify/snippets/cards/issuing-cards.mdx b/mintlify/snippets/cards/issuing-cards.mdx index 823c32879..e64006fd8 100644 --- a/mintlify/snippets/cards/issuing-cards.mdx +++ b/mintlify/snippets/cards/issuing-cards.mdx @@ -16,7 +16,8 @@ curl -X POST "$GRID_BASE_URL/cards" \ "InternalAccount:019542f5-b3e7-1d02-0000-000000000002" ], "maxSpendPerTransaction": 5000, - "maxSpendPerDay": 25000 + "maxSpendPerDay": 25000, + "maxTransactionsPerDay": 20 }' ``` @@ -28,6 +29,7 @@ curl -X POST "$GRID_BASE_URL/cards" \ | `fundingSources` | Yes | Ordered array of `InternalAccount` ids. Each must belong to the cardholder and share one card-eligible currency. The first entry is tried first by Authorization Decisioning. | | `maxSpendPerTransaction` | No | Largest amount a single card transaction may authorize, in the smallest unit of the card's currency. Omit for no limit. Supported only for card programs where Grid makes the authorization decision. | | `maxSpendPerDay` | No | Cumulative new spend allowed per UTC calendar day, in the smallest unit of the card's currency. Refunds, reversals, and expiries do not restore capacity that day. | +| `maxTransactionsPerDay` | No | Number of transactions the card may authorize per UTC calendar day. Each approved authorization counts once; reversals and expiries do not restore capacity that day. | The card's `currency` is derived from the funding sources at issue time and surfaces on the returned `Card` resource — all bound sources share diff --git a/openapi.yaml b/openapi.yaml index 95485e3d3..4fdca01c1 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -92,7 +92,7 @@ paths: $ref: '#/components/schemas/Error500' patch: summary: Update platform configuration - description: Update platform configuration settings. `cardConfigs` can establish platform-level per-transaction and UTC-calendar-day card caps. Grid enforces the lower of each platform cap and its corresponding card-specific value without replacing the card-specific value. Daily limits reset at 00:00 UTC. + description: Update platform configuration settings. `cardConfigs` can establish platform-level caps on a single card transaction, on spend during one UTC calendar day, and on the number of transactions during one UTC calendar day. Grid enforces the lower of each platform cap and its corresponding card-specific value without replacing the card-specific value. Daily limits reset at 00:00 UTC. operationId: updatePlatformConfig tags: - Platform Configuration @@ -142,6 +142,7 @@ paths: cardConfigs: maxSpendPerTransaction: 10000 maxSpendPerDay: 50000 + maxTransactionsPerDay: 50 responses: '200': description: Configuration updated successfully @@ -8552,7 +8553,7 @@ paths: description: | Issue a new card for a cardholder. Every card must be bound to at least one funding source at create time. The cardholder must have KYC status `APPROVED` before a card can be issued; otherwise the request is rejected with `CARDHOLDER_KYC_NOT_APPROVED`. - Optional `maxSpendPerTransaction` and `maxSpendPerDay` values set the card-specific caps on one transaction and one UTC calendar day. The limits are enforced by Grid for card programs where Grid makes the authorization decision, whether the card is funded by an Embedded Wallet account or custodial fiat. If the platform config sets the corresponding `cardConfigs` value, Grid enforces the lower of the card and platform caps. All values use the smallest unit of the card's currency. + Optional `maxSpendPerTransaction`, `maxSpendPerDay`, and `maxTransactionsPerDay` values set the card-specific caps on one transaction, on spend during one UTC calendar day, and on the number of transactions during one UTC calendar day. The limits are enforced by Grid for card programs where Grid makes the authorization decision, whether the card is funded by an Embedded Wallet account or custodial fiat. If the platform config sets the corresponding `cardConfigs` value, Grid enforces the lower of the card and platform caps. Amounts use the smallest unit of the card's currency. If any funding source is an Embedded Wallet internal account, the cardholder must authorize Grid to sign Spark token transactions for that card funding source by completing the delegated-key creation flow with `POST /auth/delegated-keys`. Until an active delegated key exists for that funding source, Authorization Decisioning cannot use it to fund card transactions. @@ -8579,6 +8580,7 @@ paths: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: 5000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 responses: '201': description: Card created successfully. Newly-created cards start in `PROCESSING` while the issuer provisions them. Cards funded by an Embedded Wallet internal account also require an active delegated key for that funding source before Authorization Decisioning can use it. @@ -8750,12 +8752,13 @@ paths: patch: summary: Update a card description: | - Update a card's `state`, bound `fundingSources`, and / or `maxSpendPerTransaction`, or `maxSpendPerDay`. At least one field must be supplied. + Update a card's `state`, bound `fundingSources`, and / or `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. At least one field must be supplied. - `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Any other transition returns `409 INVALID_STATE_TRANSITION`. - `fundingSources`, when supplied, fully replaces the card's bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card's currency; the list must contain at least one source. `fundingSources` cannot be supplied alongside `state: CLOSED`. - `maxSpendPerTransaction`, when supplied, replaces the card-specific per-transaction cap. Supply a positive integer in the smallest unit of the card's currency to set it or null to clear it. If the platform config sets `cardConfigs.maxSpendPerTransaction`, Grid enforces the lower of the card and platform values. Limits are supported only for card programs where Grid makes the authorization decision. `maxSpendPerTransaction` cannot be supplied alongside `state: CLOSED`. - `maxSpendPerDay`, when supplied, replaces the card-specific cap on cumulative new spend during one UTC calendar day. Supply a positive integer in the smallest unit of the card's currency to set it or null to clear it. If the platform config sets `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. `maxSpendPerDay` cannot be supplied alongside `state: CLOSED`. + - `maxTransactionsPerDay`, when supplied, replaces the card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Supply a positive integer to set it or null to clear it. If the platform config sets `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the card and platform values. Refunds, reversals, and authorization expiries do not restore capacity during the day. `maxTransactionsPerDay` cannot be supplied alongside `state: CLOSED`. This endpoint is authenticated by the platform credential alone and returns `200` directly. It deliberately does not use Grid's 202 → signed-retry pattern: that pattern signs with the session key of a credential on the owning internal account, so it models actions taken *by* the end user on their own credentials or funds. Freezing or closing a card is routinely an action taken *about* a user and without them present - fraud response, offboarding, an ops-driven freeze - and requiring the cardholder's signature would make exactly those cases impossible. Operations that expose sensitive card data (`POST /cards/{id}/reveal`, 3DS password retrieval) are SCA-railed instead, because there the cardholder is the party being served. @@ -8793,15 +8796,17 @@ paths: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 setSpendingLimit: - summary: Set the card's per-transaction spending limit + summary: Set the card's spending and transaction limits value: maxSpendPerTransaction: 10000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 clearSpendingLimit: summary: Remove the card's spending limits value: maxSpendPerTransaction: null maxSpendPerDay: null + maxTransactionsPerDay: null freezeAndUpdateSources: summary: Freeze the card and replace its funding sources in one call value: @@ -11532,6 +11537,7 @@ webhooks: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: 5000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 currency: USD processorRef: card_b81c2a4f issuerRef: lead_card_7a1b9c3d @@ -11553,6 +11559,7 @@ webhooks: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: null maxSpendPerDay: null + maxTransactionsPerDay: null currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:12:00Z' @@ -11576,6 +11583,7 @@ webhooks: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: 5000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-09T09:00:00Z' @@ -11652,6 +11660,7 @@ webhooks: - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 maxSpendPerTransaction: null maxSpendPerDay: null + maxTransactionsPerDay: null currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:30:00Z' @@ -12298,6 +12307,15 @@ components: - type: 'null' description: Platform-level cap on cumulative new spend during one UTC calendar day for every card whose authorization decisions are made by Grid. The value is interpreted in the smallest unit of each card's currency. Grid enforces the lower of this cap and the card's configured `maxSpendPerDay`; null means no platform-level daily cap. The window resets at 00:00 UTC. Refunds, reversals, and authorization expiries do not restore capacity during the day. The cap applies to existing cards and cards issued later. Provider-decided card programs are unaffected. example: 50000 + maxTransactionsPerDay: + anyOf: + - type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + - type: 'null' + description: Platform-level cap on the number of transactions each card may authorize during one UTC calendar day, for every card whose authorization decisions are made by Grid. Grid enforces the lower of this cap and the card's configured `maxTransactionsPerDay`; null means no platform-level daily transaction cap. The window resets at 00:00 UTC. Each approved authorization counts once; refunds, reversals, and authorization expiries do not restore capacity during the day. The cap applies to existing cards and cards issued later. Provider-decided card programs are unaffected. + example: 50 FeeType: type: string enum: @@ -12592,7 +12610,7 @@ components: to subsequent delivery attempts. cardConfigs: $ref: '#/components/schemas/CardConfig' - description: Update platform-level card settings. Fields omitted from the nested object are left unchanged. For either spending limit, supply null to clear the platform cap or a positive integer to set it. Omit this field at the top level to leave all card settings unchanged. + description: Update platform-level card settings. Fields omitted from the nested object are left unchanged. For any card limit, supply null to clear the platform cap or a positive integer to set it. Omit this field at the top level to leave all card settings unchanged. feeConfigs: type: array items: @@ -25975,6 +25993,7 @@ components: - fundingSources - maxSpendPerTransaction - maxSpendPerDay + - maxTransactionsPerDay - createdAt - updatedAt properties: @@ -26042,6 +26061,15 @@ components: - type: 'null' description: Card-specific cap on cumulative new spend during one UTC calendar day, in the smallest unit of the card's `currency`. The window resets at 00:00 UTC. Null means the card has no card-specific daily cap. When the platform config also supplies `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the two values without replacing this configured value. Refunds, reversals, and authorization expiries do not restore capacity during the day. Spend exactly equal to the effective limit is allowed. example: 25000 + maxTransactionsPerDay: + anyOf: + - type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + - type: 'null' + description: Card-specific cap on the number of transactions the card may authorize during one UTC calendar day. The window resets at 00:00 UTC. Null means the card has no card-specific daily transaction cap. When the platform config also supplies `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the two values without replacing this configured value. Each approved authorization counts once for the day it was authorized; refunds, reversals, and authorization expiries do not restore capacity during the day. A transaction that brings the day's count exactly to the effective limit is allowed. + example: 20 currency: type: string description: Currency the card transacts in (ISO 4217 for fiat, tickers for crypto). Derived from the funding sources at issue time — all funding sources bound to a card must be denominated in the same card-eligible currency. @@ -26132,9 +26160,16 @@ components: maximum: 9007199254740991 description: Optional card-specific cap on cumulative new spend during one UTC calendar day, in the smallest unit of the card currency derived from its funding sources. Omit this field for no card-specific daily cap. When the platform config also supplies `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the two values. The window resets at 00:00 UTC, and refunds, reversals, and authorization expiries do not restore capacity during the day. Supported only for card programs whose authorization decisions are made by Grid. Spend exactly equal to the effective limit is allowed. example: 25000 + maxTransactionsPerDay: + type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + description: Optional card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Omit this field for no card-specific daily transaction cap. When the platform config also supplies `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the two values. The window resets at 00:00 UTC. Each approved authorization counts once; refunds, reversals, and authorization expiries do not restore capacity during the day. Supported only for card programs whose authorization decisions are made by Grid. + example: 20 CardUpdateRequest: type: object - description: Update request for `PATCH /cards/{id}`. At least one of `state`, `fundingSources`, `maxSpendPerTransaction`, or `maxSpendPerDay` must be supplied. `state` 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 `fundingSources`, `maxSpendPerTransaction`, or `maxSpendPerDay`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. + description: Update request for `PATCH /cards/{id}`. At least one of `state`, `fundingSources`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay` must be supplied. `state` 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 `fundingSources`, `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. properties: state: type: string @@ -26171,6 +26206,15 @@ components: - type: 'null' description: 'Replacement card-specific UTC-calendar-day cap, in the smallest unit of the card''s currency. Omit this field to leave the current cap unchanged, supply null to clear it, or supply a positive integer to set it. When the platform config also supplies `cardConfigs.maxSpendPerDay`, Grid enforces the lower of the two values. Refunds, reversals, and authorization expiries do not restore capacity during the day. Supported only for card programs whose authorization decisions are made by Grid. Cannot be supplied alongside `state: CLOSED`.' example: 25000 + maxTransactionsPerDay: + anyOf: + - type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + - type: 'null' + description: 'Replacement card-specific cap on the number of transactions the card may authorize during one UTC calendar day. Omit this field to leave the current cap unchanged, supply null to clear it, or supply a positive integer to set it. When the platform config also supplies `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the two values. Refunds, reversals, and authorization expiries do not restore capacity during the day. Supported only for card programs whose authorization decisions are made by Grid. Cannot be supplied alongside `state: CLOSED`.' + example: 20 CardRevealResponse: type: object required: diff --git a/openapi/components/schemas/cards/Card.yaml b/openapi/components/schemas/cards/Card.yaml index f0628123c..efeb9b832 100644 --- a/openapi/components/schemas/cards/Card.yaml +++ b/openapi/components/schemas/cards/Card.yaml @@ -7,6 +7,7 @@ required: - fundingSources - maxSpendPerTransaction - maxSpendPerDay + - maxTransactionsPerDay - createdAt - updatedAt properties: @@ -93,6 +94,24 @@ properties: Refunds, reversals, and authorization expiries do not restore capacity during the day. Spend exactly equal to the effective limit is allowed. example: 25000 + maxTransactionsPerDay: + anyOf: + - type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + - type: 'null' + description: >- + Card-specific cap on the number of transactions the card may authorize + during one UTC calendar day. The window resets at 00:00 UTC. Null means + the card has no card-specific daily transaction cap. When the platform + config also supplies `cardConfigs.maxTransactionsPerDay`, Grid enforces + the lower of the two values without replacing this configured value. + Each approved authorization counts once for the day it was authorized; + refunds, reversals, and authorization expiries do not restore capacity + during the day. A transaction that brings the day's count exactly to the + effective limit is allowed. + example: 20 currency: type: string description: >- diff --git a/openapi/components/schemas/cards/CardCreateRequest.yaml b/openapi/components/schemas/cards/CardCreateRequest.yaml index a1aaf911d..faa05357d 100644 --- a/openapi/components/schemas/cards/CardCreateRequest.yaml +++ b/openapi/components/schemas/cards/CardCreateRequest.yaml @@ -72,3 +72,18 @@ properties: day. Supported only for card programs whose authorization decisions are made by Grid. Spend exactly equal to the effective limit is allowed. example: 25000 + maxTransactionsPerDay: + type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + description: >- + Optional card-specific cap on the number of transactions the card may + authorize during one UTC calendar day. Omit this field for no + card-specific daily transaction cap. When the platform config also + supplies `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of + the two values. The window resets at 00:00 UTC. Each approved + authorization counts once; refunds, reversals, and authorization + expiries do not restore capacity during the day. Supported only for + card programs whose authorization decisions are made by Grid. + example: 20 diff --git a/openapi/components/schemas/cards/CardUpdateRequest.yaml b/openapi/components/schemas/cards/CardUpdateRequest.yaml index ca4bc9698..1ec87d48e 100644 --- a/openapi/components/schemas/cards/CardUpdateRequest.yaml +++ b/openapi/components/schemas/cards/CardUpdateRequest.yaml @@ -1,13 +1,13 @@ type: object description: >- Update request for `PATCH /cards/{id}`. At least one of `state`, - `fundingSources`, `maxSpendPerTransaction`, or `maxSpendPerDay` must be - supplied. `state` + `fundingSources`, `maxSpendPerTransaction`, `maxSpendPerDay`, or + `maxTransactionsPerDay` must be supplied. `state` 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 `fundingSources`, `maxSpendPerTransaction`, or - `maxSpendPerDay`. + cannot be combined with `fundingSources`, `maxSpendPerTransaction`, + `maxSpendPerDay`, or `maxTransactionsPerDay`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in. @@ -72,3 +72,21 @@ properties: programs whose authorization decisions are made by Grid. Cannot be supplied alongside `state: CLOSED`. example: 25000 + maxTransactionsPerDay: + anyOf: + - type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + - type: 'null' + description: >- + Replacement card-specific cap on the number of transactions the card may + authorize during one UTC calendar day. Omit this field to leave the + current cap unchanged, supply null to clear it, or supply a positive + integer to set it. When the platform config also supplies + `cardConfigs.maxTransactionsPerDay`, Grid enforces the lower of the two + values. Refunds, reversals, and authorization expiries do not restore + capacity during the day. Supported only for card programs whose + authorization decisions are made by Grid. Cannot be supplied alongside + `state: CLOSED`. + example: 20 diff --git a/openapi/components/schemas/config/CardConfig.yaml b/openapi/components/schemas/config/CardConfig.yaml index 6baf4e033..b3952544d 100644 --- a/openapi/components/schemas/config/CardConfig.yaml +++ b/openapi/components/schemas/config/CardConfig.yaml @@ -33,3 +33,21 @@ properties: restore capacity during the day. The cap applies to existing cards and cards issued later. Provider-decided card programs are unaffected. example: 50000 + maxTransactionsPerDay: + anyOf: + - type: integer + format: int32 + minimum: 1 + maximum: 2147483647 + - type: 'null' + description: >- + Platform-level cap on the number of transactions each card may authorize + during one UTC calendar day, for every card whose authorization + decisions are made by Grid. Grid enforces the lower of this cap and the + card's configured `maxTransactionsPerDay`; null means no platform-level + daily transaction cap. The window resets at 00:00 UTC. Each approved + authorization counts once; refunds, reversals, and authorization + expiries do not restore capacity during the day. The cap applies to + existing cards and cards issued later. Provider-decided card programs + are unaffected. + example: 50 diff --git a/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml b/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml index 5f1b3d8fc..a38b06a2b 100644 --- a/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml +++ b/openapi/components/schemas/config/PlatformConfigUpdateRequest.yaml @@ -27,7 +27,7 @@ properties: $ref: ./CardConfig.yaml description: >- Update platform-level card settings. Fields omitted from the nested - object are left unchanged. For either spending limit, supply null to + object are left unchanged. For any card limit, supply null to clear the platform cap or a positive integer to set it. Omit this field at the top level to leave all card settings unchanged. feeConfigs: diff --git a/openapi/paths/cards/cards.yaml b/openapi/paths/cards/cards.yaml index f61a2a9d7..743a127ca 100644 --- a/openapi/paths/cards/cards.yaml +++ b/openapi/paths/cards/cards.yaml @@ -7,13 +7,15 @@ post: with `CARDHOLDER_KYC_NOT_APPROVED`. - Optional `maxSpendPerTransaction` and `maxSpendPerDay` values set the - card-specific caps on one transaction and one UTC calendar day. The limits + Optional `maxSpendPerTransaction`, `maxSpendPerDay`, and + `maxTransactionsPerDay` values set the card-specific caps on one + transaction, on spend during one UTC calendar day, and on the number of + transactions during one UTC calendar day. The limits are enforced by Grid for card programs where Grid makes the authorization decision, whether the card is funded by an Embedded Wallet account or custodial fiat. If the platform config sets the corresponding `cardConfigs` value, Grid enforces the lower of the card and platform caps. - All values use the smallest unit of the card's currency. + Amounts use the smallest unit of the card's currency. If any funding source is an Embedded Wallet internal account, the @@ -50,6 +52,7 @@ post: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: 5000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 responses: '201': description: >- diff --git a/openapi/paths/cards/cards_{id}.yaml b/openapi/paths/cards/cards_{id}.yaml index 7b64655bd..61835767b 100644 --- a/openapi/paths/cards/cards_{id}.yaml +++ b/openapi/paths/cards/cards_{id}.yaml @@ -55,8 +55,8 @@ patch: summary: Update a card description: > Update a card's `state`, bound `fundingSources`, and / or - `maxSpendPerTransaction`, or `maxSpendPerDay`. At least one field must be - supplied. + `maxSpendPerTransaction`, `maxSpendPerDay`, or `maxTransactionsPerDay`. + At least one field must be supplied. - `state` transitions are limited to `ACTIVE ⇄ FROZEN` and @@ -86,6 +86,14 @@ patch: expiries do not restore capacity during the day. `maxSpendPerDay` cannot be supplied alongside `state: CLOSED`. + - `maxTransactionsPerDay`, when supplied, replaces the card-specific cap on + the number of transactions the card may authorize during one UTC calendar + day. Supply a positive integer to set it or null to clear it. If the + platform config sets `cardConfigs.maxTransactionsPerDay`, Grid enforces the + lower of the card and platform values. Refunds, reversals, and authorization + expiries do not restore capacity during the day. `maxTransactionsPerDay` + cannot be supplied alongside `state: CLOSED`. + This endpoint is authenticated by the platform credential alone and returns `200` directly. It deliberately does not use Grid's 202 → @@ -153,15 +161,17 @@ patch: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 setSpendingLimit: - summary: Set the card's per-transaction spending limit + summary: Set the card's spending and transaction limits value: maxSpendPerTransaction: 10000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 clearSpendingLimit: summary: Remove the card's spending limits value: maxSpendPerTransaction: null maxSpendPerDay: null + maxTransactionsPerDay: null freezeAndUpdateSources: summary: Freeze the card and replace its funding sources in one call value: diff --git a/openapi/paths/platform/config.yaml b/openapi/paths/platform/config.yaml index 60394a7b5..efc2d71f9 100644 --- a/openapi/paths/platform/config.yaml +++ b/openapi/paths/platform/config.yaml @@ -29,7 +29,9 @@ patch: summary: Update platform configuration description: >- Update platform configuration settings. `cardConfigs` can establish - platform-level per-transaction and UTC-calendar-day card caps. Grid + platform-level caps on a single card transaction, on spend during one UTC + calendar day, and on the number of transactions during one UTC calendar + day. Grid enforces the lower of each platform cap and its corresponding card-specific value without replacing the card-specific value. Daily limits reset at 00:00 UTC. @@ -82,6 +84,7 @@ patch: cardConfigs: maxSpendPerTransaction: 10000 maxSpendPerDay: 50000 + maxTransactionsPerDay: 50 responses: '200': description: Configuration updated successfully diff --git a/openapi/webhooks/card-funding-source-change.yaml b/openapi/webhooks/card-funding-source-change.yaml index 32fd780fd..1002317cf 100644 --- a/openapi/webhooks/card-funding-source-change.yaml +++ b/openapi/webhooks/card-funding-source-change.yaml @@ -62,6 +62,7 @@ post: - InternalAccount:019542f5-b3e7-1d02-0000-000000000003 maxSpendPerTransaction: null maxSpendPerDay: null + maxTransactionsPerDay: null currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:30:00Z' diff --git a/openapi/webhooks/card-state-change.yaml b/openapi/webhooks/card-state-change.yaml index 2009f2fc4..001254d2e 100644 --- a/openapi/webhooks/card-state-change.yaml +++ b/openapi/webhooks/card-state-change.yaml @@ -62,6 +62,7 @@ post: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: 5000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 currency: USD processorRef: card_b81c2a4f issuerRef: lead_card_7a1b9c3d @@ -83,6 +84,7 @@ post: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: null maxSpendPerDay: null + maxTransactionsPerDay: null currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-08T14:12:00Z' @@ -106,6 +108,7 @@ post: - InternalAccount:019542f5-b3e7-1d02-0000-000000000002 maxSpendPerTransaction: 5000 maxSpendPerDay: 25000 + maxTransactionsPerDay: 20 currency: USD createdAt: '2026-05-08T14:10:00Z' updatedAt: '2026-05-09T09:00:00Z'