Agent skill

Foursquare Webhooks Skill

Receive and authenticate Foursquare Movement SDK (formerly Pilgrim SDK) server webhooks. Use when setting up Foursquare webhook handlers, checking the Pilgrim-Secret header (there is no payload signature), or handling placeArrival, placeDeparture, geofenceEnter and geofenceExit events.

Install this skill

npx skills add hookdeck/webhook-skills --skill foursquare-webhooks


Foursquare's Movement SDK (formerly Pilgrim SDK) can call your server every time a user arrives at or departs from a place, triggers a geofence, or has a Journey status change. The server webhook is a JSON POST to the URL you set in the Developer Console, in addition to the SDK's regular in-app callback.

When to Use This Skill

  • How do I receive Foursquare Movement SDK (Pilgrim) webhooks?
  • How do I verify Foursquare webhooks? Is there a Foursquare webhook signature?
  • How do I check the Pilgrim-Secret header?
  • How do I handle placeArrival, placeDeparture, or geofenceEnter events?
  • Why does my Foursquare webhook handler return 401?

Verification (core)

Foursquare does not sign webhook payloads. Every request carries the secret from the Pilgrim page of the Developer Console, raw with no prefix, in the Pilgrim-Secret header. Compare it to your stored secret in constant time.

const crypto = require('crypto');

// Hash both sides so the buffers are always the same length
const sha256 = (v) => crypto.createHash('sha256').update(v).digest();

function verifyPilgrimSecret(headerValue, expectedSecret) {
  if (!headerValue || !expectedSecret) return false;
  return crypto.timingSafeEqual(sha256(headerValue), sha256(expectedSecret));
}

// verifyPilgrimSecret(req.headers['pilgrim-secret'], process.env.FOURSQUARE_PILGRIM_SECRET)

No body signature means the raw body isn't security-critical. The examples still read it raw (express.raw({ type: '*/*' })) so any Content-Type verifies, then parse the JSON after the secret check passes.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Common Event Types

Dispatch on the payload's eventType field:

eventTypeFamilyTriggered when
placeArrivalVisitUser arrives at a place
placeDepartureVisitUser leaves a place
placeHistoricalVisitA visit is reported after the fact
geofenceEnterGeofenceUser enters a geofence
geofenceDwellGeofenceUser stays inside a geofence
geofencePresenceGeofenceUser is found inside a geofence
geofenceExitGeofenceUser leaves a geofence
journeysInProgress, journeysApproaching, journeysArrived, journeysCompleted, journeysCanceledJourneysJourney status changes

Visit payloads carry pilgrimVisitId and placeEvent; geofence payloads carry geofenceEvent; Journey payloads carry journeyId. All include timestamp (ms since epoch), lat, lng, installId, and user. See references/overview.md.

Environment Variables

FOURSQUARE_PILGRIM_SECRET=your_pilgrim_secret   # Developer Console > Pilgrim page

The examples fail closed (return 500) if it isn't set.

Local Development

For local webhook testing, run the Hookdeck CLI via npx (no install required):

npx hookdeck-cli listen 3000 foursquare --path /webhooks/foursquare

No account required. The CLI provides a public URL that tunnels to your local server, plus a web UI for inspecting requests.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 3, 2026

View on GitHub →