Python SDK for NEXUS Escrow-as-a-Service API
Project description
NEXUS Escrow Python SDK
Official Python SDK for the NEXUS Escrow-as-a-Service API. Create non-custodial Monero escrows with browser-based FROST multisig.
Installation
pip install nexus-escrow
Quick Start
import asyncio
from nexus_escrow import NexusClient, EscrowStatus
async def main():
async with NexusClient(api_key="nxs_your_api_key") as client:
# Create an escrow
escrow = await client.create_escrow(
buyer_address="4...", # Monero address
seller_address="4...", # Monero address
amount=1_000_000_000_000, # 1 XMR in piconero
description="Payment for services",
)
print(f"Escrow created: {escrow.id}")
print(f"Fund address: {escrow.escrow_address}")
# Check escrow status
escrow = await client.get_escrow(escrow.id)
print(f"Status: {escrow.status}")
asyncio.run(main())
Features
- Async/await support with
httpx - Full type hints with Pydantic v2 models
- Automatic retries for transient errors
- FROST DKG and threshold signing
- E2EE escrow chat (X25519 + ChaCha20Poly1305)
- Webhook signature verification
- Fee estimation (network + platform)
- API key management
- Context manager for resource cleanup
Managers
All functionality is organized into managers accessible from the client:
| Manager | Access | Description |
|---|---|---|
EscrowManager |
client.escrows |
Escrow CRUD and lifecycle |
DkgManager |
client.dkg |
FROST Distributed Key Generation |
SigningManager |
client.signing |
FROST threshold CLSAG signing |
WebhookManager |
client.webhooks |
Webhook CRUD and verification |
FeeManager |
client.fees |
Network and platform fee estimation |
AnalyticsManager |
client.analytics |
Usage analytics |
ChatManager |
client.chat |
E2EE escrow messaging |
ApiKeyManager |
client.api_keys |
API key lifecycle |
Usage
Creating Escrows
from nexus_escrow import NexusClient
async with NexusClient(api_key="nxs_...") as client:
escrow = await client.escrows.create(
buyer_address="4...",
seller_address="4...",
amount=5_000_000_000_000, # 5 XMR
description="Freelance project payment",
metadata={"project_id": "proj_123"},
expires_in_hours=72,
)
Escrow Lifecycle (Shipping Flow)
# Vendor: confirm shipment
await client.escrows.confirm_shipped(
escrow_id,
tracking_info="USPS 1234567890",
estimated_delivery_days=7,
)
# Buyer: confirm receipt and trigger release
await client.escrows.confirm_receipt(
escrow_id,
consent_confirmed=True,
feedback="Excellent service",
)
FROST DKG (Distributed Key Generation)
# Initialize DKG for an escrow
await client.dkg.init(escrow_id)
# Each participant submits Round 1 package
await client.dkg.submit_round1(escrow_id, role="buyer", package=hex_round1)
# Get all Round 1 packages
packages = await client.dkg.get_round1_packages(escrow_id)
# Each participant submits Round 2 packages
await client.dkg.submit_round2(
escrow_id, role="buyer", packages={"1": hex_pkg1, "2": hex_pkg2}
)
# Complete DKG with group public key
await client.dkg.complete(
escrow_id,
group_pubkey=hex_group_key,
multisig_address="4...",
multisig_view_key=hex_view_key,
)
FROST Signing
# Initialize signing session
tx_data = await client.signing.init(escrow_id)
# Each signer submits nonces
await client.signing.submit_nonces(
escrow_id,
role="buyer",
r_public=hex_r,
r_prime_public=hex_r_prime,
commitment_hash=hex_commit,
)
# Each signer submits partial signature
await client.signing.submit_partial_signature(
escrow_id,
role="buyer",
partial_signature=json_sig,
partial_key_image=hex_ki,
)
# Aggregate and broadcast
result = await client.signing.complete(escrow_id)
print(f"TX broadcast: {result['tx_hash']}")
Fee Estimation
# Network fee estimate
estimate = await client.fees.estimate(priority="normal")
print(f"Fee for 2-output TX: {estimate.fee_xmr} XMR")
# All priority levels
all_fees = await client.fees.estimate_all()
print(f"Recommended: {all_fees.recommended}")
# Platform fee for a specific amount
platform_fee = await client.fees.estimate_client_fee(1_000_000_000_000)
print(f"Platform fee: {platform_fee.fee_percent}%")
print(f"Net amount: {platform_fee.net_amount_atomic} piconero")
# Daemon health
health = await client.fees.daemon_health()
print(f"Healthy daemons: {health.healthy}/{health.total}")
Analytics
stats = await client.analytics.usage(period="30d")
print(f"Total escrows: {stats.total_escrows}")
print(f"Volume: {stats.total_volume_atomic} piconero")
E2EE Chat
# Register X25519 keypair for encrypted messaging
await client.chat.register_keypair(escrow_id, public_key=hex_x25519_pub)
# Get all participants' public keys
keypairs = await client.chat.get_keypairs(escrow_id)
# Send encrypted message (encrypted 3x: buyer, vendor, arbiter)
await client.chat.send_message(
escrow_id,
encrypted_content_buyer=base64_buyer,
encrypted_content_vendor=base64_vendor,
encrypted_content_arbiter=base64_arbiter,
sender_ephemeral_pubkey=hex_ephemeral,
nonce=hex_nonce,
)
# Get messages
result = await client.chat.get_messages(escrow_id, limit=50)
API Keys
# Create a new API key
key = await client.api_keys.create("Production Key")
print(f"Key: {key['raw_key']}") # Only shown once
# List all keys
keys = await client.api_keys.list()
# Revoke a key
await client.api_keys.revoke("key_id")
Webhooks
Register a Webhook
from nexus_escrow import WebhookEventType
webhook = await client.webhooks.register(
url="https://your-app.com/webhooks/nexus",
events=[
WebhookEventType.ESCROW_FUNDED,
WebhookEventType.ESCROW_RELEASED,
WebhookEventType.ESCROW_SHIPPED,
WebhookEventType.ESCROW_DISPUTED,
],
)
# Store webhook.secret securely for verification
Verify Webhook Signatures
from nexus_escrow import WebhookManager, NexusWebhookVerificationError
@app.post("/webhooks/nexus")
async def handle_webhook(request: Request):
payload = await request.body()
signature = request.headers.get("X-Nexus-Signature", "")
timestamp = request.headers.get("X-Nexus-Timestamp")
try:
WebhookManager.verify_signature(
payload=payload,
signature=signature,
secret=WEBHOOK_SECRET,
timestamp=timestamp,
)
except NexusWebhookVerificationError:
raise HTTPException(401, "Invalid signature")
event = WebhookManager.parse_event(payload)
if event.type == WebhookEventType.ESCROW_FUNDED:
print(f"Escrow {event.escrow_id} was funded!")
Webhook Delivery Management
# Get delivery history
deliveries = await client.webhooks.get_deliveries("whk_...", limit=50)
# Retry a failed delivery
await client.webhooks.retry_delivery("whk_...", "dlv_...")
# Get delivery statistics
stats = await client.webhooks.get_stats("whk_...")
Handling Disputes
# Open a dispute
escrow = await client.escrows.dispute(
escrow_id="esc_...",
reason="Seller did not deliver as agreed",
evidence_urls=["https://example.com/screenshot.png"],
)
# Resolve dispute (arbitrator only)
from nexus_escrow import DisputeResolution
escrow = await client.escrows.resolve(
escrow_id="esc_...",
resolution=DisputeResolution.BUYER_WINS,
reason="Seller failed to provide proof of delivery",
)
Error Handling
from nexus_escrow import (
NexusApiError,
NexusAuthenticationError,
NexusNotFoundError,
NexusValidationError,
NexusRateLimitError,
)
try:
escrow = await client.get_escrow("esc_invalid")
except NexusNotFoundError:
print("Escrow not found")
except NexusAuthenticationError:
print("Invalid API key")
except NexusValidationError as e:
print(f"Validation error: {e.message}")
print(f"Details: {e.details}")
except NexusRateLimitError as e:
print(f"Rate limited, retry after {e.retry_after} seconds")
except NexusApiError as e:
print(f"API error [{e.status_code}]: {e.message}")
Configuration
client = NexusClient(
api_key="nxs_...",
base_url="https://api.nexus.io", # Default
timeout=30.0, # Request timeout in seconds
max_retries=3, # Retries for 5xx errors
)
API Reference
EscrowManager (client.escrows)
| Method | Description |
|---|---|
create(...) |
Create a new escrow |
get(id) |
Get escrow by ID |
list(...) |
List escrows with filters |
get_funding_instructions(id) |
Get funding address/amount |
check_funding(id) |
Check if escrow is funded |
join(id) |
Join escrow as counterparty |
notify_funding(id) |
Notify platform of funding |
mark_delivered(id) |
Mark goods as delivered (vendor) |
confirm_delivery(id) |
Confirm delivery receipt (buyer) |
release(id, signature) |
Release funds to seller |
refund(id) |
Refund funds to buyer |
dispute(id, reason) |
Open a dispute |
resolve(id, resolution) |
Resolve a dispute |
cancel(id) |
Cancel unfunded escrow |
confirm_shipped(id) |
Confirm shipment (vendor) |
confirm_receipt(id) |
Confirm receipt and trigger release (buyer) |
set_payout_address(id, address) |
Set vendor payout address |
set_refund_address(id, address) |
Set buyer refund address |
DkgManager (client.dkg)
| Method | Description |
|---|---|
init(id) |
Initialize FROST DKG |
submit_round1(id, role, package) |
Submit Round 1 package |
get_round1_packages(id) |
Get all Round 1 packages |
submit_round2(id, role, packages) |
Submit Round 2 packages |
get_round2_packages(id, role) |
Get Round 2 packages for role |
complete(id, group_pubkey, ...) |
Complete DKG |
get_status(id) |
Get DKG status |
get_lagrange_coefficients(...) |
Get Lagrange coefficients |
SigningManager (client.signing)
| Method | Description |
|---|---|
init(id) |
Initialize signing session |
submit_nonces(id, ...) |
Submit nonce commitment |
get_nonces(id) |
Get all nonces |
submit_partial_signature(id, ...) |
Submit partial CLSAG sig |
get_status(id) |
Get signing status |
complete(id) |
Aggregate and broadcast TX |
get_tx_data(id) |
Get TX data for signing |
get_first_signer_data(id) |
Get first signer data |
WebhookManager (client.webhooks)
| Method | Description |
|---|---|
register(url, events) |
Register a new webhook |
get(id) |
Get webhook by ID |
list() |
List all webhooks |
update(id, ...) |
Update webhook config |
delete(id) |
Delete a webhook |
activate(id) |
Activate a webhook |
deactivate(id) |
Deactivate a webhook |
rotate_secret(id) |
Rotate signing secret |
test(id) |
Send test event |
get_deliveries(id) |
Get delivery history |
retry_delivery(id, delivery_id) |
Retry failed delivery |
get_stats(id) |
Get delivery statistics |
verify_signature(...) |
Verify webhook signature (static) |
parse_event(payload) |
Parse webhook payload (static) |
FeeManager (client.fees)
| Method | Description |
|---|---|
estimate(priority) |
Get fee estimate |
estimate_all() |
Get all priority estimates |
daemon_health() |
Get daemon health status |
get_client_fees() |
Get client fee config |
estimate_client_fee(amount) |
Estimate platform fee |
AnalyticsManager (client.analytics)
| Method | Description |
|---|---|
usage(period) |
Get usage analytics |
ChatManager (client.chat)
| Method | Description |
|---|---|
register_keypair(id, public_key) |
Register X25519 keypair |
get_keypairs(id) |
Get all participants' keys |
send_message(id, ...) |
Send E2EE message |
get_messages(id) |
Get message history |
mark_read(id, message_id) |
Mark message as read |
export_for_dispute(id) |
Export chat for disputes |
ApiKeyManager (client.api_keys)
| Method | Description |
|---|---|
create(name) |
Create new API key |
list() |
List all API keys |
get(id) |
Get API key details |
revoke(id) |
Revoke an API key |
delete(id) |
Permanently delete key |
Types
Enums
EscrowStatus:CREATED,FUNDED,SHIPPED,RELEASING,RELEASED,DISPUTED,RESOLVED,CANCELLED,EXPIREDDisputeResolution:BUYER_WINS,SELLER_WINS,SPLITWebhookEventType:ESCROW_CREATED,ESCROW_FUNDED,ESCROW_SHIPPED,ESCROW_RELEASED, etc.FrostRole:BUYER,VENDOR,ARBITERFeePriority:UNIMPORTANT,NORMAL,ELEVATED,PRIORITYApiKeyTier:FREE,PRO,ENTERPRISE
Models
Escrow: Escrow resource with all fieldsEscrowList: Paginated escrow listFundingInstructions: Address and amount for fundingDkgStatus,DkgParticipants: DKG state trackingLagrangeCoefficients: Lagrange coefficients for signingSigningStatus,TxSigningData: Signing session stateFeeEstimate,AllFeeEstimates: Network fee estimatesClientFeeConfig,ClientFeeEstimate: Platform fee infoDaemonHealth,DaemonHealthSummary: Daemon statusUsageAnalytics: Usage analyticsWebhook,WebhookList,WebhookEvent: Webhook typesWebhookDelivery,WebhookDeliveryStats: Delivery trackingChatKeypairResponse,ChatKeypairsDto: Chat key exchangeChatMessage,ChatMessageList: Encrypted messagesApiKeyInfo,ApiKeyCreationResponse: API key typesUsageStats: Account usage stats
Requirements
- Python 3.10+
- httpx >= 0.24.0
- pydantic >= 2.0.0
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
nexus_escrow-0.3.0.tar.gz
(24.3 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nexus_escrow-0.3.0.tar.gz.
File metadata
- Download URL: nexus_escrow-0.3.0.tar.gz
- Upload date:
- Size: 24.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dff4757ef0109aa64971787c3f13dd6e1ed794a2fadb7d158ae21436d490728d
|
|
| MD5 |
5f2a4fee1b89a182226f6ddd65f54b56
|
|
| BLAKE2b-256 |
59c80bbfaf4740ff47709d3def9742adbb557566ca0b6527afe0836dae55597d
|
File details
Details for the file nexus_escrow-0.3.0-py3-none-any.whl.
File metadata
- Download URL: nexus_escrow-0.3.0-py3-none-any.whl
- Upload date:
- Size: 29.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d91740ec2d49375825a06c2dc3a2efdaad9859939dd28b19a02a0131ecfe490
|
|
| MD5 |
44881a1cc8cce375935aebe1453397f8
|
|
| BLAKE2b-256 |
0f0393eac6a52c2caf0473eef021bda7a80327bfbdbc5178f0baa96d506b1e43
|