docs: Exa integration page (agent-driven API-credit purchases via card delegation) - #266
Conversation
… card delegation) New integrations/exa.mdx between OpenClaw and BuildShip: the Exa partnership page detailing how agents buy and top up Exa API keys with x402 card delegation. Codeless format (prose contracts + SDK method anchors; the package typings are the call reference). Content blind-validated end to end with real purchases on both branches (owner-created delegation via discovery, and programmatic createDelegation fallback) on a fresh API key + latest SDK. Mintlify auto-serves the page as .md and auto-lists it in llms.txt via the docs.json navigation entry.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
…verification The embed-session flow is documented on the staging agentic-instructions surface; live-environment support is unconfirmed beyond route existence. Re-add once verified on prod.
There was a problem hiding this comment.
Pull request overview
Adds a new Mintlify integration page documenting how agents can autonomously purchase/top up Exa API credits using Nevermined’s x402 card-delegation flow, and wires the page into the Integrations navigation.
Changes:
- Added
integrations/exa.mdxdescribing the Exa plan, prerequisites, purchase contract, and troubleshooting for agent-driven purchases. - Updated
docs.jsonto include the new Exa page in the “Protocols & Partners” integrations group.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| integrations/exa.mdx | New integration doc for Exa’s x402 card-delegation purchase flow and operational guidance. |
| docs.json | Adds Exa to the Integrations navigation so the page is discoverable. |
Suppressed comments (1)
integrations/exa.mdx:76
- The troubleshooting entries for
ERR_PACKAGE_PATH_NOT_EXPORTEDand the deprecatedenvironmentoption are currently broader/more specific than what the rest of the docs describe. Aligning these to the documented behavior (subpath import + deprecation warning whenenvironmentis provided) will reduce confusion for readers.
| A delegation you spent from disappears from `getPurchasingPower()` | It is exhausted. Inspect it with `listDelegations()`; create or top up a delegation to continue. |
| Exa search returns `402` with `NO_MORE_CREDITS` | The Exa key's credits are spent. Repeat the purchase flow to top up the same key. |
|
|
||
| ## The flow | ||
|
|
||
| Handled with the Nevermined Payments SDK (`npm install @nevermined-io/payments`; the package is ESM-only; in a fresh npm project set `"type": "module"` in `package.json`, or the import fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`). The package's bundled TypeScript definitions are the authoritative call reference for every method named below. For broader context, start from the [Payments overview](/products/payments/overview) and the [documentation index](https://nevermined.ai/docs/llms.txt). |
|
|
||
| 1. **Initialize** the SDK with your Nevermined API key only (`Payments.getInstance({ nvmApiKey })`). The environment is derived from the key prefix; do not pass an `environment` option. | ||
| 2. **Find the delegation to pay with**; see [Getting a delegation](#getting-a-delegation) below. | ||
| 3. **Mint the x402 access token** for the Exa plan ID via `payments.x402.getX402AccessToken`, using the `nvm:card-delegation` scheme and referencing the delegation by ID (`delegationConfig: { delegationId }`). No agentId is needed for this flow: the method's arguments are `(planId, agentId?, tokenOptions?)`, so pass the plan ID, `undefined`, and the token options. Tokens cannot create delegations on the fly; the delegation must exist first. |
…r to the new integrations/exa page
aaitor
left a comment
There was a problem hiding this comment.
Approving. This is a well-built, codeless integration page and it holds up under verification: I checked every SDK-shaped claim against the @nevermined-io/payments source and the core purchase flow is accurate end to end. Two MEDIUM tightenings and one LOW confirmation below — none blocks merge, but the environment-warning troubleshooting row (L74) is worth correcting because it currently trains an agent to ignore a signal that is actually actionable.
What I verified against the SDK (local payments, v1.9.0 — PR tested 1.10.0)
getX402AccessToken(planId, agentId?, tokenOptions?)— exact match (src/x402/token.ts:64); the "pass planId,undefined, token options" instruction is correct.- Scheme
nvm:card-delegation→ maps to providerstripe(src/common/types.ts:514); default scheme isnvm:erc4337, so passing it explicitly (as the page says) is required. ✓ createDelegationpayload —provider: "stripe"✓,currency: "usd"✓ (the JSDoc example usesusdcfor erc4337; the page correctly usesusdfor card),spendingLimitCents/durationSecsarenumberon write ✓.- Budget-fields-as-strings on read ✓ (
DelegationSummary.remainingBudgetCents/amountSpentCents/spendingLimitCents: string),transactionCount: number✓. The write-number/read-stringsplit is a subtle detail the page gets right. getPurchasingPower()excludes exhausted delegations — confirmed: it callslistDelegations({ accessible: true }), which returns only Active/non-expired/with-budget (src/x402/delegation-api.ts:151,161). ✓nvmApiKey-only init with environment derived from key prefix ✓ (PaymentOptions.environmentis@deprecated,src/common/types.ts:15).payment-signatureheader ✓ (matches project convention, not deprecatedX-402).
Verification I ran
gh pr view/diff/checks— CI green (2/2, Mintlify preview 🟢).- Read all 3 changed files at head (
exa.mdxnew,docs.jsonnav,solutions/api-providers.mdxlink swaps); nav order confirmed openclaw → exa → buildship. - Cross-checked ~12 specific SDK claims against
paymentssource (citations above). Base ismain, no PR chain to trace. - Over-engineering / leanness pass: lean already, ship — 0 net-removable lines. The exhausted-delegation fact repeats across three sections, but that aids the cold-agent-lands-mid-page use case the page is written for; not debt.
solutions/api-providers.mdxL386 swaps Exa's external.mdmirror (a real-provider example) for the internal guide. Slight pedagogical downgrade for that section, but the right call given Exa's mirror teaches the retired shape — no change needed.
Copilot's suppressed comment (L76 troubleshooting alignment) is low-value; not echoing it.
| |---|---| | ||
| | Token mint fails: `Required token-generation input is missing or incomplete (HTTP 402)` | The mint referenced no existing delegation (e.g., legacy create-on-the-fly `delegationConfig` with card details). Create or discover a delegation first and pass `delegationConfig: { delegationId }`. | | ||
| | `ERR_PACKAGE_PATH_NOT_EXPORTED` on import | The SDK is ESM-only. Set `"type": "module"` in `package.json` or use `.mts`. | | ||
| | Console warning: `The 'environment' option is deprecated…` even though you never passed it | Known SDK issue; harmless. The environment is derived from your key prefix. | |
There was a problem hiding this comment.
MEDIUM — this row is inaccurate on the SDK source I can see, and "harmless" is the wrong framing.
The deprecation warning is gated (src/api/base-payments.ts:132):
if (options.environment && !environmentOptionDeprecationWarned) { … console.warn("[DEPRECATED] The 'environment' option is deprecated…") }It fires only when environment is actually passed — never "even though you never passed it". If an agent sees this warning, something is still passing environment (a wrapper, scaffold, or copied example), and the fix is to remove it — not to ignore it as a "known SDK issue". Documenting a correct, actionable signal as harmless trains agents to suppress it.
I'm on v1.9.0 locally; the PR tested 1.10.0. Two options:
- If 1.10.0 genuinely fires this with no
environmentpassed, that's an SDK regression worth apaymentsissue — link it here rather than calling it "harmless". - Otherwise, correct the row:
| | Console warning: `The 'environment' option is deprecated…` even though you never passed it | Known SDK issue; harmless. The environment is derived from your key prefix. | | |
| | Console warning: `The 'environment' option is deprecated…` | Something is still passing the deprecated `environment` option (a wrapper or copied example). Remove it — the environment is derived from your key prefix. | |
| [Exa](https://exa.ai) accepts autonomous payments through Nevermined's [x402 card-delegation](/specs/x402-card-delegation) scheme. An agent holding a Nevermined API key mints an x402 access token against the Exa plan and exchanges it at Exa's purchase endpoint for a working Exa API key. Repeat purchases top up the same key. | ||
|
|
||
| <Info> | ||
| Exa's Nevermined plan ID:<br />`27800462147494506865542649899724877617306579171265399959488097895839186996870`<br />The purchase is for API credits, not for a single search request. \$7 covers roughly 1,000 standard searches; see [Exa pricing](https://exa.ai/pricing) for current rates. |
There was a problem hiding this comment.
MEDIUM — state the environment this plan ID belongs to. Plan IDs are per-environment (live vs sandbox are separate backends), and Payments derives the environment from the API key prefix (getEnvironmentFromApiKey). A cold agent — exactly the page's stated test persona — handed a sandbox-prefixed key will mint against this live plan ID, the plan won't resolve, and the resulting error isn't in the troubleshooting table, so it dead-ends silently.
The flow charges a real $7, so it's inherently a live-key flow — just say so. One line in this <Info> block, e.g.:
This plan and purchase run on live; use a live-prefixed Nevermined API key.
And optionally a troubleshooting row for the mismatch (plan-not-found / wrong environment → check the key is live-prefixed).
|
|
||
| ## Verifying charges | ||
|
|
||
| Use `payments.delegation.listDelegations()` to inspect spend per delegation (`amountSpentCents`, `transactionCount`, status). Fully spent delegations are marked `Exhausted` and no longer appear in `getPurchasingPower()` results; purchasing power lists only delegations that can still pay. |
There was a problem hiding this comment.
LOW — confirm the literal Exhausted status value. The structural claim is verified (exhausted delegations drop out of getPurchasingPower() — it filters on accessible: true). But Exhausted as a specific status string isn't in the SDK (DelegationSummary.status is an untyped string; the only enumerated statuses the SDK documents are Active/Revoked on payment methods), so it's backend-emitted and I can't confirm the exact casing/wording from source. Since you runtime-verified this, just double-check Exhausted matches what listDelegations() actually returns byte-for-byte — a reader will string-match on it.
|
Follow-ups on the review comments, all landed:
Copilot's two: the ESM error reproduces on the main package import (not only the 🤖 Generated with Claude Code |
- State the plan runs on live and needs a live-prefixed key (Info block) - Reword the environment-warning troubleshooting row: actionable when the option is passed; link payments#416 for the 1.10.0 self-firing case - Add a wrong-environment troubleshooting row (sandbox key vs live plan)
What
Adds
integrations/exa.mdxto the Integrations section, between OpenClaw and BuildShip, detailing the Exa partnership: agents autonomously buy and top up Exa API keys ($7/purchase) via the x402 card-delegation scheme.Because the page enters
docs.jsonnavigation, Mintlify automatically serves it as.md(for agent ingestion) and lists it in the generatedllms.txt— no manual index edits needed.Why
Exa's own integration doc still teaches the retired create-on-the-fly delegation shape (rejected by the backend for current-version API keys since the #2392 rollout in nvm-monorepo), so agents following it dead-end at token mint. This page is the canonical, current-SDK reference on our side; Exa will be asked to align theirs separately.
Content notes
getPurchasingPower(), preferred) and programmaticcreateDelegationfallback (with card-selection caveat and budget/duration sizing).auth/clilogin flow and manual dashboard.Validation
Every claim on the page is runtime-verified. Content was blind-tested on 2026-08-02/03 by cold agents (fresh context, this page + a live API key only, latest SDK 1.10.0, newly created key): both branches completed real $7 purchases first try with zero failed calls, including a settle through an agent-created delegation. The embedded login flow was executed end to end on live as well.
🤖 Generated with Claude Code