{
  "openapi": "3.0.0",
  "paths": {
    "/api/v1/health": {
      "get": {
        "operationId": "AppController_getHealth",
        "parameters": [],
        "responses": {
          "200": {
            "description": "API is running"
          }
        },
        "summary": "Shallow health check",
        "tags": ["health"]
      }
    },
    "/api/v1/health/ready": {
      "get": {
        "operationId": "AppController_getHealthReady",
        "parameters": [],
        "responses": {
          "200": {
            "description": "All dependencies reachable"
          },
          "503": {
            "description": "One or more dependencies unreachable"
          }
        },
        "summary": "Deep health check — verifies DB and Redis connectivity",
        "tags": ["health"]
      }
    },
    "/api/v1/programs/{programId}/commissions": {
      "get": {
        "operationId": "CommissionsController_list",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program UUID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sortOrder",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["asc", "desc"],
              "type": "string"
            }
          },
          {
            "name": "sortBy",
            "required": false,
            "in": "query",
            "schema": {
              "enum": [
                "createdAt",
                "amountCents",
                "status",
                "commissionType",
                "needsReview",
                "partnerName"
              ],
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateTo",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateFrom",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "needsReview",
            "required": false,
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["pending", "approved", "rejected", "paid", "refunded"],
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated commission list"
          },
          "400": {
            "description": "Invalid query parameters"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List commissions for a program",
        "tags": ["Commissions"]
      }
    },
    "/api/v1/programs/{programId}/commissions/stats": {
      "get": {
        "operationId": "CommissionsController_stats",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program UUID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateTo",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateFrom",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "needsReview",
            "required": false,
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["pending", "approved", "rejected", "paid", "refunded"],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Commission stats"
          },
          "400": {
            "description": "Invalid query parameters"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get commission stats for a program",
        "tags": ["Commissions"]
      }
    },
    "/api/v1/programs/{programId}/commissions/bulk-approve": {
      "patch": {
        "operationId": "CommissionsController_bulkApprove",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program UUID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "All commissions approved"
          },
          "207": {
            "description": "Partial success"
          },
          "400": {
            "description": "Invalid request body"
          },
          "401": {
            "description": "Unauthorized"
          },
          "422": {
            "description": "All commissions failed to approve"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Bulk approve commissions",
        "tags": ["Commissions"]
      }
    },
    "/api/v1/programs/{programId}/commissions/bulk-reject": {
      "patch": {
        "operationId": "CommissionsController_bulkReject",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program UUID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "All commissions rejected"
          },
          "207": {
            "description": "Partial success"
          },
          "400": {
            "description": "Invalid request body"
          },
          "401": {
            "description": "Unauthorized"
          },
          "422": {
            "description": "All commissions failed to reject"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Bulk reject commissions",
        "tags": ["Commissions"]
      }
    },
    "/api/v1/programs/{programId}/commissions/{commissionId}": {
      "get": {
        "operationId": "CommissionsController_getOne",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program UUID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commissionId",
            "required": true,
            "in": "path",
            "description": "Commission UUID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Commission detail"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Commission not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get commission detail",
        "tags": ["Commissions"]
      }
    },
    "/api/v1/programs/{programId}/commissions/{commissionId}/approve": {
      "patch": {
        "operationId": "CommissionsController_approve",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program UUID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commissionId",
            "required": true,
            "in": "path",
            "description": "Commission UUID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Commission approved"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Commission not found"
          },
          "409": {
            "description": "Commission is not in a pending state or hold active"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Approve a commission",
        "tags": ["Commissions"]
      }
    },
    "/api/v1/programs/{programId}/commissions/{commissionId}/approve-override": {
      "patch": {
        "operationId": "CommissionsController_approveOverride",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program UUID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commissionId",
            "required": true,
            "in": "path",
            "description": "Commission UUID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["reason"],
                "properties": {
                  "reason": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "Mandatory merchant-internal reason for the early approval"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Commission approved early"
          },
          "400": {
            "description": "Invalid request body (reason)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Commission not found"
          },
          "409": {
            "description": "Not pending, flagged for review, or hold not active (COMMISSION_NOT_PENDING / COMMISSION_NEEDS_REVIEW / COMMISSION_HOLD_NOT_ACTIVE)"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Approve a commission early, overriding the active refund hold",
        "tags": ["Commissions"]
      }
    },
    "/api/v1/programs/{programId}/commissions/{commissionId}/reject": {
      "patch": {
        "operationId": "CommissionsController_reject",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program UUID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commissionId",
            "required": true,
            "in": "path",
            "description": "Commission UUID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Commission rejected"
          },
          "400": {
            "description": "Invalid request body"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Commission not found"
          },
          "409": {
            "description": "Commission is in a terminal status"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Reject a commission",
        "tags": ["Commissions"]
      }
    },
    "/webhooks/mailersend": {
      "post": {
        "operationId": "MailerSendWebhookController_handleMailerSendWebhook",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Webhook processed successfully"
          },
          "401": {
            "description": "Invalid webhook signature"
          }
        },
        "summary": "MailerSend webhook endpoint for bounce/complaint processing",
        "tags": ["webhooks"]
      }
    },
    "/api/v1/auth/email/verify": {
      "post": {
        "operationId": "EmailVerifyPublicController_verify",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  }
                },
                "required": ["token"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Email verified"
          },
          "400": {
            "description": "Token invalid, expired, or email changed since issue"
          }
        },
        "summary": "Consume an email verification token",
        "tags": ["Auth — Email Verification"]
      }
    },
    "/api/v1/auth/merchant/email/resend-verification": {
      "post": {
        "operationId": "EmailVerifyMerchantController_resendVerification",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Verification email sent"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Resend email verification for current merchant",
        "tags": ["Auth — Email Verification"]
      }
    },
    "/api/v1/auth/partner/email/resend-verification": {
      "post": {
        "operationId": "EmailVerifyPartnerController_resendVerification",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Verification email sent"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Resend email verification for current partner",
        "tags": ["Auth — Email Verification"]
      }
    },
    "/api/v1/notifications/preferences": {
      "get": {
        "operationId": "NotificationPreferencesController_get",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Effective preferences"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get notification preferences (merchant)",
        "tags": ["notifications"]
      },
      "patch": {
        "operationId": "NotificationPreferencesController_update",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Updated preferences"
          },
          "400": {
            "description": "Validation error"
          },
          "409": {
            "description": "Protected category"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update notification preferences (merchant)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/partner/notifications/preferences": {
      "get": {
        "operationId": "NotificationPreferencesPartnerController_get",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Effective preferences"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get notification preferences (partner)",
        "tags": ["notifications"]
      },
      "patch": {
        "operationId": "NotificationPreferencesPartnerController_update",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Updated preferences"
          },
          "400": {
            "description": "Validation error"
          },
          "409": {
            "description": "Protected category"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update notification preferences (partner)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/notifications": {
      "get": {
        "operationId": "InAppNotificationMerchantController_list",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Notification feed page"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List in-app notifications (merchant)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/notifications/unread-count": {
      "get": {
        "operationId": "InAppNotificationMerchantController_unreadCount",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Unread count"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Unread in-app notification count (merchant)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/notifications/{id}/read": {
      "post": {
        "operationId": "InAppNotificationMerchantController_markRead",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Marked read"
          },
          "404": {
            "description": "Not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Mark a notification read (merchant)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/notifications/read-all": {
      "post": {
        "operationId": "InAppNotificationMerchantController_markAll",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Marked all read"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Mark all (optionally filtered) read (merchant)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/notifications/{id}/dismiss": {
      "post": {
        "operationId": "InAppNotificationMerchantController_dismiss",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dismissed"
          },
          "404": {
            "description": "Not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Dismiss a notification (merchant; account-wide, idempotent — 097b follow-up)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/notifications/dismiss-read": {
      "post": {
        "operationId": "InAppNotificationMerchantController_dismissRead",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Read notifications dismissed"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Dismiss all read notifications, optionally filtered (merchant; \"Clear read\")",
        "tags": ["notifications"]
      }
    },
    "/api/v1/partner/notifications": {
      "get": {
        "operationId": "InAppNotificationPartnerController_list",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Notification feed page"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List in-app notifications (partner)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/partner/notifications/unread-count": {
      "get": {
        "operationId": "InAppNotificationPartnerController_unreadCount",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Unread count"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Unread in-app notification count (partner)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/partner/notifications/{id}/read": {
      "post": {
        "operationId": "InAppNotificationPartnerController_markRead",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Marked read"
          },
          "404": {
            "description": "Not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Mark a notification read (partner)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/partner/notifications/read-all": {
      "post": {
        "operationId": "InAppNotificationPartnerController_markAll",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Marked all read"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Mark all (optionally filtered) read (partner)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/partner/notifications/{id}/dismiss": {
      "post": {
        "operationId": "InAppNotificationPartnerController_dismiss",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dismissed"
          },
          "404": {
            "description": "Not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Dismiss a notification (partner; idempotent — 097b follow-up)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/partner/notifications/dismiss-read": {
      "post": {
        "operationId": "InAppNotificationPartnerController_dismissRead",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Read notifications dismissed"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Dismiss all read notifications, optionally filtered (partner; \"Clear read\")",
        "tags": ["notifications"]
      }
    },
    "/api/v1/notifications/stream": {
      "get": {
        "operationId": "InAppNotificationMerchantStreamController_streamEvents",
        "parameters": [],
        "responses": {
          "200": {
            "description": ""
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "SSE stream of in-app notifications (merchant; text/event-stream)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/partner/notifications/stream": {
      "get": {
        "operationId": "InAppNotificationPartnerStreamController_streamEvents",
        "parameters": [],
        "responses": {
          "200": {
            "description": ""
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "SSE stream of in-app notifications (partner; text/event-stream)",
        "tags": ["notifications"]
      }
    },
    "/api/v1/workspace/notification-log": {
      "get": {
        "operationId": "TestModeController_getNotificationLog",
        "parameters": [
          {
            "name": "status",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["suppressed", "redirected", "delivered"],
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "recipient",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "pageSize",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated notification log"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get suppressed notification log (test mode only)",
        "tags": ["test-mode"]
      }
    },
    "/api/v1/workspace/notification-log/{id}": {
      "get": {
        "operationId": "TestModeController_getNotificationLogDetail",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Single notification log entry"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "NOTIFICATION_LOG_NOT_FOUND — row missing OR workspace is live"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get a single notification log entry, including the rendered HTML body",
        "tags": ["test-mode"]
      }
    },
    "/api/v1/workspace/test-data": {
      "delete": {
        "description": "Deletes all entities created in test mode (programs, participants, tracking links, clicks, notification log). API keys are NOT affected. X-Mode header is ignored — always operates on test data regardless of current mode.",
        "operationId": "TestModeController_resetTestData",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Test data deleted synchronously"
          },
          "202": {
            "description": "Test data deletion queued (large dataset)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "409": {
            "description": "RESET_IN_PROGRESS: A reset is already in progress"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Reset all test data for the workspace",
        "tags": ["test-mode"]
      }
    },
    "/api/v1/gdpr/partner-data-erasure": {
      "post": {
        "description": "Scoped to the requesting merchant — only anonymizes invite data within programs owned by this merchant account.",
        "operationId": "GdprController_erasePartnerData",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["partnerEmail", "partnerGpaId"],
                "properties": {
                  "partnerEmail": {
                    "type": "string",
                    "format": "email"
                  },
                  "partnerGpaId": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Erasure completed successfully"
          },
          "400": {
            "description": "Validation error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Anonymize partner invite data (GDPR Art. 17 Right to Erasure)",
        "tags": ["GDPR"]
      }
    },
    "/api/v1/partner/account/erase": {
      "post": {
        "description": "Anonymizes all PII across all programs. Requires typed confirmation phrase \"DELETE\". Protected by the erasure throttle zone.",
        "operationId": "PartnerErasureController_eraseAccount",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Erasure completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "erased_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Confirmation phrase missing or invalid (CONFIRMATION_INVALID)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Partner account not found (PARTNER_NOT_FOUND)"
          },
          "409": {
            "description": "Already erased (ALREADY_ERASED) or payout blocking (ERASURE_BLOCKED_PAYOUT)"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Request GDPR Art. 17 erasure of partner account (self-service)",
        "tags": ["partner-erasure"]
      }
    },
    "/api/v1/programs/{programId}/partners/{participantId}/erase": {
      "post": {
        "description": "Anonymizes PII for a single ParticipantRecord in the merchant's program. Requires typed confirmation phrase \"DELETE\".",
        "operationId": "MerchantErasureController_eraseParticipant",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "participantId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Erasure completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "erased_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Confirmation phrase missing or invalid (CONFIRMATION_INVALID)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Access denied (ACCESS_DENIED)"
          },
          "404": {
            "description": "Participant not found (PARTICIPANT_NOT_FOUND)"
          },
          "409": {
            "description": "Already erased (ALREADY_ERASED) or payout blocking (ERASURE_BLOCKED_PAYOUT)"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Merchant-initiated GDPR Art. 17 erasure of a participant",
        "tags": ["merchant-erasure"]
      }
    },
    "/api/v1/webhooks": {
      "post": {
        "operationId": "OutboundWebhooksController_create",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Endpoint created; signingSecret returned once"
          },
          "400": {
            "description": "Malformed URL"
          },
          "422": {
            "description": "HTTPS required (live mode), SSRF blocked, or endpoint limit exceeded"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Register a new webhook endpoint",
        "tags": ["Webhooks"]
      },
      "get": {
        "operationId": "OutboundWebhooksController_list",
        "parameters": [],
        "responses": {
          "200": {
            "description": "List of webhook endpoints"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List webhook endpoints for the current mode",
        "tags": ["Webhooks"]
      }
    },
    "/api/v1/webhooks/{id}": {
      "get": {
        "operationId": "OutboundWebhooksController_findOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook endpoint details (no signingSecret)"
          },
          "404": {
            "description": "Endpoint not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get a single webhook endpoint",
        "tags": ["Webhooks"]
      },
      "patch": {
        "operationId": "OutboundWebhooksController_update",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated webhook endpoint"
          },
          "404": {
            "description": "Endpoint not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update webhook endpoint subscriptions or active state",
        "tags": ["Webhooks"]
      },
      "delete": {
        "operationId": "OutboundWebhooksController_delete",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Endpoint deleted"
          },
          "404": {
            "description": "Endpoint not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Delete a webhook endpoint",
        "tags": ["Webhooks"]
      }
    },
    "/api/v1/webhooks/{id}/rotate-secret": {
      "post": {
        "operationId": "OutboundWebhooksController_rotateSecret",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "New signingSecret returned once"
          },
          "404": {
            "description": "Endpoint not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Rotate the signing secret; returns new secret once",
        "tags": ["Webhooks"]
      }
    },
    "/api/v1/webhooks/{id}/test-ping": {
      "post": {
        "operationId": "OutboundWebhooksController_testPing",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Test ping result (success, httpStatusCode, errorMessage)"
          },
          "404": {
            "description": "Endpoint not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Send a test ping to the webhook endpoint (synchronous, 10s timeout)",
        "tags": ["Webhooks"]
      }
    },
    "/api/v1/analytics/fraud-summary": {
      "get": {
        "operationId": "AnalyticsController_getFraudSummary",
        "parameters": [
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tz",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fraud event count across all programs for the merchant"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get aggregated fraud event count",
        "tags": ["Analytics"]
      }
    },
    "/api/v1/analytics/metrics": {
      "get": {
        "operationId": "AnalyticsController_getMetrics",
        "parameters": [
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tz",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregate KPIs: attributed revenue, clicks, conversions, conversion rate, and prior period equivalents"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get aggregate analytics metrics for a date range",
        "tags": ["Analytics"]
      }
    },
    "/api/v1/analytics/trend": {
      "get": {
        "operationId": "AnalyticsController_getTrend",
        "parameters": [
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "granularity",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tz",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "metric",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Metric values grouped by time bucket with zero-filled gaps"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get conversion / clicks / revenue trend grouped by day or ISO week",
        "tags": ["Analytics"]
      }
    },
    "/api/v1/analytics/top-partners": {
      "get": {
        "operationId": "AnalyticsController_getTopPartners",
        "parameters": [
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tz",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 10 partners ranked by attributed revenue for the selected date range"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get top 10 partners ranked by attributed revenue",
        "tags": ["Analytics"]
      }
    },
    "/api/v1/analytics/partners": {
      "get": {
        "operationId": "AnalyticsController_getPartnerPerformanceList",
        "parameters": [
          {
            "name": "pageSize",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "fraudOnly",
            "required": false,
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["asc", "desc"],
              "type": "string"
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "schema": {
              "enum": [
                "attributedRevenue",
                "conversions",
                "clicks",
                "conversionRate",
                "fraudFlagCount"
              ],
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of partner performance metrics"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get paginated partner performance list with filtering and sorting",
        "tags": ["Analytics"]
      }
    },
    "/api/v1/analytics/partners/export": {
      "get": {
        "operationId": "AnalyticsController_exportPartnerPerformanceCsv",
        "parameters": [
          {
            "name": "fraudOnly",
            "required": false,
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["asc", "desc"],
              "type": "string"
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "schema": {
              "enum": [
                "attributedRevenue",
                "conversions",
                "clicks",
                "conversionRate",
                "fraudFlagCount"
              ],
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV file download"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Export partner performance data as CSV",
        "tags": ["Analytics"]
      }
    },
    "/api/v1/analytics/partners/{partnerId}": {
      "get": {
        "operationId": "AnalyticsController_getPartnerDetail",
        "parameters": [
          {
            "name": "partnerId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tz",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Partner detail with current and prior period metrics"
          },
          "404": {
            "description": "Participant not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get detailed performance metrics for a single partner",
        "tags": ["Analytics"]
      }
    },
    "/api/v1/analytics/partners/{partnerId}/trend": {
      "get": {
        "operationId": "AnalyticsController_getPartnerTrend",
        "parameters": [
          {
            "name": "partnerId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "granularity",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tz",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Conversion trend grouped by day or ISO week for a single partner"
          },
          "404": {
            "description": "Participant not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get conversion trend for a single partner",
        "tags": ["Analytics"]
      }
    },
    "/api/v1/analytics/partners/{partnerId}/conversions": {
      "get": {
        "operationId": "AnalyticsController_getPartnerConversions",
        "parameters": [
          {
            "name": "partnerId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "pageSize",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tz",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of conversions with fraud flags for a single partner"
          },
          "404": {
            "description": "Participant not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get paginated conversions for a single partner",
        "tags": ["Analytics"]
      }
    },
    "/api/v1/workspace/api-keys": {
      "get": {
        "description": "Returns publishable key + secret state (not_generated | issued) per mode. Never exposes plaintext secret.",
        "operationId": "ApiKeysController_list",
        "parameters": [],
        "responses": {
          "200": {
            "description": "API keys list returned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "mode": {
                            "type": "string",
                            "enum": ["test", "live"]
                          },
                          "publishableKey": {
                            "type": "string"
                          },
                          "secret": {
                            "oneOf": [
                              {
                                "type": "object",
                                "properties": {
                                  "state": {
                                    "type": "string",
                                    "enum": ["not_generated"]
                                  }
                                }
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "state": {
                                    "type": "string",
                                    "enum": ["issued"]
                                  },
                                  "issuedAt": {
                                    "type": "string"
                                  },
                                  "prefix": {
                                    "type": "string"
                                  },
                                  "lastFour": {
                                    "type": "string"
                                  }
                                }
                              }
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get active API keys per mode (new v2 shape)",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/api-keys/generate": {
      "post": {
        "description": "Issues sk_* for the given mode for the first time. Returns the plain-text secret once. Use Rotate for subsequent issuance.",
        "operationId": "ApiKeysController_generate",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["mode"],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": ["test", "live"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Secret key generated; returned in plain text once",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string"
                    },
                    "publishableKey": {
                      "type": "string"
                    },
                    "secretKey": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "JWT missing or invalid"
          },
          "409": {
            "description": "Secret key already issued — use Rotate"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Generate secret key for a mode (first-time issuance)",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/api-keys/reveal": {
      "post": {
        "description": "Requires password verification. Returns the stored plain-text sk_test_* for the active row. mode===live always returns 403.",
        "operationId": "ApiKeysController_reveal",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["mode", "password"],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": ["test", "live"]
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Secret key revealed (test mode only)"
          },
          "401": {
            "description": "Wrong password (rate-limited)"
          },
          "403": {
            "description": "Live secret keys cannot be revealed"
          },
          "404": {
            "description": "No test secret key has been generated yet"
          },
          "429": {
            "description": "Rate-limited — too many failed password attempts"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Reveal test-mode secret key via password re-auth",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/api-keys/rotate": {
      "post": {
        "description": "Retires the current sk_* and issues a new one. pk_* is never changed. Returns the new plain-text secret once.",
        "operationId": "ApiKeysController_rotate",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["mode"],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": ["test", "live"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New secret key returned in plain text once; publishable key unchanged",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string"
                    },
                    "publishableKey": {
                      "type": "string"
                    },
                    "secretKey": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "JWT missing or invalid"
          },
          "404": {
            "description": "Secret key not yet generated — use Generate first"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Rotate secret key for a mode — preserves publishable key",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace": {
      "get": {
        "operationId": "WorkspaceController_getWorkspace",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Workspace details returned"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get current workspace details",
        "tags": ["workspace"]
      },
      "patch": {
        "operationId": "WorkspaceController_updateWorkspace",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  },
                  "languagePreference": {
                    "type": "string",
                    "enum": ["en", "de", "fr", "es", "it", "nl", "pl"],
                    "nullable": true
                  },
                  "testModeRedirectEmail": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 320,
                    "nullable": true,
                    "description": "Optional address that receives every test-mode email instead of the original recipient. `null` clears it."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Workspace updated"
          },
          "400": {
            "description": "Validation error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update workspace name, language preference, or test-mode redirect email",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/mode": {
      "patch": {
        "operationId": "WorkspaceController_toggleMode",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": ["test", "live"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Mode toggled"
          },
          "409": {
            "description": "Live key not connected"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Toggle workspace mode (test/live)",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/stripe/connect": {
      "post": {
        "operationId": "WorkspaceController_connectStripe",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "stripeKey": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stripe key connected"
          },
          "400": {
            "description": "Invalid Stripe key"
          },
          "403": {
            "description": "Key missing required permissions"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Connect a Stripe restricted key",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/stripe/disconnect": {
      "post": {
        "operationId": "WorkspaceController_disconnectStripe",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": ["test", "live"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stripe disconnected"
          },
          "404": {
            "description": "Stripe connection not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Disconnect a Stripe connection",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/snippet": {
      "get": {
        "operationId": "WorkspaceController_getSnippet",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Snippet returned"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get tracking snippet HTML",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/onboarding": {
      "patch": {
        "operationId": "WorkspaceController_setOnboardingCompleted",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Onboarding marked complete"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Mark onboarding as completed",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/billing": {
      "get": {
        "operationId": "WorkspaceController_getBillingSettings",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Billing settings returned"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get billing/subscription settings (read-only)",
        "tags": ["workspace"]
      }
    },
    "/api/v1/workspace/billing/visit": {
      "post": {
        "operationId": "WorkspaceController_recordPricingVisit",
        "parameters": [],
        "responses": {
          "204": {
            "description": "Visit recorded"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Record that the user visited the pricing page",
        "tags": ["workspace"]
      }
    },
    "/api/v1/programs/{programId}/audit": {
      "get": {
        "operationId": "AuditController_listAuditLog",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "fraud_only",
            "required": false,
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "partner_id",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["pending", "converted", "expired", "duplicate"],
              "type": "string"
            }
          },
          {
            "name": "source",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["link", "code"],
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of audit log entries"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Program belongs to another workspace"
          },
          "404": {
            "description": "Program not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List attribution events with audit detail",
        "tags": ["Audit"]
      }
    },
    "/api/v1/programs/{programId}/audit/export": {
      "get": {
        "operationId": "AuditController_exportCsv",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "fraud_only",
            "required": false,
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "partner_id",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["pending", "converted", "expired", "duplicate"],
              "type": "string"
            }
          },
          {
            "name": "source",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["link", "code"],
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV file download"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Program belongs to another workspace"
          },
          "404": {
            "description": "Program not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Export filtered attribution events as CSV",
        "tags": ["Audit"]
      }
    },
    "/api/v1/programs/{programId}/audit/{attributionEventId}": {
      "get": {
        "operationId": "AuditController_getAuditDetail",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "attributionEventId",
            "required": true,
            "in": "path",
            "description": "Attribution Event ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit event detail"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Program belongs to another workspace"
          },
          "404": {
            "description": "Program or attribution event not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get audit detail for a single attribution event",
        "tags": ["Audit"]
      }
    },
    "/api/v1/partner/programs/{programId}/commissions": {
      "get": {
        "operationId": "PartnerCommissionsController_listPartnerCommissions",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["pending", "approved", "rejected", "paid", "refunded"],
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of partner commissions"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Enrollment not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List partner commission history",
        "tags": ["Partner — Commissions"]
      }
    },
    "/api/v1/auth/merchant/register": {
      "post": {
        "operationId": "AuthController_register",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created"
          },
          "400": {
            "description": "Validation error"
          },
          "409": {
            "description": "Email already exists"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "summary": "Register a new merchant account",
        "tags": ["auth"]
      }
    },
    "/api/v1/auth/merchant/login": {
      "post": {
        "operationId": "AuthController_login",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Login successful"
          },
          "401": {
            "description": "Invalid credentials"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "summary": "Log in as a merchant user",
        "tags": ["auth"]
      }
    },
    "/api/v1/auth/merchant/refresh": {
      "post": {
        "operationId": "AuthController_refresh",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Token refreshed"
          },
          "401": {
            "description": "Invalid refresh token"
          }
        },
        "summary": "Refresh access token using refresh token cookie",
        "tags": ["auth"]
      }
    },
    "/api/v1/auth/merchant/logout": {
      "post": {
        "operationId": "AuthController_logout",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Logged out"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Log out the current merchant user",
        "tags": ["auth"]
      }
    },
    "/api/v1/auth/merchant/forgot-password": {
      "post": {
        "operationId": "AuthController_forgotPassword",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "If email exists, a reset link was sent"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "summary": "Request a password reset email",
        "tags": ["auth"]
      }
    },
    "/api/v1/auth/merchant/reset-password": {
      "post": {
        "operationId": "AuthController_resetPassword",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password reset successful"
          },
          "400": {
            "description": "Invalid or expired reset token"
          }
        },
        "summary": "Reset password using a token",
        "tags": ["auth"]
      }
    },
    "/api/v1/auth/merchant/change-password": {
      "post": {
        "operationId": "AuthController_changeMerchantPassword",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["currentPassword", "newPassword"],
                "properties": {
                  "currentPassword": {
                    "type": "string"
                  },
                  "newPassword": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password changed successfully"
          },
          "400": {
            "description": "Validation error or new password equals current"
          },
          "401": {
            "description": "Current password is incorrect"
          },
          "429": {
            "description": "Too many failed attempts — rate limited"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Change merchant account password",
        "tags": ["auth"]
      }
    },
    "/api/v1/auth/merchant/waitlist-invite/validate": {
      "post": {
        "operationId": "AuthController_validateWaitlistInvite",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["token"],
                "properties": {
                  "token": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token validation result"
          }
        },
        "summary": "Validate a waitlist invite token",
        "tags": ["auth"]
      }
    },
    "/api/v1/auth/merchant/accept-tos": {
      "post": {
        "operationId": "TosController_accept",
        "parameters": [],
        "responses": {
          "204": {
            "description": "Acceptance recorded"
          },
          "400": {
            "description": "Invalid or stale ToS version"
          },
          "401": {
            "description": "Unauthenticated"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Accept the current Terms of Use version",
        "tags": ["auth"]
      }
    },
    "/api/v1/admin/waitlist-invites": {
      "post": {
        "operationId": "WaitlistInviteController_sendInvite",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email"],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invite sent"
          },
          "401": {
            "description": "Missing or invalid credentials"
          },
          "403": {
            "description": "Admin endpoint disabled — BULL_BOARD_PASSWORD not set"
          },
          "404": {
            "description": "Email not found on waitlist"
          }
        },
        "security": [
          {
            "basic": []
          }
        ],
        "summary": "Send a waitlist access invite email",
        "tags": ["admin"]
      }
    },
    "/api/v1/dashboard/activity": {
      "get": {
        "operationId": "DashboardController_getActivity",
        "parameters": [
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "example": "2026-05-13",
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "example": "2026-05-20",
              "type": "string"
            }
          },
          {
            "name": "tz",
            "required": false,
            "in": "query",
            "description": "IANA timezone — when provided, from/to are interpreted as start-of-day in this timezone (matches /analytics/trend behaviour so the trend chart and activity feed agree on day boundaries).",
            "schema": {
              "example": "Europe/Berlin",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "1-50, defaults to 10",
            "schema": {
              "example": 10,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Unified event feed sorted by timestamp descending"
          },
          "400": {
            "description": "Invalid date range (bad format, future, exceeds 90 days, or to <= from)"
          },
          "401": {
            "description": "Missing or invalid access token"
          },
          "429": {
            "description": "Too many requests"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Recent activity feed: up to N most-recent events (conversion, commission, partner_approved) for the current merchant workspace",
        "tags": ["Dashboard"]
      }
    },
    "/api/v1/auth/partner/register": {
      "post": {
        "operationId": "PartnerAuthController_register",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "displayName": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Partner account created"
          },
          "400": {
            "description": "Validation error"
          },
          "409": {
            "description": "Email already exists"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "summary": "Register a new partner account",
        "tags": ["Partner Auth"]
      }
    },
    "/api/v1/auth/partner/login": {
      "post": {
        "operationId": "PartnerAuthController_login",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Login successful"
          },
          "401": {
            "description": "Invalid credentials"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "summary": "Log in as a partner",
        "tags": ["Partner Auth"]
      }
    },
    "/api/v1/auth/partner/refresh": {
      "post": {
        "operationId": "PartnerAuthController_refresh",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Token refreshed"
          },
          "401": {
            "description": "Invalid refresh token"
          }
        },
        "summary": "Refresh partner access token using refresh token cookie",
        "tags": ["Partner Auth"]
      }
    },
    "/api/v1/auth/partner/logout": {
      "post": {
        "operationId": "PartnerAuthController_logout",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Logged out"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Log out the current partner",
        "tags": ["Partner Auth"]
      }
    },
    "/api/v1/auth/partner/forgot-password": {
      "post": {
        "operationId": "PartnerAuthController_forgotPassword",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "If email exists, a reset link was sent"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "summary": "Request a partner password reset email",
        "tags": ["Partner Auth"]
      }
    },
    "/api/v1/auth/partner/reset-password": {
      "post": {
        "operationId": "PartnerAuthController_resetPassword",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password reset successful"
          },
          "400": {
            "description": "Invalid or expired reset token"
          }
        },
        "summary": "Reset partner password using a token",
        "tags": ["Partner Auth"]
      }
    },
    "/api/v1/dpa/generate": {
      "post": {
        "description": "Generates a GDPR Art. 28 Data Processing Agreement for the workspace. Requires workspace membership.",
        "operationId": "DpaController_generate",
        "parameters": [],
        "responses": {
          "201": {
            "description": "DPA generated successfully"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — not a workspace member"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Generate a new DPA version",
        "tags": ["DPA"]
      }
    },
    "/api/v1/dpa": {
      "get": {
        "description": "Returns all DPA records ordered by generated_at DESC. Requires workspace membership.",
        "operationId": "DpaController_list",
        "parameters": [],
        "responses": {
          "200": {
            "description": "DPA list returned"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — not a workspace member"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List all DPA versions for the workspace",
        "tags": ["DPA"]
      }
    },
    "/api/v1/dpa/{id}": {
      "get": {
        "description": "Returns the full DPA record including all resolved sections. Requires workspace membership.",
        "operationId": "DpaController_getById",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "DPA record returned"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — not a workspace member"
          },
          "404": {
            "description": "DPA record not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get a single DPA record with full content",
        "tags": ["DPA"]
      }
    },
    "/api/v1/programs": {
      "post": {
        "operationId": "ProgramsController_create",
        "parameters": [],
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProgramDTOSwagger"
                }
              }
            }
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Unauthorized"
          },
          "409": {
            "description": "PROGRAM_ALREADY_EXISTS: Workspace already has a program"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create affiliate program",
        "tags": ["Programs"]
      },
      "get": {
        "operationId": "ProgramsController_list",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProgramListResponseSwagger"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List programs for merchant",
        "tags": ["Programs"]
      }
    },
    "/api/v1/programs/{id}": {
      "get": {
        "operationId": "ProgramsController_getOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProgramDTOSwagger"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "PROGRAM_NOT_FOUND: Program not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get program by ID",
        "tags": ["Programs"]
      },
      "patch": {
        "operationId": "ProgramsController_update",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProgramDTOSwagger"
                }
              }
            }
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "PROGRAM_NOT_FOUND: Program not found"
          },
          "422": {
            "description": "INVALID_COMMISSION_RATE: Commission rate exceeds bounds for commission type\n\nPROGRAM_ARCHIVED: Cannot update archived program"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update program fields",
        "tags": ["Programs"]
      }
    },
    "/api/v1/programs/{id}/status": {
      "patch": {
        "operationId": "ProgramsController_updateStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProgramDTOSwagger"
                }
              }
            }
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "PROGRAM_NOT_FOUND: Program not found"
          },
          "422": {
            "description": "INVALID_STATUS_TRANSITION: Invalid status transition"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update program status",
        "tags": ["Programs"]
      }
    },
    "/api/v1/programs/{id}/unarchive": {
      "post": {
        "description": "Restores an archived programme to active within the undo window. Cancels the deferred \"Programme Closed\" fan-out so partners are never emailed (silent undo). Does not resurrect erased partners.",
        "operationId": "ProgramsController_unarchive",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProgramDTOSwagger"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "PROGRAM_NOT_FOUND: Program not found"
          },
          "409": {
            "description": "PROGRAM_SLOT_OCCUPIED: Another programme occupies this mode slot"
          },
          "410": {
            "description": "PROGRAM_RESTORE_WINDOW_EXPIRED: The restore window has expired"
          },
          "422": {
            "description": "PROGRAM_NOT_ARCHIVED: Programme is not archived"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Restore (unarchive) a programme within the undo window",
        "tags": ["Programs"]
      }
    },
    "/api/v1/programs/{id}/pin": {
      "patch": {
        "operationId": "ProgramsController_togglePin",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TogglePinRequestSwagger"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pin status updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProgramDTOSwagger"
                }
              }
            }
          },
          "400": {
            "description": "Validation error"
          },
          "404": {
            "description": "Program not found"
          },
          "422": {
            "description": "Pin limit reached or program archived"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Toggle program pin status",
        "tags": ["Programs"]
      }
    },
    "/api/v1/partner/profile": {
      "get": {
        "operationId": "PartnerController_getProfile",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Partner profile"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get current partner profile",
        "tags": ["Partner Portal"]
      },
      "patch": {
        "operationId": "PartnerController_updateProfile",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "displayName": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "currentPassword": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Profile updated"
          },
          "400": {
            "description": "Validation error or password required"
          },
          "401": {
            "description": "Unauthorized or invalid password"
          },
          "409": {
            "description": "Email already exists"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update current partner profile",
        "tags": ["Partner Portal"]
      }
    },
    "/api/v1/partner/profile/change-password": {
      "post": {
        "operationId": "PartnerController_changePassword",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["currentPassword", "newPassword"],
                "properties": {
                  "currentPassword": {
                    "type": "string"
                  },
                  "newPassword": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password changed successfully — returns new access token and sets refreshed partner_refresh_token cookie"
          },
          "400": {
            "description": "Validation error or new password same as current"
          },
          "401": {
            "description": "Current password is incorrect"
          },
          "429": {
            "description": "Too many failed attempts — rate limited"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Change partner account password",
        "tags": ["Partner Portal"]
      }
    },
    "/api/v1/partner/programs": {
      "get": {
        "operationId": "PartnerController_getPrograms",
        "parameters": [
          {
            "name": "pageSize",
            "required": false,
            "in": "query",
            "description": "Page size (default: 50, max: 100)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (default: 1, min: 1)",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of programs"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List programs the partner is enrolled in",
        "tags": ["Partner Portal"]
      }
    },
    "/api/v1/partner/programs/{programId}/enrollment": {
      "get": {
        "operationId": "PartnerController_getEnrollment",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Enrollment details"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Enrollment not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get partner enrollment details for a program",
        "tags": ["Partner Portal"]
      }
    },
    "/api/v1/partner/programs/{slug}": {
      "get": {
        "operationId": "PartnerController_getPublicProgram",
        "parameters": [
          {
            "name": "slug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Program details"
          },
          "404": {
            "description": "Program not found"
          },
          "410": {
            "description": "Program not available"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get public program details by slug",
        "tags": ["Partner Portal"]
      }
    },
    "/api/v1/partner/programs/{slug}/apply": {
      "post": {
        "operationId": "PartnerController_submitApplication",
        "parameters": [
          {
            "name": "slug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["fullName", "email", "websiteUrl"],
                "properties": {
                  "fullName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "websiteUrl": {
                    "type": "string",
                    "description": "Website, social link, or @handle. Resolved to a canonical URL server-side using websitePlatform."
                  },
                  "websitePlatform": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "twitter",
                      "linkedin",
                      "github",
                      "instagram",
                      "website"
                    ],
                    "description": "Platform the handle/URL belongs to (spec 098). Required only when websiteUrl is a bare handle; omit/null for a full URL (platform is then inferred from the domain)."
                  },
                  "vatId": {
                    "type": "string",
                    "description": "Optional EU VAT ID (e.g. DE123456789)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Application submitted"
          },
          "400": {
            "description": "Validation error"
          },
          "403": {
            "description": "Email mismatch for authenticated partner"
          },
          "404": {
            "description": "Program not found"
          },
          "409": {
            "description": "Application already exists"
          },
          "410": {
            "description": "Program not available"
          },
          "422": {
            "description": "Application could not be processed"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Submit a partner application to a program",
        "tags": ["Partner Portal"]
      }
    },
    "/api/v1/partner/payouts": {
      "get": {
        "operationId": "PartnerPayoutsController_getPayouts",
        "parameters": [
          {
            "name": "pageSize",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "programId",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Payout history with stats"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get partner payout history with stats",
        "tags": ["Partner — Payouts"]
      }
    },
    "/api/v1/partner/payouts/summary": {
      "get": {
        "operationId": "PartnerPayoutsController_getSummary",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Pending balances by currency"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get lightweight payout summary for nav badge",
        "tags": ["Partner — Payouts"]
      }
    },
    "/api/v1/partner/payouts/{disbursementId}/commissions": {
      "get": {
        "operationId": "PartnerPayoutsController_getCommissionBreakdown",
        "parameters": [
          {
            "name": "disbursementId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "pageSize",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated commission breakdown"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get commission breakdown for a disbursement",
        "tags": ["Partner — Payouts"]
      }
    },
    "/api/v1/partner/stats": {
      "get": {
        "operationId": "PartnerPortalController_getStats",
        "parameters": [
          {
            "name": "programId",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "example": "2026-03-31"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "example": "2026-03-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Partner stats"
          },
          "400": {
            "description": "Invalid date format"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get partner dashboard statistics with trend comparison",
        "tags": ["Partner — Portal"]
      }
    },
    "/api/v1/partner/analytics/trend": {
      "get": {
        "operationId": "PartnerPortalController_getTrend",
        "parameters": [
          {
            "name": "programId",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "example": "2026-03-31"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "example": "2026-03-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Daily trend points; zero-filled for empty days"
          },
          "400": {
            "description": "Invalid query parameters"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get daily-bucketed clicks/conversions/earnings for the partner",
        "tags": ["Partner — Portal"]
      }
    },
    "/api/v1/partner/activity": {
      "get": {
        "operationId": "PartnerPortalController_getActivity",
        "parameters": [
          {
            "name": "programId",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent activity items"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get partner recent activity feed",
        "tags": ["Partner — Portal"]
      }
    },
    "/api/v1/partner/activity/history": {
      "get": {
        "operationId": "PartnerPortalController_getActivityHistory",
        "parameters": [
          {
            "name": "pageSize",
            "required": false,
            "in": "query",
            "description": "Page size: 20, 50, or 100",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "1-based page number (max 500)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "tz",
            "required": false,
            "in": "query",
            "description": "IANA timezone for date boundary calculation (e.g., America/New_York)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "required": false,
            "in": "query",
            "description": "Search query (channel label or external transaction ID)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": false,
            "in": "query",
            "schema": {
              "example": "2026-03-31",
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": false,
            "in": "query",
            "schema": {
              "example": "2026-01-01",
              "type": "string"
            }
          },
          {
            "name": "programId",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["click", "conversion"],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated activity history"
          },
          "400": {
            "description": "Invalid query parameters"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get paginated partner activity history",
        "tags": ["Partner — Portal"]
      }
    },
    "/api/v1/partner/links": {
      "get": {
        "operationId": "PartnerPortalController_getAggregatedLinks",
        "parameters": [
          {
            "name": "pageSize",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "programId",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated aggregated links"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get aggregated referral links across all programs",
        "tags": ["Partner — Portal"]
      }
    },
    "/api/v1/programs/{id}/invites": {
      "post": {
        "operationId": "InvitesController_create",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["type"],
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": ["general", "personal"]
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "maxUses": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateInviteResponseSwagger"
                }
              }
            }
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Access denied to program"
          },
          "404": {
            "description": "PROGRAM_NOT_FOUND: Program not found"
          },
          "409": {
            "description": "INVITE_ALREADY_EXISTS: Active personal invite already exists for this email"
          },
          "422": {
            "description": "PROGRAM_NOT_ACTIVE: Program is not active"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create an invite link for a program",
        "tags": ["Invites"]
      },
      "get": {
        "operationId": "InvitesController_findAll",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["active", "used", "revoked", "expired"],
              "type": "string"
            }
          },
          {
            "name": "pageSize",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedInvitesResponseSwagger"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "PROGRAM_NOT_FOUND: Program not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List invites for a program",
        "tags": ["Invites"]
      }
    },
    "/api/v1/programs/{id}/invites/{inviteId}/revoke": {
      "patch": {
        "operationId": "InvitesController_revoke",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "inviteId",
            "required": true,
            "in": "path",
            "description": "Invite ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevokeResponseSwagger"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "INVITE_NOT_FOUND: Invite not found"
          },
          "409": {
            "description": "INVITE_NOT_ACTIVE: Invite is not active"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Revoke an invite",
        "tags": ["Invites"]
      }
    },
    "/api/v1/programs/{id}/invites/{inviteId}/resend": {
      "post": {
        "operationId": "InvitesController_resend",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "inviteId",
            "required": true,
            "in": "path",
            "description": "Invite ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResendResponseSwagger"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "INVITE_NOT_FOUND: Invite not found; PROGRAM_NOT_FOUND: Program not found"
          },
          "409": {
            "description": "INVITE_NOT_ACTIVE: Invite is not active; INVITE_NOT_RESENDABLE: Invite is not resendable"
          },
          "429": {
            "description": "Rate limited; response body includes code RATE_LIMITED and retryAfterSeconds"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Resend an invite email",
        "tags": ["Invites"]
      }
    },
    "/api/v1/programs/{id}/invites/{inviteId}/redemptions": {
      "get": {
        "operationId": "InvitesController_listRedemptions",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "inviteId",
            "required": true,
            "in": "path",
            "description": "Invite ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "pageSize",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedRedemptionsResponseSwagger"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "INVITE_NOT_FOUND: Invite not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List redemptions for an invite",
        "tags": ["Invites"]
      }
    },
    "/api/v1/programs/{id}/partners": {
      "get": {
        "operationId": "ParticipantsController_list",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "includeErased",
            "required": false,
            "in": "query",
            "description": "When true, include GDPR-erased participants in the list. Absent/false excludes them (the default).",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "dateTo",
            "required": false,
            "in": "query",
            "description": "ISO 8601 datetime with timezone offset (e.g. 2026-12-31T23:59:59.999Z)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateFrom",
            "required": false,
            "in": "query",
            "description": "ISO 8601 datetime with timezone offset (e.g. 2026-01-01T00:00:00.000Z)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["pending", "approved", "suspended", "rejected"],
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of partners"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Program belongs to another workspace"
          },
          "404": {
            "description": "Program not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List partners enrolled in a program",
        "tags": ["Partners"]
      }
    },
    "/api/v1/programs/{id}/partners/stats": {
      "get": {
        "operationId": "ParticipantsController_stats",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Partner stats"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Program belongs to another workspace"
          },
          "404": {
            "description": "Program not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get partner stats for a program",
        "tags": ["Partners"]
      }
    },
    "/api/v1/programs/{id}/partners/{partnerId}": {
      "get": {
        "operationId": "ParticipantsController_getOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "partnerId",
            "required": true,
            "in": "path",
            "description": "Participant ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Partner record"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Program belongs to another workspace"
          },
          "404": {
            "description": "Program or partner not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get a single partner by ID",
        "tags": ["Partners"]
      }
    },
    "/api/v1/programs/{id}/partners/status": {
      "patch": {
        "operationId": "ParticipantsController_bulkUpdateStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "All transitions succeeded"
          },
          "207": {
            "description": "Partial success — some transitions failed"
          },
          "400": {
            "description": "Validation error or cross-program IDs"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Program belongs to another workspace"
          },
          "404": {
            "description": "Program not found"
          },
          "422": {
            "description": "All transitions failed"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Bulk update partner statuses",
        "tags": ["Partners"]
      }
    },
    "/api/v1/programs/{id}/partners/{partnerId}/status": {
      "patch": {
        "operationId": "ParticipantsController_updateStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Program ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "partnerId",
            "required": true,
            "in": "path",
            "description": "Participant ID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated partner status"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Program belongs to another workspace"
          },
          "404": {
            "description": "Program or participant not found"
          },
          "422": {
            "description": "Invalid status transition"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update single partner status",
        "tags": ["Partners"]
      }
    },
    "/api/v1/invite/{token}": {
      "get": {
        "operationId": "PublicInviteController_getContext",
        "parameters": [
          {
            "name": "token",
            "required": true,
            "in": "path",
            "description": "Invite token",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InviteContextResponseSwagger"
                }
              }
            }
          },
          "404": {
            "description": "INVITE_NOT_FOUND: Invite not found"
          }
        },
        "summary": "Get invite context (program info and status)",
        "tags": ["Public Invite"]
      }
    },
    "/api/v1/invite/{token}/redeem": {
      "post": {
        "operationId": "PublicInviteController_redeem",
        "parameters": [
          {
            "name": "token",
            "required": true,
            "in": "path",
            "description": "Invite token",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["fullName", "websiteUrl"],
                "properties": {
                  "fullName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "websiteUrl": {
                    "type": "string",
                    "description": "Website, social link, or @handle. Resolved to a canonical URL server-side using websitePlatform."
                  },
                  "websitePlatform": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "twitter",
                      "linkedin",
                      "github",
                      "instagram",
                      "website"
                    ],
                    "description": "Platform the handle/URL belongs to (spec 098). Required only when websiteUrl is a bare handle; omit/null for a full URL (platform is then inferred from the domain)."
                  },
                  "vatId": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedeemInviteResponseSwagger"
                }
              }
            }
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Partner must be authenticated"
          },
          "403": {
            "description": "EMAIL_MISMATCH: Email does not match personal invite"
          },
          "404": {
            "description": "INVITE_NOT_FOUND: Invite not found"
          },
          "410": {
            "description": "INVITE_REVOKED | INVITE_USED | INVITE_EXPIRED | INVITE_FULL"
          },
          "422": {
            "description": "PROGRAM_INACTIVE: Program is not active"
          },
          "429": {
            "description": "RATE_LIMITED: Too many requests"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Redeem an invite link (partner must be authenticated)",
        "tags": ["Public Invite"]
      }
    },
    "/api/v1/payouts": {
      "get": {
        "operationId": "PayoutsController_getPayouts",
        "parameters": [
          {
            "name": "programId",
            "required": false,
            "in": "query",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "showBelowThreshold",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["true", "false"]
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "format": "date",
              "example": "2026-03-31",
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "format": "date",
              "example": "2026-03-01",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Payout report"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get payout report rows and stats for a period",
        "tags": ["payouts"]
      }
    },
    "/api/v1/payouts/export": {
      "get": {
        "operationId": "PayoutsController_exportCsv",
        "parameters": [
          {
            "name": "programId",
            "required": false,
            "in": "query",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "format": "date",
              "example": "2026-03-31",
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "format": "date",
              "example": "2026-03-01",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV file download"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Export payout report as CSV",
        "tags": ["payouts"]
      }
    },
    "/api/v1/payouts/mark-paid": {
      "post": {
        "operationId": "PayoutsController_markAsPaid",
        "parameters": [],
        "requestBody": {
          "required": true,
          "description": "Mark as paid request",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["partnerIds", "from", "to", "idempotencyKey"],
                "properties": {
                  "partnerIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minItems": 1,
                    "maxItems": 100
                  },
                  "from": {
                    "type": "string",
                    "format": "date",
                    "example": "2026-03-01"
                  },
                  "to": {
                    "type": "string",
                    "format": "date",
                    "example": "2026-03-31"
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Mark as paid result"
          },
          "400": {
            "description": "Validation error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Mark partners as paid for a period",
        "tags": ["payouts"]
      }
    },
    "/api/v1/payouts/partners/{partnerId}/commissions": {
      "get": {
        "operationId": "PayoutsController_getCommissionBreakdown",
        "parameters": [
          {
            "name": "partnerId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": true,
            "in": "query",
            "schema": {
              "format": "date",
              "example": "2026-03-31",
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "schema": {
              "format": "date",
              "example": "2026-03-01",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Commission breakdown for partner"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get commission breakdown for a partner in a period",
        "tags": ["payouts"]
      }
    },
    "/api/v1/partner/programs/{programId}/links": {
      "post": {
        "operationId": "TrackingLinksController_createLink",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Referral link created"
          },
          "400": {
            "description": "Validation error"
          },
          "403": {
            "description": "Partner not approved"
          },
          "404": {
            "description": "Program enrollment not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create a referral link",
        "tags": ["Partner — Referral Links"]
      },
      "get": {
        "operationId": "TrackingLinksController_listLinks",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of referral links"
          },
          "404": {
            "description": "Program enrollment not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List referral links",
        "tags": ["Partner — Referral Links"]
      }
    },
    "/api/v1/partner/programs/{programId}/links/{linkId}": {
      "patch": {
        "operationId": "TrackingLinksController_updateLinkLabel",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "linkId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Label updated"
          },
          "400": {
            "description": "Validation error"
          },
          "403": {
            "description": "Partner not approved"
          },
          "404": {
            "description": "Link not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update referral link label",
        "tags": ["Partner — Referral Links"]
      },
      "delete": {
        "operationId": "TrackingLinksController_deleteLink",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "linkId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Link deleted"
          },
          "403": {
            "description": "Partner not approved"
          },
          "404": {
            "description": "Link not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Delete a referral link",
        "tags": ["Partner — Referral Links"]
      }
    },
    "/api/v1/programs/{programId}/promo-codes": {
      "post": {
        "operationId": "PromoCodesController_create",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Promo code created"
          },
          "400": {
            "description": "Validation error or past expiry date"
          },
          "403": {
            "description": "Partner not approved"
          },
          "404": {
            "description": "Program or participant not found"
          },
          "409": {
            "description": "Duplicate promo code in program"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create a promo code for a program",
        "tags": ["Promo Codes"]
      },
      "get": {
        "operationId": "PromoCodesController_list",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "schema": {
              "enum": ["all", "active", "inactive"],
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of promo codes"
          },
          "404": {
            "description": "Program not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List promo codes for a program",
        "tags": ["Promo Codes"]
      }
    },
    "/api/v1/programs/{programId}/promo-codes/{id}": {
      "get": {
        "operationId": "PromoCodesController_findOne",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Promo code details"
          },
          "404": {
            "description": "Promo code not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get a single promo code",
        "tags": ["Promo Codes"]
      },
      "patch": {
        "operationId": "PromoCodesController_updateExpiry",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated promo code"
          },
          "404": {
            "description": "Promo code not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update expiry date of a promo code",
        "tags": ["Promo Codes"]
      }
    },
    "/api/v1/programs/{programId}/promo-codes/{id}/status": {
      "patch": {
        "operationId": "PromoCodesController_setActive",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated promo code"
          },
          "404": {
            "description": "Promo code not found"
          },
          "422": {
            "description": "Cannot reactivate an expired promo code"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Deactivate or reactivate a promo code",
        "tags": ["Promo Codes"]
      }
    },
    "/api/v1/partner/programs/{programId}/promo-codes": {
      "get": {
        "operationId": "PartnerPromoCodesController_listForPartner",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of partner promo codes"
          },
          "404": {
            "description": "Enrollment not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List promo codes assigned to the partner",
        "tags": ["Partner — Promo Codes"]
      }
    },
    "/v1/clicks": {
      "post": {
        "operationId": "ClicksController_registerClick",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Click registered successfully"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Invalid or missing merchant_id"
          },
          "422": {
            "description": "Unable to process click — token not found"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "summary": "Register a click event",
        "tags": ["tracking"]
      }
    },
    "/v1/tracking/snippet-ping": {
      "post": {
        "operationId": "SnippetPingController_recordPing",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Ping accepted (idempotent)"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Invalid or missing merchant_id"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "summary": "Record snippet first-install heartbeat",
        "tags": ["tracking"]
      }
    },
    "/api/v1/partner/export": {
      "post": {
        "operationId": "PartnerExportController_triggerExport",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Export bundle created and ready for download",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "download_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — invalid or missing partner JWT"
          },
          "422": {
            "description": "Export bundle too large (EXPORT_TOO_LARGE)"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Trigger GDPR Art. 15/20 data export for partner",
        "tags": ["partner-export"]
      }
    },
    "/api/v1/exports/{token}": {
      "get": {
        "operationId": "ExportDownloadController_downloadExport",
        "parameters": [
          {
            "name": "token",
            "required": true,
            "in": "path",
            "description": "Opaque export download token",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON export bundle",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Token not found or expired (EXPORT_EXPIRED)"
          }
        },
        "summary": "Download a previously generated data export bundle",
        "tags": ["export-download"]
      }
    },
    "/api/v1/programs/{programId}/partners/{participantId}/export": {
      "post": {
        "operationId": "MerchantExportController_triggerExport",
        "parameters": [
          {
            "name": "programId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "participantId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Export bundle created and ready for download",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "download_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — invalid or missing merchant JWT"
          },
          "403": {
            "description": "Forbidden — participant belongs to a different merchant (EXPORT_FORBIDDEN)"
          },
          "404": {
            "description": "Participant not found (PARTICIPANT_NOT_FOUND)"
          },
          "422": {
            "description": "Export bundle too large (EXPORT_TOO_LARGE)"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Trigger GDPR Art. 15/20 data export for a specific participant",
        "tags": ["merchant-export"]
      }
    },
    "/api/v1/feedback": {
      "post": {
        "operationId": "FeedbackController_submit",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Feedback delivered"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Missing or invalid JWT"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Submit in-app feedback (merchant)",
        "tags": ["feedback"]
      }
    },
    "/api/v1/partner/feedback": {
      "post": {
        "operationId": "FeedbackPartnerController_submit",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Feedback delivered"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Missing or invalid JWT"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Submit in-app feedback (partner)",
        "tags": ["feedback"]
      }
    },
    "/api/v1/conversions": {
      "post": {
        "description": "Report a non-Stripe conversion (signup, form submit, custom goal) and resolve attribution to a partner.",
        "operationId": "ConversionsController_createConversion",
        "parameters": [],
        "requestBody": {
          "required": true,
          "description": "Canonical conversion payload. Field names use snake_case (e.g. `external_transaction_id`, not `external_id` or `transactionId`).",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["external_transaction_id", "event_type"],
                "properties": {
                  "external_transaction_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  },
                  "event_type": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "click_id": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true
                  },
                  "promo_code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "nullable": true
                  },
                  "amount_cents": {
                    "type": "integer",
                    "minimum": 0,
                    "default": 0
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[A-Z]{3}$",
                    "nullable": true,
                    "description": "ISO 4217 3-letter code; required when amount_cents > 0"
                  },
                  "occurred_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Defaults to the server time of receipt when omitted"
                  },
                  "prospect_email": {
                    "type": "string",
                    "format": "email",
                    "nullable": true
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "nullable": true,
                    "description": "Arbitrary key-value pairs, max 4 KB serialised"
                  }
                }
              },
              "examples": {
                "clickIdAttribution": {
                  "summary": "click_id attribution (signup)",
                  "value": {
                    "click_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
                    "external_transaction_id": "signup_12345",
                    "event_type": "signup",
                    "amount_cents": 0
                  }
                },
                "promoCodeAttribution": {
                  "summary": "promo_code attribution (paid purchase)",
                  "value": {
                    "promo_code": "PARTNER20",
                    "external_transaction_id": "order_99999",
                    "event_type": "purchase",
                    "amount_cents": 4900,
                    "currency": "EUR"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Conversion created"
          },
          "400": {
            "description": "Validation error"
          },
          "401": {
            "description": "Invalid or missing API key"
          },
          "409": {
            "description": "Duplicate external_transaction_id (FR-006)"
          },
          "422": {
            "description": "No attribution signal or test/live mode mismatch"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create a conversion event",
        "tags": ["conversions"]
      }
    },
    "/api/v1/feedback-prompts/evaluate": {
      "get": {
        "operationId": "FeedbackPromptsController_evaluate",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Evaluation result returned"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Evaluate which feedback banner to show for this session",
        "tags": ["feedback-prompts"]
      }
    },
    "/api/v1/feedback-prompts/{eventId}/submit": {
      "post": {
        "operationId": "FeedbackPromptsController_submit",
        "parameters": [
          {
            "name": "eventId",
            "required": true,
            "in": "path",
            "description": "UUID of the feedback event",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "responseValue": {
                    "type": "object"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response submitted"
          },
          "404": {
            "description": "FEEDBACK_EVENT_NOT_FOUND"
          },
          "409": {
            "description": "FEEDBACK_EVENT_ALREADY_TERMINAL"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Submit merchant response to a shown banner",
        "tags": ["feedback-prompts"]
      }
    },
    "/api/v1/feedback-prompts/{eventId}/dismiss": {
      "post": {
        "operationId": "FeedbackPromptsController_dismiss",
        "parameters": [
          {
            "name": "eventId",
            "required": true,
            "in": "path",
            "description": "UUID of the feedback event",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "enum": [
                      "user_dismissed",
                      "manual",
                      "auto_close",
                      "tab_hidden",
                      "before_unload"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Dismiss recorded"
          },
          "404": {
            "description": "FEEDBACK_EVENT_NOT_FOUND"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Record a banner dismiss (explicit ✕ or auto-close)",
        "tags": ["feedback-prompts"]
      }
    }
  },
  "info": {
    "title": "Selgeo API",
    "description": "Privacy-first affiliate platform",
    "version": "1.0",
    "contact": {}
  },
  "tags": [],
  "servers": [
    {
      "url": "https://api.selgeo.com",
      "description": "Production"
    },
    {
      "url": "http://localhost:4000",
      "description": "Development"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearer": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http"
      },
      "basic": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic credentials (BULL_BOARD_USERNAME / BULL_BOARD_PASSWORD) for admin-only endpoints"
      },
      "api-key": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Secret API key (sk_test_* / sk_live_*) passed as Bearer token"
      }
    },
    "schemas": {
      "ProgramDTOSwagger": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "name": {
            "type": "string",
            "example": "Growth Partners"
          },
          "slug": {
            "type": "string",
            "example": "growth-partners"
          },
          "status": {
            "type": "string",
            "enum": ["active", "inactive", "archived"]
          },
          "approvalMode": {
            "type": "string",
            "enum": ["manual", "auto"]
          },
          "commissionType": {
            "type": "string",
            "enum": ["percentage", "flat_amount"]
          },
          "commissionRate": {
            "type": "number",
            "example": 20
          },
          "commissionBasis": {
            "type": "string",
            "enum": ["gross", "net"]
          },
          "currency": {
            "type": "string",
            "example": "EUR"
          },
          "attributionWindowDays": {
            "type": "number",
            "example": 30
          },
          "minimumPayoutThreshold": {
            "type": "number",
            "example": 0
          },
          "targetUrl": {
            "type": "string",
            "format": "uri",
            "example": "https://example.com",
            "nullable": true,
            "description": "Programme's destination URL — partner links must point here or a subdomain."
          },
          "approvedParticipantsCount": {
            "type": "number",
            "example": 12,
            "description": "Number of approved, non-erased participants — the partners who would be emailed if the programme is archived. Populated by GET /programs/:id (0 on list responses)."
          },
          "archivedAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-03-20T10:00:00Z",
            "nullable": true,
            "description": "When the programme was archived, or null if it is not archived."
          },
          "restoreWindowExpiresAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-03-20T10:30:00Z",
            "nullable": true,
            "description": "End of the restore (undo) window (archivedAt + undo window), or null when not archived. The programme is restorable only while this timestamp is in the future."
          },
          "createdAt": {
            "type": "string",
            "example": "2026-03-20T10:00:00Z"
          },
          "updatedAt": {
            "type": "string",
            "example": "2026-03-20T10:00:00Z"
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "status",
          "approvalMode",
          "commissionType",
          "commissionRate",
          "commissionBasis",
          "currency",
          "attributionWindowDays",
          "minimumPayoutThreshold",
          "targetUrl",
          "approvedParticipantsCount",
          "archivedAt",
          "restoreWindowExpiresAt",
          "createdAt",
          "updatedAt"
        ]
      },
      "ProgramListResponseSwagger": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProgramDTOSwagger"
            }
          },
          "pagination": {
            "type": "object",
            "example": {
              "page": 1,
              "pageSize": 50,
              "total": 1,
              "totalPages": 1
            }
          }
        },
        "required": ["data", "pagination"]
      },
      "TogglePinRequestSwagger": {
        "type": "object",
        "properties": {
          "isPinned": {
            "type": "boolean",
            "description": "Whether to pin the program",
            "example": true
          }
        },
        "required": ["isPinned"]
      },
      "CreateInviteResponseSwagger": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "type": {
            "type": "string",
            "enum": ["general", "personal"]
          },
          "status": {
            "type": "string",
            "enum": ["active", "used", "revoked", "expired"]
          },
          "email": {
            "type": "string",
            "format": "email",
            "example": "partner@example.com",
            "nullable": true
          },
          "autoApprove": {
            "type": "boolean",
            "example": true
          },
          "maxUses": {
            "type": "integer",
            "example": 10,
            "nullable": true
          },
          "useCount": {
            "type": "number",
            "example": 0
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-12-31T23:59:59Z",
            "nullable": true
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time",
            "example": null,
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "example": "2026-03-20T10:00:00Z"
          },
          "inviteUrl": {
            "type": "string",
            "format": "uri",
            "example": "https://app.example.com/invite/abc123",
            "nullable": true
          }
        },
        "required": [
          "id",
          "type",
          "status",
          "email",
          "autoApprove",
          "maxUses",
          "useCount",
          "expiresAt",
          "revokedAt",
          "createdAt",
          "inviteUrl"
        ]
      },
      "InviteDTOSwagger": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "type": {
            "type": "string",
            "enum": ["general", "personal"]
          },
          "status": {
            "type": "string",
            "enum": ["active", "used", "revoked", "expired"]
          },
          "email": {
            "type": "string",
            "format": "email",
            "example": "partner@example.com",
            "nullable": true
          },
          "autoApprove": {
            "type": "boolean",
            "example": true
          },
          "maxUses": {
            "type": "integer",
            "example": 10,
            "nullable": true
          },
          "useCount": {
            "type": "number",
            "example": 0
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-12-31T23:59:59Z",
            "nullable": true
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time",
            "example": null,
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "example": "2026-03-20T10:00:00Z"
          },
          "inviteUrl": {
            "type": "string",
            "format": "uri",
            "example": "https://app.example.com/invite/abc123",
            "nullable": true
          }
        },
        "required": [
          "id",
          "type",
          "status",
          "email",
          "autoApprove",
          "maxUses",
          "useCount",
          "expiresAt",
          "revokedAt",
          "createdAt",
          "inviteUrl"
        ]
      },
      "PaginatedInvitesResponseSwagger": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InviteDTOSwagger"
            }
          },
          "pagination": {
            "type": "object",
            "example": {
              "page": 1,
              "pageSize": 20,
              "total": 5,
              "totalPages": 1
            }
          }
        },
        "required": ["data", "pagination"]
      },
      "RevokeResponseSwagger": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "status": {
            "type": "string",
            "example": "revoked"
          },
          "revokedAt": {
            "type": "string",
            "example": "2026-03-20T10:00:00Z"
          }
        },
        "required": ["id", "status", "revokedAt"]
      },
      "ResendResponseSwagger": {
        "type": "object",
        "properties": {
          "sentAt": {
            "type": "string",
            "example": "2026-05-18T10:00:00Z"
          }
        },
        "required": ["sentAt"]
      },
      "RedemptionRecordSwagger": {
        "type": "object",
        "properties": {
          "partnerName": {
            "type": "string",
            "example": "Jane Doe"
          },
          "partnerEmail": {
            "type": "string",
            "example": "jane@example.com"
          },
          "redeemedAt": {
            "type": "string",
            "example": "2026-03-20T10:00:00Z"
          },
          "participantId": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          }
        },
        "required": [
          "partnerName",
          "partnerEmail",
          "redeemedAt",
          "participantId"
        ]
      },
      "PaginatedRedemptionsResponseSwagger": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RedemptionRecordSwagger"
            }
          },
          "pagination": {
            "type": "object",
            "example": {
              "page": 1,
              "pageSize": 20,
              "total": 3,
              "totalPages": 1
            }
          }
        },
        "required": ["data", "pagination"]
      },
      "InviteContextProgramSwagger": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "example": "Growth Partners"
          },
          "commissionType": {
            "type": "string",
            "enum": ["percentage", "flat_amount"]
          },
          "commissionRate": {
            "type": "number",
            "example": 20
          },
          "commissionBasis": {
            "type": "string",
            "enum": ["gross", "net"]
          },
          "commissionCurrency": {
            "type": "string",
            "example": "EUR"
          },
          "attributionWindowDays": {
            "type": "number",
            "example": 30
          }
        },
        "required": [
          "name",
          "commissionType",
          "commissionRate",
          "commissionBasis",
          "commissionCurrency",
          "attributionWindowDays"
        ]
      },
      "InviteContextResponseSwagger": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "example": "abc123token"
          },
          "inviteType": {
            "type": "string",
            "enum": ["general", "personal"]
          },
          "computedStatus": {
            "type": "string",
            "enum": ["active", "expired", "used", "revoked"]
          },
          "targetEmail": {
            "type": "string",
            "format": "email",
            "example": "partner@example.com",
            "nullable": true
          },
          "program": {
            "$ref": "#/components/schemas/InviteContextProgramSwagger"
          },
          "alreadyEnrolled": {
            "type": "boolean",
            "example": false
          }
        },
        "required": [
          "token",
          "inviteType",
          "computedStatus",
          "targetEmail",
          "program",
          "alreadyEnrolled"
        ]
      },
      "RedeemInviteResponseSwagger": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": ["pending", "approved", "already_enrolled"]
          },
          "participantId": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          }
        },
        "required": ["status", "participantId"]
      }
    }
  }
}
