API Reference
Server-to-server integration guide for tracking referrals, claims, conversions, and refunds from any platform.
Base URL: https://api-production.advocateloop.com
Authentication
Two authentication methods are supported. Use whichever fits your integration.
Option 1: API Key (recommended for most integrations)
Send your API key and brand ID as HTTP headers on every request.
X-API-Key: your-api-key-hereX-Brand-ID: your-brand-uuid-hereYour API key and brand ID are available in the Advocate Loop dashboard under Settings → API.
Option 2: Webhook Signature (for platforms that sign payloads)
For integrations where your platform signs outgoing webhook payloads (e.g., Zapier, custom webhooks), use HMAC-SHA256 signature verification.
Required headers:
X-Brand-ID: your-brand-uuid-hereX-AL-Signature: hmac-sha256-hex-signatureX-AL-Timestamp: unix-timestamp (optional, enables 5-minute replay protection)How to compute the signature:
- Take the raw JSON request body as a string
- Compute HMAC-SHA256 using your API key as the secret
- Hex-encode the result
- Send it in the
X-AL-Signatureheader
const crypto = require('crypto');
const body = JSON.stringify(payload);const signature = crypto .createHmac('sha256', YOUR_API_KEY) .update(body) .digest('hex');The webhook signature method is currently only available on POST /api/v1/convert (see the Convert endpoint below). All other endpoints use API key authentication.
Endpoints
Track Visit (Click)
Record when a visitor clicks a referral link on your site.
POST /api/v1/track/visitAuth: API KeyRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
referral_code | string | Yes | The referral code from the URL |
visitor_id | string | No | Visitor identifier (UUID). If omitted, one is generated |
existing_code | string | No | Referral code already stored on the visitor’s device |
share_channel | string | No | Traffic source (e.g., sms, email, facebook). Typically from utm_source |
landing_page | string | No | The full URL the visitor landed on |
Response (200):
{ "success": true, "data": { "store_code": true, "clear_existing": false, "code": "V2AVMRDJ", "referrer_first_name": "Sarah", "visitor_id": "729979f7-447b-4c81-a885-e863aa94f7e4" }}Track Claim
Record when a customer uses or claims a referral discount. Creates a claim record with status: pending.
POST /api/v1/track/claimAuth: API KeyRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
referral_code | string | Yes | The referral code being claimed |
email or customer_email | string | Yes | Customer’s email address |
visitor_id | string | No | Visitor identifier from click tracking |
name or customer_name | string | No | Customer’s name |
order_id | string | No | Order/transaction identifier |
raw_value | number | No | Order value before discount |
claim_value | number | No | Order value after discount |
discount_value | number | No | Discount amount configured |
discount_type | string | No | percentage, fixed, or custom |
type or claim_type | string | No | referral (default) or advocate |
products | array | No | Products in the order. See Products Array section below |
coupons_applied | array | No | Coupons used: [{ code, discount_amount, discount_tax }] |
metadata | object | No | Any additional data you want to store |
Response (201):
{ "success": true, "data": { "id": 42, "claim_id": 42, "referral_code": "V2AVMRDJ", "status": "pending", "discount_applied": 5.70, "claim_value": 51.30 }}Track Conversion
Record a completed order/payment. This is the most important endpoint — it records the conversion and credits the advocate’s reward.
Call this only after payment succeeds, not at checkout time.
POST /api/v1/track/conversionAuth: API KeyRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
email or customer_email | string | Yes | Customer’s email address |
order_id | string | Yes | Unique order/transaction identifier |
order_value | number | Yes | Final order value (after discounts) |
referral_code | string | No | Referral code. If omitted, the API resolves it from coupons_applied or prior attribution |
visitor_id | string | No | Visitor identifier. Helps with attribution but not required for cross-device flows |
claim_id | integer | No | Explicit claim ID to link. Usually resolved automatically via email |
referrer_reward_paid | number | No | Reward amount paid to the referrer |
referee_reward_paid | number | No | Reward amount given to the customer |
discount_value | number | No | Discount configuration value |
discount_type | string | No | percentage, fixed, or custom |
type | string | No | referral (default) or advocate |
products | array | No | Products in the order. See Products Array section below |
coupons_applied | array | No | All coupons used: [{ code, discount_amount, discount_tax }] |
source | string | No | Source identifier (e.g., shopify, custom, api) |
status | string | No | Override status. Default: completed |
metadata | object | No | Any additional data |
Response (201):
{ "success": true, "data": { "id": 15, "status": "completed", "order_value": 51.30, "discount_applied": 5.70, "total_discount": 5.70, "order_subtotal": 57.00, "claim_id": 42 }}Sending attribution data:
Provide as much identifying information as you have. For server-to-server integrations, sending customer_email along with either coupons_applied or referral_code is typically sufficient.
Track Refund
Record a full or partial refund. Updates the claim and conversion status.
POST /api/v1/track/refundAuth: API KeyRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
order_id | string | Yes* | Order identifier. Used to find the conversion to update |
refund_amount | number | Yes | Amount being refunded |
is_full_refund | boolean | Yes | Whether this is a complete refund |
conversion_id | integer | No | Explicit conversion ID (alternative to order_id lookup) |
claim_id | integer | No | Explicit claim ID (alternative to order_id lookup) |
referral_code | string | No | Referral code (for logging) |
refund_id | string | No | Your platform’s refund identifier |
total_refunded | number | No | Cumulative refund total |
order_total | number | No | Original order total |
refund_reason | string | No | Reason for the refund |
new_status | string | No | Override status. Default: refunded (full) or partial_refund (partial) |
source | string | No | Source identifier |
metadata | object | No | Any additional data |
*Either order_id or conversion_id is required so the API can find the record to update.
Response (200):
{ "success": true, "data": { "status": "refunded", "claim_updated": true, "conversion_updated": true, "claim_id": 42, "conversion_id": 15 }}Check Eligibility
Check whether a customer is eligible for a referral discount before applying it.
POST /api/v1/track/check-eligibilityAuth: API KeyRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
referral_code | string | Yes | The referral code to check |
customer_email | string | Yes | Customer’s email address |
type | string | No | referral or advocate |
Response (200, eligible):
{ "eligible": true}Response (200, not eligible):
{ "eligible": false, "error": "already_redeemed", "message": "This customer has already used a referral discount."}Convert (Webhook)
Simplified claim creation for external webhook sources (Calendly, Zapier, custom integrations). Uses webhook signature authentication instead of API key.
POST /api/v1/convertAuth: Webhook SignatureRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
referral_code | string | Yes | The referral code |
email | string | Yes | Customer’s email address |
value | number | No | Transaction value (before discount). Default: 0 |
customer_name | string | No | Customer’s name |
metadata | object | No | Any additional data |
Response (201):
{ "success": true, "data": { "claim_id": 43, "referral_code": "V2AVMRDJ", "raw_value": 99.00, "claim_value": 93.30, "discount_applied": 5.70, "created_at": "2026-04-14T20:00:00.000Z" }}Duplicate protection: Repeat submissions of the same claim are prevented. If a duplicate is detected, the endpoint returns 409 Conflict with the existing claim_id.
Common Integration Patterns
Products Array
The products field accepted by /track/claim and /track/conversion is a JSON array of product objects. Any fields you send are stored, but the dashboard uses these specific fields for the “Top Products by Revenue” report:
| Field | Aliases | Type | Default | Used for |
|---|---|---|---|---|
name | title, product_name | string | ”Unknown Product” | Product identification in reports |
quantity | qty | integer | 1 | Units sold calculation |
price | total, line_total | number | 0 | Revenue calculation (price × quantity) |
Additional fields like sku, variant, category, size, image_url, etc. are preserved in the database but not actively processed by the dashboard. Include them if you want the data available for future reporting.
Example:
{ "products": [ { "name": "Classic T-Shirt", "price": 29.99, "quantity": 2, "sku": "TSH-001", "variant": "Large / Black", "category": "Apparel" }, { "name": "Canvas Tote", "price": 15.00, "quantity": 1, "sku": "BAG-010" } ]}Shopify (or any e-commerce platform without a plugin)
On referral link click (storefront JS or server-side):
POST /api/v1/track/visit{ "referral_code": "V2AVMRDJ", "landing_page": "https://yourstore.com/?ref=V2AVMRDJ" }At checkout (before payment, to create the claim):
POST /api/v1/track/claim{ "referral_code": "V2AVMRDJ", "customer_email": "buyer@example.com", "order_id": "ORD-12345", "raw_value": 57.00, "coupons_applied": [{ "code": "V2AVMRDJ", "discount_amount": 5.70 }], "products": [ { "name": "Classic T-Shirt", "price": 29.99, "quantity": 1, "sku": "TSH-001" }, { "name": "Canvas Tote", "price": 27.01, "quantity": 1, "sku": "BAG-010" } ]}After payment succeeds (webhook from payment processor):
POST /api/v1/track/conversion{ "customer_email": "buyer@example.com", "order_id": "ORD-12345", "order_value": 51.30, "coupons_applied": [{ "code": "V2AVMRDJ", "discount_amount": 5.70 }], "products": [ { "name": "Classic T-Shirt", "price": 29.99, "quantity": 1, "sku": "TSH-001" }, { "name": "Canvas Tote", "price": 27.01, "quantity": 1, "sku": "BAG-010" } ]}On refund (webhook from payment processor):
POST /api/v1/track/refund{ "order_id": "ORD-12345", "refund_amount": 51.30, "is_full_refund": true}Booking / SaaS / Service Business
For businesses without a traditional checkout (appointment booking, subscriptions, service sign-ups), you typically need just the claim and conversion:
When customer signs up with a referral code:
POST /api/v1/track/claim{ "referral_code": "V2AVMRDJ", "customer_email": "client@example.com", "customer_name": "Jane Smith", "raw_value": 150.00}When payment is confirmed (immediately or later):
POST /api/v1/track/conversion{ "customer_email": "client@example.com", "order_id": "BOOKING-789", "order_value": 142.50, "referral_code": "V2AVMRDJ"}Zapier / Make / n8n (webhook-based)
Use the /api/v1/convert endpoint with webhook signature authentication. This is simpler than the full claim + conversion flow and suitable for integrations where you just need to record that a referral happened.
Zapier setup:
- Trigger: your platform event (new booking, form submission, etc.)
- Action: Webhooks by Zapier → POST
- URL:
https://api-production.advocateloop.com/api/v1/convert - Headers:
X-Brand-ID,X-AL-Signature,X-AL-Timestamp - Body:
{ "referral_code": "...", "email": "...", "value": ... }
See the Authentication section above for how to compute the signature.
Error Handling
All endpoints return JSON with a success field.
Success responses:
{ "success": true, "data": { ... } }Error responses:
{ "success": false, "error": "Human-readable error message" }HTTP status codes:
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created (new claim or conversion) |
| 400 | Bad request (missing required fields) |
| 401 | Authentication failed (bad API key or missing headers) |
| 403 | Forbidden (brand ID doesn’t match API key) |
| 404 | Not found (invalid referral code) |
| 409 | Conflict (duplicate claim within 24 hours) |
| 429 | Rate limited |
| 500 | Internal server error |
Rate Limits
| Endpoint | Limit |
|---|---|
/track/visit | 100/min per IP |
/track/claim | 20/hour per IP |
/track/conversion | 20/hour per IP |
/track/refund | 20/hour per IP |
/track/check-eligibility | 100/min per IP |
/convert (webhook) | 100/min per IP |
Rate-limited responses return 429 Too Many Requests.
Testing
Use a test brand and real API key from your dashboard. All endpoints work identically in test and production — there’s no sandbox mode. To clean up test data, delete the relevant rows from your database.
Quick test with curl:
# Record a clickcurl -X POST https://api-production.advocateloop.com/api/v1/track/visit \ -H "Content-Type: application/json" \ -H "X-API-Key: your-api-key" \ -H "X-Brand-ID: your-brand-id" \ -d '{"referral_code": "TESTCODE"}'
# Create a claimcurl -X POST https://api-production.advocateloop.com/api/v1/track/claim \ -H "Content-Type: application/json" \ -H "X-API-Key: your-api-key" \ -H "X-Brand-ID: your-brand-id" \ -d '{"referral_code": "TESTCODE", "customer_email": "test@example.com", "raw_value": 100}'
# Record a conversioncurl -X POST https://api-production.advocateloop.com/api/v1/track/conversion \ -H "Content-Type: application/json" \ -H "X-API-Key: your-api-key" \ -H "X-Brand-ID: your-brand-id" \ -d '{"customer_email": "test@example.com", "order_id": "TEST-001", "order_value": 95.00, "referral_code": "TESTCODE"}'