Skip to content

Understanding the limitations of v1

Version 1 covers the main moments of a referral: a referrer joins, a friend’s order is pending, a referral succeeds. Some changes send no event, and a few fields follow rules that are easy to miss. Read this page before you build an integration that must never miss a change.

When an event is missing, the API is the fallback: run a regular sync with updated_since to catch up.

  • A referral is rejected. A pending referral that is rejected, for example after a fraud check or a manual review, sends no event. referral.pending is the last event for it.
  • Referrals approved right away. referral.pending is sent only when Bloop holds a referral for approval. A referral approved right away sends only referral.succeeded.
  • A reward is revoked after a refund. When a refund revokes a reward after referral.succeeded, no event follows.
  • A referrer leaves a campaign. No event. The membership status becomes inactive and the referrer’s updated_at changes, so a sync finds it. If they join again later, Bloop sends a new referrer.joined with a new joined_at.
  • Referrers added by a bulk import. Imported referrers send no referrer.joined when they are imported. They appear in the API with source: imported.
  • Referral orders you add by hand. A referral order you add with Add order on the Orders tab does not send referral.pending. It sends referral.succeeded when it is approved, which is right away unless its reward needs your approval.
  • Referrals approved by an older approval schedule. A small number of referrals scheduled for approval before version 1 went live are approved without referral.succeeded.
  • While your plan does not include the API, or Bloop is uninstalled. Events are skipped and never delivered later, even after you upgrade or reinstall.
  • While an endpoint is disabled. Events are not kept for it. Deliveries that were still waiting when it was disabled are marked failed and not sent later.
  • In the first 60 seconds after you add an endpoint or subscribe it to an event. Events in that window can be missed.
  • During a Bloop incident. If Bloop cannot confirm your subscriptions, or cannot hand the event over for delivery, at the moment the event happens, that event is not sent.
  • An approval that fails partway. If an approval fails partway through, the referral can end up approved without referral.succeeded.
  • referrer.joined waits for the discount code. In a campaign that gives referrers a personal discount code, the event is sent only once the code exists. If Shopify refuses to create the code, the event arrives later, when the code is created. If the code is never created, no event is sent.
  • Delivery stops after 7 attempts. After the last retry, about 21 hours after the event, the delivery is marked failed. Bloop does not send it again by itself. You can resend it from the app for 30 days.
  • Customers who ask to erase their data. When a customer asks Shopify to erase their data, Bloop removes their details from stored events and cancels deliveries still waiting. Those events can no longer be resent.

updated_at changes when the referrer or one of their memberships changes in Bloop. It does not change when:

  • the customer’s name changes in Shopify (first_name, last_name);
  • you rename a campaign (campaign_name);
  • you change your sharing domain (share_link);
  • a referral is rejected after its reward was issued: referral_revenue drops but updated_at stays.

The API returns the values Bloop holds at the time of the request. Names come from the copy Bloop keeps from Shopify, so they can lag behind Shopify. For the latest name, look the customer up in Shopify by shopify_customer_id. When you need an exact referral_revenue, read the referrer again instead of relying on updated_at.

  • source. Referrers created by any import report imported. A source Bloop cannot map reports unknown.
  • join_source. storefront is a join on your storefront, and also the value when Bloop cannot tell where the join came from, for example a referrer you invite from the admin or import. segment is a real-time update from a Shopify segment. segment_sync is a bulk re-sync after the audience of a campaign changed.
  • successful_referrals and referral_revenue can disagree. successful_referrals counts pending referrals as well as approved ones. referral_revenue adds up approved referrals only.
  • Currencies. referral_revenue and reward amounts use your store currency. order.total uses the currency of the order. If your store changed currency, referral_revenue can mix amounts in both currencies.
  • order.name is # followed by the order number, for example #1043. A custom prefix or suffix you set for order names in Shopify is not included.
  • referral.order and referral.referee.email can be null. order is null when Bloop has no order record for the referral, which can happen for referrals created before version 1. referee.email is null when the referee has no email, for example a referral you add by hand without one. The event is still sent, so check for null before you read order.total or match on the email.
  • joined_at is the most recent time the referrer joined the campaign, not the first.
  • referee.discount_code is null for referrals you add by hand.
  • reward.value and reward.value_type are null for discount code rewards issued before version 1.
  • reward can be null in referral.succeeded. The approval issued no reward: the campaign needs more referrals first, the next tier is not reached, rewards are turned off, or you add a referral by hand without sending a reward. It is also null when you add a referral by hand and the reward could not be created at that moment, even if you create it later.
  • Timestamps carry milliseconds. The envelope created_at and the X-Bloop-Triggered-At header look like 2026-10-01T03:12:00.123Z. Parse them as RFC 3339 times instead of matching one exact format. updated_at in the API has second precision.
  • The signing key is the whole secret. Compute the webhook signature with the full secret, including the whsec_ prefix. A secret without the prefix gives a different signature.
  • plan in GET /v1/me is a display name. Don’t branch on it.
  • Encode + in emails. A + sent as is in a query string is read as a space. Send [email protected] as somchai%2Bline%40example.com.
  • Every request that passes the key and store checks counts toward the rate limit, including GET /v1/me and requests that fail with 400, 403 insufficient_scope or 404. Bloop checks a request in this order: key, store access, rate limit, path, scope, parameters.
  • Only GET exists. Any other method, including OPTIONS and HEAD, returns 404 not_found once the key is valid.
  • GET /v1/me and GET /v1/referrers/{id} take no query parameters. Adding one returns 400 invalid_request.
  • Email lookups are not checked for email format. Any non-empty value up to 320 characters is accepted, and a value that matches no referrer returns 404 referrer_not_found.
  • A malformed referrer id is a 404, not a 400. GET /v1/referrers/abc returns 404 referrer_not_found, the same answer as an id that does not exist.
  • 503 service_unavailable is temporary. Wait the number of seconds in Retry-After and retry.
  • No browser calls. The API sends no CORS headers.
  • A list can return a referrer twice when it changes while you page. It is never skipped.
  • Read-only. Version 1 has no endpoint that creates or changes data, and no endpoint that lists referrals or campaigns.
  • Keys don’t expire, and their scope is fixed. The scope is set when a key is created and cannot be changed later.
  • Treating referral.succeeded as final. A refund can revoke the reward later without an event.
  • Reporting successful_referrals as approved referrals. It includes pending ones.
  • Relying on webhooks alone. Pair them with a scheduled sync.
  • Removing whsec_ from the secret. Verification then fails for every webhook.