Webhooks

Webhooks are notifications about events in our system that are sent to a configured HTTPS endpoint. This section describes all available webhook events and the data contained in them.

Integrating an IHD webhook into your solution will help you receive delivery events close to realtime and avoid polling to refresh the data.

Request verification

To verify that an incoming webhook request was not maliciously modified we include a digital signature as a part of the request headers. The signature must be validated to ensure no fraudulent event notification has occurred.

The IHD-Signature header is included in each signed event and contains a timestamp and one or more signatures. The timestamp is prefixed by t=, and each signature is "s", followed by an integer. Currently, the only valid live signature scheme is s1

X-Signature:
t=1492774577,
s1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

IHD generates signatures using a hash-based message authentication code (HMAC) with SHA-256.

To verify webhook event signatures, you can use the following steps.

Step 1 Extraction

Extract the timestamp and signatures from the header
Split the IHD-Signature header, using the , character as the separator, to get a list of elements. Then split each element, using the = character as the separator, to get a prefix and value pair.

The value for the prefix t corresponds to the timestamp, and s1 corresponds to the signature. You can discard all other elements.

Step 2 Prepare the signed_payload string

The signed_payload string is created by concatenating:

The timestamp (as a string)
The character .
The actual JSON payload (that is, the request body)

Step 3 Determine the expected signature

Compute an HMAC with the SHA256 hash function. Use your CLIENT SECRET as the key, and use the signed_payload string as the message.

Step 4: Compare the signatures

Compare the signature (or signatures) in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.

To protect against timing attacks, use a constant-time string comparison to compare the expected signature to each of the received signatures.

Retries on failure

Once IHD webhook payload is posted to the target URL, any HTTP response code other than 2XX is considered a failure. In this case IHD will re-try to trigger the webhook notification 4 more times based on these intervals: 10 secs, 1 min, 5 mins, 10 mins.

Upon failure of the last try, IHD will send an email to the partner notifying them of the failure. Notification emails are sent once every hour in order to avoid sending too many failure emails.

Webhook notifications

The webhook payloads include 2 main properties:

  • event_id: Unique id for the webhook trigger
  • event_time: Unix timestamp for the webhook trigger
  • event: Event name for the webhook trigger
  • data: Data object that relates to triggered event

Delivery notifications

Triggered when an order transitions into another status (e.g. accepted -> pickup). The following are the delivery events:

delivery.new
delivery.update
delivery.rejected
delivery.canceled
delivery.delivered
delivery.returned

delivery.delayed.pickup
delivery.delayed.dropoff

delivery.refund.requested
delivery.refund.approved
delivery.refund.declined

billing.invoice.charged
billing.invoice.failed

Here's a sample response that you can expect. In this instance the order status has transitioned into delivered status.

{
  "event_id": "<guid>",
  "event_time": 1645329838,
  "event": "delivery.delivered",
  "data": {
    "delivery": {
      "id": "1232ea9c-e2a7-4a16-9327-977314913d4f",
      "service_provider": "postmates",
      "order_id": "del_224nBy6TS2fIGoa_8SNAQ",
      "order_reference": "1",
      "order_type": "delivery",
      "status": "delivered",
      "quote": null,
      "pickup": {
        "name": "Bob's Pizza",
        "phone_number": "15552853000",
        "address": "165 Jefferson st, San Francisco, CA, USA",
        "notes": "Tell our staff that you are here to pick up an RTO Order",
        "location": {
          "lat": 37.8081966,
          "lng": -122.41522974
        },
        "detailed_address": {
          "city": "San Francisco",
          "country": "US",
          "state": "CA",
          "street_address_1": "165 Jefferson St",
          "street_address_2": "",
          "sublocality_level_1": "",
          "zip_code": "94133"
        },
        "verification": {
          "picture": {
            "image_url": ""
          }
        }
      },
      "dropoff": {
        "name": "Terry G.",
        "phone_number": "15558125337",
        "address": "11 Benton Avenue, San Francisco, CA, USA",
        "notes": "",
        "location": {
          "lat": 37.74254,
          "lng": -122.43656
        },
        "detailed_address": {
          "city": "SF",
          "country": "US",
          "state": "",
          "street_address_1": "11 Benton Avenue",
          "street_address_2": "",
          "sublocality_level_1": "",
          "zip_code": "94112"
        },
        "verification": {
          "picture": {
            "image_url": ""
          }
        }
      },
      "order_items": [
        {
          "name": "Mushroom pizza",
          "quantity": 1,
          "price": 2764
        }
      ],
      "distance": 2176,
      "created_at": "2022-02-15 03:17:01",
      "updated_at": "2022-02-15 04:55:52",
      "pickup_at_ready": "2022-02-15 04:01:58",
      "pickup_at_eta": "2022-02-15 04:48:13",
      "live_pickup_at_eta": "2022-02-15 04:48:13",
      "pickup_at": "2022-02-15 04:48:14",
      "dropoff_at_eta": "2022-02-15 04:55:49",
      "live_dropoff_at_eta": "2022-02-15 04:55:49",
      "dropoff_at": "2022-02-15 04:55:49",
      "customer_fee": 0,
      "fee": 850,
      "tip": 0,
      "total": 0,
      "currency": "usd",
      "tax": 0,
      "tracking_url": "https://www.ubereats.com/orders/12eef72c-10ba-4d2d-9f20-aa1affc48d01",
      "courier": {
        "name": "Junjie",
        "rating": "4.95",
        "vehicle_type": "car",
        "phone_number": "+11283337624",
        "location": {
          "lat": 0,
          "lng": 0
        },
        "img_href": ""
      },
      "location": {
        "id": "167a94e0-010c-4419-8303-2210523c4201"
      },
      "requires_id": false,
      "requires_signature": false,
      "refund": null,
      "manager_note": null,
      "tags": null,
      "attributes": {
        "refundable": true
      }
    }
  }
}
"refund": {
        "status": "rejected",
        "request_at": "2021-04-06 00:48:02",
        "refund_note": "Refund Note",
        "resolved_at": "2021-04-06 00:48:16",
        "requester_id": "052b4d7c-0585-4a5a-a47e-cec6c0498648",
        "refund_amount": 120,
        "reject_reason": "N/A",
        "requester_name": "Support Account"
      },