Webhooks

Receive real-time notifications for transaction status changes, bundle results, and billing events. Webhooks can be configured via the SDK or at connect time through the config builder.

Configure at Connect Time

1let config = Config::builder()
2    .api_key("sk_live_...")
3    .webhook_url("https://yourapp.com/webhooks/slipstream")
4    .webhook_events(vec![
5        "transaction.confirmed".into(),
6        "transaction.failed".into(),
7        "bundle.confirmed".into(),
8    ])
9    .webhook_notification_level("final")  // "all" | "final" | "confirmed"
10    .build()?;
1const config = configBuilder()
2    .apiKey("sk_live_...")
3    .webhookUrl("https://yourapp.com/webhooks/slipstream")
4    .webhookEvents(["transaction.confirmed", "transaction.failed"])
5    .webhookNotificationLevel("final")
6    .build();

Register via SDK

Register — Rust

1let webhook = client.register_webhook(
2    "https://yourapp.com/webhooks/slipstream",
3    Some(vec![
4        "transaction.confirmed".into(),
5        "transaction.failed".into(),
6        "bundle.confirmed".into(),
7    ]),
8    Some("final"),  // notification level
9).await?;
10
11println!("Webhook ID: {}", webhook.id);
12println!("Secret: {:?}", webhook.secret);  // Only shown on register
13println!("Active: {}", webhook.is_active);

Register — TypeScript

1const webhook = await client.registerWebhook(
2    "https://yourapp.com/webhooks/slipstream",
3    ["transaction.confirmed", "transaction.failed", "bundle.confirmed"],
4    "final",
5);
6
7console.log("Webhook ID:", webhook.id);
8console.log("Secret:", webhook.secret);

Register — Python

1webhook = await client.register_webhook(
2    url="https://yourapp.com/webhooks/slipstream",
3    events=["transaction.confirmed", "transaction.failed", "bundle.confirmed"],
4    notification_level="final",
5)
6
7print(f"Webhook ID: {webhook.id}")
8print(f"Secret: {webhook.secret}")

Event Types

EventDescription
transaction.sentTransaction sent to the network
transaction.confirmedTransaction confirmed on-chain
transaction.failedTransaction failed or expired
bundle.sentBundle sent to the network
bundle.confirmedBundle confirmed on-chain
bundle.failedBundle failed or dropped
billing.low_balanceToken balance below threshold
billing.depletedToken balance reached zero
billing.deposit_receivedSOL deposit received and credited

Notification Levels

LevelEvents Delivered
allEvery status change (sent, confirmed, failed)
finalOnly final states (confirmed + failed) — default
confirmedOnly confirmed events

Managing Webhooks

Get Current Webhook — Rust

1if let Some(webhook) = client.get_webhook().await? {
2    println!("URL: {}", webhook.url);
3    println!("Events: {:?}", webhook.events);
4    println!("Active: {}", webhook.is_active);
5}

Delete Webhook — Rust

1client.delete_webhook().await?;

Get/Delete — TypeScript

1const webhook = await client.getWebhook();
2if (webhook) console.log("URL:", webhook.url);
3
4await client.deleteWebhook();

Get/Delete — Python

1webhook = await client.get_webhook()
2if webhook:
3    print(f"URL: {webhook.url}")
4
5await client.delete_webhook()

HMAC-SHA256 Verification

Every webhook request includes an X-Slipstream-Signature header. Verify it with the secret returned during registration.

Verify — Node.js

1import crypto from "crypto";
2
3function verifyWebhook(body: string, signature: string, secret: string): boolean {
4    const expected = crypto
5        .createHmac("sha256", secret)
6        .update(body)
7        .digest("hex");
8    return crypto.timingSafeEqual(
9        Buffer.from(signature),
10        Buffer.from(expected),
11    );
12}
13
14app.post("/webhooks/slipstream", (req, res) => {
15    const signature = req.headers["x-slipstream-signature"] as string;
16    if (!verifyWebhook(JSON.stringify(req.body), signature, SIGNING_SECRET)) {
17        return res.status(401).json({ error: "Invalid signature" });
18    }
19
20    const event = req.body;
21    switch (event.type) {
22        case "transaction.confirmed":
23            console.log("TX confirmed:", event.data.signature);
24            break;
25        case "billing.low_balance":
26            console.log("Low balance warning");
27            break;
28    }
29
30    res.status(200).json({ received: true });
31});

Verify — Python

1import hmac
2import hashlib
3
4def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
5    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
6    return hmac.compare_digest(signature, expected)

Verify — Rust

1use hmac::{Hmac, Mac};
2use sha2::Sha256;
3
4fn verify_webhook(body: &[u8], signature: &str, secret: &str) -> bool {
5    let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap();
6    mac.update(body);
7    let expected = hex::encode(mac.finalize().into_bytes());
8    expected == signature
9}

Next Steps