Raw signing API
For integrations that build and broadcast their own transactions (perps
venues, bridges, DEX order signing), ZenVault signs through /v1/signing with
the MPC keys of one of your workspace accounts (accountId); you cannot sign
for vaults you don't own.
All POSTs are asynchronous and honor Idempotency-Key. The response is a signing
request in state pending; poll GET /v1/signing/{id} (~2s interval) until a
terminal state. result carries the signature once state is completed.
(completed is the success terminal; failed / cancelled / timed_out are
the failure terminals.)
| Endpoint | Signs | result shape |
|---|---|---|
POST /v1/signing/eth-sign | A 32-byte hash (hash) | { hash, signature } |
POST /v1/signing/personal-sign | EIP-191 message (data: UTF-8 or 0x-hex) | { hash, signature } |
POST /v1/signing/eth-sign-typed-data | EIP-712 payload (data: JSON string, version: v1/v3/v4) | { hash, signature } |
POST /v1/signing/eth-sign-transaction | A full EVM tx (to, value, gasLimit, nonce, data?, gasPrice or maxFeePerGas+maxPriorityFeePerGas) | { txHash, signedTransaction, signature } |
POST /v1/signing/mpc-sign | Raw payloads (signAlg: secp256k1/ed25519, dataList: [{ data }]) — e.g. Solana tx messages | { signatures: [{ data, signature }] } |
All EVM endpoints take chainId. ZenVault does not run an RPC client for this
path: you supply gas, nonce and chainId, and you broadcast the returned
signedTransaction.
:::warning You own the authorization
ZenVault signs exactly what you submit — there is no amount/destination
policy on raw signing the way there is on transfers. Gate calls
behind your own allowlist (contract addresses, function selectors, max value)
before they leave your service. Screening still
applies to eth-sign-transaction.
:::
Example — sign & broadcast a raw EVM tx
REQ=$(curl -s -X POST "$BASE/v1/signing/eth-sign-transaction" \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"accountId":"<ACCOUNT_ID>","chainId":42161,"to":"0x...","value":"0",
"gasLimit":120000,"nonce":7,"data":"0x095ea7b3...",
"maxFeePerGas":"30000000000","maxPriorityFeePerGas":"1500000000"}')
ID=$(echo "$REQ" | jq -r .id)
# poll until completed, then broadcast result.signedTransaction via your own RPC
curl -s "$BASE/v1/signing/$ID" -H "Authorization: Bearer $KEY" | jq .result