Configure Webhooks via API

Set up webhook targets and event filters programmatically using the Ordergroove Webhooks API
View as Markdown

Ordergroove can send webhook events to an endpoint or endpoints of your choosing. In order to set up which webhook events you want triggered to which endpoint, you’ll need to:

  1. Configure each target endpoint that you want events to be sent to
  2. Configure which events you want sent to the target endpoint
Legacy Method

We recommend setting up webhooks directly in the Ordergroove merchant dashboard under Developers > Webhooks. If you need to set up webhooks via API, please contact your Customer Success Manager or technical resource at Ordergroove.


Authentication

We recommend you set up webhooks directly in the Ordergroove merchant dashboard, under Developers > Webhooks. If you need to set webhooks up via API, please contact your Customer Success Manager or technical resource at Ordergroove to discuss authentication options.


Creating a Target Endpoint

Platform — Shopify

For merchants on Shopify, please make sure you create a new Target Endpoint and do not update or use an existing endpoint that is configured for Shopify Event Notifications.

Destination

  • Staging: https://staging.webhooks.ordergroove.com/webhook_targets/
  • Production: https://webhooks.ordergroove.com/webhook_targets/
1{
2 "merchant": "abc12345",
3 "target_url": "https://www.mywebhooktargeturl.com/receive/",
4 "enabled": true
5}

For more information on all targets that exist, how to update targets, or how to get target metrics, check out our Webhook Targets REST endpoint documentation for additional details.


Defining Events to Send to a Webhook Target

Once you’ve created a Webhook Target, you can define which events you want to be sent to that endpoint. The ID returned in the response of the Target Creation step is what will be used in this API request for the target_id in the path parameter.

Destination

  • Staging: https://staging.webhooks.ordergroove.com/webhook_targets/{target_id}/filters
  • Production: https://webhooks.ordergroove.com/webhook_targets/{target_id}/filters

To define multiple events, use a pipe separator as shown:

Example Request Body
1{"pattern": "subscription.*|order.*"}

Subscriber Event Filters

  • subscriber.create
  • subscriber.cancel
  • subscriber.* (for all events)

Subscription Event Filters

  • subscription.create (this event is not triggered when creation is from your Ordergroove Admin)
  • subscription.cancel
  • subscription.sku_swap
  • subscription.reactivate
  • subscription.change_frequency
  • subscription.change_components
  • subscription.change_quantity
  • subscription.change_shipping_address
  • subscription.change_payment
  • subscription.change_live
  • subscription.* (for all events)

Order Event Filters

  • order.change_shipping_address
  • order.change_payment
  • order.change_billing
  • order.change_next_order_date (this event is not triggered when creation is from your Ordergroove Admin)
  • order.skip_order
  • order.send_now
  • order.cancel
  • order.success
  • order.generic_error
  • order.reject
  • order.reminder
  • order.retryable_placement_failure
  • order.* (for all events)

Order Item Event Filters

  • item.create
  • item.change_quantity
  • item.remove
  • item.item_subscribe
  • item.update_price
  • item.successfully_placed
  • item.* (for all events)

Receiving Event Notifications

Payload Verification

Requests will be signed using the HMAC-SHA256 algorithm with a symmetric key we store and share with you at the time of configuration.

A webhook event will be delivered with the request header OrderGroove-Signature which will contain:

  • a timestamp of transmission
  • a hex-encoded representation of a signature generated with HMAC-SHA256
Example Signature
OrderGroove-Signature: ts=1592570791,sig=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

It is strongly encouraged to verify the authenticity of events. A Python example below shows how one might go about using the JSON values provided in the header to verify the signature.

1ts = "1592570791"
2payload = u''
3sig_key = "shared symmetric key"
4sig_input = "{ts}.{payload}".format(ts=ts, payload=payload)
5
6hash_obj = hmac.new(key=bytes(sig_key, 'ascii'), msg=bytes(sig_input, 'utf-8'), digestmod=hashlib.sha256)
7generated_sig = hash_obj.hexdigest() # "5257a869e7...."
8return generated_signature == header_signature

As shown above, via the sig_input variable, the signature is driven by:

  • the timestamp provided in the request header
  • a period (.)
  • the payload of the webhook event

Signing Key Retrieval

Signing keys are considered sensitive data and should only be retrieved when absolutely necessary.

  • Staging: https://staging.webhooks.ordergroove.com/webhook_targets/<target_id>/signing_key
  • Production: https://webhooks.ordergroove.com/webhook_targets/<target_id>/signing_key
Response
1{
2 "signing_key": "5f15ef298dc15df9bc6eda35"
3}

Signing Key Rotation

Like passwords, keys should be rotated on a regular basis. While we do not dictate this cadence (yet), you may rotate keys for a particular target on demand. Once this process is activated, events will be delivered with one signature for each active key.

Patch Request Destination:

  • Staging: https://staging.webhooks.ordergroove.com/webhook_targets/<target_id>/signing_key/rotate
  • Production: https://webhooks.ordergroove.com/webhook_targets/<target_id>/signing_key
Response
1{
2 "signing_key": "5f15ef298dc15df9bc6eda35",
3 "expiring_signing_key": "GBBYToAvYGBBYToAvYGBBYToAvY",
4 "signing_key_expiry": 1597260663
5}

The signing_key_expiry field will tell you when the expiring_signing_key will be discarded and no longer used. Consecutive requests to this endpoint will generate a new value in the signing_key, but the timer for the signing_key_expiry will not update. You will have 24 hours from the first time this endpoint is hit to make sure your applications are using the new key, whichever it may be. Any subsequent requests sent to the “Signing Key Retrieval” endpoint will only respond with the new key.

Key Rotation

If the rotation of a signing key occurred, there will be two sig fields present, providing one signature for each key.

Example Signature
sig=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,sig=da32affa62cdca35257a869e7e56ff536d0ce8e108d8bdcebefa51cad7e77a0e

Retries and Automatic URL Disabling

  • Retries: If a target does not return a 2xx HTTP response status, the delivery of the event will be retried for no more than 3 days using an exponential back-off for each subsequent retry attempt. After 3 days, the delivery will be marked as a failure. The first retry will be delayed for 60 seconds and will be subsequently doubled.
  • URL Disabling: After 3 days, if a target does not provide a 2xx HTTP response status, the URL will automatically be disabled. Any success will reset the disable logic. Re-enabling a disabled URL will also reset the disable logic.

Subscriber Events Response

NameTypeDescriptionExample
idstrIdentifier of the event”mmmm4444nnnn3333pppp”
typestrThe type of event that occurredSee below for full list of event types
createdintTime since epoch when the event occurred1622589211
datajsonEvent data objectSee JSON below
data.object.typestrResource type of event”Subscription”
data.object.merchantstrIdentifier of merchant”aaaa1111bbbb2222cccc”
data.merchant_user_idstrID of consumer from merchant system”dddjjjjkk3343”
data.object.subscriptionstrID of corresponding subscription that triggered the subscriber event”ssss3333llll2222bbbb”
data.object.session_idstrSession ID obtained from og_session_id cookie if present”aaaa1111bbbb2222cccc.450125.1299622365”
data.object.first_namestrCustomer first name”John”
data.object.last_namestrCustomer last name”Smith”
data.object.emailstrCustomer emailjohn.smith@email.com
data.object.phone_numberstrCustomer phone number”555-555-5555”
data.object.phone_typeintPhone type enum2
data.object.phone_type_displaystrReadable version of phone type enum. Options: “invalid”, “landline”, “mobile”, “voip""mobile”
data.object.liveboolCustomer status. true if they have any active subscription, false if none are activetrue
data.object.createdintUnix timestamp when the customer was created1298407706
data.object.last_updatedintUnix timestamp when the customer was last updated1301323663
data.object.extra_dataobjectKey/value store for any extra information about the customer{"some":"extra","fields":"here"}
data.object.localeintLocale enum. Options 1–461
data.object.locale_displaystrReadable version of locale enum”en-us”
Example Data
1{
2 "id": "mmmm4444nnnn3333pppp",
3 "type": "subscriber.create",
4 "created": 1622589211,
5 "data": {
6 "object": {
7 "type": "subscriber",
8 "merchant": "aaaa1111bbbb2222cccc",
9 "merchant_user_id": "dddjjjjkk3343",
10 "subscription": "ssss3333llll2222bbbb",
11 "session_id": "aaaa1111bbbb2222cccc.450125.1299622365",
12 "first_name": "John",
13 "last_name": "Smith",
14 "email": "john.smith@email.com",
15 "phone_number": "555-555-5555",
16 "phone_type": 2,
17 "phone_type_display": "mobile",
18 "live": true,
19 "created": 1298407706,
20 "last_updated": 1301323663,
21 "extra_data": {},
22 "locale": 1,
23 "locale_display": "en-us"
24 }
25 }
26}

Subscription Events Response

NameTypeDescriptionExample
idstrIdentifier of the event”mmmm4444nnnn3333pppp”
typestrThe type of event that occurredSee below for full list of event types
createdintTime since epoch when the event occurred1622589211
dataobjectEvent data objectSee JSON below
data.object.typestrResource type of event”Subscription”
data.object.merchantstrIdentifier of merchant”aaaa1111bbbb2222cccc”
data.object.public_idstrPublic ID of resource. Should be used to retrieve additional resource object data via OG API if necessary.”zzzz9999yyyy8888xxxx”
data.object.customerstrThe merchant_user_id — the ID from the merchant system, not our internal public ID”h183738”
data.object.productstrThe external product ID from the merchant’s system”SKUabc”
data.object.componentsarrayList of objects representing the bundle components/products[{"product": "product_1", "public_id": "...", "quantity": 1}]
data.object.prepaid_subscription_contextobjectPrepaid information returned only if prepaid is enabled{"prepaid_orders_remaining": 0, "prepaid_orders_per_billing": 3, "renewal_behavior": "autorenew", ...} or null
data.object.extra_dataobjectKey/value store for any extra information{"some": "extra", "fields": "here"}
data.object.quantityintNumber of items1
data.object.pricestrPrice of the subscription when leveraging price lock feature”12.99”
data.object.frequency_daysintOrder placement interval in days30
data.object.reminder_daysintDays before order placement to send email reminder (minimum of 5)5
data.object.everyintNumber of periods1
data.object.every_periodintType of period enum. Options: 1, 2, 33
data.object.every_period_displaystrReadable version of period enum. Options: “Days”, “Weeks”, “Months""Months”
data.object.cancelledintUnix timestamp of when the subscription was cancelled, if no longer active1301325188
data.object.cancel_reasonstrPipe-delimited cancel reason code and cancel reason details”4|Overstocked”
data.object.cancel_reason_codeintCode ID of the cancel reason4
data.object.session_idstrSession ID, obtained from og_session_id cookie”aaaa1111bbbb2222cccc.450125.1299622365”
data.object.merchant_order_idstrOrder ID in your system, corresponding to the checkout that created the subscription”12345678”
data.object.liveboolWhether the subscription is active. true means the customer wants to continue receiving the product; false means they do not.true
data.object.external_idstrExternal representation of the subscription in your system. For Shopify integrations, this is the Shopify contract ID.”external-identifier”
data.object.createdintUnix timestamp when the subscription was created1300375961
data.object.start_datestrDate the subscription started. Format: “YYYY-MM-DD""2021-03-25”
data.object.offerstrOffer record ID”a748aa648ac811e8af3bbc764e106cf4”
data.object.paymentstrPayment record ID”070001bc02fd11e99542bc764e1043b0”
data.object.shipping_addressstrShipping address record ID”66c25cd0564011e9abc5bc764e107990”
data.object.updatedintUnix timestamp when the subscription was updated1300375961
Example Data
1{
2 "id": "mmmm4444nnnn3333pppp",
3 "type": "subscription.create",
4 "created": 1622589211,
5 "data": {
6 "object": {
7 "type": "subscription",
8 "merchant": "aaaa1111bbbb2222cccc",
9 "public_id": "zzzz9999yyyy8888xxxx",
10 "customer": "m1234576",
11 "product": "SKUabc",
12 "components": [
13 {"product": "product_1"},
14 {"product": "product_2"}
15 ],
16 "extra_data": {},
17 "quantity": 1,
18 "price": null,
19 "frequency_days": 56,
20 "reminder_days": 10,
21 "every": 2,
22 "every_period": 2,
23 "every_period_display": "Weeks",
24 "cancelled": 1301325188,
25 "cancel_reason": "",
26 "cancel_reason_code": null,
27 "session_id": "aaaa1111bbbb2222cccc.450125.1299622365",
28 "merchant_order_id": "12345678",
29 "live": true,
30 "external_id": null,
31 "created": 1300375961,
32 "start_date": "2021-03-25",
33 "offer": "a748aa648ac811e8af3bbc764e106cf4",
34 "payment": "070001bc02fd11e99542bc764e1043b0",
35 "shipping_address": "66c25cd0564011e9abc5bc764e107990",
36 "updated": 1300375961
37 }
38 }
39}

Order Events Responses

NameTypeDescriptionExample
idstrIdentifier of the event”mmmm4444nnnn3333pppp”
typestrThe type of event that occurredSee below for full list of event types
createdintTime since epoch when the event occurred1622589211
dataobjectEvent data objectSee JSON below
data.object.typestrResource type of event”Order”
data.object.merchantstrIdentifier of merchant”aaaa1111bbbb2222cccc”
data.object.public_idstrPublic ID of resource. Should be used to retrieve additional resource object data via OG API if necessary.”zzzz9999yyyy8888xxxx”
data.object.customerstrThe merchant_user_id — the ID from the merchant system, not our internal public ID”h183738”
data.object.paymentstrPayment record ID”a748aa648ac811e8af3bbc764e106cf4”
data.object.shipping_addressstrShipping address record ID”66c25cd0564011e9abc5bc764e107990”
data.object.sub_totalstrSubtotal of items in order”20.00”
data.object.tax_totalstrTax applied to order”5.00”
data.object.shipping_totalstrShipping cost applied to order”0.00”
data.object.discount_totalstrDiscount applied to order”0.00”
data.object.totalstrTotal price of order”25.00”
data.object.createdintUnix timestamp when the order was created1300375961
data.object.placestrDate for order placement. Format: “YYYY-MM-DD""2021-05-01”
data.object.cancelledstrUnix timestamp when the order was cancelled1300375961
data.object.triesintNumber of attempts to place the order0
data.object.generic_error_countintNumber of times your system returned a generic error response (“999”) when we attempted to place the order0
data.object.statusintStatus enum1
data.object.status_displaystrReadable version of status enum”Unsent”
data.object.order_merchant_idstrOrder ID in your system after we have placed an order and you respond that it was processed successfully”ABC123”
data.object.rejected_messagestrMessage your system provides when you respond with “REJECT” to an order we attempt to place. Format depends on your responses — we store the string as provided.”Expiration Date is in the past”
data.object.extra_dataobjectKey/value store for any extra information
Example Data
1{
2 "id": "mmmm4444nnnn3333pppp",
3 "type": "order.success",
4 "created": 1622589211,
5 "data": {
6 "object": {
7 "type": "order",
8 "merchant": "aaaa1111bbbb2222cccc",
9 "public_id": "zzzz9999yyyy8888xxxx",
10 "customer": "m1234576",
11 "payment": "a748aa648ac811e8af3bbc764e106cf4",
12 "shipping_address": "66c25cd0564011e9abc5bc764e107990",
13 "discount_total": "0.00",
14 "shipping_total": "0.00",
15 "sub_total": "0.00",
16 "tax_total": "0.00",
17 "total": "0.00",
18 "extra_data": {},
19 "generic_error_count": 0,
20 "order_merchant_id": null,
21 "place": "2021-04-06",
22 "rejected_message": null,
23 "status": 1,
24 "status_display": "Unsent",
25 "tries": 0,
26 "cancelled": null,
27 "created": 1301093861
28 }
29 }
30}

Item Events Responses

NameTypeDescriptionExample
idstrIdentifier of the event”mmmm4444nnnn3333pppp”
typestrThe type of event that occurredSee below for full list of event types
createdintTime since epoch when the event occurred1622589211
datajsonEvent data objectSee JSON below
data.object.typestrResource type of event”Subscription”
data.object.merchantstrIdentifier of merchant”aaaa1111bbbb2222cccc”
data.object.public_idstrPublic ID of resource. Should be used to retrieve additional resource object data via OG API if necessary.”zzzz9999yyyy8888xxxx”
data.customerstrThe merchant_user_id — the ID from the merchant system, not our internal public ID”h183738”
data.productstrThe external product ID from the merchant’s system”SKUabc”
data.orderstrThe public ID from Ordergroove’s system of the order in which this item exists”1234e74444dd115555d8bc7cccc043b0”
data.subscriptionstrIf the item is part of a subscription, this will contain the public ID of that subscription in Ordergroove’s system”bc7cccc043b01234e74444155”
Example Data
1{
2 "id": "mmmm4444nnnn3333pppp",
3 "type": "item.create",
4 "created": 1622589211,
5 "data": {
6 "object": {
7 "type": "item",
8 "merchant": "aaaa1111bbbb2222cccc",
9 "public_id": "zzzz9999yyyy8888xxxx",
10 "customer": "m1234576",
11 "product": "SKUabc",
12 "order": "1234e74444dd115555d8bc7cccc043b0",
13 "subscription": "bc7cccc043b01234e74444155"
14 }
15 }
16}