{
  "openapi": "3.0.1",
  "info": {
    "title": "Referly API",
    "description": "REST API for managing affiliates, referrals, sales, affiliate links, coupons and promotional codes in your Referly affiliate program.\n\nEvery request must send `Authorization: Bearer <YOUR_API_KEY>`. The token is scoped to a single affiliate program, so no program ID is ever required in the request — every read and write is automatically limited to that program.\n\nRequests are rate limited per token, per endpoint and per method.",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://www.referly.so/api/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/affiliates": {
      "get": {
        "summary": "Get affiliate(s)",
        "description": "Returns a single affiliate when `id` or `email` is supplied, or every affiliate in the program when neither is. Looking up an affiliate that does not exist returns `null` with a 200, not a 404.\n\nEach affiliate includes an `affiliateLinks` array holding every referral link slug they own — this is the source of truth for links. The legacy `link` field is deprecated and is usually `null`; use the [affiliate link endpoints](/api-reference/links/get) to manage links.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "Identifies one affiliate. Accepts the affiliate's UUID, one of their affiliate link slugs, or a numeric click ID (the click's affiliate is returned).",
            "schema": {
              "type": "string"
            },
            "example": "9f1c2f2e-1b3a-4f5c-8d7e-6a5b4c3d2e1f"
          },
          {
            "name": "email",
            "in": "query",
            "description": "The email address of the affiliate to retrieve. Ignored when `id` is set.",
            "schema": {
              "type": "string",
              "format": "email"
            },
            "example": "affiliate@example.com"
          }
        ],
        "responses": {
          "200": {
            "description": "A single affiliate when filtering by `id` or `email` (`null` if no match), otherwise every affiliate in the program.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Affiliate"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Affiliate"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "summary": "Create an affiliate",
        "description": "Creates an affiliate in your program and gives them their first referral link.\n\n`firstName`, `lastName` and `email` are required; the email must be unique within the program. If you do not send `affiliateLink`, a slug is generated from the email address. If you do send one and it is already taken, the request fails rather than silently picking another slug.\n\nWhen `commissionRate` is omitted the affiliate inherits the program's default rate. When `affiliateStatus` is omitted, the program's auto-approve setting decides whether the affiliate starts active or pending.\n\nCreating an affiliate also triggers the admin notification email, the affiliate welcome email (active affiliates only, unless `sendWelcomeEmail` is `false`), any automatic promo codes, and the `affiliate.created` webhook.",
        "requestBody": {
          "description": "The affiliate to create",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewAffiliate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The created affiliate, including its generated `affiliateLinks` entry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Affiliate"
                }
              }
            }
          },
          "400": {
            "description": "A required field is missing, an affiliate with this email already exists, the requested affiliate link is already in use, `commissionPlanId` or `affiliateGroupId` does not exist in this program, the commission plan does not belong to the given group, or the account has reached its affiliate limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "summary": "Update an affiliate",
        "description": "Updates an affiliate identified by `affiliateId` or `emailAddress`.\n\nThis endpoint returns a **count of updated records**, not the affiliate object. Call [Get Affiliate(s)](/api-reference/affiliates/get) afterwards if you need the updated affiliate.\n\nSending `affiliateLink` **adds another** referral link to the affiliate — it does not rename or replace existing links. Use the [affiliate link endpoints](/api-reference/links/update) to rename or remove a link.",
        "requestBody": {
          "description": "Fields to update",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AffiliateUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The number of affiliate records that matched and were updated. `count` is `0` when no affiliate in this program matched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateCount"
                }
              }
            }
          },
          "400": {
            "description": "Neither `affiliateId` nor `emailAddress` was supplied, the requested affiliate link is already in use, or the update failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "summary": "Delete affiliates",
        "description": "Permanently deletes an affiliate from the program, along with the records that cascade from them (their affiliate links, referrals, sales and commissions). This cannot be undone.\n\n`affiliateId` accepts either the affiliate's UUID or their email address. The request succeeds with the same response even when nothing matched, so check the affiliate exists first if that matters to you. Fires the `affiliate.deleted` webhook.",
        "requestBody": {
          "description": "The affiliate to delete",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AffiliateDelete"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The affiliate was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteMessage"
                }
              }
            }
          },
          "400": {
            "description": "`affiliateId` was not supplied, or the delete failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/referrals": {
      "get": {
        "summary": "Get referral(s)",
        "description": "Returns referrals for your program. Supply `id` to fetch one referral, `affiliateId` / `affiliateEmail` to list everything a given affiliate referred, or `externalId` to look a referral up by the ID it has in your own system. With no parameters, every referral in the program is returned.\n\nA referral that does not exist comes back as `null` with a 200, not a 404.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "The referral's UUID. If no referral has that ID, the value is retried as a referral email address. Returns a single referral.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "affiliateId",
            "in": "query",
            "description": "Return every referral belonging to this affiliate UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "affiliateEmail",
            "in": "query",
            "description": "Return every referral belonging to the affiliate with this email address.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "name": "externalId",
            "in": "query",
            "description": "Return every referral whose `referredUserExternalId` matches — the ID you gave the referred user in your own database.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A single referral when filtering by `id` (`null` if no match), otherwise an array.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Referral"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Referral"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "summary": "Create a referral",
        "description": "Records a new referral — someone an affiliate sent your way who has now signed up.\n\nYou must identify the affiliate with exactly one of `affiliateId`, `affiliateEmail` or `promoCode`. `affiliateId` is flexible: it accepts an affiliate UUID, one of their affiliate link slugs, or a numeric click ID.\n\nWhen you pass `promoCode`, the code is validated first — it must exist, be active, be unexpired, and be under both its own and its coupon's redemption limits. On success the redemption counters on the promotional code and its coupon are both incremented.\n\n`name`, `email` and `referredUserExternalId` are always required. Referrals created here are always recorded as active.",
        "requestBody": {
          "description": "The referral to create",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewReferral"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The created referral, looked up by email after creation. Returns `null` if the referral could not be resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Referral"
                }
              }
            }
          },
          "400": {
            "description": "None of `affiliateId` / `affiliateEmail` / `promoCode` was supplied; `name`, `email` or `referredUserExternalId` is missing; the promo code was not found, is inactive, has expired, has hit its redemption limit, or has no affiliate attached; or the account has reached its monthly referral limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "summary": "Update a referral",
        "description": "Updates a referral identified by `referralId`, and fires the `referral.updated` webhook.\n\nUse `subscriptionStatus` to change whether a referral counts as active. Any field you send is applied directly to the referral, so sending a field that is not listed below will fail the request.",
        "requestBody": {
          "description": "Fields to update",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReferralUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated referral.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Referral"
                }
              }
            }
          },
          "400": {
            "description": "`referralId` was missing, no referral in this program has that ID, or the request contained a field that cannot be updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "summary": "Delete referrals",
        "description": "Permanently deletes a referral and the sales that cascade from it. This cannot be undone.\n\n`referralId` accepts either the referral's UUID or the referred user's email address. The request succeeds with the same response even when nothing matched. Fires the `referral.deleted` webhook.",
        "requestBody": {
          "description": "The referral to delete",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReferralDelete"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The referral was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteMessage"
                }
              }
            }
          },
          "400": {
            "description": "`referralId` was not supplied, or the delete failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/links": {
      "get": {
        "summary": "Get affiliate link(s)",
        "description": "Returns affiliate links for your program, each enriched with its click, referral and sale counts, the revenue it has generated, and a ready-to-share full URL for every base URL configured on your program.\n\nFilter by `id` or `link` for one link, or by `affiliateId` / `affiliateEmail` for every link an affiliate owns. With no parameters, every link in the program is returned.\n\nThis endpoint always returns an array. A filter that matches nothing returns `[]`.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "The affiliate link's UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "link",
            "in": "query",
            "description": "The link slug, for example `jane-doe`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "affiliateId",
            "in": "query",
            "description": "Return every link owned by this affiliate UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "affiliateEmail",
            "in": "query",
            "description": "Return every link owned by the affiliate with this email address.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The matching affiliate links. Empty when nothing matched.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AffiliateLink"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The affiliate links could not be fetched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create an affiliate link",
        "description": "Creates an additional referral link for an existing affiliate. An affiliate can own any number of links, which is useful for running separate campaigns under one affiliate.\n\nIdentify the affiliate with either `affiliateId` or `affiliateEmail` — a link cannot exist without an affiliate. The `link` slug may contain only letters, numbers and hyphens, and must be unique within the program.\n\nFires the `affiliate.updated` webhook for the link's owner.",
        "requestBody": {
          "description": "The affiliate link to create",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewAffiliateLink"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The created affiliate link, with counters at zero and its full URLs already generated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AffiliateLink"
                }
              }
            }
          },
          "400": {
            "description": "`link` was missing, neither `affiliateId` nor `affiliateEmail` was supplied, the affiliate was not found in this program, the slug contains characters other than letters, numbers and hyphens, or the slug is already used in this program.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The affiliate link could not be created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Update an affiliate link",
        "description": "Renames an affiliate link's slug. Identify the link with either `id` or its current `link` slug, and give the new slug as `newLink`.\n\nThe new slug may contain only letters, numbers and hyphens, and must be unique within the program.\n\nRenaming a slug breaks any referral URL already shared using the old slug — existing clicks, referrals and sales stay attached to the link, but new traffic on the old URL will not be tracked. Fires the `affiliate.updated` webhook for the link's owner.",
        "requestBody": {
          "description": "The rename to apply",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AffiliateLinkUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated affiliate link, with regenerated full URLs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AffiliateLink"
                }
              }
            }
          },
          "400": {
            "description": "`newLink` was missing, neither `id` nor `link` was supplied, the new slug contains characters other than letters, numbers and hyphens, or the new slug is already used in this program.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No affiliate link matched the `id` or `link` supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The affiliate link could not be updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete affiliate link",
        "description": "Permanently deletes an affiliate link, identified by either `id` or its `link` slug.\n\nClicks, referrals and sales already attributed to the link are kept — their link reference is simply cleared. Any referral URL using this slug stops tracking immediately. Fires the `affiliate.updated` webhook for the link's former owner.",
        "requestBody": {
          "description": "The affiliate link to delete",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AffiliateLinkDelete"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The affiliate link was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "description": "Confirmation message"
                    },
                    "deletedLink": {
                      "type": "object",
                      "description": "The link that was removed",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid",
                          "description": "The deleted affiliate link ID"
                        },
                        "link": {
                          "type": "string",
                          "description": "The deleted link slug"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Neither `id` nor `link` was supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No affiliate link matched the `id` or `link` supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The affiliate link could not be deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/coupons": {
      "get": {
        "summary": "Get coupons",
        "description": "Returns coupons for your program. A coupon defines the discount itself — how much comes off, in what currency, and for how long. The codes customers actually type at checkout are [promotional codes](/api-reference/promotional-codes/get), which each point at a coupon.\n\nFilter with `id`, `externalId` or `name` for a single coupon. With no parameters, every coupon in the program is returned, newest first.\n\nEach coupon comes back with its `promotionalCodes` (including the affiliate each is assigned to) and its `autoCouponRule` if one is configured.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "The coupon's UUID. Returns a single coupon.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "externalId",
            "in": "query",
            "description": "The coupon's ID in the connected billing provider, for example a Stripe coupon ID. Returns a single coupon.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "description": "The coupon's name. Returns a single coupon.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A single coupon when filtering by `id`, `externalId` or `name` (`null` if no match), otherwise every coupon in the program.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Coupon"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Coupon"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The coupons could not be fetched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a coupon",
        "description": "Creates a coupon — the discount definition that promotional codes are built on.\n\nOnly `name` is required. Set `couponType` to `PERCENTAGE` and supply `percentOff` (1–100), or set it to `FLAT` and supply both `amountOff` and `currency`. When `couponType` is omitted the coupon is stored as `PERCENTAGE`, so send `percentOff` alongside it.\n\n`duration` controls how long the discount keeps applying to a subscription: `once`, `forever` (the default here), or `repeating` — which additionally requires `durationInMonths`.\n\nFires the `coupon.created` webhook.",
        "requestBody": {
          "description": "The coupon to create",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewCoupon"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The created coupon.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Coupon"
                }
              }
            }
          },
          "400": {
            "description": "`name` was missing; `couponType` is `PERCENTAGE` and `percentOff` is missing or outside 1–100; `couponType` is `FLAT` and `amountOff` or `currency` is missing; `duration` is `repeating` and `durationInMonths` is missing; or a coupon with this `externalId` already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The coupon could not be created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Update a coupon",
        "description": "Updates a coupon identified by `couponId`, and fires the `coupon.updated` webhook.\n\nChanging the discount affects every promotional code that points at this coupon, including codes already in circulation. Set `valid` to `false` to stop a coupon being applied without deleting it.\n\nEvery field you send is applied directly to the coupon, so sending a field that is not listed below will fail the request.",
        "requestBody": {
          "description": "Fields to update",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CouponUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated coupon.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Coupon"
                }
              }
            }
          },
          "400": {
            "description": "`couponId` was missing, `percentOff` is outside 1–100, `amountOff` is not greater than zero, or the request contained a field that cannot be updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No coupon with that ID exists in this program.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The coupon could not be updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a coupon",
        "description": "Permanently deletes a coupon **and every promotional code attached to it**. Any code your affiliates are already sharing stops working immediately. This cannot be undone — set `valid` to `false` with [Update a Coupon](/api-reference/coupons/update) if you only want to stop new redemptions.\n\nFires the `coupon.deleted` webhook.",
        "requestBody": {
          "description": "The coupon to delete",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CouponDelete"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The coupon and its promotional codes were deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "description": "Confirmation message"
                    },
                    "deletedCoupon": {
                      "type": "object",
                      "description": "The coupon that was removed",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid",
                          "description": "The deleted coupon ID"
                        },
                        "name": {
                          "type": "string",
                          "nullable": true,
                          "description": "The deleted coupon's name"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`couponId` was not supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No coupon with that ID exists in this program.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The coupon could not be deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/promotional-codes": {
      "get": {
        "summary": "Get promotional codes",
        "description": "Returns promotional codes for your program. A promotional code is the string a customer types at checkout; it inherits its discount from the [coupon](/api-reference/coupons/get) it belongs to, and can be assigned to an affiliate so redemptions are credited to them.\n\n`id`, `code` and `externalId` return a single code. `couponId`, `affiliateId` and `affiliateEmail` return every matching code, newest first. With no parameters, every code in the program is returned.\n\nEach code comes back with its parent `coupon` and the `affiliate` it is assigned to.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "The promotional code's UUID. Returns a single code.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "code",
            "in": "query",
            "description": "The code string itself, for example `JANE20`. Returns a single code.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "externalId",
            "in": "query",
            "description": "The code's ID in the connected billing provider, for example a Stripe promotion code ID. Returns a single code.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "couponId",
            "in": "query",
            "description": "Return every code belonging to this coupon.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "affiliateId",
            "in": "query",
            "description": "Return every code assigned to this affiliate UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "affiliateEmail",
            "in": "query",
            "description": "Return every code assigned to the affiliate with this email address.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A single promotional code when filtering by `id`, `code` or `externalId` (`null` if no match), otherwise an array.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PromotionalCode"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PromotionalCode"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The promotional codes could not be fetched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a promotional code",
        "description": "Creates a promotional code against an existing coupon. `couponId` is always required — the coupon supplies the discount.\n\nThere are two ways to create a code:\n\n- **Give it yourself.** Send `code` with the exact string you want. It must be unique within the program.\n- **Have it generated.** Set `isAutoGenerated` to `true` and send `codeStructure` describing which pieces to combine — the affiliate's first name, last name, email prefix, the discount value, the coupon name, and/or random characters. An affiliate is required in this mode, and you can shape the result further with `prefix`, `randomCharsLength` and `randomCharsCase`.\n\nAssign the code to an affiliate with `affiliateId` or `affiliateEmail` so their redemptions are tracked. Assigned codes are also connected to that affiliate's links automatically in the background.\n\nFires the `promotional_code.created` webhook.",
        "requestBody": {
          "description": "The promotional code to create",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewPromotionalCode"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The created promotional code, with its parent coupon and assigned affiliate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionalCode"
                }
              }
            }
          },
          "400": {
            "description": "`couponId` was missing; `code` was missing and `isAutoGenerated` was not set; `isAutoGenerated` was set without an affiliate; or the resulting code already exists in this program.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The coupon was not found in this program, or the affiliate was not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The promotional code could not be created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Update a promotional code",
        "description": "Updates a promotional code identified by `promotionalCodeId`, and fires the `promotional_code.updated` webhook.\n\nSet `active` to `false` to switch a code off without deleting it — this is the safe way to retire a code an affiliate has already shared. Changing `code` renames it, and the new string must be unique within the program.\n\nEvery field you send is applied directly to the code, so sending a field that is not listed below will fail the request.",
        "requestBody": {
          "description": "Fields to update",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromotionalCodeUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated promotional code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionalCode"
                }
              }
            }
          },
          "400": {
            "description": "`promotionalCodeId` was missing, the new `code` is already used in this program, or the request contained a field that cannot be updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No promotional code with that ID exists in this program.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The promotional code could not be updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a promotional code",
        "description": "Permanently deletes a promotional code. Any customer who tries to use it at checkout after this will be rejected. Sales already recorded against the code keep their history.\n\nThis cannot be undone — set `active` to `false` with [Update a Promotional Code](/api-reference/promotional-codes/update) if you only want to stop new redemptions. Fires the `promotional_code.deleted` webhook.",
        "requestBody": {
          "description": "The promotional code to delete",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromotionalCodeDelete"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The promotional code was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "description": "Confirmation message"
                    },
                    "deletedCode": {
                      "type": "object",
                      "description": "The promotional code that was removed",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid",
                          "description": "The deleted promotional code ID"
                        },
                        "code": {
                          "type": "string",
                          "description": "The deleted code string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`promotionalCodeId` was not supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No promotional code with that ID exists in this program.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The promotional code could not be deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sales": {
      "get": {
        "summary": "Get sale(s)",
        "description": "Returns sales for your program. Filter by a single sale with `id`, by the affiliate who earned it, by the referral it belongs to, or by the IDs the sale has in your own system (`saleExternalId` and `saleExternalInvoiceId`). With no parameters, every sale in the program is returned.\n\nEvery sale is returned with a `commissionEarned` field — the commission recorded for the affiliate credited with that sale.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "The numeric sale ID. Returns a single sale.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "affiliateId",
            "in": "query",
            "description": "Return every sale credited to this affiliate UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "affiliateEmail",
            "in": "query",
            "description": "Return every sale credited to the affiliate with this email address.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "name": "referralId",
            "in": "query",
            "description": "Return every sale belonging to this referral UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "saleExternalId",
            "in": "query",
            "description": "Return every sale whose `externalId` matches — the ID of the sale in your own system.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "saleExternalInvoiceId",
            "in": "query",
            "description": "Return every sale whose `externalInvoiceId` matches — the ID of the invoice in your own system. Useful when you invoice customers on purchase.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A single sale when filtering by `id` (`null` if no match), otherwise an array.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Sale"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Sale"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "summary": "Create a sale",
        "description": "Records a sale and pays out the resulting commission.\n\nYou must identify who the sale belongs to with one of `referralId`, `email` or `promoCode`. `referralId` is flexible: it accepts a referral UUID, an affiliate UUID, an affiliate link slug, or a numeric click ID. `email` matches an existing referral by email address.\n\n`totalEarned` is required and must be non-zero. A customer `name` and `email` are also required — they are taken from the matched referral when you do not send them explicitly.\n\n`commissionRate` overrides the affiliate's rate for this sale only. `tax` and `shipping` are deducted before commission is calculated, but only when your program has the matching deduction setting enabled.\n\nWhen you pass `promoCode`, the code is validated for existence, active state, expiry and redemption limits, and its redemption counters are incremented on success.",
        "requestBody": {
          "description": "The sale to record",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewSale"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The recorded sale. Returns `null` when no affiliate could be resolved from the details supplied — check the response before assuming a commission was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sale"
                }
              }
            }
          },
          "400": {
            "description": "None of `referralId` / `email` / `promoCode` was supplied; `totalEarned`, `name` or `email` is missing; the promo code was not found, is inactive, has expired, has hit its redemption limit, or has no affiliate attached; or the sale could not be created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "summary": "Update a sale",
        "description": "Updates a sale identified by `saleId`, and fires the `sale.updated` webhook.\n\n`commissionEarned` is handled separately from the other fields: it updates the commission record for the affiliate credited with the sale, not the sale itself. Every other field you send is applied directly to the sale, so sending a field that is not listed below will fail the request.",
        "requestBody": {
          "description": "Fields to update",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SaleUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated sale, including its recalculated `commissionEarned`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sale"
                }
              }
            }
          },
          "400": {
            "description": "`saleId` was missing, or the request contained a field that cannot be updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No sale with that ID exists in this program.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "summary": "Mark a sale as refunded",
        "description": "Marks a sale as refunded and reverses the commission attached to it.\n\nIf the sale was already refunded the request still succeeds, with `alreadyRefunded` set to `true`. A sale cannot be refunded once it has passed your program's refund window, has been added to a payout batch, has already been paid out, or has been marked as manually paid — each of those returns a 400 with a `reason` you can branch on.",
        "requestBody": {
          "description": "The sale to refund",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SaleRefund"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The refund was processed, or the sale was already refunded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SaleRefundResponse"
                }
              }
            }
          },
          "400": {
            "description": "The sale is not eligible to be refunded. `reason` is one of `outside_refund_window`, `included_in_payout_batch`, `already_paid` or `manually_paid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SaleRefundError"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No sale with that ID exists in this program.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SaleRefundError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "summary": "Delete sale",
        "description": "Permanently deletes a sale and the commissions that cascade from it. This cannot be undone.\n\nThe request succeeds with the same response even when no sale matched. Fires the `sale.deleted` webhook.",
        "requestBody": {
          "description": "The sale to delete",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SaleDelete"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The sale was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteMessage"
                }
              }
            }
          },
          "400": {
            "description": "`saleId` was not supplied, or the delete failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProgramNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AffiliateLinkRecord": {
        "type": "object",
        "description": "A single referral link belonging to an affiliate",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Affiliate link record ID"
          },
          "link": {
            "type": "string",
            "description": "Link slug (path segment) for this affiliate"
          },
          "userId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Affiliate user ID"
          },
          "programId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Affiliate program ID"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When this link was created"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When this link was last updated"
          }
        }
      },
      "Affiliate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The affiliate ID"
          },
          "firstName": {
            "type": "string",
            "description": "First name of the affiliate"
          },
          "lastName": {
            "type": "string",
            "description": "Last name of the affiliate"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Full name of the affiliate (optional)"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email of the affiliate"
          },
          "password": {
            "type": "string",
            "nullable": true,
            "description": "Hashed Password of the affiliate (optional)",
            "deprecated": true
          },
          "emailVerified": {
            "type": "boolean",
            "nullable": true,
            "description": "Flag to indicate if the email has been verified"
          },
          "image": {
            "type": "string",
            "nullable": true,
            "description": "Profile image URL of the affiliate"
          },
          "detailsComplete": {
            "type": "boolean",
            "description": "Flag to indicate if affiliate details are complete"
          },
          "programId": {
            "type": "string",
            "format": "uuid",
            "description": "The program ID that the affiliate is associated with"
          },
          "payoutEmail": {
            "type": "string",
            "format": "email",
            "description": "Email to be used for payouts"
          },
          "paymentMethod": {
            "type": "string",
            "description": "Payment method for the affiliate (e.g., WISE)"
          },
          "commissionRate": {
            "type": "number",
            "format": "float",
            "description": "Commission rate for the affiliate (0 - 100)"
          },
          "link": {
            "type": "string",
            "nullable": true,
            "deprecated": true,
            "description": "Deprecated legacy field and almost always `null`. Read `affiliateLinks` instead."
          },
          "affiliateLinks": {
            "type": "array",
            "description": "All affiliate link records for this affiliate. This is the source of truth for referral link slugs.",
            "items": {
              "$ref": "#/components/schemas/AffiliateLinkRecord"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "INACTIVE",
              "INVITED",
              "DECLINED",
              "DEACTIVATED",
              "BANNED"
            ],
            "description": "Affiliate status. `INACTIVE` is shown as **Pending** in the dashboard and is kept for backwards compatibility."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the affiliate was created"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the affiliate was last updated"
          },
          "numberOfReferredUsers": {
            "type": "integer",
            "description": "Number of users referred by the affiliate"
          },
          "numberOfClicks": {
            "type": "integer",
            "description": "Number of clicks generated by the affiliate"
          },
          "totalCommissionEarned": {
            "type": "number",
            "format": "float",
            "description": "Total commission earned by the affiliate"
          },
          "source": {
            "type": "string",
            "nullable": true,
            "description": "How the affiliate joined. Affiliates created through this API have `API`."
          },
          "commissionPlanId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The commission plan assigned to this affiliate, if any"
          },
          "affiliateGroupId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The affiliate group this affiliate belongs to, if any"
          }
        }
      },
      "NewAffiliate": {
        "type": "object",
        "required": [
          "firstName",
          "lastName",
          "email"
        ],
        "properties": {
          "firstName": {
            "type": "string",
            "description": "First name of the affiliate"
          },
          "lastName": {
            "type": "string",
            "description": "Last name of the affiliate"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email of the affiliate"
          },
          "commissionRate": {
            "type": "number",
            "format": "float",
            "description": "Commission rate for the affiliate"
          },
          "affiliateLink": {
            "type": "string",
            "description": "Optional slug for the affiliate's first referral link. If omitted, a slug is generated automatically."
          },
          "affiliateStatus": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ],
            "description": "Status of the new affiliate. If omitted, the program's auto-approve setting decides whether the affiliate is created active or inactive."
          },
          "sendWelcomeEmail": {
            "type": "boolean",
            "default": true,
            "description": "Set to false to skip the affiliate welcome email. The welcome email is only sent when the affiliate is created active."
          },
          "commissionPlanId": {
            "type": "string",
            "format": "uuid",
            "description": "Assign the affiliate to a commission plan in this program. Returns 400 if the plan does not exist in this program."
          },
          "affiliateGroupId": {
            "type": "string",
            "format": "uuid",
            "description": "Assign the affiliate to an affiliate group in this program. Returns 400 if the group does not exist, or if commissionPlanId belongs to a different group."
          }
        }
      },
      "AffiliateUpdate": {
        "type": "object",
        "description": "Identify the affiliate with either affiliateId or emailAddress.",
        "properties": {
          "affiliateId": {
            "type": "string",
            "description": "The ID of the affiliate (required if emailAddress is not provided)"
          },
          "emailAddress": {
            "type": "string",
            "format": "email",
            "description": "The email of the affiliate (required if affiliateId is not provided)"
          },
          "firstName": {
            "type": "string",
            "description": "First name of the affiliate"
          },
          "lastName": {
            "type": "string",
            "description": "Last name of the affiliate"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email of the affiliate"
          },
          "commissionRate": {
            "type": "number",
            "format": "float",
            "description": "Commission rate for the affiliate"
          },
          "affiliateStatus": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ],
            "description": "Set the affiliate active or pending. Any other value falls back to the program's auto-approve setting."
          },
          "affiliateLink": {
            "type": "string",
            "description": "Adds an additional referral link with this slug. Existing links are kept — this never renames or replaces them. Fails with 400 if the slug is already used in this program."
          }
        }
      },
      "AffiliateDelete": {
        "type": "object",
        "required": [
          "affiliateId"
        ],
        "properties": {
          "affiliateId": {
            "type": "string",
            "description": "The ID of the affiliate to delete. An affiliate email address is also accepted."
          }
        }
      },
      "Referral": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The referral ID"
          },
          "affiliateId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The affiliate credited with this referral"
          },
          "affiliateProgramId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The affiliate program this referral belongs to"
          },
          "affiliateLinkId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The affiliate link the referral came through"
          },
          "commissionPlanId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The commission plan applied to this referral"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Name of the referred user"
          },
          "email": {
            "type": "string",
            "format": "email",
            "nullable": true,
            "description": "Email of the referred user"
          },
          "referredUserExternalId": {
            "type": "string",
            "description": "The ID you maintain for this user in your own system — for example your database user ID or a Stripe customer ID."
          },
          "plan": {
            "type": "string",
            "nullable": true,
            "description": "Plan the referred user is on. Defaults to `N/A` when not supplied."
          },
          "subscriptionStatus": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "SUBMITTED",
              "DECLINED"
            ],
            "nullable": true,
            "description": "Status of the referral. Referrals created through this API are always `ACTIVE`."
          },
          "submissionType": {
            "type": "string",
            "enum": [
              "MANUAL",
              "AUTOMATIC"
            ],
            "nullable": true,
            "description": "Whether the referral was submitted by hand or captured automatically"
          },
          "referralMedium": {
            "type": "string",
            "enum": [
              "COUPON",
              "LINK"
            ],
            "nullable": true,
            "description": "How the referral was tracked"
          },
          "source": {
            "type": "string",
            "enum": [
              "UNKNOWN",
              "API",
              "INTEGRATION",
              "MANUAL",
              "IMPORTED",
              "AFFILIATE_SUBMITTED"
            ],
            "description": "Where the referral came from. Referrals created through this API have `API`."
          },
          "totalRevenue": {
            "type": "number",
            "format": "float",
            "description": "Total revenue attributed to this referral"
          },
          "totalCommission": {
            "type": "number",
            "format": "float",
            "description": "Total commission generated by this referral"
          },
          "initialLandingPage": {
            "type": "string",
            "nullable": true,
            "description": "The first page the referred user landed on"
          },
          "notes": {
            "type": "string",
            "nullable": true,
            "description": "Free-text notes on the referral"
          },
          "metadata": {
            "type": "object",
            "nullable": true,
            "description": "Arbitrary JSON you can attach to the referral"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the referral was created"
          }
        }
      },
      "NewReferral": {
        "type": "object",
        "required": [
          "name",
          "email",
          "referredUserExternalId"
        ],
        "properties": {
          "affiliateId": {
            "type": "string",
            "description": "Identifies the affiliate to credit. Accepts an affiliate UUID, one of their affiliate link slugs, or a numeric click ID. Required unless `affiliateEmail` or `promoCode` is supplied."
          },
          "affiliateEmail": {
            "type": "string",
            "format": "email",
            "description": "Email of the affiliate to credit. Required unless `affiliateId` or `promoCode` is supplied."
          },
          "promoCode": {
            "type": "string",
            "description": "A promotional code belonging to the affiliate to credit. Required unless `affiliateId` or `affiliateEmail` is supplied. The code is validated for existence, active state, expiry and redemption limits, and its redemption counters are incremented on success."
          },
          "name": {
            "type": "string",
            "description": "Name of the referred user"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email of the referred user"
          },
          "referredUserExternalId": {
            "type": "string",
            "description": "The ID you maintain for this user in your own system — for example your database user ID or a Stripe customer ID."
          },
          "plan": {
            "type": "string",
            "default": "N/A",
            "description": "Plan the referred user signed up on."
          }
        }
      },
      "ReferralUpdate": {
        "type": "object",
        "required": [
          "referralId"
        ],
        "properties": {
          "referralId": {
            "type": "string",
            "format": "uuid",
            "description": "The referral to update"
          },
          "name": {
            "type": "string",
            "description": "Name of the referred user"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email of the referred user. Lowercased before saving."
          },
          "referredUserExternalId": {
            "type": "string",
            "description": "The ID you maintain for this user in your own system."
          },
          "plan": {
            "type": "string",
            "description": "Plan the referred user is on"
          },
          "subscriptionStatus": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "SUBMITTED",
              "DECLINED"
            ],
            "description": "Status of the referral"
          },
          "notes": {
            "type": "string",
            "description": "Free-text notes on the referral"
          },
          "metadata": {
            "type": "object",
            "description": "Arbitrary JSON to attach to the referral"
          }
        }
      },
      "ReferralDelete": {
        "type": "object",
        "required": [
          "referralId"
        ],
        "properties": {
          "referralId": {
            "type": "string",
            "description": "The referral to delete. Accepts the referral's UUID or the referred user's email address."
          }
        }
      },
      "Sale": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "The sale ID"
          },
          "affiliateId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The affiliate credited with this sale"
          },
          "referralId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The referral this sale belongs to"
          },
          "affiliateProgramId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The affiliate program this sale belongs to"
          },
          "affiliateLinkId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The affiliate link the sale was attributed to"
          },
          "promotionalCodeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The promotional code used on this sale, if any"
          },
          "externalId": {
            "type": "string",
            "nullable": true,
            "description": "The ID of this sale in your own system. Unique per program."
          },
          "externalInvoiceId": {
            "type": "string",
            "nullable": true,
            "description": "The ID of the invoice in your own system. Unique per program."
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Name of the customer"
          },
          "email": {
            "type": "string",
            "format": "email",
            "nullable": true,
            "description": "Email of the customer"
          },
          "totalEarned": {
            "type": "number",
            "format": "float",
            "description": "Gross value of the sale"
          },
          "commissionRate": {
            "type": "number",
            "format": "float",
            "nullable": true,
            "description": "Commission rate applied to this sale"
          },
          "commissionEarned": {
            "type": "number",
            "format": "float",
            "nullable": true,
            "description": "Commission recorded for the credited affiliate. Merged in by the API from the sale's commission record — it is not a column on the sale itself."
          },
          "taxAmount": {
            "type": "number",
            "format": "float",
            "description": "Tax deducted before commission was calculated"
          },
          "shippingAmount": {
            "type": "number",
            "format": "float",
            "description": "Shipping deducted before commission was calculated"
          },
          "productsBought": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Product identifiers attached to this sale"
          },
          "clicks": {
            "type": "integer",
            "description": "Clicks attributed to this sale"
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "REFUNDED"
            ],
            "description": "Whether the sale is live or has been refunded"
          },
          "refundedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the sale was marked refunded"
          },
          "paymentTrigger": {
            "type": "string",
            "enum": [
              "SIGNUP",
              "PURCHASE",
              "BONUS",
              "CONTENT_REWARD"
            ],
            "description": "What triggered the commission for this sale"
          },
          "source": {
            "type": "string",
            "enum": [
              "UNKNOWN",
              "API",
              "INTEGRATION",
              "MANUAL",
              "IMPORTED",
              "AUTOMATED"
            ],
            "description": "Where the sale came from. Sales created through this API have `API`."
          },
          "metadata": {
            "type": "object",
            "nullable": true,
            "description": "Arbitrary JSON you can attach to the sale"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the sale was recorded"
          }
        }
      },
      "NewSale": {
        "type": "object",
        "required": [
          "totalEarned"
        ],
        "properties": {
          "referralId": {
            "type": "string",
            "description": "Identifies who the sale belongs to. Accepts a referral UUID, an affiliate UUID, an affiliate link slug, or a numeric click ID. Required unless `email` or `promoCode` is supplied."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email of the customer. Also used to match an existing referral. Required unless `referralId` or `promoCode` is supplied."
          },
          "promoCode": {
            "type": "string",
            "description": "A promotional code belonging to the affiliate to credit. Required unless `referralId` or `email` is supplied. Validated for existence, active state, expiry and redemption limits; its redemption counters are incremented on success."
          },
          "name": {
            "type": "string",
            "description": "Name of the customer. Required unless it can be taken from the matched referral."
          },
          "totalEarned": {
            "type": "number",
            "format": "float",
            "description": "Gross value of the sale. Must be non-zero."
          },
          "commissionRate": {
            "type": "number",
            "format": "float",
            "description": "Overrides the affiliate's commission rate for this sale only. Defaults to the affiliate's own rate."
          },
          "externalId": {
            "type": "string",
            "description": "The ID of this sale in your own system. Must be unique within the program."
          },
          "externalInvoiceId": {
            "type": "string",
            "description": "The ID of the invoice in your own system. Must be unique within the program."
          },
          "product": {
            "description": "A single product line for this sale",
            "allOf": [
              {
                "$ref": "#/components/schemas/Product"
              }
            ]
          },
          "tax": {
            "type": "number",
            "format": "float",
            "description": "Tax to deduct before commission is calculated. Only applied when your program has tax deduction enabled."
          },
          "shipping": {
            "type": "number",
            "format": "float",
            "description": "Shipping to deduct before commission is calculated. Only applied when your program has shipping deduction enabled."
          }
        }
      },
      "Product": {
        "type": "object",
        "properties": {
          "productId": {
            "type": "string",
            "description": "Product ID"
          },
          "quantity": {
            "type": "number",
            "description": "Quantity purchased"
          },
          "price": {
            "type": "number",
            "format": "float",
            "description": "Unit price at time of sale"
          },
          "name": {
            "type": "string",
            "description": "Product name"
          }
        }
      },
      "SaleUpdate": {
        "type": "object",
        "required": [
          "saleId"
        ],
        "properties": {
          "saleId": {
            "type": "integer",
            "description": "The sale to update"
          },
          "name": {
            "type": "string",
            "description": "Name of the customer"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email of the customer"
          },
          "totalEarned": {
            "type": "number",
            "format": "float",
            "description": "Gross value of the sale"
          },
          "commissionRate": {
            "type": "number",
            "format": "float",
            "description": "Commission rate recorded on the sale"
          },
          "commissionEarned": {
            "type": "number",
            "format": "float",
            "description": "Sets the commission recorded for the credited affiliate. Applied to the commission record rather than the sale."
          },
          "externalId": {
            "type": "string",
            "description": "The ID of this sale in your own system"
          },
          "externalInvoiceId": {
            "type": "string",
            "description": "The ID of the invoice in your own system"
          },
          "metadata": {
            "type": "object",
            "description": "Arbitrary JSON to attach to the sale"
          }
        }
      },
      "SaleDelete": {
        "type": "object",
        "required": [
          "saleId"
        ],
        "properties": {
          "saleId": {
            "type": "integer",
            "description": "The sale to delete"
          }
        }
      },
      "SaleRefund": {
        "type": "object",
        "required": [
          "saleId"
        ],
        "properties": {
          "saleId": {
            "type": "integer",
            "description": "The sale ID to mark as refunded"
          }
        }
      },
      "SaleRefundResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the refund request was handled successfully"
          },
          "alreadyRefunded": {
            "type": "boolean",
            "description": "Whether the sale had already been marked as refunded before this request"
          },
          "reason": {
            "type": "string",
            "description": "Why the request resolved the way it did — for example `refunded` or `already_refunded`."
          }
        }
      },
      "SaleRefundError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human readable description of why the sale could not be refunded"
          },
          "reason": {
            "type": "string",
            "description": "Machine readable reason: `outside_refund_window`, `included_in_payout_batch`, `already_paid`, `manually_paid` or `sale_not_found`"
          }
        }
      },
      "AffiliateLink": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The affiliate link ID"
          },
          "link": {
            "type": "string",
            "description": "The affiliate link string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the affiliate link was created"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the affiliate link was last updated"
          },
          "affiliate": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "description": "The affiliate ID"
              },
              "name": {
                "type": "string",
                "description": "Full name of the affiliate"
              },
              "email": {
                "type": "string",
                "format": "email",
                "description": "Email of the affiliate"
              }
            },
            "nullable": true,
            "description": "The affiliate who owns this link. `null` if the link has no owner."
          },
          "program": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "description": "The program ID"
              },
              "name": {
                "type": "string",
                "description": "Name of the affiliate program"
              },
              "currency": {
                "type": "string",
                "description": "Currency used by the program"
              }
            },
            "nullable": true,
            "description": "The program this link belongs to. `null` if the link has no program."
          },
          "clicks": {
            "type": "integer",
            "description": "Number of clicks on this affiliate link"
          },
          "referrals": {
            "type": "integer",
            "description": "Number of referrals generated by this link"
          },
          "sales": {
            "type": "integer",
            "description": "Number of sales generated by this link"
          },
          "totalRevenue": {
            "type": "number",
            "format": "float",
            "description": "Sum of `totalEarned` across every sale attributed to this link"
          },
          "fullURLs": {
            "type": "array",
            "description": "One entry per base URL configured on your program, with the affiliate's tracking parameter and any program URL parameters already applied. Share these directly.",
            "items": {
              "type": "object",
              "properties": {
                "baseUrl": {
                  "type": "string",
                  "description": "The base URL"
                },
                "fullUrl": {
                  "type": "string",
                  "description": "The complete affiliate URL with parameters"
                }
              }
            }
          }
        },
        "description": "An affiliate link as returned by the affiliate link endpoints. The counters and `fullURLs` are computed per request and are not stored on the link itself."
      },
      "NewAffiliateLink": {
        "type": "object",
        "required": [
          "link"
        ],
        "properties": {
          "link": {
            "type": "string",
            "description": "The slug for the new link. Letters, numbers and hyphens only, and unique within the program.",
            "example": "summer-campaign"
          },
          "affiliateId": {
            "type": "string",
            "format": "uuid",
            "description": "The affiliate who will own this link. Required unless `affiliateEmail` is supplied."
          },
          "affiliateEmail": {
            "type": "string",
            "format": "email",
            "description": "Email of the affiliate who will own this link. Required unless `affiliateId` is supplied."
          }
        }
      },
      "AffiliateLinkUpdate": {
        "type": "object",
        "required": [
          "newLink"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The affiliate link to rename. Required unless `link` is supplied."
          },
          "link": {
            "type": "string",
            "description": "The link's current slug. Required unless `id` is supplied."
          },
          "newLink": {
            "type": "string",
            "description": "The new slug. Letters, numbers and hyphens only, and unique within the program.",
            "example": "winter-campaign"
          }
        }
      },
      "AffiliateLinkDelete": {
        "type": "object",
        "description": "Supply either `id` or `link`.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The affiliate link to delete. Required unless `link` is supplied."
          },
          "link": {
            "type": "string",
            "description": "The slug of the link to delete. Required unless `id` is supplied."
          }
        }
      },
      "Coupon": {
        "type": "object",
        "description": "A discount definition. Promotional codes point at a coupon for their value.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The coupon ID"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Name of the coupon"
          },
          "externalId": {
            "type": "string",
            "nullable": true,
            "description": "The coupon's ID in the connected billing provider, for example a Stripe coupon ID"
          },
          "couponType": {
            "type": "string",
            "enum": [
              "PERCENTAGE",
              "FLAT"
            ],
            "description": "Whether the discount is a percentage or a fixed amount"
          },
          "percentOff": {
            "type": "number",
            "format": "float",
            "nullable": true,
            "description": "Percentage taken off, 1–100. Used when `couponType` is `PERCENTAGE`."
          },
          "amountOff": {
            "type": "number",
            "format": "float",
            "nullable": true,
            "description": "Fixed amount taken off. Used when `couponType` is `FLAT`."
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "description": "Currency for `amountOff`, for example `USD`"
          },
          "duration": {
            "type": "string",
            "enum": [
              "once",
              "forever",
              "repeating"
            ],
            "description": "How long the discount keeps applying to a subscription"
          },
          "durationInMonths": {
            "type": "integer",
            "nullable": true,
            "description": "Number of months the discount repeats for. Only set when `duration` is `repeating`."
          },
          "maxRedemptions": {
            "type": "integer",
            "nullable": true,
            "description": "Total redemptions allowed across every promotional code on this coupon. `null` means unlimited."
          },
          "timesRedeemed": {
            "type": "integer",
            "description": "How many times this coupon has been redeemed so far"
          },
          "couponCategory": {
            "type": "string",
            "enum": [
              "CUSTOMER",
              "PAYOUT"
            ],
            "description": "Whether the coupon discounts customers or is used to pay affiliates as a non-cash reward"
          },
          "limitToProducts": {
            "type": "boolean",
            "description": "Whether the coupon only applies to `productIds`"
          },
          "productIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Products the coupon is restricted to"
          },
          "collectionIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Collections the coupon is restricted to"
          },
          "valid": {
            "type": "boolean",
            "description": "Whether the coupon can currently be redeemed"
          },
          "redeemBy": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Last date the coupon can be redeemed"
          },
          "integrationType": {
            "type": "string",
            "enum": [
              "NONE",
              "STRIPE",
              "CHARGEBEE",
              "PADDLE",
              "SHOPIFY",
              "WOOCOMMERCE",
              "ZYLVIE",
              "POLAR"
            ],
            "nullable": true,
            "description": "The billing provider this coupon is synced with"
          },
          "affiliateProgramId": {
            "type": "string",
            "format": "uuid",
            "description": "The affiliate program this coupon belongs to"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the coupon was created"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the coupon was last updated"
          },
          "promotionalCodes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PromotionalCode"
            },
            "description": "Promotional codes that point at this coupon"
          },
          "autoCouponRule": {
            "type": "object",
            "nullable": true,
            "description": "Rule used to generate codes automatically for affiliates, if configured"
          }
        }
      },
      "NewCoupon": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the coupon",
            "example": "Launch 20%"
          },
          "couponType": {
            "type": "string",
            "enum": [
              "PERCENTAGE",
              "FLAT"
            ],
            "default": "PERCENTAGE",
            "description": "Whether the discount is a percentage or a fixed amount"
          },
          "percentOff": {
            "type": "number",
            "format": "float",
            "description": "Percentage taken off, 1–100. Required when `couponType` is `PERCENTAGE`."
          },
          "amountOff": {
            "type": "number",
            "format": "float",
            "description": "Fixed amount taken off. Required when `couponType` is `FLAT`."
          },
          "currency": {
            "type": "string",
            "description": "Currency for `amountOff`. Required when `couponType` is `FLAT`.",
            "example": "USD"
          },
          "duration": {
            "type": "string",
            "enum": [
              "once",
              "forever",
              "repeating"
            ],
            "default": "forever",
            "description": "How long the discount keeps applying to a subscription"
          },
          "durationInMonths": {
            "type": "integer",
            "description": "Number of months the discount repeats for. Required when `duration` is `repeating`."
          },
          "maxRedemptions": {
            "type": "integer",
            "description": "Total redemptions allowed across every promotional code on this coupon. Omit for unlimited."
          },
          "limitToProducts": {
            "type": "boolean",
            "default": false,
            "description": "Restrict the coupon to `productIds`"
          },
          "productIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Products the coupon applies to"
          },
          "valid": {
            "type": "boolean",
            "default": true,
            "description": "Whether the coupon can be redeemed straight away"
          },
          "redeemBy": {
            "type": "string",
            "format": "date-time",
            "description": "Last date the coupon can be redeemed"
          },
          "externalId": {
            "type": "string",
            "description": "The coupon's ID in the connected billing provider. Must be unique."
          },
          "integrationType": {
            "type": "string",
            "enum": [
              "NONE",
              "STRIPE",
              "CHARGEBEE",
              "PADDLE",
              "SHOPIFY",
              "WOOCOMMERCE",
              "ZYLVIE",
              "POLAR"
            ],
            "description": "The billing provider this coupon is synced with"
          }
        }
      },
      "CouponUpdate": {
        "type": "object",
        "required": [
          "couponId"
        ],
        "properties": {
          "couponId": {
            "type": "string",
            "format": "uuid",
            "description": "The coupon to update"
          },
          "name": {
            "type": "string",
            "description": "Name of the coupon"
          },
          "couponType": {
            "type": "string",
            "enum": [
              "PERCENTAGE",
              "FLAT"
            ],
            "description": "Whether the discount is a percentage or a fixed amount"
          },
          "percentOff": {
            "type": "number",
            "format": "float",
            "description": "Percentage taken off, 1–100"
          },
          "amountOff": {
            "type": "number",
            "format": "float",
            "description": "Fixed amount taken off. Must be greater than zero."
          },
          "currency": {
            "type": "string",
            "description": "Currency for `amountOff`"
          },
          "duration": {
            "type": "string",
            "enum": [
              "once",
              "forever",
              "repeating"
            ],
            "description": "How long the discount keeps applying to a subscription"
          },
          "durationInMonths": {
            "type": "integer",
            "description": "Number of months the discount repeats for"
          },
          "maxRedemptions": {
            "type": "integer",
            "description": "Total redemptions allowed"
          },
          "limitToProducts": {
            "type": "boolean",
            "description": "Restrict the coupon to `productIds`"
          },
          "productIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Products the coupon applies to"
          },
          "valid": {
            "type": "boolean",
            "description": "Set to `false` to stop the coupon being redeemed"
          },
          "redeemBy": {
            "type": "string",
            "format": "date-time",
            "description": "Last date the coupon can be redeemed"
          }
        }
      },
      "CouponDelete": {
        "type": "object",
        "required": [
          "couponId"
        ],
        "properties": {
          "couponId": {
            "type": "string",
            "format": "uuid",
            "description": "The coupon to delete. Its promotional codes are deleted with it."
          }
        }
      },
      "PromotionalCode": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The promotional code ID"
          },
          "code": {
            "type": "string",
            "description": "The actual promotional code string"
          },
          "couponId": {
            "type": "string",
            "format": "uuid",
            "description": "The parent coupon ID"
          },
          "affiliateId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The affiliate ID this code is assigned to"
          },
          "externalId": {
            "type": "string",
            "nullable": true,
            "description": "External ID (e.g., Stripe promotion code ID)"
          },
          "active": {
            "type": "boolean",
            "description": "Whether the promotional code is active"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Expiration date for the code"
          },
          "maxRedemptions": {
            "type": "integer",
            "nullable": true,
            "description": "How many times this code can be redeemed. `null` means unlimited. Redemptions are also capped by the parent coupon's own limit."
          },
          "timesRedeemed": {
            "type": "integer",
            "description": "Number of times this code has been redeemed"
          },
          "firstTimeOrder": {
            "type": "boolean",
            "description": "Whether this code is limited to first-time orders"
          },
          "minimumAmount": {
            "type": "number",
            "format": "float",
            "nullable": true,
            "description": "Minimum purchase amount required"
          },
          "minimumAmountCurrency": {
            "type": "string",
            "nullable": true,
            "description": "Currency for minimum amount"
          },
          "limitToCustomers": {
            "type": "boolean",
            "description": "Whether code is limited to specific customers"
          },
          "customerId": {
            "type": "string",
            "nullable": true,
            "description": "Specific customer ID this code is limited to"
          },
          "limitToAffiliate": {
            "type": "boolean",
            "description": "Whether only the assigned affiliate may redeem this code"
          },
          "isAutoGenerated": {
            "type": "boolean",
            "description": "Whether this code was generated from a `codeStructure` rather than supplied by hand"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the code was created"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the code was last updated"
          },
          "coupon": {
            "description": "The coupon this code takes its discount from",
            "allOf": [
              {
                "$ref": "#/components/schemas/Coupon"
              }
            ]
          },
          "affiliate": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "firstName": {
                "type": "string"
              },
              "lastName": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              }
            },
            "description": "Affiliate details"
          },
          "isAffiliateGenerated": {
            "type": "boolean",
            "description": "Whether the affiliate created this code themselves from their portal"
          }
        },
        "description": "A code customers enter at checkout. Its discount comes from the coupon it belongs to."
      },
      "NewPromotionalCode": {
        "type": "object",
        "required": [
          "couponId"
        ],
        "properties": {
          "couponId": {
            "type": "string",
            "format": "uuid",
            "description": "The coupon ID this promotional code belongs to"
          },
          "code": {
            "type": "string",
            "description": "The exact code string customers will type. Required unless `isAutoGenerated` is `true`. Must be unique within the program.",
            "example": "JANE20"
          },
          "affiliateId": {
            "type": "string",
            "format": "uuid",
            "description": "Assign the code to this affiliate so their redemptions are credited. Required when `isAutoGenerated` is `true` unless `affiliateEmail` is supplied."
          },
          "affiliateEmail": {
            "type": "string",
            "format": "email",
            "description": "Assign the code to the affiliate with this email address. Alternative to `affiliateId`."
          },
          "externalId": {
            "type": "string",
            "description": "The code's ID in the connected billing provider, for example a Stripe promotion code ID"
          },
          "active": {
            "type": "boolean",
            "description": "Whether the code is active (default: true)"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Expiration date"
          },
          "maxRedemptions": {
            "type": "integer",
            "description": "Maximum redemptions"
          },
          "firstTimeOrder": {
            "type": "boolean",
            "description": "Limit to first-time orders (default: false)"
          },
          "minimumAmount": {
            "type": "number",
            "format": "float",
            "description": "Minimum purchase amount"
          },
          "minimumAmountCurrency": {
            "type": "string",
            "description": "Currency for minimum amount"
          },
          "limitToCustomers": {
            "type": "boolean",
            "description": "Limit to specific customers (default: false)"
          },
          "customerId": {
            "type": "string",
            "description": "Specific customer ID"
          },
          "limitToAffiliate": {
            "type": "boolean",
            "description": "Only affiliate can use (default: false)"
          },
          "isAutoGenerated": {
            "type": "boolean",
            "description": "Generate the code from `codeStructure` instead of using `code`. Requires an affiliate."
          },
          "codeStructure": {
            "type": "object",
            "description": "Which pieces to combine into the generated code, in this order: first name, last name, email prefix, discount value, coupon name, then random characters. Required when `isAutoGenerated` is `true`.",
            "properties": {
              "firstName": {
                "type": "boolean",
                "description": "Include affiliate's first name"
              },
              "lastName": {
                "type": "boolean",
                "description": "Include affiliate's last name"
              },
              "email": {
                "type": "boolean",
                "description": "Include part of affiliate's email"
              },
              "discountAmount": {
                "type": "boolean",
                "description": "Include discount value"
              },
              "couponName": {
                "type": "boolean",
                "description": "Include coupon name"
              },
              "randomChars": {
                "type": "boolean",
                "description": "Add random characters"
              }
            }
          },
          "prefix": {
            "type": "string",
            "description": "String placed at the front of a generated code"
          },
          "randomCharsLength": {
            "type": "integer",
            "description": "Length of random characters (default: 4)",
            "default": 4
          },
          "randomCharsCase": {
            "type": "string",
            "enum": [
              "uppercase",
              "lowercase",
              "mixed"
            ],
            "description": "Case for random characters (default: mixed)",
            "default": "mixed"
          }
        }
      },
      "PromotionalCodeUpdate": {
        "type": "object",
        "required": [
          "promotionalCodeId"
        ],
        "properties": {
          "promotionalCodeId": {
            "type": "string",
            "format": "uuid",
            "description": "The promotional code ID to update"
          },
          "code": {
            "type": "string",
            "description": "Rename the code. Must be unique within the program."
          },
          "active": {
            "type": "boolean",
            "description": "Set to `false` to retire the code without deleting it"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Expiration date"
          },
          "maxRedemptions": {
            "type": "integer",
            "description": "Maximum redemptions"
          },
          "firstTimeOrder": {
            "type": "boolean",
            "description": "Limit to first-time orders"
          },
          "minimumAmount": {
            "type": "number",
            "format": "float",
            "description": "Minimum purchase amount"
          },
          "minimumAmountCurrency": {
            "type": "string",
            "description": "Currency for minimum amount"
          },
          "limitToCustomers": {
            "type": "boolean",
            "description": "Limit to specific customers"
          },
          "customerId": {
            "type": "string",
            "description": "Specific customer ID"
          },
          "limitToAffiliate": {
            "type": "boolean",
            "description": "Only affiliate can use"
          },
          "externalId": {
            "type": "string",
            "description": "The code's ID in the connected billing provider"
          }
        }
      },
      "PromotionalCodeDelete": {
        "type": "object",
        "required": [
          "promotionalCodeId"
        ],
        "properties": {
          "promotionalCodeId": {
            "type": "string",
            "format": "uuid",
            "description": "The promotional code ID to delete"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human readable description of what went wrong"
          }
        }
      },
      "UpdateCount": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "description": "How many records matched the filter and were updated",
            "example": 1
          }
        }
      },
      "DeleteMessage": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Confirmation message"
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "The `Authorization` header is missing, is not a `Bearer` header, or the token is not valid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "API access is not enabled for this account.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ProgramNotFound": {
        "description": "The affiliate program this token belongs to no longer exists.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests for this token on this endpoint. Please slow down.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}