{
  "openapi": "3.0.0",
  "info": {
    "title": "WEIR External API v2",
    "version": "3.1.0",
    "description": "# WEIR External API\n\nExternal API for third-party integrations. Access licenses and mentions you have permission to view, manage webhooks, and bookmark content.\n\n## Authentication Overview\n\nThis API uses Bearer tokens for authentication. There are two types of endpoints:\n\n### 🔓 Public Endpoints (No Authentication)\n- `GET /health` - Health check\n- `GET /metadata` - Public license catalog (rate-limited)\n- `GET /.well-known/weir` - Discovery endpoint\n- `POST /auth/token` - Exchange API key for access token\n- `POST /auth/token/refresh` - Refresh access token\n- `POST /token/info` - Introspect token validity\n\n### 🔑 Authenticated Endpoints\nAll other endpoints require a valid **External API Token** obtained by exchanging your API key and secret.\n\n## How to Authenticate\n\n### Step 1: Create API Key\nGenerate an API key in WEIR Developer Settings at https://weir.ai/developers\n\n### Step 2: Obtain Access Token\n```bash\ncurl -X POST https://wapi.weir.ai/auth/token \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"api_key\": \"YOUR_API_KEY\", \"api_secret\": \"YOUR_API_SECRET\"}'\n```\n\n### Step 3: Use Token in Requests\n```bash\ncurl https://wapi.weir.ai/licenses \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\"\n```\n\n## Scopes\n\nTokens are issued with specific scopes based on your API key permissions:\n- `license:read` - View licenses\n- `mention:read` - View mentions\n- `webhook:manage` - Create/update/delete webhooks\n\n## Rate Limits\n\nRate limits are based on your plan tier. Headers included in responses:\n- `X-RateLimit-Limit` - Maximum requests per window\n- `X-RateLimit-Remaining` - Requests remaining\n- `X-RateLimit-Reset` - Unix timestamp when window resets\n\n## Changelog\n\n### v3.1.0 (December 2024) - Improved Rate Limiting & Security\n- **Improved:** Magic link rate limiting now only counts unused OTP requests (better UX for retries)\n- **Improved:** Magic links are automatically deleted after successful verification (security enhancement)\n- **Improved:** Generic error responses for certain authentication failures (prevents account enumeration)\n- **Note:** Rate limit of 3 unused magic links per hour per email remains unchanged\n\n### v3.0.0 (December 2024) - BREAKING CHANGE: Passwordless Authentication\n- **Removed:** Password-based authentication endpoints\n- **Modified:** `POST /register` now passwordless (no password field)\n- **Modified:** `POST /magic-link/verify` accepts `{ email, otp }` or `{ token }`\n- **New:** 6-digit OTP sent with magic link emails\n- **New:** OTP autofill support on iOS/Android\n- See migration guide: https://weir.ai/developers/migration-v3\n\n### v2.15.0 (December 2024)\n- Removed internal-only endpoints from external API spec\n- Added `POST /auth/token` documentation\n- Added `POST /token/info` for token introspection\n- Added `POST /webhooks/{id}/test` for webhook testing\n- Cleaned up unused schemas",
    "contact": {
      "name": "WEIR Developer Support",
      "url": "https://weir.ai/developers"
    }
  },
  "servers": [
    {
      "url": "https://wapi.weir.ai",
      "description": "WEIR External API v2 (Production)"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "summary": "Health check",
        "description": "## 🔓 Public Endpoint (No Authentication Required)\n\nReturns API health status, version, and basic diagnostics. Use this to verify API availability before making authenticated requests.\n\n**Token Requirements:** None",
        "tags": [
          "System"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "API is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "healthy"
                      ]
                    },
                    "api_type": {
                      "type": "string",
                      "enum": [
                        "external"
                      ]
                    },
                    "version": {
                      "type": "string",
                      "example": "3.1.0"
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "status": "healthy",
                  "api_type": "external",
                  "version": "3.1.0",
                  "timestamp": "2024-12-22T10:30:00Z"
                }
              }
            }
          }
        }
      }
    },
    "/auth/token": {
      "post": {
        "summary": "Exchange API key for access token",
        "description": "## 🔓 Public Endpoint (No Authentication Required)\n\nExchange your API key and secret for an access token and refresh token. This is the primary authentication endpoint for the External API.\n\n**Token Lifetimes:**\n- Access tokens: 1 hour (3600 seconds)\n- Refresh tokens: 30 days\n\n**Rate Limit:** 10 requests per hour per API key",
        "tags": [
          "Authentication"
        ],
        "security": [],
        "operationId": "exchangeApiKey",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenExchangeRequest"
              },
              "example": {
                "api_key": "wapi_abc123...",
                "api_secret": "wapi_secret_xyz789..."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tokens issued successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenExchangeResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "access_token": "eyJhbGciOiJIUzI1NiIs...",
                    "refresh_token": "wrtx_abc123xyz789...",
                    "token_type": "Bearer",
                    "expires_in": 3600,
                    "scopes": [
                      "license:read",
                      "mention:read"
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing required fields",
            "content": {
              "application/json": {
                "example": {
                  "success": false,
                  "error": {
                    "code": "VALIDATION_ERROR",
                    "message": "api_key and api_secret are required"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials",
            "content": {
              "application/json": {
                "examples": {
                  "invalid_key": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "INVALID_CREDENTIALS",
                        "message": "Invalid API key"
                      }
                    }
                  },
                  "invalid_secret": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "INVALID_CREDENTIALS",
                        "message": "Invalid API secret"
                      }
                    }
                  },
                  "inactive": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "CREDENTIAL_INACTIVE",
                        "message": "API key is inactive"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "example": {
                  "success": false,
                  "error": {
                    "code": "RATE_LIMITED",
                    "message": "Too many authentication attempts"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/token/refresh": {
      "post": {
        "summary": "Refresh access token",
        "description": "## 🔓 Public Endpoint (No Authentication Required)\n\nExchange a valid refresh token for a new access token. Use this to maintain API access without re-authenticating.\n\n**Token Lifetimes:**\n- Access tokens: 1 hour (3600 seconds)\n- Refresh tokens: 30 days (2592000 seconds)\n\n**Best Practice:** Refresh proactively before expiry (e.g., 5 minutes before).\n\n**Refresh Token Prefix:** External API refresh tokens use `wrtx_` prefix.",
        "tags": [
          "Authentication"
        ],
        "security": [],
        "operationId": "refreshToken",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenRefreshRequest"
              },
              "example": {
                "refresh_token": "wrtx_abc123xyz789..."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New access token issued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenRefreshResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "access_token": "eyJhbGciOiJIUzI1NiIs...",
                    "token_type": "Bearer",
                    "expires_in": 3600
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing refresh token",
            "content": {
              "application/json": {
                "example": {
                  "success": false,
                  "error": {
                    "code": "VALIDATION_ERROR",
                    "message": "Refresh token is required"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or expired refresh token",
            "content": {
              "application/json": {
                "examples": {
                  "invalid": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "UNAUTHORIZED",
                        "message": "Invalid refresh token"
                      }
                    }
                  },
                  "expired": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "TOKEN_EXPIRED",
                        "message": "Refresh token has expired"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/token/info": {
      "post": {
        "summary": "Introspect token",
        "description": "## 🔓 Public Endpoint (No Authentication Required)\n\nCheck the validity and metadata of an access token. Useful for verifying token status before making requests.\n\n**Use Cases:**\n- Check if a token is still valid before making API calls\n- Get token expiration time for proactive refresh\n- Verify token scopes",
        "tags": [
          "Authentication"
        ],
        "security": [],
        "operationId": "introspectToken",
        "requestBody": {
          "required": false,
          "description": "Token can be provided in request body or Authorization header",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Access token to introspect"
                  }
                }
              },
              "example": {
                "token": "eyJhbGciOiJIUzI1NiIs..."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token information",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenInfoResponse"
                },
                "examples": {
                  "valid": {
                    "value": {
                      "success": true,
                      "data": {
                        "active": true,
                        "token_type": "external",
                        "scopes": [
                          "license:read",
                          "mention:read"
                        ],
                        "user_id": "550e8400-e29b-41d4-a716-446655440000",
                        "issued_at": "2024-12-15T10:00:00Z",
                        "expires_at": "2024-12-15T11:00:00Z",
                        "expires_in": 2400,
                        "revoked": false
                      }
                    }
                  },
                  "expired": {
                    "value": {
                      "success": true,
                      "data": {
                        "active": false,
                        "token_type": "external",
                        "expires_at": "2024-12-15T09:00:00Z",
                        "revoked": false
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No token provided",
            "content": {
              "application/json": {
                "example": {
                  "success": false,
                  "error": {
                    "code": "VALIDATION_ERROR",
                    "message": "Token is required"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/licenses": {
      "get": {
        "summary": "List accessible licenses",
        "description": "## 🔑 Authenticated Endpoint\n\nRetrieve licenses you have permission to access. Returns only licenses where you have viewer, trustee, or licensor permissions.\n\n**Token Requirements:**\n- **Token Type:** External API Token\n- **Header:** `Authorization: Bearer <token>`\n- **Required Scopes:** `license:read`",
        "tags": [
          "Licenses"
        ],
        "x-scope": [
          "license:read"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number for pagination. Defaults to 1.",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1,
              "example": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Number of items per page. Maximum 100, defaults to 20.",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100,
              "example": 20
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by license status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "private",
                "public",
                "expired"
              ],
              "example": "public"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Keyword search across license title and description.",
            "schema": {
              "type": "string",
              "example": "actor"
            }
          },
          {
            "name": "license_type",
            "in": "query",
            "required": false,
            "description": "Filter by license type key.",
            "schema": {
              "type": "string",
              "example": "balanced"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Field to sort by.",
            "schema": {
              "type": "string",
              "enum": [
                "created_at",
                "updated_at",
                "title"
              ],
              "default": "created_at"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "description": "Sort order.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "name": "include_image_urls",
            "in": "query",
            "required": false,
            "description": "Include pre-signed image URLs (valid 1 hour).",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of accessible licenses",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/License"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or expired token"
          },
          "403": {
            "description": "Insufficient scope (requires license:read)"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/licenses/{id}": {
      "get": {
        "summary": "Get license details",
        "description": "## 🔑 Authenticated Endpoint\n\nRetrieve details for a specific license. Requires viewer permission or higher.\n\n**Token Requirements:**\n- **Token Type:** External API Token\n- **Header:** `Authorization: Bearer <token>`\n- **Required Scopes:** `license:read`",
        "tags": [
          "Licenses"
        ],
        "x-scope": [
          "license:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "License ID (UUID or external_id like 'WR7A2K9X5').",
            "schema": {
              "type": "string",
              "example": "WR7A2K9X5"
            }
          },
          {
            "name": "include_images",
            "in": "query",
            "required": false,
            "description": "Include reference images in response.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "include_image_urls",
            "in": "query",
            "required": false,
            "description": "Include pre-signed image URLs (requires include_images=true).",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "License details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/License"
                }
              }
            }
          },
          "403": {
            "description": "No permission to access this license"
          },
          "404": {
            "description": "License not found"
          }
        }
      },
      "patch": {
        "summary": "Update license",
        "description": "## 🔑 Authenticated Endpoint\n\nUpdate mutable fields on a license you own.\n\n**Token Requirements:**\n- **Token Type:** External API Token\n- **Header:** `Authorization: Bearer <token>`\n- **Required Scopes:** `license:write`",
        "tags": [
          "Licenses"
        ],
        "x-scope": [
          "license:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "License ID (UUID or external_id like 'WR7A2K9X5').",
            "schema": {
              "type": "string",
              "example": "WR7A2K9X5"
            }
          },
          {
            "name": "include_images",
            "in": "query",
            "required": false,
            "description": "Include reference images in response.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "include_image_urls",
            "in": "query",
            "required": false,
            "description": "Include pre-signed image URLs (requires include_images=true).",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true,
                "description": "Mutable license fields to update."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "License updated"
          },
          "401": {
            "description": "Missing or invalid token"
          },
          "403": {
            "description": "Token lacks the license:write scope"
          },
          "404": {
            "description": "License not found"
          }
        }
      }
    },
    "/licenses/{id}/mentions": {
      "get": {
        "summary": "Get license mentions",
        "description": "## 🔑 Authenticated Endpoint\n\nRetrieve mentions detected for a specific license. Requires viewer permission or higher on the license.\n\n**Token Requirements:**\n- **Token Type:** External API Token\n- **Header:** `Authorization: Bearer <token>`\n- **Required Scopes:** `mention:read`",
        "tags": [
          "Mentions"
        ],
        "x-scope": [
          "mention:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "License ID (UUID format).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by mention status.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "confirmed",
                "flagged",
                "ignored"
              ]
            }
          },
          {
            "name": "min_confidence",
            "in": "query",
            "required": false,
            "description": "Minimum confidence score (0.0-1.0).",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of mentions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Mention"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "No permission to access this license's mentions"
          },
          "404": {
            "description": "License not found"
          }
        }
      }
    },
    "/licenses/{id}/images": {
      "get": {
        "summary": "Get license reference images",
        "description": "## 🔑 Authenticated Endpoint\n\nRetrieve public reference images linked to a license. Returns signed URLs for image access.\n\n**Token Requirements:**\n- **Token Type:** External API Token\n- **Header:** `Authorization: Bearer <token>`\n- **Required Scopes:** `license:read`",
        "tags": [
          "Licenses"
        ],
        "x-scope": [
          "license:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "License ID (UUID or external_id).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of reference images",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LicenseImage"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "No permission to access this license"
          },
          "404": {
            "description": "License not found"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "summary": "List webhooks",
        "description": "## 🔑 Authenticated Endpoint\n\nRetrieve all webhooks configured for your account.\n\n**Token Requirements:**\n- **Required Scopes:** `webhook:manage`",
        "tags": [
          "Webhooks"
        ],
        "x-scope": [
          "webhook:manage"
        ],
        "responses": {
          "200": {
            "description": "List of webhooks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create webhook",
        "description": "## 🔑 Authenticated Endpoint\n\nCreate a new webhook endpoint. URL must be HTTPS and domain must be verified.\n\n**Token Requirements:**\n- **Required Scopes:** `webhook:manage`",
        "tags": [
          "Webhooks"
        ],
        "x-scope": [
          "webhook:manage"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "description": "Invalid webhook configuration"
          }
        }
      }
    },
    "/webhooks/{id}": {
      "get": {
        "summary": "Get webhook details",
        "description": "## 🔑 Authenticated Endpoint\n\nRetrieve webhook configuration and recent delivery logs.\n\n**Token Requirements:**\n- **Required Scopes:** `webhook:manage`",
        "tags": [
          "Webhooks"
        ],
        "x-scope": [
          "webhook:manage"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook details with delivery logs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookWithLogs"
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found"
          }
        }
      },
      "patch": {
        "summary": "Update webhook",
        "description": "## 🔑 Authenticated Endpoint\n\nUpdate webhook configuration.\n\n**Token Requirements:**\n- **Required Scopes:** `webhook:manage`",
        "tags": [
          "Webhooks"
        ],
        "x-scope": [
          "webhook:manage"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete webhook",
        "description": "## 🔑 Authenticated Endpoint\n\nPermanently delete a webhook.\n\n**Token Requirements:**\n- **Required Scopes:** `webhook:manage`",
        "tags": [
          "Webhooks"
        ],
        "x-scope": [
          "webhook:manage"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Webhook deleted"
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "summary": "Test webhook",
        "description": "## 🔑 Authenticated Endpoint\n\nSend a test event to verify your webhook endpoint is working correctly. Returns the delivery result.\n\n**Token Requirements:**\n- **Required Scopes:** `webhook:manage`",
        "tags": [
          "Webhooks"
        ],
        "x-scope": [
          "webhook:manage"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event_type": {
                    "type": "string",
                    "enum": [
                      "license.status_changed",
                      "mention.detected"
                    ],
                    "default": "mention.detected",
                    "description": "Type of test event to send"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Test event sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "delivered": {
                          "type": "boolean"
                        },
                        "response_code": {
                          "type": "integer"
                        },
                        "response_time_ms": {
                          "type": "integer"
                        },
                        "error_message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "delivered": true,
                    "response_code": 200,
                    "response_time_ms": 245
                  }
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found"
          }
        }
      }
    },
    "/bookmarks": {
      "get": {
        "summary": "List bookmarks",
        "description": "## 🔑 Authenticated Endpoint\n\nRetrieve all bookmarked mentions.\n\n**Token Requirements:**\n- **Required Scopes:** `mention:read`",
        "tags": [
          "Bookmarks"
        ],
        "x-scope": [
          "mention:read"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of bookmarks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Bookmark"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create bookmark",
        "description": "## 🔑 Authenticated Endpoint\n\nBookmark a mention for later reference.\n\n**Token Requirements:**\n- **Required Scopes:** `mention:read`",
        "tags": [
          "Bookmarks"
        ],
        "x-scope": [
          "mention:read"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "mention_id"
                ],
                "properties": {
                  "mention_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 1000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Bookmark created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bookmark"
                }
              }
            }
          }
        }
      }
    },
    "/bookmarks/{id}": {
      "delete": {
        "summary": "Delete bookmark",
        "description": "## 🔑 Authenticated Endpoint\n\nRemove a bookmark.\n\n**Token Requirements:**\n- **Required Scopes:** `mention:read`",
        "tags": [
          "Bookmarks"
        ],
        "x-scope": [
          "mention:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Bookmark deleted"
          }
        }
      }
    },
    "/metadata": {
      "get": {
        "summary": "Public license catalog",
        "description": "## 🔓 Public Endpoint (No Authentication Required)\n\nReturns a public catalog of discoverable licenses in JSON + JSON-LD format. Designed for AI agents, search engines, and integrators.\n\n**Token Requirements:** None\n\n**Rate Limit:** 10 requests per minute (unauthenticated)",
        "tags": [
          "Metadata Discovery"
        ],
        "security": [],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of licenses to return.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of records to skip for pagination.",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public license catalog with JSON-LD structured data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MetadataResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "example": {
                  "error": "Rate limit exceeded",
                  "message": "Maximum 10 requests per minute",
                  "retry_after": 60
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/weir": {
      "get": {
        "summary": "Well-known discovery endpoint",
        "description": "## 🔓 Public Endpoint (No Authentication Required)\n\nStandard discovery endpoint following the `.well-known` convention. Returns metadata about the WEIR service.\n\n**Token Requirements:** None",
        "tags": [
          "Metadata Discovery"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Discovery information",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WellKnownResponse"
                },
                "example": {
                  "weir_metadata": "https://weir.ai/metadata",
                  "metadata_version": "1.0",
                  "powered_by": "WEIR",
                  "public": true,
                  "documentation": "https://weir.ai/developers"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "External API access token obtained from /auth/token endpoint using your API key and secret.\n\n**How to obtain:**\n```bash\ncurl -X POST https://wapi.weir.ai/auth/token \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"api_key\": \"YOUR_API_KEY\", \"api_secret\": \"YOUR_API_SECRET\"}'\n```"
      }
    },
    "schemas": {
      "TokenExchangeRequest": {
        "type": "object",
        "required": [
          "api_key",
          "api_secret"
        ],
        "properties": {
          "api_key": {
            "type": "string",
            "description": "Your API key",
            "example": "wapi_abc123..."
          },
          "api_secret": {
            "type": "string",
            "description": "Your API secret",
            "example": "wapi_secret_xyz789..."
          }
        }
      },
      "TokenExchangeResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "data": {
            "type": "object",
            "properties": {
              "access_token": {
                "type": "string",
                "description": "JWT access token"
              },
              "refresh_token": {
                "type": "string",
                "description": "Refresh token (wrtx_ prefix)"
              },
              "token_type": {
                "type": "string",
                "example": "Bearer"
              },
              "expires_in": {
                "type": "integer",
                "description": "Seconds until expiry",
                "example": 3600
              },
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Granted scopes"
              }
            }
          }
        }
      },
      "TokenRefreshRequest": {
        "type": "object",
        "required": [
          "refresh_token"
        ],
        "properties": {
          "refresh_token": {
            "type": "string",
            "description": "Valid refresh token (wrtx_ prefix)",
            "example": "wrtx_abc123..."
          }
        }
      },
      "TokenRefreshResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "data": {
            "type": "object",
            "properties": {
              "access_token": {
                "type": "string",
                "description": "New access token"
              },
              "token_type": {
                "type": "string",
                "example": "Bearer"
              },
              "expires_in": {
                "type": "integer",
                "description": "Seconds until expiry",
                "example": 3600
              }
            }
          }
        }
      },
      "TokenInfoResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "data": {
            "type": "object",
            "properties": {
              "active": {
                "type": "boolean",
                "description": "Whether token is currently valid"
              },
              "token_type": {
                "type": "string",
                "enum": [
                  "external"
                ],
                "description": "Token type"
              },
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Granted scopes"
              },
              "user_id": {
                "type": "string",
                "format": "uuid",
                "description": "User ID associated with token"
              },
              "issued_at": {
                "type": "string",
                "format": "date-time"
              },
              "expires_at": {
                "type": "string",
                "format": "date-time"
              },
              "expires_in": {
                "type": "integer",
                "description": "Seconds until expiry (if active)"
              },
              "revoked": {
                "type": "boolean",
                "description": "Whether token has been revoked"
              }
            }
          }
        }
      },
      "License": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "external_id": {
            "type": "string",
            "example": "WR7A2K9X5"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "private",
              "public",
              "expired"
            ]
          },
          "license_type_key": {
            "type": "string",
            "description": "License type identifier (e.g., protect, earn, blocked)",
            "example": "earn"
          },
          "user_role": {
            "type": "string",
            "enum": [
              "viewer",
              "trustee",
              "licensor"
            ],
            "description": "Your permission level"
          },
          "full_terms": {
            "$ref": "#/components/schemas/FullTerms"
          },
          "identity": {
            "type": "object",
            "description": "Identity metadata associated with the license",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "legal_name": {
                "type": "string"
              },
              "stage_name": {
                "type": "string",
                "nullable": true
              },
              "identity_type": {
                "type": "string",
                "enum": [
                  "person",
                  "estate",
                  "organization",
                  "character"
                ],
                "description": "Type of identity (person for living individuals, estate for deceased, organization for companies, character for fictional characters)"
              }
            }
          },
          "valid_from": {
            "type": "string",
            "format": "date-time"
          },
          "valid_until": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FullTerms": {
        "type": "object",
        "nullable": true,
        "description": "Resolved negotiation terms merging license type defaults with instance overrides. Visibility depends on your role: owners/managers see all fields including pricing strategy; subscribers see joint terms only (no fee data); viewers do not receive this field.",
        "properties": {
          "license_type_key": {
            "type": "string",
            "description": "License type identifier",
            "example": "earn"
          },
          "commercial_use": {
            "type": "boolean",
            "description": "Whether commercial use is permitted"
          },
          "min_fee": {
            "type": "integer",
            "description": "Minimum fee in cents (owner/manager only)",
            "nullable": true
          },
          "preferred_fee": {
            "type": "integer",
            "description": "Preferred fee in cents (owner/manager only)",
            "nullable": true
          },
          "max_duration_months": {
            "type": "integer",
            "description": "Maximum license duration (owner/manager only)",
            "nullable": true
          },
          "exclusivity": {
            "type": "string",
            "enum": [
              "none",
              "category",
              "full"
            ],
            "description": "Exclusivity level"
          },
          "exclusivity_max_days": {
            "type": "integer",
            "description": "Max exclusivity duration in days (owner/manager only)",
            "nullable": true
          },
          "allowed_usage": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Permitted usage types"
          },
          "blocked_usage": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Prohibited usage types"
          },
          "blocked_categories": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Blocked industry categories"
          },
          "approved_platforms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Approved distribution platforms"
          },
          "attribution_required": {
            "type": "boolean",
            "description": "Whether attribution is required"
          },
          "modification_allowed": {
            "type": "boolean",
            "description": "Whether content modification is allowed"
          }
        }
      },
      "Mention": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "license_id": {
            "type": "string",
            "format": "uuid"
          },
          "confidence_score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "confirmed",
              "flagged",
              "ignored"
            ]
          },
          "match_type": {
            "type": "string"
          },
          "matched_snippet": {
            "type": "string"
          },
          "source_url": {
            "type": "string"
          },
          "source_type": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "content_asset": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "title": {
                "type": "string"
              },
              "source": {
                "type": "string"
              },
              "image_url": {
                "type": "string",
                "format": "uri",
                "description": "Pre-signed URL (when include_image_urls=true)"
              }
            }
          }
        }
      },
      "LicenseImage": {
        "type": "object",
        "description": "Reference image linked to a license",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "content_asset_id": {
            "type": "string",
            "format": "uuid"
          },
          "license_id": {
            "type": "string",
            "format": "uuid"
          },
          "is_cover_image": {
            "type": "boolean"
          },
          "is_public": {
            "type": "boolean"
          },
          "display_order": {
            "type": "integer"
          },
          "image_url": {
            "type": "string",
            "format": "uri",
            "description": "Signed URL (valid 1 hour)"
          },
          "original_name": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "license.status_changed",
                "license.updated",
                "mention.detected",
                "mention.confirmed"
              ]
            }
          },
          "license_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter to specific licenses (null = all accessible)"
          },
          "is_active": {
            "type": "boolean"
          },
          "failure_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookCreate": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS URL (domain must be verified)"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "license.status_changed",
                "license.updated",
                "mention.detected",
                "mention.confirmed"
              ]
            },
            "minItems": 1
          },
          "license_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter to specific licenses (omit for all accessible)"
          }
        }
      },
      "WebhookUpdate": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "license_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "is_active": {
            "type": "boolean"
          }
        }
      },
      "WebhookWithLogs": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Webhook"
          },
          {
            "type": "object",
            "properties": {
              "recent_deliveries": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "event_type": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "success",
                        "failed"
                      ]
                    },
                    "response_code": {
                      "type": "integer"
                    },
                    "error_message": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "Bookmark": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "mention_id": {
            "type": "string",
            "format": "uuid"
          },
          "notes": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "mention": {
            "$ref": "#/components/schemas/Mention"
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "per_page": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "total_pages": {
            "type": "integer"
          }
        }
      },
      "MetadataResponse": {
        "type": "object",
        "description": "Public license metadata catalog with JSON-LD structured data",
        "properties": {
          "metadata_version": {
            "type": "string",
            "example": "1.0"
          },
          "powered_by": {
            "type": "string",
            "example": "WEIR"
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "total_count": {
            "type": "integer"
          },
          "returned_count": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "licenses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetadataLicense"
            }
          },
          "@context": {
            "type": "string",
            "example": "https://schema.org"
          },
          "@graph": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JsonLdPerson"
            }
          }
        }
      },
      "MetadataLicense": {
        "type": "object",
        "properties": {
          "display_name": {
            "type": "string"
          },
          "license_type": {
            "type": "string"
          },
          "license_type_key": {
            "type": "string"
          },
          "expiration_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "is_verified": {
            "type": "boolean"
          },
          "terms_url": {
            "type": "string",
            "format": "uri"
          },
          "purchase_url": {
            "type": "string",
            "format": "uri"
          },
          "display_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "JsonLdPerson": {
        "type": "object",
        "properties": {
          "@type": {
            "type": "string",
            "example": "Person"
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "identifier": {
            "type": "string"
          },
          "license": {
            "type": "object",
            "properties": {
              "@type": {
                "type": "string",
                "example": "CreativeWork"
              },
              "license": {
                "type": "string",
                "format": "uri"
              },
              "validThrough": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "WellKnownResponse": {
        "type": "object",
        "properties": {
          "weir_metadata": {
            "type": "string",
            "format": "uri"
          },
          "metadata_version": {
            "type": "string"
          },
          "powered_by": {
            "type": "string"
          },
          "public": {
            "type": "boolean"
          },
          "documentation": {
            "type": "string",
            "format": "uri"
          },
          "rate_limits": {
            "type": "object",
            "properties": {
              "public": {
                "type": "string"
              },
              "authenticated": {
                "type": "string"
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "object"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "success",
          "error"
        ]
      }
    }
  },
  "tags": [
    {
      "name": "Authentication",
      "description": "Token management and authentication"
    },
    {
      "name": "Licenses",
      "description": "License management and access"
    },
    {
      "name": "Mentions",
      "description": "License-scoped mention detection results"
    },
    {
      "name": "Webhooks",
      "description": "Webhook configuration and testing"
    },
    {
      "name": "Bookmarks",
      "description": "Bookmark management for mentions"
    },
    {
      "name": "Metadata Discovery",
      "description": "Public discovery endpoints for AI agents and search engines"
    },
    {
      "name": "System",
      "description": "Health checks and system information"
    }
  ]
}
