Building Real-Time Integrations with Webhooks
Why webhooks?
Polling an API every few seconds works — until it doesn't. Webhooks flip the model: instead of asking "did anything happen?", the platform tells you the moment something does.
This matters when you're building trading bots, portfolio dashboards, or notification systems where latency is critical.
Setting up your first webhook
Head to the Developer Portal and create a new webhook endpoint. You'll need a publicly accessible URL that can receive POST requests.
import express from "express";
import crypto from "crypto";
const app = express();
app.use(express.json());
app.post("/webhook", (req, res) => {
const signature = req.headers["x-jup-signature"];
const expected = crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest("hex");
if (signature !== expected) {
return res.status(401).json({ error: "Invalid signature" });
}
console.log("Event received:", req.body.type);
res.status(200).json({ ok: true });
});
app.listen(3000);Event types
You can subscribe to any combination of events:
| Event | Description |
|---|---|
swap.completed | A swap has been executed successfully |
order.filled | A limit or trigger order has been filled |
order.cancelled | An order was cancelled by the user or system |
account.updated | Account settings or API key changes |
Handling failures gracefully
Webhooks can fail — your server might be down, or the request might time out. Jupiter retries failed deliveries with exponential backoff:
- 1st retry: 30 seconds
- 2nd retry: 5 minutes
- 3rd retry: 30 minutes
- Final retry: 2 hours
After all retries are exhausted, the event is marked as failed and visible in your dashboard.
Idempotency
Each event includes a unique event_id. Always check for duplicates before processing:
import redis
r = redis.Redis()
def handle_event(event):
event_id = event["event_id"]
if r.sismember("processed_events", event_id):
return # Already handled
# Process the event
process(event)
r.sadd("processed_events", event_id)
r.expire(f"processed_events", 86400 * 7)Best practices
- Respond quickly — return a 200 status within 5 seconds, then process asynchronously
- Verify signatures — always validate the
x-jup-signatureheader - Log everything — store raw payloads for debugging
- Use a queue — push events to a message queue for reliable processing
Webhooks are one of the most powerful tools in the platform. Once you've set them up, you'll wonder how you ever built without them.