API reference · App API
Events
Report what your users do, server to server. Registrations are attributed to the link or code that brought the user; later events earn commissions and rewards.
Send events
/v1/eventsReports one event, or a batch of up to 500 as { "events": [...] }. Events are stored first and processed right after, so a 202 means received, not yet attributed or paid.
Deduplicated on (eventId, environment): sending an event again returns duplicate and changes nothing, so retry freely after a timeout or a 5xx.
user.registered decides who referred the user. Send it with the attributionKey from POST /v1/attributions and the new appUserId: the key is bound to that user and the referral it stands for wins. Every later event may then carry the key instead of appUserId. Sending the clickId or affiliateCode the user arrived with on user.registered still works too.
AuthSigned request with an app key, fg_live_… or fg_test_…
Request body · one of
Batch
1 to 500 events, processed in order.
eventsarray of objectsrequiredChild attributes
eventIdstringrequiredYour stable id for this occurrence, e.g. registered:<user id> or order:<order id>. Sending it again is safe: it is accepted once.
typestringrequiredDotted event type, e.g. user.registered. Commission and reward rules trigger on it.
versionintegerVersion of your payload for this type. Defaults to 1.
occurredAtstringrequiredWhen it happened in your system, ISO 8601 with Z or an offset.
appUserIdstringYour pseudonymous id for the user. Never an email address or phone number. Required unless attributionKey names a user already.
attributionKeystringThe key from POST /v1/attributions. On user.registered, with appUserId, it binds the key to the new user and attributes them to that referral. On any event after that it may stand in for appUserId.
clickIdstringfg_click from the landing URL, universal link or Play install referrer. A value that is not a click id is ignored, never refused.
affiliateCodestringThe publisher’s code: fg_ref from the same places, or a code the user typed.
reversesEventIdstringThe eventId of the event this one undoes, for refunds and rejections.
amountintegerInteger minor units (cents). Percentage rules pay a share of it.
currencystringISO 4217 code in capitals. Send it whenever amount is set.
Matches ^[A-Z]{3}$
metadataobjectFree-form facts about the event. Rules can match on them; country (ISO alpha-2) on a registration picks the market.
Event
Something a user did in your app.
eventIdstringrequiredYour stable id for this occurrence, e.g. registered:<user id> or order:<order id>. Sending it again is safe: it is accepted once.
typestringrequiredDotted event type, e.g. user.registered. Commission and reward rules trigger on it.
versionintegerVersion of your payload for this type. Defaults to 1.
occurredAtstringrequiredWhen it happened in your system, ISO 8601 with Z or an offset.
appUserIdstringYour pseudonymous id for the user. Never an email address or phone number. Required unless attributionKey names a user already.
attributionKeystringThe key from POST /v1/attributions. On user.registered, with appUserId, it binds the key to the new user and attributes them to that referral. On any event after that it may stand in for appUserId.
clickIdstringfg_click from the landing URL, universal link or Play install referrer. A value that is not a click id is ignored, never refused.
affiliateCodestringThe publisher’s code: fg_ref from the same places, or a code the user typed.
reversesEventIdstringThe eventId of the event this one undoes, for refunds and rejections.
amountintegerInteger minor units (cents). Percentage rules pay a share of it.
currencystringISO 4217 code in capitals. Send it whenever amount is set.
Matches ^[A-Z]{3}$
metadataobjectFree-form facts about the event. Rules can match on them; country (ISO alpha-2) on a registration picks the market.
Returns · 202
One entry per event sent, in the order sent.
acceptedarray of objectsrequiredChild attributes
eventIdstringrequiredstatusstringrequiredduplicate: this eventId was received before, and nothing was processed twice. rejected: its attributionKey could not be used (see reason); the event was not stored, so the same eventId may be sent again once fixed.
One of accepted duplicate rejected
reasonstringWhy a rejected event was not taken: - unknown_key: no key like it belongs to your app in this environment. - key_expired: the key was never registered, and its attribution window has closed. - key_bound_to_other_user: the key was registered for another appUserId. - key_not_registered: the key is not bound to a user yet. Only user.registered with an appUserId binds it. - key_declined: you declined the key.
One of unknown_key key_expired key_bound_to_other_user key_not_registered key_declined
Errors
- 400
validation_error The request does not match the schema.
detailssays which fields failed and why.- 400
renamed_field An event still sends
referralCode, which is nowaffiliateCode. Nothing is stored; in a batch,details.eventslists the positions of those events.- 401
missing_signature One of
x-fg-key,x-fg-timestampandx-fg-signatureis missing.- 401
invalid_timestamp x-fg-timestampis not Unix time in seconds.- 401
stale_timestamp x-fg-timestampis more than 300 seconds from the server clock. Sign each attempt again, and keep your clock in sync.- 401
invalid_key The key id is unknown or revoked, or the app is archived.
- 401
invalid_signature The signature does not match. Sign the exact bytes you send.
- 429
too_many_attempts Too many requests with this key in the last minute. Wait
Retry-Afterseconds.
BODY='{"events":[{"eventId":"registered:u_8F2K","type":"user.registered","occurredAt":"2026-11-09T09:30:00Z","appUserId":"u_8F2K","attributionKey":"fga_k7qxa2mpw4ht9bvc3rnd8ezj","metadata":{"country":"SE"}}]}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$FG_SECRET" | sed 's/^.*= //')
curl -X POST "https://api.flowgroup.se/v1/events" \
-H "content-type: application/json" \
-H "x-fg-key: $FG_KEY_ID" \
-H "x-fg-timestamp: $TS" \
-H "x-fg-signature: $SIG" \
--data-raw "$BODY"import { createFlowGrowth } from '@flow/growth-sdk';
const fg = createFlowGrowth({
baseUrl: 'https://api.flowgroup.se',
keyId: process.env.FG_KEY_ID!, // fg_test_… in the sandbox
secret: process.env.FG_SECRET!,
});
const res = await fg.track({
type: 'user.registered',
eventId: 'registered:u_1042', // your own stable id: retries come back as "duplicate"
appUserId: 'u_1042',
attributionKey, // from fg.createAttribution: confirms the referral
});
// later events: the key alone says who it is about
await fg.track({ type: 'purchase.completed', eventId: 'order:o-77120', attributionKey, amount: 1999, currency: 'USD' });
// many at once (500 per request, split for you): await fg.trackBatch([...]){
"accepted": [
{
"eventId": "registered:u_8F2K",
"status": "accepted"
},
{
"eventId": "order:o-77120",
"status": "rejected",
"reason": "key_not_registered"
}
]
}