Before you encrypt
This guide assumes you already have a fully signed request payload. Encryption is the confidentiality step between signing and submission. Do not mutate the request after signing and before encryption.Encrypt the signed payload correctly
- Fetch the operator public key from
GET /v2/encryption-key. - Serialize the signed request payload to JSON bytes exactly as it will be submitted.
- Generate a fresh ephemeral private key for this submission attempt.
- Derive the shared secret with ECDH against the operator public key and compute
keccak256(shared_secret). - Use the first 16 bytes of that hash as the AES-GCM key.
- Generate a fresh 12-byte nonce.
- Prefix the plaintext request bytes with their 4-byte big-endian length.
- Encrypt that payload with AES-GCM.
- Assemble the final byte payload in the required order:
ciphertext || tag || nonce || client_public_key_compressed. - Submit those bytes to
POST /v2/request.
Retry posture
| Error class | Retry posture |
|---|---|
InvalidEncryption | Rebuild fresh encryption material and resubmit |
RateLimit | Retry with bounded backoff and jitter |
NotAcceptingRequests | Pause writes, confirm operator readiness, then retry |
ServiceUnavailable | Retry only after the transient condition clears |
SafetyFailure | Do not blind-retry; fix the underlying request or account state first |
Practical checks before you hit submit
- Confirm the payload was signed before encryption, not after.
- Confirm the request bytes were not reformatted after signing.
- Confirm the operator public key came from the current
GET /v2/encryption-keyresponse. - Confirm you are posting bytes, not a JSON wrapper, to
POST /v2/request.
Where builders usually go wrong
| Mistake | Consequence |
|---|---|
| Encrypting an unsigned request | The request reaches the operator but still fails authorization |
| Mutating the request after signing | Signature recovery fails even though encryption succeeded |
| Reusing ephemeral key material or nonce | Invalid or unsafe encryption behavior |
Retrying SafetyFailure unchanged | Repeated deterministic rejection instead of progress |