Skip to main content

External signing

Sign an EVM write with a key the platform never holds. A transaction preparation binds the write, records your submission and links it to one request.

External signing lets the holder of a custody account sign a write on an EVM network with a key the platform never holds, and still have the platform record that write in its transaction lifecycle. It works through a transaction preparation, a record that binds one write, its signer and its network for ten minutes. A retry of the same prepare or finalise call records one write, and a new preparation is a new write.

The preparation covers custody accounts linked through Privy, Dynamic or a browser wallet, the three providers that leave signing to the holder. The key sits with the holder's wallet or wallet provider. The alternative is the regular API route, where the platform asks a managed custody provider to sign for the account.

Keeping keys in your own HSM or custody system

Choose one of two setups.

  • Connect it as a managed custody provider. The platform connects to IBM Digital Asset Haven, DFNS, Fireblocks, Luna HSM, Ripple custody and Utila as managed providers, and its own platform key store is managed too. The key stays in the HSM or the provider's system, the provider's policy or quorum decides whether to sign, and the platform asks for each signature. This is managed signing, and the preparation routes on this page refuse these accounts. The custody provider page describes the providers, and the custody approval page describes how their policy holds a signature.
  • Hold the key as a self-custody account and sign through preparations. The account's EVM address is linked to a self-custody provider configuration. You sign each prepared write yourself, and the platform checks the result against the preparation. An HSM the platform has no managed connector for can take this route when a wallet that speaks EIP-1193 fronts its EVM key and is linked as a browser wallet. That wallet signs the challenge that links the address, then signs each prepared write.

On the second route the platform never receives the signed transaction bytes. For a plain transaction, your wallet or service sends the transaction to the network and then reports the sender, the nonce, which is the sending address's sequence number, and the transaction hash. The platform checks what you report against the frozen write and follows the nonce on the network. It does not check which device produced the signature, so the evidence that a key sits in an HSM comes from your own key management.

Linking the address starts with POST /api/v2/users/me/custody-account-control-preparations, which returns a challenge for one enabled self-custody provider configuration and one EVM address. Choose investor custody walks through the linking calls.

Signing setups the preparation accepts

The preparation records its signing method in signingMethod. Two values are possible on these routes, and the user-operation method has two ways to reach the network.

Setup
signingMethod
Executor, the address that sends the write
Who signs
Who sends it to the network
Request state at finalise
Browser wallet transaction
eip-1193-transaction
The custody account's own address
The holder's wallet
The holder's wallet
BROADCASTING
Owner-signed user operation
eip-4337-user-operation
The participant's smart wallet
The holder, as the smart wallet's owner
The platform
QUEUED, then execution starts
Provider-submitted user operation
eip-4337-user-operation
The participant's smart wallet
The holder
The holder's wallet provider
BROADCASTING

A smart wallet is a contract account that acts through user operations, signed instructions an EntryPoint contract validates and executes. User operations covers the bundler that sends them.

The platform picks the method when you prepare. You ask for executionMode prefer-aa, which uses the smart wallet, through account abstraction, when it can, or direct. With prefer-aa the preparation is an owner-signed user operation, and the smart wallet is the executor, when two conditions hold: the provider supports account abstraction, and the participant has a smart wallet on that network whose owner is the custody account's address. In every other case, and with direct, the preparation is a browser wallet transaction from the custody account's own address. The user-operation path needs a smart wallet with a single owner key. A smart wallet that uses weighted signers is refused with a binding conflict.

The preparation routes refuse managed signing. A custody account whose provider holds the key gets DALP-14032 on the prepare call, and a finalise call that carries a managed authorisation gets DALP-14033. For that account, send the write through its regular API route.

Who can prepare and sign

The three preparation routes accept a signed-in session or an API key, and they require no organisation role. The custody account decides eligibility. It must be active, belong to the participant the request acts as, and hold an eligible organisation context on an enabled EVM network. Its provider configuration must serve that network, and its executor wallet must not be archived. A request that fails any of these gets DALP-14032.

A preparation belongs to the user and organisation that created it. Any other user or organisation that reads or advances it gets DALP-14031, the same answer as for a preparation that does not exist.

What a preparation binds

The prepare call fixes these facts and stores a digest over them: the calling user, the participant, the organisation, the custody account, its provider configuration, the network, the executor and the source address, the operation's kind and its digest, the summary, the signing method and the expiry. Each later claim, attachment and finalise call is checked against the stored facts it touches.

Send an Idempotency-Key header on the prepare call. The key belongs to the calling user. A repeat with the same key and the same binding returns the stored preparation. The same key with a different binding is refused with DALP-14033. Without the header, each call creates a new preparation.

The preparation expires ten minutes after it is created. A preparation that nobody has claimed cannot be claimed or finalised after that time.

Steps from prepare to transaction request

  1. Prepare the write

    Call POST /api/v2/transaction-preparations with custodyAccountId, executionMode, a summary and the operation: its kind, contractAddress, callData and an optional value. The response is the preparation in state prepared. It names the signingMethod, the executor, the expiresAt time and, for a user operation, preparedUserOperation with the userOperationHash and the previewData of the operation.

    For a user operation the platform builds a preview before you sign. When the organisation's gas payer on that network is Paymaster-paid, the preview carries the paymaster's sponsorship, so the smart wallet pays no gas. Otherwise a smart wallet that cannot cover the gas and the native currency the operation sends gets DALP-14034, with smartWalletAddress, heldWei and the missing shortfallWei. A smart wallet can hold one open owner-signed operation per network. A second one is refused with DALP-14033 until the first finishes or expires.

  2. Read the preparation

    GET /api/v2/transaction-preparations/{preparationId} returns the bound write and its progress: the signing method, the expiry, the submission claim, the attached handle or artifact and, once finalised, transactionRequestId.

  3. Claim the submission before anything is sent

    Call POST /api/v2/transaction-preparations/{preparationId}/transitions with action set to claim. A claim reserves one submission attempt. Repeating a claim with the same claimKey succeeds, so choose a key you can regenerate.

    • Browser wallet transaction. The claim has submissionKind eip-1193, a claimKey, and the sender, chainId, nonce, to, value and data you are about to send. They must match the prepared sender, network, destination, value and call data. The claim reserves that sender, network and nonce against every other preparation. The body also requires a startBlock, but the platform ignores its value and records the network's current block as the start of its search for the transaction.
    • Provider-submitted user operation. The claim has submissionKind provider-handle and a claimKey. It applies only to a user-operation preparation, and the key cannot belong to another preparation under the same provider configuration.
    • Owner-signed user operation sent by the platform. Make no claim. Go to finalise.
  4. Sign the write, and send it from your wallet

    For a browser wallet transaction, the wallet signs and sends the claimed transaction. Once it is sent, the nonce is used and the claim can no longer be abandoned.

    For a user operation, sign the userOperationHash the preparation returned. The platform recovers the signer from that hash with no prefix added. A personal-message signature adds a prefix, so it does not recover to the account. A typed-data signature works when its hash equals userOperationHash.

    On the provider path, record the provider's handle with action attach and an attachment of claimKey and providerHandle. The handle must name the active claim. A repeat of the same handle succeeds, and a different handle is refused with DALP-14240.

  5. Finalise

    Finalising cannot be undone: a consumed preparation stays linked to its transaction request. It links the preparation to a request and does not prove the chain executed the call, so follow the request to a terminal status.

    Call the transitions route with action finalize and an artifact whose kind matches the prepared signing method. The artifact also carries a _tag: Eip1193TransactionAuthorization for a browser wallet transaction and Eip4337UserOperationAuthorization for a user operation.

    • Browser wallet transaction. The artifact carries sender, chainId, nonce and transactionHash, which is null when the wallet returned no hash. It must match the persisted claim.
    • Owner-signed user operation. The artifact carries userOperationHash and signature. The platform checks that the hash is the prepared one, recomputes it from the operation's own fields, requires a canonical signature and verifies that it recovers to the custody account's address. The request enters the queue and the platform starts execution.
    • Provider-submitted user operation. The same artifact, finalised against the claim that holds the handle. The platform checks the hash against the prepared one. It does not recompute the hash or verify the signature on this path, because the provider submitted the operation. The EntryPoint verifies the signature when it executes the operation, so a bad signature fails on chain.

    The response is the preparation in state consumed, with transactionRequestId. A repeat with the same artifact returns the same request, and a different artifact is refused with DALP-14033.

  6. Follow the transaction request

    Read GET /api/v2/transaction-requests/{transactionId}, or its /stream, until status is COMPLETED, FAILED, DEAD_LETTER or CANCELLED. Keep reading a DEAD_LETTER request, one the platform stopped retrying, while its mayStillLand is true, because its call can still land. A write that your wallet already sent enters as BROADCASTING, and confirmation tracking follows a request that carries a transaction hash. An owner-signed user operation enters as QUEUED and runs the platform's execution from there.

To release a provider claim that has no handle, call the transitions route with action abandon. The preparation returns to prepared. Claim again with a new key, because the preparation refuses every key it has abandoned. A browser wallet claim cannot be abandoned, because its nonce may already have reached the network. A claim that holds a provider handle cannot be abandoned either.

Preparation states

A preparation moves through four states and ends consumed or expired.Four states: prepared, submission-claimed, consumed and expired. A claim moves prepared to submission-claimed, and an abandon moves a provider claim back to prepared. Finalise moves submission-claimed to consumed. Finalising an owner-signed user operation moves prepared straight to consumed, and the platform submits it. Ten minutes passing moves an unclaimed preparation to expired. For an expired browser wallet claim, reconciliation moves it to consumed when the wallet's nonce moved past the claim, and to expired when the nonce is still open.ClaimAbandonFinaliseNonce movedFinalise, platform submitsTen minutes passNonce still openpreparedbound for ten minutessubmission-claimedone attempt reservedconsumedlinked to one requestexpiredprepare againa call you maketime passing or platform reconciliationA preparation moves through four states and ends consumed or expired.The same figure as the desktop variant, drawn portrait for phone widths. A claim moves prepared to submission-claimed, and an abandon moves a provider claim back. Finalise moves submission-claimed to consumed, and finalising an owner-signed user operation moves prepared straight to consumed. Ten minutes passing moves an unclaimed preparation to expired. Reconciliation of an expired browser wallet claim moves it to consumed when the nonce moved, and to expired when the nonce is still open.ClaimAbandonFinaliseNoncemovedNoncestill openTenminutes passPlatformsubmitspreparedten minutessubmission-claimedone attemptconsumedone requestexpiredprepare againa call you maketime or reconciliation

Your calls move a preparation through prepared, submission-claimed and consumed, and time or reconciliation moves it to expired or consumed.

A preparation has four states, and the read returns the current one in state.

  • prepared: the write is bound and nothing is claimed. A claim moves it to submission-claimed. Finalising an owner-signed user operation moves it straight to consumed, and the platform submits the operation.
  • submission-claimed: one submission attempt is reserved. Finalise moves it to consumed. An abandon moves a provider claim without a handle back to prepared.
  • consumed: the preparation is permanently linked to its transaction request.
  • expired: the write can no longer be claimed or finalised, and no transaction request exists for it. Prepare the write again.

An unclaimed preparation expires ten minutes after it is created. A claim or finalise call after that time is refused with DALP-12012. The platform records the expired state when it refuses a claim or a finalise. Until then, the read can still show prepared with an expiresAt time in the past.

A claimed provider submission does not expire on its own. Finalise it, or abandon it while it has no handle.

When a browser wallet claim outlives its preparation

A browser wallet claim whose ten minutes have passed is checked by the platform's background reconciliation, which reads the sender's nonce on the network.

  • When the pending nonce equals the reserved nonce and the confirmed nonce has not passed it, nothing was sent. The preparation expires and the claim key is retired.
  • When the pending nonce has moved past the reservation but the confirmed nonce has not, the wallet sent something that the network has not mined. The platform consumes the preparation and records a BROADCASTING request with the claimed sender, network, nonce and start block, and no transaction hash. It starts no new execution.
  • When the confirmed nonce has moved past the reservation, the platform looks for the transaction that used the nonce in a finalised block. When that transaction has the claimed destination, value and call data, the platform consumes the preparation and records a BROADCASTING request with that transaction hash, so confirmation tracking follows it. When it finds no such transaction, or the transaction carries another call, it leaves the preparation as it is.
  • A nonce slot that is blocked, or that the network does not answer for, leaves the preparation as it is.

A request recorded without a transaction hash shows only that the wallet sent some transaction at that nonce. It does not prove that the intended call succeeded, and confirmation tracking skips a request without a transaction hash. Check the network for the wallet's transaction at that nonce before you send anything again.

Writes that need more than one step

Some Console and Investor portal flows need several writes in order, such as an approval followed by a purchase. The public prepare route prepares one operation. A platform flow prepares the sequence itself. For a smart wallet it prepares one user operation that carries the extra calls, with the final write last. For a plain wallet it prepares one linked preparation per step, and the read names the earlier step a preparation waits on.

A linked step can be claimed only after the earlier step's transaction request is COMPLETED. Until then, its claim is refused as pending, even when the earlier step is only claimed. When the earlier request failed, was cancelled, or was dead-lettered with mayStillLand reading false, the claim is refused as failed and the whole sequence must be prepared again. While a dead-lettered earlier request may still land, the claim stays refused as pending. A step whose own time ran out, or whose earlier step expired, is refused as expired.

Refusals and what the caller does

When the preview of an owner-signed write reverts, the call answers 422 with the contract's own error, such as DALP-1009 for a compliance refusal, or DALP-0447 with revertData when the catalogue does not know the revert. No preparation is stored. A token write that a browser smart wallet signs answers the same way, with the contract's error instead of a generic queue refusal. A preview that the platform refuses without revert bytes stays a binding conflict, DALP-14033.

Kind
Code
HTTP
What the platform observed
What the caller does
Expired
DALP-12012
410
The ten minutes ran out before an unclaimed preparation was claimed or finalised, or an earlier step of its sequence expired.
Prepare the write again. Nothing was recorded as a transaction request.
Claim conflict
DALP-14240
409
The preparation already holds a different claim, the claim key was abandoned, the sender and nonce belong to another preparation, the claimed transaction is not the prepared one, the artifact has no matching claim, or the provider handle differs.
Read the preparation. Resume the claim you hold, or wait until the other preparation that holds the nonce is sent or expires.
Binding conflict
DALP-14033
409
The call does not match how the preparation is bound: another signing method or executor, a managed authorisation, a signature that does not recover to the account, a reused idempotency key with another binding, a smart wallet with weighted signers, or a second open owner-signed operation on the same smart wallet.
Send the claim, attachment or artifact that matches signingMethod and executor. Use a new idempotency key for a different write.
Preimage mismatch
DALP-14241
409
The user operation hash in the artifact is not the prepared one.
Sign the userOperationHash the preparation returned.
Pending
DALP-14429
409, retryable
An earlier step of the sequence has no completed transaction request.
Wait for the earlier step to complete, then claim again.
Failed
DALP-14430
409
An earlier step of the sequence failed, was cancelled, or was dead-lettered and cannot land.
Prepare the sequence again. This step can never be claimed.
Not found
DALP-14031
404
No preparation with this id belongs to the calling user and organisation.
Use the id the prepare call returned to you.
Ineligible account
DALP-14032
422
The custody account is managed, inactive, held by another participant, not eligible on this network, or its executor wallet is archived.
Prepare with a self-custody account you hold on that network.
Smart wallet gas
DALP-14034
422
The smart wallet cannot cover the operation's gas and the native currency it sends, and the paymaster does not pay for it. data names smartWalletAddress, heldWei and shortfallWei.
Send at least shortfallWei to the smart wallet, then prepare again.

The Platform API error reference lists each code with its full text.

In the Console and the Investor portal

The Console's token transfer sheet and the Investor portal use the browser wallet path for a custody account linked to a browser wallet. Both need writable browser storage and the browser's lock API. They store each submission attempt, so a reload resumes a matching recorded attempt and does not send it again. When a submission stops, the Console names the reason with messages such as these:

  • “The transaction expired before the wallet sent it, so the platform can no longer record it. Start the transaction again.”
  • “Another session holds the wallet submission for this transaction. Finish it there.”
  • “Another transaction from this wallet holds its next nonce until it is sent or expires. Finish that transaction, or start this one again in ten minutes.”
  • “The wallet may have sent the transaction, but the platform could not record it. Check the wallet's activity before sending anything again.”

Terms that sound similar

Three other pages use a similar term for something different.

  • The custody approval page describes an external custody provider or HSM whose policy decides whether to release a signature the platform asked for. That is managed signing, which these routes refuse.
  • Multisig describes an external seat: a signer address on a weighted smart wallet with no platform identity behind it. A weighted smart wallet cannot use the owner-signed path on this page.
  • Single sign-on describes external sign-in to the Console through an OpenID Connect identity provider. It decides who signs in, and it signs no transaction.

Read custody provider for the managed and self-custody providers, smart wallet for the account an owner-signed operation spends from, Embed a Privy wallet for a worked user-operation example, and Transaction lifecycle for the path a request follows once it is recorded.

Was this page helpful?