HeldSway

Affiliates API

Manage affiliate registrations, profiles, lifecycle states (approve, suspend, reactivate, terminate), statistics, and exports.


Authentication & Scopes

All endpoints require a Bearer access token obtained via POST /v1/auth/token.

Scope Access
affiliates:read List, view profile, view aggregate & individual stats, and export affiliates
affiliates:write Create, update, delete, approve, suspend, reactivate, and terminate affiliates


List Affiliates

Retrieve a paginated list of all affiliates for your business.

GET /v1/affiliates

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

Query Parameters

Parameter Type Default Description
page integer 1 Page number
per_page integer 25 Results per page. Max 100
status string — Filter by status: pending | active | suspended | terminated
commission_type string — Filter by commission override: default (inherits business default) | custom (override set)
search string — Search across name, email, and referral code
sort string created_at Sort field: created_at | referral_code | status | commission_rate
direction string desc Sort direction: asc | desc

Example Request

curl "https://api.heldsway.com/api/v1/affiliates?status=active&per_page=10&sort=created_at&direction=desc" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "OK",
  "data": {
    "items": [
      {
        "id": 42,
        "referral_code": "ABC12345",
        "status": "active",
        "commission_rate": "10.00",
        "notes": null,
        "approved_at": "2026-04-01T09:00:00.000000Z",
        "suspended_at": null,
        "terminated_at": null,
        "created_at": "2026-03-15T14:22:00.000000Z",
        "affiliate_user": {
          "id": 7,
          "name": "Jane Doe",
          "email": "jane@example.com",
          "phone": null
        }
      }
    ],
    "meta": {
      "current_page": 1,
      "per_page": 10,
      "total": 87,
      "last_page": 9
    }
  }
}

Get Affiliate

Retrieve a single affiliate by ID.

GET /v1/affiliates/{id}

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

Path Parameters

Parameter Type Description
id integer Affiliate ID

Example Request

curl "https://api.heldsway.com/api/v1/affiliates/42" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "OK",
  "data": {
    "id": 42,
    "referral_code": "ABC12345",
    "status": "active",
    "commission_rate": "10.00",
    "notes": "VIP partner",
    "approved_at": "2026-04-01T09:00:00.000000Z",
    "suspended_at": null,
    "terminated_at": null,
    "created_at": "2026-03-15T14:22:00.000000Z",
    "affiliate_user": {
      "id": 7,
      "name": "Jane Doe",
      "email": "jane@example.com",
      "phone": "+14155552671"
    },
    "referral_links_count": 3
  }
}

Error Responses

Status Code Description
404 NOT_FOUND Affiliate does not exist for this business

Get Affiliate Stats

Returns aggregate counts of affiliates grouped by status for your business.

GET /v1/affiliates/stats

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

Example Request

curl "https://api.heldsway.com/api/v1/affiliates/stats" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "OK",
  "data": {
    "total": 120,
    "active": 85,
    "pending": 20,
    "suspended": 10,
    "terminated": 5
  }
}

Response Fields

Field Type Description
total integer Total number of affiliates across all statuses
active integer Affiliates with status = active
pending integer Affiliates with status = pending (awaiting approval)
suspended integer Affiliates with status = suspended
terminated integer Affiliates with status = terminated

Note: Soft-deleted affiliates are excluded from all counts. total equals the sum of all four status counts.



Get Affiliate Stats (Individual)

Returns performance statistics for a single affiliate: total earned commission, pending payouts, total orders, and referral clicks.

GET /v1/affiliates/{id}/stats

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

Path Parameters

Parameter Type Description
id integer Affiliate ID

Query Parameters

Parameter Type Default Description
date_from date — Start of date range (inclusive). Format: YYYY-MM-DD. Omit for all-time
date_to date — End of date range (inclusive). Format: YYYY-MM-DD. Must be ≥ date_from

Example Request — All-time stats

curl "https://api.heldsway.com/api/v1/affiliates/42/stats" \
  -H "Authorization: Bearer <access_token>"

Example Request — Date-filtered stats

curl "https://api.heldsway.com/api/v1/affiliates/42/stats?date_from=2026-04-01&date_to=2026-04-30" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "OK",
  "data": {
    "total_earned": 175.00,
    "pending_payout": 35.00,
    "total_orders": 12,
    "referral_clicks": 483
  }
}

Response Fields

Field Type Description
total_earned float Sum of commission_amount across all conversions (all statuses)
pending_payout float Sum of commission_amount for conversions with pending status only
total_orders integer Total number of conversions (all statuses)
referral_clicks integer Total number of tracking clicks

Note: When no date range is specified, all-time totals are returned. total_earned includes commissions in all statuses (pending, approved, rejected, cancelled). pending_payout only includes commissions awaiting approval.

Error Responses

Status Code Description
404 NOT_FOUND Affiliate does not exist for this business
422 — Invalid date format or date_to is before date_from. Form Request failure: Laravel’s standard validation body, no code (see Error Reference → Validation Errors (422))

Export Affiliates

Downloads affiliates as a CSV file. Supports the same filters as List Affiliates but without pagination — returns all matching records. An additional ids parameter allows exporting a specific selection of affiliates (e.g., from checkbox selection in the UI).

GET /v1/affiliates/export

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

Query Parameters

Parameter Type Required Constraints Description
ids string No Comma-separated integers Export only these affiliate IDs (e.g., 42,55,78). When provided, status and search are ignored — exact selection takes precedence
status string No pending, active, suspended, terminated Filter by status. Ignored when ids is set
commission_type string No default, custom Filter by commission type (default = business default, custom = override set)
search string No max:255 Search by name, email, or referral code. Ignored when ids is set
sort string No created_at, referral_code, status, commission_rate Sort field. Default: created_at
direction string No asc, desc Sort direction. Default: desc

Filter precedence: When ids is provided, the endpoint returns exactly those affiliates (scoped to your business) regardless of any status or search parameters. Sorting still applies.

Example — Export all affiliates

curl "https://api.heldsway.com/api/v1/affiliates/export" \
  -H "Authorization: Bearer <access_token>" \
  -o affiliates.csv

Example — Export only active affiliates

curl "https://api.heldsway.com/api/v1/affiliates/export?status=active&sort=referral_code&direction=asc" \
  -H "Authorization: Bearer <access_token>" \
  -o active-affiliates.csv

Example — Export selected affiliates by ID

curl "https://api.heldsway.com/api/v1/affiliates/export?ids=42,55,78" \
  -H "Authorization: Bearer <access_token>" \
  -o selected-affiliates.csv

Success Response — HTTP 200

Content-Type: text/csv Content-Disposition: attachment; filename="affiliates-export-2026-04-17.csv"

ID,Name,Email,Phone,Referral Code,Commission Rate,Status,Joined,Approved At,Suspended At,Terminated At,Notes
42,"Jane Doe",jane@example.com,+14155552671,ABC12345,10.00,active,"2026-03-15 14:22:00","2026-04-01 09:00:00",,,"VIP partner"
55,"John Smith",john@example.com,,XK9P2QR7,,pending,"2026-04-13 11:30:00",,,,

CSV Columns

Column Description
ID Unique affiliate ID
Name Affiliate’s full name (empty if no linked user)
Email Affiliate’s email address (empty if no linked user)
Phone Phone number (empty if not set)
Referral Code Unique tracking code used in referral links
Commission Rate Affiliate-specific override rate (%). Empty if inheriting business default
Status Current status: pending, active, suspended, or terminated
Joined When the affiliate record was created (YYYY-MM-DD HH:MM:SS)
Approved At When approved (empty if never approved)
Suspended At When suspended (empty if never suspended)
Terminated At When terminated (empty if not terminated)
Notes Internal notes (may contain newlines; properly CSV-escaped)

Error Responses

Status Code Description
401 UNAUTHORIZED Missing or invalid Bearer token
403 FORBIDDEN Token does not have affiliates:read scope
422 — Invalid ids format (each value must be a positive integer) or invalid filter values. Form Request failure: Laravel’s standard validation body, no code (see Error Reference → Validation Errors (422))



Create Affiliate

Create a new affiliate account directly. The affiliate is immediately set to active status — no approval step required. Use this when you are adding affiliates on their behalf (e.g., migrating from another system, programmatic onboarding).

If the email already exists as an affiliate user account (across any business), they will be linked to your business without changing their password. If no account exists, one is created with the provided credentials.

POST /v1/affiliates

Required scope: affiliates:write Rate limit: 60 requests/minute

Request Body

Field Type Required Constraints Description
name string Yes max 255 Affiliate’s full name
email string Yes valid email, max 255 Affiliate’s email address
password string Yes min 8, max 255 Account password
phone string No max 30 Phone number
commission_type string No percentage | fixed Commission calculation mode. Defaults to percentage
commission_rate float No 0–100 (percentage) or 0–999999.99 (fixed) Override commission rate. Omit to use business default
notes string No max 1000 Internal notes
referral_code string No alphanumeric, max 32, unique Custom referral code. Auto-generated if omitted
destination_url string No valid URL, max 2048 Default landing page URL for this affiliate’s store link

Example Request

curl -X POST "https://api.heldsway.com/api/v1/affiliates" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Doe",
    "email": "jane@example.com",
    "password": "SecurePass123!",
    "phone": "+14155552671",
    "commission_rate": 12.5,
    "notes": "Referred by our sales team",
    "referral_code": "JANE2026"
  }'

Success Response — 201 Created

{
  "success": true,
  "message": "Created",
  "data": {
    "id": 42,
    "referral_code": "JANE2026",
    "status": "active",
    "commission_rate": "12.50",
    "notes": "Referred by our sales team",
    "approved_at": "2026-04-13T10:00:00.000000Z",
    "suspended_at": null,
    "terminated_at": null,
    "created_at": "2026-04-13T10:00:00.000000Z",
    "affiliate_user": {
      "id": 7,
      "name": "Jane Doe",
      "email": "jane@example.com",
      "phone": "+14155552671"
    },
    "referral_link": {
      "id": 10,
      "slug": "JANE2026",
      "destination_url": "https://example.com/promo",
      "tracking_url": "https://yourbusiness.heldsway.com/r/JANE2026",
      "qr_code_url": "https://yourbusiness.heldsway.com/r/JANE2026/qr",
      "qr_code_svg_url": "https://yourbusiness.heldsway.com/r/JANE2026/qr?format=svg"
    }
  }
}

Error Responses

Status Code Description
409 AFFILIATE_EXISTS This email is already an affiliate of your business
422 — Missing or invalid fields. Form Request failure: Laravel’s standard validation body, no code (see Error Reference → Validation Errors (422))

Affiliate Self-Registration

Allow an individual to register themselves as an affiliate for your business. This endpoint is intended for use in your own frontend — your backend calls our API with your access token, passing the user’s details.

Prerequisite: The business setting allow_self_registration must be enabled. If it is off, this endpoint returns 403. This setting is configured in your business dashboard under Affiliate Program → Settings.

Affiliates created via this endpoint start with **pending** status and must be approved by you before they can earn commissions.

POST /v1/affiliates/register

Required scope: affiliates:write Rate limit: 30 requests/minute

Request Body

Field Type Required Constraints Description
name string Yes max 255 Applicant’s full name
email string Yes valid email, max 255 Applicant’s email
password string Yes min 8, max 255 Account password they will use to log in
phone string No max 30 Phone number

Example Request

curl -X POST "https://api.heldsway.com/api/v1/affiliates/register" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "John Smith",
    "email": "john@example.com",
    "password": "MyPassword123!"
  }'

Success Response — 201 Created

{
  "success": true,
  "message": "Registration submitted. Pending approval.",
  "data": {
    "id": 55,
    "referral_code": "XK9P2QR7",
    "status": "pending",
    "commission_rate": null,
    "notes": null,
    "approved_at": null,
    "suspended_at": null,
    "terminated_at": null,
    "created_at": "2026-04-13T11:30:00.000000Z",
    "affiliate_user": {
      "id": 12,
      "name": "John Smith",
      "email": "john@example.com",
      "phone": null
    }
  }
}

Error Responses

Status Code Description
403 REGISTRATION_DISABLED allow_self_registration is not enabled for this business
409 AFFILIATE_EXISTS This email is already an affiliate of your business
422 — Missing or invalid fields. Form Request failure: Laravel’s standard validation body, no code (see Error Reference → Validation Errors (422))

Update Affiliate

Update editable fields on an existing affiliate. Only include fields you want to change — all fields are optional.

PATCH /v1/affiliates/{id}

Required scope: affiliates:write Rate limit: 60 requests/minute

Path Parameters

Parameter Type Description
id integer Affiliate ID

Request Body

Field Type Required Constraints Description
commission_type string No percentage | fixed Commission calculation mode
commission_rate float|null No 0–100 (percentage) or 0–999999.99 (fixed) Override rate. Send null to revert to business default
notes string|null No max 1000 Internal notes
destination_url string|null No valid URL, max 2048 Update affiliate’s default storefront destination URL

Example Request

curl -X PATCH "https://api.heldsway.com/api/v1/affiliates/42" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "commission_rate": 15.0,
    "notes": "Upgraded to premium tier"
  }'

Success Response — 200 OK

Returns the updated Affiliate Object.

Error Responses

Status Code Description
404 NOT_FOUND Affiliate does not exist for this business
422 — Invalid field values. Form Request failure: Laravel’s standard validation body, no code (see Error Reference → Validation Errors (422))

Delete Affiliate

Soft-delete an affiliate from your business. This removes the affiliate record but does not delete their user account — they may still be an affiliate for other businesses. Historical conversion and click data is preserved.

DELETE /v1/affiliates/{id}

Required scope: affiliates:write Rate limit: 60 requests/minute

Path Parameters

Parameter Type Description
id integer Affiliate ID

Example Request

curl -X DELETE "https://api.heldsway.com/api/v1/affiliates/42" \
  -H "Authorization: Bearer <access_token>"

Success Response — 204 No Content

Empty body.

Error Responses

Status Code Description
404 NOT_FOUND Affiliate does not exist for this business

Approve Affiliate

Approve a pending affiliate, moving them to active status and allowing them to start earning commissions.

POST /v1/affiliates/{id}/approve

Required scope: affiliates:write Rate limit: 60 requests/minute

Path Parameters

Parameter Type Description
id integer Affiliate ID

Example Request

curl -X POST "https://api.heldsway.com/api/v1/affiliates/55/approve" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "Affiliate approved.",
  "data": {
    "id": 55,
    "status": "active",
    "approved_at": "2026-04-13T12:00:00.000000Z",
    ...
  }
}

Error Responses

Status Code Description
404 NOT_FOUND Affiliate not found
409 INVALID_STATUS Affiliate is not in pending status

Suspend Affiliate

Suspend an active or pending affiliate. Suspended affiliates cannot earn new commissions. Existing approved commissions are not affected.

POST /v1/affiliates/{id}/suspend

Required scope: affiliates:write Rate limit: 60 requests/minute

Path Parameters

Parameter Type Description
id integer Affiliate ID

Request Body

Field Type Required Description
reason string No Internal reason for suspension (max 500 chars). Appended to notes.

Example Request

curl -X POST "https://api.heldsway.com/api/v1/affiliates/42/suspend" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Suspected fraudulent activity under review." }'

Success Response — 200 OK

{
  "success": true,
  "message": "Affiliate suspended.",
  "data": {
    "id": 42,
    "status": "suspended",
    "suspended_at": "2026-04-13T12:30:00.000000Z",
    ...
  }
}

Error Responses

Status Code Description
404 NOT_FOUND Affiliate not found
409 INVALID_STATUS Affiliate is already suspended or terminated

Reactivate Affiliate

Reactivate a suspended affiliate, restoring them to active status.

POST /v1/affiliates/{id}/reactivate

Required scope: affiliates:write Rate limit: 60 requests/minute

Path Parameters

Parameter Type Description
id integer Affiliate ID

Example Request

curl -X POST "https://api.heldsway.com/api/v1/affiliates/42/reactivate" \
  -H "Authorization: Bearer <access_token>"

Success Response — 200 OK

{
  "success": true,
  "message": "Affiliate reactivated.",
  "data": {
    "id": 42,
    "status": "active",
    "suspended_at": null,
    ...
  }
}

Error Responses

Status Code Description
404 NOT_FOUND Affiliate not found
409 INVALID_STATUS Affiliate is not suspended

Terminate Affiliate

Permanently terminate an affiliate. This is irreversible — terminated affiliates cannot be reactivated. Use suspend if you may want to reinstate them later.

POST /v1/affiliates/{id}/terminate

Required scope: affiliates:write Rate limit: 60 requests/minute

Path Parameters

Parameter Type Description
id integer Affiliate ID

Request Body

Field Type Required Description
reason string No Reason for termination (max 500 chars). Appended to notes.

Example Request

curl -X POST "https://api.heldsway.com/api/v1/affiliates/42/terminate" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Violated affiliate terms of service." }'

Success Response — 200 OK

{
  "success": true,
  "message": "Affiliate terminated.",
  "data": {
    "id": 42,
    "status": "terminated",
    "terminated_at": "2026-04-13T13:00:00.000000Z",
    ...
  }
}

Error Responses

Status Code Description
404 NOT_FOUND Affiliate not found
409 INVALID_STATUS Affiliate is already terminated