Skip to main content

Overview

Delegated access works by creating a specialized business-controlled user within each end-user’s sub-organization that has carefully scoped permissions to perform only specific actions, such as signing transactions to designated addresses. This can enable your backend to do things like:
  • Automate onchain actions such as staking, redemptions, or limit orders
  • Sign transactions to whitelisted addresses without user involvement
  • Perform scheduled operations (e.g. payouts, rebalances)
  • Respond to specific onchain events programmatically

Implementation flow

You can implement Delegated Access for an embedded wallet in two ways, depending on whether the setup runs from the frontend (recommended) or the backend. This approach uses the end-user’s authenticated session to configure delegated access directly within their sub-organization. The flow is:
  • Create an API-only user (Delegated User) with a P-256 API key authenticator that you control. This key can then be used server-side to sign transactions on the user’s behalf.
  • Define policies for the Delegated User that strictly limit which transactions they are allowed to sign.
Why this approach: This avoids temporarily granting the Delegated User root access — see Caution for more details. Since you’re using the client-side authenticated session, the API-only user is created directly as a non-root user within the sub-organization. Advantages:
  • There’s no possibility of elevated (root-level) access for the delegated user.
Limitations:
  • You can’t assume the delegated user or its policies already exist. Before referencing it, you’ll need to call fetchOrCreateP256ApiKeyUser and fetchOrCreatePolicies to ensure the user and permissions are properly set up.
For a detailed step-by-step guide see Client-side Delegated Access setup.

2. Backend

If you prefer to configure delegated access entirely server-side, the flow differs because the end-user in the embedded wallet model does not hold an API key. In this case, you must:
  • Create the sub-organization with two root users: the end-user and your Delegated User (API key authenticator).
  • Use the delegated access API key to add policies explicitly granting the DA user the limited actions you want them to perform.
  • Update the root quorum so that only the end-user remains a root user.

Caution ⚠️

When the delegated access setup is performed from the backend, your service (not the end-user) initiates and approves the sub-organization creation, delegated user addition, and root quorum updates. This means the delegated user is temporarily added to the root quorum without direct end-user consent. If any of these operations fail — particularly the quorum update — the delegated user may unintentionally retain root privileges, effectively gaining unrestricted access to the user’s wallet.
If you adopt this approach, implement strict validation to confirm that:
  • The root quorum was successfully updated; and
  • The delegated user no longer retains unintended permissions once setup completes.
Advantages:
  • Guarantees that the delegated user exists and can perform the required actions without additional setup.
Limitations:
  • Risk of elevated access if the delegated user isn’t successfully removed as a root user, it may retain unintended control.
  • Since all actions are service-initiated, there’s no explicit end-user approval in this flow.
For most end-user applications, it’s recommended to perform delegated access setup client-side, where all actions are explicitly initiated and approved by the user. For a detailed step-by-step guide see Server-side Delegated Access setup.

Frequently Asked Questions

Policy Design and Creation

Yes — if the delegated access (DA) user is part of the root quorum, they can create policies unilaterally. Once removed from the root quorum, only the remaining quorum member (typically the end-user) can make further policy changes.Note: This can also be done even if the initiating (delegated) user is not a root user, provided a policy explicitly grants them permission to create policies. However, such a configuration should be carefully scoped — additional restrictive policies are typically required to prevent the delegated user from granting themselves broader or unintended access.
When the delegated access setup is performed client-side, all actions are initiated and approved by the authenticated end-user within their session. This ensures full transparency and explicit user consent for every operation.
When performed server-side, the setup happens without direct user involvement. This approach is generally used only in enterprise or custodial environments, where your backend manages sub-organizations and delegated users on behalf of end-users.
For typical end-user applications, the client-side setup is the recommended and more secure approach.
As long as the DA user remains authorized, they can remove policies programmatically. If they’ve been removed from the quorum, policy deletion will require the user’s explicit approval.NOTE: Turnkey is looking to support the concept of ‘one-time-use policies’ to make it easier to manage redundany policies.

Security & Risk Management

Yes — if the key is attached to a broad policy. That’s why it’s important to limit the scope of policies and enforce API hygiene practices. It’s also recommended to have a policy in place that would allow this user to self-delete in case of a potential key leak:
{
  policyName: `Allow the Delegated user to self-delete`,
  effect: "EFFECT_ALLOW",
  consensus: `approvers.any(user, user.id == '${delegated_userid}')`,
  condition: `activity.type == 'ACTIVITY_TYPE_DELETE_USERS' && activity.params.user_ids.count() == 1 && '${delegated_userid}' in activity.params.user_ids`,
  notes: "Allow the Delegated user to delete itself in case of a key leak"
}
In effect, yes. The key difference is that granular policies can restrict what a DA user can do, offering better security hygiene even if there’s still elevated access.
Typically this is a combination of, or all of the following practices, though not exclusive to just these:
  • Using short-lived keys whenever viable
  • Rotating API keys regurarly
  • Monitoring the usage
  • Secure storage (e.g. in HSMs or vaults)

Best Practices

You can define strict transaction conditions. For example:
solana.tx.instructions.count() == 1 &&
solana.tx.transfers.count() == 1 &&
solana.tx.transfers.all(transfer, transfer.to == '<SPECIFIC_ADDRESS>')
You can also consider the following:
  • Recipient address restrictions (ie allowlisting addresses)
  • Contract method selectors
  • Transaction structure invariants
  • Blockhash constraints (on Solana)
Yes — this is a common pattern. You can add a policy per order (limit, stop loss, TWAP, etc.) using either the end-user’s authenticated session or a Delegated Access (DA) user, as long as the DA user has the necessary permissions defined by policy.
Turnkey’s Policy Engine shines through its flexibility. There are many different approaches you can take based on your requirements, but various themes we see include:
  • Using broad policies with business-controlled API keys
  • Using **fine-grained policies, **scoped to predictable transaction shapes
  • Using delegated access to implement limit orders, automation flows, or advanced trading logic (e.g. perps, TWAPs)
  • Ensuring strong operational security (e.g. tight scoping & expiring keys) is increasingly common

EVM and SVM-Specific Strategies

Yes, on Solana via solana.tx.recent_blockhash, which restricts a transaction’s validity to a ~60–90 second window. Not ideal for delayed executions (e.g. limit orders), but useful for immediate, single-use actions.
Yes, though it’s limited today. You can inspect calldata (e.g., using eth.tx.data[...]) and enforce conditions like:
eth.tx.to == '<TOKEN_CONTRACT>' &&
eth.tx.data[0..4] == '<ERC20_FUNCTION_SELECTOR>'
Granular support for calldata parsing and value limits is coming soon.
Not entirely. Even if you allowlist the router, it could still be abused to swap all assets. You can’t control downstream behavior unless you control the contract.
Suggestion: Only allow DA keys to interact with contracts you fully trust or control. Limit scope as much as possible (e.g., to specific instructions, amounts, or recipients).
I