Hanzo

API Reference

Complete REST API reference for Hanzo MPC

API Reference

All endpoints are served at https://mpc.hanzo.ai and require a Bearer token from Hanzo IAM.

Authentication

Every request must include an Authorization header:

Authorization: Bearer <token>

Obtain tokens from Hanzo IAM at https://hanzo.id. See IAM documentation for details.

Base URL

EnvironmentURL
Productionhttps://mpc.hanzo.ai
Staginghttps://stg.mpc.hanzo.ai

POST /api/wallets

Create a new MPC wallet. This triggers a distributed key generation (DKG) ceremony across the configured number of parties.

Request

curl -X POST https://mpc.hanzo.ai/api/wallets \
  -H "Authorization: Bearer $HANZO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "protocol": "cggmp21",
    "curve": "secp256k1",
    "threshold": 2,
    "parties": 3,
    "label": "eth-hot-wallet",
    "metadata": {
      "department": "treasury",
      "owner": "ops@example.com"
    }
  }'

Request Body

FieldTypeRequiredDescription
protocolstringYesCryptographic protocol. One of: cggmp21, frost, lsss, tfhe, ringtail, quasar, bls, doerner
curvestringYesElliptic curve. One of: secp256k1, ed25519, ristretto, bls12-381, p256, sr25519
thresholdintegerYesMinimum signers required (t)
partiesintegerYesTotal number of key share holders (n)
labelstringNoHuman-readable wallet label
metadataobjectNoArbitrary key-value metadata

Protocol-Curve Compatibility

ProtocolCompatible Curves
cggmp21secp256k1, p256
frosted25519, ristretto, secp256k1
lsssAny curve
tfheLattice (internal)
ringtailModule-LWE (internal)
quasarbls12-381
blsbls12-381
doernersecp256k1

Response 201 Created

{
  "wallet_id": "w_3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "protocol": "cggmp21",
  "curve": "secp256k1",
  "threshold": 2,
  "parties": 3,
  "public_key": "04a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "addresses": {
    "ethereum": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "bitcoin": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    "bitcoin_legacy": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
  },
  "label": "eth-hot-wallet",
  "metadata": {
    "department": "treasury",
    "owner": "ops@example.com"
  },
  "keygen_duration_ms": 22,
  "created_at": "2026-02-23T10:30:00Z"
}

Response Fields

FieldTypeDescription
wallet_idstringUnique wallet identifier (prefixed w_)
protocolstringProtocol used for DKG
curvestringElliptic curve
thresholdintegerSigning threshold (t)
partiesintegerTotal parties (n)
public_keystringHex-encoded public key
addressesobjectDerived chain addresses
labelstringWallet label
metadataobjectCustom metadata
keygen_duration_msintegerDKG ceremony duration
created_atstringISO 8601 timestamp

Errors

StatusCodeDescription
400invalid_protocolUnsupported protocol name
400invalid_curveUnsupported curve or incompatible with protocol
400invalid_thresholdThreshold must be >= 1 and ≤ parties
401unauthorizedMissing or invalid Bearer token
403forbiddenToken lacks mpc:wallets:create scope
500keygen_failedDKG ceremony failed (check node health)

POST /api/sign

Sign a message or transaction hash using the specified wallet. The coordinator selects t available nodes and runs the threshold signing protocol.

Request

curl -X POST https://mpc.hanzo.ai/api/sign \
  -H "Authorization: Bearer $HANZO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "wallet_id": "w_3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "message": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "encoding": "hex"
  }'

Request Body

FieldTypeRequiredDescription
wallet_idstringYesTarget wallet ID
messagestringYesMessage or transaction hash to sign
encodingstringNoMessage encoding: hex (default), base64, utf8
signersinteger[]NoSpecific signer indices to use. If omitted, coordinator selects automatically
metadataobjectNoAudit metadata (e.g., transaction context)

Response 200 OK

{
  "signature": {
    "r": "0x1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b",
    "s": "0x4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e",
    "v": 27
  },
  "signers": [1, 3],
  "wallet_id": "w_3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "protocol": "cggmp21",
  "duration_ms": 45,
  "timestamp": "2026-02-23T10:31:00Z"
}

Signature Format by Protocol

ProtocolSignature Fields
CGGMP21r, s, v (ECDSA)
FROSTR, s (Schnorr)
BLSsignature (compressed G1 point)
Quasarbls_signature, pq_signature (dual)
Doernerr, s, v (ECDSA)

Errors

StatusCodeDescription
400invalid_messageMessage is empty or malformed
400invalid_encodingUnsupported encoding format
404wallet_not_foundWallet ID does not exist
401unauthorizedMissing or invalid Bearer token
403forbiddenToken lacks mpc:sign scope
503insufficient_signersFewer than t nodes are available
500signing_failedProtocol execution failed
500identifiable_abortMalicious signer detected (includes blame proof)

Identifiable Abort Response

When a signer deviates from the protocol:

{
  "error": "identifiable_abort",
  "blame": {
    "party_index": 2,
    "proof": "base64-encoded-blame-proof",
    "reason": "invalid_commitment"
  },
  "timestamp": "2026-02-23T10:31:00Z"
}

POST /api/reshare

Perform a proactive share refresh. This generates new shares for the same underlying key, optionally changing the threshold or party count. The public key and all derived addresses remain unchanged.

Request

curl -X POST https://mpc.hanzo.ai/api/reshare \
  -H "Authorization: Bearer $HANZO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "wallet_id": "w_3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "new_threshold": 3,
    "new_parties": 5
  }'

Request Body

FieldTypeRequiredDescription
wallet_idstringYesTarget wallet ID
new_thresholdintegerNoNew signing threshold. Defaults to current
new_partiesintegerNoNew total parties. Defaults to current
reasonstringNoAudit reason (e.g., scheduled_rotation, suspected_compromise)

Response 200 OK

{
  "wallet_id": "w_3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "previous": {
    "threshold": 2,
    "parties": 3,
    "epoch": 1
  },
  "current": {
    "threshold": 3,
    "parties": 5,
    "epoch": 2
  },
  "public_key": "04a1b2c3d4e5f6...",
  "addresses_unchanged": true,
  "reshare_duration_ms": 180,
  "timestamp": "2026-02-23T10:32:00Z"
}

Errors

StatusCodeDescription
400invalid_thresholdNew threshold exceeds new party count
404wallet_not_foundWallet ID does not exist
401unauthorizedMissing or invalid Bearer token
403forbiddenToken lacks mpc:reshare scope
409reshare_in_progressAnother reshare is already running for this wallet
500reshare_failedProtocol execution failed

GET /api/wallets

List all wallets accessible to the authenticated user.

Request

curl https://mpc.hanzo.ai/api/wallets \
  -H "Authorization: Bearer $HANZO_TOKEN"

Query Parameters

ParameterTypeDefaultDescription
limitinteger20Results per page (max 100)
offsetinteger0Pagination offset
protocolstring-Filter by protocol
labelstring-Filter by label (substring match)

Response 200 OK

{
  "wallets": [
    {
      "wallet_id": "w_3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "protocol": "cggmp21",
      "curve": "secp256k1",
      "threshold": 2,
      "parties": 3,
      "public_key": "04a1b2c3d4e5f6...",
      "addresses": {
        "ethereum": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
        "bitcoin": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
      },
      "label": "eth-hot-wallet",
      "epoch": 2,
      "created_at": "2026-02-23T10:30:00Z",
      "last_reshare": "2026-02-23T10:32:00Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}

GET /api/wallets/:id

Get detailed information about a specific wallet.

Request

curl https://mpc.hanzo.ai/api/wallets/w_3fa85f64-5717-4562-b3fc-2c963f66afa6 \
  -H "Authorization: Bearer $HANZO_TOKEN"

Response 200 OK

{
  "wallet_id": "w_3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "protocol": "cggmp21",
  "curve": "secp256k1",
  "threshold": 2,
  "parties": 3,
  "public_key": "04a1b2c3d4e5f6...",
  "addresses": {
    "ethereum": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "bitcoin": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    "bitcoin_legacy": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
    "bitcoin_taproot": "bc1p...",
    "solana": "7nYB...",
    "cosmos": "cosmos1...",
    "lux_c": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "lux_x": "X-lux1...",
    "lux_p": "P-lux1..."
  },
  "label": "eth-hot-wallet",
  "metadata": {
    "department": "treasury",
    "owner": "ops@example.com"
  },
  "epoch": 2,
  "node_status": [
    {"index": 0, "healthy": true, "last_seen": "2026-02-23T10:35:00Z"},
    {"index": 1, "healthy": true, "last_seen": "2026-02-23T10:35:00Z"},
    {"index": 2, "healthy": true, "last_seen": "2026-02-23T10:35:00Z"}
  ],
  "signing_count": 142,
  "last_signed": "2026-02-23T10:34:00Z",
  "created_at": "2026-02-23T10:30:00Z",
  "last_reshare": "2026-02-23T10:32:00Z"
}

GET /health

Health check endpoint. No authentication required.

Request

curl https://mpc.hanzo.ai/health

Response 200 OK

{
  "status": "healthy",
  "version": "1.4.0",
  "nodes": {
    "total": 3,
    "healthy": 3,
    "degraded": 0,
    "offline": 0
  },
  "nats": "connected",
  "consul": "connected",
  "uptime_seconds": 864000,
  "timestamp": "2026-02-23T10:35:00Z"
}

Status Values

StatusMeaning
healthyAll nodes operational, messaging and discovery connected
degradedSome nodes offline but threshold still achievable
unhealthyFewer healthy nodes than minimum threshold

Error Response Format

All errors follow a consistent structure:

{
  "error": "error_code",
  "message": "Human-readable description of what went wrong",
  "details": {},
  "request_id": "req_abc123",
  "timestamp": "2026-02-23T10:35:00Z"
}

Rate Limits

EndpointLimitWindow
POST /api/wallets10per minute
POST /api/sign100per minute
POST /api/reshare5per minute
GET /api/wallets60per minute
GET /health300per minute

Rate limit headers are included in every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1708689360

Pagination

List endpoints support cursor-based pagination:

# First page
curl "https://mpc.hanzo.ai/api/wallets?limit=10"

# Next page
curl "https://mpc.hanzo.ai/api/wallets?limit=10&offset=10"

Webhook Notifications

Configure webhooks to receive real-time events:

curl -X POST https://mpc.hanzo.ai/api/webhooks \
  -H "Authorization: Bearer $HANZO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/mpc",
    "events": ["wallet.created", "sign.completed", "sign.failed", "reshare.completed"],
    "secret": "whsec_your_webhook_secret"
  }'

Event Types

EventTrigger
wallet.createdDKG ceremony completed
sign.completedSignature produced
sign.failedSigning failed or aborted
reshare.completedShare refresh completed
reshare.failedShare refresh failed
node.offlineAn MPC node became unreachable
node.recoveredAn offline node came back

How is this guide?

Last updated on

On this page