KeepFace · AsyncAPI Specification

Keepface Affiliate Webhooks

Version

View Spec View on GitHub CompanyInfluencer MarketingCreator EconomyMarketingSocial MediaAffiliate MarketingAdvocacyCampaign ManagementMCPAI AgentsAttributionWebhooksAsyncAPIWebhooksEvents

AsyncAPI Specification

keepface-affiliate-webhooks.yml Raw ↑
generated: '2026-08-13'
method: searched
source: https://help.keepface.com/brand/affiliate-program/api-reference/
docs:
- https://help.keepface.com/brand/affiliate-program/api-reference/
- https://help.keepface.com/brand/affiliate-program/postback-hmac-integration/
- https://help.keepface.com/influencer/affiliate-program/shopify-webhook/

spec_type: none
asyncapi_published: false

# IMPORTANT — READ THE DIRECTION. Keepface's documented event surface is INBOUND:
# the brand's backend, its Shopify store, or a browser pixel POSTs sale and refund
# events INTO Keepface. Keepface does not publish an outbound webhook catalogue —
# it sends no HTTP callbacks to subscriber endpoints. Its own event notifications
# (campaign, outreach, wallet, billing, system) are delivered only as in-app,
# email and mobile push, all account-gated, with no webhook channel offered.
# This artifact therefore records a real, signed, versioned, documented webhook
# CONTRACT that integrators must implement against — not an event-subscription
# feed.

direction: inbound
outbound_webhooks: false
outbound_note: >-
  Probed the notification documentation. Keepface offers exactly three delivery
  channels — in-app (always on), email, and mobile push — across five event
  groups (Campaign, Outreach, Wallet, Billing, System). No webhook or HTTP
  callback channel is offered on any event, at any tier.

base_url: https://api.keepface.ai/api/v2
transport: HTTPS POST, application/json
event_count: 4

receivers:

- name: affiliate.sale
  endpoint: POST /api/v2/affiliate/postback/{brand_id}
  direction: brand backend -> Keepface
  purpose: report a completed purchase so it can be attributed to a creator
  security: HMAC-SHA256 body signature (X-KF-Signature over "<timestamp>.<body>")
  replay_window_seconds: 300
  idempotency_key: (brand_id, order_id)
  payload_fields:
  - {name: token, type: string, length: 8, required: token OR discount_code, note: mixed-case
      alphanumeric, case-sensitive}
  - {name: discount_code, type: string, max_length: 64, required: token OR discount_code,
    note: case-insensitive on lookup}
  - {name: order_id, type: string, max_length: 128, required: true, note: the idempotency
      key}
  - {name: gross_amount, type: number, minimum: 0, required: true, note: major units
      — 149.00 is $149, not 14900}
  - {name: net_amount, type: number, minimum: 0, required: false, note: defaults to
      gross}
  - {name: currency, type: string, length: 3, required: true, note: ISO 4217, uppercase}
  - {name: customer_email, type: string, required: false, note: fraud and dedupe; hashed
      before storage}
  - {name: customer_ip, type: string, required: false, note: fraud and geo; hashed before
      storage}
  - {name: customer_country, type: string, length: 2, required: false, note: ISO 3166-1
      alpha-2; falls back to IP geo}
  - {name: skus, type: "string[]", required: false, note: evaluated against excluded_skus}
  - {name: categories, type: "string[]", required: false, note: evaluated against excluded_categories}
  - {name: brand_confirmed_at, type: string, format: ISO 8601, required: false, note: defaults
      to now}
  - {name: source_method, type: enum, required: false, values: [postback]}
  - {name: metadata, type: object, required: false, note: free-form, surfaced in admin
      drilldown}
  responses:
  - {status: 201, body: '{data: conversion}', meaning: new conversion created}
  - {status: 200, body: '{ok: true, created: false}', meaning: idempotent replay}
  - {status: 200, body: '{ok: false, reason: unknown_brand}', meaning: brand does not
      exist}
  - {status: 200, body: '{ok: false, reason: affiliate_disabled}', meaning: affiliate
      paused}
  - {status: 400, body: '{error: invalid_json}'}
  - {status: 401, body: '{error: invalid_signature}'}
  - {status: 401, body: '{error: stale_timestamp}'}
  - {status: 422, body: '{error: rejected, message: ...}', meaning: anti-fraud or restriction}
  - {status: 429, body: '{error: Too Many Requests}'}
  rate_limit: 120 per minute per IP

- name: affiliate.refund
  endpoint: POST /api/v2/affiliate/postback/{brand_id}/refund
  direction: brand backend -> Keepface
  purpose: claw back commission on a refunded order
  security: HMAC-SHA256 body signature
  idempotency_key: (brand_id, order_id, refund_id) when refund_id is present, otherwise
    (brand_id, order_id, sha256(refund_amount + currency))
  payload_fields:
  - {name: order_id, type: string, required: true, note: must match an existing conversion}
  - {name: refund_id, type: string, required: false, note: optional but recommended}
  - {name: refund_amount, type: number, required: true}
  - {name: currency, type: string, length: 3, required: true}
  - {name: brand_confirmed_at, type: string, format: ISO 8601, required: false}
  - {name: metadata, type: object, required: false}
  rate_limit: 120 per minute per IP

- name: affiliate.pixel
  endpoint: POST /api/v2/affiliate/pixel/{brand_id}
  direction: buyer browser -> Keepface
  purpose: browser-side sale event fired from the checkout success page
  security: Origin allowlist (configured at Brands > Edit > Affiliate > Tracking >
    Allowed pixel origins)
  cors_preflight: OPTIONS /api/v2/affiliate/pixel/{brand_id}
  idempotency_key: (brand_id, order_id)
  payload_fields: same shape as affiliate.sale
  rate_limit: 240 per minute per IP

- name: affiliate.shopify
  endpoint: POST /api/v2/affiliate/shopify/{brand_id}
  direction: Shopify -> Keepface
  purpose: receive Shopify orders, refunds and app-uninstall webhooks
  security: Shopify HMAC — header X-Shopify-Hmac-Sha256, base64(hmac_sha256(shopify_secret,
    raw_body))
  idempotency_key: (brand_id, order_id) using Shopify's own order.id
  failure: '412 {error: shopify_not_configured} when the brand has no Shopify secret'
  rate_limit: 120 per minute per IP

retry_expectations:
  # What Keepface asks a sender to do — the sender is the integrator here.
  retry_on: [429, 5xx]
  do_not_retry_on: [200, 201, 401, 412, 422]
  backoff: exponential 1s, 2s, 4s, 8s
  max_attempts: 5
  honour_retry_after: true

x-evidence:
- {url: 'https://help.keepface.com/brand/affiliate-program/api-reference/', http_status: 200,
  fetched: '2026-08-13'}
- {url: 'https://help.keepface.com/brand/affiliate-program/postback-hmac-integration/',
  http_status: 200, fetched: '2026-08-13'}
- {url: 'https://help.keepface.com/brand/notifications/channels/', http_status: 200,
  note: 'confirms three delivery channels, none of them a webhook', fetched: '2026-08-13'}