Back to docs

Webhooks

Receive real-time notifications for Styx events

Real-time Event Notifications

Webhooks allow your application to receive real-time notifications when events occur on the Styx protocol. Instead of polling, you'll receive HTTP POST requests to your endpoint as events happen.

Pro Feature: Webhooks are available on Pro and Enterprise plans.Upgrade now

Setting Up Webhooks

1. Create webhook endpoint in dashboard

  1. 1.Go to Dashboard → Settings → Webhooks
  2. 2.Click "Add Webhook Endpoint"
  3. 3.Enter your HTTPS endpoint URL
  4. 4.Select the events you want to receive
  5. 5.Copy the signing secret (you'll need this to verify webhooks)

2. Create your webhook handler

import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.raw({ type: 'application/json' }));

const WEBHOOK_SECRET = process.env.STYX_WEBHOOK_SECRET!;

app.post('/webhooks/styx', (req, res) => {
  const signature = req.headers['x-styx-signature'] as string;
  const timestamp = req.headers['x-styx-timestamp'] as string;
  
  // Verify signature
  const payload = `${timestamp}.${req.body.toString()}`;
  const expectedSignature = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');
  
  if (!crypto.timingSafeEqual(
    Buffer.from(signature), 
    Buffer.from(expectedSignature)
  )) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Verify timestamp is within 5 minutes
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return res.status(401).json({ error: 'Timestamp too old' });
  }
  
  // Process the event
  const event = JSON.parse(req.body.toString());
  console.log('Received event:', event.type);
  
  switch (event.type) {
    case 'message.received':
      handleNewMessage(event.data);
      break;
    case 'airdrop.claimed':
      handleAirdropClaim(event.data);
      break;
    // ... handle other events
  }
  
  // Always return 200 quickly
  res.json({ received: true });
});

app.listen(3000);

Event Types

message.received

A new private message was received for a registered address

Example payload:
{
  "type": "message.received",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "signature": "5yN4...",
    "slot": 242567890,
    "recipient": "8Bfv...",
    "sender": "3Kxj...",
    "instructionTag": 3,
    "encryptedPayload": "base64..."
  }
}
airdrop.claimed

A recipient claimed tokens from a WhisperDrop campaign

Example payload:
{
  "type": "airdrop.claimed",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "signature": "4kM7...",
    "campaignId": "abc123...",
    "claimant": "7Yxn...",
    "amount": 1000000000,
    "stealthClaim": true
  }
}
campaign.created

A new WhisperDrop campaign was initialized

Example payload:
{
  "type": "campaign.created",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "signature": "2bNj...",
    "campaignPDA": "9Abc...",
    "authority": "8Bfv...",
    "tokenMint": "EPjF...",
    "expiresAt": "2024-02-15T10:30:00Z"
  }
}
campaign.expired

A WhisperDrop campaign expired and tokens were reclaimed

Example payload:
{
  "type": "campaign.expired",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "signature": "6kPq...",
    "campaignPDA": "9Abc...",
    "unclaimedTokens": 5000000000
  }
}

Webhook Security

Always verify webhook signatures to ensure events are genuinely from Styx and haven't been tampered with.

Verify the signature

Every webhook includes an X-Styx-Signature header. Compute the expected HMAC and compare using a timing-safe function.

Check the timestamp

The X-Styx-Timestamp header contains a Unix timestamp. Reject webhooks older than 5 minutes to prevent replay attacks.

Use HTTPS only

Webhook endpoints must use HTTPS. We will not send events to HTTP endpoints.

Rotate secrets regularly

You can rotate your webhook secret from the dashboard. Both secrets will be valid for 24 hours during rotation.

Retry Policy

If your endpoint doesn't respond with a 2xx status code, we'll retry with exponential backoff:

AttemptDelayTime Since First Attempt
1Immediate0s
25 seconds5s
330 seconds35s
45 minutes~5.5min
530 minutes~36min
62 hours~2.5hrs

After 6 failed attempts, the webhook will be marked as failed. You can view failed webhooks in your dashboard and manually retry them.

Best Practices

Return 200 quickly

Acknowledge the webhook with a 200 response before doing heavy processing. Use a queue for async processing.

Handle duplicates

In rare cases, webhooks may be delivered more than once. Use the event ID for idempotency.

Log all events

Log the full event payload for debugging. Make sure to filter sensitive data.

Handle timeouts

Webhooks timeout after 30 seconds. If your endpoint takes longer, we'll retry.