HeldSway

Reports & Analytics API

Generate aggregate performance statistics, commission time-series charts, top-earner leaderboards, campaign distribution, payout summaries, and detailed coupon redemption reports.


Authentication & Scopes

All endpoints require a Bearer access token with the reports:read scope. See Authentication.

Authorization: Bearer <access_token>

Endpoints Summary

Core Performance Reports

Method Path Scope Throttle Description
GET /v1/reports/stats reports:read 120/min Summary stat cards with period-over-period trend percentages
GET /v1/reports/commission-chart reports:read 120/min Daily/weekly/monthly commission time-series chart
GET /v1/reports/top-affiliates reports:read 120/min Top-performing affiliates ranked by earnings
GET /v1/reports/clicks-by-campaign reports:read 120/min Traffic distribution grouped by campaign tag
GET /v1/reports/payouts reports:read 120/min Payout history report
GET /v1/reports/export reports:read 30/min Export performance metrics as CSV

Coupon Performance Reports

Method Path Scope Throttle Description
GET /v1/reports/coupons/stats reports:read 120/min Coupon summary metrics with period trends
GET /v1/reports/coupons/redemptions-chart reports:read 120/min Daily coupon redemptions chart data
GET /v1/reports/coupons/top reports:read 120/min Top coupons ranked by revenue
GET /v1/reports/coupons/performance reports:read 120/min Paginated coupon performance breakdown
GET /v1/reports/coupons/{id}/conversions reports:read 120/min Conversions generated by a specific coupon
GET /v1/reports/coupons/links reports:read 120/min Referral links attached to coupons
GET /v1/reports/coupons/export reports:read 30/min Export coupon report as CSV

Performance analytics and payout overview for affiliate programs. Retrieve stat cards with trend indicators, time-series commission charts, affiliate leaderboards, campaign breakdowns, payout history, and full CSV exports.


All endpoints require a Bearer access token with the appropriate scope. See Prerequisites & Authentication.

Scope Access
reports:read Stats, charts, tables, payouts, export

Endpoints

Method Path Scope Throttle Description
GET /v1/reports/stats reports:read 120/min Headline stat cards with trend percentages
GET /v1/reports/commission-chart reports:read 120/min Commission over time (daily/weekly/monthly)
GET /v1/reports/top-affiliates reports:read 120/min Top affiliates ranked by earnings
GET /v1/reports/clicks-by-campaign reports:read 120/min Click counts grouped by referral link campaign
GET /v1/reports/coupons/stats reports:read 120/min Coupon headline stats with trends
GET /v1/reports/coupons/redemptions-chart reports:read 120/min Daily coupon redemptions
GET /v1/reports/coupons/top reports:read 120/min Top coupons by revenue
GET /v1/reports/coupons/performance reports:read 120/min Coupon performance table (paginated)
GET /v1/reports/coupons/{id}/conversions reports:read 120/min Latest conversions for one coupon
GET /v1/reports/coupons/links reports:read 120/min Referral links with a coupon attached
GET /v1/reports/coupons/export reports:read 30/min CSV export of the coupon performance table
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

All endpoints accept date range filters. When omitted, defaults to the last 30 days.

Parameter Type Required Default Constraints Description
from date No 30 days ago YYYY-MM-DD, must be <= to Start of date range (inclusive)
to date No today YYYY-MM-DD, must be >= from End of date range (inclusive)
location_id string No — max:64 Filter metrics by store location ID
source_domain string No — max:255 Filter metrics by origin storefront domain

Report Stats — GET /v1/reports/stats

Returns four headline metrics with percentage trends compared to the previous period of equal length.

Example

curl "https://api.heldsway.com/api/v1/reports/stats?from=2026-03-17&to=2026-04-16" \
  -H "Authorization: Bearer <access_token>"

Success Response — HTTP 200

{
  "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",
      "location_id": null,
      "source_domain": null
    }
  }
}

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 Attributed conversions (every status except cancelled; rejected and $0 orders count) ÷ sessions × 100, rounded to 1 decimal, where sessions is the same session count as total_referral_clicks below. Can read above 100% if a session converts more than once in range
total_referral_clicks integer Distinct visiting sessions in the date range (MetricsService::sessions()), not a raw click count despite the name. Counted from tracking_pageviews distinct sessions first, falling back to a distinct-session count from tracking_clicks, then to a raw tracking_clicks row count only when neither table has session-tagged rows in 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. Returns 0.0 when 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

curl "https://api.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

{
  "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

curl "https://api.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

{
  "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",
      "location_id": null,
      "source_domain": null
    }
  }
}

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 Conversions attributed to this affiliate in the range, every status including cancelled, $0 orders included
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

curl "https://api.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

{
  "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",
      "location_id": null,
      "source_domain": null
    }
  }
}

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

curl "https://api.heldsway.com/api/v1/reports/payouts?status=completed&per_page=10" \
  -H "Authorization: Bearer <access_token>"

Success Response — HTTP 200

{
  "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

curl "https://api.heldsway.com/api/v1/reports/export?from=2026-04-01&to=2026-04-16&sections=stats,top_affiliates" \
  -H "Authorization: Bearer <access_token>" \
  -o affiliate-reports.csv

Success Response — HTTP 200

Content-Type: text/csv Content-Disposition: attachment; filename="affiliate-reports-2026-04-16.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:00

CSV 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 sections is specified, only the requested sections appear
  • Unknown section keys in the sections parameter are silently ignored
  • Payout timestamps use YYYY-MM-DD HH:MM:SS format (not ISO 8601)

Payout Status Workflow

  ┌─────────┐
  │ pending  │ ← initial state (payout recorded)
  └────┬─────┘
       │
  ┌────┴────┐
  ▼         ▼
┌───────────┐ ┌────────┐
│ completed │ │ failed │
└───────────┘ └────────┘
  • **pending**: Payout has been recorded but not yet processed
  • **completed**: Payment was successfully processed (paid_at is set)
  • **failed**: Payment processing failed

Error Reference

HTTP Status Error Code Cause
401 UNAUTHORIZED Missing or invalid Bearer token
402 SUBSCRIPTION_REQUIRED The business has no active subscription (see Error Reference → Subscription Required (402))
403 FORBIDDEN Token does not have reports:read scope
403 FEATURE_NOT_AVAILABLE GET /v1/reports/export and GET /v1/reports/coupons/export only — the plan does not include CSV export
422 — Invalid query parameters (bad date format, invalid granularity, etc.). Report endpoints validate with Form Requests, so the body is Laravel’s standard validation response with no code
429 RATE_LIMIT_EXCEEDED Rate limit exceeded
500 REPORT_ERROR Internal error during report generation

Error Response Format

A validation failure returns Laravel’s standard validation body:

{
  "message": "The selected granularity is invalid.",
  "errors": {
    "granularity": ["The selected granularity is invalid."]
  }
}

Every other error uses the standard envelope (see Error Reference in the Affiliate Management API section).


Rate Limiting

Endpoint Group Limit
Read endpoints (stats, chart, tables, payouts, coupon reports) 120 requests/min
Export (/v1/reports/export, /v1/reports/coupons/export) 30 requests/min

Exceeding the limit returns HTTP 429 Too Many Requests with error code RATE_LIMIT_EXCEEDED.

Coupon Reports

Coupon (discount code) performance for the business: headline stats, a daily redemptions chart, top coupons by revenue, a paginated performance table, per-coupon conversion drill-down, coupon-linked referral links, and a CSV export. Source: Modules\AffiliateSupport\Http\Controllers\Api\CouponReportApiController, backed by ReportingService.

Every coupon report endpoint accepts the date range below; the other parameters are listed per endpoint.

Parameter Type Required Default Constraints Description
from date No 30 days ago YYYY-MM-DD, must be <= to Start of the range (inclusive, from 00:00:00)
to date No today YYYY-MM-DD, must be >= from End of the range (inclusive, to 23:59:59)

A redemption is a conversion of this business that has a discount code, is not cancelled, and has converted_at inside the range ($0 orders count). Gross sales add each conversion’s subtotal, or its order_total when subtotal is 0 or empty.


Coupon Stats

Headline coupon metrics with trends against the previous period of equal length (source: CouponReportApiController::stats()).

GET /v1/reports/coupons/stats

Required scope: reports:read Rate limit: 120 requests/minute

Query Parameters

from, to only (rules from CouponReportStatsRequest::rules()).

Example Request

curl "https://api.heldsway.com/api/v1/reports/coupons/stats?from=2026-03-17&to=2026-04-16" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "Coupon report statistics retrieved.",
  "data": {
    "total_redemptions": 12,
    "gross_sales": 1840.5,
    "net_revenue": 1650.25,
    "total_discount": 190.25,
    "total_commission": 184.05,
    "avg_order_value": 153.38,
    "conversion_rate": 40,
    "trends": {
      "total_redemptions": 20,
      "gross_sales": 12.4,
      "net_revenue": 10.1,
      "total_discount": 35.2,
      "total_commission": 12.4,
      "avg_order_value": -6.3,
      "conversion_rate": 100
    },
    "filters": { "from": "2026-03-17", "to": "2026-04-16" }
  }
}

Response Fields

Field Type Description
total_redemptions integer Number of redemptions in the range
gross_sales number Gross sales of those redemptions
total_discount number Sum of discount_applied
net_revenue number gross_sales − total_discount
total_commission number Sum of commission_amount
avg_order_value number gross_sales ÷ total_redemptions (0 when there are none)
conversion_rate number Redemptions as a percentage of all the business’s non-cancelled conversions in the range, coupon or not
trends.* number Percentage change of each metric against the previous period, rounded to 1 decimal. When the previous value is 0: 100 if the current value is above 0, else 0
filters.from, filters.to string Applied range

Error Responses

Status Code Description
422 — Bad date format or from after to. Form Request failure: Laravel’s standard validation body, no code
500 REPORT_ERROR Failed to fetch coupon report statistics

Coupon Redemptions Chart

Daily count of redemptions across the range (source: CouponReportApiController::redemptionsChart()).

GET /v1/reports/coupons/redemptions-chart

Required scope: reports:read Rate limit: 120 requests/minute

Query Parameters

from, to only (rules from CouponReportStatsRequest::rules()).

Example Request

curl "https://api.heldsway.com/api/v1/reports/coupons/redemptions-chart?from=2026-04-01&to=2026-04-03" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "Coupon redemptions chart data retrieved.",
  "data": {
    "labels": ["Apr 01", "Apr 02", "Apr 03"],
    "data": [2, 0, 5],
    "filters": { "from": "2026-04-01", "to": "2026-04-03" }
  }
}

labels has one M d entry for every day in the range; days without redemptions have 0 in data.

Error Responses

Status Code Description
422 — Bad date format or from after to. Form Request failure: Laravel’s standard validation body, no code
500 REPORT_ERROR Failed to fetch coupon redemptions chart data

Top Coupons

Coupons ranked by revenue in the range (source: CouponReportApiController::top()).

GET /v1/reports/coupons/top

Required scope: reports:read Rate limit: 120 requests/minute

Query Parameters

Rules from CouponReportTopRequest::rules(), plus from / to.

Parameter Type Required Default Constraints Description
limit integer No 10 1–50 Number of coupons to return

Example Request

curl "https://api.heldsway.com/api/v1/reports/coupons/top?limit=3" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "Top coupons by revenue retrieved.",
  "data": {
    "labels": ["SUMMER20", "JANE15", "WELCOME10"],
    "data": [980.5, 540, 320.75],
    "filters": { "from": "2026-03-17", "to": "2026-04-16" }
  }
}

labels[i] is a coupon code and data[i] the revenue of its redemptions — per order, subtotal (or order_total when subtotal is 0 or empty) minus discount_applied, never below 0 per order — ordered by revenue descending.

Error Responses

Status Code Description
422 — Bad date or limit outside 1–50. Form Request failure: Laravel’s standard validation body, no code
500 REPORT_ERROR Failed to fetch top coupons

Coupon Performance

Paginated table of the business’s discount codes (soft-deleted codes excluded) with their redemption totals in the range (source: CouponReportApiController::performance()). Only redemptions attributed to an affiliate are counted here; codes with no redemptions are listed with zeros.

GET /v1/reports/coupons/performance

Required scope: reports:read Rate limit: 120 requests/minute

Query Parameters

Rules from CouponReportPerformanceRequest::rules(), plus from / to.

Parameter Type Required Default Constraints Description
status string No — active or expired active: active and not past expires_at. expired: inactive, or past expires_at
affiliate_ids[] integer[] No — integers Only codes bound to these affiliates
search string No — max 100 Case-insensitive substring match on the code
sort_by string No gross_sales redemptions, gross_sales, discount_given, commission, first_used, last_used, dc.code Sort column (dc.code sorts by code)
sort_dir string No desc asc or desc Sort direction
per_page integer No 25 1–100 Page size
page integer No 1 ≥ 1 Page number

Example Request

curl "https://api.heldsway.com/api/v1/reports/coupons/performance?status=active&sort_by=redemptions&per_page=10" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "Coupon performance table retrieved.",
  "data": {
    "items": [
      {
        "id": 57,
        "code": "JANE15",
        "affiliate_name": "Jane Smith",
        "affiliate_code": "JANE2026",
        "type": "percentage",
        "value": "15.00",
        "redemptions": 4,
        "gross_sales": 540,
        "discount_given": 81,
        "net_revenue": 459,
        "commission": 68.85,
        "aov": 135,
        "first_used": "2026-03-20 14:03:11",
        "last_used": "2026-04-12 09:41:02",
        "is_active": true,
        "expires_at": null
      }
    ],
    "meta": { "current_page": 1, "per_page": 10, "total": 1, "last_page": 1 },
    "filters": { "from": "2026-03-17", "to": "2026-04-16" }
  }
}

Response Fields

Field Type Description
items[].id, code, type, value, is_active, expires_at mixed The discount code’s own columns (value is the stored decimal string; expires_at a database timestamp string or null)
items[].affiliate_name, affiliate_code string|null Name and referral code of the affiliate the code is bound to
items[].redemptions integer Redemptions in the range
items[].gross_sales, discount_given, net_revenue, commission number Totals in the range; net_revenue = gross_sales − discount_given
items[].aov number gross_sales ÷ redemptions (0 when there are none)
items[].first_used, last_used string|null First and last converted_at in the range, as database timestamp strings
meta object Pagination: current_page, per_page, total, last_page

Error Responses

Status Code Description
422 — Invalid filter, sort or pagination value. Form Request failure: Laravel’s standard validation body, no code
500 REPORT_ERROR Failed to fetch coupon performance table

Coupon Conversions (Drill-down)

The latest 100 conversions that used one coupon (source: CouponReportApiController::drillDown()). Only conversions of this business that are attributed to an affiliate, not cancelled, and fall inside the range are returned, newest first ($0 orders included). An unknown coupon ID returns an empty list, not 404.

GET /v1/reports/coupons/{id}/conversions

Required scope: reports:read Rate limit: 120 requests/minute

Path Parameters

Parameter Type Description
id integer Discount code ID (items[].id from Coupon Performance)

Query Parameters

from, to only (rules from CouponReportStatsRequest::rules()).

Example Request

curl "https://api.heldsway.com/api/v1/reports/coupons/57/conversions" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "Coupon drill-down data retrieved.",
  "data": {
    "conversions": [
      {
        "order_id": "ORD-10492",
        "subtotal": 150,
        "discount_applied": 22.5,
        "commission_amount": 19.13,
        "status": "approved",
        "converted_at": "2026-04-12T09:41:02.000000Z",
        "affiliate_code": "JANE2026"
      }
    ],
    "filters": { "from": "2026-03-17", "to": "2026-04-16" }
  }
}

subtotal falls back to order_total when the conversion has no subtotal. status is a conversion status value (see module-8 §9.3 enums).

Error Responses

Status Code Description
422 — Bad date format or from after to. Form Request failure: Laravel’s standard validation body, no code
500 REPORT_ERROR Failed to fetch coupon drill-down data

Referral links that have a discount code attached, ranked by clicks in the range (source: CouponReportApiController::links() → ReportingService::couponLinksPerformance()).

GET /v1/reports/coupons/links

Required scope: reports:read Rate limit: 120 requests/minute

Query Parameters

Rules from CouponReportLinksRequest::rules(), plus from / to.

Parameter Type Required Default Constraints Description
discount_code_id integer No — ≥ 1 Only links carrying this discount code
affiliate_id integer No — ≥ 1 Only links of this affiliate
limit integer No 20 1–100 Number of links to return

Example Request

curl "https://api.heldsway.com/api/v1/reports/coupons/links?discount_code_id=57" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "Coupon-linked referral links retrieved.",
  "data": {
    "items": [
      {
        "slug": "jane-promo",
        "destination_url": "https://example.com/promo",
        "campaign": "Spring 2026",
        "product_id": null,
        "product_name": null,
        "affiliate_code": "JANE2026",
        "discount_code": { "id": 57, "code": "JANE15", "type": "percentage", "value": "15.00" },
        "clicks": 40,
        "conversions": 4,
        "conversion_rate": 10,
        "revenue": 540,
        "commission": 68.85
      }
    ],
    "filters": { "from": "2026-03-17", "to": "2026-04-16" }
  }
}

Response Fields

Field Type Description
items[].clicks integer Distinct clicks on the link that landed in the range
items[].conversions integer Distinct conversions from those clicks that are attributed and not cancelled
items[].conversion_rate number conversions ÷ clicks × 100, rounded to 1 decimal
items[].revenue number Revenue of those conversions: per order, subtotal (or order_total when subtotal is 0 or empty) minus discount_applied, never below 0 per order
items[].commission number The stored commission_amount when positive; otherwise an estimate from the link’s best active commission rule, then the affiliate’s rate, then the business default rate. Suppressed commissions count as 0

A query failure is logged and returns 200 with an empty items array.

Error Responses

Status Code Description
422 — Invalid date, ID or limit. Form Request failure: Laravel’s standard validation body, no code

Export Coupon Report

Download the coupon performance table as CSV (source: CouponReportApiController::export()). It applies the same filters as Coupon Performance but ignores sort_by, sort_dir, per_page and page: rows are ordered by gross sales descending, capped at 10,000.

GET /v1/reports/coupons/export

Required scope: reports:read Rate limit: 30 requests/minute

Also requires the real-time-dashboard-csv-export plan feature.

Query Parameters

Same as Coupon Performance (CouponReportPerformanceRequest); only from, to, status, affiliate_ids[] and search affect the output.

Example Request

curl "https://api.heldsway.com/api/v1/reports/coupons/export?status=active" \
  -H "Authorization: Bearer <access_token>" \
  -o coupon-reports.csv

Success Response — 200 OK

Content-Type: text/csv Content-Disposition: attachment; filename="coupon-reports-2026-04-16.csv"

Code,Affiliate,Type,Value,Redemptions,"Gross Sales","Discount Given","Net Revenue",Commission,AOV,"First Used","Last Used",Status
JANE15,"Jane Smith",percentage,15.00,4,540,81,459,68.85,135,2026-03-20T14:03:11Z,2026-04-12T09:41:02Z,Active

First Used and Last Used are the first and last converted_at in the range, in UTC, as ISO 8601 with a Z suffix (empty when the code has no redemptions). Status is Active or Inactive from the code’s is_active flag only; an active code past its expiry still exports as Active.

Error Responses

Status Code Description
403 FEATURE_NOT_AVAILABLE The plan does not include real-time-dashboard-csv-export
422 — Invalid filter value. Form Request failure: Laravel’s standard validation body, no code

The export has no error handler of its own: a failure while building the rows returns Laravel’s generic 500 JSON body.