WebhooksArchitectureTutorial

Webhooks for Country Data Changes — Get Notified When ISO Codes Update

ApogeoAPI6 min read

Most apps using country / state / city data cache it locally for performance. The downside: when a country splits, an ISO code changes, or a state gets renamed, your cache goes stale until the next manual refresh.

ApogeoAPI's webhook system pushes those events to your endpoint as soon as the underlying data changes. This tutorial covers the full setup with HMAC signature verification.

Available events

EventFired when
country.createdA new country is added to the database (rare — average ~1/year)
country.updatedAny field on a country changes (capital, currency, name, region)
country.deletedA country is removed (extremely rare)
state.updatedA state/province record changes
currency_rate.updatedLive FX refresh — fires every 4 hours per currency

Register the webhook

From your dashboard at app.apogeoapi.com → Webhooks → Add endpoint. Paste your URL, choose which events to subscribe to, and copy the secret. Or via API:

curl -X POST https://api.apogeoapi.com/v1/webhooks \
  -H "X-API-Key: $APOGEOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/apogeoapi",
    "events": ["country.updated", "state.updated"],
    "active": true
  }'

Response includes a secret field — save it; you'll need it to verify signatures.

Receive + verify in Node.js / Express

import express from 'express';
import crypto from 'node:crypto';

const app = express();
const SECRET = process.env.APOGEO_WEBHOOK_SECRET!;

// IMPORTANT: webhook handlers need the RAW body to verify the HMAC.
// Use express.raw() instead of express.json().
app.post(
  '/webhooks/apogeoapi',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.header('X-ApogeoAPI-Signature');
    if (!signature) return res.status(400).send('missing signature');

    const expected = crypto
      .createHmac('sha256', SECRET)
      .update(req.body)
      .digest('hex');

    if (
      !crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expected)
      )
    ) {
      return res.status(401).send('invalid signature');
    }

    const event = JSON.parse(req.body.toString('utf8'));
    handleEvent(event);
    res.status(204).end();
  }
);

interface Event {
  id: string;
  event: string;
  data: Record;
  occurredAt: string;
}

function handleEvent(event: Event) {
  switch (event.event) {
    case 'country.updated':
      console.log('Country changed:', event.data);
      // invalidateCache(event.data.iso2)
      break;
    case 'currency_rate.updated':
      // updateLocalRate(event.data.currency, event.data.usdRate)
      break;
  }
}

Payload examples

country.updated

{
  "id": "evt_2026_03_27_abc123",
  "event": "country.updated",
  "data": {
    "iso2": "TR",
    "name": "Türkiye",
    "previousName": "Turkey",
    "changedFields": ["name", "translations"]
  },
  "occurredAt": "2026-03-27T14:32:11Z"
}

currency_rate.updated

{
  "id": "evt_2026_03_27_def456",
  "event": "currency_rate.updated",
  "data": {
    "currency": "ARS",
    "usdRate": 1480.5,
    "previousUsdRate": 1450.2,
    "lastUpdated": "2026-03-27T14:00:00Z"
  },
  "occurredAt": "2026-03-27T14:00:03Z"
}

Retry behavior

If your endpoint returns non-2xx, ApogeoAPI retries with exponential backoff: 1 min, 5 min, 30 min, 2h, 8h. After 5 failed attempts, the webhook is marked inactive and an email is sent to your account address.

To handle retries idempotently, key your local processing on the id field. ApogeoAPI guarantees the same id for retries of the same event.

Testing locally

Use ngrok or Cloudflare Tunnel to expose your localhost endpoint, register that URL as the webhook target, and trigger test events from the dashboard's "Send test event" button.

What we don't deliver via webhooks

  • City updates. 150K cities = too much churn for webhooks. Use cities/{id} with If-Modified-Since instead.
  • API key events. These are account-level, not data-level. Different webhook type (planned).
  • IP geolocation events. No "fired when an IP changes country" semantic exists. Just re-call the endpoint.

Webhooks are a paid-tier feature on ApogeoAPI (Basic and above). Free tier customers should poll endpoints with conservative TTLs instead. Get started at apogeoapi.com — 14-day full trial includes webhooks.

Try ApogeoAPI free

1,000 requests/month forever. 14-day full-access trial. No credit card.

Get your free API key