Skip to main content
Developer docs

Guide

Sync leads to your CRM

Pull the people a flipbook captured — through lead forms, gated access or CTA submissions — and keep them flowing into your own systems on a schedule.

Before you start

  • An API key with the exports:read and subscribers:read scopes.
  • The reader-level analytics in the last step need the Company plan. The lead export itself does not.
  1. 1

    Decide between a pull and a push

    There are two ways to get leads out. Exports are a pull: you ask for a flipbook's leads and get a file back — simplest to schedule, and the right choice for a nightly CRM sync.

    Webhooks are a push: Flipbooker calls you the moment someone submits a form. Use those when a salesperson needs to follow up within minutes rather than overnight.

    Most teams run both — webhooks for speed, a nightly export to backfill anything a failed delivery missed.

  2. 2

    Export a flipbook's leads

    Ask for the leads captured by one flipbook. Unlike the analytics exports it carries no plan-tier requirement — the exports:read scope is the only thing checked.

    The response is a file download, not JSON — write it straight to disk rather than parsing it as an API envelope.

    GET /exports/books/{bookId}/leads
    curl https://app.flipbooker.com/api/v1/exports/books/4821/leads \
      -H "Authorization: Bearer $FLIPBOOKER_API_KEY" \
      -o leads-4821.csv
  3. 3

    Or read subscribers as JSON

    If you would rather work with structured data than a file, GET /subscribers returns everyone across the workspace's flipbooks — including readers added by email gating, not just form submissions.

    It is cursor-paginated, so follow meta.next_cursor until has_more is false.

    GET /subscribers
    curl -G https://app.flipbooker.com/api/v1/subscribers \
      -H "Authorization: Bearer $FLIPBOOKER_API_KEY" \
      -d limit=100
  4. 4

    Sync incrementally, not from scratch

    Re-importing every lead each night will create duplicates in most CRMs. Keep a high-water mark of the last record you processed and upsert on email address.

    The snippet below walks every page, then hands only the new records to your CRM client.

    Nightly sync
    const KEY = process.env.FLIPBOOKER_API_KEY
    let cursor = null
    const fresh = []
    
    do {
      const url = new URL('https://app.flipbooker.com/api/v1/subscribers')
      url.searchParams.set('limit', '100')
      if (cursor) url.searchParams.set('cursor', cursor)
    
      const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } })
      if (!res.ok) throw new Error(`Flipbooker returned ${res.status}`)
    
      const { data, meta } = await res.json()
      for (const person of data) {
        if (person.created_at > lastSyncedAt) fresh.push(person)
      }
      cursor = meta?.has_more ? meta.next_cursor : null
    } while (cursor)
    
    // Upsert on email so a re-run is harmless.
    await crm.upsertContacts(fresh, { matchOn: 'email' })
    Watch the rate limit
    A full sync is a burst of requests against one endpoint, which is capped at 100 per minute. Page with limit=100 and back off on a 429 rather than running the loop flat out.
  5. 5

    Prove where a lead came from

    Sales will ask which document produced a lead. Pair the export with GET /analytics/readers for the same book_id to attach engagement — how long they read, how far they got — to each contact.

    For campaign-level attribution, create a tracked link per channel and filter analytics by tracked_link_id.

    GET /analytics/readers
    curl -G https://app.flipbooker.com/api/v1/analytics/readers \
      -H "Authorization: Bearer $FLIPBOOKER_API_KEY" \
      -d book_id=4821 \
      -d sort=engagement

Where to next