Docs
Get an API key

Create your first price watch

Create your first hotel price watch and safely process its webhook event.

In short

7 calls, in the order this page makes themFor people and agents
  1. 1GET/v1/watcher/whoamiWho am I acting as
  2. 2POST/v1/watcher/webhooksRegister the webhook endpoint
  3. 3POST/v1/watcher/travel/hotel/watchesCreate a hotel watch
  4. 4GET/v1/watcher/eventsEvent log
  5. 5GET/v1/watcher/travel/hotel/watchesList hotel watches
  6. 6POST/v1/watcher/travel/hotel/watches/{watch_id}/rearmRearm a hotel watch
  7. 7DELETE/v1/watcher/travel/hotel/watches/{watch_id}Delete a hotel watch

The same list heads this page's Markdown:/watcher/getting-started-watches.md

This tutorial takes one request — “Tell me when this Tokyo hotel costs 450 USD or less” — from API setup to a safely handled price-drop event.

What you will build

You will:

  1. Confirm the user identity attached to your API key.
  2. Register an endpoint for Watcher events.
  3. Create a price watch for one hotel and stay.
  4. Handle the signed event when the total reaches the user's threshold.
  5. Recover events if your webhook receiver misses a delivery.

Before you begin

You need:

  • An OEX API key that acts for the end user creating the watch.
  • A public HTTP endpoint such as https://your.service/hooks/oex.
  • A secure secret store for the webhook signing secret.

Every request in this tutorial goes through the Watcher gateway. Set your API key once:

export OEX_API_KEY="<your API key>"

1. Verify your API identity

Confirm which organisation and user the gateway will attach to your requests:

Request
curl "https://api.oex.sh/v1/watcher/whoami" \
  -H "x-api-key: $OEX_API_KEY"
Response
{
  "data": {
    "org_id": "org_demo",
    "user_id": "usr_maya"
  }
}

Store watches under the intended end user's credential. A user can list and manage only their own watches.

2. Register your webhook endpoint

Register the receiver before creating a watch. This prevents you from missing an event if the current price already qualifies.

Request
curl -X POST "https://api.oex.sh/v1/watcher/webhooks" \
  -H "content-type: application/json" \
  -H "x-api-key: $OEX_API_KEY" \
  -d '{
    "url": "https://your.service/hooks/oex"
  }'
Response
{
  "data": {
    "org_id": "org_demo",
    "url": "https://your.service/hooks/oex",
    "events": [
      "travel.hotel.watch.triggered",
      "travel.hotel.trip_watch.triggered",
      "travel.hotel.watch.purchased",
      "travel.hotel.watch.purchase_failed"
    ],
    "secret": "whsec_..."
  }
}

Save secret now. Watcher never returns it again. Registering another endpoint replaces the current URL and rotates the secret.

The registration belongs to the organisation, not an individual user. The optional events field lets you subscribe to a subset; omitting it subscribes the endpoint to all four event types.

3. Create a hotel watch

Create a watch for two adults staying from 10 to 13 September 2026. The threshold is the total for the full stay.

Request
curl -X POST "https://api.oex.sh/v1/watcher/travel/hotel/watches" \
  -H "content-type: application/json" \
  -H "x-api-key: $OEX_API_KEY" \
  -d '{
    "hotel_id": "hb_123456",
    "check_in": "2026-09-10",
    "check_out": "2026-09-13",
    "occupancy": [
      {"adults": 2}
    ],
    "currency": "USD",
    "guest_nationality": "US",
    "trigger": {
      "type": "below_total",
      "amount": 450
    }
  }'

Watcher prices the stay once while creating it:

Response
{
  "data": {
    "id": "wtc_01hq3example",
    "status": "active",
    "mode": "notify",
    "hotel_id": "hb_123456",
    "check_in": "2026-09-10",
    "check_out": "2026-09-13",
    "occupancy": [
      {"adults": 2}
    ],
    "currency": "USD",
    "guest_nationality": "US",
    "trigger": {
      "type": "below_total",
      "amount": 450
    },
    "mandate_id": null,
    "payment_instrument_id": null,
    "booking_id": null,
    "purchase_failure": null,
    "last_total": 512.4,
    "last_checked_at": "2026-08-25T09:15:00+00:00",
    "triggered_at": null,
    "notified_total": null,
    "created_at": "2026-08-25T09:15:00+00:00",
    "current_total": 512.4
  }
}

Save the wtc_ ID. You will use it to retrieve, rearm, or delete this watch.

The request must use future dates, a 3-letter currency code, a 2-letter guest nationality, and 1–4 rooms. Each room can contain 1–8 adults.

4. Interpret the current price

Use current_total to give the user immediate feedback:

  • Above 450: The watch is ready. Watcher will continue checking.
  • At or below 450: Tell the user the price already meets their goal and offer the normal booking flow. The active watch will still trigger on its next check.
  • null: Watcher could not get a current price. The watch is still active and will try again on its scheduled checks.

Notification prices are advisory. Do not save them as bookable offers; fetch a fresh offer when the user decides to book.

5. Receive the price-drop event

When the hotel total reaches 450 USD or less, Watcher sends a JSON POST to your registered URL:

{
  "id": "evt_01hq4example",
  "type": "travel.hotel.watch.triggered",
  "created_at": "2026-08-28T10:20:00+00:00",
  "data": {
    "watch": {
      "id": "wtc_01hq3example",
      "status": "triggered",
      "mode": "notify",
      "hotel_id": "hb_123456",
      "check_in": "2026-09-10",
      "check_out": "2026-09-13",
      "occupancy": [
        {"adults": 2}
      ],
      "currency": "USD",
      "guest_nationality": "US",
      "trigger": {
        "type": "below_total",
        "amount": 450
      },
      "mandate_id": null,
      "payment_instrument_id": null,
      "booking_id": null,
      "purchase_failure": null,
      "last_total": 438.7,
      "last_checked_at": "2026-08-28T10:20:00+00:00",
      "triggered_at": "2026-08-28T10:20:00+00:00",
      "notified_total": 438.7,
      "created_at": "2026-08-25T09:15:00+00:00"
    },
    "user_id": "usr_maya",
    "current": {
      "total": 438.7,
      "currency": "USD",
      "room_type": "Deluxe King",
      "board": "Room only",
      "refundable": true
    }
  }
}

For every delivery:

  1. Verify oex-signature against the raw request body.
  2. Reject stale requests and invalid signatures.
  3. Deduplicate by the top-level event id.
  4. Return a 2xx response within 10 seconds.
  5. Notify the user or do other slow work asynchronously.

Follow Handle webhooks reliably for signature verification, retries, and a production checklist.

6. Reconcile missed events

Webhook delivery is at-least-once, but your receiver may still be unavailable beyond the retry window. Read the organisation's event log as a safety net:

Request
curl "https://api.oex.sh/v1/watcher/events?after=evt_01hq4example&limit=50" \
  -H "x-api-key: $OEX_API_KEY"

The response is ordered from oldest to newest. Save the last event ID you processed and pass it as after on the next request. Process the event in each entry's payload using the same deduplication path as webhook deliveries.

For your first reconciliation request, omit after. A supplied event ID must belong to your organisation.

7. Manage the watch

List the current user's hotel watches:

Request
curl "https://api.oex.sh/v1/watcher/travel/hotel/watches" \
  -H "x-api-key: $OEX_API_KEY"

Rearm a triggered watch with the same threshold:

Request
curl -X POST "https://api.oex.sh/v1/watcher/travel/hotel/watches/wtc_01hq3example/rearm" \
  -H "content-type: application/json" \
  -H "x-api-key: $OEX_API_KEY" \
  -d '{}'

To change the threshold while rearming, send:

Response
{
  "trigger": {
    "type": "below_total",
    "amount": 425
  }
}

Delete a watch the user no longer needs:

Request
curl -X DELETE "https://api.oex.sh/v1/watcher/travel/hotel/watches/wtc_01hq3example" \
  -H "x-api-key: $OEX_API_KEY"

Next steps

On this page