Supported endpoints
Idempotency keys are supported on the following Enclave MPC API endpoints:Idempotency keys are optional. If the
Idempotency-Key header is omitted, the
request is processed normally without idempotency enforcement.Using the iOS, Android and Flutter SDKs
The iOS SDK (8.1.0 and later), the Android SDK (10.1.0 and later) and the Flutter SDK (2.1.0 and later) send the key for you. On iOS and Android, pass it in the request options for eth_sendTransaction, sol_signAndSendTransaction and sol_signAndConfirmTransaction, or in the sendAsset parameters on EVM and Solana chains. On Flutter, pass idempotencyKey to request for the same methods, to sendTransaction on EVM chains, or to sendAsset on EVM and Solana chains. Portal enforces it whether the SDK signs on the device or through the Enclave MPC API.
See the iOS, Android and Flutter guides.
Broadcast RPC methods
Idempotency keys are only compatible with RPC methods that broadcast transactions.How it works
- Include the
Idempotency-Keyheader with a unique value in your request. - Portal creates a record with status
PENDINGand processes the request normally. - On a successful broadcast via an RPC provider, the record is updated to
BROADCASTED. On failure, it is updated toFAILED. - Any subsequent request with the same idempotency key from the same client is rejected with a
409— it will not be re-processed, regardless of the original request’s outcome.
If a request stays in
PENDING status for more than 10 minutes (for example,
due to an unexpected process interruption), it is automatically marked as
UNKNOWN.Expected response handling flow
Follow the expected flow in order to safely retry, handle response codes, and to protect yourself against double spending.- Submit a transaction with a unique idempotency key.
- Add retry logic to handle failures and uncertain outcomes:
a. if the status code is
409and theidisIDEMPOTENT_REQUEST_IN_PROGRESSthen retry with backoff. b. if the status code is409and theidisIDEMPOTENT_REQUEST_ALREADY_COMPLETEDthen stop retrying. The transaction has been broadcast. c. if the status code is409and theidisIDEMPOTENT_REQUEST_PREVIOUSLY_FAILEDthen stop retrying. The original request failed, or its outcome is unknown (for example, a request markedUNKNOWNafter stayingPENDING), so it may have been broadcast. Check the chain or your transaction history before re-submitting with a different idempotency key. Re-submitting without checking risks double spending. d. if the status code is409and theidisIDEMPOTENT_REQUEST_UNEXPECTED_STATEthen stop retrying. The transaction has failed in an unknown state. Follow error handling logic. You may not want to automatically resend without verifying balances first. It is not safe to spend again. You are at risk of double spending. e. if the status code is422then stop retrying. You are submitting different transactions with the same idempotency key. Follow error handling logic.
Key format
V4 UUIDs are a good default choice for idempotency keys (e.g.
550e8400-e29b-41d4-a716-446655440000).Key expiration
Portal keeps an idempotency key for at least 24 hours after the request was last updated, then removes it. Once it is removed, the same key value is treated as a new request, so a retry is protected only while the key is kept. Check the chain before retrying an operation that is older than 24 hours, whatever key you use.Response behavior
Error format
Idempotency errors follow the same structure as other MPC errors documented on the Error Codes page:Example
Best practices
- Generate a unique key per request: Use a V4 UUID or another random identifier with enough entropy to avoid collisions.
- Store the key before sending: Persist the key to your database or logs before making the request, so you can correlate retries.
- Reuse the same key only for identical retries: If you need to retry the exact same operation with the same request body, reuse the key. If any parameters change, generate a new key.
- Handle rejection responses gracefully: When you receive a
409or422response, check theidfield to determine whether the original request is still processing, already succeeded, or failed. - Use distinct keys per logical operation: Each independent transaction or signing operation should have its own unique idempotency key.