✈
    TravelmodeDevelopers

    Weather Intelligence API · v1

    🔎/
    📖Overview🧪API Reference (Try It)
    Guides
    🚀Getting Started🔐Authentication⏱️Rate Limits📡Endpoints⚠️Errors💳Billing🧊Cache & Freshness🪝Webhooks📝Attribution
    ⬇️Download openapi.yaml🔑Manage API Keys
    Developers / Endpoints

    Endpoints

    Last verified: 2026-05-03 (Task #334 — docs accuracy pass).

    Reference for every Weather Intelligence endpoint. The OpenAPI spec in openapi.yaml is the source of truth for full schemas — the sections here cover the human-readable summary, the required scope, and at least one paired request + response example per endpoint.

    Response envelope

    Every successful response returns:

    {
      "data": <endpoint-specific payload>,
      "meta": {
        "request_id": "uuid",
        "generated_at": "iso-8601",
        "environment": "production",
        "cache_status": "HIT|MISS|STALE|MIXED",
        "snapshot_id": "uuid",
        "billing_units": 1,
        "items_analyzed": 1,
        "segments_analyzed": 0,
        "units": "metric",
        "language": "en"
      }
    }
    

    meta.cache_status, meta.snapshot_id, meta.items_analyzed, and meta.segments_analyzed are populated only on the endpoints they make sense for. Errors share the same meta block but ship the payload under error instead of data (see errors.md).

    Standard headers on every response

    HeaderNotes
    X-Request-IdSame value as meta.request_id.
    X-Cache-StatusSame value as meta.cache_status (when applicable).
    X-Weather-Snapshot-IdSame value as meta.snapshot_id (when applicable).
    X-RateLimit-LimitCap for the requests quota dimension on the calling plan.
    X-RateLimit-RemainingRequests left in the current monthly window.
    X-RateLimit-ResetUnix-seconds when the monthly window rolls over.

    Current weather

    GET /v1/weather/current  ·  Scope: weather:read  ·  Bills: 1 unit

    Query parameters

    NameRequiredNotes
    latyes-90 to 90
    lngyes-180 to 180
    unitsnometric (default), imperial, standard
    languagenoBCP-47 (en, en-GB, …)
    activity_typenoFree-form. Unknown values fall back to the outdoor default profile.

    Request

    curl -s "https://api.travelmode.app/v1/weather/current?lat=48.8566&lng=2.3522" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": {
        "location": {
          "lat": 48.8566, "lng": 2.3522,
          "geohash": "u09tu", "geohash_precision": 5,
          "timezone": "Europe/Paris", "place_type": "city"
        },
        "current": {
          "observed_at": "2026-04-25T18:00:00.000Z",
          "temperature": 14.2, "feels_like": 12.9,
          "humidity_pct": 71, "pressure_hpa": 1013,
          "wind": { "speed_ms": 4.1, "gust_ms": 7.2, "direction_deg": 220 },
          "cloud_cover_pct": 75, "visibility_m": 10000,
          "uv_index": 1, "precipitation_mm": 0.2,
          "condition": { "code": "500", "label": "light rain", "icon": "10d" }
        },
        "alerts": [],
        "risk": { "level": "low", "reasons": [], "recommendation": null, "activity_type": null },
        "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
        "provider": "openweather",
        "provider_version": "onecall/3.0",
        "attribution": "Weather data provided by OpenWeather",
        "fetched_at": "2026-04-25T18:01:13.412Z"
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:01:13.500Z",
        "environment": "production",
        "cache_status": "HIT",
        "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
        "billing_units": 1,
        "units": "metric",
        "language": "en"
      }
    }
    

    Forecast

    GET /v1/weather/forecast  ·  Scope: weather:read  ·  Bills: 1 unit (daily) / 2 units (hourly)

    Query parameters

    NameRequiredNotes
    latyes
    lngyes
    daysno1–8 (default 7). Clamped to provider max.
    granularitynodaily (default) or hourly.
    units, languagenoSame as current.

    Request

    curl -s "https://api.travelmode.app/v1/weather/forecast?lat=48.8566&lng=2.3522&days=3&granularity=daily" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": {
        "location": { "lat": 48.8566, "lng": 2.3522, "geohash": "u09tu", "geohash_precision": 5, "timezone": "Europe/Paris", "place_type": "city" },
        "granularity": "daily",
        "days": 3,
        "hourly": null,
        "daily": [
          {
            "local_date": "2026-04-26",
            "sunrise": "2026-04-26T04:55:00.000Z",
            "sunset":  "2026-04-26T18:55:00.000Z",
            "temperature": { "min": 8.1, "max": 16.4 },
            "feels_like":  { "min": 6.9, "max": 15.2 },
            "humidity_pct": 65,
            "wind": { "speed_ms": 3.2, "gust_ms": 6.1, "direction_deg": 200 },
            "cloud_cover_pct": 40,
            "precipitation_mm": 1.2,
            "precipitation_probability_pct": 35,
            "uv_index_max": 5,
            "condition": { "code": "500", "label": "light rain", "icon": "10d" }
          }
        ],
        "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
        "provider": "openweather",
        "provider_version": "onecall/3.0",
        "attribution": "Weather data provided by OpenWeather",
        "fetched_at": "2026-04-25T18:01:13.412Z"
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:01:13.500Z",
        "environment": "production",
        "cache_status": "HIT",
        "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
        "billing_units": 1,
        "items_analyzed": 3,
        "units": "metric",
        "language": "en"
      }
    }
    

    The daily array is shown trimmed to one entry for brevity — a real days=3 response carries three entries (and days=7 carries seven).


    Timeline

    POST /v1/weather/timeline  ·  Scope: weather:timeline  ·  Bills: 2 units × items_analyzed

    Accepts up to 100 items per call. Each item gets weather + risk; the response also carries an aggregate summary (overall risk, main issue, best outdoor window).

    activity_type is free-form. Canonical MVP values: walking, walking_tour, hiking, beach, dining, outdoor_dining, museum, shopping, driving, train, ferry, cycling, airport_transfer, event, sightseeing, free_time.

    Request

    curl -s -X POST "https://api.travelmode.app/v1/weather/timeline" \
      -H "Authorization: Bearer tm_weather_..." \
      -H "Content-Type: application/json" \
      -d '{
        "units": "metric",
        "language": "en",
        "items": [
          {
            "id": "morning_walk",
            "start": "2026-05-15T08:00:00.000Z",
            "end":   "2026-05-15T09:30:00.000Z",
            "lat": 48.8566, "lng": 2.3522,
            "activity_type": "walking_tour",
            "timezone": "Europe/Paris"
          },
          {
            "id": "louvre_visit",
            "start": "2026-05-15T10:00:00.000Z",
            "end":   "2026-05-15T13:00:00.000Z",
            "lat": 48.8606, "lng": 2.3376,
            "activity_type": "museum",
            "timezone": "Europe/Paris"
          }
        ]
      }'
    

    Response (200 OK)

    {
      "data": {
        "items": [
          {
            "id": "morning_walk",
            "activity_type": "walking_tour",
            "outdoor": true,
            "start": "2026-05-15T08:00:00.000Z",
            "weather": {
              "observed_at": "2026-05-15T08:00:00.000Z",
              "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
              "cache_status": "HIT",
              "temperature_c": 12.4, "feels_like_c": 11.2,
              "condition": "light rain",
              "precipitation_probability": 65,
              "precipitation_intensity_mm_h": 1.2,
              "wind_speed_kph": 14.4, "wind_gust_kph": 22.0,
              "uv_index": 2
            },
            "risk": {
              "level": "watch",
              "reasons": [
                { "code": "rain_likely", "level": "watch", "message": "Light rain likely (65% chance)" }
              ],
              "recommendation": "Bring a rain jacket; light rain expected during your walking tour."
            }
          },
          {
            "id": "louvre_visit",
            "activity_type": "museum",
            "outdoor": false,
            "start": "2026-05-15T10:00:00.000Z",
            "weather": {
              "observed_at": "2026-05-15T10:00:00.000Z",
              "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
              "cache_status": "HIT",
              "temperature_c": 13.0, "feels_like_c": 12.0,
              "condition": "light rain",
              "precipitation_probability": 60,
              "precipitation_intensity_mm_h": 0.8,
              "wind_speed_kph": 12.6, "wind_gust_kph": 20.5,
              "uv_index": 2
            },
            "risk": { "level": "low", "reasons": [] }
          }
        ],
        "summary": {
          "overall_risk": "watch",
          "main_issue": "Light rain likely (65% chance) for walking tour at 10:00 AM",
          "best_outdoor_window": {
            "start": "2026-05-15T13:00:00.000Z",
            "end":   "2026-05-15T17:00:00.000Z",
            "level": "low"
          }
        }
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:01:13.500Z",
        "environment": "production",
        "cache_status": "HIT",
        "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
        "billing_units": 4,
        "items_analyzed": 2,
        "units": "metric",
        "language": "en"
      }
    }
    

    Validation errors

    CodeWhen
    bad_requestTop-level body failed schema (missing items, etc).
    timeline_item_invalidIndividual item failed validation. details carries index, field, and issues.

    Route

    POST /v1/weather/route  ·  Scope: weather:route  ·  Bills: 3 units × segments_analyzed

    Samples points along a route and returns weather + risk per segment, plus an overall_risk summary.

    transport_mode is one of car, bus, train, ferry, walking, cycling, shuttle, transfer. The optional polyline field accepts an encoded Google polyline string, an array of [lat, lng] tuples, or an array of { lat, lng } objects. When omitted, the analyzer samples a great-circle line between origin and destination.

    Request

    curl -s -X POST "https://api.travelmode.app/v1/weather/route" \
      -H "Authorization: Bearer tm_weather_..." \
      -H "Content-Type: application/json" \
      -d '{
        "origin":      { "lat": 48.8566, "lng": 2.3522 },
        "destination": { "lat": 49.2583, "lng": 4.0317 },
        "transport_mode": "car",
        "start_time": "2026-05-15T12:00:00.000Z",
        "units": "metric",
        "language": "en"
      }'
    

    Response (200 OK)

    {
      "data": {
        "origin":      { "lat": 48.8566, "lng": 2.3522 },
        "destination": { "lat": 49.2583, "lng": 4.0317 },
        "transport_mode": "car",
        "segments": [
          {
            "index": 0,
            "start_point": { "lat": 48.8566, "lng": 2.3522 },
            "end_point":   { "lat": 49.0571, "lng": 3.1920 },
            "start_time":  "2026-05-15T12:00:00.000Z",
            "end_time":    "2026-05-15T13:00:00.000Z",
            "distance_m": 67000, "duration_minutes": 60,
            "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
            "cache_status": "HIT",
            "condition": {
              "label": "clear", "temperature_c": 17.2,
              "wind_speed_kph": 16.0, "wind_gust_kph": 24.0,
              "precipitation_mm": 0.0, "visibility_km": 10.0
            },
            "risk": { "level": "low", "reasons": [], "recommendation": null }
          },
          {
            "index": 1,
            "start_point": { "lat": 49.0571, "lng": 3.1920 },
            "end_point":   { "lat": 49.2583, "lng": 4.0317 },
            "start_time":  "2026-05-15T13:00:00.000Z",
            "end_time":    "2026-05-15T14:00:00.000Z",
            "distance_m": 76000, "duration_minutes": 60,
            "snapshot_id": "8a8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e7a",
            "cache_status": "MISS",
            "condition": {
              "label": "heavy rain", "temperature_c": 14.8,
              "wind_speed_kph": 38.0, "wind_gust_kph": 60.0,
              "precipitation_mm": 4.5, "visibility_km": 4.0
            },
            "risk": {
              "level": "warning",
              "reasons": [
                { "code": "heavy_rain", "level": "warning", "message": "Heavy rain reduces visibility" }
              ],
              "recommendation": "Slow down and consider stopping if visibility drops further."
            }
          }
        ],
        "overall_risk": {
          "level": "warning",
          "reasons": [
            { "code": "heavy_rain", "level": "warning", "message": "Heavy rain reduces visibility" }
          ],
          "recommendation": "Slow down and consider stopping if visibility drops further.",
          "main_issue": "Heavy rain on the second leg of the drive"
        }
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T12:00:13.500Z",
        "environment": "production",
        "cache_status": "MIXED",
        "snapshot_id": "8a8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e7a",
        "billing_units": 6,
        "segments_analyzed": 2,
        "units": "metric",
        "language": "en"
      }
    }
    

    Validation errors

    CodeWhen
    route_invalidBody failed schema (bad coords, malformed polyline, unknown transport).

    Watches

    All three watch endpoints require the weather:watch scope.

    Create a watch

    POST /v1/weather/watch  ·  Bills: 1 unit + 1/day per active watch

    A watch monitors a place / timeline item / route window / trip. Whenever the upstream snapshot moves enough to cross one of the watch's change_rules, a weather.change_detected event is recorded and dispatched to the configured webhook URL. change_rules is optional — defaults from PRD §18.2 are merged in for any field you don't supply.

    Request

    curl -s -X POST "https://api.travelmode.app/v1/weather/watch" \
      -H "Authorization: Bearer tm_weather_..." \
      -H "Content-Type: application/json" \
      -d '{
        "target_type": "place",
        "target_ref": { "place_id": "city:paris" },
        "latitude":  48.8566,
        "longitude": 2.3522,
        "place_type": "city",
        "activity_type": "walking_tour",
        "outdoor": true,
        "starts_at": "2026-05-15T00:00:00.000Z",
        "ends_at":   "2026-05-22T00:00:00.000Z",
        "webhook_url": "https://example.com/hooks/weather"
      }'
    

    Response (201 Created)

    {
      "data": {
        "watch_id": "1f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6a",
        "status": "active",
        "target_type": "place",
        "target_ref": { "place_id": "city:paris" },
        "starts_at": "2026-05-15T00:00:00.000Z",
        "ends_at":   "2026-05-22T00:00:00.000Z",
        "change_rules": {
          "precipitation_probability_delta": 30,
          "temperature_c_delta": 5,
          "wind_kph_delta": 20,
          "uv_index_delta": 3,
          "detect_severe_alerts": true,
          "detect_risk_level_change": true
        },
        "activity_type": "walking_tour",
        "transport_mode": null,
        "outdoor": true,
        "webhook_url": "https://example.com/hooks/weather",
        "next_check_at": "2026-05-15T00:00:00.000Z",
        "created_at": "2026-04-25T18:01:13.500Z"
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:01:13.500Z",
        "environment": "production",
        "billing_units": 1
      }
    }
    

    Get a watch

    GET /v1/weather/watch/{watch_id}  ·  Bills: 1 unit

    Returns the watch hydrated with its latest snapshot, latest risk assessment, and the most recent change events. Returns 404 if the watch isn't owned by the calling developer account.

    Request

    curl -s "https://api.travelmode.app/v1/weather/watch/1f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6a" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": {
        "watch_id": "1f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6a",
        "status": "active",
        "target_type": "place",
        "target_ref": { "place_id": "city:paris" },
        "latitude": 48.8566,
        "longitude": 2.3522,
        "starts_at": "2026-05-15T00:00:00.000Z",
        "ends_at":   "2026-05-22T00:00:00.000Z",
        "change_rules": {
          "precipitation_probability_delta": 30,
          "temperature_c_delta": 5,
          "wind_kph_delta": 20,
          "uv_index_delta": 3,
          "detect_severe_alerts": true,
          "detect_risk_level_change": true
        },
        "activity_type": "walking_tour",
        "transport_mode": null,
        "outdoor": true,
        "webhook_url": "https://example.com/hooks/weather",
        "next_check_at": "2026-05-15T00:30:00.000Z",
        "last_checked_at": "2026-05-15T00:15:00.000Z",
        "last_risk_level": "low",
        "failure_count": 0,
        "created_at": "2026-04-25T18:01:13.500Z",
        "updated_at": "2026-05-15T00:15:00.000Z",
        "expired_at": null,
        "latest_snapshot": {
          "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
          "forecast_type": "current",
          "valid_from": "2026-05-15T00:00:00.000Z",
          "valid_to":   "2026-05-15T01:00:00.000Z",
          "fetched_at": "2026-05-15T00:15:00.000Z",
          "freshness_band": "current",
          "metrics": {
            "temperature_c": 14.2,
            "precipitation_probability": 25,
            "wind_speed_kph": 18.0
          },
          "alerts": []
        },
        "latest_risk": {
          "risk_assessment_id": "5e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
          "level": "low",
          "recommendation": null,
          "reasons": []
        },
        "recent_change_events": []
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-05-15T00:15:01.500Z",
        "environment": "production",
        "snapshot_id": "4e8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6f",
        "billing_units": 1
      }
    }
    

    Cancel a watch

    DELETE /v1/weather/watch/{watch_id}  ·  Bills: 1 unit

    Soft-cancels the watch (status='cancelled', expired_at=now). The refresh worker stops picking it up. Returns 404 if the watch isn't owned by the calling developer account.

    Request

    curl -s -X DELETE "https://api.travelmode.app/v1/weather/watch/1f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6a" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": {
        "watch_id": "1f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6a",
        "status": "cancelled",
        "expired_at": "2026-05-15T01:00:00.000Z"
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-05-15T01:00:00.500Z",
        "environment": "production",
        "billing_units": 1
      }
    }
    

    Webhooks

    All /v1/webhooks endpoints (and the attempts endpoint) require the weather:webhooks scope. See webhooks.md for signing details and event types.

    Create a webhook

    POST /v1/webhooks  ·  Bills: 1 unit

    The response includes a secret once (whsec_<32 hex chars>). Use it to verify the X-Travelmode-Signature header on every delivery. Subsequent reads only return the public secret_prefix.

    Request

    curl -s -X POST "https://api.travelmode.app/v1/webhooks" \
      -H "Authorization: Bearer tm_weather_..." \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://example.com/hooks/weather",
        "events": [
          "weather.change_detected",
          "weather.severe_alert_added",
          "weather.watch_expired",
          "weather.watch_failed",
          "weather.snapshot_refreshed",
          "weather.webhook_delivery_failed",
          "weather.webhook_endpoint_disabled"
        ],
        "description": "Production weather notifier"
      }'
    

    Response (201 Created)

    {
      "data": {
        "webhook_id": "9f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6b",
        "url": "https://example.com/hooks/weather",
        "events": [
          "weather.change_detected",
          "weather.severe_alert_added",
          "weather.watch_expired",
          "weather.watch_failed",
          "weather.snapshot_refreshed",
          "weather.webhook_delivery_failed",
          "weather.webhook_endpoint_disabled"
        ],
        "description": "Production weather notifier",
        "status": "active",
        "secret": "whsec_2c5b1f7e8b3d4f5a6c7d8e9f0a1b2c3d",
        "secret_prefix": "whsec_2c5b1f7e8b3d",
        "failure_count": 0,
        "last_delivery_at": null,
        "last_success_at": null,
        "last_failure_at": null,
        "disabled_at": null,
        "created_at": "2026-04-25T18:01:13.500Z",
        "updated_at": "2026-04-25T18:01:13.500Z"
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:01:13.500Z",
        "environment": "production",
        "billing_units": 1
      }
    }
    

    List webhook endpoints

    GET /v1/webhooks  ·  Bills: 0 units

    Returns every endpoint the calling developer owns, including disabled ones. Secrets are never returned — only the public secret_prefix.

    Request

    curl -s "https://api.travelmode.app/v1/webhooks" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": {
        "webhooks": [
          {
            "webhook_id": "9f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6b",
            "url": "https://example.com/hooks/weather",
            "events": [
              "weather.change_detected",
              "weather.severe_alert_added"
            ],
            "description": "Production weather notifier",
            "status": "active",
            "secret_prefix": "whsec_2c5b1f7e8b3d",
            "failure_count": 0,
            "last_delivery_at": "2026-04-25T17:55:13.000Z",
            "last_success_at":  "2026-04-25T17:55:13.000Z",
            "last_failure_at":  null,
            "disabled_at": null,
            "created_at": "2026-04-25T18:01:13.500Z",
            "updated_at": "2026-04-25T18:01:13.500Z"
          }
        ]
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:01:13.500Z",
        "environment": "production",
        "billing_units": 0
      }
    }
    

    Get one webhook endpoint

    GET /v1/webhooks/{webhook_id}  ·  Bills: 0 units

    Returns one endpoint owned by the calling developer (404 if not owned).

    Request

    curl -s "https://api.travelmode.app/v1/webhooks/9f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6b" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": {
        "webhook_id": "9f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6b",
        "url": "https://example.com/hooks/weather",
        "events": [
          "weather.change_detected",
          "weather.severe_alert_added"
        ],
        "description": "Production weather notifier",
        "status": "active",
        "secret_prefix": "whsec_2c5b1f7e8b3d",
        "failure_count": 0,
        "last_delivery_at": "2026-04-25T17:55:13.000Z",
        "last_success_at":  "2026-04-25T17:55:13.000Z",
        "last_failure_at":  null,
        "disabled_at": null,
        "created_at": "2026-04-25T18:01:13.500Z",
        "updated_at": "2026-04-25T18:01:13.500Z"
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:01:13.500Z",
        "environment": "production",
        "billing_units": 0
      }
    }
    

    Disable a webhook endpoint

    DELETE /v1/webhooks/{webhook_id}  ·  Bills: 1 unit

    Soft-disables the endpoint (status='disabled', disabled_at=now). The delivery worker stops targeting it; the row itself is retained so historical attempts stay queryable.

    Request

    curl -s -X DELETE "https://api.travelmode.app/v1/webhooks/9f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6b" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": {
        "webhook_id": "9f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6b",
        "status": "disabled",
        "disabled_at": "2026-04-25T18:30:00.000Z"
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:30:00.500Z",
        "environment": "production",
        "billing_units": 1
      }
    }
    

    List delivery attempts

    GET /v1/webhooks/{webhook_id}/attempts?limit=50  ·  Bills: 0 units

    Returns the most-recent delivery attempts for a webhook. Each attempt carries response status, response time, response body excerpt, the signature/timestamp pair we sent, and the schedule timeline. limit defaults to 50 and is capped at 200.

    Request

    curl -s "https://api.travelmode.app/v1/webhooks/9f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6b/attempts?limit=10" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": {
        "webhook_id": "9f8df5f0-8d9f-4a37-9c1a-9b2b3c4d5e6b",
        "attempts": [
          {
            "attempt_id": "aa00aa00-0000-4000-8000-000000000001",
            "event_id":   "ee00ee00-0000-4000-8000-000000000001",
            "event_type": "weather.change_detected",
            "attempt_number": 1,
            "status": "delivered",
            "response_status": 200,
            "response_time_ms": 142,
            "response_body_excerpt": "{\"ok\":true}",
            "error_message": null,
            "request_signature": "9d3e7c1a4b5f6e2d8c0a1b3e5d7f9c2a4b6e8d0f1a3c5e7b9d1f3a5c7e9b1d3f",
            "request_timestamp":  "2026-04-25T17:55:13.000Z",
            "scheduled_at":  "2026-04-25T17:55:12.000Z",
            "attempted_at":  "2026-04-25T17:55:13.000Z",
            "completed_at":  "2026-04-25T17:55:13.142Z",
            "next_retry_at": null
          },
          {
            "attempt_id": "aa00aa00-0000-4000-8000-000000000002",
            "event_id":   "ee00ee00-0000-4000-8000-000000000002",
            "event_type": "weather.severe_alert_added",
            "attempt_number": 2,
            "status": "retrying",
            "response_status": 503,
            "response_time_ms": 1240,
            "response_body_excerpt": "Service Unavailable",
            "error_message": "Upstream returned 503",
            "request_signature": "1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a3c5e7b9d1f3a",
            "request_timestamp":  "2026-04-25T17:50:13.000Z",
            "scheduled_at":  "2026-04-25T17:50:12.000Z",
            "attempted_at":  "2026-04-25T17:50:13.000Z",
            "completed_at":  "2026-04-25T17:50:14.240Z",
            "next_retry_at": "2026-04-25T17:55:14.240Z"
          }
        ]
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:00:00.500Z",
        "environment": "production",
        "billing_units": 0
      }
    }
    

    Get usage

    GET /v1/usage  ·  Scope: any valid key  ·  Bills: 0 units

    Returns the calling key's plan, environment, current-period bounds, plan limits, and current usage + remaining for every quota dimension. Calling this endpoint never burns request quota and is explicitly excluded from rate-limit enforcement.

    Request

    curl -s "https://api.travelmode.app/v1/usage" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": {
        "plan": "free",
        "environment": "production",
        "is_internal": false,
        "period": {
          "start": "2026-04-01T00:00:00.000Z",
          "end":   "2026-05-01T00:00:00.000Z"
        },
        "limits": {
          "requests_per_month": 1000,
          "timeline_items_per_month": 100,
          "active_watches": 0,
          "route_calls_per_month": 0
        },
        "usage": {
          "requests":       { "limit": 1000, "used": 42, "remaining": 958 },
          "timeline_items": { "limit":  100, "used":  6, "remaining":  94 },
          "route_calls":    { "limit":    0, "used":  0, "remaining":   0 },
          "active_watches": { "limit":    0, "used":  0, "remaining":   0 }
        }
      },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:01:13.500Z",
        "environment": "production",
        "billing_units": 0
      }
    }
    

    Health

    GET /v1/weather/health  ·  Scope: weather:read  ·  Bills: 0 units

    Tiny ping that proves the API surface is reachable and the caller's key + scope check passed. Doesn't depend on the upstream weather provider being warm — safe to call on any frequency.

    Request

    curl -s "https://api.travelmode.app/v1/weather/health" \
      -H "Authorization: Bearer tm_weather_..."
    

    Response (200 OK)

    {
      "data": { "status": "ok", "environment": "production" },
      "meta": {
        "request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
        "generated_at": "2026-04-25T18:01:13.500Z",
        "environment": "production",
        "billing_units": 0
      }
    }
    
    Previous
    ← Rate Limits
    Next
    Errors →