Signing needs a token carrying the sign scope. Both routes address a public
key directly — you never walk account → wallet → address to sign.
Sign a message
curl -X POST "$API_URL/sign/message" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{"address":"0xabc…","message":"hello from tee-docker"}'{ "address": "0xabc…", "signature": "0x…" }Choosing an encoding
Omit encoding and the address signs with its VM’s natural default. Name one
explicitly when the verifier expects a particular scheme:
encoding |
Use when |
|---|---|
personal_sign |
The verifier expects the EIP-191 personal-message prefix |
raw |
You are signing pre-hashed or otherwise pre-framed bytes |
ed25519 |
Signing with an ed25519 key |
With an explicit encoding the response may also carry messageHash, so you can
check what was actually signed:
{ "address": "0xabc…", "signature": "0x…", "messageHash": "0x…" }Sign typed data (EIP-712)
curl -X POST "$API_URL/sign/typed-data" \
-H "Authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{
"address": "0xabc…",
"typedData": {
"domain": { "name": "MyDapp", "version": "1", "chainId": 1, "verifyingContract": "0xdef…" },
"types": { "Order": [ { "name": "maker", "type": "address" }, { "name": "amount", "type": "uint256" } ] },
"primaryType": "Order",
"message": { "maker": "0xabc…", "amount": "1000" }
}
}'The response includes both intermediate hashes, so you can verify the domain binding independently:
{
"address": "0xabc…",
"signature": "0x…",
"domainSeparator": "0x…",
"structHash": "0x…"
}chainId is never guessed
chainId is optional. When omitted, the signature is bound to the address’s
own network, not to a default chain.
Typed-data signing is EVM-only. Calling it with a non-EVM address returns 422
with unsupported_for_kind.
Failures worth handling
| Code | Status | What happened |
|---|---|---|
scope_denied |
403 | Token lacks sign |
account_not_found |
404 | No such address in this workspace |
account_locked |
423 | The owning account is locked — its own password, an explicit lock, or a lapsed custody window. Unlock it |
unsupported_for_kind |
422 | Typed data on a non-EVM address |
invalid_body |
400 | Malformed request shape |
An address that exists but belongs to another tenant returns account_not_found
— identical to one that does not exist anywhere.
Related
- Scopes and permissions — why
signis separate fromwrite - Sign endpoints — exact shapes
- Send transactions — signing that also submits