Understanding webhook events
Every webhook Bloop sends is a POST with a JSON body called the envelope. The envelope says which event happened, and data holds the fields of that event. This page is generated from the same contract Bloop checks every event against before sending it. How to verify and acknowledge a delivery is in How to receive webhooks.
Payload versions: v1.
Envelope
Section titled “Envelope”Body Bloop POSTs to a merchant webhook endpoint. data follows the schema of type. Consumers must ignore unknown fields.
| Field | Type | Description |
|---|---|---|
id |
string | Event id, same value as the X-Bloop-Event-Id header. |
type |
string | Event type, same value as the X-Bloop-Topic header. |
api_version |
string | Payload version pinned on the endpoint, same value as X-Bloop-API-Version. |
created_at |
string | When the event happened (UTC), same instant as X-Bloop-Triggered-At. |
shop |
string | myshopify domain of the shop, same value as X-Bloop-Shop-Domain. |
test |
boolean | true only for webhook.test. |
data |
object | Event payload, see the schema of type. |
{ "id": "evt_e43789bb-3254-5bcc-b32f-f0e727307e2c", "type": "referrer.joined", "api_version": "v1", "created_at": "2026-10-01T03:12:00Z", "shop": "test-store.myshopify.com", "test": false, "data": { "referrer": { "id": "12345", "shopify_customer_id": "7012345678901", "first_name": "Somchai", "last_name": null, "source": "auto_enrolled", "created_at": "2026-10-01T03:12:00Z", "updated_at": "2026-10-01T03:12:00Z", "campaigns": [ { "campaign_id": "88", "campaign_name": "VIP referral", "status": "active", "joined_at": "2026-10-01T03:12:00Z", "share_code": "AB12CD34", "share_link": "https://refer.example.com/AB12CD34", "discount_code": "REF_AB12CD34", "successful_referrals": 3, "referral_revenue": { "amount": "120.00", "currency": "THB" } } ] }, "campaign": { "campaign_id": "88", "campaign_name": "VIP referral", "status": "active", "joined_at": "2026-10-01T03:12:00Z", "share_code": "AB12CD34", "share_link": "https://refer.example.com/AB12CD34", "discount_code": "REF_AB12CD34", "successful_referrals": 3, "referral_revenue": { "amount": "120.00", "currency": "THB" } }, "join_source": "segment" }}Event types
Section titled “Event types”| Event | You can subscribe | What it means |
|---|---|---|
referrer.joined |
Yes | A membership became active and has its discount code (or the campaign does not use share codes). |
referral.pending |
Yes | A referred order was recorded and waits for approval. |
referral.succeeded |
Yes | The referral was approved and the reward step of that approval finished, with or without a reward. |
webhook.test |
No, sent on demand from the admin | Sent on demand from the Bloop admin to one endpoint. |
referrer.joined
Section titled “referrer.joined”A membership became active and has its discount code (or the campaign does not use share codes).
Fields of data
Section titled “Fields of data”| Field | Type | Description |
|---|---|---|
referrer |
Referrer | |
campaign |
Membership | |
join_source |
string | storefront: joined on the storefront; segment: real-time segment webhook; segment_sync: bulk re-sync after the audience changed. New values may be added. Values: storefront, segment, segment_sync. |
Example data
Section titled “Example data”{ "referrer": { "id": "12345", "shopify_customer_id": "7012345678901", "first_name": "Somchai", "last_name": null, "source": "auto_enrolled", "created_at": "2026-10-01T03:12:00Z", "updated_at": "2026-10-01T03:12:00Z", "campaigns": [ { "campaign_id": "88", "campaign_name": "VIP referral", "status": "active", "joined_at": "2026-10-01T03:12:00Z", "share_code": "AB12CD34", "share_link": "https://refer.example.com/AB12CD34", "discount_code": "REF_AB12CD34", "successful_referrals": 3, "referral_revenue": { "amount": "120.00", "currency": "THB" } } ] }, "campaign": { "campaign_id": "88", "campaign_name": "VIP referral", "status": "active", "joined_at": "2026-10-01T03:12:00Z", "share_code": "AB12CD34", "share_link": "https://refer.example.com/AB12CD34", "discount_code": "REF_AB12CD34", "successful_referrals": 3, "referral_revenue": { "amount": "120.00", "currency": "THB" } }, "join_source": "segment"}referral.pending
Section titled “referral.pending”A referred order was recorded and waits for approval.
Fields of data
Section titled “Fields of data”| Field | Type | Description |
|---|---|---|
referral |
Referral | |
referrer |
ReferrerSummary | |
campaign |
Membership |
Example data
Section titled “Example data”{ "referral": { "id": "5521", "status": "pending", "campaign_id": "88", "referee": { "shopify_customer_id": "7012345670000", "discount_code": "BLOOP-AB12CD34" }, "order": { "shopify_order_id": "5899123456789", "name": "#1043", "total": { "amount": "1290.00", "currency": "THB" }, "created_at": "2026-10-03T08:01:00Z" }, "approval_expected_at": "2026-10-17T08:01:05Z", "created_at": "2026-10-03T08:01:05Z", "updated_at": "2026-10-03T08:01:05Z" }, "referrer": { "id": "12345", "shopify_customer_id": "7012345678901", "first_name": "Somchai", "last_name": null }, "campaign": { "campaign_id": "88", "campaign_name": "VIP referral", "status": "active", "joined_at": "2026-10-01T03:12:00Z", "share_code": "AB12CD34", "share_link": "https://refer.example.com/AB12CD34", "discount_code": "REF_AB12CD34", "successful_referrals": 3, "referral_revenue": { "amount": "120.00", "currency": "THB" } }}referral.succeeded
Section titled “referral.succeeded”The referral was approved and the reward step of that approval finished, with or without a reward.
Fields of data
Section titled “Fields of data”| Field | Type | Description |
|---|---|---|
referral |
Referral | |
referrer |
ReferrerSummary | |
campaign |
Membership | |
reward |
Reward or null | The reward of this referral, null when the approval issued no reward. |
Example data
Section titled “Example data”{ "referral": { "id": "5521", "status": "succeeded", "campaign_id": "88", "referee": { "shopify_customer_id": "7012345670000", "discount_code": "BLOOP-AB12CD34" }, "order": { "shopify_order_id": "5899123456789", "name": "#1043", "total": { "amount": "1290.00", "currency": "THB" }, "created_at": "2026-10-03T08:01:00Z" }, "approval_expected_at": null, "created_at": "2026-10-03T08:01:05Z", "updated_at": "2026-10-17T08:01:05Z" }, "referrer": { "id": "12345", "shopify_customer_id": "7012345678901", "first_name": "Somchai", "last_name": null }, "campaign": { "campaign_id": "88", "campaign_name": "VIP referral", "status": "active", "joined_at": "2026-10-01T03:12:00Z", "share_code": "AB12CD34", "share_link": "https://refer.example.com/AB12CD34", "discount_code": "REF_AB12CD34", "successful_referrals": 3, "referral_revenue": { "amount": "3870.00", "currency": "THB" } }, "reward": { "id": "901", "type": "discount_code", "code": "BLOOP-RW-7Q2", "value": "10", "value_type": "percentage", "currency": null, "description": "10% off your next order", "expires_at": null, "payout_status": null }}webhook.test
Section titled “webhook.test”Sent on demand from the Bloop admin to one endpoint.
Fields of data
Section titled “Fields of data”| Field | Type | Description |
|---|---|---|
message |
string | Fixed human readable text. |
Example data
Section titled “Example data”{ "message": "This is a test webhook from BLOOP."}Objects
Section titled “Objects”A money amount. amount is a decimal string, currency is an ISO 4217 code.
| Field | Type | Description |
|---|---|---|
amount |
string | Decimal string, for example “120.00”. |
currency |
string | ISO 4217 currency code of the shop. |
Membership
Section titled “Membership”One referrer in one campaign. Same shape in GET /v1/referrers (campaigns[]) and in webhook data.campaign.
| Field | Type | Description |
|---|---|---|
campaign_id |
string | Opaque campaign id. |
campaign_name |
string | Campaign name at read time. |
status |
string | Membership status. New values may be added. Values: active, inactive. |
joined_at |
string | When the referrer last joined this campaign (UTC). A re-join after leaving moves it forward. |
share_code |
string | Referral share code of this membership. |
share_link |
string | Personal referral link: share URL of the shop followed by “/” and share_code. |
discount_code |
string or null | Personal discount code. null when the campaign does not use share codes or the code could not be created yet. |
successful_referrals |
integer | Number of successful referrals in this campaign. |
referral_revenue |
Money |
Referrer
Section titled “Referrer”A referrer of the shop with every campaign membership.
| Field | Type | Description |
|---|---|---|
id |
string | Opaque Bloop referrer id. Do not parse or compare order. |
email |
string | Email of the referrer as stored by Bloop. |
shopify_customer_id |
string or null | Numeric Shopify customer id, null when unknown. |
first_name |
string or null | First name from the Shopify customer, null when unknown. |
last_name |
string or null | Last name from the Shopify customer, null when unknown. |
source |
string | How the referrer was created. New values may be added. Values: self_registered, converted_customer, admin_invited, auto_enrolled, imported, unknown. |
created_at |
string | When the referrer was created (UTC). |
updated_at |
string | Latest change of the referrer or of any of its memberships (UTC). Use it to keep the newest copy. |
campaigns |
array of Membership | Every membership of the referrer in campaigns of this shop. |
ReferrerSummary
Section titled “ReferrerSummary”Short referrer object used inside referral events.
| Field | Type | Description |
|---|---|---|
id |
string | Opaque Bloop referrer id. |
email |
string | Email of the referrer as stored by Bloop. |
shopify_customer_id |
string or null | Numeric Shopify customer id, null when unknown. |
first_name |
string or null | First name, null when unknown. |
last_name |
string or null | Last name, null when unknown. |
Referral
Section titled “Referral”A referral: an order placed by a referred friend (referee).
| Field | Type | Description |
|---|---|---|
id |
string | Opaque referral id. |
status |
string | Referral status. New values may be added. Values: pending, succeeded. |
campaign_id |
string or null | Opaque campaign id, null when the campaign no longer exists. |
referee |
object | The referred friend. |
referee.email |
string or null | Email of the referee, null when unknown. |
referee.shopify_customer_id |
string or null | Numeric Shopify customer id, null when unknown. |
referee.discount_code |
string or null | Discount code the referee used, null when none. |
order |
object or null | The order of the referee, null when the referral has no order record. |
order.shopify_order_id |
string | Numeric Shopify order id. |
order.name |
string | Order name, for example “#1043”. |
order.total |
Money | |
order.created_at |
string | When the order was created (UTC). |
approval_expected_at |
string or null | When a pending referral is expected to be approved (UTC). null unless status is pending. |
created_at |
string | When the referral was created (UTC). |
updated_at |
string | Latest change of the referral (UTC). |
Reward
Section titled “Reward”The reward issued to the referrer for one referral.
| Field | Type | Description |
|---|---|---|
id |
string | Opaque reward id. |
type |
string | Reward type. New values may be added. Values: discount_code, store_credit, cash, custom, free_product. |
code |
string or null | Discount code of the reward, null for types without a code (store_credit, cash, custom). |
value |
string or null | Decimal string, null when the reward has no numeric value. |
value_type |
string or null | How value applies, null when value is null. Values: percentage, fixed_amount, null. |
currency |
string or null | ISO 4217 code for fixed amounts, null otherwise. |
description |
string or null | Human readable summary of the reward. |
expires_at |
string or null | Expiry of the reward (UTC), null when it does not expire. |
payout_status |
string or null | Payout state of a cash reward, null for other types. Values: unpaid, paid, null. |
Next steps
Section titled “Next steps”- Verify signatures and handle retries in How to receive webhooks.
- Look up the referrer behind an event in Understanding the API endpoints.
- Check which changes send no event in Understanding the limitations of v1.

