Support

Inbound Webhooks

Receive events from external platforms and award seeds to your customers automatically. Inbound webhooks let you connect any third-party tool - quiz apps, forms

📥 Inbound Webhooks

Receive events from external platforms and award seeds to your customers automatically. Inbound webhooks let you connect any third-party tool — quiz apps, forms, review platforms, Zapier, Make, and more — to your LoyaltyTree loyalty program.


How It Works

The inbound webhook system uses a Sources & Codes model:

  1. Create a Source — A source represents an external platform or tool (e.g. "Typeform", "Zapier", "My Quiz App"). Each source gets a unique webhook URL and a secret key for security.
  2. Add Codes to the Source — Each code defines a specific action that awards seeds (e.g. "quiz_completed", "form_submitted", "birthday_claimed"). You set how many seeds each code awards, with optional limits.
  3. Send POST requests — Your external platform sends a POST request to the webhook URL with the code and customer info. LoyaltyTree finds or creates the customer and awards the seeds automatically.

External PlatformPOST to Webhook URLLoyaltyTree validates & awards seedsCustomer balance updated


Getting Started

Step 1: Create a Webhook Source

Navigate to Stores → [Your Store] → Integrations → Inbound Webhooks. Click + Add Source and give it a name that describes the external platform (e.g. "Typeform Quizzes" or "Zapier Automation").

When the source is created, you'll receive:

  • Webhook URL — The endpoint your external platform will send requests to
  • Webhook Secret — A secret key for signing requests (recommended for security)

Step 2: Add Codes

Each code represents a specific action you want to reward. Click + Add Code on your source to create one.

Field Required Description
Code Yes The exact value sent in the webhook payload (e.g. quiz_completed). This must match what your external platform sends.
Display Name Yes A human-friendly name shown in the admin dashboard and transaction history (e.g. "Quiz Completed").
Description No An optional note for your team about when this code is used.
Seeds Amount Yes How many seeds to award each time this code is triggered. Defaults to 1.
Max Seeds/Customer No The maximum total seeds a single customer can earn from this code. Leave empty for unlimited. Once a customer reaches this limit, further webhook calls for them will return success but award 0 seeds.
Cooldown (hours) No Minimum hours between awards for the same customer on this code. Prevents abuse by rate-limiting how often a customer can earn seeds. Leave empty for no cooldown.
Enabled Toggle to enable or disable this code without deleting it. Disabled codes return a 403 error.

Step 3: Configure Your External Platform

Set up your external platform (Zapier, Typeform, custom app, etc.) to send a POST request to your webhook URL whenever the action occurs. See the Request Format section below for the exact payload format.


Codes & Limits Explained

Codes and their limits work together to give you precise control over how seeds are awarded:

Seeds Amount

Each code has a fixed seed amount. Every time the webhook is triggered with that code, the customer receives exactly that many seeds. For example, if you set "quiz_completed" to 5 seeds, every qualifying webhook call awards 5 seeds.

Max Seeds Per Customer

This sets a lifetime cap per customer per code. It's the total seeds that customer can earn from this specific code, not the number of times they can trigger it.

Example: You create a code quiz_completed with Seeds Amount = 5 and Max Seeds/Customer = 15.
  • 1st quiz completed → +5 seeds (total: 5) ✅
  • 2nd quiz completed → +5 seeds (total: 10) ✅
  • 3rd quiz completed → +5 seeds (total: 15) ✅
  • 4th quiz completed → +0 seeds (limit reached) — returns success but no seeds awarded

Cooldown (Hours)

Sets a minimum wait time between seed awards for the same customer on the same code. The cooldown timer starts from the last successful award.

Example: You create a code daily_visit with Seeds Amount = 2 and Cooldown = 24 hours.
  • Monday 10am → +2 seeds ✅
  • Monday 3pm → +0 seeds (cooldown active, try again in 19 hours)
  • Tuesday 11am → +2 seeds ✅
Tip: You can combine both limits! For example, set a code with Seeds = 10, Max = 30, and Cooldown = 168 hours (7 days) to award 10 seeds per week, up to 30 total.

Request Format

Send a POST request to your webhook URL with the following JSON body:

{
  "code": "your_code_here",
  "customer": {
    "email": "customer@example.com",
    "shopify_customer_id": "12345",
    "first_name": "Jane",
    "last_name": "Smith"
  },
  "metadata": {
    "quiz_score": 95,
    "source_page": "spring-quiz"
  }
}
Field Required Description
code Yes The code value that matches one of your configured codes (e.g. quiz_completed)
customer.email Yes* Customer's email address. Used to find or create the customer. Required for new customers.
customer.shopify_customer_id No* The customer's Shopify ID. Can be used instead of email to identify existing customers.
customer.first_name No Customer's first name. Used when creating new customers.
customer.last_name No Customer's last name. Used when creating new customers.
metadata No Any additional data you want to store with the transaction (e.g. quiz scores, page info). Stored as JSON and visible in logs.

* At least one of email or shopify_customer_id is required. Email is required when the customer doesn't already exist in LoyaltyTree.


HMAC Signature Verification (Recommended)

To verify that requests are genuinely coming from your platform (and not from someone who found your webhook URL), sign your requests using HMAC-SHA256.

How to sign your request:
  1. Take the raw JSON request body as a string
  2. Create an HMAC-SHA256 hash using your Webhook Secret as the key
  3. Include the hex-encoded hash in one of the supported headers

Supported signature headers (LoyaltyTree checks all of these):

  • X-Webhook-Signature
  • X-Hub-Signature-256
  • X-Signature

The signature value can be either the raw hex hash or prefixed with sha256= (both formats are accepted).

Alternative: Plain Token Authentication

If HMAC signing is not possible in your platform, you can pass your webhook secret as a plain token in the X-Token header. LoyaltyTree will compare it directly to your secret.

Example: Signing with Node.js

const crypto = require('crypto');

const payload = JSON.stringify({ code: 'quiz_completed', customer: { email: 'jane@example.com' } });

const signature = crypto .createHmac('sha256', 'your_webhook_secret') .update(payload) .digest('hex');

fetch('https://loyaltytree.eco/webhooks/inbound/YOUR_SOURCE_ID', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Webhook-Signature': signature }, body: payload });

Example: Signing with Python

import hmac, hashlib, json, requests

payload = json.dumps({ "code": "quiz_completed", "customer": {"email": "jane@example.com"} })

signature = hmac.new( b'your_webhook_secret', payload.encode('utf-8'), hashlib.sha256 ).hexdigest()

requests.post( 'https://loyaltytree.eco/webhooks/inbound/YOUR_SOURCE_ID', headers={ 'Content-Type': 'application/json', 'X-Webhook-Signature': signature }, data=payload )

Example: Using cURL (for testing)

curl -X POST https://loyaltytree.eco/webhooks/inbound/YOUR_SOURCE_ID \
  -H "Content-Type: application/json" \
  -H "X-Token: your_webhook_secret" \
  -d '{"code":"quiz_completed","customer":{"email":"jane@example.com"}}'

Response Format

Successful Response (200)

{
  "success": true,
  "seeds_awarded": 5,
  "transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "customer_id": "f0e1d2c3-b4a5-6789-0123-456789abcdef"
}

Successful but Limited (200)

When a cooldown or max seeds limit prevents the award, you still get a 200 response but with 0 seeds and an explanation:

{
  "success": true,
  "seeds_awarded": 0,
  "message": "Cooldown active. Try again in 18 hour(s)."
}

// or

{ "success": true, "seeds_awarded": 0, "message": "Maximum seeds (15) already awarded for this code." }

Error Responses

HTTP Status Error Meaning
400 Missing required field: code The code field was not included in the request body
400 Missing required field: customer.email or customer.shopify_customer_id No customer identifier was provided
400 Unknown code: xyz The code sent doesn't match any configured codes for this source
401 Invalid webhook signature or token The HMAC signature or token doesn't match. Check your webhook secret.
403 Webhook source is disabled The source has been disabled in the admin dashboard
403 Code is disabled: xyz The specific code has been disabled
404 Webhook source not found The source ID in the URL doesn't exist. Check the webhook URL.

Use Case Examples

🧩 Quiz Completion

Reward customers for completing a quiz on your site (built with Typeform, Google Forms, etc.).

  • Code: quiz_completed
  • Seeds: 10
  • Max Seeds: 10 (one-time only)
  • Cooldown: none
📋 Survey Response

Award seeds when a customer completes a post-purchase survey.

  • Code: survey_completed
  • Seeds: 5
  • Max Seeds: 25 (up to 5 surveys)
  • Cooldown: 168 hours (once per week)
🎂 Birthday Reward

Give seeds on a customer's birthday via your marketing platform.

  • Code: birthday_reward
  • Seeds: 25
  • Max Seeds: none (annual)
  • Cooldown: 8760 hours (365 days)
📸 Instagram Tag

Reward customers who tag your brand on Instagram (verified via your social media team or tool).

  • Code: instagram_tag
  • Seeds: 15
  • Max Seeds: 60 (up to 4 tags)
  • Cooldown: 72 hours (once every 3 days)

Connecting with Zapier

Zapier is one of the most popular ways to connect LoyaltyTree with hundreds of other apps. We are building a dedicated LoyaltyTree integration in the Zapier App Store, which will make setup even easier — look for it in the Zapier marketplace.

💡 Coming Soon: Zapier App Store Integration

We're adding LoyaltyTree as a native Zapier app. Once available, you'll be able to search for "LoyaltyTree" in the Zapier app directory and connect it directly — no webhook configuration needed. Stay tuned!

In the meantime, you can use Zapier's Webhooks by Zapier action to connect right now:

Setting up with Zapier Webhooks:
  1. Create a Zap with your desired trigger (e.g. "New Typeform Response")
  2. Add an action step: Webhooks by Zapier → POST
  3. Set the URL to your LoyaltyTree inbound webhook URL
  4. Set Payload Type to JSON
  5. Map the fields:
    • code → your code value (e.g. "quiz_completed")
    • customer.email → the respondent's email from the trigger
  6. Under Headers, add X-Token with your webhook secret value
  7. Test and enable your Zap

 

Connecting with Make (Integromat)

You can also use Make's HTTP / Make a request module to send webhooks to LoyaltyTree. Configure it the same way as Zapier — set the URL, add the JSON body with code and customer, and include your secret in the X-Token header.


Webhook Logs

Every inbound webhook call is logged and visible in the Inbound Webhooks section of your Integrations page. Logs show:

  • Timestamp – When the webhook was received
  • Source – Which source received it
  • Status – Success or failure
  • Details – Code used, seeds awarded, or error message
  • Payload – The full request body (encrypted at rest)

Use the logs to verify your integration is working correctly and to troubleshoot any issues.

 


Troubleshooting

Getting 401 "Invalid webhook signature"
  • Make sure you're signing the exact JSON body string that you're sending (no extra whitespace or reformatting)
  • Verify you're using the correct webhook secret (you can reveal it in the admin dashboard)
  • If HMAC signing is not possible, use the X-Token header with your secret as a plain value
  • You can regenerate the secret from the source settings if needed
Getting 400 "Unknown code"
  • The code value in your request must exactly match a code you've configured in the admin dashboard
  • Codes are case-sensitive: Quiz_Completed is different from quiz_completed
  • Check that the code is enabled (not disabled)
Seeds awarded is 0
  • Check if the customer has hit the Max Seeds/Customer limit for this code
  • Check if the Cooldown is still active — the response message will tell you how many hours to wait
  • Both return HTTP 200 with seeds_awarded: 0 and an explanation in the message field
Customer not being created
  • New customers require an email address. If you only send shopify_customer_id, the customer must already exist in LoyaltyTree.
  • If both email and Shopify ID are provided, LoyaltyTree first searches by email, then by Shopify ID, and creates a new customer only if neither match.

Security

  • Each source gets a unique 64-character webhook secret, auto-generated and encrypted at rest
  • Secrets can be regenerated at any time from the source settings (old secret stops working immediately)
  • Request payloads are encrypted in the logs for privacy
  • Signature verification uses timing-safe comparison to prevent timing attacks
  • Sources and codes can be individually disabled without deleting them

Was this helpful?

Let us know how we can improve our documentation.

contact_support

Still need help?

Our support team is available to assist you with any questions or technical issues.

Contact Support arrow_forward
feedback

Suggest an edit

Did you find an error or missing information? Help us improve our documentation.

Edit on GitHub open_in_new