Skip to content

feat: document SCA_SESSION_REQUIRED (409) and ACCOUNT_LOCKED (423) on quote authorize - #759

Closed
ls-bolt[bot] wants to merge 1 commit into
mainfrom
07-29-error409-sca-session-required
Closed

feat: document SCA_SESSION_REQUIRED (409) and ACCOUNT_LOCKED (423) on quote authorize#759
ls-bolt[bot] wants to merge 1 commit into
mainfrom
07-29-error409-sca-session-required

Conversation

@ls-bolt

@ls-bolt ls-bolt Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

This PR has been claimed. The active PR is now #761.

Summary

Two error codes the API already returns were missing from the spec, so a generated client rejected the response instead of surfacing the code.

  • SCA_SESSION_REQUIRED added to the Error409 code enum. The API returns this with HTTP 409 when the customer's Strong Customer Authentication login session is missing or expired. Because it was absent from the enum, a generated client raised a validation error while deserializing the response — so an integrator got an opaque failure rather than a code they could act on. It is deliberately distinct from a 401, which means the platform's API credentials were rejected: a 401 is not customer-recoverable, whereas SCA_SESSION_REQUIRED means "re-authenticate the customer, then retry".

  • POST /quotes/{quoteId}/authorize now documents 409 SCA_SESSION_REQUIRED and 423 ACCOUNT_LOCKED. This is the endpoint that surfaces both. Its 409 description previously covered only the "SCA not required / no pending challenge" cases, and it declared no 423 at all — even though the endpoint returns ACCOUNT_LOCKED when the customer's login is temporarily locked after too many failed authorization attempts. Error423 already existed and needed no change; this just references it.

Both changes are additive and non-breaking, so info.version stays 2025-10-13.

Known limitation this does not fix

While verifying the above against the running implementation, I found that the error envelope does not match what the ErrorNNN schemas declare. The API sends:

{"code": "SCA_SESSION_REQUIRED", "reason": "..."}

but every ErrorNNN schema requires status, code, and message. So a generated Python client rejects a real error body on status (int_type) and message (string_type) regardless of this change:

('status',)  | int_type    | Input should be a valid integer
('message',) | string_type | Input should be a valid string

This is pre-existing and applies to every documented error response across the spec, not just the two codes here — it is not introduced by this PR, and this PR is still a strict improvement (the enum is the published contract, and integrators reading the raw JSON body now have the code documented). But it does mean the typed-model path is not yet usable for error handling, so reconciling the envelope is worth its own change. Two options, both spec-wide decisions I did not want to make unilaterally: make the server emit status/message, or change the schemas to match reality (reason, and status not required).

Relatedly, Error423's own description points readers at details.lockedUntil / details.failedAttempts, but no error path populates them — those fields appear only on the 200 body of POST /sca/record-event. I left that description alone rather than expand this PR's scope, and worded the new 423 on the authorize endpoint to promise only what is actually sent.

Test plan

  • make lint exits 0 (Redocly + Spectral, --fail-severity=error); the 630 remaining findings are pre-existing warnings/infos, 0 errors.
  • make build rebundled openapi.yaml and mintlify/openapi.yaml; re-running it is a no-op, so the committed bundles byte-match the build output.
  • Confirmed against the running implementation that both codes are genuinely emitted on this endpoint, each covered by a test asserting the status/code pair at the HTTP boundary — the 423 case is newly covered by a test added alongside this change. The 423 body was captured directly to confirm the wording matches what is sent.
  • Diff is 4 files: 2 spec sources + the 2 generated bundles.

Public

Documents two error codes the Grid API already returns: SCA_SESSION_REQUIRED (409) is added to the shared Error409 enum, and POST /quotes/{quoteId}/authorize now documents both it and 423 ACCOUNT_LOCKED.

@ls-bolt ls-bolt Bot added the bolt label Jul 29, 2026
@vercel

vercel Bot commented Jul 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 Jul 29, 2026 5:46am
grid-wallet-demo Ignored Ignored Preview Jul 29, 2026 5:46am

Request Review

akanter commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

github-actions Bot commented Jul 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

go

chore(internal): regenerate SDK with no functional changes

kotlin

chore(internal): regenerate SDK with no functional changes

openapi

feat(api): add 423 error response and SCA_SESSION_REQUIRED enum to authorization

php

chore(internal): regenerate SDK with no functional changes

python

chore(internal): regenerate SDK with no functional changes

ruby

chore(internal): regenerate SDK with no functional changes

typescript

chore(internal): regenerate SDK with no functional changes

Edit this comment to update them. They will appear in their respective SDK's changelogs.

grid-typescript studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ⏭️ (prev: build ✅) → lint ⏭️ (prev: lint ❗) → test ✅

grid-openapi studio · code · diff

Your SDK build had at least one "warning" diagnostic, but this did not represent a regression.
generate ⚠️

grid-ruby studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ⏭️ (prev: build ✅) → lint ✅test ✅

grid-go studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ⏭️ (prev: build ✅) → lint ❗test ❗

go get github.com/stainless-sdks/grid-go@2edf549bbd12102abaf2cce059eee5fdccafa4b3
grid-kotlin studio · code · diff

Your SDK build had at least one "warning" diagnostic, but this did not represent a regression.
generate ⚠️build ⏭️ (prev: build ✅) → lint ⏭️ (prev: lint ✅) → test ❗

grid-python studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ⏭️ (prev: build ✅) → lint ⏭️ (prev: lint ❗) → test ❗

grid-php studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅lint ✅test ✅

grid-cli studio · code · diff

Your SDK build had at least one "warning" diagnostic, but this did not represent a regression.
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-07-29 05:50:07 UTC

@mintlify

mintlify Bot commented Jul 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 Jul 29, 2026, 5:41 AM

@ls-bolt
ls-bolt Bot force-pushed the 07-29-error409-sca-session-required branch from ba24c0c to 065e823 Compare July 29, 2026 05:46
@jklein24 jklein24 closed this Jul 29, 2026
jklein24 added a commit that referenced this pull request Jul 30, 2026
… quote authorize (#761)

## Summary

Two error codes the API already returns were missing from the spec, so a
generated client rejected the response instead of surfacing the code.

- **`SCA_SESSION_REQUIRED` added to the `Error409` `code` enum.** The
API returns this with HTTP 409 when the customer's Strong Customer
Authentication login session is missing or expired. Because it was
absent from the enum, a generated client raised a validation error while
deserializing the response — so an integrator got an opaque failure
rather than a code they could act on. It is deliberately distinct from a
`401`, which means the *platform's* API credentials were rejected: a
`401` is not customer-recoverable, whereas `SCA_SESSION_REQUIRED` means
"re-authenticate the customer, then retry".

- **`POST /quotes/{quoteId}/authorize` now documents `409
SCA_SESSION_REQUIRED` and `423 ACCOUNT_LOCKED`.** This is the endpoint
that surfaces both. Its `409` description previously covered only the
"SCA not required / no pending challenge" cases, and it declared no
`423` at all — even though the endpoint returns `ACCOUNT_LOCKED` when
the customer's login is temporarily locked after too many failed
authorization attempts. `Error423` already existed and needed no change;
this just references it.

Both changes are additive and non-breaking, so `info.version` stays
`2025-10-13`.

## Known limitation this does not fix

While verifying the above against the running implementation, I found
that the error **envelope** does not match what the `ErrorNNN` schemas
declare. The API sends:

```json
{"code": "SCA_SESSION_REQUIRED", "reason": "..."}
```

but every `ErrorNNN` schema requires `status`, `code`, and `message`. So
a generated Python client rejects a real error body on `status`
(`int_type`) and `message` (`string_type`) regardless of this change:

```
('status',)  | int_type    | Input should be a valid integer
('message',) | string_type | Input should be a valid string
```

This is pre-existing and applies to **every** documented error response
across the spec, not just the two codes here — it is not introduced by
this PR, and this PR is still a strict improvement (the enum is the
published contract, and integrators reading the raw JSON body now have
the code documented). But it does mean the typed-model path is not yet
usable for error handling, so reconciling the envelope is worth its own
change. Two options, both spec-wide decisions I did not want to make
unilaterally: make the server emit `status`/`message`, or change the
schemas to match reality (`reason`, and `status` not required).

Relatedly, `Error423`'s own description points readers at
`details.lockedUntil` / `details.failedAttempts`, but no error path
populates them — those fields appear only on the `200` body of `POST
/sca/record-event`. I left that description alone rather than expand
this PR's scope, and worded the new `423` on the authorize endpoint to
promise only what is actually sent.

## Test plan

- `make lint` exits 0 (Redocly + Spectral, `--fail-severity=error`); the
630 remaining findings are pre-existing warnings/infos, 0 errors.
- `make build` rebundled `openapi.yaml` and `mintlify/openapi.yaml`;
re-running it is a no-op, so the committed bundles byte-match the build
output.
- Confirmed against the running implementation that both codes are
genuinely emitted on this endpoint, each covered by a test asserting the
status/code pair at the HTTP boundary — the `423` case is newly covered
by a test added alongside this change. The `423` body was captured
directly to confirm the wording matches what is sent.
- Diff is 4 files: 2 spec sources + the 2 generated bundles.

## Public

Documents two error codes the Grid API already returns:
`SCA_SESSION_REQUIRED` (409) is added to the shared `Error409` enum, and
`POST /quotes/{quoteId}/authorize` now documents both it and `423
ACCOUNT_LOCKED`.


Original PR: #759
@jklein24
jklein24 deleted the 07-29-error409-sca-session-required branch July 30, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants