# 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.

```javascript
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/](https://github.com/hookdeck/webhook-skills/tree/main/skills/foursquare-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/foursquare-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/foursquare-webhooks/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](https://github.com/hookdeck/webhook-skills/blob/main/skills/foursquare-webhooks/references/overview.md).

## Environment Variables

```bash
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):

```bash
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](https://github.com/hookdeck/webhook-skills/blob/main/skills/foursquare-webhooks/references/overview.md) - Movement SDK webhook concepts, event types, payload fields
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/foursquare-webhooks/references/setup.md) - Set the webhook URL and find the Pilgrim secret in the Developer Console
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/foursquare-webhooks/references/verification.md) - Pilgrim-Secret check, constant-time compare, gotchas
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/foursquare-webhooks/examples/) - Express, Next.js, and FastAPI handlers with tests