Pique Docs

Webhooks

Receive real-time webhook notifications when Pique makes decisions about visitor sessions.

When Pique makes an actionable decision about a visitor session, it sends a webhook to your configured endpoints. Webhooks are delivered via Svix, which handles signing, retries, and delivery logging.

Payload format

Every webhook has the event type decision.made and the following JSON body:

{
  "type": "decision.made",
  "decision": {
    "id": "a1b2c3d4-...",
    "action": "cart_abandonment",
    "reason": "Added \"Summer Dress\" to cart but left without purchasing.",
    "llmReasoning": "This visitor spent 4 minutes browsing 3 products before adding..."
  },
  "session": {
    "visitorId": "f8e7d6c5-...",
    "email": "customer@example.com",
    "eventCount": 12,
    "durationSeconds": 245,
    "productsViewed": 3,
    "cartItems": ["Summer Dress"]
  },
  "timestamp": "2025-01-15T14:32:00.000Z"
}

Signature verification

Every webhook is signed by Svix using HMAC-SHA256. When you create an endpoint, you receive a signing secret (starting with whsec_). Verify incoming webhooks using the Svix SDK:

import { Webhook } from "svix";

const wh = new Webhook("whsec_your_signing_secret");

function handleWebhook(req, res) {
  try {
    const payload = wh.verify(req.body, {
      "svix-id": req.headers["svix-id"],
      "svix-timestamp": req.headers["svix-timestamp"],
      "svix-signature": req.headers["svix-signature"],
    });
    // payload is verified — process it
    console.log(payload);
    res.status(200).json({ ok: true });
  } catch (err) {
    res.status(400).json({ error: "Invalid signature" });
  }
}

Klaviyo integration

The most common integration is connecting Pique decisions to Klaviyo flows. Here's a step-by-step guide.

1. Create a Klaviyo API key

In your Klaviyo account, go to Settings → API Keys and create a private API key with write access to Events (Track API). Copy the key.

2. Set up a webhook relay

Deploy a small HTTP endpoint that receives Pique webhooks and forwards them to Klaviyo's Track API. Below is a complete example:

import express from "express";
import { Webhook } from "svix";

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

const SVIX_SECRET = process.env.SVIX_SECRET;
const KLAVIYO_API_KEY = process.env.KLAVIYO_API_KEY;

app.post("/webhook", async (req, res) => {
  const wh = new Webhook(SVIX_SECRET);

  let payload;
  try {
    payload = wh.verify(req.body, {
      "svix-id": req.headers["svix-id"],
      "svix-timestamp": req.headers["svix-timestamp"],
      "svix-signature": req.headers["svix-signature"],
    });
  } catch {
    return res.status(400).json({ error: "Invalid signature" });
  }

  if (!payload.session.email) {
    return res.status(200).json({ skipped: "no email" });
  }

  const eventName = {
    cart_abandonment: "Pique Cart Abandonment",
    browse_abandonment: "Pique Browse Abandonment",
    price_sensitive: "Pique Price Sensitive",
    gentle_nudge: "Pique Gentle Nudge",
  }[payload.decision.action] || `Pique ${payload.decision.action}`;

  await fetch("https://a.klaviyo.com/api/events", {
    method: "POST",
    headers: {
      Authorization: `Klaviyo-API-Key ${KLAVIYO_API_KEY}`,
      "Content-Type": "application/json",
      revision: "2024-10-15",
    },
    body: JSON.stringify({
      data: {
        type: "event",
        attributes: {
          metric: { data: { type: "metric", attributes: { name: eventName } } },
          profile: {
            data: {
              type: "profile",
              attributes: { email: payload.session.email },
            },
          },
          properties: {
            action: payload.decision.action,
            reason: payload.decision.reason,
            llmReasoning: payload.decision.llmReasoning,
            productsViewed: payload.session.productsViewed,
            cartItems: payload.session.cartItems,
            durationSeconds: payload.session.durationSeconds,
          },
          time: payload.timestamp,
        },
      },
    }),
  });

  res.status(200).json({ ok: true });
});

app.listen(3001, () => console.log("Relay listening on :3001"));

3. Map actions to Klaviyo flows

In Klaviyo, create flows triggered by the metric names above. For example:

  • Pique Cart Abandonment → Send an abandoned cart email with the items from cartItems
  • Pique Browse Abandonment → Send a product recommendation email
  • Pique Price Sensitive → Send a discount or bundle offer
  • Pique Gentle Nudge → Send a soft follow-up with the products they viewed

The llmReasoning field is available as a Klaviyo event property — you can use it in email templates for personalized copy or internal context.

Generic webhook

For any HTTP endpoint, the flow is:

  1. Add a webhook endpoint in Settings → Webhooks
  2. Copy the signing secret
  3. Verify the svix-signature header on incoming requests
  4. Parse the JSON body and route on decision.action
  5. Respond with 2xx within 15 seconds

Svix retries failed deliveries with exponential backoff. You can view delivery logs and manually retry from the Webhooks section in your dashboard.

On this page