ClaimModal returns a failed or rejected EVM deposit to the user, on the chain it came from. The user pastes the deposit’s transaction hash, the modal looks it up, and the user signs to authorize where the funds go.
There is no source chain to pick — the hash is looked up across every chain, so a deposit that landed on a chain your deposit flow doesn’t offer is still claimable.
On the
./claim subpath. Applies to deposits in failed or rejected status. See
troubleshooting for the operator-side equivalent in the
dashboard.How authorization works
The deposit’srecipient — the in-app wallet the funds were headed to — signs an EIP-712 struct naming the deposit and the destination. The service verifies that signature against the same recipient before moving anything.
That signature is the authorization, so this needs no backend of your own. The request goes from the browser through your proxy, which contributes only your project API key. That key cannot move funds through this route without a signature, so there is nothing for a page to borrow.
signRecovery and expects a signature back. For an embedded wallet that is headless, so the user sees a single confirmation at most.
The signer is the deposit’s
recipient, never the deposit account. Deposit accounts
are service-managed and hold no user
key, so nothing can sign for them.POST /deposits/recover alongside the GET /deposits used for the lookup. Both are in the reference proxy.
Signing
Return whatever therecipient address’s own verifier accepts. signRecovery receives the exact typed data to sign, so you never construct it yourself.
ox is already a dependency of viem, so this adds nothing to your install.
What the user signs
Handling failures
The response carries a machine-readablecode. Switch on that rather than the HTTP status — the code is the contract, and the correct advice differs between codes that share a status.
The modal maps these to copy and to whether it offers a retry, so you get this for free. Handle them yourself only if you drive your own UI.
When the user can’t sign
Some recipients have no key to sign with: a wallet the user has lost, or a recipient your app controls rather than the user. For those,@rhinestone/deposit-modal/server exports createRefundHandler, which authorizes on your say-so instead of a signature — you decide who the caller is, and it verifies the deposit belongs to them before spending your API key.
authorize returns the deposit recipient the caller owns, or null to reject with 401. Add refundDestination to pin where the money goes rather than letting the request choose. depositRecipient never decides the destination — the handler lists deposits settling to it and rejects a txHash that isn’t among them.
It returns a (Request) => Promise<Response>, so it mounts in any fetch-based runtime: Next.js route handlers, Hono, Bun.serve, Cloudflare Workers.
Props reference
Required
signRecovery receives:
Prefills
Backend
Display
Callbacks
Lifecycle events
ClaimLifecycleEvent shares no variants with the deposit or withdraw unions, so don’t reuse a handler across them.