Skip to main content

REST API Endpoint Reference

Reference AffiliateBase REST API endpoints, account-scoped query parameters, request fields, response shapes, and error codes.

Table of Contents

This reference lists every /api/v1 endpoint in the AffiliateBase REST API with its parameters, request fields, and response contracts. It is written for developers building an integration. Authentication and permissions are covered in API Keys and REST API.

Global contract

  • Auth (one of): Authorization: Bearer <API_KEY>, Authorization: Basic <base64(API_KEY:)>, X-Api-Key: <API_KEY>. OAuth access tokens issued to AI connectors are refused with 403 FORBIDDEN.
  • Account context (required): ?account_id=<ACCOUNT_ID> or X-Account-Id: <ACCOUNT_ID>
  • Pagination defaults: page=1, limit=25, max limit=100
  • Array params: state[]=due&state[]=pending or state=due,pending
  • Expand params: expand[]=campaign&expand[]=sale or expand=campaign,sale
  • Timestamp filters (updated_since, updated_until) must be ISO-8601 where supported. An invalid value returns 422 VALIDATION_ERROR.
  • Permissions: read requests (GET) need the read action and all other methods need write, for example v1.referrals.write.

Common error envelope:

JSON
{
  "error": "Account ID is required",
  "code": "MISSING_ACCOUNT_ID"
}

Common error codes: MISSING_ACCOUNT_ID, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_ERROR, INVALID_PARAMETER (400), TOO_MANY_REQUESTS (429; see Rate limits)

Payout endpoints return operation-specific 422 codes: PAYOUT_EXECUTION_FAILED, PAYOUT_RUN_CREATE_FAILED, PAYOUT_RUN_EXECUTION_FAILED, PAYOUT_RUN_REMEDIATION_FAILED, PAYOUT_EXPORT_CREATE_FAILED, PAYOUT_EXPORT_DOWNLOAD_FAILED, PAYOUT_EXPORT_DELETE_FAILED, and PAYOUT_EXPORT_SETTLEMENT_FAILED.

API keys

  • GET /api/v1/api_keys
    • Query: page, limit
    • Role (session auth): owner or admin. API key auth needs a full-access key or exactly v1.api_keys.manage.
  • POST /api/v1/api_keys
    • Body:
      • name (string, required)
      • permissions (object, required, non-empty)
      • expires_at (ISO datetime, optional)
    • Notes:
      • permissions must be an object.
      • A request that omits permissions or sends an empty object returns 422 VALIDATION_ERROR. For full access, send { "all": true }.
      • A key authenticated by API key can only create keys whose permissions it already holds.
    • Role (session auth): owner or admin
  • DELETE /api/v1/api_keys/:id
    • Revokes key (does not hard-delete row)
    • Returns success message JSON
    • Role (session auth): owner or admin

Affiliates

  • GET /api/v1/affiliates
    • Query:
      • page, limit
      • campaign_id
      • email
      • state or state[] (pending|active|disabled|suspicious|rejected|banned)
      • stripe_customer_id
      • updated_since, updated_until (ISO-8601)
      • expand[] (campaign, links, coupon, commission_stats)
  • POST /api/v1/affiliates
    • Body:
      • email (required)
      • campaign_id (recommended; if omitted, an arbitrary non-archived campaign is used (422 if none). Always send campaign_id)
      • first_name, last_name (optional)
      • state (optional, defaults to active)
      • paypal_email, wise_email (optional)
    • If the email already belongs to an AffiliateBase user, the request succeeds only when that user is already an affiliate or team member on this account. Otherwise it returns 422; use POST /api/v1/invitations with kind=affiliate so the person can accept.
    • Users created through this endpoint start with an unconfirmed email address.
  • GET /api/v1/affiliates/:id
    • Query: expand[] (campaign, links, coupon, commission_stats)
  • PATCH /api/v1/affiliates/:id
    • Body:
      • state
      • campaign_id
      • paypal_email
      • wise_email
    • state cannot be set to banned, or changed while the affiliate is banned. Both return 422 VALIDATION_ERROR; use the ban and unban endpoints below.
  • DELETE /api/v1/affiliates/:id
    • Archives by default
    • Returns 204 No Content
  • POST /api/v1/affiliates/:id/ban
    • Body:
      • reason (required: fraud, fake_traffic, self_referral, tos_violation, brand_abuse, spam, other)
      • note (optional, up to 500 characters; kept for your audit trail, never returned by the API or sent in webhooks)
      • dry_run (boolean, optional; previews the impact without changing anything)
      • idempotency_key (optional; or send the Idempotency-Key header)
      • requested_by (optional; who asked for the ban, recorded in the status history)
    • Bans every affiliate record of the same person on the account, including archived ones. It voids unpaid commissions (voids are final), removes or reduces payouts that have not been submitted, archives links, and stops promo codes. Payouts already submitted or exported are left as they are and listed in impact.untouched_payouts.
    • Returns a slim affiliate view (id, state, campaign_id, archived, banned_at, ban_reason, without contact or payout details) plus impact, dry_run, and noop. Banning someone who is already banned returns 200 with noop: true and an empty impact. Retrying with the same idempotency key returns the original impact without banning again.
    • Errors: 422 VALIDATION_ERROR for a missing or unknown reason or a note over 500 characters; 404 NOT_FOUND for an unknown affiliate.
    • Permission: a full-access key, or a key with exactly v1.affiliates.ban. Session auth: owner or admin.
  • POST /api/v1/affiliates/:id/unban
    • Body:
      • note (required, up to 500 characters)
      • dry_run, idempotency_key, requested_by (optional, as for ban)
    • Moves every banned record of the person to disabled, never straight to active. Reactivate separately with PATCH /api/v1/affiliates/:id. Voided commissions stay voided, and archived links and stopped promo codes stay stopped.
    • Returns the same slim affiliate view plus impact, dry_run, and noop. Unbanning an affiliate that is not banned returns 422 VALIDATION_ERROR.
    • Permission: same as ban.
  • Affiliate objects include banned_at and ban_reason. Both are null unless state is banned.

Affiliate coupons

  • GET /api/v1/affiliates/:affiliate_id/coupon
  • POST /api/v1/affiliates/:affiliate_id/coupon
  • PATCH /api/v1/affiliates/:affiliate_id/coupon
  • DELETE /api/v1/affiliates/:affiliate_id/coupon
    • Returns 204 No Content
  • POST /api/v1/affiliates/:affiliate_id/coupon/sync
    • Body:
      • force (boolean, optional)

Campaigns

  • GET /api/v1/campaigns
    • Query: page, limit
  • POST /api/v1/campaigns
  • PATCH /api/v1/campaigns/:id
    • Body fields (partial updates supported):
      • name, slug, url
      • attribution_model (last_click|first_click; defaults to last_click)
      • private, private_tokens
      • reward_type (percent|fixed)
      • commission_percent
      • commission_amount_cents, commission_amount_currency
      • minimum_payout_cents, minimum_payout_currency
      • max_commission_period_months, max_commissions
      • introductory_commission_percent, introductory_commission_months (percent campaigns; both required together, or both blank to clear)
      • days_before_referrals_expire, days_until_commissions_are_due
      • affiliate_dashboard_text, custom_reward_description, welcome_text
      • customers_visible_to_affiliates, sale_description_visible_to_affiliates
      • parameter_type
      • stripe_coupon_id
      • promo_code_provisioning (automatic|individual; defaults to automatic)
      • default
  • GET /api/v1/campaigns/:id
  • DELETE /api/v1/campaigns/:id
    • Archives by default
    • Returns 204 No Content

promo_code_provisioning=individual disables future automatic code generation for the campaign. Create or import each affiliate’s chosen code through the promo-code endpoints. Switching modes does not rename, stop, or replace existing codes. Explicit calls to the singular affiliate coupon creation endpoint still create a code.

  • GET /api/v1/links
    • Query:
      • affiliate_id
      • campaign_id
      • page, limit
  • POST /api/v1/links
    • Body:
      • affiliate_id (required)
      • campaign_id (required)
      • url (required)
      • token (optional; auto-generated if missing)
  • GET /api/v1/links/:id
  • PATCH /api/v1/links/:id
    • Body: affiliate_id, campaign_id, url, token
  • DELETE /api/v1/links/:id
    • Archives by default
    • Returns 204 No Content

Referrals

  • GET /api/v1/referrals
    • Query:
      • page, limit
      • affiliate_id
      • conversion_state or conversionState
      • stripe_customer_id
      • email (customer email)
      • updated_since, updated_until (ISO-8601)
      • expand[] (link, customer, affiliate)
  • POST /api/v1/referrals
    • Body:
      • affiliate_id (required)
      • campaign_id (required when link_id is omitted)
      • link_id (optional)
      • conversion_state (visitor|lead|conversion, optional)
      • expires_at (ISO datetime, optional)
  • GET /api/v1/referrals/:id
    • Query: expand[] (link, customer, affiliate)
  • PATCH /api/v1/referrals/:id
    • Body:
      • conversion_state (visitor|lead|conversion)
      • deactivated_at (ISO datetime; deactivates the referral. Sending null for a deactivated referral returns 422, because reactivation is not supported)
      • customer_email (optional; binds/creates customer)
  • DELETE /api/v1/referrals/:id
    • Soft-deactivates referral
    • Returns 204 No Content

Commissions

  • GET /api/v1/commissions
    • Query:
      • page, limit
      • state or state[]
      • affiliate_id
      • campaign_id
      • updated_since, updated_until (ISO-8601)
      • expand[] (campaign, sale)
  • GET /api/v1/commissions/:id
    • Returns the commission object directly, not wrapped in data.
    • Includes expanded campaign and sale data.
    • The expanded sale includes referral, customer, and affiliate details when present.
    • Common enrichment fields:
      • sale.referral.customer.email
      • sale.referral.customer.name
      • sale.referral.stripe_customer_id
      • sale.affiliate.id
      • sale.affiliate.email
      • sale.affiliate.first_name
      • sale.affiliate.last_name
  • PATCH /api/v1/commissions/:id
    • Body:
      • due_at (ISO datetime, optional)
    • Notes:
      • Changing due_at recalculates pending|due, unless the commission is paid, processing, or voided.
      • paid_at is rejected with 422 VALIDATION_ERROR. Commissions become paid through payout settlement: POST /api/v1/payouts/:id/pay or a payout export payment.
      • A commission held for fraud review cannot have due_at changed (422); resolve it with POST /api/v1/commissions/:id/review.
  • DELETE /api/v1/commissions/:id
    • Soft-voids commission
    • Body:
      • void_reason (optional)
    • Returns object, id, deleted: false, state, adjustment_commission_id, and post_submission_adjustment.
    • If the commission’s payout was already exported, paid, or sent for payment, the commission is left as is and a negative adjustment commission (adjustment_commission_id, post_submission_adjustment: true) is created to net against the affiliate’s next payout.
  • POST /api/v1/commissions/:id/review
    • Body:
      • review_action (approve|suspicious|deactivate)
    • approve releases the hold and returns the commission to pending or due. suspicious keeps it held and suspends the affiliate. deactivate voids the commission and suspends the affiliate.

Payouts

  • GET /api/v1/payouts
    • Query:
      • page, limit
      • affiliate_id
      • state or state[]
      • eligible=true (returns eligible payout candidates; non-paginated)
      • updated_since, updated_until (ISO-8601)
      • expand[] (affiliate, commissions)
  • GET /api/v1/payouts/:id
  • POST /api/v1/payouts/:id/pay
    • Body:
      • payment_reference (optional)
      • dry_run (boolean, optional; previews without paying)
      • idempotency_key and requested_by (optional; recorded in the audit trail. The Idempotency-Key header is also accepted)
    • If the payout’s method is Stripe, this sends real money through Stripe. PayPal and Wise payouts are recorded as paid; you send the money yourself.
    • A Stripe payout that belongs to a payout export is refused with 422. Settle the export instead, or delete it with DELETE /api/v1/payout_exports/:id (Void export before download in the dashboard) if it has not been downloaded.

Payout exports

Payout export endpoints require the owner or admin role for session auth.

  • GET /api/v1/payout_exports
    • Query:
      • limit
  • POST /api/v1/payout_exports
    • Body:
      • payout_ids (array, optional)
      • export_format (csv|paypal|wise, optional)
      • notes (optional)
  • GET /api/v1/payout_exports/:id
    • Query:
      • export_format (csv|paypal|wise, optional)
    • Notes:
      • Returns metadata plus filename and content
  • DELETE /api/v1/payout_exports/:id
  • POST /api/v1/payout_exports/:payout_export_id/payment
    • Body:
      • payment_reference (optional)

Payout runs

Payout run endpoints require the owner or admin role for session auth.

  • POST /api/v1/payout_runs
    • Body:
      • idempotency_key (optional; generated if omitted)
      • execute_now (boolean, optional, default false)
      • affiliate_ids (array, optional)
      • payment_reference (optional; used when execute_now=true)
  • PUT /api/v1/payout_runs/:id/execute
    • Body:
      • async (boolean, optional, default true)
      • payment_reference (optional)
      • dry_run (boolean, optional; previews without executing)
      • idempotency_key (optional; or send the Idempotency-Key or X-Idempotency-Key header. A key repeated within 24 hours returns replayed: true without executing again)
  • PUT /api/v1/payout_runs/:id/remediate
    • Body:
      • remediation_action (required)
      • issue_code (optional)
      • payout_ids (array, optional)
      • dry_run, idempotency_key (optional, as for execute)
  • GET /api/v1/payout_runs/reconcile
    • Query:
      • lookback_days (optional, default 30)

Invitations

  • GET /api/v1/invitations
    • Query:
      • kind (member|affiliate, optional)
      • status (pending|accepted|revoked, optional)
      • page, limit
  • POST /api/v1/invitations
    • Body:
      • kind (member|affiliate, required)
      • email (required)
      • role (for member, optional, defaults to member)
      • campaign_id or campaignId (for affiliate, required)
  • GET /api/v1/invitations/:id
    • Query:
      • kind (optional but recommended)
  • PATCH /api/v1/invitations/:id
    • Body:
      • kind (optional but recommended)
      • invitation_action or status_action (accept|revoke)
      • status=revoked (alias for revoke)
      • role (member-invitation role update path)
    • Accepting (accept) requires a signed-in user whose email matches the invitation, so it works from the dashboard session and not with an API key. An API-key acceptance request returns 422, and user_id is ignored.
    • Only an account owner can grant the owner role. A full-access API key acts as the owner who created it. A scoped key can never grant owner.
  • DELETE /api/v1/invitations/:id
    • Query/body:
      • kind (optional but recommended)
    • Returns 204 No Content
  • POST /api/v1/invitations/:id/resend
    • Query/body:
      • kind (optional but recommended)

LLM and SDK integration tip

Use the machine-readable contract when generating clients:

Example request

Terminal
curl "https://app.affiliatebase.io/api/v1/commissions?account_id=<ACCOUNT_ID>&state=due&expand=campaign,sale&page=1&limit=25" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json"

Individually assigned promo codes

Use these endpoints to manage individually assigned Stripe promo codes. Stripe operations require a connected Stripe account and an active affiliate in an active campaign. They use the existing v1.affiliate_coupons.read and v1.affiliate_coupons.write API permissions.

EndpointBehavior
GET /api/v1/affiliates/:affiliate_id/promo_codesList assignments; use q for exact Stripe code search or discounts=true for valid discounts.
POST /api/v1/affiliates/:affiliate_id/promo_codesPreview/import an exact stripe_promotion_code_id, or use mode=create, code and stripe_coupon_id.
PATCH /api/v1/affiliates/:affiliate_id/promo_codes/:idPreview or persist the current provider observation.
DELETE /api/v1/affiliates/:affiliate_id/promo_codes/:idPreview or stop tracking locally, keeping Stripe redemption and historical records intact.

Writes default to dry_run=true. Execute using dry_run=false; import and creation also require a stable Idempotency-Key. After an uncertain response, retry with the same key and payload. On OPERATION_PENDING, check the original result. On RECONCILIATION_REQUIRED, review the original operation before another creation attempt. An assignment response does not prove paid attribution.

Display code to customers. tracking_token is an opaque compatibility identifier. Ownership uses the exact provider ID, account and live/test mode. Codes cannot be transferred to another affiliate or reactivated after tracking stops. The existing singular /coupon endpoint remains the legacy campaign default.

The affiliate_promo_codes MCP tool exposes the same operations and defaults to preview. A connector token needs explicit mcp.affiliate_coupons.read / mcp.affiliate_coupons.write permissions. To grant them, select the optional Allow promo-code management checkbox when you create a token. Existing tokens keep their current permissions.

See Manage Promo Codes with MCP for copyable tool calls, permission setup and retry guidance, or Assign Stripe Promo Codes to Affiliates for the merchant workflow.