Skip to content

Subscribe to Order Channel with Signed Payload

Provide signed payload on order requests for additional security

Execute orders with cryptographic signatures to ensure non-repudiation and prevent tampering by intermediate systems.

Signed intent is an optional security feature that allows you to cryptographically sign critical order parameters using your RSA private key. This is the same feature described in API Key Management → but applied to WebSocket order execution.

Standard order execution is covered on the Order Execution → page. This page only covers the additional signed intent functionality.


Consider using signed intent when you require:

Requirement Benefit
Non-repudiation Prove the order came from you
High-value trades Additional security for large orders
Audit trails Cryptographic proof for compliance
Multi-layer architecture Prevent middleware tampering
Regulatory compliance Meet institutional security requirements

Before using signed intent: See API Key Management → for detailed setup instructions.


Add a signedIntent object to your standard order message →:

{
"messageType": "order",
"quoteId": "YdB4DodjOC5FkBDrYmKLW/Dwnz+x9bVbXyshrwz8yntjA+kZ...",
"side": "BUY",
"clientRequestId": "TESTCLIENT1",
"accountGrpUuid": "7687282f-1073-441b-9ff4-694e6b49effe",
"quantity": "100000",
"signedIntent": {
"payload": "eyJxdW90ZUlkIjoiMjAyNDAzMTUtQVREVVNELTk5ODg3NyIs...",
"signature": "M4eIw8Db3jKl9xQ2vN7pZ..."
}
}
Field Type Required Description
payload string Base64-encoded JSON containing order parameters
signature string Base64-encoded RSA signature of the payload

The payload is a JSON object containing only the critical order parameters:

{
"quoteId": "YdB4DodjOC5FkBDrYmKLW/Dwnz+x9bVbXyshrwz8yntjA+kZ...",
"side": "BUY",
"quantity": "100000",
"timestamp": 1710490000000
}
Field Required Type Description
quoteId Yes String Quote ID from price stream
side Yes String BUY or SELL
quantity Yes String Trade quantity
timestamp Recommended Long Unix timestamp in milliseconds (prevents replay attacks)

Step One: Create JSON

  • Construct the payload JSON object with required fields

Step Two: Base64 Encode Payload

  • Encode the JSON string to Base64
  • This becomes your payload field

Step Three: Sign the Base64 String

  • Sign using your RSA private key with SHA256
  • Use PKCS1v15 padding

Step Four: Base64 Encode Signature

  • Encode the binary signature to Base64
  • This becomes your signature field
  • Signing Algorithm: RSA-SHA256 (RS256)
  • Padding: PKCS1v15
  • Key Size: 4096-bit (recommended) or 2048-bit (minimum)
  • Encoding: Base64 for both payload and signature

Python
import json
import base64
import time
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
def generate_signed_order(quote_id, side, quantity, private_key_path):
"""Generate signed intent for order execution"""
# Load Private Key
with open(private_key_path, "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None
)
# Prepare the Payload JSON
intent_json = json.dumps({
"quoteId": quote_id,
"side": side,
"quantity": str(quantity),
"timestamp": int(time.time() * 1000)
})
# Base64 Encode the Payload
payload_b64 = base64.b64encode(
intent_json.encode('utf-8')
).decode('utf-8')
# Sign the Base64 Payload String (not the raw JSON)
signature = private_key.sign(
payload_b64.encode('utf-8'),
padding.PKCS1v15(),
hashes.SHA256()
)
# Base64 Encode the Signature
signature_b64 = base64.b64encode(signature).decode('utf-8')
return {
"payload": payload_b64,
"signature": signature_b64
}
# Usage
signedIntent = generate_signed_order(
quote_id="20240315-AEDUSD-998877",
side="BUY",
quantity=1.5,
private_key_path="zodia_private_key.pem"
)
# Complete order message
order_message = {
"messageType": "order",
"quoteId": "20240315-AEDUSD-998877",
"side": "BUY",
"clientRequestId": "my-ref-001",
"accountGrpUuid": "acc-grp-768",
"quantity": "1.5",
"signedIntent": signedIntent
}
ws.send(json.dumps(order_message))
JavaScript
const crypto = require('crypto');
const fs = require('fs');
function generateSignedOrder(quoteId, side, quantity, privateKeyPath) {
// Load Private Key
const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
// Prepare the Payload JSON
const intentJson = JSON.stringify({
quoteId: quoteId,
side: side,
quantity: quantity.toString(),
timestamp: Date.now()
});
// Base64 Encode the Payload
const payloadB64 = Buffer.from(intentJson, 'utf8').toString('base64');
// Sign the Base64 Payload String (not the raw JSON)
const signer = crypto.createSign('RSA-SHA256');
signer.update(payloadB64);
const signature = signer.sign(privateKey);
// Base64 Encode the Signature
const signatureB64 = signature.toString('base64');
return {
payload: payloadB64,
signature: signatureB64
};
}
// Usage
const signedIntent = generateSignedOrder(
'20240315-AEDUSD-998877',
'BUY',
1.5,
'zodia_private_key.pem'
);
// Complete order message
const orderMessage = {
messageType: 'order',
quoteId: '20240315-AEDUSD-998877',
side: 'BUY',
clientRequestId: 'my-ref-001',
accountGrpUuid: 'acc-grp-768',
quantity: '1.5',
signedIntent: signedIntent
};
ws.send(JSON.stringify(orderMessage));

Complete Trading Flow with Signed Intent Example

Section titled “Complete Trading Flow with Signed Intent Example”
Python
import json
import time
class SignedOrderExecutor:
def __init__(self, ws, private_key_path):
self.ws = ws
self.private_key_path = private_key_path
self.pending_orders = {}
def on_price_update(self, msg):
"""Handle price stream and execute with signed intent"""
if msg['messageType'] != 'pricestream':
return
# Check trading criteria
offer = float(msg['offer']['price'])
if not self.should_trade(offer):
return
# Generate signed order
quote_id = msg['quoteId']
client_id = f"ORDER-{int(time.time())}"
# Create signed intent
signedIntent = generate_signed_order(
quote_id=quote_id,
side='BUY',
quantity=msg['offer']['quantity'],
private_key_path=self.private_key_path
)
# Send order with signed intent
order = {
'messageType': 'order',
'quoteId': quote_id,
'side': 'BUY',
'clientRequestId': client_id,
'quantity': msg['offer']['quantity'],
'signedIntent': signedIntent
}
self.ws.send(json.dumps(order))
self.pending_orders[client_id] = {
'quote_id': quote_id,
'sent_at': time.time()
}
print(f"Sent signed order: {client_id}")
def on_order_response(self, msg):
"""Handle order confirmation"""
if msg['messageType'] == 'order':
client_id = msg['clientRequestId']
if msg['orderStatus'] == 'SUCCESS':
print(f"Signed order filled: {client_id}")
print(f"Transaction: {msg['transactionId']}")
else:
print(f"Order failed: {msg['message']}")
self.pending_orders.pop(client_id, None)
elif msg['messageType'] == 'error':
print(f"Error {msg['code']}: {msg['message']}")
if msg.get('clientRequestId'):
self.pending_orders.pop(msg['clientRequestId'], None)
def should_trade(self, price):
"""Your trading logic"""
return price < 3.67
# Usage
executor = SignedOrderExecutor(ws, 'zodia_private_key.pem')
def handle_message(ws, message):
msg = json.loads(message)
if msg['messageType'] == 'pricestream':
executor.on_price_update(msg)
elif msg['messageType'] in ['order', 'error']:
executor.on_order_response(msg)

When you submit an order with signedIntent, Zodia Markets receives the order with both standard fields and signed intent and then performs Signature validation using your registered public key. Base64 payload is decoded to get signed parameters and compared with order message fields. If the signature is valid and parameters match, order executes

{
"timestamp": 1718113605032,
"messageType": "error",
"message": "Signature verification failed",
"code": "INVALID_SIGNATURE",
"clientRequestId": "my-ref-001"
}