Skip to main content
This guide walks through pointing a Qustody deployment at a Quantum Chain RPC endpoint and sending a first transaction through the platform. It assumes you already have a running Qustody API and a synced Quantum Chain node.

Architecture

Three independent components must reach each other:
  1. Qustody API → Quantum Chain RPC (HTTP and optionally WebSocket)
  2. Qustody API → Post-quantum signing service (HTTPS or gRPC mTLS)
  3. Qustody API → Your webhook endpoint

Step 1. Configure the RPC connection

Set the node endpoints in Qustody’s environment:
CUSTODY_NODE_RPC_URL=http://quantum-chain-rpc.internal:8545
CUSTODY_NODE_WS_URL=ws://quantum-chain-rpc.internal:8546
CUSTODY_NODE_BACKUP_RPC_URLS=https://rpc.alt.example,https://rpc.alt2.example
CUSTODY_CHAIN_ID=20803
CUSTODY_CONFIRMATION_DEPTH=12
WebSocket is required if you want Qustody to monitor incoming deposits in real time. Verify reachability from the Qustody host:
curl -s -X POST "$CUSTODY_NODE_RPC_URL" \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_chainId","id":1,"params":[]}'
# expect "result":"0x5143"   (20803)

Step 2. Configure the signing service

Pick the signer mode that matches your operations model.
CUSTODY_SIGNER_TYPE=grpc
CUSTODY_SIGNER_BACKEND=enqlave        # or "qey"
CUSTODY_SIGNER_GRPC_TARGET=signer.internal:443
CUSTODY_SIGNER_GRPC_CLIENT_CERT=/etc/qustody/client.crt
CUSTODY_SIGNER_GRPC_CLIENT_KEY=/etc/qustody/client.key
CUSTODY_SIGNER_GRPC_CA_CERT=/etc/qustody/ca.crt
CUSTODY_SIGNER_GRPC_TIMEOUT=30s

Option B — Callback (signer pushes signatures)

CUSTODY_SIGNER_TYPE=callback
Your signer polls GET /v1/transactions?status=PENDING_SIGNATURE, fetches signing payloads, and POSTs back signatures. See External signing.

Option C — Remote HTTPS

CUSTODY_SIGNER_TYPE=remote
CUSTODY_SIGNER_ENDPOINT=https://signer.example/sign
CUSTODY_SIGNER_API_KEY=<bearer>
Restart the API and check /readyz returns 200.

Step 3. Bootstrap a tenant and admin user

# Tenant
TENANT_ID=$(curl -s -X POST "$BASE_URL/v1/tenants" \
  -H "Authorization: Bearer $BOOTSTRAP_KEY" \
  -d '{"name":"acme","display_name":"Acme Inc"}' | jq -r .id)

# Admin user
USER_ID=$(curl -s -X POST "$BASE_URL/v1/users" \
  -H "Authorization: Bearer $BOOTSTRAP_KEY" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -d '{"email":"alice@acme.example","name":"Alice"}' | jq -r .id)

# Assign admin role
curl -X POST "$BASE_URL/v1/users/$USER_ID/roles" \
  -H "Authorization: Bearer $BOOTSTRAP_KEY" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -d '{"roleId":"admin"}'

Step 4. Create a vault and wallet

# Vault
VAULT_ID=$(curl -s -X POST "$BASE_URL/v1/vaults" \
  -H "Authorization: Bearer $TENANT_KEY" \
  -d '{"name":"treasury"}' | jq -r .id)

# Wallet — register the post-quantum public key your signer will use
WALLET_ID=$(curl -s -X POST "$BASE_URL/v1/wallets" \
  -H "Authorization: Bearer $TENANT_KEY" \
  -d "{
    \"vault_id\": \"$VAULT_ID\",
    \"public_key\": \"0xa288bd50d4d48a02...\",
    \"address\": \"0xfF81E5c74E14DB5B4EE407d66B1B063C8F305c51\"
  }" | jq -r .id)
If CUSTODY_SIGNER_KEYGEN_MODE=qey, omit public_key — Qustody asks the signer to mint one and registers it automatically.

Step 5. Subscribe to webhooks

WEBHOOK_RESPONSE=$(curl -s -X POST "$BASE_URL/v1/webhooks" \
  -H "Authorization: Bearer $TENANT_KEY" \
  -d '{
    "url": "https://app.example.com/webhooks/qustody",
    "events": ["transaction.broadcast","transaction.confirmed","transaction.failed"]
  }')

echo "Secret: $(echo $WEBHOOK_RESPONSE | jq -r .secret)"
Store the secret immediately — it is shown only once. See Webhooks for HMAC verification.

Step 6. Send your first transaction

curl -X POST "$BASE_URL/v1/transactions" \
  -H "Authorization: Bearer $TENANT_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d "{
    \"wallet_id\": \"$WALLET_ID\",
    \"to_address\": \"0x1865476914c85900110Ffc6B092436366E98Db55\",
    \"value\": \"1000000000000000000\",
    \"asset\": \"QRC\"
  }"
The transaction transitions through SUBMITTED → PENDING_AUTHORIZATION → APPROVED → PENDING_SIGNATURE → BROADCASTING → BROADCAST → CONFIRMED. Each transition fires a webhook. See Transaction lifecycle for the full state diagram.

Step 7. Verify on chain

When you receive transaction.confirmed:
TX_HASH=$(curl -s "$BASE_URL/v1/transactions/$TX_ID" \
  -H "Authorization: Bearer $TENANT_KEY" | jq -r .tx_hash)

curl -s -X POST "$CUSTODY_NODE_RPC_URL" \
  -H 'Content-Type: application/json' \
  --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"id\":1,\"params\":[\"$TX_HASH\"]}"
The receipt’s status should be 0x1 (success).

Operational checklist

Backup RPC

Configure CUSTODY_NODE_BACKUP_RPC_URLS with at least one alternate node. The circuit breaker fails over automatically.

mTLS to signer

For gRPC mode, mutual TLS prevents stolen client tokens from leaking signing access.

Confirmation depth

Default 12 blocks (~3 minutes) is good for most flows. Raise for very high-value transfers.

Monitor metrics

Watch signer_circuit_breaker_state, node_rpc_errors_total, txpool/queued. Alert on circuit breakers open longer than 1 minute.

Where to go next