Skip to main content
Developer docs

Guide

Receive webhooks

Subscribe an endpoint to reader events, verify that each delivery genuinely came from Flipbooker, and debug the ones that never arrive.

Before you start

  • An API key with the webhooks:read and webhooks:write scopes.
  • A publicly reachable HTTPS endpoint that responds with a 2xx status quickly.
  1. 1

    Choose the events you care about

    Nine event types are available. Subscribe only to what you will act on — every extra event is another delivery your endpoint has to absorb.

    access_requested, access_granted, access_denied, flipbook_opened, page_dwelled, link_clicked, download_requested, cta_submitted and session_ended.

  2. 2

    Create the subscription

    Set a signing_secret at creation — without one you cannot verify that a delivery came from Flipbooker. Generate a long random string and store it alongside your API key.

    Narrow the firehose with filters: restrict to specific book_ids, tracked_link_ids or recipient_emails. Every filtered flipbook must belong to your workspace.

    POST /webhook-subscriptions
    curl -X POST https://app.flipbooker.com/api/v1/webhook-subscriptions \
      -H "Authorization: Bearer $FLIPBOOKER_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "CRM sync",
        "provider": "generic",
        "target_url": "https://example.com/hooks/flipbooker",
        "signing_secret": "whsec_...",
        "event_types": ["flipbook_opened", "cta_submitted", "session_ended"],
        "filters": { "book_ids": [4821] }
      }'
    provider must be generic or zapier
    Use "generic" for your own endpoint. "zapier" is reserved for Zapier's own subscription handshake.
  3. 3

    Verify the signature on every delivery

    Each delivery carries X-Webhook-Signature, X-Webhook-Timestamp and X-Webhook-Event. The signature is an HMAC-SHA256 of "{timestamp}.{raw body}", keyed with your signing secret.

    Compute it over the raw request body — do not parse and re-serialise the JSON first, or the bytes will differ and the signature will never match. Compare with a constant-time function, and reject timestamps more than 5 minutes old to block replays.

    Express example
    import crypto from 'node:crypto'
    
    // Give this route the RAW body, not a parsed one.
    app.post('/hooks/flipbooker',
      express.raw({ type: 'application/json' }),
      (req, res) => {
        const signature = req.get('X-Webhook-Signature') ?? ''
        const timestamp = Number(req.get('X-Webhook-Timestamp') ?? 0)
    
        // Reject anything outside Flipbooker's 5 minute tolerance.
        if (Math.abs(Date.now() / 1000 - timestamp) > 300) {
          return res.status(400).send('stale')
        }
    
        const expected = crypto
          .createHmac('sha256', process.env.FLIPBOOKER_WEBHOOK_SECRET)
          .update(`${timestamp}.${req.body.toString('utf8')}`)
          .digest('hex')
    
        // timingSafeEqual throws on a length mismatch, so check that first.
        if (signature.length !== expected.length) {
          return res.status(401).send('bad signature')
        }
        const ok = crypto.timingSafeEqual(
          Buffer.from(expected, 'hex'),
          Buffer.from(signature, 'hex')
        )
        if (!ok) return res.status(401).send('bad signature')
    
        // Acknowledge fast, then do the slow work out of band.
        res.sendStatus(202)
        queue.push(JSON.parse(req.body))
      }
    )
  4. 4

    Send yourself a test delivery

    Trigger a real delivery against the subscription to confirm your endpoint accepts it. A 202 means Flipbooker queued it.

    You can also fire an ad-hoc sample without a subscription using POST /webhooks, which posts a sample payload to any URL you name.

    POST /webhook-subscriptions/{subscription}/test
    curl -X POST https://app.flipbooker.com/api/v1/webhook-subscriptions/$SUBSCRIPTION_ID/test \
      -H "Authorization: Bearer $FLIPBOOKER_API_KEY"
  5. 5

    Debug what went wrong

    When events stop arriving, read the delivery history before changing anything. It records each attempt with the status your endpoint returned, so you can tell a Flipbooker problem from a your-server problem.

    A delivery that never got a response usually means your endpoint took too long — acknowledge within a few seconds and process asynchronously.

    GET /webhook-subscriptions/{subscription}/deliveries
    curl https://app.flipbooker.com/api/v1/webhook-subscriptions/$SUBSCRIPTION_ID/deliveries \
      -H "Authorization: Bearer $FLIPBOOKER_API_KEY"

Where to next