Superfiliate

Partners API

Webhooks

Get customer changes pushed to your endpoint, verify signatures, and handle retries.

Topics

Five topics are available:

TopicFires whenPayload
customer_updatedA customer is created or updated.The full customer object — the same shape returned by the customer endpoints.
gifting_reward_updatedA creator submits gift details and product selections, or fulfillment details change.The full merchant-managed gifting reward, including immutable product snapshots in selected_products.
discount_code_createdA custom-commerce discount code is created.The discount code object from the discount code endpoint. Requires discounts.read to subscribe.
discount_code_updatedA custom-commerce discount code is updated.The discount code object. Requires discounts.read to subscribe.
discount_code_deletedA custom-commerce discount code is deleted.Snapshot of the discount code before deletion. Requires discounts.read to subscribe.

An example delivery:

POST · customer_updatedjson
{
  "id": 123345567,
  "email": "enrolled@superfiliate.com",
  "phone": "+12125551234",
  "first_name": "Alice",
  "last_name": "Wonder",
  "instagram_handle": "alice",
  "tiktok_handle": "alice_tiktok",
  "balance": "10.0",
  "code": "CODE-FOR-FRIENDS",
  "reward_code": "CODE-TO-REDEEM-BALANCE",
  "microsite_url": "https://shop.com/CODE-FOR-FRIENDS",
  "redeem_url": "https://shop.com/discount/CODE-TO-REDEEM-BALANCE?utm_...",
  "campaign_id": 7890,
  "enrolled_at": "2024-01-01 12:00:00 UTC",
  "tags": ["VIP Customer", "Newsletter Subscriber"]
}
POST · gifting_reward_updatedjson
{
  "id": 123456,
  "gifting_stage": "ready_to_send",
  "campaign": { "id": 345678, "name": "Summer gifting" },
  "creator": {
    "id": 789012,
    "first_name": "Alex",
    "last_name": "Creator",
    "email": "alex@example.com",
    "phone": "+15555550123"
  },
  "shipping_address": {
    "address1": "123 Main St",
    "city": "New York",
    "country_code": "US",
    "zip": "10001"
  },
  "selected_products": [
    {
      "product_external_id": "coffee-bundle",
      "variant_external_id": "coffee-dark",
      "product_title": "Coffee bundle",
      "variant_title": "Dark roast",
      "image_url": "https://example.com/dark-roast.jpg",
      "sku": "DARK-ROAST",
      "options": [{ "name": "Roast", "value": "Dark" }],
      "quantity": 1,
      "selected_at": "2026-08-11T18:00:00Z"
    }
  ],
  "fulfillment": null,
  "details_submitted_at": "2026-08-11T18:00:00Z",
  "created_at": "2026-08-10T18:00:00Z",
  "updated_at": "2026-08-11T18:00:00Z"
}

Delivery & retries

  • Respond with a 2xx quickly — do the heavy lifting after you acknowledge.
  • Anything else is retried multiple times using a randomized exponential backoff algorithm, for roughly 20 days before we give up.
  • Make your handler idempotent: deliveries can arrive more than once and out of order. Treat each payload as the latest known state of that resource, not as an event log.

Headers

HeaderContents
X-SUPERFILIATE-HMAC-SHA256Base64 HMAC signature of the raw request body, used for verification below.
X-SUPERFILIATE-SHOP-DOMAINThe store the event belongs to, eg. example.com — useful when one endpoint serves several stores.
X-SHOPIFY-SHOP-DOMAINThe Shopify domain for Shopify integrations, eg. example.myshopify.com.

Verify signatures

Every delivery is signed with your client_secret. We compute an HMAC-SHA256 digest of the raw request body, Base64-encode it, then compare it with the signature header in constant time.

class WebhooksController < ApplicationController
  before_action :verify_webhook_authenticity!

  def index
    shop_domain = request.headers["X-SUPERFILIATE-SHOP-DOMAIN"]
    Rails.logger.info("Webhook received: #{shop_domain} #{request.raw_post}")
  end

  private

  def verify_webhook_authenticity!
    hmac_header = request.headers["X-SUPERFILIATE-HMAC-SHA256"]
    calculated_hmac = Base64.strict_encode64(
      OpenSSL::HMAC.digest("sha256", CLIENT_SECRET, request.raw_post)
    )
    return if ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header)

    head :unauthorized
  end
end

Manage subscriptions

Subscriptions are managed entirely through the API: