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-Secretheader? - How do I handle
placeArrival,placeDeparture, orgeofenceEnterevents? - 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 anyContent-Typeverifies, 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:
eventType | Family | Triggered when |
|---|---|---|
placeArrival | Visit | User arrives at a place |
placeDeparture | Visit | User leaves a place |
placeHistorical | Visit | A visit is reported after the fact |
geofenceEnter | Geofence | User enters a geofence |
geofenceDwell | Geofence | User stays inside a geofence |
geofencePresence | Geofence | User is found inside a geofence |
geofenceExit | Geofence | User leaves a geofence |
journeysInProgress, journeysApproaching, journeysArrived, journeysCompleted, journeysCanceled | Journeys | Journey 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
- references/overview.md - Movement SDK webhook concepts, event types, payload fields
- references/setup.md - Set the webhook URL and find the Pilgrim secret in the Developer Console
- references/verification.md - Pilgrim-Secret check, constant-time compare, gotchas
- examples/ - Express, Next.js, and FastAPI handlers with tests