User Wallets API
User-wallet routes operate on the authenticated user’s embedded wallet. Routes that create, list, or revoke additional signer credentials require a personal user session with recent MFA. UsewalletIndex to target a non-default wallet. When omitted, Steward uses walletIndex: 0.
Encrypted Private Key Import
Encrypted import lets an authenticated user import an existing EVM or Solana private key into an embedded user wallet without sending a top-level plaintextprivateKey field. The flow is exposed
through the API and SDK, and @stwd/react includes a hosted import component that encrypts the key
client-side before submit. Sessions are bound to the selected wallet index so one indexed wallet
cannot consume another wallet’s import.
STEWARD_ALLOW_PRIVATE_KEY_IMPORT=true and
STEWARD_ALLOW_USER_PRIVATE_KEY_IMPORT=true
Request headers: Authorization: Bearer <user-session-jwt> and
Content-Type: application/json. Responses that carry import-session material
are returned with Cache-Control: no-store.
Initialize
Submit
Encrypt the private key client-side withX25519-HKDF-SHA256-AES-256-GCM using the init public key.
The AES-GCM AAD is the importSessionId; the HKDF info string is:
appClientId when the returned AAD value is null.
Submit only the encrypted envelope:
walletIndex is optional only when importing the default wallet. If supplied,
it must match the walletIndex returned by init. Wrong-index submits return
400 and do not consume the original session; a later submit with the original
index can still succeed. Once the tenant, user, app client, agent, chain, wallet
index, envelope shape, and decrypted key validate, Steward atomically consumes
the session before writing wallet storage. Replay after a successful submit
returns 400 with "Encrypted import session is invalid or expired".
Session records are serialized through Steward’s KeyStore envelope, then stored through the shared
auth StoreBackend (RedisBackend when Redis is configured, otherwise Postgres auth_kv_store
when migrations are available, otherwise memory for local/dev fallback). This is not an HSM-backed
import-session store. Replay, expiry, tenant/user/app/wallet mismatch, malformed envelopes, and
top-level privateKey fields fail closed. Successful import writes encrypted vault storage,
reapplies user-wallet default policies, emits redacted audit/webhook metadata, and returns:
SDK
cURL
/ready probe reports
checks.importSessionStore.source and fails production readiness when import sessions are backed by
memory unless STEWARD_ALLOW_MEMORY_IMPORT_SESSION_STORE=true.
Additional Signer Credentials
Additional signers let a user create bounded delegated credentials for one wallet index without exporting the wallet private key. User-wallet signers are always HMAC delegated signers and are limited to signing permissions such assign_message and sign_transaction. Private-key export,
recovery, owner, policy, and quorum permissions are rejected by the API.
The credential secret is generated by Steward and returned only in the create response. It is never
returned by list or revoke responses, and signer metadata redacts credential hashes.
List Signers
Create Signer
credentialSecret, policyIds, publicKey, or non-HMAC key settings. Steward
generates the signer credential and stores only a server-side hash.
Response:
credentialSecret immediately. Steward displays it once and cannot retrieve it later.
Revoke Signer
status: "revoked" and does not include credentialSecret.