From 7cd2850f51c371cf14cdbb19385b38cce790836e Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:02:23 +0200 Subject: [PATCH 01/17] docs(api): document the dashboard route to API keys Integrators only found the programmatic OTP route, so point Quick Start and AI Agent Integration at the dashboard steps in Authentication. --- docs/api/pages/02-quick-start-with-the-sdk.md | 2 ++ .../pages/03-authentication-and-partner-keys.md | 15 +++++++++++++++ docs/api/pages/12-ai-agent-integration.md | 2 ++ 3 files changed, 19 insertions(+) diff --git a/docs/api/pages/02-quick-start-with-the-sdk.md b/docs/api/pages/02-quick-start-with-the-sdk.md index 9d15884d0..8e263bfbc 100644 --- a/docs/api/pages/02-quick-start-with-the-sdk.md +++ b/docs/api/pages/02-quick-start-with-the-sdk.md @@ -12,6 +12,8 @@ bun add @vortexfi/sdk ## Initialize In Node.js +Create the credential in the Vortex dashboard: sign in with your email, open **API keys**, and click **Create credential**. Existing customers sign in with the email of their onboarded profile. The secret key is shown once. The full steps are in [Authentication And API Keys](https://api-docs.vortexfinance.co/authentication-and-partner-keys). + ```js import { VortexSdk, diff --git a/docs/api/pages/03-authentication-and-partner-keys.md b/docs/api/pages/03-authentication-and-partner-keys.md index a4f0067e2..c23cf3721 100644 --- a/docs/api/pages/03-authentication-and-partner-keys.md +++ b/docs/api/pages/03-authentication-and-partner-keys.md @@ -7,6 +7,21 @@ Vortex issues one API credential with two values for one profile subject: Both values share one immutable credential ID, subject profile, optional partner, environment, expiry, and revocation lifecycle. If a request sends both values, they must belong to the same credential or Vortex returns `403 CREDENTIAL_MISMATCH`. +## Get An API Key From The Dashboard + +The quickest way to get a credential is the Vortex dashboard: + +1. Open . It issues production keys (`pk_live_*` / `sk_live_*`). For sandbox keys (`pk_test_*` / `sk_test_*`), use . +2. Enter your email and the 6-digit code Vortex sends you. The first sign-in with a new email creates your profile. +3. Open **API keys** and click **Create credential**. Name the credential and choose an expiration of up to two years. +4. Copy both values from the confirmation dialog. The secret key is shown once: after the dialog closes it cannot be retrieved, only revoked and replaced with a new credential. + +The credential authenticates the profile you signed in with. If you are already a Vortex customer, sign in with the email of your onboarded profile. A different email creates a new profile that has not completed KYC or KYB, and its key cannot register ramps until that profile is onboarded. + +Keep the secret key on your backend, in a secret manager, and send it as `X-API-Key` only from there; see Secret Handling below. Only the public key may appear in browser code. + +To issue credentials from your own code instead, use the OTP and credential endpoints under Provision A Profile-Managed Credential below. + ## Capability Matrix | Task | Public value | Secret value | Supabase Bearer | diff --git a/docs/api/pages/12-ai-agent-integration.md b/docs/api/pages/12-ai-agent-integration.md index f16c59464..9d495eb84 100644 --- a/docs/api/pages/12-ai-agent-integration.md +++ b/docs/api/pages/12-ai-agent-integration.md @@ -26,6 +26,8 @@ The SDK paths support BRL (PIX), USD (ACH), MXN (SPEI), COP, and ARS (CBU). EUR Ramping requires an onboarded (KYC/KYB-approved) user. Onboarding is a separate, corridor-specific flow that most corridors also expose through the API — see Section H before assuming the app or Widget is required. +Server-side paths authenticate with an API credential. The account holder creates it in the Vortex dashboard: sign in with the email of the onboarded profile, open **API keys**, and click **Create credential**. The secret key is shown once and belongs on your backend. See [Authentication And API Keys](https://api-docs.vortexfinance.co/authentication-and-partner-keys). + Do not expose an `sk_*` or reimplement signing against the raw ramp API in a browser. An approved origin means Vortex has added your exact browser origin to its allowlist; request it at before you integrate, because unapproved origins fail at the CORS preflight. Use the browser build of `@vortexfi/sdk` with Bearer authentication on an approved origin, or use the Widget. Browser SDK users explicitly accept that ephemeral secrets are generated in browser memory and backed up to plaintext same-origin localStorage by default. ## C. Python (`vortex-sdk-python`) From 7a1c1167fb6320d656b608f57b6dcd315d9a10c7 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:03:12 +0200 Subject: [PATCH 02/17] docs(api): show bank transfer instructions from update, not start The API releases achPaymentData on the first ramp update and on status; the start response never carries it, so integrators following the docs read undefined. --- .agents/skills/vortex-integration/SKILL.md | 8 +++++--- docs/api/pages/02-quick-start-with-the-sdk.md | 7 ++++--- docs/api/pages/04-ramp-lifecycle.md | 4 ++-- docs/api/pages/09-fiat-corridors.md | 2 +- docs/api/pages/12-ai-agent-integration.md | 2 +- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.agents/skills/vortex-integration/SKILL.md b/.agents/skills/vortex-integration/SKILL.md index ce4f0b9fe..c916bf590 100644 --- a/.agents/skills/vortex-integration/SKILL.md +++ b/.agents/skills/vortex-integration/SKILL.md @@ -369,10 +369,12 @@ const { rampProcess } = await vortex.registerRamp(quote, { destinationAddress: "0xUserWalletAddress" }); -const started = await vortex.startRamp(rampProcess.id); +// Bank transfer instructions the user must pay arrive with the registered ramp +// (registerRamp performs the update that releases them); startRamp does not repeat them. +console.log(rampProcess.achPaymentData); -// Bank transfer instructions the user must pay are on the START response: -console.log(started.achPaymentData); +// After the user initiates the transfer, start before the ramp's expiresAt. +const started = await vortex.startRamp(rampProcess.id); ``` No user-signed on-chain transactions on buys. Unlike BRL there is no QR code — display the `achPaymentData` deposit instructions verbatim; the ramp continues automatically once the fiat deposit is confirmed. diff --git a/docs/api/pages/02-quick-start-with-the-sdk.md b/docs/api/pages/02-quick-start-with-the-sdk.md index 8e263bfbc..774a95059 100644 --- a/docs/api/pages/02-quick-start-with-the-sdk.md +++ b/docs/api/pages/02-quick-start-with-the-sdk.md @@ -162,10 +162,11 @@ const { rampProcess } = await sdk.registerRamp(quote, { // fiatAccountId is optional for onramp }); -const started = await sdk.startRamp(rampProcess.id); - // Show the user how to pay via SPEI -console.log(started.achPaymentData); +console.log(rampProcess.achPaymentData); + +// After the user initiates the SPEI transfer, start the ramp. +const started = await sdk.startRamp(rampProcess.id); ``` No user-signed on-chain transactions are required for the bank-transfer onramp shown here. The SDK signs its ephemeral transactions during `registerRamp`. diff --git a/docs/api/pages/04-ramp-lifecycle.md b/docs/api/pages/04-ramp-lifecycle.md index 993ee689b..8c7f3f0bb 100644 --- a/docs/api/pages/04-ramp-lifecycle.md +++ b/docs/api/pages/04-ramp-lifecycle.md @@ -26,11 +26,11 @@ Use `POST /v1/ramp/update` to submit signed transactions and route-specific tran The SDK performs this automatically for the buy flows it supports. Direct API integrations must ensure that each signature or transaction hash matches the transaction returned by Vortex for the same ramp and phase. EUR BUY requires both the profile-linked owner's typed-data permit and the ephemeral-owned signatures returned at registration; the SDK signs the ephemeral set and returns the permit as a user-owned transaction. -On buys, the fiat payment instructions (`depositQrCode` for BRL, `ibanPaymentData` for EUR) are withheld until the presigned transactions pass validation: they are released on the update response and on `GET /v1/ramp/{id}`, not on the register response. SDK integrations receive supported-corridor instructions directly from `registerRamp`, which performs the update internally. For EUR, the owner permit must also be submitted (`submitUserTransactions` in the SDK, or the update endpoint directly) before `ibanPaymentData` is released. +On buys, the fiat payment instructions (`depositQrCode` for BRL, `ibanPaymentData` for EUR) are withheld until the presigned transactions pass validation: they are released on the update response and on `GET /v1/ramp/{id}`, not on the register response. SDK integrations receive supported-corridor instructions directly from `registerRamp`, which performs the update internally. For EUR, the owner permit must also be submitted (`submitUserTransactions` in the SDK, or the update endpoint directly) before `ibanPaymentData` is released. For USD, MXN, COP, and ARS, the first update response returns the bank transfer instructions as `achPaymentData`, which `GET /v1/ramp/{id}` also returns afterwards. ## 4. Start The Ramp -Use `POST /v1/ramp/start` after required signatures, transaction hashes, and fiat payment steps are complete. For BRL buys, call start after the user completes the PIX payment. For EUR buys, submit all signatures, display the released IBAN instructions, and call start after the user initiates the SEPA transfer. For USD, MXN, COP, and ARS buys the order is inverted: call start first — the start response's `achPaymentData` contains the bank transfer instructions the user must pay. +Use `POST /v1/ramp/start` after required signatures, transaction hashes, and fiat payment steps are complete. For BRL buys, call start after the user completes the PIX payment. For EUR buys, submit all signatures, display the released IBAN instructions, and call start after the user initiates the SEPA transfer. For USD, MXN, COP, and ARS buys, display the `achPaymentData` instructions and call start after the user initiates the bank transfer. The start response does not repeat `achPaymentData`. If a BRL PIX payment is confirmed by the payment partner but the client cannot call start (for example because the managed profile was deleted or its corridor policy changed after registration), Vortex automatically starts the already-signed persisted ramp. This recovery is tied to the exact provider ticket issued at registration; it does not authorize new ramps or bypass payment verification. diff --git a/docs/api/pages/09-fiat-corridors.md b/docs/api/pages/09-fiat-corridors.md index 5cfca6ca7..e226e44f3 100644 --- a/docs/api/pages/09-fiat-corridors.md +++ b/docs/api/pages/09-fiat-corridors.md @@ -171,7 +171,7 @@ Sells pay out to a saved bank account referenced by `fiatAccountId` in the regis ### Payment Instructions On Buys -After `POST /v1/ramp/start`, the response's `achPaymentData` contains the bank transfer instructions the user must pay (beneficiary, account, and reference details for the corridor's rail). Display them to the user verbatim; the ramp continues automatically once the fiat deposit is confirmed. +The first `POST /v1/ramp/update` response returns `achPaymentData`: the bank transfer instructions the user must pay (beneficiary, account, and reference details for the corridor's rail). `GET /v1/ramp/{id}` returns them too, and the SDK's `registerRamp` returns them on `rampProcess` because it performs that update. Display them to the user verbatim, and call `POST /v1/ramp/start` after the user initiates the transfer; the start response does not repeat them. The ramp continues automatically once the fiat deposit is confirmed. ### Limits diff --git a/docs/api/pages/12-ai-agent-integration.md b/docs/api/pages/12-ai-agent-integration.md index 9d495eb84..2b196f669 100644 --- a/docs/api/pages/12-ai-agent-integration.md +++ b/docs/api/pages/12-ai-agent-integration.md @@ -162,7 +162,7 @@ On a **buy**, where the fiat payment instructions appear depends on the corridor - **BRL**: `depositQrCode` (PIX) is released once the presigned transactions submitted via update pass validation — on the update response and on `GET /v1/ramp/{id}`, not on the register response. Show it; wait for the user to pay; then call start. (The SDK performs the update inside `registerRamp`, so SDK callers see it on the returned ramp process.) - **EUR**: the direct client must first submit the linked owner's EIP-712 permit and every ephemeral signature. `ibanPaymentData` (IBAN, BIC, receiver name, payment reference) is released only after the complete set validates. Show it; the user initiates the SEPA transfer; then call start before the start deadline. -- **USD, MXN, COP, ARS**: call start first; the start response's `achPaymentData` contains the bank transfer instructions for the corridor's rail (ACH, SPEI, CBU). Display them verbatim; the ramp continues automatically once the deposit is confirmed. +- **USD, MXN, COP, ARS**: the first update response contains `achPaymentData`, the bank transfer instructions for the corridor's rail (ACH, SPEI, CBU); `GET /v1/ramp/{id}` returns them too, and the start response does not. Display them verbatim; the user initiates the transfer; then call start before the start deadline. The ramp continues automatically once the deposit is confirmed. On a **supported sell**, the user signs the user-owned transaction(s), you submit them via update, then call start. Vortex pays out to the user's PIX key (BRL) or the saved bank account referenced by `fiatAccountId` (USD, MXN, COP, ARS). EUR SELL is unavailable. From 9b2491ea4721c2b3d48b5d47350367438aa27877 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:03:12 +0200 Subject: [PATCH 03/17] docs(sdk): read bank transfer instructions from the registered ramp --- packages/sdk/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 4fdb4b0fb..888d966d6 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -81,9 +81,11 @@ const { rampProcess } = await sdk.registerRamp(quote, { // fiatAccountId is optional for onramp. }); -// Inspect off-chain fiat payment instructions before starting. +// Fiat payment instructions arrive with the registered ramp; startRamp does not repeat them. +console.log("Pay via:", rampProcess.achPaymentData); + +// Start after the user initiates the bank transfer. const startedRamp = await sdk.startRamp(rampProcess.id); -console.log("Pay via:", startedRamp.achPaymentData); ``` Quotes can be requested without any key (anonymous rate discovery). Registering through the SDK requires either a `secretKey` or an `accessTokenProvider` to resolve to an onboarded user. A secret can be a user-scoped key or a partner key delegated to the user; a `publicKey` or partner-only secret key is insufficient. The same user must have completed Alfredpay KYC for the country, so registration resolves to that user's Alfredpay customer automatically. From 4518e63405c6b0d91c83794e16f2ea708cb1c59c Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:04:44 +0200 Subject: [PATCH 04/17] docs(api): add an own-account path for bots and treasury jobs Self-ramping integrators had to infer from managed-profile pages that they need no child profiles; spell out the setup and per-ramp sequence and link it from Quick Start. --- docs/api/pages/02-quick-start-with-the-sdk.md | 2 ++ docs/api/pages/12-ai-agent-integration.md | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/docs/api/pages/02-quick-start-with-the-sdk.md b/docs/api/pages/02-quick-start-with-the-sdk.md index 774a95059..da2f73621 100644 --- a/docs/api/pages/02-quick-start-with-the-sdk.md +++ b/docs/api/pages/02-quick-start-with-the-sdk.md @@ -2,6 +2,8 @@ This page walks through complete BRL and bank-transfer-corridor (USD, MXN, COP, ARS) ramps end-to-end using `@vortexfi/sdk` in Node.js or a modern browser. +Ramping for your own account, for example from a trading bot? You need one onboarded profile and one API key, and no managed profiles. The step-by-step sequence is in section B.1 of [AI Agent Integration](https://api-docs.vortexfinance.co/ai-agent-integration). + ## Install ```bash diff --git a/docs/api/pages/12-ai-agent-integration.md b/docs/api/pages/12-ai-agent-integration.md index 2b196f669..df2af9796 100644 --- a/docs/api/pages/12-ai-agent-integration.md +++ b/docs/api/pages/12-ai-agent-integration.md @@ -30,6 +30,27 @@ Server-side paths authenticate with an API credential. The account holder create Do not expose an `sk_*` or reimplement signing against the raw ramp API in a browser. An approved origin means Vortex has added your exact browser origin to its allowlist; request it at before you integrate, because unapproved origins fail at the CORS preflight. Use the browser build of `@vortexfi/sdk` with Bearer authentication on an approved origin, or use the Widget. Browser SDK users explicitly accept that ephemeral secrets are generated in browser memory and backed up to plaintext same-origin localStorage by default. +### B.1 Ramping On Your Own Account + +Use this path when you ramp for yourself or your own business, for example from a trading bot, a treasury job, or a payout script. You need one onboarded profile and one secret key on your server. Managed child profiles, `X-Managed-Profile-Id`, and browser origin approval are not needed. + +One-time setup, all in the Vortex dashboard (): + +1. Sign in with your email. Existing customers use the email of their onboarded profile. +2. Under **Onboarding**, complete KYC (individual) or KYB (business) for each corridor you will use. To sell into USD, MXN, COP, or ARS, also add the payout bank account there. Section H covers doing this through the API instead. +3. Under **API keys**, create a credential and store its secret key on your server. See [Authentication And API Keys](https://api-docs.vortexfinance.co/authentication-and-partner-keys). + +Then, for each ramp: + +1. **Quote.** `sdk.createQuote()` or `POST /v1/quotes`. +2. **Register.** `sdk.registerRamp()` or `POST /v1/ramp/register`. On a buy, pass the receiving wallet as `destinationAddress` (for EUR, also your linked wallet as `walletAddress`). On a sell, pass your own wallet as `walletAddress`, plus `pixDestination` for BRL or the payout account's `fiatAccountId` for USD, MXN, COP, and ARS (`sdk.listDomesticFiatAccounts()` or `GET /v1/domestic/fiatAccounts` returns it). +3. **Sign and update.** The SDK signs the ephemeral transactions and submits them inside `registerRamp`. On a sell, and for the owner permit on an EUR buy, your wallet is the user wallet: validate the returned user transactions, sign or send them with your wallet key, and submit them with `sdk.submitUserTransactions()` or `POST /v1/ramp/update`. +4. **Fund.** On a buy, pay the instructions released by the update: `depositQrCode` for BRL (PIX), `achPaymentData` for USD, MXN, COP, and ARS, or `ibanPaymentData` for EUR. On a sell, what your wallet signed or sent in step 3 funds the ramp; there is no separate funding step. +5. **Start.** `sdk.startRamp()` or `POST /v1/ramp/start`, before the `expiresAt` returned by register and update (15 minutes after registration). After that deadline, update and start are refused. +6. **Track.** Poll `GET /v1/ramp/{id}` until `status` is `COMPLETE` or `FAILED`, or register a webhook (Section D.6). + +Signing in step 3 is the one case where a server signs the user-owned transactions that Section D.4 routes to the user's wallet: the funds are your own. Keep that wallet key in a secret manager, separate from the per-ramp ephemeral keys. + ## C. Python (`vortex-sdk-python`) `vortex-sdk-python` is a process-bridge wrapper around the native Node.js SDK. It spawns the Node SDK and exposes a Python-friendly surface, so the behavior, custody model, and supported flows match `@vortexfi/sdk` exactly. From 7a93582ac88811589de0c6fd43b1ec6f9f27113d Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:09:35 +0200 Subject: [PATCH 05/17] docs(api): list the ramp status values the API returns SimpleStatus described COMPLETED, but every ramp response maps phases to TransactionStatus, which returns COMPLETE, so spec-following clients never detected completion. --- docs/api/openapi/vortex.openapi.d.ts | 7 +++++-- docs/api/openapi/vortex.openapi.json | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/api/openapi/vortex.openapi.d.ts b/docs/api/openapi/vortex.openapi.d.ts index 3a9a82016..cf8ea1cf3 100644 --- a/docs/api/openapi/vortex.openapi.d.ts +++ b/docs/api/openapi/vortex.openapi.d.ts @@ -3316,8 +3316,11 @@ export interface components { }[]; }; }; - /** @description `PENDING`, `FAILED`, `COMPLETED` */ - SimpleStatus: string; + /** + * @description Overall ramp status. `COMPLETE` and `FAILED` are terminal; use this field, not `currentPhase`, to detect the end of a ramp. + * @enum {string} + */ + SimpleStatus: "PENDING" | "COMPLETE" | "FAILED"; StartKYC2Request: { documentType: components["schemas"]["KYCDocType"]; taxId: string; diff --git a/docs/api/openapi/vortex.openapi.json b/docs/api/openapi/vortex.openapi.json index 67ff4cf38..6224c2ecc 100644 --- a/docs/api/openapi/vortex.openapi.json +++ b/docs/api/openapi/vortex.openapi.json @@ -3720,7 +3720,8 @@ "type": "object" }, "SimpleStatus": { - "description": "`PENDING`, `FAILED`, `COMPLETED`", + "description": "Overall ramp status. `COMPLETE` and `FAILED` are terminal; use this field, not `currentPhase`, to detect the end of a ramp.", + "enum": ["PENDING", "COMPLETE", "FAILED"], "type": "string" }, "StartKYC2Request": { From 1f6704d0c03bc67a833a3ee1a588be35d2429e8a Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:11:09 +0200 Subject: [PATCH 06/17] docs(repo): use current SDK names in the vortex-integration skill The skill still called listAlfredpayFiatAccounts, which no longer exists, passed an alpha-3 country, and read a nonexistent id field, so its sell recipe could not run. --- .agents/skills/vortex-integration/SKILL.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.agents/skills/vortex-integration/SKILL.md b/.agents/skills/vortex-integration/SKILL.md index c916bf590..8a867feb8 100644 --- a/.agents/skills/vortex-integration/SKILL.md +++ b/.agents/skills/vortex-integration/SKILL.md @@ -19,7 +19,7 @@ A machine-loadable capability catalog for AI coding agents integrating Vortex in - `pk_live_*` / `pk_test_*` — public value, sent as `X-Public-Key` for attribution and approved low-sensitivity reads. Quote/widget body `apiKey` remains compatibility transport; if both are present they must match. - `sk_live_*` / `sk_test_*` — secret value, sent only in `X-API-Key`. **Never expose `sk_*` in a browser or mobile app.** It is returned only when the credential is created. - If both values are configured, they must belong to the same credential or Vortex returns `403 CREDENTIAL_MISMATCH`. A valid secret may be used without a public value. - - **Ramp registration requires an authenticated profile in every corridor.** The SDK accepts either a secret credential for its bound profile or an `accessTokenProvider` for that profile's renewable Supabase Bearer session; raw API clients may use the secret credential or that Supabase Bearer session directly. Provider identity (BRL tax ID, Alfredpay customer, or Monerium profile) is derived from the authenticated profile, never from request fields. Shared dummy/ownerless profiles are invalid. + - **Ramp registration requires an authenticated profile in every corridor.** The SDK accepts either a secret credential for its bound profile or an `accessTokenProvider` for that profile's renewable Supabase Bearer session; raw API clients may use the secret credential or that Supabase Bearer session directly. Provider identity (BRL tax ID, bank-transfer customer, or Monerium profile) is derived from the authenticated profile, never from request fields. Shared dummy/ownerless profiles are invalid. - Profile-managed credentials use `POST/GET/DELETE /v1/api-credentials` with a Supabase Bearer session. One profile may have at most five active non-expired credentials; revoke by credential ID disables both values atomically with no DELETE body. - **Decimals**: all amounts are strings. Never parse them through JS `Number` — use `BigInt`, `decimal.js`, or equivalent. - **Quote TTL**: quotes expire (see `expiresAt`). Re-quote, never reuse stale quotes. @@ -351,7 +351,7 @@ The user wants to ramp USD, MXN, COP, or ARS over their domestic banking rail. R ## Prerequisites - The user completed KYC for the corridor's country via the Vortex app or Widget, and the SDK is authenticated with that user's own `sk_*` key or Supabase session. - Buy: `destinationAddress` (required); `fiatAccountId`, `walletAddress` optional. -- Sell: `fiatAccountId` and `walletAddress` (both required). List saved accounts with `vortex.listAlfredpayFiatAccounts(country)`. +- Sell: `fiatAccountId` and `walletAddress` (both required). List saved accounts with `vortex.listDomesticFiatAccounts(country)`. ## SDK recipe (onramp, MXN shown — substitute fiat + rail for USD/COP/ARS) ```js @@ -381,10 +381,10 @@ No user-signed on-chain transactions on buys. Unlike BRL there is no QR code — ## SDK recipe (offramp) ```js -const accounts = await vortex.listAlfredpayFiatAccounts("MEX"); +const accounts = await vortex.listDomesticFiatAccounts("MX"); const { rampProcess, unsignedTransactions } = await vortex.registerRamp(quote, { - fiatAccountId: accounts[0].id, + fiatAccountId: accounts[0].fiatAccountId, walletAddress: "0xUserWalletAddress" }); @@ -398,8 +398,8 @@ await vortex.startRamp(rampProcess.id); The SDK cannot **create** fiat accounts; they are created during onboarding in the Vortex app or Widget. `fiatAccountId` is opaque to the SDK. ## Common failures -- `MissingAlfredpayOnrampParametersError` / `MissingAlfredpayOfframpParametersError` — `destinationAddress`, `fiatAccountId`, or `walletAddress` missing. -- `AlfredpayOnrampKycRequiredError` — the authenticated user has no approved KYC for the corridor's country. +- `MissingDomesticOnrampParametersError` / `MissingDomesticOfframpParametersError` — `destinationAddress`, `fiatAccountId`, or `walletAddress` missing. +- `DomesticOnrampKycRequiredError` — the authenticated user has no approved KYC for the corridor's country. - `400` "requires an API key linked to a user" on register — the supplied API credential or Bearer session is not bound to an eligible profile. Authenticate as the onboarded user or provision a managed profile and issue a credential for that explicit subject. - `InsufficientBalanceError` — in the default `"prefunded"` mode, the offramp pre-flight found the source wallet balance below the quote's input amount. A deliberate register-then-fund integration may use `offrampFundingMode: "deferred"`; it must fund before submitting user transactions and starting the ramp. @@ -747,7 +747,7 @@ Include this payload (with secrets redacted) in any support ticket. | `InvalidNetworkError` | Network not in `Networks` enum | Use `discover-supported-corridors` | | `MissingRequiredFieldsError` / `MissingBrlParametersError` / `MissingBrlOfframpParametersError` | Body field missing | Fill the missing field; do not retry blindly | | `SubaccountNotFoundError` / `KycInvalidError` | BRL KYC issue | Direct user through KYC; do not retry programmatically | -| `AlfredpayOnrampKycRequiredError` | Bank-transfer-corridor KYC issue | Onboard or provision the credential's bound profile; do not retry programmatically | +| `DomesticOnrampKycRequiredError` | Bank-transfer-corridor KYC issue | Onboard or provision the credential's bound profile; do not retry programmatically | | Raw EUR registration `400` / `409` | Missing approved binding/profile or not exactly one Polygon EOA/IBAN match | Provision or reconcile the user out of band; do not submit caller-selected provider identity | | `VortexSdkError` with `code === "CREDENTIAL_MISMATCH"` | Configured public and secret values belong to different credentials | Load both values from the same credential; never infer pairing by name | | `VortexSdkError` with `code === "provider_limit_exceeded"` | The provider account limit is exhausted | Stop retrying registration; wait for provider capacity to reset or contact Vortex support | From 206a11fe26535e9b42b0dee352e6bf1488c895a1 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:11:10 +0200 Subject: [PATCH 07/17] docs(sdk): drop the payment partner's name from the README Public docs name corridors, not providers, and the fiat-account note now points to the SDK method that returns the ID. --- packages/sdk/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 888d966d6..d5c48b1cf 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -54,7 +54,7 @@ console.log("Please do the pix transfer using the following code: ", depositQrCo const startedRamp = await sdk.startRamp(rampProcess.id); ``` -### Alfredpay (USD / MXN / COP / ARS) onramp +### Bank-transfer (USD / MXN / COP / ARS) onramp ```typescript import { VortexSdk, FiatToken, EvmToken, EPaymentMethod, Networks, RampDirection } from "@vortexfi/sdk"; @@ -88,7 +88,7 @@ console.log("Pay via:", rampProcess.achPaymentData); const startedRamp = await sdk.startRamp(rampProcess.id); ``` -Quotes can be requested without any key (anonymous rate discovery). Registering through the SDK requires either a `secretKey` or an `accessTokenProvider` to resolve to an onboarded user. A secret can be a user-scoped key or a partner key delegated to the user; a `publicKey` or partner-only secret key is insufficient. The same user must have completed Alfredpay KYC for the country, so registration resolves to that user's Alfredpay customer automatically. +Quotes can be requested without any key (anonymous rate discovery). Registering through the SDK requires either a `secretKey` or an `accessTokenProvider` to resolve to an onboarded user. A secret can be a user-scoped key or a partner key delegated to the user; a `publicKey` or partner-only secret key is insufficient. The same user must have completed KYC for the country, so registration resolves to that user's verified payment profile automatically. Use `sdk.getRampInfo()` to read the credential-bound, sanitized KYC and buy/sell availability by country. It accepts either configured key and returns no identifiers, limits, or personal data. @@ -110,7 +110,7 @@ const sdk = new VortexSdk({ The provider is awaited before every API request, so tokens refreshed after SDK construction are used automatically. If both `secretKey` and `accessTokenProvider` are configured, the secret key takes precedence and the provider is not called. A configured `publicKey` continues to be sent for attribution with either authentication mechanism. -### Alfredpay (USD / MXN / COP / ARS) offramp +### Bank-transfer (USD / MXN / COP / ARS) offramp ```typescript const quote = await sdk.createQuote({ @@ -124,7 +124,7 @@ const quote = await sdk.createQuote({ }); const { rampProcess, unsignedTransactions } = await sdk.registerRamp(quote, { - fiatAccountId: "", + fiatAccountId: "", walletAddress: "0x1234567890123456789012345678901234567890" }); @@ -137,7 +137,7 @@ await sdk.submitUserTransactions(rampProcess.id, unsignedTransactions, { const startedRamp = await sdk.startRamp(rampProcess.id); ``` -> `fiatAccountId` is opaque to the SDK. It is required for offramp and optional for onramp. Consumers create or look up the user's Alfredpay fiat account out-of-band (via the Vortex backend) and pass the ID in. +> `fiatAccountId` is opaque to the SDK. It is required for offramp and optional for onramp. The account is created during onboarding; look up its `fiatAccountId` with `sdk.listDomesticFiatAccounts(country)` and pass it in. ### Deferred offramp funding From 8d2b857c6405cea9c1d615f59e0c416bc7d7237a Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:37:54 +0200 Subject: [PATCH 08/17] docs(api): state EUR availability once and name the provider neutrally Pages disagreed on EUR: some described out-of-band provisioning with no SDK support, others a live SDK flow. The published SDK 0.9.0 has no EUR support and production is not yet activated, so every page now says sandbox now, production pending, SDK support in the next release. The same passages now say 'EUR provider' in prose while released routes, error codes and phase values keep their names, as recorded in the README exceptions. --- docs/api/README.md | 3 +++ docs/api/openapi/vortex.openapi.d.ts | 4 ++-- docs/api/openapi/vortex.openapi.json | 4 ++-- docs/api/pages/01-overview.md | 6 +++--- docs/api/pages/02-quick-start-with-the-sdk.md | 2 +- docs/api/pages/04-ramp-lifecycle.md | 2 +- docs/api/pages/09-fiat-corridors.md | 8 ++++---- docs/api/pages/12-ai-agent-integration.md | 6 ++++-- 8 files changed, 20 insertions(+), 15 deletions(-) diff --git a/docs/api/README.md b/docs/api/README.md index 1a7cabc2b..0786030cb 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -78,6 +78,9 @@ Deliberate exceptions, all of them things a rename would break or misrepresent: - Internal provider-protocol types, service files, workers, and log lines. These model a specific third party's API and are never returned to a partner. - `Sumsub`, which is the caller's own vendor rather than a Vortex payment partner. +- The EUR corridor's released API names: the `/v1/monerium/*` and `/v1/monerium-b2b/*` routes, + `MONERIUM_*` error codes, `monerium*` phase values, and the SDK's `Monerium*` error classes. + Prose around them still says "EUR provider". Deprecated provider-named SDK aliases stay exported until the next major release. diff --git a/docs/api/openapi/vortex.openapi.d.ts b/docs/api/openapi/vortex.openapi.d.ts index cf8ea1cf3..065cbe2c6 100644 --- a/docs/api/openapi/vortex.openapi.d.ts +++ b/docs/api/openapi/vortex.openapi.d.ts @@ -1021,7 +1021,7 @@ export interface paths { }; /** * Discover KYC or KYB requirements - * @description Returns versioned document and ordered action metadata for an existing supported onboarding flow. GET operations, status polling, and readiness checks are intentionally omitted and remain documented in the integration guides and OpenAPI. Request fields and bodies are defined only by the referenced OpenAPI schemas and are not duplicated at the top level. This endpoint does not return profile state or customer PII. Monerium is outside this discovery proposal. + * @description Returns versioned document and ordered action metadata for an existing supported onboarding flow. GET operations, status polling, and readiness checks are intentionally omitted and remain documented in the integration guides and OpenAPI. Request fields and bodies are defined only by the referenced OpenAPI schemas and are not duplicated at the top level. This endpoint does not return profile state or customer PII. EUR onboarding is not part of discovery; it runs in the Dashboard or Widget. */ get: operations["getOnboardingRequirements"]; put?: never; @@ -3047,7 +3047,7 @@ export interface components { /** @enum {string} */ provider: "alfredpay" | "avenia" | "monerium" | "mykobo"; rail: string | null; - /** @description EUR onramp readiness of an approved Monerium account, measured against the chain the active onramp mints on. Null for other providers, for non-approved accounts, and when the account's OAuth session must be renewed (see error). */ + /** @description EUR onramp readiness of an approved EUR provider account, measured against the chain the active onramp mints on. Null for other providers, for non-approved accounts, and when the account's OAuth session must be renewed (see error). */ ramp: Record & (null | { chain: string; /** @enum {string} */ diff --git a/docs/api/openapi/vortex.openapi.json b/docs/api/openapi/vortex.openapi.json index 6224c2ecc..3f6170102 100644 --- a/docs/api/openapi/vortex.openapi.json +++ b/docs/api/openapi/vortex.openapi.json @@ -3032,7 +3032,7 @@ "type": ["string", "null"] }, "ramp": { - "description": "EUR onramp readiness of an approved Monerium account, measured against the chain the active onramp mints on. Null for other providers, for non-approved accounts, and when the account's OAuth session must be renewed (see error).", + "description": "EUR onramp readiness of an approved EUR provider account, measured against the chain the active onramp mints on. Null for other providers, for non-approved accounts, and when the account's OAuth session must be renewed (see error).", "oneOf": [ { "type": "null" @@ -8591,7 +8591,7 @@ }, "/v1/onboarding/requirements": { "get": { - "description": "Returns versioned document and ordered action metadata for an existing supported onboarding flow. GET operations, status polling, and readiness checks are intentionally omitted and remain documented in the integration guides and OpenAPI. Request fields and bodies are defined only by the referenced OpenAPI schemas and are not duplicated at the top level. This endpoint does not return profile state or customer PII. Monerium is outside this discovery proposal.", + "description": "Returns versioned document and ordered action metadata for an existing supported onboarding flow. GET operations, status polling, and readiness checks are intentionally omitted and remain documented in the integration guides and OpenAPI. Request fields and bodies are defined only by the referenced OpenAPI schemas and are not duplicated at the top level. This endpoint does not return profile state or customer PII. EUR onboarding is not part of discovery; it runs in the Dashboard or Widget.", "operationId": "getOnboardingRequirements", "parameters": [ { diff --git a/docs/api/pages/01-overview.md b/docs/api/pages/01-overview.md index 48c29f39e..2197e188d 100644 --- a/docs/api/pages/01-overview.md +++ b/docs/api/pages/01-overview.md @@ -6,7 +6,7 @@ These docs are written for partner developers integrating Vortex into a backend, ## Supported Corridors -The current SDK release supports BRL/PIX and bank-transfer corridors for USD (ACH), MXN (SPEI), COP (ACH), and ARS (CBU), where enabled by country and route configuration. The backend API also supports EUR/SEPA buys for pre-provisioned approved users through a direct integration that collects a typed-data signature from the user's linked owner wallet. The current SDK, Widget, and Dashboard do not implement that EUR signing journey, and EUR sells are unavailable. AssetHub is not available for these corridors. Other fiat currencies are exposed through reference data endpoints and are added incrementally. +The current SDK release supports BRL/PIX and bank-transfer corridors for USD (ACH), MXN (SPEI), COP (ACH), and ARS (CBU), where enabled by country and route configuration. EUR/SEPA buys are available in sandbox through the direct API, the Dashboard, and the Widget; production activation is pending, and SDK support ships with the next `@vortexfi/sdk` release (0.9.0 does not support EUR). The user onboards with the EUR provider and links the paying wallet in the Dashboard or Widget, and that wallet signs a typed-data permit for each buy. EUR sells are unavailable. AssetHub is not available for these corridors. Other fiat currencies are exposed through reference data endpoints and are added incrementally. For crypto, Vortex supports USDC and USDT across the listed EVM networks plus USDC on AssetHub. Stablecoin pegs and routes are subject to liquidity on the Nabla AMM and the wider Pendulum/Hydration corridor. @@ -17,11 +17,11 @@ Every Vortex ramp follows the same shape: 1. **Quote** — your application requests pricing for a route. 2. **Register** — your application creates per-chain ephemeral accounts and submits their public addresses with the quote ID. Vortex returns one or more **unsigned** transactions that move funds through the ramp. 3. **Sign and update** — your application signs each unsigned transaction with the correct key (ephemeral key for SDK-controlled accounts, user wallet for the user's funds) and submits the signed payloads back to Vortex. -4. **Settle fiat** — on buys, the user pays on the corridor's rail (a PIX QR for BRL, a SEPA transfer for the direct-API EUR flow, bank transfer instructions for USD/MXN/COP/ARS); on supported sells, Vortex pays out on that rail after settlement. +4. **Settle fiat** — on buys, the user pays on the corridor's rail (a PIX QR for BRL, a SEPA transfer for EUR, bank transfer instructions for USD/MXN/COP/ARS); on supported sells, Vortex pays out on that rail after settlement. 5. **Start** — your application calls start once signatures and fiat payment are in place. 6. **Track** — Vortex drives the on-chain phase machine. Your application listens via webhooks or polls the ramp status endpoint. -The SDK wraps steps 2, 3, and parts of 5 for supported flows. Direct API integrations must implement them explicitly. EUR BUY additionally needs the Monerium-linked wallet's typed-data permit, which the SDK returns as a user-owned transaction. +The SDK wraps steps 2, 3, and parts of 5 for supported flows. Direct API integrations must implement them explicitly. EUR BUY additionally needs a typed-data permit from the wallet linked with the EUR provider; from its next release, the SDK returns that permit as a user-owned transaction. ## Recommended Integration Paths diff --git a/docs/api/pages/02-quick-start-with-the-sdk.md b/docs/api/pages/02-quick-start-with-the-sdk.md index da2f73621..01fc81d5f 100644 --- a/docs/api/pages/02-quick-start-with-the-sdk.md +++ b/docs/api/pages/02-quick-start-with-the-sdk.md @@ -177,7 +177,7 @@ Quotes can be requested without any key (anonymous rate discovery). Registering The SDK cannot mint credentials or run KYC. Onboard the real user through the Vortex app or Widget, or use Vortex's managed-profile workflow, then use a credential bound to that profile. The secret is shown only once at creation; see [Authentication And API Credentials](https://api-docs.vortexfinance.co/authentication-and-partner-keys). This applies to buys and sells in all four bank-transfer corridors. -EUR/SEPA BUY works through the SDK once the user is onboarded with Monerium and has linked the paying wallet (dashboard or widget): pass that wallet as `walletAddress`, then sign the returned owner permit with `submitUserTransactions` before the SEPA instructions are released. See [Fiat Corridors](https://api-docs.vortexfinance.co/fiat-corridors). EUR SELL is unavailable. +EUR/SEPA BUY is available in sandbox, with production activation pending. SDK support ships with the next `@vortexfi/sdk` release; 0.9.0 does not support EUR. With that release, once the user is onboarded with the EUR provider and has linked the paying wallet (Dashboard or Widget), pass that wallet as `walletAddress`, then sign the returned owner permit with `submitUserTransactions` before the SEPA instructions are released. See [Fiat Corridors](https://api-docs.vortexfinance.co/fiat-corridors). EUR SELL is unavailable. ### Offramp (Sell) diff --git a/docs/api/pages/04-ramp-lifecycle.md b/docs/api/pages/04-ramp-lifecycle.md index 8c7f3f0bb..214ef5a84 100644 --- a/docs/api/pages/04-ramp-lifecycle.md +++ b/docs/api/pages/04-ramp-lifecycle.md @@ -24,7 +24,7 @@ Only public addresses are sent to Vortex. The matching ephemeral secret keys mus Use `POST /v1/ramp/update` to submit signed transactions and route-specific transaction hashes. -The SDK performs this automatically for the buy flows it supports. Direct API integrations must ensure that each signature or transaction hash matches the transaction returned by Vortex for the same ramp and phase. EUR BUY requires both the profile-linked owner's typed-data permit and the ephemeral-owned signatures returned at registration; the SDK signs the ephemeral set and returns the permit as a user-owned transaction. +The SDK performs this automatically for the buy flows it supports. Direct API integrations must ensure that each signature or transaction hash matches the transaction returned by Vortex for the same ramp and phase. EUR BUY requires both the profile-linked owner's typed-data permit and the ephemeral-owned signatures returned at registration; from its next release (0.9.0 has no EUR support), the SDK signs the ephemeral set and returns the permit as a user-owned transaction. On buys, the fiat payment instructions (`depositQrCode` for BRL, `ibanPaymentData` for EUR) are withheld until the presigned transactions pass validation: they are released on the update response and on `GET /v1/ramp/{id}`, not on the register response. SDK integrations receive supported-corridor instructions directly from `registerRamp`, which performs the update internally. For EUR, the owner permit must also be submitted (`submitUserTransactions` in the SDK, or the update endpoint directly) before `ibanPaymentData` is released. For USD, MXN, COP, and ARS, the first update response returns the bank transfer instructions as `achPaymentData`, which `GET /v1/ramp/{id}` also returns afterwards. diff --git a/docs/api/pages/09-fiat-corridors.md b/docs/api/pages/09-fiat-corridors.md index e226e44f3..a0088d77c 100644 --- a/docs/api/pages/09-fiat-corridors.md +++ b/docs/api/pages/09-fiat-corridors.md @@ -29,7 +29,7 @@ Pin the `requirementsVersion` you integrated against and re-check discovery when | `MX` | `api` | `api` | | `US` | `hosted` | `hosted` | -EUR onboarding is not part of discovery. The active EUR ramp accepts only users whose approved provider profile, Polygon EOA, and IBAN were provisioned out of band and bound to their Vortex legal entity. Automated onboarding, wallet linking, IBAN provisioning, and external-user import are not part of the current integration. Provider state is always authoritative: no discovery step, client notification, or completion event can mark a verification approved. +EUR onboarding is not part of discovery. Users onboard with the EUR provider, link their paying Polygon wallet, and receive their IBAN in the Dashboard or Widget; the API does not run these steps or import external profiles (see EUR (SEPA) below). Provider state is always authoritative: no discovery step, client notification, or completion event can mark a verification approved. ## BRL (PIX) @@ -183,12 +183,12 @@ Authenticated clients can request account limits with `POST /v1/limits`, passing EUR uses the `"sepa"` rail identifier. New EUR BUY quotes use a Polygon source route and deliver to supported EVM destinations, Polygon included. AssetHub is not available as a destination for this flow. New EUR SELL quotes are rejected. -EUR BUY is supported through the SDK, the direct API, the Dashboard, and the Widget. Registration requires the normal quote ID, a fresh EVM signing account, `additionalData.destinationAddress`, and (for the SDK) `walletAddress`, the wallet linked to the user's Monerium profile. Supply `additionalData.customerType` (`"individual"` or `"business"`) to select the same legal profile used for onboarding and wallet linking. The SDK exposes this as optional `EurOnrampAdditionalData.customerType`; it becomes required when both legal types have Monerium profiles, otherwise the API returns `409 MONERIUM_CUSTOMER_TYPE_REQUIRED`. Profile UUID, address, and IBAN are still derived server-side and caller-supplied identity fields are rejected. A user without a Monerium binding gets `MONERIUM_ONBOARDING_REQUIRED`; a user whose backend Monerium session expired gets `MONERIUM_REAUTHENTICATION_REQUIRED` and must reconnect Monerium in the Dashboard or Widget. +EUR BUY is available in sandbox through the direct API, the Dashboard, and the Widget; production activation is pending. SDK support ships with the next `@vortexfi/sdk` release (0.9.0 does not support EUR). Registration requires the normal quote ID, a fresh EVM signing account, `additionalData.destinationAddress`, and (for the SDK) `walletAddress`, the wallet linked to the user's EUR provider profile. Supply `additionalData.customerType` (`"individual"` or `"business"`) to select the same legal profile used for onboarding and wallet linking. The SDK exposes this as optional `EurOnrampAdditionalData.customerType`; it becomes required when both legal types have EUR provider profiles, otherwise the API returns `409 MONERIUM_CUSTOMER_TYPE_REQUIRED`. Profile UUID, address, and IBAN are still derived server-side and caller-supplied identity fields are rejected. A user without a EUR provider binding gets `MONERIUM_ONBOARDING_REQUIRED`; a user whose backend EUR provider session expired gets `MONERIUM_REAUTHENTICATION_REQUIRED` and must reconnect the EUR provider in the Dashboard or Widget. -Registration succeeds only for a pre-provisioned individual or business legal entity with an approved local EUR provider binding, a live approved provider profile, and exactly one existing Polygon EOA/IBAN destination. The user must control that linked EOA. +Registration succeeds only for an individual or business legal entity with an approved local EUR provider binding, a live approved provider profile, and exactly one existing Polygon EOA/IBAN destination. The user must control that linked EOA. The register response includes unsigned ephemeral transactions and an EIP-712 permit whose signer is the linked owner EOA. Route transactions by `signer`: sign ephemeral-owned entries with the fresh ephemeral key and send the permit to the owner's wallet. Submit the complete signed set to `POST /v1/ramp/update`. Only then does `ibanPaymentData` expose the IBAN, receiver name, BIC, and payment reference. Display those values verbatim, have the user initiate the SEPA transfer, and call `POST /v1/ramp/start` before the ramp's start deadline. -Onboarding, wallet linking, and IBAN provisioning happen in the Dashboard or Widget (Monerium OAuth, then linking the paying wallet); the API does not import external profiles or manage KYC/KYB lifecycle state. Moving an existing IBAN to a new wallet requires a separate, informed confirmation because future deposits to that IBAN will mint to the new wallet and other services may rely on the old one. Those setup steps must be complete before registration. The owner permit expires one week after preparation (the swap presign deadline); late settlement can require manual resolution. +Onboarding, wallet linking, and IBAN provisioning happen in the Dashboard or Widget (signing in with the EUR provider, then linking the paying wallet); the API does not import external profiles or manage KYC/KYB lifecycle state. Moving an existing IBAN to a new wallet requires a separate, informed confirmation because future deposits to that IBAN will mint to the new wallet and other services may rely on the old one. Those setup steps must be complete before registration. The owner permit expires one week after preparation (the swap presign deadline); late settlement can require manual resolution. --- diff --git a/docs/api/pages/12-ai-agent-integration.md b/docs/api/pages/12-ai-agent-integration.md index df2af9796..4c93bee98 100644 --- a/docs/api/pages/12-ai-agent-integration.md +++ b/docs/api/pages/12-ai-agent-integration.md @@ -22,7 +22,7 @@ When you point an AI coding agent at Vortex: | Browser, mobile, or WebView preferring a hosted UX and hosted custody | Use the [Vortex Widget](https://api-docs.vortexfinance.co/widget-integration). | | Anything else (Go, Rust, Elixir, Java, Ruby, PHP, .NET, Deno, edge runtimes, …) | Reimplement the SDK behavior against the raw API as described in Section D below. | -The SDK paths support BRL (PIX), USD (ACH), MXN (SPEI), COP, and ARS (CBU). EUR (SEPA) BUY currently requires a direct API integration because the linked owner must sign typed data; EUR SELL is unavailable. See [Fiat Corridors](https://api-docs.vortexfinance.co/fiat-corridors) for per-corridor requirements. +The SDK paths support BRL (PIX), USD (ACH), MXN (SPEI), COP, and ARS (CBU). EUR (SEPA) BUY is available in sandbox, with production activation pending; until the next `@vortexfi/sdk` release it requires a direct API integration in which the linked owner wallet signs a typed-data permit. EUR SELL is unavailable. See [Fiat Corridors](https://api-docs.vortexfinance.co/fiat-corridors) for per-corridor requirements. Ramping requires an onboarded (KYC/KYB-approved) user. Onboarding is a separate, corridor-specific flow that most corridors also expose through the API — see Section H before assuming the app or Widget is required. @@ -49,6 +49,8 @@ Then, for each ramp: 5. **Start.** `sdk.startRamp()` or `POST /v1/ramp/start`, before the `expiresAt` returned by register and update (15 minutes after registration). After that deadline, update and start are refused. 6. **Track.** Poll `GET /v1/ramp/{id}` until `status` is `COMPLETE` or `FAILED`, or register a webhook (Section D.6). +EUR buys are available in sandbox only for now, and until the next SDK release they use the API calls above rather than the SDK methods. + Signing in step 3 is the one case where a server signs the user-owned transactions that Section D.4 routes to the user's wallet: the funds are your own. Keep that wallet key in a secret manager, separate from the per-ramp ephemeral keys. ## C. Python (`vortex-sdk-python`) @@ -195,7 +197,7 @@ On a **supported sell**, the user signs the user-owned transaction(s), you submi ## E. Mandatory Client Responsibilities -These are not optional. The SDK handles them for supported corridors; a custom client must implement them explicitly. The current EUR BUY flow is one such custom-client path. +These are not optional. The SDK handles them for supported corridors; a custom client must implement them explicitly. Until the next SDK release, EUR BUY is one such custom-client path. 1. **Ephemeral key custody.** Generate fresh per-ramp keypairs. Store them encrypted, keyed by `rampId`. Keep them until the ramp is `COMPLETE` or `FAILED` **and** any recovery window has passed. Never transmit secrets to Vortex, support, logs, or analytics. See [Ephemeral Key Custody](https://api-docs.vortexfinance.co/ephemeral-key-custody). 2. **Payload validation before signing.** Every field that affects funds movement must match what your application requested. From 70da01176155804ef82295de8bc51aa97cf27041 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:37:55 +0200 Subject: [PATCH 09/17] docs(sdk): name the EUR provider neutrally in the README --- packages/sdk/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index d5c48b1cf..15a3dee2a 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -203,11 +203,11 @@ Gets the current status of a ramp process. ##### `registerRamp(quote: Q, additionalData: RegisterRampAdditionalData): Promise<{ rampProcess: RampProcess; unsignedTransactions: UnsignedTx[] }>` Registers a new ramp process. Creates fresh Substrate and EVM ephemeral accounts, submits the quote and ephemeral addresses to the API, then signs and submits the returned ephemeral-owned transactions. Returns the ramp process and the user-owned `unsignedTransactions` that the caller must sign or broadcast. -For EUR/SEPA BUY, pass `walletAddress`: the wallet linked to the user's Monerium profile (see the +For EUR/SEPA BUY, pass `walletAddress`: the wallet linked to the user's EUR provider profile (see the Fiat Corridors guide). The backend mints EURe to that wallet and returns its ERC-2612 permit as a user-owned typed-data transaction in `unsignedTransactions`; sign and submit it with `submitUserTransactions` (or `getTypedDataToSign` + `submitUserSignature`) before the SEPA -instructions (`ibanPaymentData`) are released. The user must already be onboarded with Monerium +instructions (`ibanPaymentData`) are released. The user must already be onboarded with the EUR provider and have that wallet linked; otherwise registration fails with `MoneriumOnboardingRequiredError` or `MoneriumReauthenticationRequiredError`. EUR SELL is unavailable for new quotes. From 4435e3662374d4188803834c3687b87383a03b65 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 15:37:56 +0200 Subject: [PATCH 10/17] docs(repo): align the integration skill with EUR availability The skill told agents EUR BUY is active through the SDK, which neither npm 0.9.0 nor production supports yet. --- .agents/skills/vortex-integration/SKILL.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.agents/skills/vortex-integration/SKILL.md b/.agents/skills/vortex-integration/SKILL.md index 8a867feb8..7b95a4e21 100644 --- a/.agents/skills/vortex-integration/SKILL.md +++ b/.agents/skills/vortex-integration/SKILL.md @@ -19,12 +19,12 @@ A machine-loadable capability catalog for AI coding agents integrating Vortex in - `pk_live_*` / `pk_test_*` — public value, sent as `X-Public-Key` for attribution and approved low-sensitivity reads. Quote/widget body `apiKey` remains compatibility transport; if both are present they must match. - `sk_live_*` / `sk_test_*` — secret value, sent only in `X-API-Key`. **Never expose `sk_*` in a browser or mobile app.** It is returned only when the credential is created. - If both values are configured, they must belong to the same credential or Vortex returns `403 CREDENTIAL_MISMATCH`. A valid secret may be used without a public value. - - **Ramp registration requires an authenticated profile in every corridor.** The SDK accepts either a secret credential for its bound profile or an `accessTokenProvider` for that profile's renewable Supabase Bearer session; raw API clients may use the secret credential or that Supabase Bearer session directly. Provider identity (BRL tax ID, bank-transfer customer, or Monerium profile) is derived from the authenticated profile, never from request fields. Shared dummy/ownerless profiles are invalid. + - **Ramp registration requires an authenticated profile in every corridor.** The SDK accepts either a secret credential for its bound profile or an `accessTokenProvider` for that profile's renewable Supabase Bearer session; raw API clients may use the secret credential or that Supabase Bearer session directly. Provider identity (BRL tax ID, bank-transfer customer, or EUR provider profile) is derived from the authenticated profile, never from request fields. Shared dummy/ownerless profiles are invalid. - Profile-managed credentials use `POST/GET/DELETE /v1/api-credentials` with a Supabase Bearer session. One profile may have at most five active non-expired credentials; revoke by credential ID disables both values atomically with no DELETE body. - **Decimals**: all amounts are strings. Never parse them through JS `Number` — use `BigInt`, `decimal.js`, or equivalent. - **Quote TTL**: quotes expire (see `expiresAt`). Re-quote, never reuse stale quotes. - **Presigned counts**: this is **per ephemeral-signed transaction, not per ramp**. Each transaction an ephemeral key signs must be submitted as 5 presigned variants — 1 primary plus exactly 4 backups with consecutive nonces in `meta.additionalTxs` (`NUMBER_OF_PRESIGNED_TXS = 5`); the API rejects any other backup count. A ramp can contain several ephemeral-signed transactions across its phases. (The SDK builds these for you; only raw-API integrations need to construct them.) -- **Currently implemented SDK corridors**: BRL via PIX, USD via ACH, MXN via SPEI, COP via ACH, and ARS via CBU support BUY and SELL; EUR via SEPA (Monerium) supports BUY only. EUR BUY needs `walletAddress` (the user's Monerium-linked wallet, linked in the Dashboard or Widget) and the returned owner permit signed through `submitUserTransactions`. Supply `customerType` to select the same individual or business Monerium profile used at onboarding; it is required when both types are bound (`MONERIUM_CUSTOMER_TYPE_REQUIRED` otherwise). `MONERIUM_ONBOARDING_REQUIRED` / `MONERIUM_REAUTHENTICATION_REQUIRED` mean the user must (re)connect Monerium first. These corridors deliver to EVM networks only (no AssetHub). +- **Currently implemented SDK corridors**: BRL via PIX, USD via ACH, MXN via SPEI, COP via ACH, and ARS via CBU support BUY and SELL; EUR via SEPA supports BUY only, is available in sandbox with production activation pending, and needs the next `@vortexfi/sdk` release (0.9.0 has no EUR support; use the direct API until then). EUR BUY needs `walletAddress` (the wallet linked with the EUR provider in the Dashboard or Widget) and the returned owner permit signed through `submitUserTransactions`. Supply `customerType` to select the same individual or business EUR provider profile used at onboarding; it is required when both types are bound (`MONERIUM_CUSTOMER_TYPE_REQUIRED` otherwise). `MONERIUM_ONBOARDING_REQUIRED` / `MONERIUM_REAUTHENTICATION_REQUIRED` mean the user must (re)connect the EUR provider first. These corridors deliver to EVM networks only (no AssetHub). - **EUR currency value**: TypeScript uses the member `FiatToken.EURC`, which serializes to the wire value `"EUR"`. Raw JSON clients must send `"EUR"`, with `"sepa"` as the rail identifier. - **taxId is deprecated for BRL**: the user's tax ID is derived server-side from the authenticated profile. Sending a `taxId` that mismatches the derived one is rejected; stop sending it in new integrations. - **Deferred offramp funding**: the SDK checks the source wallet balance at `registerRamp` by default. Server integrations that register before funding a temporary wallet may configure `offrampFundingMode: "deferred"`. This skips only the SDK pre-flight; fund the exact `walletAddress` before signing/submitting user transactions, then update and start before the registration window expires. Backend execution-time balance checks remain authoritative. @@ -267,22 +267,23 @@ triggers: ## When to use The user wants to buy crypto with EUR and is already corridor-ready: an approved Vortex EUR provider binding, a live approved provider profile, exactly one existing Polygon EOA/IBAN destination, and access to that EOA for typed-data signing. Both individual and business legal entities may qualify. The active route delivers to supported EVM destinations, Polygon included. -Users become corridor-ready by completing Monerium OAuth onboarding in the Dashboard or Widget and linking the wallet they will pay in with (`POST /v1/monerium/wallet`); this flow does not cover onboarding, wallet linking, or IBAN provisioning. EUR SELL is unavailable. +EUR BUY is available in sandbox; production activation is pending. Users become corridor-ready by completing the EUR provider's OAuth onboarding in the Dashboard or Widget and linking the wallet they will pay in with (`POST /v1/monerium/wallet`); this flow does not cover onboarding, wallet linking, or IBAN provisioning. EUR SELL is unavailable. ## Prerequisites - Quote with TypeScript member `inputCurrency: FiatToken.EURC` (raw JSON value `"EUR"`), `from: "sepa"`, and a supported EVM destination. - A secret credential or Supabase session for the corridor-ready legal entity. -- `additionalData.destinationAddress`; do not submit profile, Monerium address, or IBAN identity. +- `additionalData.destinationAddress`; do not submit profile, provider address, or IBAN identity. - `additionalData.customerType` (`"individual"` or `"business"`) when the user owns both legal profiles; use the same type as onboarding and wallet linking. - A fresh EVM ephemeral key and a wallet-signing channel for the profile-linked Polygon owner. ## SDK recipe +Requires the next `@vortexfi/sdk` release; 0.9.0 has no EUR support. ```js -// walletAddress must be the wallet linked to the Monerium profile; a mismatch throws EurOnrampError. +// walletAddress must be the wallet linked to the EUR provider profile; a mismatch throws EurOnrampError. const { rampProcess, unsignedTransactions } = await vortex.registerRamp(quote, { customerType: "individual", destinationAddress: "0xDestinationWallet", - walletAddress: "0xMoneriumLinkedWallet" + walletAddress: "0xProviderLinkedWallet" }); // unsignedTransactions holds the owner's EIP-712 permit; the ephemeral txs are signed by the SDK. @@ -313,7 +314,7 @@ The permit expires one week after preparation. If SEPA settlement arrives after consumes its nonce, automatic execution stops for manual resolution. ## Common failures -- `400` approved-profile error: the effective legal entity has no approved local Monerium/EUR binding or the live provider profile is not approved. +- `400` approved-profile error: the effective legal entity has no approved local EUR provider binding or the live provider profile is not approved. - `409 MONERIUM_CUSTOMER_TYPE_REQUIRED`: both legal types are bound; repeat registration with the type used for wallet linking. - `409` expected-one-destination error: the profile does not have exactly one matching Polygon EOA/IBAN destination. Vortex does not create, select, or move one in this release. - Contract-wallet error: the linked mint destination must be an EOA for the ERC-2612 handoff. @@ -697,7 +698,7 @@ try { ## Current corridor reality (August 2026) - **BRL via PIX**: onramp and offramp both live. `taxId` deprecated — derived from the user-linked key. -- **EUR via SEPA**: BUY is active (`FiatToken.EURC`, rail `"sepa"`) for an approved Monerium user with one Polygon EOA/IBAN destination, through the SDK (`walletAddress` + `submitUserTransactions` for the owner permit), the Widget, the Dashboard, and the direct API. Onboarding and wallet linking happen in the Dashboard or Widget. Destinations: any supported EVM network. SELL is unavailable. +- **EUR via SEPA**: BUY is available in sandbox, production activation pending (`FiatToken.EURC`, rail `"sepa"`), for an approved EUR provider user with one Polygon EOA/IBAN destination, through the SDK from its next release (`walletAddress` + `submitUserTransactions` for the owner permit), the Widget, the Dashboard, and the direct API. Onboarding and wallet linking happen in the Dashboard or Widget. Destinations: any supported EVM network. SELL is unavailable. - **USD (ACH) / MXN (SPEI) / COP (ACH) / ARS (CBU)**: onramp and offramp live via the AlfredPay corridor; registration requires an authenticated user identity. Route resolver determines availability per-combination. - Live corridors deliver to EVM networks; AssetHub ramp execution is currently disabled. From 718e64cba7e7aa95f2338b72851abb51a6b308df Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 16:01:12 +0200 Subject: [PATCH 11/17] docs(api): mark business EUR accounts as sandbox-only for now The business EUR onramp account and its deposit events share the EUR provider activation that production is still waiting on, so they carry the same sandbox note as EUR buys. --- docs/api/openapi/vortex.openapi.d.ts | 4 ++-- docs/api/openapi/vortex.openapi.json | 6 +++--- docs/api/pages/07-webhooks.md | 4 +++- docs/api/pages/14-managed-profiles.md | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/api/openapi/vortex.openapi.d.ts b/docs/api/openapi/vortex.openapi.d.ts index 065cbe2c6..83c45cc73 100644 --- a/docs/api/openapi/vortex.openapi.d.ts +++ b/docs/api/openapi/vortex.openapi.d.ts @@ -957,7 +957,7 @@ export interface paths { }; /** * Get the acting profile's EUR onramp account - * @description Returns the acting profile's business EUR onramp account: status, dedicated IBAN, forwarding contract, and payout configuration. A partner manager acts for a child via `X-Managed-Profile-Id` (EU corridor and business customer type policy applies), or the child's own credential authenticates directly. Strictly scoped to the acting profile; no account, profile, or IBAN selector is accepted. + * @description Available in sandbox; production activation is pending. Returns the acting profile's business EUR onramp account: status, dedicated IBAN, forwarding contract, and payout configuration. A partner manager acts for a child via `X-Managed-Profile-Id` (EU corridor and business customer type policy applies), or the child's own credential authenticates directly. Strictly scoped to the acting profile; no account, profile, or IBAN selector is accepted. * * **Auth:** `X-API-Key` or Supabase Bearer. */ @@ -979,7 +979,7 @@ export interface paths { }; /** * List the acting profile's EUR deposits - * @description Returns the acting profile's EUR deposits newest first, with every allocated conversion portion and aggregate attributed USDC. A per-swap cap can split one deposit across multiple executions. This is the polling surface for payment-received / converted status; the deposit webhook events cover push delivery. A partner manager acts for a child via `X-Managed-Profile-Id` (EU corridor and business customer type policy applies), or the child's own credential authenticates directly. Strictly scoped to the acting profile; no account, profile, or IBAN selector is accepted. + * @description Available in sandbox; production activation is pending. Returns the acting profile's EUR deposits newest first, with every allocated conversion portion and aggregate attributed USDC. A per-swap cap can split one deposit across multiple executions. This is the polling surface for payment-received / converted status; the deposit webhook events cover push delivery. A partner manager acts for a child via `X-Managed-Profile-Id` (EU corridor and business customer type policy applies), or the child's own credential authenticates directly. Strictly scoped to the acting profile; no account, profile, or IBAN selector is accepted. * * **Auth:** `X-API-Key` or Supabase Bearer. */ diff --git a/docs/api/openapi/vortex.openapi.json b/docs/api/openapi/vortex.openapi.json index 3f6170102..d76dff56b 100644 --- a/docs/api/openapi/vortex.openapi.json +++ b/docs/api/openapi/vortex.openapi.json @@ -8438,7 +8438,7 @@ "/v1/monerium-b2b/account": { "get": { "deprecated": false, - "description": "Returns the acting profile's business EUR onramp account: status, dedicated IBAN, forwarding contract, and payout configuration. A partner manager acts for a child via `X-Managed-Profile-Id` (EU corridor and business customer type policy applies), or the child's own credential authenticates directly. Strictly scoped to the acting profile; no account, profile, or IBAN selector is accepted.\n\n**Auth:** `X-API-Key` or Supabase Bearer.", + "description": "Available in sandbox; production activation is pending. Returns the acting profile's business EUR onramp account: status, dedicated IBAN, forwarding contract, and payout configuration. A partner manager acts for a child via `X-Managed-Profile-Id` (EU corridor and business customer type policy applies), or the child's own credential authenticates directly. Strictly scoped to the acting profile; no account, profile, or IBAN selector is accepted.\n\n**Auth:** `X-API-Key` or Supabase Bearer.", "operationId": "getMoneriumB2bAccount", "parameters": [ { @@ -8481,7 +8481,7 @@ "/v1/monerium-b2b/deposits": { "get": { "deprecated": false, - "description": "Returns the acting profile's EUR deposits newest first, with every allocated conversion portion and aggregate attributed USDC. A per-swap cap can split one deposit across multiple executions. This is the polling surface for payment-received / converted status; the deposit webhook events cover push delivery. A partner manager acts for a child via `X-Managed-Profile-Id` (EU corridor and business customer type policy applies), or the child's own credential authenticates directly. Strictly scoped to the acting profile; no account, profile, or IBAN selector is accepted.\n\n**Auth:** `X-API-Key` or Supabase Bearer.", + "description": "Available in sandbox; production activation is pending. Returns the acting profile's EUR deposits newest first, with every allocated conversion portion and aggregate attributed USDC. A per-swap cap can split one deposit across multiple executions. This is the polling surface for payment-received / converted status; the deposit webhook events cover push delivery. A partner manager acts for a child via `X-Managed-Profile-Id` (EU corridor and business customer type policy applies), or the child's own credential authenticates directly. Strictly scoped to the acting profile; no account, profile, or IBAN selector is accepted.\n\n**Auth:** `X-API-Key` or Supabase Bearer.", "operationId": "listMoneriumB2bDeposits", "parameters": [ { @@ -11019,7 +11019,7 @@ "properties": { "events": { "items": { - "description": "(optional): Array of event types to subscribe to. Transaction events [\"TRANSACTION_CREATED\", \"STATUS_CHANGE\"] are the default when omitted. The account-scoped deposit events [\"DEPOSIT_RECEIVED\", \"DEPOSIT_CONVERTED\"] must be requested explicitly, cannot be mixed with transaction events, require a profile-scoped secret credential, and take no quoteId/sessionId.", + "description": "(optional): Array of event types to subscribe to. Transaction events [\"TRANSACTION_CREATED\", \"STATUS_CHANGE\"] are the default when omitted. The account-scoped deposit events [\"DEPOSIT_RECEIVED\", \"DEPOSIT_CONVERTED\"] must be requested explicitly, cannot be mixed with transaction events, require a profile-scoped secret credential, and take no quoteId/sessionId. Deposit events are available in sandbox; production activation is pending.", "type": "string" }, "type": "array" diff --git a/docs/api/pages/07-webhooks.md b/docs/api/pages/07-webhooks.md index 0522f7cc2..a7931e091 100644 --- a/docs/api/pages/07-webhooks.md +++ b/docs/api/pages/07-webhooks.md @@ -6,7 +6,7 @@ You can subscribe to: - **Transaction creation** — a new ramp is registered. - **Status changes** — a ramp's status moves between `PENDING`, `COMPLETE`, and `FAILED`. -- **Deposit events** — for partner managers with business EUR onramp accounts: a client's EUR deposit was received (`DEPOSIT_RECEIVED`) or converted and forwarded (`DEPOSIT_CONVERTED`). See [Deposit Events](#deposit-events) — they follow account-scoped rules and durable delivery. +- **Deposit events** — for partner managers with business EUR onramp accounts: a client's EUR deposit was received (`DEPOSIT_RECEIVED`) or converted and forwarded (`DEPOSIT_CONVERTED`). See [Deposit Events](#deposit-events) — they follow account-scoped rules and durable delivery. Business EUR onramp accounts are available in sandbox; production activation is pending. ## Security Model @@ -105,6 +105,8 @@ Status values: ## Deposit Events +Business EUR onramp accounts, and with them deposit events, are available in sandbox; production activation is pending. + Managers whose business clients hold EUR onramp accounts can subscribe to deposit events instead of polling `GET /v1/monerium-b2b/deposits`. These subscriptions follow account-scoped rules: - Register with your **manager profile's own secret key** (no `X-Managed-Profile-Id` header, no `quoteId`/`sessionId`) and an explicit `events` list containing only deposit events. Mixing them with transaction events is rejected, as is a partner-scoped credential. diff --git a/docs/api/pages/14-managed-profiles.md b/docs/api/pages/14-managed-profiles.md index 4335fb2a4..cdb7369c6 100644 --- a/docs/api/pages/14-managed-profiles.md +++ b/docs/api/pages/14-managed-profiles.md @@ -15,7 +15,7 @@ Manager status is granted by Vortex, not self-service. During partner onboarding - **Allowed corridors** — the countries (`BR`, `AR`, `CO`, `MX`, `US`, `EU`) your children may operate in. - **Optional customer-type narrowing** — restrict children to `individual` or `business`; a null policy allows both wherever the corridor's canonical capability matrix does. -Every delegated operation re-checks this policy at request time, so a corridor removed from your manager record immediately blocks new mutations for children in that corridor (in-flight ramps continue). Automated EUR onboarding and provider binding are not available for managed children. A non-technical child that operations has already provisioned with an approved EUR provider binding, Polygon EOA, and IBAN may use the direct-API EUR BUY flow when the manager policy allows that corridor. The `EU` corridor also covers the dedicated business EUR onramp account surface (`GET /v1/monerium-b2b/account` and `GET /v1/monerium-b2b/deposits` under delegation or a child credential), available to business children whose accounts Vortex provisions during partner onboarding. +Every delegated operation re-checks this policy at request time, so a corridor removed from your manager record immediately blocks new mutations for children in that corridor (in-flight ramps continue). Automated EUR onboarding and provider binding are not available for managed children. A non-technical child that operations has already provisioned with an approved EUR provider binding, Polygon EOA, and IBAN may use the direct-API EUR BUY flow when the manager policy allows that corridor. The `EU` corridor also covers the dedicated business EUR onramp account surface (`GET /v1/monerium-b2b/account` and `GET /v1/monerium-b2b/deposits` under delegation or a child credential), available to business children whose accounts Vortex provisions during partner onboarding. EUR, including this business account surface, is available in sandbox; production activation is pending. ## Create A Managed Child From e8a13b5f78d4ba878e038ff5a22fb221af7e8c58 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 16:14:45 +0200 Subject: [PATCH 12/17] docs(api): complete the own-account setup and signing steps Review found the own-account path sent EUR users to production keys, skipped EUR wallet linking and IBAN provisioning, omitted the country for the payout-account lookup, did not say direct clients sign the ephemeral transactions, and assumed verification creates the payout account. --- docs/api/pages/09-fiat-corridors.md | 2 +- docs/api/pages/12-ai-agent-integration.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/api/pages/09-fiat-corridors.md b/docs/api/pages/09-fiat-corridors.md index a0088d77c..019ca81ab 100644 --- a/docs/api/pages/09-fiat-corridors.md +++ b/docs/api/pages/09-fiat-corridors.md @@ -167,7 +167,7 @@ Ramp registration resolves KYC and payment identity from the effective profile, ### Fiat Accounts -Sells pay out to a saved bank account referenced by `fiatAccountId` in the register call. It is required for sells and optional for buys. The account is created during onboarding in the Vortex app or Widget; the ID is opaque to the SDK and the API client. +Sells pay out to a saved bank account referenced by `fiatAccountId` in the register call. It is required for sells and optional for buys. Verification does not create it: add it after the corridor is verified, with **Add pay-out account** under Onboarding in the Dashboard, in the Widget, or with `POST /v1/domestic/fiatAccounts`. The ID is opaque to the SDK and the API client. ### Payment Instructions On Buys diff --git a/docs/api/pages/12-ai-agent-integration.md b/docs/api/pages/12-ai-agent-integration.md index 4c93bee98..831396a74 100644 --- a/docs/api/pages/12-ai-agent-integration.md +++ b/docs/api/pages/12-ai-agent-integration.md @@ -34,17 +34,17 @@ Do not expose an `sk_*` or reimplement signing against the raw ramp API in a bro Use this path when you ramp for yourself or your own business, for example from a trading bot, a treasury job, or a payout script. You need one onboarded profile and one secret key on your server. Managed child profiles, `X-Managed-Profile-Id`, and browser origin approval are not needed. -One-time setup, all in the Vortex dashboard (): +One-time setup, all in the Vortex dashboard: for production, or for sandbox. Each dashboard issues keys only for its own API (`https://api.vortexfinance.co` or `https://api-sandbox.vortexfinance.co`), and profiles and onboarding do not carry over between them. EUR is sandbox-only for now, so set up EUR in the sandbox dashboard. 1. Sign in with your email. Existing customers use the email of their onboarded profile. -2. Under **Onboarding**, complete KYC (individual) or KYB (business) for each corridor you will use. To sell into USD, MXN, COP, or ARS, also add the payout bank account there. Section H covers doing this through the API instead. +2. Under **Onboarding**, complete KYC (individual) or KYB (business) for each corridor you will use. To sell into USD, MXN, COP, or ARS, also click **Add pay-out account** for that corridor once it is verified. For EUR, onboarding with the EUR provider is not enough: also link the Polygon wallet you will pay from and wait until your IBAN is provisioned. Section H covers onboarding through the API for the corridors that support it; EUR setup is only available in the dashboard or Widget. 3. Under **API keys**, create a credential and store its secret key on your server. See [Authentication And API Keys](https://api-docs.vortexfinance.co/authentication-and-partner-keys). Then, for each ramp: 1. **Quote.** `sdk.createQuote()` or `POST /v1/quotes`. -2. **Register.** `sdk.registerRamp()` or `POST /v1/ramp/register`. On a buy, pass the receiving wallet as `destinationAddress` (for EUR, also your linked wallet as `walletAddress`). On a sell, pass your own wallet as `walletAddress`, plus `pixDestination` for BRL or the payout account's `fiatAccountId` for USD, MXN, COP, and ARS (`sdk.listDomesticFiatAccounts()` or `GET /v1/domestic/fiatAccounts` returns it). -3. **Sign and update.** The SDK signs the ephemeral transactions and submits them inside `registerRamp`. On a sell, and for the owner permit on an EUR buy, your wallet is the user wallet: validate the returned user transactions, sign or send them with your wallet key, and submit them with `sdk.submitUserTransactions()` or `POST /v1/ramp/update`. +2. **Register.** `sdk.registerRamp()` or `POST /v1/ramp/register`. On a buy, pass the receiving wallet as `destinationAddress` (for EUR, also your linked wallet as `walletAddress`). On a sell, pass your own wallet as `walletAddress`, plus `pixDestination` for BRL or the payout account's `fiatAccountId` for USD, MXN, COP, and ARS (`sdk.listDomesticFiatAccounts("MX")` or `GET /v1/domestic/fiatAccounts?country=MX` returns it; substitute your sell corridor's country: `US`, `MX`, `CO`, or `AR`). +3. **Sign and update.** The SDK signs the ephemeral transactions and submits them inside `registerRamp`. A direct API client must sign every returned ephemeral-owned transaction itself (Section D.4) and submit them through `POST /v1/ramp/update`; BRL and EUR payment instructions are released, and start is accepted, only after they validate. On a sell, and for the owner permit on an EUR buy, your wallet is the user wallet: validate the returned user transactions, sign or send them with your wallet key, and submit them with `sdk.submitUserTransactions()` or `POST /v1/ramp/update`. 4. **Fund.** On a buy, pay the instructions released by the update: `depositQrCode` for BRL (PIX), `achPaymentData` for USD, MXN, COP, and ARS, or `ibanPaymentData` for EUR. On a sell, what your wallet signed or sent in step 3 funds the ramp; there is no separate funding step. 5. **Start.** `sdk.startRamp()` or `POST /v1/ramp/start`, before the `expiresAt` returned by register and update (15 minutes after registration). After that deadline, update and start are refused. 6. **Track.** Poll `GET /v1/ramp/{id}` until `status` is `COMPLETE` or `FAILED`, or register a webhook (Section D.6). From 9c12a053a753b9e7a0b490dd6d4ac65b8adcb6c6 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 16:14:46 +0200 Subject: [PATCH 13/17] docs(sdk): mark EUR buy as post-0.9.0 and add payout accounts first --- packages/sdk/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 15a3dee2a..1a49d9251 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -137,7 +137,7 @@ await sdk.submitUserTransactions(rampProcess.id, unsignedTransactions, { const startedRamp = await sdk.startRamp(rampProcess.id); ``` -> `fiatAccountId` is opaque to the SDK. It is required for offramp and optional for onramp. The account is created during onboarding; look up its `fiatAccountId` with `sdk.listDomesticFiatAccounts(country)` and pass it in. +> `fiatAccountId` is opaque to the SDK. It is required for offramp and optional for onramp. Verification does not create it: add the pay-out account first (Dashboard **Add pay-out account** after the corridor is verified), then look up its `fiatAccountId` with `sdk.listDomesticFiatAccounts(country)` and pass it in. ### Deferred offramp funding @@ -203,6 +203,7 @@ Gets the current status of a ramp process. ##### `registerRamp(quote: Q, additionalData: RegisterRampAdditionalData): Promise<{ rampProcess: RampProcess; unsignedTransactions: UnsignedTx[] }>` Registers a new ramp process. Creates fresh Substrate and EVM ephemeral accounts, submits the quote and ephemeral addresses to the API, then signs and submits the returned ephemeral-owned transactions. Returns the ramp process and the user-owned `unsignedTransactions` that the caller must sign or broadcast. +EUR/SEPA BUY requires an SDK release newer than 0.9.0; with 0.9.0, use the direct API flow in the Fiat Corridors guide. For EUR/SEPA BUY, pass `walletAddress`: the wallet linked to the user's EUR provider profile (see the Fiat Corridors guide). The backend mints EURe to that wallet and returns its ERC-2612 permit as a user-owned typed-data transaction in `unsignedTransactions`; sign and submit it with From 581894704c5f475046c0b3f9fa7c9ed5b77f4144 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 16:24:16 +0200 Subject: [PATCH 14/17] docs(api): route the EUR permit by its signer in the own-account path The API derives the EUR permit owner from the provider binding and ignores walletAddress, which only the SDK uses; accounts holding both legal types must also send customerType. --- docs/api/pages/12-ai-agent-integration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/pages/12-ai-agent-integration.md b/docs/api/pages/12-ai-agent-integration.md index 831396a74..ebb790d39 100644 --- a/docs/api/pages/12-ai-agent-integration.md +++ b/docs/api/pages/12-ai-agent-integration.md @@ -43,8 +43,8 @@ One-time setup, all in the Vortex dashboard: Date: Tue, 29 Sep 2026 16:24:16 +0200 Subject: [PATCH 15/17] docs(repo): give the skill's sell recipe a sell quote and payout guard The sell example reused the buy quote, so registerRamp dispatched it to the onramp handler, and it indexed an empty account list for sellers who had not added a pay-out account. --- .agents/skills/vortex-integration/SKILL.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.agents/skills/vortex-integration/SKILL.md b/.agents/skills/vortex-integration/SKILL.md index 7b95a4e21..eeb63f715 100644 --- a/.agents/skills/vortex-integration/SKILL.md +++ b/.agents/skills/vortex-integration/SKILL.md @@ -352,7 +352,7 @@ The user wants to ramp USD, MXN, COP, or ARS over their domestic banking rail. R ## Prerequisites - The user completed KYC for the corridor's country via the Vortex app or Widget, and the SDK is authenticated with that user's own `sk_*` key or Supabase session. - Buy: `destinationAddress` (required); `fiatAccountId`, `walletAddress` optional. -- Sell: `fiatAccountId` and `walletAddress` (both required). List saved accounts with `vortex.listDomesticFiatAccounts(country)`. +- Sell: `fiatAccountId` and `walletAddress` (both required). Verification does not create a pay-out account: the user adds one after verification (Dashboard **Add pay-out account**, the Widget, or `POST /v1/domestic/fiatAccounts`). List saved accounts with `vortex.listDomesticFiatAccounts(country)`. ## SDK recipe (onramp, MXN shown — substitute fiat + rail for USD/COP/ARS) ```js @@ -382,9 +382,22 @@ No user-signed on-chain transactions on buys. Unlike BRL there is no QR code — ## SDK recipe (offramp) ```js +const sellQuote = await vortex.createQuote({ + rampType: RampDirection.SELL, + from: Networks.Polygon, + to: EPaymentMethod.SPEI, + network: Networks.Polygon, + inputAmount: "10", + inputCurrency: EvmToken.USDC, + outputCurrency: FiatToken.MXN +}); + const accounts = await vortex.listDomesticFiatAccounts("MX"); +if (accounts.length === 0) { + throw new Error("Add a pay-out account for MX before selling"); +} -const { rampProcess, unsignedTransactions } = await vortex.registerRamp(quote, { +const { rampProcess, unsignedTransactions } = await vortex.registerRamp(sellQuote, { fiatAccountId: accounts[0].fiatAccountId, walletAddress: "0xUserWalletAddress" }); @@ -396,7 +409,7 @@ await vortex.submitUserTransactions(rampProcess.id, unsignedTransactions, { await vortex.startRamp(rampProcess.id); ``` -The SDK cannot **create** fiat accounts; they are created during onboarding in the Vortex app or Widget. `fiatAccountId` is opaque to the SDK. +The SDK cannot **create** fiat accounts, and verification does not create one either: the user adds it in the Dashboard (**Add pay-out account**) or Widget, or a server calls `POST /v1/domestic/fiatAccounts`. `fiatAccountId` is opaque to the SDK. ## Common failures - `MissingDomesticOnrampParametersError` / `MissingDomesticOfframpParametersError` — `destinationAddress`, `fiatAccountId`, or `walletAddress` missing. From be9b040f6d95018897a22474c237481bd24b3d91 Mon Sep 17 00:00:00 2001 From: Marcel Ebert Date: Tue, 29 Sep 2026 16:31:53 +0200 Subject: [PATCH 16/17] docs(api): pass the DomesticCountry enum in payout-account lookups listDomesticFiatAccounts takes the exported DomesticCountry string enum, so the "MX" literal fails TypeScript type-checking. --- .agents/skills/vortex-integration/SKILL.md | 2 +- docs/api/pages/12-ai-agent-integration.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.agents/skills/vortex-integration/SKILL.md b/.agents/skills/vortex-integration/SKILL.md index eeb63f715..b2647b090 100644 --- a/.agents/skills/vortex-integration/SKILL.md +++ b/.agents/skills/vortex-integration/SKILL.md @@ -392,7 +392,7 @@ const sellQuote = await vortex.createQuote({ outputCurrency: FiatToken.MXN }); -const accounts = await vortex.listDomesticFiatAccounts("MX"); +const accounts = await vortex.listDomesticFiatAccounts(DomesticCountry.MX); // DomesticCountry from @vortexfi/sdk if (accounts.length === 0) { throw new Error("Add a pay-out account for MX before selling"); } diff --git a/docs/api/pages/12-ai-agent-integration.md b/docs/api/pages/12-ai-agent-integration.md index ebb790d39..09624fe97 100644 --- a/docs/api/pages/12-ai-agent-integration.md +++ b/docs/api/pages/12-ai-agent-integration.md @@ -43,7 +43,7 @@ One-time setup, all in the Vortex dashboard: Date: Tue, 29 Sep 2026 16:31:54 +0200 Subject: [PATCH 17/17] docs(sdk): note EUR is sandbox-only next to the SDK version requirement --- packages/sdk/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 1a49d9251..b8a4db58e 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -204,6 +204,7 @@ Gets the current status of a ramp process. Registers a new ramp process. Creates fresh Substrate and EVM ephemeral accounts, submits the quote and ephemeral addresses to the API, then signs and submits the returned ephemeral-owned transactions. Returns the ramp process and the user-owned `unsignedTransactions` that the caller must sign or broadcast. EUR/SEPA BUY requires an SDK release newer than 0.9.0; with 0.9.0, use the direct API flow in the Fiat Corridors guide. +EUR is available in sandbox only; production activation is pending. For EUR/SEPA BUY, pass `walletAddress`: the wallet linked to the user's EUR provider profile (see the Fiat Corridors guide). The backend mints EURe to that wallet and returns its ERC-2612 permit as a user-owned typed-data transaction in `unsignedTransactions`; sign and submit it with