Skip to content

feat(openapi): add rail + settlement-timing fields to OutgoingTransaction#633

Merged
mohamedwane merged 2 commits into
mainfrom
claude/outgoing-transaction-schema-fields-tbnqe3
Jun 29, 2026
Merged

feat(openapi): add rail + settlement-timing fields to OutgoingTransaction#633
mohamedwane merged 2 commits into
mainfrom
claude/outgoing-transaction-schema-fields-tbnqe3

Conversation

@mohamedwane

Copy link
Copy Markdown
Contributor

Summary

Adds four optional, nullable fields to the OutgoingTransaction schema so the rail and settlement-timing data that sparkcore already emits on outgoing_payment.* webhooks (today as a raw-JSON post-process, webdev #29090) becomes first-class in the typed API model. This makes the fields show up in generated clients/SDKs and the public webhook docs. The change is purely additive and backward-compatible.

Fields added

Field Type Nullable encoding
paymentRail PaymentRail enum ref anyOf: [$ref, {type: 'null'}]
railSelectionMode new RailSelectionMode enum (AUTO/MANUAL) anyOf: [$ref, {type: 'null'}]
expectedSettlementAt string, format: date-time type: [string, 'null']
settlementTimelineSeconds integer type: [integer, 'null']

All four are not added to required and are nullable — they are null for instant rails, AUTO mode before a rail is resolved, and non-direct-destination transactions.

Changes

  • openapi/components/schemas/transactions/OutgoingTransaction.yaml — the four new properties.
  • openapi/components/schemas/common/RailSelectionMode.yaml — new named enum schema (placed alongside PaymentRail.yaml, matching the repo's one-enum-per-file convention).
  • openapi/openapi.yaml — version bump 2026-06-252026-06-29 (date-based release convention).
  • openapi.yaml + mintlify/openapi.yaml — regenerated bundles (npm run build:openapi).
  • mintlify/ramps/platform-tools/webhooks.mdx — surfaced the new fields in the OUTGOING_PAYMENT webhook payload example. Other webhook docs render the model via $ref and pick up the fields automatically.

Conventions followed

  • Nullable idioms match existing repo usage: anyOf + type: 'null' for refs (as in Card.yaml), and type: [..., 'null'] lists for scalars (as in AgentSpendingLimits.yaml).
  • The paymentRail value is the grid-api PaymentRail name (e.g. FASTER_PAYMENTS), matching the enum exactly — no internal aliases.

Verification

  • redocly validation: ✅ valid (warnings are all pre-existing and unrelated).
  • spectral lint --fail-severity=error: ✅ exit 0, no errors.
  • Confirmed required is unchanged (still only type, sentAmount, source) and the regenerated OutgoingTransaction model includes all four fields as optional/nullable. The only non-additive line across the bundles is the version bump.

Follow-up

Downstream webdev change is tracked as AT-5666: once a new grid-api package version is published, bump the pinned version, regenerate the client, and move sparkcore's webhook enrichment out of the raw-dict post-process into the typed model.

🤖 Generated with Claude Code


Generated by Claude Code

…tion

Add four optional, nullable fields to the OutgoingTransaction schema so the
rail and settlement-timing data that sparkcore already emits on
outgoing_payment.* webhooks becomes first-class in the typed API model:

- paymentRail: nullable PaymentRail enum, the rail used to settle the txn
- railSelectionMode: nullable AUTO/MANUAL enum (new RailSelectionMode schema)
- expectedSettlementAt: nullable date-time of expected settlement
- settlementTimelineSeconds: nullable integer seconds to settlement

All four are additive and backward-compatible: not in `required`, and
nullable for instant rails, AUTO mode before a rail is resolved, and
non-direct-destination transactions.

Bump the spec version to 2026-06-29, rebundle openapi.yaml +
mintlify/openapi.yaml, and surface the new fields in the ramps
outgoing-payment webhook payload example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FTtfvC6Qcsh3M62jVaCT1Y
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
grid-flow-builder Ignored Ignored Preview Jun 29, 2026 10:29pm
grid-wallet-demo Ignored Ignored Preview Jun 29, 2026 10:29pm

Request Review

@mintlify

mintlify Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
Grid 🟢 Ready View Preview Jun 29, 2026, 10:10 PM

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

✱ Stainless preview builds for grid

This PR will update the grid SDKs with the following commit messages.

cli

chore(internal): regenerate SDK with no functional changes

csharp

feat(api): add payment rail and settlement timing fields to OutgoingTransaction

go

feat(api): add payment rail and settlement timeline fields to OutgoingTransaction

kotlin

feat(api): add settlement/rail fields and enums to OutgoingTransaction

openapi

feat(api): add payment rail and settlement fields to OutgoingTransaction

php

feat(api): add payment rail and settlement timeline fields to OutgoingTransaction

python

feat(api): add settlement/rail fields to OutgoingTransaction

ruby

feat(api): add payment rail and settlement timing fields to outgoing_transaction

typescript

feat(api): add settlement timing and rail fields to outgoing transactions
⚠️ grid-openapi studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️

grid-ruby studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ✅lint ✅test ✅

⚠️ grid-go studio · code

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

go get github.com/stainless-sdks/grid-go@4a7d8052a2815a7d1e9d6c37e924816a892f3334
grid-typescript studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ✅lint ✅test ✅

npm install https://pkg.stainless.com/s/grid-typescript/ee7768062646ee28bbe364d07a2ec7c0826445c8/dist.tar.gz
⚠️ grid-kotlin studio · code

Your SDK build had a failure in the test CI job, which is a regression from the base state.
generate ⚠️build ✅lint ✅test ❗

⚠️ grid-python studio · code

Your SDK build had a failure in the lint CI job, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

pip install https://pkg.stainless.com/s/grid-python/9123b057ab2d2cdc91f676b82be8e6a853baa794/grid-0.0.1-py3-none-any.whl
⚠️ grid-csharp studio · code

Your SDK build had a failure in the build CI job, which is a regression from the base state.
generate ⚠️build ❗lint ✅test ❗

grid-php studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅lint ✅test ✅

⚠️ grid-cli studio · code

Your SDK build had a failure in the test CI job, which is a regression from the base state.
generate ⚠️build ⏭️lint ⏭️test ❗


This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-06-29 22:42:52 UTC

@greptile-apps

greptile-apps Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR promotes four settlement-timing and rail-selection fields (paymentRail, railSelectionMode, expectedSettlementAt, settlementTimelineSeconds) from a raw-JSON post-process into the typed OutgoingTransaction schema, alongside a new RailSelectionMode enum. All changes are purely additive — fields are optional and nullable, required is untouched, and the bundles are regenerated consistently.

  • Schema additions (OutgoingTransaction.yaml, RailSelectionMode.yaml): nullable idioms follow existing conventions (anyOf+null for enum refs, type: [x, 'null'] for scalars); the new RailSelectionMode file matches the structure of PaymentRail.yaml.
  • Generated bundles (openapi.yaml, mintlify/openapi.yaml): diffs are identical to the source changes, confirming make build was run correctly.
  • Webhook docs (webhooks.mdx): the OUTGOING_PAYMENT example is updated with plausible ACH values; no frontmatter or structural changes needed.

Confidence Score: 4/5

Safe to merge — purely additive schema extension with no breaking changes to existing fields or required properties.

The change is clean and backward-compatible. Two field descriptions in OutgoingTransaction.yaml use phrasing that could mislead consumers: paymentRail implies it will eventually become non-null for every transaction, and expectedSettlementAt lists instant rails as an example of 'not yet known' when instant settlement is actually immediately known. Neither causes a runtime defect, but both could cause incorrect client behavior if consumers poll or branch on these assumptions.

openapi/components/schemas/transactions/OutgoingTransaction.yaml — the two description suggestions on paymentRail and expectedSettlementAt are worth a quick look before publishing the new API version.

Important Files Changed

Filename Overview
openapi/components/schemas/transactions/OutgoingTransaction.yaml Adds four optional nullable fields (paymentRail, railSelectionMode, expectedSettlementAt, settlementTimelineSeconds); structure and nullable idioms follow existing conventions, but two field descriptions have minor accuracy issues
openapi/components/schemas/common/RailSelectionMode.yaml New two-value enum (AUTO/MANUAL) matching the structure of the adjacent PaymentRail.yaml; clean and consistent
openapi/openapi.yaml Source root spec with version bump only (2026-06-25 → 2026-06-29); RailSelectionMode is auto-discovered by bundler via $ref chain
openapi.yaml Generated bundle; diff matches source changes exactly — version bump plus RailSelectionMode schema and four new OutgoingTransaction fields
mintlify/openapi.yaml Second generated bundle (Mintlify copy); identical changes to openapi.yaml, consistent
mintlify/ramps/platform-tools/webhooks.mdx Adds the four new fields to the OUTGOING_PAYMENT webhook example; JSON syntax is valid and example values are plausible for an ACH transaction

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Platform
    participant GridAPI
    participant SparkCore
    participant Beneficiary

    Platform->>GridAPI: "POST /quotes/{id}/execute"
    GridAPI->>SparkCore: Initiate OutgoingTransaction
    SparkCore-->>GridAPI: "Transaction created (paymentRail=null, railSelectionMode=AUTO)"
    GridAPI-->>Platform: OUTGOING_PAYMENT.PENDING webhook

    SparkCore->>SparkCore: Resolve payment rail
    SparkCore-->>GridAPI: "Rail resolved (paymentRail=ACH, expectedSettlementAt set)"
    GridAPI-->>Platform: OUTGOING_PAYMENT.PROCESSING webhook (paymentRail, railSelectionMode, expectedSettlementAt, settlementTimelineSeconds)

    SparkCore->>Beneficiary: Initiate ACH transfer
    SparkCore-->>GridAPI: Transaction settled on Grid side
    GridAPI-->>Platform: "OUTGOING_PAYMENT.COMPLETED webhook (settledAt=now, expectedSettlementAt=T+1d)"

    Beneficiary-->>Beneficiary: ACH clears at T+1d (expectedSettlementAt)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Platform
    participant GridAPI
    participant SparkCore
    participant Beneficiary

    Platform->>GridAPI: "POST /quotes/{id}/execute"
    GridAPI->>SparkCore: Initiate OutgoingTransaction
    SparkCore-->>GridAPI: "Transaction created (paymentRail=null, railSelectionMode=AUTO)"
    GridAPI-->>Platform: OUTGOING_PAYMENT.PENDING webhook

    SparkCore->>SparkCore: Resolve payment rail
    SparkCore-->>GridAPI: "Rail resolved (paymentRail=ACH, expectedSettlementAt set)"
    GridAPI-->>Platform: OUTGOING_PAYMENT.PROCESSING webhook (paymentRail, railSelectionMode, expectedSettlementAt, settlementTimelineSeconds)

    SparkCore->>Beneficiary: Initiate ACH transfer
    SparkCore-->>GridAPI: Transaction settled on Grid side
    GridAPI-->>Platform: "OUTGOING_PAYMENT.COMPLETED webhook (settledAt=now, expectedSettlementAt=T+1d)"

    Beneficiary-->>Beneficiary: ACH clears at T+1d (expectedSettlementAt)
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
openapi/components/schemas/transactions/OutgoingTransaction.yaml:76-83
The `paymentRail` description says "Null until a rail is resolved," but the phrase "until" implies the field will eventually become non-null for every transaction. Per the PR description, it is *permanently* null for instant rails and non-direct-destination transactions. Consumers relying on polling for a non-null value would wait forever in those cases.

```suggestion
      paymentRail:
        anyOf:
          - $ref: ../common/PaymentRail.yaml
          - type: 'null'
        description: >-
          The payment rail used to settle this transaction (e.g. ACH, WIRE,
          NEFT, FASTER_PAYMENTS). Uses the same values as the PaymentRail sent
          on quote requests. Null when no external rail is used (e.g. instant
          or intra-network transfers) or before a rail is resolved.
```

### Issue 2 of 2
openapi/components/schemas/transactions/OutgoingTransaction.yaml:92-99
The parenthetical "e.g. instant rails" is listed as an example of when settlement time is *not yet known*, but instant rails settle immediately — so the time is actually known (it's now). This makes the description self-contradictory. A developer reading it might conclude instant-rail transactions can eventually gain a non-null `expectedSettlementAt`, which is incorrect.

```suggestion
      expectedSettlementAt:
        type:
          - string
          - 'null'
        format: date-time
        description: >-
          Expected settlement time at the beneficiary. Null for instant rails
          (settlement is immediate) and before a rail with deferred settlement
          is resolved.
```

Reviews (1): Last reviewed commit: "feat(openapi): add rail + settlement-tim..." | Re-trigger Greptile

Comment on lines +76 to +83
paymentRail:
anyOf:
- $ref: ../common/PaymentRail.yaml
- type: 'null'
description: >-
The payment rail used to settle this transaction (e.g. ACH, WIRE,
NEFT, FASTER_PAYMENTS). Uses the same values as the PaymentRail sent
on quote requests. Null until a rail is resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The paymentRail description says "Null until a rail is resolved," but the phrase "until" implies the field will eventually become non-null for every transaction. Per the PR description, it is permanently null for instant rails and non-direct-destination transactions. Consumers relying on polling for a non-null value would wait forever in those cases.

Suggested change
paymentRail:
anyOf:
- $ref: ../common/PaymentRail.yaml
- type: 'null'
description: >-
The payment rail used to settle this transaction (e.g. ACH, WIRE,
NEFT, FASTER_PAYMENTS). Uses the same values as the PaymentRail sent
on quote requests. Null until a rail is resolved.
paymentRail:
anyOf:
- $ref: ../common/PaymentRail.yaml
- type: 'null'
description: >-
The payment rail used to settle this transaction (e.g. ACH, WIRE,
NEFT, FASTER_PAYMENTS). Uses the same values as the PaymentRail sent
on quote requests. Null when no external rail is used (e.g. instant
or intra-network transfers) or before a rail is resolved.
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/transactions/OutgoingTransaction.yaml
Line: 76-83

Comment:
The `paymentRail` description says "Null until a rail is resolved," but the phrase "until" implies the field will eventually become non-null for every transaction. Per the PR description, it is *permanently* null for instant rails and non-direct-destination transactions. Consumers relying on polling for a non-null value would wait forever in those cases.

```suggestion
      paymentRail:
        anyOf:
          - $ref: ../common/PaymentRail.yaml
          - type: 'null'
        description: >-
          The payment rail used to settle this transaction (e.g. ACH, WIRE,
          NEFT, FASTER_PAYMENTS). Uses the same values as the PaymentRail sent
          on quote requests. Null when no external rail is used (e.g. instant
          or intra-network transfers) or before a rail is resolved.
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +92 to +99
expectedSettlementAt:
type:
- string
- 'null'
format: date-time
description: >-
Expected settlement time at the beneficiary. Null when not yet known
(e.g. instant rails, or before a rail is resolved).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The parenthetical "e.g. instant rails" is listed as an example of when settlement time is not yet known, but instant rails settle immediately — so the time is actually known (it's now). This makes the description self-contradictory. A developer reading it might conclude instant-rail transactions can eventually gain a non-null expectedSettlementAt, which is incorrect.

Suggested change
expectedSettlementAt:
type:
- string
- 'null'
format: date-time
description: >-
Expected settlement time at the beneficiary. Null when not yet known
(e.g. instant rails, or before a rail is resolved).
expectedSettlementAt:
type:
- string
- 'null'
format: date-time
description: >-
Expected settlement time at the beneficiary. Null for instant rails
(settlement is immediate) and before a rail with deferred settlement
is resolved.
Prompt To Fix With AI
This is a comment left during a code review.
Path: openapi/components/schemas/transactions/OutgoingTransaction.yaml
Line: 92-99

Comment:
The parenthetical "e.g. instant rails" is listed as an example of when settlement time is *not yet known*, but instant rails settle immediately — so the time is actually known (it's now). This makes the description self-contradictory. A developer reading it might conclude instant-rail transactions can eventually gain a non-null `expectedSettlementAt`, which is incorrect.

```suggestion
      expectedSettlementAt:
        type:
          - string
          - 'null'
        format: date-time
        description: >-
          Expected settlement time at the beneficiary. Null for instant rails
          (settlement is immediate) and before a rail with deferred settlement
          is resolved.
```

How can I resolve this? If you propose a fix, please make it concise.

Address review feedback on OutgoingTransaction field descriptions:

- paymentRail: replace "Null until a rail is resolved" (which implied the
  field always eventually becomes non-null) with an accurate description of
  the permanent-null cases — instant/intra-network transfers and
  non-direct-destination transactions — as well as the pre-resolution case.
- expectedSettlementAt: instant rails settle immediately (time is known, not
  "not yet known"); reword so it's null for instant rails and before a
  deferred-settlement rail is resolved.

Wording only; no schema, type, or required-field changes. Rebundled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FTtfvC6Qcsh3M62jVaCT1Y
@mohamedwane mohamedwane merged commit 8888091 into main Jun 29, 2026
9 checks passed
@mohamedwane mohamedwane deleted the claude/outgoing-transaction-schema-fields-tbnqe3 branch June 29, 2026 22:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants