Skip to main content
One setup, two effects, one Privy app: execution (Privy signs your users’ trades) and auth (users sign in with a Privy access token). Both turn on when you save Privy as the wallet provider. Privy sign-in is not available on its own, because AGG finds a user’s Privy wallets by the Privy identity that sign-in records.
By default, AGG provisions and signs with its own custodial wallets. If you already run Privy server wallets, you can point AGG at them instead: AGG reads the wallets that already exist in your Privy app and signs against them on your users’ behalf. No new wallets are created, so your users’ balances and addresses are unchanged.

What You Need From Privy

Three values, all from your Privy dashboard:
Privy issues the authorization private key once and cannot recover it. Store your own copy before you leave the Privy dashboard. If you lose it, you must create a new authorization key in Privy and re-save it in AGG.
The key is accepted in any of the shapes Privy hands out: the wallet-auth:MIGH... form, a bare base64 PKCS#8 body, or a full -----BEGIN PRIVATE KEY----- PEM block.

Ownership Model

Wallets must be app-owned: AGG holds the authorization key and signs every wallet call with a privy-authorization-signature header, with no user present. This is not a preference — it is required by how execution works. AGG signs inside detached execution graphs that run minutes or hours after the originating request, and several flows have no user request behind them at all: limit-order fills, settlement webhooks, and managed balance refills.
  • Server-owned wallets (owner is your authorization key or a key quorum containing it) — works.
  • User-owned wallets with your server key as an additional signer (owner: { user_id } + additional_signers: [{ signer_id: <key quorum id> }]) — works, as long as the key quorum contains the authorization key you saved in AGG.
  • User-owned wallets with no additional signer — does not work. Only the user’s own session can sign, and there is no user session at signing time. AGG refuses these at provisioning with PRIVY_WALLET_NOT_SIGNABLE.
By default AGG uses the wallets you already have: your signup flow creates them, and we read them. If your users can reach trading without a Privy wallet, turn on Create wallets for users who have none under Settings → Wallet Provider and enter a Signer ID. AGG then creates one for a chain a user has no wallet on, built the same way as a user-owned wallet you create yourself:
The user owns the wallet, and the signer lets AGG sign trades with no user present. Use the key quorum your backend already attaches to your users’ wallets, so your signer policies and key rotation also cover wallets AGG creates. It must contain the authorization key saved in AGG. AGG never creates an app-owned wallet, and it won’t save the toggle without a signer ID. The toggle only ever fills an empty chain. A chain that already has a wallet is never touched — if we cannot sign with that wallet we report it rather than creating a second one beside it, because your user’s funds may be in the wallet we skipped. Unlike the wallet provider itself, this setting stays changeable for the life of the app: turning it on affects only users who have no wallet yet. AGG finds a user’s wallets in two places, so either ownership model works without extra configuration:
  • User-owned wallets — from the user’s linked accounts. No entity needed.
  • Server-owned wallets — by entity: { id: "<your Privy user id>", type: "user" }. Entity is immutable once set, so a server-owned wallet created without one can never be attributed to a user.
Of the wallets found, AGG uses the first per chain it can actually sign with, proven by signing a harmless message. Both an Ethereum and a Solana wallet are required. If one does not exist yet — for example because your onboarding creates wallets asynchronously — provisioning fails with PRIVY_WALLET_NOT_READY and succeeds on a later attempt once the wallet exists. If your onboarding also creates wallets, possibly after your user has signed in to AGG, leave wallet creation off. Otherwise both sides could create a wallet for the same chain.

Setup — Execution

  1. Go to Settings → Wallet Provider in the AGG dashboard.
  2. Change the provider dropdown from Portal (default) to Privy.
  3. Enter the app ID, app secret, and authorization private key.
  4. Save. Secrets are encrypted before storage and are never returned to the dashboard — the form shows only whether they are set.
  5. Validate. This round-trips your credentials against Privy from the service that actually signs, so a typo fails here rather than hours later inside an execution run.
From then on, the first time each user reaches an execution surface AGG resolves their existing EVM and Solana wallets from your Privy app and signs there.
AGG requires both an EVM and a Solana wallet per user. If your app only provisions Ethereum wallets, resolution fails for that user with a specific error rather than half-provisioning them.

The Provider Lock

User migrations are not supported. If you have wallets created you cannot change wallet provider. Please create a new instance to change providers.
Once any wallet exists for one of your app’s users, the provider is fixed. There is no migration path — the two custodians hold key material in different systems and neither exports it, so switching would strand every existing wallet and the funds in it. The rule is enforced on the API, not just in the dashboard. Credential rotation is never blocked. If your Privy app secret or authorization key leaks, replace it in the same form at any time, lock or no lock. Rotation requires all three fields together — a partial update is rejected, because the three values are one credential and mixing generations produces silent signing failures.

Privy Auth — enabled with the wallet provider

There is no separate Privy setting on the Auth tab. Once Privy is saved as the wallet provider (above), AGG accepts Privy access tokens for the app and verifies them against the Privy app ID in those credentials, using Privy’s published public keys at https://auth.privy.io/api/v1/apps/{appId}/jwks.json. There is no callback URL to register. Unlike the OAuth providers, Privy never redirects — your app holds the token already and posts it to AGG directly.

Exchanging a token

Your frontend obtains a Privy access token through Privy’s own client SDK, then posts it to AGG:
This is the same /auth/verify endpoint used for wallet sign-in — the body is a union on kind, and everything after identity is proven is shared. A success returns the same access + refresh token pair as every other AGG sign-in method. The Privy DID (sub) becomes the user’s provider account ID, so the same person signing into two of your apps gets one identity across both.

Errors

Every failure is a 401 with a message:

Troubleshooting

Validate fails after saving credentials. The message is Privy’s own. A 401 usually means the app secret or the authorization private key does not belong to the app ID you entered — all three must come from the same Privy app. Re-copy them together rather than one at a time. Every Privy sign-in fails with Web3 authentication failed. The tokens your frontend is minting come from a different Privy app than the one saved under Settings → Wallet Provider. This is almost always a staging token being sent to a production app, or vice versa. The provider dropdown is greyed out. Wallets already exist for this app’s users. See The Provider Lock — the fix is a new app, and credential rotation is still available below the dropdown. Validation passes but execution fails later. Validation proves the credentials are accepted by Privy; it does not prove your wallets can meet a signing policy you have configured in Privy. Check that no Privy policy or additional quorum member blocks the authorization key AGG holds.