Agent skill

AppsFlyer Webhooks Skill

Receive and authenticate AppsFlyer Push API (raw data streaming) webhooks. Use when setting up an AppsFlyer Push API endpoint, debugging the custom authorization header AppsFlyer sends, or handling raw-data records such as install and in-app events like af_purchase. AppsFlyer does not sign payloads: there is no signature header or HMAC.

Install this skill

npx skills add hookdeck/webhook-skills --skill appsflyer-webhooks


AppsFlyer's Push API (raw data streaming) sends each attributed install and in-app event to your server as a raw-data record, in near real time. Choose POST: with GET, AppsFlyer puts the data in the query string.

When to Use This Skill

  • How do I receive AppsFlyer Push API webhooks?
  • How do I authenticate / verify an AppsFlyer webhook?
  • Why is my AppsFlyer authorization header check failing?
  • How do I handle install and af_purchase events?
  • How do I deduplicate AppsFlyer raw-data records?

No Signature: Custom Authorization Header Only

AppsFlyer does not sign payloads. There is no signature header, no HMAC and no timestamp. Instead you configure a custom authorization header (a header name and a token value) per app ID in the Push API settings, and AppsFlyer sends it on every request. Your endpoint compares the received value to the token you stored, in constant time.

WhatValue
Header nameWhatever you configured (this skill defaults to Authorization; env APPSFLYER_AUTH_HEADER_NAME)
Header valueThe token you configured (env APPSFLYER_AUTH_TOKEN), compared as the full value
MethodPOST with a JSON raw-data record

Tokens are set per app ID. If you stream several apps, either use one token for all of them or look up the expected token by app_id after parsing.

Verification (core)

const crypto = require('crypto');

// AppsFlyer sends the header name/value you configured. No HMAC, no signature.
function verifyAppsflyerToken(received, expected) {
  if (!received || !expected) return false;            // fail closed
  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b); // length guard first
}

// Express: req.headers[(process.env.APPSFLYER_AUTH_HEADER_NAME || 'authorization').toLowerCase()]

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

AppsFlyer has no SDK with a webhook verification helper, so every example does this compare directly (crypto.timingSafeEqual in Node, hmac.compare_digest in Python). Authenticate before you parse the JSON body.

Common Event Types

The event name is the record's event_name field.

event_nameMeaning
installAn attributed install
af_purchaseStandard in-app purchase event
af_complete_registrationStandard in-app registration event
your own event nameIn-app events are named by your app, so the set differs per app

Every record includes appsflyer_id, app_id and event_time. There is no unique event ID, so build an idempotency key from appsflyer_id, event_name and event_time. See references/overview.md.

Environment Variables

APPSFLYER_AUTH_TOKEN=your_shared_secret_token     # the header value you configured
APPSFLYER_AUTH_HEADER_NAME=Authorization          # the header name you configured

With no token set, the examples reject every request with a 500 rather than accept unauthenticated webhooks.

Local Development

npx hookdeck-cli listen 3000 appsflyer --path /webhooks/appsflyer

No account required — the CLI creates a guest account on first run and gives you a public HTTPS URL plus a web UI for inspecting requests (including your custom header). Use port 8000 for the FastAPI example.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 2, 2026

View on GitHub →