Hooks API
Hooks API
Hooks allow you to extend the functionality of Bubblehouse with your own code.
Using the API settings page, you should specify a hook URL (we’ll be calling a single URL for all hooks, replacing :name with an actual hook name).
At the very least, you need to implement the Hello1 hook. We call it whenever you save the API settings page and once per day while hooks are enabled. It returns the list of other hooks that you want to subscribe to.
Hook requests
A hook request is a POST to the URL you have provided, with :name in the URL replaced with the actual hook name like Hello1.
Request headers:
Content-Type: application/jsonAccept: application/jsonX-Request-ID: <request identifier>X-BH-Urgency: real-time(orurgent, orregular)
Request body is the input JSON object for the hook.
The response must use a 2xx status and include a valid output JSON object. A bodyless 204 response is rejected. We don’t care about the Content-Type or other headers you set.
Every HTTP attempt gets a new X-Request-ID, including repeated attempts for the same event or reward. Do not use this header as a durable operation idempotency key. See Time limits and retries for each hook’s retry behavior.
We recommend that you use a web server that supports HTTP 2 to speed up high-volume hook processing.
We may send unexpected Hello hooks
Your code _MUST_ be prepared to receive calls of unexpected hooks, at least in the Hello family, and _MUST_ return a 404 error code for those. We won't actually be sending random hooks, but if we ever introduce `Hello2`, we think we will try it first before invoking `Hello1`.Real-time, urgent and regular calls
There are 3 levels of urgency that hook calls may have:
Real-time calls need to be processed immediately, and we cannot proceed until we get output from them.
Public real-time hooks include:
Hello1when invoked from the API settings page,LoadCart1when the first-party widget loads a cart from your storefront,ApplyCartChange1when the first-party widget applies loyalty changes to that cart,ApplySubscriptionDiscount1when a customer applies points to a subscription,AddProductToSubscription1when Bubblehouse adds a reward product to a subscription,- there’s a handful of private hooks that are real-time, particularly when dealing with custom promo objects;
- there are certain configurations where CreateDiscount hook is also invoked in real-time mode.
Urgent hooks need to be processed as soon as possible, but the user flow continues while waiting on these.
An example is redeeming points; the customer is waiting for their coupon code to be available, but we pre-generate the code string, and we expect the CreateDiscount hook to complete while the customer is copying the code into your checkout page.
Regular hooks are simply notified about events at Bubblehouse, and can take their time to complete.
If you’re putting hooks into a job queue, we recommend that you run real-time hooks right away without queueing, and arrange for urgent hooks to take priority.
You can detect urgency level by checking X-BH-Urgency. Please be sure to follow the HTTP standard and use case-insensitive comparisons when looking for an HTTP header.
Time limits and retries
The HTTP client stops waiting after 240 seconds. Real-time hooks block a customer or processing flow, so respond within 10 seconds whenever possible.
Bubblehouse treats a timeout, network error, non-2xx status, malformed JSON, or invalid result as a failed attempt. Retry behavior differs by hook:
Hello1: Bubblehouse calls this hook when someone saves the API settings and once per day while hooks are enabled. Each call is one attempt; we do not retry it immediately after a failure. The next daily call or another settings save starts a new attempt.LoadCart1andApplyCartChange1: During a widget or checkout flow, Bubblehouse makes one attempt.CreateDiscount4: During an interactive redemption, Bubblehouse makes one attempt. Achievement coupon creation retries a failed call once immediately, then up to 20 more times with delays that grow from 1 second to 8 hours. Other scheduled rewards can request coupon creation again later if Bubblehouse still has no coupon recorded. A repeated call can have a differentcodeand always has a differentX-Request-ID; this hook provides no universal retry identifier.DeactivateDiscount4: Bubblehouse retries a failed call once after 5 seconds, then up to 20 more times with delays that grow from 1 minute to 8 hours. Repeated calls carry the samecodeandcode_id, but a newX-Request-ID.SendEvent1:sent,skipped,invalid_data, andunsubscribedare terminal and are not retried.failure, a request failure, or an invalid response leaves the event pending; Bubblehouse tries it again when outgoing event delivery runs again, with no fixed retry time. Repeated calls for the same event keep the samebhidand get a newX-Request-ID.ApplySubscriptionDiscount1: During a customer-initiated redemption, Bubblehouse makes one attempt. A timeout, network error, non-2xx response, or invalid result shows the customer a generic redemption failure message.applied:falseshows a failure message selected fromfailure_reason, or a generic message when the reason is empty, and does not consume points. The customer can try again. When automatic point application is enabled, no customer is waiting and no failure message is displayed; while the customer still has points, Bubblehouse tries again on a later automatic pass, normally 10 minutes later.AddProductToSubscription1: During customer redemption, Bubblehouse makes one attempt. A failed call oradded:falseis not retried. If the reward permits fallback, Bubblehouse gives the fallback reward and does not show the hook failure to the customer. If the reward requires subscription delivery, Bubblehouse rejects the redemption and shows the customer “This reward requires an active subscription.” During scheduled subscription reward delivery, no customer is waiting and no failure message is displayed. A validadded:false,retry:trueresult retries once after 5 seconds, then up to 20 more times with delays that grow from 1 minute to 8 hours.added:truesucceeds;added:false,retry:false, timeouts, network errors, non-2xx responses, and invalid results record a terminal delivery failure.
Authentication
We allow you to configure a value for an Authorization: <whatever> header. This allows you to present a bearer token, basic credentials, or something else of your choice.
The webhook URL must use HTTPS. We recommend ngrok for local testing. We do not further sign webhooks Amazon-style, which would be pretty pointless; HTTPS and token authentication is enough for normal API calls, and thus must also be enough for webhooks.
Examples
Here’s a very simple example of a Hooks API server using Express.js:
const express = require('express')
const app = express()
const port = 4000
app.use(express.json())
app.post('/Hello1', (req, res) => {
res.json({
magic: req.body.magic,
hooks: [
'CreateDiscount4',
'DeactivateDiscount4',
],
})
})
app.post('/CreateDiscount4', (req, res) => {
console.log(`TODO: create coupon code ${req.body.code} as ${JSON.stringify(req.body, null, 2)}`)
res.json({ ok: true })
})
app.post('/DeactivateDiscount4', (req, res) => {
console.log(`TODO: deactivate coupon code ${req.body.code || req.body.code_id}`)
res.json({ ok: true })
})
app.listen(port, () => {
console.log(`Hook server listening on port ${port}`)
})
Note that Express automatically handles 404 errors for us. Please ensure that your endpoint returns 404 errors for unknown hooks just as well.