# Add session details to account Source: https://docs.rhinestone.dev/api-reference/deposit-service/accounts/add-session-details-to-account https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /account/{address}/session Add session details (chain IDs, session digests, and signature) to an existing account # Check if account is registered Source: https://docs.rhinestone.dev/api-reference/deposit-service/accounts/check-if-account-is-registered https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /check/{address} Check if an account is registered and return its target chain and token if registered # Create a Solana token account (ATA) for an account Source: https://docs.rhinestone.dev/api-reference/deposit-service/accounts/create-a-solana-token-account-ata-for-an-account https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /account/{address}/token-accounts Create the Solana Associated Token Account (ATA) for a registered account's Solana deposit address and an SPL mint, so transfers from senders that omit a create-ATA instruction (some CEXes) do not bounce on-chain. Allowlisted clients only. Idempotent: returns the existing ATA with created=false if it already exists. Rejects native/wrapped SOL (no ATA needed). # Fetch or backfill deposit addresses for a registered account Source: https://docs.rhinestone.dev/api-reference/deposit-service/accounts/fetch-or-backfill-deposit-addresses-for-a-registered-account https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /account/{address}/deposit-addresses Return the EVM, Solana, and Tron deposit addresses for an already-registered account. Missing Solana and Tron deposit addresses are backfilled on first call (derivation and Rhino.fi mint as needed) without modifying the account config (session details, target). Idempotent on repeat. # Prepare account for registration Source: https://docs.rhinestone.dev/api-reference/deposit-service/accounts/prepare-account-for-registration https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /setup-account Derive the Rhinestone smart account for (ownerAddress, sessionOwnerAddress), compare against the existing registration and target, and return either the Solana deposit address (if no re-registration is needed) or the unsigned session details and factory parameters the client must sign before calling /register. # Register a new account Source: https://docs.rhinestone.dev/api-reference/deposit-service/accounts/register-a-new-account https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /register Register a new account with factory, factory data, session details, and target. `target.chain` must use a CAIP-2 chain identifier (for example "eip155:8453"). # Register a server-managed account Source: https://docs.rhinestone.dev/api-reference/deposit-service/accounts/register-a-server-managed-account https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /register-managed Register a server-owned smart account. Provide a salt for deterministic addressing and a target chain/token. # Configure client settings Source: https://docs.rhinestone.dev/api-reference/deposit-service/clients/configure-client-settings https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /setup Configure webhook URL, secret, sponsorship rules, and source-token whitelist rules for a client. Deposit whitelist keys must use CAIP-2 chain identifiers (for example "eip155:8453"). # Read client settings Source: https://docs.rhinestone.dev/api-reference/deposit-service/clients/read-client-settings https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /setup Returns the stored client configuration — webhook URL/secret, sponsorship rules, source-token whitelist, and price-deviation guard — for the authenticated project. # List centralized exchanges for the Connect picker Source: https://docs.rhinestone.dev/api-reference/deposit-service/onramp/list-centralized-exchanges-for-the-connect-picker https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /onramp/swapped/connect-exchanges Returns the centralized exchanges offered by the Swapped Connect picker. Served from a maintained static list (Swapped exposes no enumeration API). # List localized Swapped payment methods Source: https://docs.rhinestone.dev/api-reference/deposit-service/onramp/list-localized-swapped-payment-methods https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /onramp/swapped/payment-methods Returns the configured popular method (when merchant-enabled) followed by the supported subset of credit card, bank transfer, and Apple Pay for the trusted edge-resolved x-user-country. A missing/unknown country or unavailable catalog returns the null-country fallback so clients can use their built-in global defaults. # Mint a signed Swapped Connect URL (CEX funding) Source: https://docs.rhinestone.dev/api-reference/deposit-service/onramp/mint-a-signed-swapped-connect-url-cex-funding https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /onramp/swapped/connect-url Returns a signed Swapped Connect URL the modal embeds in an iframe so the user can fund from a centralized exchange. Current callers send the exact picker rate as acceptedExchangeFeeBps; omission preserves legacy fee-free delivery, while a stale rate returns 409 before any URL or session state is minted. Requires a configured public webhook callback URL. # Mint a signed Swapped widget URL (fiat on-ramp) Source: https://docs.rhinestone.dev/api-reference/deposit-service/onramp/mint-a-signed-swapped-widget-url-fiat-on-ramp https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /onramp/swapped/widget-url Returns a signed Swapped widget URL the modal embeds in an iframe for the fiat on-ramp. The destination asset is fixed server-side (the configured currency code); the caller supplies the smart account that receives the crypto. `methodSelectionSource: "personalized"` opts a trusted regional selection into strict merchant-catalog validation; fallback, configured, and legacy selections retain exact-method pass-through. # Poll the latest Swapped order status for a smart account Source: https://docs.rhinestone.dev/api-reference/deposit-service/onramp/poll-the-latest-swapped-order-status-for-a-smart-account https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /onramp/swapped/status/{smartAccount} Returns the latest Swapped order recorded for the smart account (from notify webhooks), or `{ ok: false, reason: "no_order" }` when none exists yet. # Preview a bridge quote before depositing Source: https://docs.rhinestone.dev/api-reference/deposit-service/processing/preview-a-bridge-quote-before-depositing https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /quotes/preview Returns the fees and expected output for bridging `amount` of `sourceToken` from `sourceChainId` to the account's registered target, before any funds are deposited. The orchestrator quotes against the supplied amount as if the deposit wallet already held it; the result is indicative and may differ from the quote applied once a real deposit lands. EVM source chains only. Scoped to the caller's tenant. # Recover a failed deposit with an owner signature Source: https://docs.rhinestone.dev/api-reference/deposit-service/processing/recover-a-failed-deposit-with-an-owner-signature https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /deposits/recover Return a failed or rejected deposit's source-chain funds to a user-chosen recipient, authorized by the smart-account owner's EIP-712 signature (not just the API key). The server reconstructs the RecoverDeposit typed data from the stored deposit and verifies it against the account via ERC-1271/ERC-6492, then runs the same claim + transfer path as the refund endpoint. EVM Nexus accounts created by the deposit modal only. Authenticate with an `x-api-key` (write scope). # Refund a failed deposit Source: https://docs.rhinestone.dev/api-reference/deposit-service/processing/refund-a-failed-deposit https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /deposits/refund Transfer the funds from a failed deposit back to a user-provided address on the same chain. Authenticate with an `x-api-key` (write scope) or a Bearer platform token; the credential must belong to the client that registered the account. # Relay a Polymarket deposit-wallet withdrawal Source: https://docs.rhinestone.dev/api-reference/deposit-service/processing/relay-a-polymarket-deposit-wallet-withdrawal https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /polymarket/withdraw Relay a user-signed Polymarket deposit-wallet `WALLET` batch (approve/unwrap pUSD or transfer USDC.e) through the Polymarket relayer using server-held builder credentials. Submitted calls must target known Polymarket contracts. # Relay a Safe withdrawal via shared Nexus relayer Source: https://docs.rhinestone.dev/api-reference/deposit-service/processing/relay-a-safe-withdrawal-via-shared-nexus-relayer https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /safe/withdraw Relay a signed Safe execTransaction via a shared AWS-owned Nexus account with sponsored gas # Retry failed deposits for an account Source: https://docs.rhinestone.dev/api-reference/deposit-service/processing/retry-failed-deposits-for-an-account https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /deposits/retry Retry all failed deposits for a given account address. Authenticate with an `x-api-key` (write scope) or a Bearer platform token; the credential must belong to the client that registered the account. # Aggregated deposit statistics Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/aggregated-deposit-statistics https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /deposits/stats Returns aggregated deposit statistics (total deposits, unique depositors, and total USD volume) for the authenticated client over the requested rolling time window. Only completed deposits are counted. # Check route liquidity for an amount Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/check-route-liquidity-for-an-amount https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /liquidity Returns whether the given source→destination route can fulfil the requested amount, along with the underlying liquidity metadata from the orchestrator. # Get a Solana wallet portfolio Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/get-a-solana-wallet-portfolio https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /portfolio/solana/{address} Returns a normalised SPL portfolio for the given Solana address by paginating through Helius wallet balances. # Get a wallet portfolio Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/get-a-wallet-portfolio https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /portfolio/{address} Returns a normalised token portfolio for the given address. If the address is a Solana base58 public key, the Solana portfolio is returned; otherwise the EVM portfolio is aggregated from the orchestrator's `/accounts/:address/portfolio` endpoint and priced via the price service. # Get migratable DeFi positions Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/get-migratable-defi-positions https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /positions/{address} Returns the address's DeFi positions in one normalised shape, each carrying the largest amount that can actually be withdrawn right now. `maxWithdrawable` is capped against the venue's full withdraw validation — health factor (with a buffer above the liquidation edge), E-Mode thresholds, zero-LTV collateral, paused reserves and pool liquidity — so a client never asks a user to sign an exit the protocol would reject. Currently covers Aave v3 across every market on the deposit-enabled chains; a `failures` entry appears when a venue could not be reached, so an empty list is distinguishable from a failed lookup. # Get USD unit prices for token symbols Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/get-usd-unit-prices-for-token-symbols https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /prices Returns USD unit prices keyed by uppercased token symbol. Used to convert a USD amount into estimated destination-token units for tokens not present in the portfolio (e.g. a SOL or ETH destination the user does not hold). # List candidate quotes considered for a deposit Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/list-candidate-quotes-considered-for-a-deposit https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /deposits/{id}/quotes Returns every quote the orchestrator (or Solana bridge adapter) returned for this deposit, including which one was attempted and its terminal outcome. Scoped to the caller's tenant. # List deposits for the authenticated client Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/list-deposits-for-the-authenticated-client https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /deposits Returns the authenticated client's deposits, newest first. Supports filtering by account, recipient, address, status, source chain, and transaction hash. The `account` filter accepts an EVM address or a Solana public key (deposit address or swig address); `address` matches the source-chain deposit address or target-chain recipient. # List supported chains and tokens Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/list-supported-chains-and-tokens https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /chains Returns all chains supported by the deposit service, with their tokens and whether they support deposits, bridging destinations, or both # List the QR/transfer token shortlist Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/list-the-qrtransfer-token-shortlist https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /qr/tokens Returns the curated per-chain token shortlist the QR / manual-transfer flow offers as "what are you sending?". That flow has no connected wallet to read balances from, so it needs a suggested set rather than a discovered one. Filtered against the authenticated project's `depositWhitelist` when one is configured, so the flow cannot offer a token the client would reject on arrival. This is a suggestion list, not a capability list — `/chains` remains the authority on what the service supports. # Prepare an Aave position unwind Source: https://docs.rhinestone.dev/api-reference/deposit-service/utilities/prepare-an-aave-position-unwind https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /positions/{address}/unwind Returns an **unsigned** `Pool.withdraw` transaction that exits an Aave position straight to the project's deposit address, so the existing deposit pipeline ingests the proceeds. Nothing is executed: the holding EOA owns the aTokens, so only it can authorise the withdraw, and the service holds no key on this path. The amount is capped server-side against Aave's full withdraw validation and is refused outright if the proceeds would fall foul of the project's deposit policy — that check happens here because this is the last point before an irreversible signature. # List persisted webhook events for the calling client Source: https://docs.rhinestone.dev/api-reference/deposit-service/webhooks/list-persisted-webhook-events-for-the-calling-client https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json get /webhooks/events Returns webhook events delivered (or attempted) to the caller's webhook URL. Use for backfill or replay verification. Results are id-desc; pass the last `nextCursor` to paginate. # Re-attempt delivery of a stored webhook event Source: https://docs.rhinestone.dev/api-reference/deposit-service/webhooks/re-attempt-delivery-of-a-stored-webhook-event https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /webhooks/events/{id}/resend Re-sends an existing webhook event to the configured webhook URL using its original eventId. Useful for replaying a delivery that failed permanently. Does not allocate a new event id. # Send a test webhook to the configured URL Source: https://docs.rhinestone.dev/api-reference/deposit-service/webhooks/send-a-test-webhook-to-the-configured-url https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/deposit-service.json post /webhooks/test Sends a single webhook with fixture data to the client's configured URL. Uses the client's real webhook secret so signature verification works as in production. Envelope and persisted payload both carry `"test": true` so the receiver can filter and the dashboard can mark it. # Introduction Source: https://docs.rhinestone.dev/api-reference/introduction Reference for the Rhinestone REST APIs. The Rhinestone platform exposes two REST APIs: * **Orchestrator** — quote, submit, and track cross-chain intents. Backs the core Warp flow. * **Deposit Service** — register accounts and process cross-chain deposits with automatic bridging and gas sponsorship. Both share the same base host, `https://v1.orchestrator.rhinestone.dev`, with the Deposit Service mounted at `/deposit-processor`. ## Authentication All endpoints require an API key sent in the `x-api-key` header. The orchestrator additionally supports short-lived JWTs as an alternative for integrators that need bounded credentials or per-request sponsorship policies. * [Get an API key](https://tally.so/r/wg22x4) * [JWT authentication](/intents/configuration/jwt-authentication) ## Errors Errors are returned with standard HTTP status codes and a JSON body. See [Error handling](/intents/guides/error-handling) for the full taxonomy and recovery patterns. ## Guides If you're integrating from scratch, start with the workflow guides — they show how the endpoints fit together end-to-end. Build the full intent flow: quote, sign, submit, track. Register accounts and accept cross-chain deposits. # Create App-Fee Withdrawal Source: https://docs.rhinestone.dev/api-reference/orchestrator/app-fees/create-app-fee-withdrawal https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json post /app-fees/withdrawals Withdraw the authenticated project's full app-fee balance to a whitelisted stablecoin on the chosen chain, paid to the project's registered payout address. # Get App-Fee Balances Source: https://docs.rhinestone.dev/api-reference/orchestrator/app-fees/get-app-fee-balances https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json get /app-fees/balances The authenticated project's app-fee balance as USD totals valued at collection time. `pendingUsd` is the payout value permanently reserved by signed withdrawals whose target-chain FILL has not completed, including failed or expired payouts. # Get App-Fee Withdrawal Source: https://docs.rhinestone.dev/api-reference/orchestrator/app-fees/get-app-fee-withdrawal https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json get /app-fees/withdrawals/{nonce} A single app-fee withdrawal owned by the authenticated project, with its status and USD value paid out. # List App-Fee Withdrawals Source: https://docs.rhinestone.dev/api-reference/orchestrator/app-fees/list-app-fee-withdrawals https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json get /app-fees/withdrawals The authenticated project's app-fee withdrawals (newest first) with each withdrawal's status and USD value paid out. # Create Intent Source: https://docs.rhinestone.dev/api-reference/orchestrator/intents/create-intent https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json post /intents Submits a quoted intent for execution. Takes the `intentId` from `POST /quotes` (`routes[].intentId`) plus signatures (origin, destination, optionally target-execution) and optional EIP-7702 authorizations. # Get Intent Source: https://docs.rhinestone.dev/api-reference/orchestrator/intents/get-intent https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json get /intents/{id} Retrieves the status of an intent along with per-claim progress across chains. # List Intents Source: https://docs.rhinestone.dev/api-reference/orchestrator/intents/list-intents https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json get /intents Retrieves a paginated list of the authenticated client's intents, newest first. # Split Intent by Liquidity Source: https://docs.rhinestone.dev/api-reference/orchestrator/intents/split-intent-by-liquidity https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json post /intents/splits Splits token amounts into multiple intents based on available relayer liquidity. Each returned intent can be filled by a single relayer. # Create Indicative Quote Source: https://docs.rhinestone.dev/api-reference/orchestrator/quotes/create-indicative-quote https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json post /quotes/estimate Computes an indicative (non-binding) quote: ranked route estimates with per-route cost breakdown and fill time. Unlike `POST /quotes`, it returns no `intentId`, `signData`, or `expiresAt` — it cannot be submitted to `POST /intents`. Use it for pre-quote UX (price previews, route discovery) without persisting an intent. # Create Quote Source: https://docs.rhinestone.dev/api-reference/orchestrator/quotes/create-quote https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json post /quotes Computes a quote for an intent: token transfers, target executions, and cost breakdown. Returns a pre-ranked `routes[]` with per-route `intentId` to pass to `POST /intents` for execution. # Get Account Portfolio Source: https://docs.rhinestone.dev/api-reference/orchestrator/utilities/get-account-portfolio https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json get /accounts/{accountAddress}/portfolio Retrieves the token balances across all supported chains for a user's abstracted account # Route liquidity limit Source: https://docs.rhinestone.dev/api-reference/orchestrator/utilities/route-liquidity-limit https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json get /liquidity Returns the largest known fillable amount for a single deposit on the given source→destination route. Relayer-market layers return exact capacity; bridge-backed layers return a conservative probed lower bound. # Supported chains and tokens Source: https://docs.rhinestone.dev/api-reference/orchestrator/utilities/supported-chains-and-tokens https://raw.githubusercontent.com/rhinestonewtf/openapi/refs/heads/main/orchestrator/blanc.json get /chains Get supported chains and tokens with additional metadata # API keys Source: https://docs.rhinestone.dev/dashboard/api-keys Create, scope, rotate, and revoke API keys API keys authenticate your integration's requests to Rhinestone. They're managed per-project from the [Dashboard](https://dashboard.rhinestone.dev) under **API keys**. An API key is shown in full **once**, at creation. Store it somewhere safe — you can't view it again. ## Creating a key Give the key a name you'll recognise later (e.g. `prod`, `ci-staging`). Press "Create". The full key is shown once. Copy it and store it securely, then press "I've saved this". A project can have up to 10 active keys at a time. ## Scoping a key By default a key is unrestricted. Open a key from the list to narrow what it can do under **Scopes**. There are three independent controls: * **Enable mainnets** — when off, the key can only target testnets; mainnet requests are rejected. On by default. * **Intents** — `None` blocks all intent endpoints, `Read` allows quotes, status, and listing, `Write` also allows submitting intents. `Write` by default. * **Deposits** — `None` blocks all deposit endpoints, `Read` allows balance and history, `Write` also allows mutations. `Write` by default. Adjust the controls and press "Save scopes". Only Owners and Admins can edit scopes. ## Rotating a key There's no in-place rotation — you rotate by creating a new key and revoking the old one: 1. Create a new key (above) and deploy it to your service. 2. Once traffic is flowing on the new key, [revoke](#revoking-a-key) the old one. Creating the new key first means there's no downtime: both keys are valid during the overlap. ## Renaming a key Open a key from the list and press "Rename". Renaming is cosmetic — it doesn't change the key's value. ## Revoking a key Open the key and press "Revoke". You'll be asked to type the key's name to confirm. ## Alternatives If you need short-lived credentials, per-request sponsorship policies, or rotation without redeploying clients, consider [JWT authentication](/dashboard/jwt-keys) instead. # JWT keys Source: https://docs.rhinestone.dev/dashboard/jwt-keys Register a signing key so your backend can issue JWTs JWT authentication lets your backend mint short-lived tokens instead of using a long-lived API key. Before you can issue JWTs, you register a **public signing key** with Rhinestone from the [Dashboard](https://dashboard.rhinestone.dev) under **API keys → JWT keys**. This page covers registering and managing keys in the dashboard. For how the SDK signs and uses JWTs — including per-request sponsorship policies — see [JWT authentication](/intents/configuration/jwt-authentication). ## Registering a key Press "Register key". You can either generate a keypair in your browser or upload a public key you already have. The dashboard generates a keypair in your browser. Only the public key is sent to Rhinestone — the private key never leaves your machine. * **Integrator ID** — your organisation's identifier, emitted as the `iss` claim in the JWTs you sign. Typically your service's name. * **Key ID** — identifier for this specific key, emitted as the `kid` header. Use a stable name you can rotate later (e.g. `prod-2026-06`). Press "Create Key". Download the private key file and store it securely on your backend. If you already generated a keypair yourself, upload just the public half as a JWK. * **Integrator ID** — emitted as the `iss` claim (see above). * **Key ID** — emitted as the `kid` header (see above). * **Public JWK** — paste the JWK JSON, or drag in a `.json` file. Supported key types are RSA and EC (curve P-256). Only the **public** key is accepted — uploads containing a private key parameter are rejected. Once registered, the key shows as **active** in the list, with its Key ID and Integrator ID. ## Rotating keys Register a new key with a new Key ID and start signing tokens with it. Tokens signed under the old `kid` keep verifying until they expire, so there's no coordinated client deploy and no revocation race. Disable the old key once nothing signs with it. ## Disabling a key Open the key and press "Disable". # Getting started Source: https://docs.rhinestone.dev/dashboard/overview Sign in, and find your way around the dashboard The [Rhinestone Dashboard](https://dashboard.rhinestone.dev) is where you manage your project: create API keys, register JWT keys, fund sponsorship, invite your team, and monitor intents and deposits. ## Signing in Access is currently invite-gated. On the sign-in screen, enter your invite code and press "Continue". Don't have a code yet? Press "Request access" to join the waitlist. Once your code is accepted, sign in with Google or GitHub. ## Finding your way around After signing in you land on your project. The sidebar holds the main sections: * **Intents** — monitor the intents your integration submits. * **Deposits** — monitor deposits, and retry or refund failed ones. * **API keys** — create and manage API keys and JWT keys. * **Settings** — sponsorship balance and team management. If your project has no API key yet, you'll be taken to a "Send your first intent" page that walks you through creating one. See [API keys](/dashboard/api-keys). # Team management Source: https://docs.rhinestone.dev/dashboard/team Invite teammates and manage roles You can invite teammates to your project and control what they can do. Team management lives in the [Dashboard](https://dashboard.rhinestone.dev) under **Settings → Team**. ## Roles | Role | Can do | | ---------- | --------------------------------------------------------- | | **Owner** | Everything, including changing roles and inviting admins. | | **Admin** | Invite and remove members, manage keys and sponsorship. | | **Member** | Read-only access to the project. | Members can't create API keys or invite others. If a teammate needs to set up keys, invite them as Admin. ## Inviting a member Enter the teammate's email. Owners can also choose whether to invite them as a Member or an Admin. Press "Send invite". The invite shows as **Pending** until the teammate accepts. You can "Resend" a pending invite from the members list. ## Changing roles and removing members From the members list, use "Change role" to promote or demote a member, or "Remove" to revoke their access. Owners can manage anyone; Admins can manage Members. ## Troubleshooting ### "Already a member of another organization" Each user can belong to **only one organization**. If you try to invite an email that already belongs to a different organization, the invite fails with this error. There's no self-serve way to move a user between organizations. [Reach out to us](http://t.me/kurt_larsen) and we'll help migrate the account. # Account registration Source: https://docs.rhinestone.dev/deposits/api/account-registration Register managed or user-owned smart accounts for cross-chain deposit processing. Every depositing user needs a registered smart account. You can let the service create one (managed) or bring your own (user-owned). ## Choose an account type | | Managed account | User-owned account | | ----------------------- | ----------------------------------------------- | ------------------------------------------------------- | | Who creates the account | Deposit service | You, via SDK | | Session key setup | Automatic | You sign session authorization | | User-facing address | Service returns deposit address | Your existing smart account address | | Recipient | Required (you specify where funds go) | Optional (defaults to the account itself) | | Best for | Existing apps, EOA users (e.g. browser wallets) | Non-custodial flows, users with existing smart accounts | Managed accounts are recommended for most integrations — simpler setup, no SDK dependency for registration. User-owned accounts are for cases where deposits should go to an existing smart account the user already controls. All examples below use these shared constants: ```ts theme={null} const DEPOSIT_SERVICE_URL = "https://v1.orchestrator.rhinestone.dev/deposit-processor"; const API_KEY = "YOUR_RHINESTONE_API_KEY"; const headers = { "Content-Type": "application/json", "x-api-key": API_KEY, }; ``` ## Register an account The service creates a Nexus smart account deterministically from your API key and a salt you provide. The same API key + salt always produces the same deposit address, so you can safely re-register if needed. Use a stable, unique identifier per user — for example, an internal user ID. Hash it for privacy: ```ts theme={null} import { keccak256, toHex } from "viem"; const salt = keccak256(toHex("user-123")); ``` ```ts theme={null} const response = await fetch(`${DEPOSIT_SERVICE_URL}/register-managed`, { method: "POST", headers, body: JSON.stringify({ account: { salt, target: { chain: "eip155:42161", // Arbitrum token: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC recipient: "0xYOUR_RECIPIENT_ADDRESS", }, }, }), }); const { evmDepositAddress, solanaDepositAddress } = await response.json(); ``` The response includes two deposit addresses: | Field | Description | | ---------------------- | ------------------------------------------- | | `evmDepositAddress` | Accepts deposits on any supported EVM chain | | `solanaDepositAddress` | Accepts deposits on Solana | Both addresses route to the same target chain and token. ```ts theme={null} const check = await fetch(`${DEPOSIT_SERVICE_URL}/check/${evmDepositAddress}`); const data = await check.json(); ``` ```json theme={null} { "isRegistered": true, "targetChain": "eip155:42161", "targetToken": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "sources": [ { "chain": "eip155:8453", "depositAddress": "" }, { "chain": "eip155:42161", "depositAddress": "" }, { "chain": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "depositAddress": "" } ] } ``` `sources` lists every chain the account can receive deposits from, each with its deposit address — EVM chains share the `evmDepositAddress`, Solana carries the `solanaDepositAddress`. For this flow, you create a Rhinestone smart account via the SDK, configure session keys so the deposit service can sign bridging transactions, and register the account with its factory data and session details. ```bash theme={null} npm install @rhinestone/sdk viem ``` ```ts theme={null} import { RhinestoneSDK, type RhinestoneAccountConfig } from "@rhinestone/sdk"; const rhinestone = new RhinestoneSDK(); const config: RhinestoneAccountConfig = { owners: { type: "ecdsa", accounts: [userSigner], }, sessions: { enabled: true }, }; const account = await rhinestone.createAccount(config); const address = account.getAddress(); const { factory, factoryData } = account.getInitData(); ``` The deposit service uses a session key to sign bridging transactions on the user's behalf. Build session details for every chain you accept deposits from, plus the target chain — a deposit arriving on a chain with no session can't be bridged. See [supported chains and tokens](/deposits/overview#supported-chains-and-tokens) for the current set. ```ts theme={null} import { toViewOnlyAccount } from "@rhinestone/sdk/utils"; import { base, optimism, arbitrum } from "viem/chains"; const RHINESTONE_SIGNER_ADDRESS = "0x177bfcdd15bc01e99013dcc5d2b09cd87a18ce9c"; const sessionSigner = toViewOnlyAccount(RHINESTONE_SIGNER_ADDRESS); const sourceChains = [base, optimism, arbitrum]; // extend to every chain you accept const sessions = sourceChains.map((chain) => ({ owners: { type: "ecdsa" as const, accounts: [sessionSigner] }, chain, })); const sessionDetails = await account.getSessionDetails(sessions); const signature = await account.signEnableSession(sessionDetails); const enableSessionDetails = { hashesAndChainIds: sessionDetails.hashesAndChainIds, signature, }; ``` ```ts theme={null} const response = await fetch(`${DEPOSIT_SERVICE_URL}/register`, { method: "POST", headers, body: JSON.stringify( { account: { address, accountParams: { factory, factoryData, sessionDetails: enableSessionDetails, }, target: { chain: "eip155:42161", // Arbitrum token: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC }, }, }, (_, v) => (typeof v === "bigint" ? v.toString() : v), ), }); const { evmDepositAddress, solanaDepositAddress } = await response.json(); ``` The JSON replacer `(_, v) => typeof v === "bigint" ? v.toString() : v` is needed because `sessionDetails.hashesAndChainIds` contains `bigint` chain IDs that `JSON.stringify` can't serialize by default. ```ts theme={null} const check = await fetch(`${DEPOSIT_SERVICE_URL}/check/${address}`); const data = await check.json(); // { isRegistered: true, targetChain: "eip155:42161", sources: [...] } ``` ### Adding source chains To accept deposits from chains that weren't included in the original registration, add new session details: ```ts theme={null} import { polygon } from "viem/chains"; const newSessions = [ { owners: { type: "ecdsa" as const, accounts: [sessionSigner] }, chain: polygon, }, ]; const newSessionDetails = await account.getSessionDetails(newSessions); const newSignature = await account.signEnableSession(newSessionDetails); await fetch(`${DEPOSIT_SERVICE_URL}/account/${address}/session`, { method: "POST", headers, body: JSON.stringify( { sessionDetails: { hashesAndChainIds: newSessionDetails.hashesAndChainIds, signature: newSignature, }, }, (_, v) => (typeof v === "bigint" ? v.toString() : v), ), }); ``` Managed accounts automatically support all available source chains — this step is only needed for user-owned accounts. ## Optional: token routing By default, all deposits are bridged to the single target token you set at registration. Token routing rules let you select the output token based on what the user deposited. When registering an account, the `target` object accepts two optional fields: | Field | Type | Description | | ------------------ | ------------- | ---------------------------------------------------------------------------------------------------- | | `outputTokenRules` | `Array` | Routing rules evaluated by match specificity | | `rejectUnmapped` | `boolean` | If `true`, deposits that don't match any rule are rejected instead of falling back to `target.token` | Each rule has: | Field | Type | Description | | -------------- | ------------------ | ---------------------------------------------------------------- | | `match.chain` | `string` (CAIP-2) | Match deposits from this source chain (e.g. `"eip155:1"`) | | `match.token` | `string` (address) | Match deposits of this source token address | | `match.symbol` | `string` | Match deposits by token symbol (case-insensitive, e.g. `"USDC"`) | | `outputToken` | `string` (address) | The final token to deliver on the target chain | A rule's `match` must specify at least one of `chain`, `token`, or `symbol`. You can combine them for more specific matches. ### Rule priority When multiple rules match a deposit, the most specific rule wins. Declaration order only matters when two rules share the same specificity. | Match type | Example | | ------------------ | -------------------------------------------------------------------- | | `chain` + `token` | Specific token from a specific chain | | `chain` + `symbol` | Any token with symbol X from chain Y | | `token` (only) | Specific token from any chain | | `symbol` (only) | Any token with symbol X from any chain | | `chain` (only) | Any token from a specific chain | | No match | Falls back to `target.token` (or rejected if `rejectUnmapped: true`) | ### Example: USDC and ETH passthrough Route USDC deposits to USDC.e and ETH deposits to WETH on Optimism, while defaulting other tokens to a fallback: ```ts theme={null} await fetch(`${DEPOSIT_SERVICE_URL}/register-managed`, { method: "POST", headers, body: JSON.stringify({ account: { salt, target: { chain: "eip155:10", // Optimism token: "0xYOUR_DEFAULT_FALLBACK_TOKEN", recipient: "0xYOUR_RECIPIENT_ADDRESS", outputTokenRules: [ { match: { symbol: "USDC" }, outputToken: "0x7f5c764cbc14f9669b88837ca1490cca17c31607", // USDC.e on Optimism }, { match: { symbol: "ETH" }, outputToken: "0x4200000000000000000000000000000000000006", // WETH on Optimism }, ], }, }, }), }); ``` With this configuration: * User deposits USDC (from any chain) → receives USDC.e on Optimism * User deposits ETH (from any chain) → receives WETH on Optimism * User deposits any other token → receives `target.token` (default fallback) ### Example: chain-specific overrides Combine `chain` and `symbol` for chain-specific routing. The chain-specific rule takes priority because `chain + symbol` outranks `symbol` alone. ```ts theme={null} outputTokenRules: [ { match: { chain: "eip155:1", symbol: "USDC" }, outputToken: "0xUSCD_BRIDGED_ADDRESS", }, { match: { symbol: "USDC" }, outputToken: "0xUSDC_NATIVE_ADDRESS", }, ]; ``` ### Example: reject unknown tokens Only accept specific tokens and reject everything else: ```ts theme={null} await fetch(`${DEPOSIT_SERVICE_URL}/register-managed`, { method: "POST", headers, body: JSON.stringify({ account: { salt, target: { chain: "eip155:10", token: "0xNOT_USED_AS_FALLBACK", recipient: "0xYOUR_RECIPIENT_ADDRESS", outputTokenRules: [ { match: { symbol: "USDC" }, outputToken: "0xUSDC_ADDRESS" }, { match: { symbol: "ETH" }, outputToken: "0xWETH_ADDRESS" }, ], rejectUnmapped: true, }, }, }), }); ``` Deposits that don't match USDC or ETH are ignored (not bridged). # Deposit processing Source: https://docs.rhinestone.dev/deposits/api/deposit-processing How deposits are detected, bridged, and settled — plus status tracking, retries, and error handling. Once an account is registered, the deposit service handles everything from detection to settlement. This page explains what happens at each stage and how to monitor the process. ## Deposit lifecycle ```mermaid actions={false} theme={null} flowchart LR D[Detection] -- valid --> B[Bridging] D -- whitelist/min --> X[Rejected] B -- success --> S[Settlement] B -- failure --> R[Retry] R --> B ``` ### Detection The service monitors registered accounts via chain indexer webhooks. When a token transfer is detected on a registered account, it goes through validation: * The token and amount are checked against your [deposit whitelist](/deposits/api/initial-setup#restrict-accepted-deposits) (if configured) * The transfer is deduplicated by chain, transaction hash, account, and token * If valid, the deposit enters the pipeline with status `processing` * If the token isn't allowed or the amount is outside your configured min/max, the deposit is **rejected** (not bridged) and a [`deposit-rejected`](/deposits/api/status-tracking#deposit-rejected) webhook is sent after `deposit-received` ### Bridging The service submits a bridging intent to the Rhinestone Orchestrator, which routes the funds through the optimal settlement layer (Across, Relay, or others). If the primary layer fails, the service tries alternatives automatically. ### Settlement Tokens arrive on the target chain in the registered target token. If a recipient address was set at registration, funds are forwarded there. The service marks the deposit as `completed` and sends a `bridge-complete` [webhook](/deposits/api/status-tracking#webhooks). ## Deposit statuses | Status | Meaning | | ------------ | ---------------------------------------------- | | `processing` | Deposit detected, bridging in progress | | `completed` | Funds arrived on target chain | | `failed` | Bridging failed — may be retried automatically | ## Retries ### Automatic Transient errors — bridge failures, session activation issues, and internal errors — are retried automatically. Configuration errors like unsupported tokens, insufficient balance, or unregistered accounts require manual resolution. ### Manual Force an immediate retry of all failed deposits for an account: ```ts theme={null} const response = await fetch(`${DEPOSIT_SERVICE_URL}/deposits/retry`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": API_KEY, }, body: JSON.stringify({ address: accountAddress, }), }); const { deposits } = await response.json(); // [{ txHash: "0x...", chain: "eip155:8453" }, ...] ``` ## Error codes When a deposit fails, the error code indicates what went wrong and whether the service will retry automatically. ### Retryable These are transient failures. The service retries automatically, or you can trigger a [manual retry](#manual). | Code | Description | | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `SESSION-2` | Session key activation failed | | `BRIDGE-1` | Bridge submission failed | | `BRIDGE-2` | No bridge quote available | | `BRIDGE-3` | Bridge timed out | | `BRIDGE-4` | Bridge provider failed | | `BRIDGE-5` | Price deviation exceeded tolerance | | `BRIDGE-7` | Bridge refunded | | `BRIDGE-8` | Bridge cancelled | | `BRIDGE-9` | Bridge rejected | | `SWAP-1` | Post-bridge swap failed | | `INTERNAL-1` | Unexpected service error | | `SPONSORSHIP-1` | A per-intent sponsorship cap (overall, gas-fee, or bridge-fee) would be exceeded; the deposit is held (`delayed`) and re-quoted automatically until the sponsored cost fits — as fees fluctuate or the cap is raised — or the hold window (\~24h) elapses. See [`deposit-delayed`](/deposits/api/status-tracking#deposit-delayed) | ### Non-retryable These indicate a configuration or input problem. Check account registration, deposit whitelist, and token support. | Code | Description | | ------------ | ----------------------------------------------------------------------------------- | | `BALANCE-1` | Account balance too low for bridging | | `BALANCE-2` | Account balance could not be read | | `BALANCE-3` | Deposit amount above the configured maximum | | `BALANCE-4` | Deposit amount below the configured minimum | | `TOKEN-1` | Token not supported on this chain | | `TOKEN-2` | Token account (Solana ATA) creation failed | | `TOKEN-3` | Token not in deposit whitelist | | `ACCOUNT-1` | Account not registered | | `ACCOUNT-2` | Account (Solana Swig wallet) creation failed | | `SESSION-1` | Smart session not found — resolved by re-registering the account | | `TRANSFER-1` | Transfer execution failed | | `BRIDGE-6` | Unsupported bridge route — no route available; the deposit is rejected, not retried | Failed deposits with non-retryable errors will not be retried automatically. Resolve the underlying issue (e.g. update your deposit whitelist or register the account) before triggering a manual retry. Whitelist and minimum rejections (`BALANCE-4`, `BALANCE-3`, `TOKEN-3`) are deposits the service deliberately won't bridge per your configuration. These are surfaced via the [`deposit-rejected`](/deposits/api/status-tracking#deposit-rejected) webhook — not `bridge-failed` — and no bridging is attempted. # Fees Source: https://docs.rhinestone.dev/deposits/api/fees Understand the fee structure for deposits. Every deposit processed through the Deposit API may incur fees depending on the source chain, target chain, and tokens involved. Fees are deducted from the deposit amount — the user receives the deposited amount minus any applicable fees. ## Fee types | Fee | Applies when | Covers | | ------------ | ------------------------------ | ---------------------------------------------------- | | **Gas** | Always | On-chain execution costs on source and target chains | | **Bridging** | Source and target chain differ | Settlement layer fee (Across, Relay, etc.) | | **Swap** | Source and target token differ | DEX execution fee for token conversion | A single deposit can incur multiple fee types. For example, depositing ETH on Arbitrum to receive USDC on Base incurs all three: gas for on-chain execution, a bridging fee for the cross-chain settlement, and a swap fee for the ETH → USDC conversion. Same-chain deposits skip the bridging fee, and same-token deposits skip the swap fee. ## How fees are applied Fees are subtracted from the deposit amount during processing. The user receives the remainder on the target chain. For example, if a user deposits 100 USDC and total fees are 0.50 USDC, the user receives 99.50 USDC on the target chain. To cover fees on behalf of your users so they receive the full amount, see [sponsorship](/deposits/api/sponsorship). ## Learn more * [Fees](/home/resources/fees) — general Rhinestone fee structure and current rates * [Sponsorship](/deposits/api/sponsorship) — cover fees on behalf of your users * [Initial setup](/deposits/api/initial-setup) — configure deposit whitelists and price deviation tolerance # Initial setup Source: https://docs.rhinestone.dev/deposits/api/initial-setup Configure webhooks, fee sponsorship, and deposit whitelists for your Deposit API client. Call `POST /setup` to configure how the deposit service handles your deposits. All fields are optional — configure what you need. Each call performs a partial update; omitted fields keep their current value. All examples below use these shared constants: ```ts theme={null} const DEPOSIT_SERVICE_URL = "https://v1.orchestrator.rhinestone.dev/deposit-processor"; const API_KEY = "YOUR_RHINESTONE_API_KEY"; const headers = { "Content-Type": "application/json", "x-api-key": API_KEY, }; ``` ## Configure a webhook Register a URL to receive notifications about deposit events. Optionally provide a secret for HMAC-SHA256 signature verification. ```ts theme={null} await fetch(`${DEPOSIT_SERVICE_URL}/setup`, { method: "POST", headers, body: JSON.stringify({ params: { webhookUrl: "https://your-domain.com/notify", webhookSecret: "your-secret-key", }, }), }); ``` When a `webhookSecret` is set, every webhook request includes an `X-Webhook-Signature` header you can verify. See [status tracking](/deposits/api/status-tracking#webhooks) for event types, payload format, and verification examples. Once your endpoint is live, fire a single fixture event with [`POST /webhooks/test`](/api-reference/deposit-service/webhooks/send-a-test-webhook-to-the-configured-url) to confirm signature handling and parsing before real deposits arrive. The envelope carries `"test": true` so receivers can filter it. ## Sponsor fees Cover gas, bridging, and swap fees on behalf of your users so they receive the full deposit amount. Sponsorship is configured per source chain using [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) identifiers. Chains without explicit config default to no sponsorship. ```ts theme={null} await fetch(`${DEPOSIT_SERVICE_URL}/setup`, { method: "POST", headers, body: JSON.stringify({ params: { sponsorship: { "eip155:8453": { gas: "all", bridging: "all" }, // Base "eip155:10": { gas: "all" }, // Optimism "eip155:42161": { gas: "deployed" }, // Arbitrum }, }, }), }); ``` See [sponsorship](/deposits/api/sponsorship) for the full list of fee types, available values, and how sponsorship is resolved at processing time. ## Restrict accepted deposits Define a whitelist of accepted tokens per source chain. Deposits of unlisted tokens are silently ignored. You can also set minimum and maximum deposit amounts per token. ```ts theme={null} await fetch(`${DEPOSIT_SERVICE_URL}/setup`, { method: "POST", headers, body: JSON.stringify({ params: { depositWhitelist: { "eip155:8453": [ { token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base minAmount: "1000000", // 1 USDC minimum maxAmount: "5000000000", // 5,000 USDC maximum }, ], "eip155:42161": [ { token: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" }, // USDC on Arbitrum, no limits { token: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", minAmount: "1000000", }, // USDT, $1 min ], }, }, }), }); ``` | Field | Type | Description | | ----------- | -------- | ---------------------------------------------------------------- | | `token` | `string` | Token address (`0x`-prefixed) | | `minAmount` | `string` | Optional. Minimum accepted amount in raw token units (inclusive) | | `maxAmount` | `string` | Optional. Maximum accepted amount in raw token units (inclusive) | If no whitelist is set, all [supported tokens](/deposits/overview#supported-chains-and-tokens) are accepted with no amount restrictions. Rejected deposits trigger a [`deposit-rejected`](/deposits/api/status-tracking#deposit-rejected) webhook — not `bridge-failed` — with error code `TOKEN-3` (token not allowed), `BALANCE-3` (amount above the configured maximum), or `BALANCE-4` (amount below the configured minimum). ## Set price deviation tolerance For stablecoin-to-stablecoin bridges, set the maximum allowed price deviation in basis points. Deposits that exceed this threshold are rejected with error code `BRIDGE-5`. ```ts theme={null} await fetch(`${DEPOSIT_SERVICE_URL}/setup`, { method: "POST", headers, body: JSON.stringify({ params: { maxPriceDeviationBps: 100, // 1% max deviation }, }), }); ``` The default is `200` (2%) if not set. Tighten this for high-value stablecoin corridors; loosen it if you see rejections during volatile periods. ## Set a minimum deposit value Set a per-client minimum deposit value in USD, applied across all tokens and source chains. Deposits priced below it are rejected — a [`deposit-rejected`](/deposits/api/status-tracking#deposit-rejected) webhook is sent with error code `BALANCE-4` and no bridging is attempted. ```ts theme={null} await fetch(`${DEPOSIT_SERVICE_URL}/setup`, { method: "POST", headers, body: JSON.stringify({ params: { minDepositUsd: 1, // reject deposits worth less than $1 }, }), }); ``` `minDepositUsd` is a non-negative `number` (USD). This is a per-client override; a platform-wide minimum floor also applies and is not client-configurable. ## Clear a configuration field Each `/setup` call is a partial update. To explicitly clear a field, pass `null` for string fields or `{}` for object fields: | To clear | Pass | | ---------------------- | ------ | | `webhookUrl` | `null` | | `webhookSecret` | `null` | | `sponsorship` | `{}` | | `depositWhitelist` | `{}` | | `maxPriceDeviationBps` | `null` | | `minDepositUsd` | `null` | ## Put it all together A single `/setup` call configuring everything at once: ```ts theme={null} await fetch(`${DEPOSIT_SERVICE_URL}/setup`, { method: "POST", headers, body: JSON.stringify({ params: { webhookUrl: "https://your-domain.com/notify", webhookSecret: "your-secret-key", sponsorship: { "eip155:8453": { gas: "all", bridging: "all" }, "eip155:10": { gas: "all" }, }, depositWhitelist: { "eip155:8453": [ { token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", minAmount: "1000000", }, ], "eip155:42161": [ { token: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" }, ], }, maxPriceDeviationBps: 150, minDepositUsd: 1, }, }), }); ``` # Fiat and CEX on-ramp Source: https://docs.rhinestone.dev/deposits/api/onramp Let users fund their account with a card, Apple Pay, a bank transfer, or a centralized exchange balance — powered by Swapped. Users without crypto can fund their account directly inside your app. The deposit service mints a signed Swapped checkout URL for a registered account; the user pays in Swapped's hosted widget; the purchased crypto lands on the account and is processed like any other deposit — bridged to the account's target and reported through the same [status tracking](/deposits/api/status-tracking). ## How it works 1. Your backend requests a signed checkout URL for the user's account. 2. You show the URL in an iframe or in-app browser. 3. The user completes payment (and KYC, when required) with Swapped. 4. You receive [`onramp-order`](/deposits/api/status-tracking#onramp-order) webhooks as the order progresses. 5. Swapped sends the purchased crypto — currently USDC on Base — to the account. From here the standard pipeline takes over: the deposit is detected, bridged to the account's target, and reported via the regular [deposit webhooks](/deposits/api/status-tracking#webhooks). ## Prerequisites * The account is [registered](/deposits/api/account-registration) — minting a checkout URL for an account that isn't registered to your project returns `403 Unauthorized`. * A [webhook URL](/deposits/api/initial-setup#configure-a-webhook) is configured if you want order updates pushed rather than polled. * Calls are made from your backend. The endpoints require your API key, which never ships in client-side code. ## Mint a checkout URL ```ts theme={null} const DEPOSIT_SERVICE_URL = "https://v1.orchestrator.rhinestone.dev/deposit-processor"; const API_KEY = "YOUR_RHINESTONE_API_KEY"; const response = await fetch(`${DEPOSIT_SERVICE_URL}/onramp/swapped/widget-url`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": API_KEY }, body: JSON.stringify({ smartAccount: "0xUSER_ACCOUNT_ADDRESS", baseCurrencyCode: "EUR", baseCurrencyAmount: 100, method: "apple-pay", // optional — omit to let Swapped choose the best method }), }); const { url, externalCustomerId, expiresAt } = await response.json(); ``` Only `smartAccount` is required — every other field, including `method`, is optional and simply prefills the widget. If you omit `method`, Swapped auto-selects the best payment method for the user based on their location and other signals: | Field | Type | Description | | -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `smartAccount` | `string` | The registered account that receives the crypto | | `email` | `string` | Prefills the user's email | | `baseCountry` | `string` | User's country (ISO 3166-1 alpha-2, e.g. `"DE"`) | | `baseCurrencyCode` | `string` | Fiat currency to charge in (e.g. `"EUR"`) | | `baseCurrencyAmount` | `number` | Prefilled purchase amount in the fiat currency | | `locale` | `string` | Widget language | | `method` | `string` | **Optional.** Preselects a payment method — accepts any method Swapped supports (see the list below). Omit it to let Swapped auto-select the best method for the user. | `method` takes a Swapped **payment group**. Swapped offers 40+ methods and the set available depends on the user's region — common values include `"creditcard"`, `"apple-pay"`, `"bank-transfer"`, `"skrill"`, `"pix"`, and `"sepa-bank-transfer"`. Pass a method's `payment_group`; the authoritative, up-to-date list for your account is Swapped's [Get Payment Methods](https://docs.swapped.com/swapped-ramp/endpoints/onramp-endpoints/get-payment-methods) endpoint. A value Swapped doesn't support for the user is ignored rather than rejected — the widget falls back to the best available method, so an unrecognized `method` never blocks checkout. The response carries the signed URL: | Field | Type | Description | | -------------------- | --------- | --------------------------------------------------------------------------- | | `url` | `string` | The checkout URL to embed | | `currencyCode` | `string` | Asset the purchase settles in, as `ASSET_NETWORK` (currently `"USDC_BASE"`) | | `sandbox` | `boolean` | `true` when the service runs against Swapped's sandbox | | `externalCustomerId` | `string` | `:` — correlates the checkout with its webhooks | | `expiresAt` | `string` | End of the order-tracking window (ISO 8601) | The URL is signed by the service, so its parameters — destination wallet and asset — can't be modified client-side. Mint a fresh URL for each checkout session. The purchase always settles in the asset reported by `currencyCode`; when the account's target differs, the bridge hop happens automatically. Full request and response schemas: [`POST /onramp/swapped/widget-url`](/api-reference/deposit-service/onramp/mint-a-signed-swapped-widget-url-fiat-on-ramp). ### Fund from an exchange To let users transfer from a centralized exchange balance (Binance, Coinbase, and others) instead of paying with fiat, mint a Connect URL. The request takes a subset of the widget fields — `smartAccount` plus optional `baseCountry`, `baseCurrencyCode`, `locale`, and a `connection` preselecting the exchange. There's no amount or payment-method prefill; the user chooses those inside the exchange flow. The response shape is identical: ```ts theme={null} const response = await fetch(`${DEPOSIT_SERVICE_URL}/onramp/swapped/connect-url`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": API_KEY }, body: JSON.stringify({ smartAccount: "0xUSER_ACCOUNT_ADDRESS", connection: "binance", }), }); ``` List the supported exchanges with [`GET /onramp/swapped/connect-exchanges`](/api-reference/deposit-service/onramp/list-centralized-exchanges-for-the-connect-picker); full schema at [`POST /onramp/swapped/connect-url`](/api-reference/deposit-service/onramp/mint-a-signed-swapped-connect-url-cex-funding). ## Show the widget Embed the URL in an iframe with payment and camera permissions — Apple Pay and camera-based KYC need them: ```html theme={null}