Appearance
Affiliate Reports API
GET /v1/reports/*
Fetch affiliate analytics data for a business. Supports multiple data sections (stats, trends, charts, tables, and paginated activity feeds), each with optional metric filtering and date range scoping.
Authentication
All endpoints require a Bearer access token with the reports:read scope.
text
Authorization: Bearer <access_token>Endpoints
| Method | Path | Scope | Throttle | Description |
|---|---|---|---|---|
GET | /v1/reports/stats | reports:read | 120/min | Aggregate statistics and trends |
GET | /v1/reports/commission-chart | reports:read | 120/min | Time-series data for charting |
GET | /v1/reports/top-affiliates | reports:read | 120/min | Top affiliates by earnings |
GET | /v1/reports/clicks-by-campaign | reports:read | 120/min | Clicks grouped by campaign |
GET | /v1/reports/payouts | reports:read | 120/min | Payout history (paginated) |
GET | /v1/reports/export | reports:read | 30/min | CSV export of report data |
Common Query Parameters
Most report endpoints share these optional date range filters:
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
from | date | 30 days ago | YYYY-MM-DD | Start of date range |
to | date | today | YYYY-MM-DD, must be >= from | End of date range |
Report Stats — GET /v1/reports/stats
Returns aggregated summary statistics and period-over-period trend data.
Example
bash
curl "https://heldsway.com/api/v1/reports/stats?from=2026-03-17&to=2026-04-16" \
-H "Authorization: Bearer <access_token>"Success Response — HTTP 200
json
{
"success": true,
"message": "Report statistics retrieved.",
"data": {
"total_commission_paid": 28.00,
"pending_payouts": 14.00,
"conversion_rate": 3.2,
"total_referral_clicks": 2,
"trends": {
"commission_paid": 14.0,
"pending_payouts": 0.0,
"conversion_rate": 1.1,
"referral_clicks": -8.3
},
"filters": {
"from": "2026-03-17",
"to": "2026-04-16"
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
total_commission_paid | number | Sum of commission_amount from approved conversions in the date range |
pending_payouts | number | Approved commission minus completed payouts (owed but not yet paid) |
conversion_rate | number | Percentage of referral clicks that resulted in a conversion |
total_referral_clicks | integer | Total distinct referral click sessions in the date range |
trends.commission_paid | number | % change vs previous period (positive = increase) |
trends.pending_payouts | number | Reserved — always 0.0 (trend requires payout history) |
trends.conversion_rate | number | Absolute percentage-point change vs previous period |
trends.referral_clicks | number | % change vs previous period |
filters.from | string | Applied start date (YYYY-MM-DD) |
filters.to | string | Applied end date (YYYY-MM-DD) |
Trend Calculation
Trends compare the requested date range against the immediately preceding period of equal length. For example, if from=2026-03-17 and to=2026-04-16 (31 days), the previous period is 2026-02-14 to 2026-03-16.
- Commission and clicks:
((current - previous) / previous) * 100, rounded to 1 decimal place. Returns0.0when the previous period value is zero. - Conversion rate: Absolute difference in percentage points (
current_rate - previous_rate), rounded to 1 decimal place.
Commission Chart — GET /v1/reports/commission-chart
Returns time-series data for rendering a commission-over-time bar chart. Supports daily, weekly, or monthly granularity.
Query Parameters
| Parameter | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
from | date | No | 30 days ago | YYYY-MM-DD | Start of date range |
to | date | No | today | YYYY-MM-DD | End of date range |
granularity | string | No | daily | daily, weekly, monthly | Time bucket size |
Example
bash
curl "https://heldsway.com/api/v1/reports/commission-chart?from=2026-04-01&to=2026-04-16&granularity=daily" \
-H "Authorization: Bearer <access_token>"Success Response — HTTP 200
json
{
"success": true,
"message": "Commission chart data retrieved.",
"data": {
"granularity": "daily",
"labels": ["Apr 01", "Apr 02", "Apr 03", "Apr 04", "Apr 05"],
"data": [4.50, 0.00, 12.00, 0.00, 7.00],
"filters": {
"from": "2026-04-01",
"to": "2026-04-16"
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
granularity | string | Applied granularity (daily, weekly, or monthly) |
labels | string[] | Human-readable time bucket labels |
data | number[] | Sum of commission_amount for each time bucket (2 decimal places) |
filters.from | string | Applied start date |
filters.to | string | Applied end date |
Label Format by Granularity
| Granularity | Format | Example |
|---|---|---|
daily | MMM DD | Apr 01 |
weekly | MMM DD – MMM DD | Apr 01 – Apr 07 |
monthly | MMM YYYY | Apr 2026 |
Empty time buckets are filled with 0.00 to ensure the chart has no gaps.
Top Affiliates — GET /v1/reports/top-affiliates
Returns the top-performing affiliates ranked by commission earned within the date range. Only affiliates with at least one conversion or click in the period are included.
Query Parameters
| Parameter | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
from | date | No | 30 days ago | YYYY-MM-DD | Start of date range |
to | date | No | today | YYYY-MM-DD | End of date range |
limit | integer | No | 10 | 1–50 | Maximum number of affiliates to return |
Example
bash
curl "https://heldsway.com/api/v1/reports/top-affiliates?from=2026-03-17&to=2026-04-16&limit=5" \
-H "Authorization: Bearer <access_token>"Success Response — HTTP 200
json
{
"success": true,
"message": "Top affiliates retrieved.",
"data": {
"items": [
{
"affiliate_id": 1,
"name": "Salman",
"referral_code": "SALMAN2026",
"earned": 140.00,
"clicks": 2,
"conversions": 5
}
],
"filters": {
"from": "2026-03-17",
"to": "2026-04-16"
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
items[].affiliate_id | integer | Affiliate record ID |
items[].name | string | Affiliate user's display name |
items[].referral_code | string | Affiliate's referral code |
items[].earned | number | Total commission earned in the date range (2 decimal places) |
items[].clicks | integer | Total referral clicks attributed to this affiliate |
items[].conversions | integer | Total conversions attributed to this affiliate |
filters.from | string | Applied start date |
filters.to | string | Applied end date |
Clicks by Campaign — GET /v1/reports/clicks-by-campaign
Returns referral click counts grouped by the campaign tag on referral links. All active campaigns for the business are included, even those with zero clicks in the date range.
Query Parameters
| Parameter | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
from | date | No | 30 days ago | YYYY-MM-DD | Start of date range |
to | date | No | today | YYYY-MM-DD | End of date range |
limit | integer | No | 10 | 1–50 | Maximum number of campaigns to return |
Example
bash
curl "https://heldsway.com/api/v1/reports/clicks-by-campaign?from=2026-03-17&to=2026-04-16" \
-H "Authorization: Bearer <access_token>"Success Response — HTTP 200
json
{
"success": true,
"message": "Clicks by campaign retrieved.",
"data": {
"items": [
{
"campaign": "mountain_rentals_brand",
"clicks": 0
},
{
"campaign": "women_skier_interest",
"clicks": 0
},
{
"campaign": "winter_sale",
"clicks": 0
},
{
"campaign": null,
"clicks": 2
}
],
"filters": {
"from": "2026-03-17",
"to": "2026-04-16"
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
items[].campaign | string|null | Campaign tag from the referral link (null = links with no campaign assigned) |
items[].clicks | integer | Total referral clicks for links with this campaign tag |
filters.from | string | Applied start date |
filters.to | string | Applied end date |
Results are ordered by clicks descending. The null campaign entry (representing links without a campaign tag) is always listed last.
Payouts — GET /v1/reports/payouts
Returns a paginated list of payout records with optional filtering by status and affiliate.
Query Parameters
| Parameter | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
from | date | No | 30 days ago | YYYY-MM-DD | Start of date range |
to | date | No | today | YYYY-MM-DD | End of date range |
status | string | No | all | pending, completed, failed | Filter by payout status |
affiliate_id | integer | No | all | Affiliate ID | Filter by affiliate |
per_page | integer | No | 15 | 1–100 | Items per page |
page | integer | No | 1 | min:1 | Page number |
Example
bash
curl "https://heldsway.com/api/v1/reports/payouts?status=completed&per_page=10" \
-H "Authorization: Bearer <access_token>"Success Response — HTTP 200
json
{
"success": true,
"message": "Payouts retrieved.",
"data": {
"items": [
{
"id": 1,
"affiliate_id": 1,
"affiliate_name": "Salman",
"amount": 28.00,
"currency": "USD",
"method": "bank_transfer",
"status": "completed",
"reference": "TXN-20260410-001",
"paid_at": "2026-04-10T14:30:00.000000Z",
"created_at": "2026-04-10T14:00:00.000000Z"
}
],
"meta": {
"current_page": 1,
"per_page": 15,
"total": 1,
"last_page": 1
},
"filters": {
"from": "2026-03-17",
"to": "2026-04-16"
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
items[].id | integer | Payout record ID |
items[].affiliate_id | integer | Affiliate record ID |
items[].affiliate_name | string | Affiliate user's display name |
items[].amount | number | Payout amount (2 decimal places) |
items[].currency | string | 3-letter currency code (default USD) |
items[].method | string|null | Payment method (bank_transfer, paypal, check, other) |
items[].status | string | pending, completed, or failed |
items[].reference | string|null | External transaction reference/ID |
items[].paid_at | string|null | ISO 8601 timestamp when payment was completed (null if pending/failed) |
items[].created_at | string | ISO 8601 timestamp of payout record creation |
meta.current_page | integer | Current page number |
meta.per_page | integer | Items per page |
meta.total | integer | Total matching records |
meta.last_page | integer | Last available page |
Date Range Behavior
Date range filters are applied differently based on payout status:
- Completed payouts: filtered by
paid_at(when the payment was made) - Pending/failed payouts: filtered by
created_at(when the payout was recorded)
This ensures completed payouts appear in the period they were paid, not when they were initially created.
Export Report — GET /v1/reports/export
Downloads a CSV file containing report data. By default exports all sections; use the sections parameter to select specific sections.
Query Parameters
| Parameter | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
from | date | No | 30 days ago | YYYY-MM-DD | Start of date range |
to | date | No | today | YYYY-MM-DD | End of date range |
sections | string | No | all | Comma-separated list | Sections to include in the export |
Available Sections
| Section Key | Description |
|---|---|
stats | Headline metrics with trend percentages |
commission_chart | Commission over time (daily granularity) |
top_affiliates | Top affiliates ranked by earnings |
clicks_by_campaign | Click counts grouped by campaign |
payouts | Payout history |
Example
bash
curl "https://heldsway.com/api/v1/reports/export?from=2026-04-01&to=2026-04-16§ions=stats,top_affiliates" \
-H "Authorization: Bearer <access_token>" \
-o affiliate-reports.csvSuccess Response — HTTP 200
Content-Type: text/csvContent-Disposition: attachment; filename="affiliate-reports-2026-04-16.csv"
csv
"--- Report Stats ---"
Metric,Value,Trend (%)
Total Commission Paid,28.00,14.0
Pending Payouts,14.00,0.0
Conversion Rate (%),3.2,1.1
Total Referral Clicks,2,-8.3
"--- Top Affiliates ---"
Name,Referral Code,Earned,Clicks,Conversions
Salman,SALMAN2026,140.00,2,5
"--- Clicks by Campaign ---"
Campaign,Clicks
mountain_rentals_brand,0
women_skier_interest,0
winter_sale,0
(no campaign),2
"--- Commission Over Time (daily) ---"
Date,Commission
Apr 01,4.50
Apr 02,0.00
Apr 03,12.00
"--- Payouts ---"
Affiliate,Amount,Currency,Method,Status,Reference,Paid At,Created At
Salman,28.00,USD,bank_transfer,completed,TXN-20260410-001,2026-04-10 14:30:00,2026-04-10 14:00:00CSV Structure
- Each section is separated by a header row in the format
--- Section Name --- - Sections appear in the order: stats, top affiliates, clicks by campaign, commission chart, payouts
- When
sectionsis specified, only the requested sections appear - Unknown section keys in the
sectionsparameter are silently ignored - Payout timestamps use
YYYY-MM-DD HH:MM:SSformat (not ISO 8601)
Payout Status Workflow
text
┌─────────┐
│ pending │ ← initial state (payout recorded)
└────┬────┘
│
┌────┴────┐
▼ ▼
┌─────────┐ ┌────────┐
│completed│ │ failed │
└─────────┘ └────────┘pending: Payout has been recorded but not yet processedcompleted: Payment was successfully processed (paid_atis set)failed: Payment processing failed
Error Reference
| HTTP Status | Error Code | Cause |
|---|---|---|
401 | UNAUTHORIZED | Missing or invalid Bearer token |
403 | FORBIDDEN | Token does not have reports:read scope |
422 | VALIDATION_ERROR | Invalid query parameters (bad date format, invalid granularity, etc.) |
429 | — | Rate limit exceeded |
500 | REPORT_ERROR | Internal error during report generation |
Error Response Format
json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"granularity": ["The selected granularity is invalid."]
}
}
}Rate Limiting
| Endpoint Group | Limit |
|---|---|
| Read endpoints (stats, chart, tables, payouts) | 120 requests/min |
| Export | 30 requests/min |
Exceeding the limit returns HTTP 429 Too Many Requests.
Get Link Stats
Returns aggregate statistics for all referral links in your business.
GET /v1/links/stats
- Required scope:
affiliates:read - Rate limit: 120 requests/minute
Example Request
bash
curl "https://heldsway.com/api/v1/links/stats" \
-H "Authorization: Bearer <access_token>"Success Response — 200 OK
json
{
"success": true,
"message": "OK",
"data": {
"total": 42,
"active": 38,
"inactive": 4,
"total_clicks": 1523
}
}Response Fields
| Field | Type | Description |
|---|---|---|
total | integer | Total number of referral links (active + inactive) |
active | integer | Links with is_active = true |
inactive | integer | Links with is_active = false |
total_clicks | integer | Sum of click_count across all links |
Note: Soft-deleted links are excluded from all counts.
total_clicksis derived from the denormalizedclick_countfield on each link, which is incremented in real time by the click tracking system.