Configure Webhooks via API

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 event to be sent to
  2. Configure which events you want sent to the target endpoint

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/

{
    "merchant": "abc12345",
    "target_url": "https://www.mywebhooktargeturl.com/receive/",
    "enabled": true
}
{
"id": "5f15ef298dc15df9bc6eda35"
"merchant": "abc12345",
"target_url": "https://www.mywebhooktargeturl.com/receive/",
"enabled": true,
"created": 1595273001,
"updated": 1597260663
}

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 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 param.

Destination:

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

Example Request Body:

To define multiple events, use a pipe separator as shown

{"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
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 provided in the header.

ts = "1592570791"
payload = u''
sig_key = "shared symmetric key"
sig_input = "{ts}.{payload}".format(ts=ts, payload=payload)

hash_obj = hmac.new(key=bytes(sig_key, 'ascii'), msg=bytes(sig_input, 'utf-8'), digestmod=hashlib.sha256)
generated_sig = hash_obj.hexdigest() # “5257a869e7....”
return generated_signature == header_signature

As shown above, by 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

{
  "signing_key": "5f15ef298dc15df9bc6eda35"
}

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

{
  "signing_key": "5f15ef298dc15df9bc6eda35",
  "expiring_signing_key": "GBBYToAvYGBBYToAvYGBBYToAvY",
  "signing_key_expiry": 1597260663
}

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.

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

Name Type Description Example
id str Identifier of the event “mmmm4444nnnn3333pppp”
type str The type of event that occurred See below for full list of event types
created int Time since epoch when the event occurred 1622589211
data json Type see below json bock
data.object.type str Resource type of event "Subscription"
data.object.merchant str Identifier of merchant "aaaa1111bbbb2222cccc"
data.merchant_user_id str ID of consumer from merchant system. "dddjjjjkk3343"
data.object.subscription str ID of corresponding subscription that triggered the subscriber event "ssss3333llll2222bbbb"
data.object.session_id str Session ID obtained from og_session_id cookie if present "aaaa1111bbbb2222cccc.450125.1299622365"
data.object.first_name str Customer first name "John"
data.object.last_name str Customer last name "Smith"
data.object.email str Customer email "[email protected]"
data.object.phone_number str Customer phone number "555-555-5555"
data.object.phone_type int Phone type enum 2
data.object.phone_type_display str

Readable version of phone type enum

Options: 

"invalid",

"landline",

"mobile",

"voip"

"mobile"
data.object.live bool

Customer status. "True" if they have any subscription active, false if no subscriptions are active

true
data.object.created int

Unix timestamp when the customer was created

1298407706
data.object.last_updated int

Unix timestamp when the customer was last updated

1301323663
data.object.extra_data object

Key/value store for any extra information about the customer

{"some":"extra","fields":"here"}
data.object.locale int

Locale enum

Options 1-46

1
data.object.locale_display str

Readable version of locale enum

"en-us"
{
  "id": "mmmm4444nnnn3333pppp",
  "type": "subscriber.create",
  "created": 1622589211,
  "data": {
    "object": {
      "type": "subscriber",
      "merchant": "aaaa1111bbbb2222cccc",
      "merchant_user_id": "dddjjjjkk3343",
      "subscription": "ssss3333llll2222bbbb",
      "session_id": "aaaa1111bbbb2222cccc.450125.1299622365",
      "first_name": "John",
      "last_name": "Smith",
      "email": "[email protected]",
      "phone_number": "555-555-5555",
      "phone_type": 2,
      "phone_type_display": "mobile",
      "live": true,
      "created": 1298407706,
      "last_updated": 1301323663,
      "extra_data": {},
      "locale": 1,
      "locale_display": "en-us",
    }
  }
}

Subscription Events Response

Name Type Description Example
id str Identifier of the event “mmmm4444nnnn3333pppp”
type str The type of event that occurred See below for full list of event types
created int Time since epoch when the event occurred 1622589211
data object Type See JSON below
data.object.type str Resource type of event "Subscription"
data.object.merchant str Identifier of merchant "aaaa1111bbbb2222cccc"
data.object.public_id str Public ID of resource. Should be used to retrieve additional resource object data via OG API if necessary. "zzzz9999yyyy8888xxxx
data.object.customer str The merchant_user_id AKA the id from merchant system - not our internal public ID "h183738"
data.object.product str The external product ID from the merchant's system "SKUabc"
data.object.components array List of objects representing the bundle components/products [
 {
 "product": "product_1",
 "public_id": "d4f6d3e4fdd311eebebd06c0a416f30c",
 "quantity": 1
 }
]
data.object.extra_data object Key/value store for any extra information {"some": "extra", "fields": "here"}
data.object.quantity int Number of items 1
data.object.price str Price of the subscription when leveraging price lock feature "12.99"
data.object.frequency_days int Order placement interval in days 30
data.object.reminder_days int Days before order placement to email reminder (minimum of 5) 5
data.object.every int Number of periods 1
data.object.every_period int Type of period enum

Options: 1, 2, 3
3
data.object.every_period_display str Readable version of period enum

Options: "Days", "Weeks", "Months"
"Months"
data.object.cancelled int Unix timestamp of when the subscription was cancelled, if no longer active. 1301325188
data.object.cancel_reason str Pipe-delimited cancel reason code and cancel reason details "4|Overstocked"
data.object.cancel_reason_code int Code ID of the cancel reason 4
data.object.session_id str Session ID, obtained from og_session_id cookie "aaaa1111bbbb2222cccc.450125.1299622365"
data.object.merchant_order_id str Order ID in your system, corresponding to the checkout that created the subscription. "12345678"
data.object.live bool Dictates if the subscription is active or inactive. "Active" meaning that the customer wants to continue receiving the product at the frequency cadence; "Inactive' meaning they do not. true
data.object.external_id str External representation of the subscription in your system. In the case of Shopify integrations, this is the Shopify contract ID. "external-identifier"
data.object.created int Unix timestamp when the subscription was created 1300375961
data.object.start_date str Date the subscription started.

Format: "YYYY-MM-DD"
"2021-03-25"
data.object.offer str Offer record ID "a748aa648ac811e8af3bbc764e106cf4"
data.object.payment str Payment record ID "070001bc02fd11e99542bc764e1043b0"
data.object.shipping_address str Shipping address record ID "66c25cd0564011e9abc5bc764e107990"
data.object.updated int Unix timestamp when the subscription was updated 1300375961
{
  "id": "mmmm4444nnnn3333pppp",
  "type": "subscription.create",
  "created": 1622589211,
  "data": {
    "object": {
      "type": "subscription",
      "merchant": "aaaa1111bbbb2222cccc",
      "public_id": "zzzz9999yyyy8888xxxx",
      "customer": "m1234576",
      "product": "SKUabc",
      "components": [
        {"product": "product_1"}, 
        {"product": "product_2"}
      ],
      "extra_data": {},
      "quantity": 1,
      "price": null,
      "frequency_days": 56,
      "reminder_days": 10,
      "every": 2,
      "every_period": 2,
      "every_period_display": "Weeks",
      "cancelled": 1301325188,
      "cancel_reason": "",
      "cancel_reason_code": null,
      "session_id": "aaaa1111bbbb2222cccc.450125.1299622365",
      "merchant_order_id": "12345678",
      "live": true,
      "external_id": null,
      "created": 1300375961,
      "start_date": "2021-03-25",
      "offer": "a748aa648ac811e8af3bbc764e106cf4",
      "payment": "070001bc02fd11e99542bc764e1043b0",
      "shipping_address": "66c25cd0564011e9abc5bc764e107990",
      "updated": 1300375961,
    }
  }
}

Order Events Responses

Name Type Description Example
id str Identifier of the event “mmmm4444nnnn3333pppp”
type str The type of event that occurred See below for full list of event types
created int Time since epoch when the event occurred 1622589211
data object Type See JSON below
data.object.type str Resource type of event "Order"
data.object.merchant str Identifier of merchant "aaaa1111bbbb2222cccc"
data.object.public_id str Public ID of resource. Should be used to retrieve additional resource object data via OG API if necessary. "zzzz9999yyyy8888xxxx
data.object.customer str The merchant_user_id AKA the id from merchant system - not our internal public ID "h183738"
data.object.payment str Payment record ID "a748aa648ac811e8af3bbc764e106cf4"
data.object.shipping_address str Shipping address record ID "66c25cd0564011e9abc5bc764e107990"
data.object.sub_total str Subtotal of items in order "20.00"
data.object.tax_total str Tax applied to order "5.00"
data.object.shipping_total str Shipping cost applied to order "0.00"
data.object.discount_total str Discount applied to order "0.00"
data.object.total str Total price of order "25.00"
data.object.created int Unix timestamp when the order was created 1300375961
data.object.place str Date for order placement

Format: "YYYY-MM-DD"
"2021-05-01"
data.object.cancelled str Unix timestamp when the order was cancelled 1300375961
data.object.tries int Number of attempts to place the order 0
data.object.generic_error_count int Number of times your system returned a generic error response when we attempted to place the order ("999") 0
data.object.status int Status enum 1
data.object.status_display str Readable version of status enum "Unsent"
data.object.order_merchant_id str Order ID in your system after we have placed an order in your system and you respond that it was processed successfully. "ABC123"
data.object.rejected_message str Message your system provides when you respond with "REJECT" to an order we attempt to place.

Format/structure depends on what your responses. We store the string of what you provide.
"Expiration Date is in the past"
data.object.extra_data object Key/value store for any extra information  
{
  "id": "mmmm4444nnnn3333pppp",
  "type": "order.success",
  "created": 1622589211,
  "data": {
    "object": {
      "type": "order",
      "merchant": "aaaa1111bbbb2222cccc",
      "public_id": "zzzz9999yyyy8888xxxx",
      "customer": "m1234576",
      "payment": "a748aa648ac811e8af3bbc764e106cf4",
      "shipping_address": "66c25cd0564011e9abc5bc764e107990",
      "discount_total": "0.00",
      "shipping_total": "0.00",
      "sub_total": "0.00",
      "tax_total": "0.00",
      "total": "0.00",
      "extra_data": {},
      "generic_error_count": 0,
      "order_merchant_id": null,
      "place": "2021-04-06",
      "rejected_message": null,
      "status": 1,
      "status_display": "Unsent",
      "tries": 0,
      "cancelled": null,
      "created": 1301093861
    }
  }
}

Item Events Responses

Name Type Description Example
id str Identifier of the event “mmmm4444nnnn3333pppp”
type str The type of event that occurred See below for full list of event types
created int Time since epoch when the event occurred 1622589211
data json Type see below json bock
data.object.type str Resource type of event "Subscription"
data.object.merchant str Identifier of merchant "aaaa1111bbbb2222cccc"
data.object.public_id str Public ID of resource. Should be used to retrieve additional resource object data via OG API if necessary. "zzzz9999yyyy8888xxxx
data.customer str The merchant_user_id AKA the id from merchant system - not our internal public ID "h183738"
data.product str The external product ID from the merchant's system "SKUabc"
data.order str The public ID from Ordergroove's system of the order in which this item exists. "1234e74444dd115555d8bc7cccc043b0"
data.subscription str If item is part of a subscription this will contain the public ID of that subscription in Ordergroove's system "bc7cccc043b01234e74444155"
{
    "id": "mmmm4444nnnn3333pppp",
    "type": "item.create",
    "created": 1622589211,
    "data": {
        "object": {
            "type": "item",
            "merchant": "aaaa1111bbbb2222cccc",
            "public_id": "zzzz9999yyyy8888xxxx",
            "customer": "m1234576",
            "product": "SKUabc",
            "order": "1234e74444dd115555d8bc7cccc043b0",
            "subscription": "bc7cccc043b01234e74444155"
        }
    }
}