Skip to main content
Check out working demo: Live Demo
Repo: privy-machines-cards-example
Deploy with Vercel
This guide is for partners already using Privy embedded wallets. Scope:
  • Partner API integration (/partner/v1) only.
  • Server-side Privy wallet control for signing and sending transactions.
  • Not a guide for Machines first-party web auth endpoints like /v1/auth/email/exchange.

Architecture

  • Your backend calls Machines Partner API.
  • Your backend also calls Privy server APIs for embedded wallet signing/transaction execution.
  • End users do not need wallet popups for embedded-wallet withdrawal execution paths.

Prerequisites

  • Privy app configured for your environment.
  • Privy embedded Ethereum wallets enabled.
  • Privy authorization key configured for server-side wallet access.
  • Machines partner API key.
  • Partner backend can securely store:
    • Machines partner key
    • Privy app secret
    • Privy authorization private key

Privy Embedded Wallet Checklist (Server-Side)

Before sending live traffic, confirm:
  1. Wallet control path
    • You can resolve each user’s Privy embedded Ethereum wallet.
    • Your backend can sign/send on that wallet using an authorization context (authorization key, user JWT, or a custom sign function).
  2. Signer binding
    • The wallet is configured with a signer path your backend can use (for example an authorization-key signer).
    • The authorization private key is in a secrets manager, never in client code.
  3. Policy attachment
    • Policy IDs for withdrawal execution are attached to the signer/wallet.
    • Rules cover eth_sendTransaction and eth_signTypedData_v4.
  4. Sponsorship setup
    • Gas sponsorship is enabled in Privy Dashboard for the chains you support.
    • Your backend passes sponsor: true for sponsored sends.
  5. Status monitoring
    • You have webhook or polling support for transaction status reconciliation.
Per user, persist at least:
  • machinesUserId (your Machines partner user mapping)
  • privyUserId (Privy user id)
  • embeddedWalletAddress (and walletId if you store it)
  • linkedAt / lastUsedAt
Per execution, persist:
  • partner idempotency key
  • Privy transaction_id / user operation hash (if returned)
  • final onchain transaction_hash
  • execution path (controller_v1 or coordinator_v2)

End-to-End Partner Flow

Call POST /partner/v1/users/resolve from your backend.

2) Create a scoped partner session

Call POST /partner/v1/sessions. Use the smallest scope set needed for the current operation.

3) Run core Machines flows

Use the partner session token for:
  • KYC
  • Agreements
  • Cards
  • Balances
  • Deposits
This guide focuses on withdrawals because that is where embedded-wallet server signing is most relevant.

4) Withdrawal flow (partner + Privy execution)

Call sequence:
  1. GET /partner/v1/withdrawals/assets
  2. POST /partner/v1/withdrawals/range
  3. POST /partner/v1/withdrawals/estimate
  4. POST /partner/v1/withdrawals (include adminAddress)
  5. Execute onchain via Privy wallet APIs
Create request example:
Use the same Idempotency-Key when retrying POST /partner/v1/withdrawals after pending responses.

Execution Model and Contract Call Paths

Machines withdrawal create response includes:
  • execution.callTarget
  • execution.callPath (controller_v1 or coordinator_v2)
  • parameters (7-arg Rain executor payload)
Always send the transaction to execution.callTarget.
  • controller_v1:
    • Call 7-arg withdrawAsset(...)
    • Selector: 0xe167d26a
    • Signature: withdrawAsset(address,address,uint256,address,uint256,bytes32,bytes)
  • coordinator_v2:
    • Build admin typed-data signature first
    • Call 10-arg withdrawAsset(...)
    • Selector: 0x4b268241
    • Signature: withdrawAsset(address,address,uint256,address,uint256,bytes32,bytes,bytes32[],bytes[],bool)

v2 Typed Data Shape

Partner-side Privy Execution (TypeScript)

Privy Method Allowances and Policy Design

For this flow, allow at minimum:
  • eth_sendTransaction
  • eth_signTypedData_v4 (needed for coordinator_v2)
Use dynamic target address rules:
  • Do not hardcode a single contract address.
  • Restrict to to the execution.callTarget values returned by Machines for supported chains/contracts.
Recommended condition dimensions:
  • ethereum_transaction:
    • chain_id in allowed chains
    • to in your dynamically maintained allowlist
    • value == 0
  • ethereum_calldata:
    • ABI for Rain withdrawal functions
    • function_name == "withdrawAsset"
  • ethereum_typed_data_domain and ethereum_typed_data_message (for v2):
    • domain name/version chain checks
    • typed message fields constrained to your expected flow
Policy modeling tip:
  • Keep reusable condition sets per chain + function family.
  • Compose those sets into authorization policies instead of duplicating large JSON blocks.

Example Policy Shape (Ethereum)

This is a concrete starting shape aligned with Privy’s Ethereum policy examples:
Practical guidance:
  • Use condition sets for to addresses and verifying contracts.
  • Keep policy values as strings matching Privy examples (chain_id, hex values, address strings).
  • Treat this policy as an allowlist; add explicit DENY rules only when needed.

Gas Sponsorship and Transaction Lifecycle

  • Use sponsor: true for embedded wallet sends to sponsor gas.
  • For gas-sponsored EVM sends, backend responses can return before final onchain hash.
  • Handle async status fields:
    • transaction_id (Privy transaction id)
    • user_operation_hash
    • hash may be empty until confirmation.
  • Track final status via:
    • Privy webhooks (recommended)
    • or transaction-status API polling by transaction_id.
  • Add operational monitoring:
    • submission failures
    • hash timeouts
    • onchain confirmation delays
    • spend anomalies and abuse patterns

Known Constraints and Gotchas

  • Production destination coverage (BTC/SOL/EVM/etc.) depends on live relay routes. Always query:
    • GET /partner/v1/withdrawals/assets
    • POST /partner/v1/withdrawals/range
    • POST /partner/v1/withdrawals/estimate before quoting or creating withdrawals.
  • Sandbox source is fixed to rUSD on Base Sepolia.
  • If withdrawal signature response is status: pending, retry with the same idempotency key.

Further Reading (Privy)