Skip to content

Document external-account GET/DELETE, and fix all 85 broken curl auth examples - #793

Merged
shreyav merged 3 commits into
mainfrom
claude/docs-external-account-get-delete
Aug 4, 2026
Merged

Document external-account GET/DELETE, and fix all 85 broken curl auth examples#793
shreyav merged 3 commits into
mainfrom
claude/docs-external-account-get-delete

Conversation

@shreyav

@shreyav shreyav commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Two things, both docs-only. Generated bundles untouched.

  1. Documents GET and DELETE for a single external accountexternal-accounts.mdx covered create and list but neither by-id operation.
  2. Fixes all 85 remaining broken curl auth examples across 17 files.

Replaces #359, which should be closed.

Part 1 — the new sections

Section Covers
Retrieving a single external account GET /customers/external-accounts/{externalAccountId}, full response example, 404
Deleting an external account DELETE /customers/external-accounts/{externalAccountId}, 204, and the 409 BENEFICIARY_TRUSTED path

Why a rewrite rather than a rebase of #359. That PR has been open since April and the spec moved under it in four ways:

  1. It documents a PATCH endpoint that does not exist. The path exposes only get: and delete: — verified in both the source path file and the bundled openapi.yaml. About a third of that PR describes an operation the API doesn't have.
  2. "accountType": "US_ACCOUNT" is not a member of ExternalAccountType. The value is USD_ACCOUNT.
  3. "accountCategory": "CHECKING" is not a field on UsdAccountInfoBase. The real field is bankAccountType.
  4. Delete's contract changed. feat: document BENEFICIARY_TRUSTED (409) on customer external-account delete #770 added a 409, which docs: sync external account docs with new GET/PATCH/DELETE endpoints #359 predates.

The 409 is why the delete section earns its place. BENEFICIARY_TRUSTED fires 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 paymentRails because the response genuinely carries it — UsdExternalAccountInfo composes UsdAccountInfo, 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: ExternalAccount for the envelope, UsdAccountInfoBase for account fields, UsdAccountInfo for paymentRails (ACH/WIRE/RTP/FEDNOW), UsdBeneficiary for 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.mdx documents curl -u "{client_id}:{client_secret}", and 178 examples across 40 other files already use -u. The -H form 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

  • 85 replacements, 85 lines added, 85 deleted — strictly 1:1, no line lost or merged
  • Zero occurrences of the broken form remain
  • Only auth lines changed — diff contains no other removals, so no -H 'Content-Type' was clobbered
  • The 11 JS/Python Basic ${credentials} usages are untouched. Those build the header from a pre-encoded value and are correct; the sweep matched only -H lines carrying the raw credential pair
  • No curl block ended up with a duplicate -u
  • MDX component tags and code fences balance across every .mdx in mintlify/
  • The new JSON example parses
  • Generated bundles byte-identical to main

Not verified: no visual render check, and make lint was not run — it fails on main regardless, because npx spectral lint resolves to a stub spectral@0.0.0 rather 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.mdx documents 21 of ~37 account types in account-types.ts. Missing: XAF, BWP, AED, BDT, EGP, GHS, GTQ, HTG, JMD, PKR, CNY, plus wallet types BASE_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.

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
@mintlify

mintlify Bot commented Aug 4, 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 Aug 4, 2026, 4:45 AM

@vercel

vercel Bot commented Aug 4, 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 Aug 4, 2026 4:59am
grid-wallet-demo Ignored Ignored Preview Aug 4, 2026 4:59am

Request Review

@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds documentation for retrieving and deleting individual external accounts.

  • Provides a GET-by-ID request and complete USD external-account response example.
  • Documents DELETE success and the trusted-beneficiary conflict remediation flow.

Confidence Score: 4/5

The 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

Important Files Changed

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

Comment thread mintlify/snippets/external-accounts.mdx Outdated

```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'

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.

P1 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.mdx documents exactly this: curl -u "{client_id}:{client_secret}"
  • 178 uses of -u across 40 files, versus 87 uses of the broken -H form 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

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.

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
@shreyav shreyav changed the title Document GET and DELETE for a single external account Document external-account GET/DELETE, and fix all 85 broken curl auth examples Aug 4, 2026
@shreyav
shreyav merged commit 86b0dcf into main Aug 4, 2026
8 checks passed
@shreyav
shreyav deleted the claude/docs-external-account-get-delete branch August 4, 2026 05:55
shreyav pushed a commit that referenced this pull request Aug 4, 2026
## 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>
shreyav added a commit that referenced this pull request Aug 4, 2026
## 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>
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