When you submit a ZK proof through Kurier, you get a job ID. Until now, tracking that job meant polling the REST API on an interval, checking over and over until the status changed.
Polling works, but it has tradeoffs. Short intervals waste requests. Long intervals miss updates. For developers building real-time UIs or automated pipelines that react to proof verification events, this creates unnecessary friction.
Kurier now supports WebSocket connections for real-time job status updates. Open a connection, subscribe to a job, and receive instant notifications as your proof moves from submission through on-chain finality.
Why WebSockets
The ZK proof verification pipeline has multiple stages: queued, optimistic verification, blockchain submission, block inclusion, and finalization. Each transition matters for different use cases.
A trading platform needs to know the moment a proof is finalized. An automated pipeline needs to trigger the next step as soon as verification passes. A monitoring dashboard needs to show live status without hammering the API.
WebSockets solve all of these by pushing updates to the client the instant they happen.
How It Works
The Kurier WebSocket API uses a simple JSON message protocol over a persistent connection.
1. Connect with your API key:
wss://api.kurier.xyz/api/v1/ws?apiKey=YOUR_API_KEY
For testnet, use wss://api-testnet.kurier.xyz/api/v1/ws?apiKey=YOUR_API_KEY.
2. Subscribe to a job after submitting a proof:
{ "type": "subscribe", "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
3. Receive status updates as they happen:
{
"type": "job-status-update",
"data": {
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "Finalized",
"event": "finalized",
"proofType": "groth16",
"txHash": "0x...",
"blockHash": "0x..."
}
}
When a job reaches a final state (Finalized, Aggregated, or Failed), the subscription is automatically cleaned up. No manual unsubscribe needed.
Subscribe to Everything
If you're running a service that submits many proofs, you can subscribe to all jobs under your API key at once:
{ "type": "subscribe-all" }
Every job submitted with that API key will stream status updates to your connection. This is useful for dashboards, monitoring systems, or batch processing workflows where you need visibility across all active jobs.
A Practical Example
Here is a complete flow: submit a proof via REST, then track it via WebSocket.
import fetch from 'node-fetch';
import WebSocket from 'ws';
const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://api.kurier.xyz/api/v1';
// Step 1: Submit a proof via REST
const response = await fetch(`${BASE_URL}/submit-proof/${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
proofData: { proof: '...', vk: '...' },
proofType: 'groth16',
vkRegistered: false,
}),
});
const { jobId } = await response.json();
console.log('Submitted job:', jobId);
// Step 2: Connect WebSocket and subscribe
const ws = new WebSocket(`${BASE_URL.replace('https', 'wss')}/ws?apiKey=${API_KEY}`);
ws.on('open', () => {
ws.send(JSON.stringify({ type: 'subscribe', jobId }));
});
ws.on('message', (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.type === 'job-status-update') {
console.log(`${msg.data.status} - ${msg.data.jobId}`);
if (msg.data.status === 'Finalized') {
console.log('Proof finalized on-chain!');
console.log('Tx:', msg.data.txHash);
ws.close();
}
}
});
// Keep-alive
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
}
}, 30_000);
Connection Details
Each API key can open up to 10 concurrent WebSocket connections, and each connection can stay open for up to 24 hours. If you need to reconnect, simply open a new connection and re-subscribe.
Rate limiting applies to subscribe and unsubscribe operations. If you exceed the limit, the server returns an error with a retryAfterSeconds field so your client knows exactly when to retry.
The WebSocket endpoint is available on both mainnet and testnet:
- Mainnet:
wss://api.kurier.xyz/api/v1/ws - Testnet:
wss://api-testnet.kurier.xyz/api/v1/ws
When to Use WebSockets vs. Polling
Both approaches return the same job status data. The choice depends on your use case:
- WebSockets are ideal for real-time UIs, automated pipelines, multi-job monitoring, and any scenario where you need to react to status changes immediately.
- REST polling is simpler for one-off scripts, CI/CD checks, or cases where you only need to check a job's final result.
You can mix both in the same application. For example, use WebSockets in your frontend dashboard while using REST polling in a cron job that reconciles final states.
Getting Started
WebSocket support is live now on both mainnet and testnet. Check out the full WebSocket API documentation for the complete message reference, error codes, and connection limits.
