Server-Side Event Tracking
Use this guide if your backend needs one Affonso endpoint for both revenue and non-revenue events.
With POST /v1/events, you can send:
- conversion events for paid purchases
- lead events for qualified signups
- trial events for trial starts
- milestone events for product or lifecycle steps such as onboarding, activation, or KYC completion
If you need the full request and response contract, see the Create Event API docs.
💡 Use POST /v1/events when your backend tracks more than just purchases.
If you only need a pure payment conversion flow, POST /v1/conversions is
still a good fit.
Before You Start
Make sure you have:
- An Affonso API key with
write:commissionspermission - At least one identifier to match the event:
referral_id,customer_id, orexternal_user_id - A stable upstream event ID for idempotency, especially for conversions
- Affiliate tracking already installed on your website
If you need the browser-side attribution setup first, start with How Affiliate Tracking Works and Signup & Lead Tracking.
What This Endpoint Does
Affonso accepts a normalized backend event and decides how to process it based
on event_type.
Supported event types
conversionfor paid salesleadfor lead-stage milestonestrialfor trial startsmilestonefor non-revenue lifecycle events
Required fields
Every request needs:
event_name- at least one matching identifier:
referral_id,customer_id, orexternal_user_id
Conversion events also require:
sale_amountsale_amount_currencyexternal_event_id
For all technical field details, see the Create Event API docs.
Quick Start
1. Pick the identifier you already control
Use the most stable identifier available in your system:
external_user_idif you want to match against the user ID in your own app or systemreferral_idif you already know the exact Affonso referralcustomer_idif you want to match against the customer ID from your payment provider or billing system, such as Stripeaffonso_idif your existing integration already uses it
Affonso only attributes the event if that identifier resolves to an existing referral in your team. If no referral matches, the request is rejected.
Recommended identifier flow
- Prefer
external_user_idwhen your backend has a stable user ID from your product or app - Use
referral_idwhen you already know the Affonso referral ID to credit - Use
customer_idwhen you want to match with the customer ID from your payment provider or billing system - If your existing integration already uses
affonso_id, you can keep using it - If you send multiple identifiers, they must resolve to the same referral
2. Send the event from your backend
Endpoint:
POST https://api.affonso.io/v1/eventsHeaders:
Authorization: Bearer sk_live_your_key_here
Content-Type: application/json3. Start with a trial or milestone event
This is a simple example for a trial start:
curl -X POST "https://api.affonso.io/v1/events" \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"event_name": "trial_started",
"event_type": "trial",
"customer_id": "cust_123",
"external_event_id": "evt_trial_123"
}'Typical use cases:
trial_starteddemo_bookedkyc_passedactivation_completed
4. Send conversion events when revenue happens
Use the same endpoint for purchases:
curl -X POST "https://api.affonso.io/v1/events" \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"event_name": "invoice_paid",
"event_type": "conversion",
"customer_id": "cust_123",
"sale_amount": 99,
"sale_amount_currency": "USD",
"external_event_id": "evt_2026_000145",
"product_ids": ["pro_monthly"],
"price_ids": ["price_123"],
"is_subscription": true,
"interval": "monthly"
}'How Responses Work
For event_type: "conversion", Affonso returns a conversion response similar
to POST /v1/conversions.
For lead, trial, and milestone, Affonso returns an event-processing
result that tells you:
- which referral was matched
- whether a commission was created
- which action was taken
- the resulting referral status
This makes it practical to use one ingestion surface while still handling revenue and non-revenue events differently in your own backend.
When To Use /v1/events
Choose POST /v1/events when:
- your backend tracks both purchase and product milestone events
- you want one endpoint for revenue and non-revenue attribution
- you need a normalized event API instead of separate custom flows
Choose a different path when:
- you only need purchase tracking: use Custom Backend Payment Provider Integration
- Segment is already your event hub: use Segment Track Events
Best Practices
- Use a stable
external_event_idso retries stay idempotent - Send
occurred_atif the upstream event happened earlier than delivery time - Prefer your own stable customer or user IDs over fragile temporary values
- Include
product_ids,price_ids,interval, andis_subscriptionon conversion events when available for better incentive matching - Keep raw provider IDs in
metadatafor debugging and reconciliation
Troubleshooting
Event was rejected because no user could be matched
Make sure you send at least one of:
referral_idexternal_user_idcustomer_id
If you need the full list of alternative field names for referral_id, use
the Create Event API docs.
Conversion event fails validation
Make sure your request includes:
sale_amountsale_amount_currencyexternal_event_id
Duplicate upstream retries create concern
Reuse the same external_event_id when retrying the exact same event. Affonso
uses that value for idempotency.
You need the exact request schema
Use the API reference:
Next Steps
- Create an Affonso API key
- Review the Create Event API docs
- If needed, harden browser attribution with Proxy Setup (Optional)


