Skip to main content
When broadcasting transactions through Portal’s Enclave MPC API, network failures, timeouts, or client-side retries can result in duplicate operations. Idempotency keys provide a mechanism to safely retry requests without the risk of processing the same operation twice.

Supported endpoints

Idempotency keys are supported on the following Enclave MPC API endpoints:
MethodEndpointApplicable Conditions
POSThttps://mpc-client.portalhq.io/v1/assets/sendAll requests.
POSThttps://mpc-client.portalhq.io/v1/signSee Broadcast RPC methods.
Idempotency keys are optional. If the Idempotency-Key header is omitted, the request is processed normally without idempotency enforcement.

Broadcast RPC methods

Idempotency keys are only compatible with RPC methods that broadcast transactions.
Chain(s)RPC method(s)
Ethereum / EVMeth_sendTransaction
Solanasol_signAndSendTransaction, sol_signAndConfirmTransaction
Trontron_sendTransaction
Stellarstellar_sendTransaction

How it works

  1. Include the Idempotency-Key header with a unique value in your request.
  2. Portal creates a record with status PENDING and processes the request normally.
  3. On a successful broadcast via an RPC provider, the record is updated to BROADCASTED. On failure, it is updated to FAILED.
  4. 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.
  1. Submit a transaction with a unique idempotency key.
  2. Add retry logic to handle failures and uncertain outcomes: a. if the status code is 409 and the id is IDEMPOTENT_REQUEST_IN_PROGRESS then retry with backoff. b. if the status code is 409 and the id is IDEMPOTENT_REQUEST_ALREADY_COMPLETED then stop retrying. The transaction has been broadcast. c. if the status code is 409 and the id is IDEMPOTENT_REQUEST_PREVIOUSLY_FAILED then stop retrying. The transaction failed cleanly. Follow error handling logic to re-submit with a different idempotency key. It is safe to submit again, without risk of double spending. d. if the status code is 409 and the id is IDEMPOTENT_REQUEST_UNEXPECTED_STATE then 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 is 422 then stop retrying. You are submitting different transactions with the same idempotency key. Follow error handling logic.

Key format

RuleDetail
Max length255 characters
Allowed charactersA-Z, a-z, 0-9, -, ., _, ~ (RFC 3986 unreserved characters)
V4 UUIDs are a good default choice for idempotency keys (e.g. 550e8400-e29b-41d4-a716-446655440000).

Key expiration

Idempotency keys expire after 24 hours. After expiration, the same key value can be reused for a new request.

Response behavior

ScenarioStatusError ID
First request with a new key200
Same key, same body — original still processing409IDEMPOTENT_REQUEST_IN_PROGRESS
Same key, same body — original succeeded409IDEMPOTENT_REQUEST_ALREADY_COMPLETED
Same key, same body — original failed409IDEMPOTENT_REQUEST_PREVIOUSLY_FAILED
Same key, same body — original in unknown state409IDEMPOTENT_REQUEST_UNEXPECTED_STATE
Same key, different body422IDEMPOTENCY_KEY_REUSED
Once a request is made with an idempotency key, that key cannot be reused for any new request within the 24-hour expiration window — even if the original request failed. Generate a new key for each new operation.

Error format

Idempotency errors follow the same structure as other MPC errors documented on the Error Codes page:
{
  "id": "IDEMPOTENT_REQUEST_ALREADY_COMPLETED",
  "message": "Request already completed"
}

Example

curl --request POST \
  --url https://mpc-client.portalhq.io/v1/assets/send \
  --header 'Authorization: Bearer [token]' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000' \
  --data '{
  "share": "share",
  "chain": "ethereum",
  "to": "0xdFd8302f44727A6348F702fF7B594f127dE3A902",
  "token": "NATIVE",
  "amount": "0.0001"
}'

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 409 or 422 response, check the id field 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.