Document external-account GET/DELETE, and fix all 85 broken curl auth examples - #793
Conversation
external-accounts.mdx covered create and list but neither of the by-id operations, so retrieving or deleting an account had no prose documentation. Written against the current spec rather than salvaged from #359, which predates several changes: it documented a PATCH endpoint the path never exposes, used US_ACCOUNT (not a member of ExternalAccountType) and accountCategory (not a field on UsdAccountInfoBase), and described delete as returning only 204. Delete does carry a 409. BENEFICIARY_TRUSTED fires when the account is a trusted SCA beneficiary, and the caller has to untrust and confirm before the delete succeeds - a failure mode worth documenting, since nothing about the call site suggests it. The GET response example is built from ExternalAccount plus UsdExternalAccountInfo, so it carries paymentRails: that field is response-only, and showing it here is the clearest place to make that concrete. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMnjJ8yWJsui7jLqqrDrL4
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
Greptile SummaryAdds documentation for retrieving and deleting individual external accounts.
Confidence Score: 4/5The authentication syntax in both new commands must be fixed before merging because copying either example results in an authentication failure. The documented API behavior matches the specification, but single quoting prevents credential expansion and the raw header bypasses curl's required HTTP Basic encoding. Files Needing Attention: mintlify/snippets/external-accounts.mdx
|
| Filename | Overview |
|---|---|
| mintlify/snippets/external-accounts.mdx | The endpoint paths, schemas, statuses, and deletion remediation match the OpenAPI contract, but both new curl commands use a nonfunctional Basic Authentication header. |
Prompt To Fix All With AI
### Issue 1
mintlify/snippets/external-accounts.mdx:1007
**Broken Basic authentication header**
When a developer copies either new command into a POSIX shell, the single quotes prevent the credential variables from expanding and curl sends an unencoded Basic header, causing the GET or DELETE request to fail authentication. Use curl's `-u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET"` form for both commands.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "Document GET and DELETE for a single ext..." | Re-trigger Greptile
|
|
||
| ```bash | ||
| curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts/ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965' \ | ||
| -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' |
There was a problem hiding this comment.
Broken Basic authentication header
When a developer copies either new command into a POSIX shell, the single quotes prevent the credential variables from expanding and curl sends an unencoded Basic header, causing the GET or DELETE request to fail authentication. Use curl's -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" form for both commands.
Prompt To Fix With AI
This is a comment left during a code review.
Path: mintlify/snippets/external-accounts.mdx
Line: 1007
Comment:
**Broken Basic authentication header**
When a developer copies either new command into a POSIX shell, the single quotes prevent the credential variables from expanding and curl sends an unencoded Basic header, causing the GET or DELETE request to fail authentication. Use curl's `-u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET"` form for both commands.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Correct, and worse than described — fixed in a606eb0.
Two independent faults, not one. The single quotes stop the shell expanding the variables, as you say. But even with expansion, Authorization: Basic $ID:$SECRET is still invalid: Basic auth requires base64(user:pass), not the raw pair. So the header fails whether or not the variables expand. -u handles both.
Checked it against the rest of the docs before changing anything, and -u is the house style rather than a deviation from it:
api-reference/authentication.mdxdocuments exactly this:curl -u "{client_id}:{client_secret}"- 178 uses of
-uacross 40 files, versus 87 uses of the broken-Hform across 17
I'd copied the -H form from the adjacent listing examples in this same file without checking it against the canonical page. My mistake.
Note this leaves the file internally inconsistent, deliberately. external-accounts.mdx has 28 occurrences of the broken form and now 2 correct ones. I've only fixed what this PR adds — converting the other 26 would bury a docs change under a mechanical sweep and make the diff hard to review.
The remaining 87 broken occurrences across 17 files are worth their own PR: every one of them fails if a developer copy-pastes it, which is the whole point of a curl example. Happy to do that sweep separately.
Generated by Claude Code
There was a problem hiding this comment.
Thanks for the thorough breakdown — you're right that I undersold the bug. The variable-expansion issue is the visible failure, but even a developer who correctly set environment variables would still hit a 401 because the raw ID:SECRET pair isn't base64-encoded. Two distinct failures for the cost of one wrong pattern.
The staged approach makes sense: fixing only what this PR adds keeps the diff reviewable, and a mechanical sweep of the remaining 87 occurrences is a clean, atomic PR with no logic to review. The house-style data (-u in 178 places vs 87 broken -H forms) makes the follow-up easy to justify and scope.
Fix looks good. The follow-up sweep would be a real developer-experience improvement — worth doing.
The -H 'Authorization: Basic $ID:$SECRET' form is wrong twice over:
single quotes stop the shell expanding the variables, and Basic auth
needs base64(user:pass) rather than the raw pair, so the header fails
even once expanded. -u does both correctly.
This matches api-reference/authentication.mdx, which documents
curl -u "{client_id}:{client_secret}" as the way to authenticate, and
the 178 uses of -u across 40 other doc files. I had copied the -H form
from the neighbouring listing examples without checking it against the
canonical page.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMnjJ8yWJsui7jLqqrDrL4
All 85 remaining curl examples passed credentials as
-H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET', which
fails two ways: single quotes stop the shell expanding the variables,
and Basic auth needs base64(user:pass) rather than the raw pair, so the
header is rejected even once expanded. Every one of these failed on
copy-paste.
-u is already what api-reference/authentication.mdx documents and what
178 examples across 40 other files use, so this converges on the
existing majority rather than introducing a style.
Only -H lines carrying the credential pair are touched. The JS and
Python examples that set an Authorization header from a pre-encoded
${credentials} value are correct and left alone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMnjJ8yWJsui7jLqqrDrL4
## Summary `InrAccountInfoBase` documents three rails for India — UPI, NEFT and RTGS — but the visualizer exposed only `vpa` and the docs only UPI. This syncs both, plus the Kotlin sample. Rebased onto current `main` (`a1cd310`); the conflict is resolved and the branch is a single commit. | File | Change | |---|---| | `account-types.ts` | `INR_ACCOUNT` gains `accountNumber`, `ifsc`, `rail`, `bankName`, each marked with the rail it belongs to | | `currencies.ts` | `allRails` gains `NEFT` and `RTGS`; label becomes `UPI / Bank Account` | | `country-support.mdx` | India row lists all three rails | | `external-accounts.mdx` | India tab split into UPI and NEFT/RTGS examples | | `samples/kotlin/.../ExternalAccounts.kt` | all five fields optional, so a NEFT payload can be built | ## What changed in the rebase **GTQ and JMD are dropped.** The original revision fixed `GTQ_ACCOUNT` fields and added `bankName` to `JMD_ACCOUNT`. Both landed on `main` through later syncs — `account-types.ts` already carries them — so those hunks are gone and this is INR-only. The title has been narrowed to match. **`paymentRails` removed from both request examples.** The original added `"paymentRails": ["UPI"]` and `["NEFT"]` to the create bodies. That field is **response-only**: `InrExternalAccountCreateInfo` composes `BaseExternalAccountInfo` + `InrAccountInfoBase` + `beneficiary`, and none of those define it, while `InrAccountInfo` (the response wrapper) does. Sending it is the same mistake #351 made. This is worth care because **`rail` and `paymentRails` are different things** and both are in play here. `rail` is a genuine request field on `InrAccountInfoBase` — singular, the rail you want to route over. `paymentRails` is the plural array Grid returns. Having both names in one payload is exactly how the earlier confusion happened, so the `<Note>` now states it outright. **New curl blocks use `-u`.** The original used `-H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET'`, which fails on copy-paste twice over — single quotes block variable expansion, and Basic auth needs `base64(user:pass)` rather than the raw pair. #793 is fixing the other 85 instances of this repo-wide. ## Verification - Both India JSON payloads parse - MDX component tags and code fences balance - `UPI`, `NEFT`, `RTGS` all confirmed as `PaymentRail` enum members - `account-types.ts` and `currencies.ts` typecheck clean under `--strict` - No `paymentRails` remains anywhere in `external-accounts.mdx` - GTQ and JMD entries verified byte-unchanged from `main` **Not verified — needs a reviewer who can build:** - **The Kotlin sample does not compile here.** `.accountNumber()`, `.ifsc()`, `.rail()` and `.bankName()` need to exist on `InrExternalAccountCreateInfo.Builder` in the pinned Grid SDK version. The `optText(...)?.let { }` pattern itself matches what the same file already does for GBP and SGD, and `optText` is imported, but whether the SDK exposes those four setters is unconfirmed. **If the SDK predates the INR schema change, this hunk will not build** — worth checking before merge. - No visual render check. `npm ci` fails in this environment (`@central-icons-react` needs `CENTRAL_LICENSE_KEY`), and `make lint` is broken on `main` regardless (`npx spectral lint` resolves to a stub `spectral@0.0.0`). The Mintlify and Vercel previews are the real checks. ## Possible conflict #793 rewrites 26 auth lines in `external-accounts.mdx`. Whichever of these merges second may need a trivial rebase; the overlap is auth lines only, not the India tab. Co-authored-by: Claude <noreply@anthropic.com>
## Summary `external-accounts.mdx` documented **21 of the 45** account types in `ExternalAccountType`. Because the page reads as an enumeration — a tab per country or rail — a developer scanning it would reasonably conclude the other 24 aren't supported. This documents all of them. Docs-only, one file, +927 lines. Generated bundles untouched. **Coverage is now 45/45**, verified by diffing the `accountType` values in the page against `account-types.ts`. | Added | Types | |---|---| | 18 fiat tabs | `AED` `BDT` `BWP` `CNY` `DKK` `EGP` `GHS` `GTQ` `HKD` `HTG` `IDR` `JMD` `MYR` `PKR` `SGD` `THB` `VND` `XAF` | | Cryptocurrency tab | Ethereum L1, Base, Polygon, Solana, Tron, Plasma, Lightning — it previously showed Spark alone | ## Scope grew during the work — worth knowing I started from a list of 17 missing types. A mechanical diff against `account-types.ts` found **24**: `DKK`, `HKD`, `IDR`, `MYR`, `SGD`, `THB`, `VND` and `LIGHTNING` weren't on my list. Stopping at 17 would have reproduced the same partial-coverage problem in a PR whose entire purpose is fixing it, so all 24 are here. ## Where the content comes from Nothing is inferred from existing examples — each type was read from its schema: - **Fields and required/optional split** — `<Ccy>AccountInfoBase.yaml` - **Beneficiary shape** — `<Ccy>Beneficiary.yaml`, which is where the real variation lives. Most need only `fullName`, but **AED** and **JMD** also require an `address`, **GTQ** requires `countryOfResidence` and `phoneNumber`, and **JMD** requires `phoneNumber`. Those are easy to miss and produce confusing validation failures. - **Per-chain asset support** — each `Payment<Chain>WalletInfo.yaml`. Ethereum carries `USDC` and `USDT`; Base and Polygon are USDC-only. Stated per chain rather than assumed uniform. - **Dual-rail currencies** (BDT, CNY, EGP, GHS, PKR) get separate bank-transfer and mobile-money examples, following the existing Colombia and El Salvador tabs. **No example sends `paymentRails`.** It's response-only, and three separate PRs (#351, #661, and the original #359 lineage) got that wrong. The single occurrence of it in this file remains where it belongs — the `GET` **response** example. Example people match the entries added to `currencies.ts` in #791, so the docs and the visualizer describe the same fictional customers. ## Verification - **All 54 curl payloads in the file parse as JSON** — not just the new ones - **Every `accountType` used is a valid `ExternalAccountType` member** - **Coverage diff is empty**: no type in `account-types.ts` is undocumented - MDX component tags and code fences balance - All new curl examples use `-u`, consistent with #793 - `openapi.yaml` and `mintlify/openapi.yaml` byte-identical to `main` **Not verified:** no visual render. This is 927 lines of new tabs, so the **Mintlify preview is the meaningful review** — worth checking that the tab strip doesn't overflow badly now that it holds 32 tabs. `make lint` was not run; it fails on `main` regardless (`npx spectral lint` resolves to a stub `spectral@0.0.0`). ## One thing this surfaces `SWIFT_ACCOUNT` is documented here but **absent from `account-types.ts`**, so the visualizer still can't produce it. That's the known gap from closed #775 — the entry was correct but unreachable without widening `FiatCurrency` past a single `accountType`. Unchanged by this PR, but the asymmetry is now visible: the docs describe a type the visualizer can't build. ## Left out deliberately I had also listed adding SWIFT to `account-model.mdx`. **That item doesn't hold up.** My earlier check used the wrong path — the file is at `platform-overview/core-concepts/account-model.mdx`, not `snippets/`. Looking at the real file, it's an explicitly illustrative page showing 8 representative types, not an enumeration. Singling out SWIFT there would recreate the curated-list problem rather than fix one. Left alone. --- _Generated by [Claude Code](https://claude.ai/code/session_01MMnjJ8yWJsui7jLqqrDrL4)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Two things, both docs-only. Generated bundles untouched.
GETandDELETEfor a single external account —external-accounts.mdxcovered create and list but neither by-id operation.Replaces #359, which should be closed.
Part 1 — the new sections
GET /customers/external-accounts/{externalAccountId}, full response example,404DELETE /customers/external-accounts/{externalAccountId},204, and the409 BENEFICIARY_TRUSTEDpathWhy a rewrite rather than a rebase of #359. That PR has been open since April and the spec moved under it in four ways:
PATCHendpoint that does not exist. The path exposes onlyget:anddelete:— verified in both the source path file and the bundledopenapi.yaml. About a third of that PR describes an operation the API doesn't have."accountType": "US_ACCOUNT"is not a member ofExternalAccountType. The value isUSD_ACCOUNT."accountCategory": "CHECKING"is not a field onUsdAccountInfoBase. The real field isbankAccountType.409, which docs: sync external account docs with new GET/PATCH/DELETE endpoints #359 predates.The
409is why the delete section earns its place.BENEFICIARY_TRUSTEDfires when the account is a trusted SCA beneficiary, and the caller must untrust and confirm before delete succeeds. Nothing at the call site hints at that.The GET example includes
paymentRailsbecause the response genuinely carries it —UsdExternalAccountInfocomposesUsdAccountInfo, which requires it. A short<Info>notes it's returned rather than sent; that distinction has already caused one real bug.Every field traces to a schema:
ExternalAccountfor the envelope,UsdAccountInfoBasefor account fields,UsdAccountInfoforpaymentRails(ACH/WIRE/RTP/FEDNOW),UsdBeneficiaryfor the minimum beneficiary shape.Part 2 — the curl auth sweep
Greptile flagged the auth header on the new examples. It was right, and the problem turned out to be repo-wide: 85 curl examples across 17 files used
-H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET'which fails two independent ways. The single quotes stop the shell expanding the variables; and Basic auth requires
base64(user:pass), not the raw pair, so the header is rejected even once expanded. Every one of these failed on copy-paste — which is the entire point of a curl example.All 85 now use
-u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET".This converges on the existing majority rather than introducing a style.
api-reference/authentication.mdxdocumentscurl -u "{client_id}:{client_secret}", and 178 examples across 40 other files already use-u. The-Hform was the minority pattern.Biggest concentrations:
snippets/external-accounts.mdx(26),payouts-and-b2b/payment-flow/list-transactions.mdx(15),rewards/developer-guides/listing-transactions.mdx(8).Verification
-H 'Content-Type'was clobberedBasic ${credentials}usages are untouched. Those build the header from a pre-encoded value and are correct; the sweep matched only-Hlines carrying the raw credential pair-u.mdxinmintlify/mainNot verified: no visual render check, and
make lintwas not run — it fails onmainregardless, becausenpx spectral lintresolves to a stubspectral@0.0.0rather than@stoplight/spectral-cli. This change touches no OpenAPI source, so the Mintlify preview is the meaningful check.Still open in this file
external-accounts.mdxdocuments 21 of ~37 account types inaccount-types.ts. Missing:XAF,BWP,AED,BDT,EGP,GHS,GTQ,HTG,JMD,PKR,CNY, plus wallet typesBASE_WALLET,POLYGON_WALLET,PLASMA_WALLET,SOLANA_WALLET,TRON_WALLET,ETHEREUM_WALLET. Out of scope here, but it's the largest remaining documentation gap in this area.