| Endpoint | Auth | Description |
|---|---|---|
POST /api/v1/keygen/reserve |
None | Reserve keygen session, get assigned enclave |
POST /api/v1/keygen/{session_id}/initialize |
None | Initialize session with encrypted coordinator data |
GET /api/v1/keygen/{keygen_session_id}/slots |
None | Get available participant slots |
POST /api/v1/keygen/{keygen_session_id}/participants |
X-Session-Signature | Register participant |
GET /api/v1/keygen/{keygen_session_id}/status |
X-Session-Signature | Check keygen progress |
| Endpoint | Auth | Description |
|---|---|---|
POST /api/v1/signing |
X-Session-Signature | Create signing session |
POST /api/v1/signing/{signing_session_id}/approve/{user_id} |
X-User-Signature | Approve signing (if required) |
GET /api/v1/signing/{signing_session_id}/status/{user_id} |
X-User-Signature | Check signing progress |
| Endpoint | Auth | Description |
|---|---|---|
POST /api/v1/keys/reserve |
None | Reserve key slot, get assigned enclave |
POST /api/v1/keys/import |
X-User-Signature | Import encrypted private key (validates auth_pubkey ownership) |
GET /api/v1/keys/{user_id}?key_id=... |
X-User-Signature | List all keys for user |
GET /api/v1/keys/{user_id}/{key_id}/status |
X-User-Signature | Check key import/store status |
DELETE /api/v1/keys/{user_id}/{key_id} |
X-User-Signature | Delete a key |
POST /api/v1/keys/{user_id}/keygen/{session_id} |
X-Session-Signature | Store key from completed keygen |
| Endpoint | Auth | Description |
|---|---|---|
POST /api/v1/sign/single |
X-User-Signature | Create single-signer signing session |
GET /api/v1/sign/single/{session_id}/status/{user_id} |
X-User-Signature | Check signing status |
| Endpoint | Auth | Description |
|---|---|---|
GET /api/v1/enclaves |
None | List all enclaves |
GET /api/v1/enclaves/{enclave_id}/public-key |
None | Get enclave public key for ECIES |
GET /api/v1/health |
None | Health check |
GET /api/v1/health/detail |
None | Detailed health check with enclave status |
GET /api/v1/version |
None | API version info |
GET /api/v1/metrics |
None | Prometheus metrics |
GET /api/v1/openapi.json |
None | OpenAPI specification |
GET /api/v1/docs |
None | Interactive API documentation (Scalar) |
POST /api/v1/keygen/reserve
{
"keygen_session_id": "uuid-v7",
"coordinator_user_id": "uuid-v7",
"expected_participants": ["user1", "user2", "user3"],
"timeout_secs": 3600,
"encrypted_taproot_tweak": "hex-encrypted-tweak",
"subset_definitions": [
{
"subset_id": "uuid-v7",
"participants": ["user1", "user2"]
}
]
}
Response includes coordinator_enclave_id and coordinator_public_key for ECIES encryption.
Subset Definitions (optional): Define participant subsets for 2-of-n signing within the larger group. Each subset gets its own aggregate key computed from BIP327-sorted participant public keys. Used for scenarios like DLC split transactions where only a subset of participants need to sign.
POST /api/v1/keygen/{session_id}/initialize
{
"coordinator_pubkey": [bytes],
"coordinator_encrypted_private_key": "hex-ecies-encrypted",
"session_public_key": [bytes],
"encrypted_session_secret": "hex-ecies-encrypted",
"encrypted_session_data": "hex-session-key-encrypted",
"encrypted_enclave_data": "hex-ecies-encrypted",
"enclave_key_epoch": 1
}
GET /api/v1/keygen/{keygen_session_id}/slots
Returns slots with user_id, enclave_id, and signer_index for each participant.
POST /api/v1/keygen/{keygen_session_id}/participants
X-Session-Signature: nonce:signature
{
"keygen_session_id": "uuid-v7",
"user_id": "uuid-v7",
"encrypted_private_key": "hex-ecies-encrypted",
"public_key": [bytes],
"encrypted_session_data": "hex-session-key-encrypted",
"enclave_public_key": "hex-pubkey",
"enclave_key_epoch": 1,
"require_signing_approval": false,
"auth_pubkey": [bytes]
}
All signing requests use batch items, where a single message is simply a batch of one.
POST /api/v1/signing
X-Session-Signature: nonce:signature
{
"signing_session_id": "uuid-v7",
"keygen_session_id": "uuid-v7",
"timeout_secs": 1800,
"batch_items": [
{
"batch_item_id": "uuid-v7",
"message_hash": [32 bytes],
"signing_mode": { ... },
"encrypted_taproot_tweak": "hex-session-encrypted",
"subset_id": "uuid-v7 or null"
}
]
}
Batch Item Fields:
batch_item_id: UUIDv7 identifier for this item (returned in results)message_hash: 32-byte hash of the message to signsigning_mode: Specifies the signature type (see below)encrypted_taproot_tweak: Per-item taproot tweak configuration (session-key encrypted)subset_id: Optional subset ID for signing with a subset aggregate key instead of the full n-of-n key. Must reference a subset defined during keygen.
The signing_mode field is a tagged enum that specifies whether to produce a regular Schnorr signature or an adaptor signature:
Regular Signature - produces a standard Schnorr signature:
{
"type": "regular",
"encrypted_message": "hex-session-encrypted"
}Adaptor Signature - produces a signature locked to adaptor point(s):
{
"type": "adaptor",
"encrypted_message": "hex-session-encrypted",
"encrypted_adaptor_configs": "hex-encrypted-adaptor-json"
}The adaptor signature can only be completed when the corresponding adaptor secret (e.g., oracle attestation) is revealed.
POST /api/v1/signing/{signing_session_id}/approve/{user_id}
X-User-Signature: nonce:signature
GET /api/v1/signing/{signing_session_id}/status/{user_id}
X-User-Signature: nonce:signature
Response when complete:
{
"signing_session_id": "uuid-v7",
"keygen_session_id": "uuid-v7",
"status": "completed",
"batch_results": [
{
"batch_item_id": "uuid-v7",
"signature": "hex-encrypted-signature",
"adaptor_signatures": [...],
"error": null
}
]
}Batch Result Fields:
batch_item_id: Matches the ID from the requestsignature: Encrypted final Schnorr signature (64 bytes when decrypted)adaptor_signatures: Optional adaptor signatures if configurederror: Error message if this item failed
| Status | Description |
|---|---|
collecting_participants |
Waiting for all participants to join |
initializing_session |
Generating nonces |
distributing_nonces |
Collecting partial signatures |
finalizing_signature |
Aggregating final signatures |
completed |
All signatures ready in batch_results |
failed |
Session failed |
POST /api/v1/keys/reserve
{
"user_id": "uuid-v7"
}
Response:
{
"key_id": "uuid-v7",
"enclave_id": 1,
"enclave_public_key": "02abc123...",
"enclave_key_epoch": 1
}Generate an auth keypair and encrypt your private key to the enclave using ECIES. The X-User-Signature proves you own the auth keypair before it's stored.
POST /api/v1/keys/import
X-User-Signature: nonce:signature
{
"key_id": "uuid-v7",
"user_id": "uuid-v7",
"encrypted_private_key": "hex-ecies-encrypted",
"auth_pubkey": [33 bytes compressed auth pubkey],
"enclave_public_key": "02abc123..."
}
GET /api/v1/keys/{user_id}/{key_id}/status
X-User-Signature: nonce:signature
Response:
{
"key_id": "uuid-v7",
"user_id": "uuid-v7",
"status": "completed",
"error_message": null
}Statuses: pending, processing, completed, failed
GET /api/v1/keys/{user_id}?key_id={key_id_for_auth}
X-User-Signature: nonce:signature
POST /api/v1/sign/single
X-User-Signature: nonce:signature
{
"user_id": "uuid-v7",
"key_id": "uuid-v7",
"encrypted_message": "hex-session-encrypted",
"signature_type": "schnorr_bip340",
"encrypted_session_secret": "hex-ecies-encrypted",
"approval_signature": "hex-ecdsa-sig",
"approval_timestamp": 1234567890
}
Signature types: ecdsa, schnorr
GET /api/v1/sign/single/{session_id}/status/{user_id}
X-User-Signature: nonce:signature
Response when complete:
{
"signing_session_id": "uuid-v7",
"user_id": "uuid-v7",
"key_id": "uuid-v7",
"status": "completed",
"encrypted_signature": "hex-session-encrypted",
"signature_type": "schnorr_bip340"
}Statuses: pending, processing, completed, failed
Store a key from a completed MuSig2 keygen session for later single-signer use:
POST /api/v1/keys/{user_id}/keygen/{keygen_session_id}
X-Session-Signature: nonce:signature
{
"key_id": "uuid-v7"
}
DELETE /api/v1/keys/{user_id}/{key_id}
X-User-Signature: nonce:signature
Format: nonce:signature
nonce: Random 16-byte hex valuesignature: ECDSA overSHA256(session_id || nonce)using session-derived key
Format: nonce:signature
nonce: Random 16-byte hex valuesignature: ECDSA overSHA256(scope_id || user_id || nonce)using auth private keyscope_id: The key_id being accessed (for key operations) or signing_session_id (for signing status)
Auth key derived via: HKDF-SHA256(master_private_key, "keymeld-session-auth-v1:keygen_session_id")
Private keys encrypted to enclave public keys using ECIES.
Session data encrypted with symmetric key derived from shared session secret.
Subset signing enables k-of-n signing within a larger MuSig2 group. This is useful for scenarios like DLC split transactions where only a subset of participants need to sign.
- Define subsets at keygen time: Include
subset_definitionsin the reserve request - Each subset gets its own aggregate key: Computed from BIP327-sorted participant public keys
- Reference subset in batch items: Set
subset_idon batch items that should use subset signing - Automatic signer index mapping: The enclave maps participant indices from the full group to subset-relative indices
For DLCs with weighted payouts where multiple players can win per outcome, subsets must be defined per-outcome (not per-player). Each outcome's split transaction key is the aggregate of the market maker + ALL winners for that outcome.
// At keygen - define subsets per outcome containing all winners + market_maker
// Outcome 0: Player 0 (60%) + Player 1 (40%)
// Outcome 1: Player 1 (50%) + Player 2 (50%)
// Outcome 2: Player 0 (50%) + Player 1 (30%) + Player 2 (20%)
{
"subset_definitions": [
{"subset_id": "subset-outcome-0", "participants": ["market_maker", "player_0", "player_1"]},
{"subset_id": "subset-outcome-1", "participants": ["market_maker", "player_1", "player_2"]},
{"subset_id": "subset-outcome-2", "participants": ["market_maker", "player_0", "player_1", "player_2"]}
]
}
// At signing - outcome txs use full n-of-n, split txs use per-outcome subsets
{
"batch_items": [
// Outcome transactions (n-of-n adaptor signatures)
{
"batch_item_id": "outcome-0",
"message_hash": [...],
"signing_mode": {"type": "adaptor", "encrypted_message": "...", "encrypted_adaptor_configs": "..."},
"encrypted_taproot_tweak": "...",
"subset_id": null
},
// ... outcome-1, outcome-2 similar
// Split transactions (k-of-k per outcome - regular signatures)
{
"batch_item_id": "split-outcome0-player0",
"message_hash": [...],
"signing_mode": {"type": "regular", "encrypted_message": "..."},
"encrypted_taproot_tweak": "...",
"subset_id": "subset-outcome-0"
}
// ... other split transactions similar, each referencing its outcome's subset
]
}Important: The subset for each outcome must include ALL winners for that outcome, not just individual players. This is because dlctix computes the split transaction's aggregate key from market_maker + all_winners.
Per-item taproot tweak configuration (encrypted with session key):
{"type": "none"}
{"type": "unspendable_taproot"}
{"type": "taproot_with_merkle_root", "merkle_root": "hex"}
{"type": "plain_tweak", "tweak": "hex"}