Appearance
Operator Reporting API
You build one read-only endpoint on your server. It returns daily totals for each affiliate. DataFlair Stats calls it on a schedule and stores the numbers. Operators see every affiliate in the app. An affiliate sees only their own rows.
What we send
DataFlair sends a GET with a date range and one credential header.
http
GET /api/reports/affiliate-daily?from=2026-02-01&to=2026-02-07 HTTP/1.1
Host: operator.example.com
X-API-Key: YOUR_KEY
Accept: application/jsonbash
curl "https://operator.example.com/api/reports/affiliate-daily?from=2026-02-01&to=2026-02-07" \
-H "X-API-Key: YOUR_KEY" \
-H "Accept: application/json"| Query parameter | Type | Required | Meaning |
|---|---|---|---|
from | YYYY-MM-DD | yes | The first day, inclusive. A UTC date. |
to | YYYY-MM-DD | yes | The last day, inclusive. A UTC date. |
DataFlair calls the URL you save on the program's Integrations page, exactly as saved. It removes any query string from that URL and adds from and to. The path /api/reports/affiliate-daily is the convention. A different path works when you save it.
If your API uses other names for from and to, or nests the rows in a different place, set a field mapping for the pull on the Integrations page. DataFlair reads the mapping for the parameter names and for the location of the rows array.
Authentication
Use a read-only credential. DataFlair sends it in one of these headers, according to the type you chose when you saved the connection:
| Type | Header |
|---|---|
| API key (recommended, the default) | X-API-Key: <key> |
| Bearer token | Authorization: Bearer <token> |
What you return
Status 200 with a JSON body:
json
{
"from": "2026-02-01",
"to": "2026-02-07",
"rows": [
{
"date": "2026-02-01",
"affiliate_id": "AFF-00042",
"registrations": 28,
"ftds": 7,
"deposit_amount": 840.00,
"commission_amount": 100.80
}
]
}rows may be empty. An empty array is a valid answer.
Fields
The response:
| Field | Type | Required | Meaning |
|---|---|---|---|
rows | array | yes | One object for each day and affiliate. |
from, to | string | no | The range you answered for. DataFlair reads them when they are present. |
Each row:
| Field | Type | Required | Meaning |
|---|---|---|---|
date | YYYY-MM-DD | yes | The reporting day of the row. |
affiliate_id | string | yes | The affiliate ID from the tracking link, for example AFF-00042. Return it exactly as you captured it. |
registrations | integer | one metric is required | Player accounts created. |
ftds | integer | one metric is required | First-time depositors. |
deposit_amount | number | one metric is required | The sum of deposits. |
commission_amount | number | one metric is required | The commission you attribute to the affiliate for the day. Leave it out or send null when you do not calculate it. |
clicks | integer | no | Clicks you track yourself. DataFlair tracks clicks on its own. |
At least one of registrations, ftds, clicks, deposit_amount, commission_amount, reported_ngr or kyc_verified_ftds must be on the first row of the response. Without one, DataFlair fails the whole pull as a schema mismatch. DataFlair also reads ggr, but it does not count toward that check. The Metrics page explains each metric.
DataFlair skips a row that has no date, no affiliate_id, or a date that is not in the form YYYY-MM-DD. It stores the other rows.
The affiliate ID must be one DataFlair generated. You do not create affiliate IDs. You capture them from the landing URL, as described on Tracking links.
Errors
| Status | What caused it | What DataFlair does |
|---|---|---|
200 | Success. | Reads the rows and stores them. |
400, and other 4xx | A bad request, or a date it could not read. | Records the run as failed. Does not retry. |
401, 403 | The credential is wrong or missing. | Records the run as failed. Does not retry. |
429 | Rate limited. | Waits for the Retry-After header, 10 seconds when the header is missing, at most 60 seconds. Then retries. |
5xx | A server error. | Waits 2 seconds, then 4 seconds, and retries. |
200 with no rows array, no date or affiliate_id on the first row, or no metric on the first row | The body does not match the contract. | Records the run as failed with a schema mismatch. Does not retry. |
Timeouts and retries
DataFlair waits up to 30 seconds for each request. It makes up to 3 attempts in one run. The waits are in the table above.
How DataFlair pulls it
Each program picks a schedule on the Integrations page:
| Schedule | When DataFlair calls your endpoint |
|---|---|
| Daily | Once a day at 03:00 UTC. |
| Hourly | Every hour, on the hour. |
| Manual | Only when an operator clicks Pull now. |
Every pull asks for a range of days, not one day. The range and how late data is corrected are on Timezone and backfill.
Test this
Save your endpoint and credential in the Pull API connection on the Integrations page. Click Test connection. DataFlair calls your endpoint for yesterday, and it reports what it received. Click Pull now to run a full pull.
Verify
Before you save the connection, run the conformance check against your endpoint. It replays the fixtures and links each failure to the section of this page that explains it.
Verify this endpoint
node tools/stats-check/bin/stats-check.mjs https://operator.example.com/api/reports/affiliate-daily --key YOUR_KEYNo playground here: the request would go to your own server. Run the checker.
The checker is not published to npm yet. See how to run it.