Embedded Yield

Build customer-signed deposits, balances, and withdrawals into your app, from first request to go-live.

Embedded Yield lets your customers earn from their own wallets inside your app. Your backend calls SDP with your API key, your app asks the customer's wallet to sign, and SDP verifies, broadcasts, and tracks every movement. SDP never holds the customer's key.

New to Earn? Run the Quickstart first, then come back to build the full integration.

How the pieces fit

PartDoesNever
Your backendHolds the API key. Previews, builds, submits, and reads. Decides which customer may use which wallet.Sends the API key to a browser or mobile app.
Your appShows quotes and status. Asks the customer's wallet to sign.Broadcasts the transaction.
The customer's walletSigns the exact transaction SDP built. Owns the position and receives payouts.Shares its private key.
SDPBuilds each transaction, verifies the signatures, records the movement, broadcasts it, and tracks it to finality.Holds the customer's key or pools their funds.

A deposit then runs: your backend builds, your app collects the signature, your backend submits, and your app shows the result as your backend tracks it.

Authenticate on your backend

Create an API key in each project with the grants your integration uses:

GrantAllows
earn.readStrategies, previews, positions, movements, earnings, and withdrawal requests.
earn.executeBuilds and submits for deposits, withdrawals, and withdrawal requests. Includes earn.read.
earn.manageBuilding and submitting a withdrawal request cancellation. Includes earn.read.

earn.execute and earn.manage do not include each other. The backend module reads one SDP_API_KEY, so give that key every grant you call through it. See Manage API keys.

Withdrawals and cancellations stay available after production access is removed or new deposits are disabled. A suspended organization still cannot authenticate, and Earn runs only on deployments whose release channel includes it.

Your API key can read every customer wallet your project has recorded. Before any request, check that the signed-in customer owns the ownerAddress you send, for example by verifying a message the wallet signed when the customer linked it. Custody-wallet scopes on the API key do not apply to customer wallets: the customer's signature authorizes each transaction.

Get the backend module

The dashboard generates a TypeScript module for a strategy with every call this guide uses:

  1. In the dashboard, open Markets > Embedded Yield > Integrate.
  2. Choose a strategy. Only fundable strategies can be selected.
  3. Click Copy all code, and save it as a server-only file such as earn.ts. Set SDP_API_KEY in your backend's environment.

The module holds the strategy ID and the API URL for the project you generated it in, so generate it again for production. It needs Node.js 20.18 or later, and @solana/kit only for its fee sponsor signer.

TaskModule function
List the cataloglistEarnStrategies
Quote a depositpreviewEarnDeposit
Build a depositbuildEarnDepositTransaction
Collect signaturessignEarnTransaction, createSponsorSigner
Submit a depositsubmitEarnDeposit
Track a movementwaitForEarnMovement, getEarnMovement
Read balanceslistEarnPositions (every page), getEarnEarnings, listEarnMovements (one page per call)
Withdraw instantlypreviewEarnWithdrawal, buildEarnWithdrawalTransaction, submitEarnWithdrawal
Choose a routegetEarnWithdrawalOptions
Request a withdrawalpreviewEarnQueuedWithdrawal, previewEarnParRedemption, buildEarnQueuedWithdrawalRequest, buildEarnParRedemptionRequest, submitEarnWithdrawalRequest
Track requestsgetEarnWithdrawalRequest, waitForEarnQueuedWithdrawal, listPendingEarnQueuedWithdrawals
Cancel a requestbuildEarnWithdrawalRequestCancellation, submitEarnWithdrawalRequestCancellation

When SDP refuses a request, a module function throws an EarnApiError with status, code, details, and retryAfter. Branch on those, never on the message.

Prefer raw requests? Every function maps to one endpoint in Deposit funds and Withdraw funds.

Make a deposit

StepModule functionEndpoint
1. QuotepreviewEarnDepositPOST /v1/earn/vault-deposit-previews
2. BuildbuildEarnDepositTransactionPOST /v1/earn/external-wallet/deposit-transactions
3. SignsignEarnTransactionIn the customer's wallet
4. SubmitsubmitEarnDepositPOST /v1/earn/external-wallet/deposits
5. TrackwaitForEarnMovementGET /v1/earn/external-wallet/movements/{movementId}

buildEarnDepositTransaction previews the deposit itself when the strategy needs a floor; call previewEarnDeposit too when you want to show your customer a quote first.

signEarnTransaction(built, customerSigner, sponsorSigner?) passes the base64 transaction to your signer, checks that the signed message is unchanged, and adds your sponsor's signature when the build named a feePayer. createSponsorSigner(secretKey, feePayerAddress) makes that sponsor signer from your fee payer's 64-byte secret key, the array a Solana CLI keypair file holds. The Quickstart shows a customer signer for a browser wallet.

Three rules keep deposits safe:

  • Sign exactly what SDP built. The wallet must not change or broadcast the transaction. SDP refuses a changed one with 400.
  • Save before you submit. Store the idempotency key with the signed transaction, then submit. After an uncertain response, retry with both unchanged; see Retry safely.
  • Track to the end. Save movementId and track it until finalized or failed.

Show balances

Read positions for withdrawals, earnings for the totals your customer sees, and movements for their history. Balances and earnings covers every field. Two rules matter most:

  • Show a missing live value as unavailable, never as zero.
  • Read every page of a list. A short list hides money.

Withdraw

  1. Read the position's live withdrawableShares with listEarnPositions, then its routes with getEarnWithdrawalOptions.
  2. For an instant withdrawal, preview, build, sign, submit, and track, as for a deposit.
  3. For a solver queue or operator redemption, create a withdrawal request and track it to fulfilled, cancelled, or failed.
  4. On startup, restore every open request with listPendingEarnQueuedWithdrawals.
StepModule functionEndpoint
Choose a routegetEarnWithdrawalOptionsPOST /v1/earn/external-wallet/withdrawal-options
Withdraw instantlybuildEarnWithdrawalTransaction, submitEarnWithdrawalPOST /v1/earn/external-wallet/withdrawal-transactions, POST /v1/earn/external-wallet/withdrawals
Preview a requestpreviewEarnQueuedWithdrawalPOST /v1/earn/external-wallet/queued-withdrawal-previews
Build a requestbuildEarnQueuedWithdrawalRequestPOST /v1/earn/external-wallet/withdrawal-request-transactions
Submit a requestsubmitEarnWithdrawalRequestPOST /v1/earn/external-wallet/withdrawal-requests
Track a requestgetEarnWithdrawalRequestGET /v1/earn/external-wallet/withdrawal-requests/{withdrawalRequestId}
Cancel a requestbuildEarnWithdrawalRequestCancellation, submitEarnWithdrawalRequestCancellationPOST /v1/earn/external-wallet/withdrawal-request-cancel-transactions, POST /v1/earn/external-wallet/withdrawal-request-cancellations

A withdrawal spends shares, not a dollar amount, and a request transaction is not a payout. Withdraw funds covers each route.

Go live

  • Get production access: request mainnet access and switch to your production project, as steps 1 and 2 of Go to mainnet show. Embedded Yield needs no custody provider or wallet funding.
  • Create a production API key with your Earn grants, and regenerate the backend module in your production project. Strategy IDs differ per environment.
  • Sign with your customer's wallet on solana:mainnet.
  • Support deposit floors. Nearly every production deposit requires minSharesOut. The module sets it from a live preview when the strategy's depositSlippage asked for one at generation, so regenerate it if a build starts requiring a floor.
  • Persist idempotency keys and signed transactions before you submit.
  • Restore open work after every restart: keep tracking each saved movementId that is not finalized or failed, and restore open requests with listPendingEarnQueuedWithdrawals.
  • Confirm investor and wallet eligibility for each strategy you offer; see Strategies.
  • Decide who pays fees, and if you sponsor them, bound and monitor your fee payer; see Fees and sponsorship.