API reference
API reference
One HTTPS API with JSON in and out. Your backend signs requests to the App API with an app key; partners call the partner API with a Bearer key. The pages below list every endpoint.
Base URL
https://api.flowgroup.seThe same host serves the sandbox and production: the key you use decides the environment. The full API is also described as an OpenAPI 3.1 document at /v1/openapi.json, generated from the same schemas the API validates with.
App API
For your backend: events, invite codes and claims, referral growth, account links and the share studio.
Partner API
For publishers and agencies: links, conversions, commissions, payouts and reports.
Signed requests
Every App API request is signed with an app key: a key id (fg_test_… or fg_live_…) and a secret, issued per app and environment.
x-fg-keyheaderrequiredx-fg-timestampheaderrequiredx-fg-signatureheaderrequiredLower-case hex HMAC-SHA256(secret, "<timestamp>.<raw body>"). A GET request signs an empty body.
const signature = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');Sign the exact bytes you send, and sign again for every retry. Events has full examples in curl and Node; the Node SDK signs for you. Reward webhooks are signed the same way in the other direction, see Webhooks.
Partner API keys
Partners create keys on the Developers page of their portal and send them as a Bearer token:
Authorization: Bearer fa_live_…Keys start with fa_live_ (production data) or fa_test_ (sandbox data: test conversions and commissions that are never paid). Each key has scopes, and an endpoint answers 403 insufficient_scope when the key lacks the one it needs:
links:readscopelinks:writescopeconversions:readscopereports:readscopeA revoked, expired or unknown key answers 401 invalid_api_key, and the keys of a suspended account answer 403 partner_suspended.
Errors
Every error has the same shape:
{
"error": {
"code": "code_inactive",
"message": "code_inactive"
}
}Branch on code, which is stable; message is for people and may change. A 400 validation_error carries details that mirror your request: properties.<field>.errors lists what is wrong with each field.
400Bad Requestvalidation_error, or a malformed body. Fix the request before sending it again.401Unauthorized403Forbiddeninsufficient_scope, partner_suspended, membership_required).404Not Foundnot_found for an unknown code.409Conflictalready_attributed, production_only).429Too Many RequestsRetry-After seconds.5xxServer errorRate limits
The default limits:
App API1,200 per minute per keyOn /v1/events, /v1/attributions, /v1/affiliates/* and /v1/referrals/*. Answers 429 too_many_attempts. Batch events, up to 500 per request.
Partner API120 per minute per keyAnswers 429 rate_limited. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset.
Pagination
Partner API lists are cursor-paginated, newest first. Pass limit (1–100, default 50) and, for the next page, the cursor from the previous response:
{ "items": [], "hasMore": true, "nextCursor": "MjAyNi0xMS0wOVQxMjozMTowNy40MTJa…" }A cursor is opaque: pass it back unchanged. When hasMore is false, nextCursor is null and you have everything.
Versioning
The API is versioned in the path (/v1). Within a version, changes are additive: new endpoints, new optional parameters, new fields in responses and new event and webhook types. Ignore fields you don't know, and treat enums as open. A change that could break a working integration gets a new version.