{
  "openapi": "3.0.3",
  "info": {
    "title": "12signals API",
    "version": "1.0.0",
    "description": "Competitive intelligence API for scraping, tracking competitors, monitoring job postings, and aggregating news. Runs as Netlify Functions."
  },
  "servers": [
    {
      "url": "https://api.12signals.com",
      "description": "Production"
    },
    {
      "url": "http://localhost:8893",
      "description": "Local Development"
    }
  ],
  "tags": [
    {
      "name": "public",
      "description": "Unauthenticated utility endpoints"
    },
    {
      "name": "api",
      "description": "API-key protected endpoints (Bearer API_SECRET_KEY)"
    },
    {
      "name": "app",
      "description": "User-authenticated endpoints (Supabase JWT, RLS enforced)"
    },
    {
      "name": "admin",
      "description": "Admin endpoints (Supabase JWT + profiles.is_devaccount = true)"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API secret key passed as Bearer token. Validated against API_SECRET_KEY env var."
      },
      "supabaseAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Supabase user JWT token. Used with RLS-enforced queries."
      },
      "supabaseAdminAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Supabase user JWT token. Requires profiles.is_devaccount = true."
      }
    },
    "schemas": {
      "send-magic-link_output": {
        "type": "object",
        "properties": {
          "sent": {
            "type": "boolean"
          }
        },
        "required": [
          "sent"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "send-magic-link_input": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "redirect_to": {
            "type": "string"
          }
        },
        "required": [
          "email"
        ]
      },
      "html-clean_output": {
        "type": "string",
        "description": "Cleaned HTML"
      },
      "html-clean_input": {
        "type": "object",
        "properties": {
          "html": {
            "type": "string",
            "minLength": 1,
            "description": "Raw HTML to clean"
          }
        },
        "required": [
          "html"
        ]
      },
      "html-soft-404_output": {
        "type": "object",
        "properties": {
          "isSoft404": {
            "type": "boolean"
          },
          "score": {
            "type": "number"
          },
          "signals": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "isSoft404",
          "score",
          "signals"
        ]
      },
      "html-soft-404_input": {
        "type": "object",
        "properties": {
          "html": {
            "type": "string",
            "minLength": 1,
            "description": "HTML to analyze for soft 404 signals"
          }
        },
        "required": [
          "html"
        ]
      },
      "urls-normalize_output": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          }
        },
        "required": [
          "url"
        ]
      },
      "urls-normalize_input": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "url"
        ]
      },
      "subscriptions-create_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "plan": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "ok",
          "plan"
        ]
      },
      "subscriptions-create_input": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string"
          }
        }
      },
      "approve-external-domain_output": {
        "type": "string",
        "description": "HTML response page"
      },
      "approve-website-redirect_output": {
        "type": "string",
        "description": "HTML response page"
      },
      "public-competitors_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "competitor": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "slug": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "website": {
                "type": "string",
                "nullable": true
              },
              "linkedin_url": {
                "type": "string",
                "nullable": true
              },
              "description": {
                "type": "string",
                "nullable": true
              },
              "kpi_snapshot": {
                "nullable": true
              }
            },
            "required": [
              "id",
              "slug",
              "name",
              "website",
              "linkedin_url",
              "description"
            ]
          },
          "activeJobs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "title": {
                  "type": "string",
                  "nullable": true
                },
                "url": {
                  "type": "string",
                  "nullable": true
                },
                "first_detected": {
                  "type": "string",
                  "nullable": true
                },
                "ended": {
                  "type": "string",
                  "nullable": true
                },
                "linkedin_job_function_code": {
                  "type": "string",
                  "nullable": true
                }
              },
              "required": [
                "id",
                "title",
                "url",
                "first_detected",
                "ended",
                "linkedin_job_function_code"
              ]
            }
          },
          "jobLifecycle": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "first_detected": {
                  "type": "string",
                  "nullable": true
                },
                "ended": {
                  "type": "string",
                  "nullable": true
                }
              },
              "required": [
                "first_detected",
                "ended"
              ]
            }
          },
          "claims": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "claim": {
                  "type": "string"
                },
                "snapshot_date": {
                  "type": "string"
                }
              },
              "required": [
                "claim",
                "snapshot_date"
              ]
            }
          }
        },
        "required": [
          "ok",
          "competitor",
          "activeJobs",
          "jobLifecycle",
          "claims"
        ]
      },
      "mailgun-webhook_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ]
      },
      "mailgun-webhook_input": {
        "type": "object",
        "properties": {
          "signature": {
            "type": "object",
            "properties": {
              "timestamp": {
                "type": "string"
              },
              "token": {
                "type": "string"
              },
              "signature": {
                "type": "string"
              }
            },
            "required": [
              "timestamp",
              "token",
              "signature"
            ]
          },
          "event-data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "event": {
                "type": "string"
              },
              "timestamp": {
                "type": "number"
              },
              "recipient": {
                "type": "string"
              },
              "user-variables": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "url": {
                "type": "string"
              },
              "message": {
                "type": "object",
                "properties": {
                  "headers": {
                    "type": "object",
                    "properties": {
                      "message-id": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            },
            "required": [
              "id",
              "event",
              "timestamp"
            ]
          }
        },
        "required": [
          "signature",
          "event-data"
        ]
      },
      "get-competitors_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "total": {
            "type": "number",
            "nullable": true,
            "description": "Total matching rows (from content-range)"
          },
          "competitors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "website": {
                  "type": "string"
                },
                "description": {
                  "type": "string",
                  "nullable": true
                },
                "active": {
                  "type": "boolean"
                },
                "linkedin_url": {
                  "type": "string",
                  "nullable": true
                },
                "created_at": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "name",
                "website",
                "description",
                "active",
                "linkedin_url",
                "created_at"
              ]
            }
          }
        },
        "required": [
          "ok",
          "total",
          "competitors"
        ]
      },
      "get-competitor-people_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "total": {
            "type": "number",
            "nullable": true,
            "description": "Total matching rows (from content-range)"
          },
          "people": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "linkedin_url": {
                  "type": "string",
                  "nullable": true
                },
                "source": {
                  "type": "string",
                  "nullable": true
                },
                "confidence": {
                  "type": "string",
                  "nullable": true
                },
                "role_id": {
                  "type": "string",
                  "nullable": true,
                  "format": "uuid"
                },
                "roles": {
                  "type": "object",
                  "nullable": true,
                  "properties": {
                    "name": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name"
                  ]
                },
                "competitor_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "created_at": {
                  "type": "string"
                },
                "updated_at": {
                  "type": "string",
                  "nullable": true
                }
              },
              "required": [
                "id",
                "name",
                "linkedin_url",
                "source",
                "confidence",
                "role_id",
                "roles",
                "competitor_id",
                "created_at",
                "updated_at"
              ]
            }
          }
        },
        "required": [
          "ok",
          "total",
          "people"
        ]
      },
      "create-competitor_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "competitor": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "website": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "active": {
                "type": "boolean"
              }
            },
            "required": [
              "id",
              "name",
              "website",
              "description",
              "active"
            ]
          }
        },
        "required": [
          "ok",
          "competitor"
        ]
      },
      "create-competitor_input": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "website": {
            "type": "string",
            "minLength": 1,
            "description": "Website URL (will be normalized)"
          },
          "description": {
            "type": "string",
            "minLength": 1
          },
          "language_code": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 639-1 two-letter language code of the website (e.g. en, de, fr)"
          },
          "translations": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            },
            "description": "Translations JSONB, e.g. { de: { description: '...' } }"
          }
        },
        "required": [
          "name",
          "website",
          "description",
          "language_code"
        ]
      },
      "scrape-url_output": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "success, error, or accepted"
          },
          "content": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "scrapedUrl": {
            "type": "string"
          },
          "scraper": {
            "type": "string",
            "description": "Which scraper tier was used"
          },
          "links": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string"
                }
              },
              "required": [
                "url"
              ]
            }
          },
          "warning": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "description": "Human-readable status message (async mode)"
          }
        },
        "required": [
          "status"
        ]
      },
      "scrape-url_input": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "description": "URL to scrape"
          },
          "scrape_links": {
            "type": "boolean",
            "description": "Extract links from the page"
          },
          "responseURL": {
            "type": "string",
            "format": "uri",
            "description": "Webhook URL for async mode"
          },
          "tier": {
            "type": "string",
            "enum": [
              "fetch",
              "scrapfly",
              "firecrawl"
            ],
            "description": "Force a specific scraper tier"
          }
        },
        "required": [
          "url"
        ]
      },
      "activity-feed_output": {
        "type": "object",
        "properties": {
          "activities": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": {
                "nullable": true
              }
            },
            "description": "Activity display items (classic mode)"
          },
          "days": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "description": "ISO date (UTC)"
                },
                "budget": {
                  "type": "number"
                },
                "activities": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": {
                      "nullable": true
                    }
                  }
                }
              },
              "required": [
                "date",
                "budget",
                "activities"
              ]
            },
            "description": "Day slices with budgeted activities (daily mode)"
          },
          "nextCursor": {
            "type": "string",
            "nullable": true,
            "description": "Next date cursor for pagination (daily mode)"
          },
          "meta": {
            "type": "object",
            "properties": {
              "count": {
                "type": "number"
              },
              "stage": {
                "type": "string",
                "description": "last-24h, last-5d, all-time (classic) or daily"
              },
              "sinceApplied": {
                "type": "string",
                "nullable": true
              },
              "minimumTarget": {
                "type": "number",
                "nullable": true
              },
              "competitorIdsCount": {
                "type": "number"
              },
              "competitorCount": {
                "type": "number"
              },
              "budget": {
                "type": "number"
              },
              "daysReturned": {
                "type": "number"
              }
            }
          }
        },
        "required": [
          "meta"
        ]
      },
      "activity-feed_input": {
        "type": "object",
        "properties": {
          "competitorIds": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "string"
              }
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "classic",
              "daily"
            ],
            "default": "classic"
          },
          "limit": {
            "type": "number",
            "minimum": 0,
            "exclusiveMinimum": true
          },
          "since": {
            "type": "string",
            "description": "ISO 8601 timestamp"
          },
          "groupJobs": {
            "type": "boolean",
            "default": true
          },
          "fallbackToActive": {
            "type": "boolean",
            "default": false
          },
          "cursor": {
            "type": "string",
            "description": "ISO date cursor for daily mode pagination (exclusive upper bound)"
          },
          "pageSize": {
            "type": "number",
            "minimum": 0,
            "exclusiveMinimum": true,
            "default": 3,
            "description": "Number of days per page in daily mode"
          }
        }
      },
      "ads-overview_output": {
        "type": "object",
        "properties": {
          "matrix": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "competitor_id": {
                  "type": "string"
                },
                "topic_slug": {
                  "type": "string"
                },
                "active_count": {
                  "type": "integer",
                  "minimum": 0
                },
                "total_count": {
                  "type": "integer",
                  "minimum": 0
                },
                "first_seen": {
                  "type": "string",
                  "nullable": true
                },
                "last_seen": {
                  "type": "string",
                  "nullable": true
                },
                "is_self": {
                  "type": "boolean"
                }
              },
              "required": [
                "competitor_id",
                "topic_slug",
                "active_count",
                "total_count",
                "first_seen",
                "last_seen",
                "is_self"
              ]
            }
          },
          "ads": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": {
                "nullable": true
              }
            }
          },
          "momentum": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "topic_slug": {
                  "type": "string"
                },
                "competitor_id": {
                  "type": "string"
                },
                "week_start": {
                  "type": "string"
                },
                "active_count": {
                  "type": "integer",
                  "minimum": 0
                },
                "total_count": {
                  "type": "integer",
                  "minimum": 0
                },
                "is_self": {
                  "type": "boolean"
                }
              },
              "required": [
                "topic_slug",
                "competitor_id",
                "week_start",
                "active_count",
                "total_count",
                "is_self"
              ]
            }
          },
          "campaigns": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "anchor_ad_id": {
                  "type": "string"
                },
                "competitor_id": {
                  "type": "string"
                },
                "title": {
                  "type": "string",
                  "nullable": true
                },
                "title_state": {
                  "type": "string",
                  "enum": [
                    "missing",
                    "current",
                    "stale"
                  ]
                },
                "ran_from": {
                  "type": "string",
                  "nullable": true
                },
                "ran_to": {
                  "type": "string",
                  "nullable": true
                },
                "ad_count": {
                  "type": "integer",
                  "minimum": 0
                },
                "active_ad_count": {
                  "type": "integer",
                  "minimum": 0
                },
                "active": {
                  "type": "boolean",
                  "nullable": true
                },
                "first_detected": {
                  "type": "string",
                  "nullable": true
                },
                "is_self": {
                  "type": "boolean"
                }
              },
              "required": [
                "anchor_ad_id",
                "competitor_id",
                "title",
                "title_state",
                "ran_from",
                "ran_to",
                "ad_count",
                "active_ad_count",
                "active",
                "first_detected",
                "is_self"
              ]
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "self_competitor_id": {
                "type": "string",
                "nullable": true
              },
              "org_id": {
                "type": "integer"
              },
              "ads_limit": {
                "type": "integer"
              },
              "locale": {
                "type": "string"
              },
              "competitors": {
                "type": "object",
                "additionalProperties": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "website": {
                      "type": "string",
                      "nullable": true
                    },
                    "brandfetch_domain": {
                      "type": "string",
                      "nullable": true
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "website",
                    "brandfetch_domain"
                  ]
                }
              },
              "filters": {
                "type": "object",
                "additionalProperties": {
                  "nullable": true
                }
              }
            },
            "required": [
              "self_competitor_id",
              "org_id",
              "ads_limit",
              "locale",
              "competitors",
              "filters"
            ]
          }
        },
        "required": [
          "matrix",
          "ads",
          "momentum",
          "campaigns",
          "meta"
        ]
      },
      "ads-overview_input": {
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "integer",
            "minimum": 0,
            "exclusiveMinimum": true
          },
          "topicSlugs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "competitorIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "UUIDs of competitors to scope to (subset of the org's competitors)"
          },
          "activeOnly": {
            "type": "boolean",
            "default": false
          },
          "periodStart": {
            "type": "string",
            "description": "ISO 8601 timestamp"
          },
          "periodEnd": {
            "type": "string",
            "description": "ISO 8601 timestamp"
          },
          "adsLimit": {
            "type": "number",
            "minimum": 0,
            "exclusiveMinimum": true,
            "maximum": 1000,
            "default": 200
          },
          "locale": {
            "type": "string",
            "default": "en"
          }
        },
        "required": [
          "organizationId"
        ]
      },
      "activity-digest-preview_output": {
        "type": "object",
        "properties": {
          "html": {
            "type": "string",
            "description": "Rendered email HTML"
          },
          "meta": {
            "type": "object",
            "properties": {
              "count": {
                "type": "number"
              }
            },
            "required": [
              "count"
            ]
          }
        },
        "required": [
          "html",
          "meta"
        ]
      },
      "activity-digest-preview_input": {
        "type": "object",
        "properties": {
          "competitorIds": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "string"
              }
            ]
          },
          "limit": {
            "type": "number",
            "minimum": 0,
            "exclusiveMinimum": true
          },
          "since": {
            "type": "string",
            "description": "ISO 8601 timestamp"
          },
          "fallbackToActive": {
            "type": "boolean",
            "default": false
          },
          "firstName": {
            "type": "string"
          },
          "appBaseUrl": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": {
                "nullable": true
              }
            },
            "description": "Pre-built display items (for Playground previews)"
          }
        }
      },
      "invite-member_output": {
        "type": "object",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "invited",
              "added"
            ],
            "description": "Whether user was invited (new) or added (existing)"
          },
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "emailSent": {
            "type": "boolean"
          },
          "emailError": {
            "type": "string"
          }
        },
        "required": [
          "action"
        ]
      },
      "invite-member_input": {
        "type": "object",
        "properties": {
          "org_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              }
            ]
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "redirect_to": {
            "type": "string"
          }
        },
        "required": [
          "org_id",
          "email"
        ]
      },
      "revoke-invitation_output": {
        "type": "object",
        "properties": {
          "revoked": {
            "type": "boolean"
          },
          "authUserDeleted": {
            "type": "boolean"
          },
          "email": {
            "type": "string",
            "format": "email"
          }
        },
        "required": [
          "revoked",
          "authUserDeleted"
        ]
      },
      "revoke-invitation_input": {
        "type": "object",
        "properties": {
          "invitation_id": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "invitation_id"
        ]
      },
      "strategy-radar_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "trends": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "slug": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "jobCount": {
                  "type": "number",
                  "description": "Jobs in the last 4 weeks with this trend"
                },
                "newsCount": {
                  "type": "number",
                  "description": "News articles in the last 4 weeks with this trend"
                },
                "score": {
                  "type": "number",
                  "description": "Combined relevance score (jobs + news)"
                },
                "isNewcomer": {
                  "type": "boolean",
                  "description": "True if competitor had no jobs in this trend before the 4-week window"
                }
              },
              "required": [
                "slug",
                "title",
                "jobCount",
                "newsCount",
                "score",
                "isNewcomer"
              ]
            },
            "description": "Top 3 trends by combined relevance"
          }
        },
        "required": [
          "ok",
          "trends"
        ]
      },
      "admin-email-templates_output": {
        "type": "object",
        "properties": {
          "templates": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "html": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "label",
                "description",
                "html"
              ]
            }
          }
        },
        "required": [
          "templates"
        ]
      },
      "admin-email-templates_input": {
        "type": "object",
        "properties": {
          "templateId": {
            "type": "string",
            "description": "Template ID to send (activity-digest, invite, trend-newsletter)"
          }
        },
        "required": [
          "templateId"
        ]
      },
      "admin-llm-costs_output": {
        "type": "object",
        "properties": {
          "byDayModel": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string"
                },
                "model": {
                  "type": "string"
                },
                "calls": {
                  "type": "number"
                },
                "cost": {
                  "type": "number"
                },
                "inputTokens": {
                  "type": "number"
                },
                "outputTokens": {
                  "type": "number"
                }
              },
              "required": [
                "date",
                "model",
                "calls",
                "cost",
                "inputTokens",
                "outputTokens"
              ]
            }
          },
          "byDayPrompt": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string"
                },
                "prompt": {
                  "type": "string"
                },
                "model": {
                  "type": "string"
                },
                "calls": {
                  "type": "number"
                },
                "cost": {
                  "type": "number"
                },
                "inputTokens": {
                  "type": "number"
                },
                "outputTokens": {
                  "type": "number"
                }
              },
              "required": [
                "date",
                "prompt",
                "model",
                "calls",
                "cost",
                "inputTokens",
                "outputTokens"
              ]
            }
          },
          "byPrompt": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "prompt": {
                  "type": "string"
                },
                "pipeline": {
                  "type": "string"
                },
                "model": {
                  "type": "string"
                },
                "calls": {
                  "type": "number"
                },
                "cost": {
                  "type": "number"
                },
                "inputTokens": {
                  "type": "number"
                },
                "outputTokens": {
                  "type": "number"
                }
              },
              "required": [
                "prompt",
                "pipeline",
                "model",
                "calls",
                "cost",
                "inputTokens",
                "outputTokens"
              ]
            }
          },
          "costPerCompetitor": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string"
                },
                "pipeline": {
                  "type": "string"
                },
                "totalCost": {
                  "type": "number"
                },
                "competitors": {
                  "type": "number"
                },
                "avgCostPerCompetitor": {
                  "type": "number"
                }
              },
              "required": [
                "date",
                "pipeline",
                "totalCost",
                "competitors",
                "avgCostPerCompetitor"
              ]
            }
          },
          "totalCost": {
            "type": "number"
          },
          "totalCalls": {
            "type": "number"
          },
          "days": {
            "type": "number"
          }
        },
        "required": [
          "byDayModel",
          "byDayPrompt",
          "byPrompt",
          "costPerCompetitor",
          "totalCost",
          "totalCalls",
          "days"
        ]
      },
      "trigger-queue_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ]
      },
      "trigger-queue_input": {
        "type": "object",
        "properties": {
          "queueKey": {
            "type": "string",
            "enum": [
              "career_page",
              "process_jobs",
              "news_check",
              "startpage",
              "startpage_links",
              "news_source_ingest",
              "daily_aggregation",
              "weekly_aggregation",
              "website_mapping",
              "linkedin_posts",
              "system_alerts",
              "cleanup_duplicate_pages",
              "kpi_consolidation",
              "kpi_research"
            ]
          }
        },
        "required": [
          "queueKey"
        ]
      },
      "remap-competitor_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ]
      },
      "remap-competitor_input": {
        "type": "object",
        "properties": {
          "competitor_id": {
            "type": "string",
            "format": "uuid"
          },
          "competitorId": {
            "type": "string",
            "format": "uuid"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          }
        },
        "description": "At least one of competitor_id, competitorId, or id must be provided"
      },
      "dev-delete-user_output": {
        "type": "object",
        "properties": {
          "deleted": {
            "type": "boolean"
          },
          "email": {
            "type": "string"
          },
          "cleanup": {
            "type": "object",
            "properties": {
              "subscriptions": {
                "type": "number"
              },
              "invitations": {
                "type": "number"
              },
              "systemAlertsNullified": {
                "type": "number"
              }
            },
            "required": [
              "subscriptions",
              "invitations",
              "systemAlertsNullified"
            ]
          }
        },
        "required": [
          "deleted"
        ]
      },
      "dev-delete-user_input": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "user_id"
        ]
      },
      "update-org-feed-variant_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "organizationId": {
            "type": "integer"
          },
          "variant": {
            "type": "string",
            "enum": [
              "default",
              "chronological",
              "balanced_demo"
            ]
          }
        },
        "required": [
          "ok",
          "organizationId",
          "variant"
        ]
      },
      "update-org-feed-variant_input": {
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "integer",
            "minimum": 0,
            "exclusiveMinimum": true
          },
          "variant": {
            "type": "string",
            "enum": [
              "default",
              "chronological",
              "balanced_demo"
            ]
          }
        },
        "required": [
          "organizationId",
          "variant"
        ]
      },
      "update-org-self-competitor_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "organizationId": {
            "type": "integer"
          },
          "selfCompetitorId": {
            "type": "string",
            "nullable": true,
            "format": "uuid"
          }
        },
        "required": [
          "ok",
          "organizationId",
          "selfCompetitorId"
        ]
      },
      "update-org-self-competitor_input": {
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "integer",
            "minimum": 0,
            "exclusiveMinimum": true
          },
          "selfCompetitorId": {
            "type": "string",
            "nullable": true,
            "format": "uuid"
          }
        },
        "required": [
          "organizationId",
          "selfCompetitorId"
        ]
      },
      "delete-competitor_output": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "deleted": {
            "type": "object",
            "properties": {
              "page_changes": {
                "type": "number"
              },
              "page_snapshots": {
                "type": "number"
              },
              "pages": {
                "type": "number"
              },
              "jobs": {
                "type": "number"
              },
              "competitor_aggregation_daily": {
                "type": "number"
              },
              "competitor_aggregation_weekly": {
                "type": "number"
              },
              "organizations_competitors": {
                "type": "number"
              },
              "subscriptions": {
                "type": "number"
              },
              "scrape_runs": {
                "type": "number"
              },
              "competitor": {
                "type": "number"
              }
            },
            "required": [
              "page_changes",
              "page_snapshots",
              "pages",
              "jobs",
              "competitor_aggregation_daily",
              "competitor_aggregation_weekly",
              "organizations_competitors",
              "subscriptions",
              "scrape_runs",
              "competitor"
            ]
          }
        },
        "required": [
          "ok",
          "deleted"
        ]
      },
      "delete-competitor_input": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "id"
        ]
      },
      "send-activity-digest_output": {
        "type": "object",
        "properties": {
          "sent": {
            "type": "number"
          },
          "skipped": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "reason"
              ]
            }
          },
          "failures": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "reason"
              ]
            }
          },
          "previews": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": {
                "nullable": true
              }
            }
          },
          "lookbackHours": {
            "type": "number"
          }
        },
        "required": [
          "lookbackHours"
        ]
      },
      "send-activity-digest_input": {
        "type": "object",
        "properties": {
          "targetUserId": {
            "type": "string",
            "format": "uuid"
          },
          "targetOrgId": {
            "type": "string"
          },
          "force": {
            "type": "boolean",
            "description": "Ignore preferred hour / cooldown"
          },
          "preview": {
            "type": "boolean",
            "description": "Return HTML previews instead of sending"
          }
        }
      }
    },
    "parameters": {}
  },
  "paths": {
    "/v1/send-magic-link": {
      "post": {
        "summary": "Send magic link login email",
        "description": "Generates a Supabase magic link for the given email and sends a styled login email via Mailgun. Always returns 200 to prevent email enumeration.",
        "tags": [
          "public"
        ],
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/send-magic-link_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/send-magic-link_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/html-clean": {
      "post": {
        "summary": "Clean raw HTML",
        "description": "Sanitizes and cleans raw HTML content. Accepts JSON body with html field or raw HTML as text/html.",
        "tags": [
          "public"
        ],
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/html-clean_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/html-clean_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/html-soft-404": {
      "post": {
        "summary": "Detect soft 404 pages",
        "description": "Analyzes HTML for soft 404 signals (error keywords, missing content). Returns a score and list of detected signals.",
        "tags": [
          "public"
        ],
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/html-soft-404_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/html-soft-404_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/urls-normalize": {
      "post": {
        "summary": "Normalize URLs",
        "description": "Strips protocol, marketing parameters, skip-link fragments, and trailing slashes. Accepts single url or batch urls array.",
        "tags": [
          "public"
        ],
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/urls-normalize_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/urls-normalize_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions-create": {
      "post": {
        "summary": "Create subscription",
        "description": "Placeholder endpoint for subscription creation.",
        "tags": [
          "public"
        ],
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/subscriptions-create_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/subscriptions-create_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve-external-domain": {
      "get": {
        "summary": "Approve or reject external career domain",
        "description": "Called via email button links. Verifies HMAC token, then approves/rejects the external domain for career page tracking. On approval, reclassifies unclassified pages on the domain.",
        "tags": [
          "public"
        ],
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "External domain record ID"
            },
            "required": true,
            "description": "External domain record ID",
            "name": "id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "approve",
                "reject"
              ]
            },
            "required": true,
            "name": "action",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "HMAC verification token"
            },
            "required": true,
            "description": "HMAC verification token",
            "name": "token",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/approve-external-domain_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve-website-redirect": {
      "get": {
        "summary": "Approve or dismiss cross-domain website redirect",
        "description": "Called via email button links when a startpage redirect to a different domain is detected. Verifies HMAC token, then updates the competitor website and startpage URL on approval.",
        "tags": [
          "public"
        ],
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Competitor UUID"
            },
            "required": true,
            "description": "Competitor UUID",
            "name": "competitorId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "New website domain to redirect to"
            },
            "required": true,
            "description": "New website domain to redirect to",
            "name": "newWebsite",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "approve",
                "dismiss"
              ]
            },
            "required": true,
            "name": "action",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "HMAC verification token"
            },
            "required": true,
            "description": "HMAC verification token",
            "name": "token",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/approve-website-redirect_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/public-competitors": {
      "get": {
        "summary": "List public competitors or get single competitor details",
        "description": "Without slug parameter: returns all active competitors (id, slug, name) for index pages and sitemaps. With slug parameter: returns full competitor data including KPIs, active jobs, job lifecycle, and positioning claims for the public dashboard page.",
        "tags": [
          "public"
        ],
        "security": [],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Competitor slug"
            },
            "required": true,
            "description": "Competitor slug",
            "name": "slug",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/public-competitors_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/mailgun-webhook": {
      "post": {
        "summary": "Mailgun event webhook (clicks, bounces, unsubscribes)",
        "description": "Receives Mailgun webhook events. HMAC-verified via MAILGUN_WEBHOOK_SIGNING_KEY. Persists every event to mailgun_events for audit/suppression. Forwards clicks to OpenPanel as email_clicked events (mapped to user via v:user-id). Opens are intentionally not subscribed (Mail Privacy Protection makes the signal unreliable).",
        "tags": [
          "internal"
        ],
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/mailgun-webhook_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/mailgun-webhook_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/get-competitors": {
      "get": {
        "summary": "List competitors",
        "description": "Query competitors with optional filters for organization, active status, and name search. Supports pagination via limit/offset. Uses Supabase service role (bypasses RLS).",
        "tags": [
          "api"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Comma-separated field list (default: id,name,website,description,active,linkedin_url,created_at)"
            },
            "required": false,
            "description": "Comma-separated field list (default: id,name,website,description,active,linkedin_url,created_at)",
            "name": "select",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": false,
            "name": "organization_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter by active status (true/false)"
            },
            "required": false,
            "description": "Filter by active status (true/false)",
            "name": "active",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Case-insensitive name search (ilike)"
            },
            "required": false,
            "description": "Case-insensitive name search (ilike)",
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": false,
            "name": "id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Sort order (default: name.asc)"
            },
            "required": false,
            "description": "Sort order (default: name.asc)",
            "name": "order",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Max results (default 100, max 1000)"
            },
            "required": false,
            "description": "Max results (default 100, max 1000)",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Pagination offset"
            },
            "required": false,
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/get-competitors_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/get-competitor-people": {
      "get": {
        "summary": "List people for a competitor",
        "description": "Query people (contacts) associated with a competitor. Requires competitor_id. Supports filtering by role and source, and pagination via limit/offset.",
        "tags": [
          "api"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Filter by competitor ID (required)"
            },
            "required": true,
            "description": "Filter by competitor ID (required)",
            "name": "competitor_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Filter by role ID"
            },
            "required": false,
            "description": "Filter by role ID",
            "name": "role_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter by role set name (e.g. sales-target). Resolves to the set's role_ids."
            },
            "required": false,
            "description": "Filter by role set name (e.g. sales-target). Resolves to the set's role_ids.",
            "name": "role_set",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter by source (manual/search)"
            },
            "required": false,
            "description": "Filter by source (manual/search)",
            "name": "source",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Sort order (default: name.asc)"
            },
            "required": false,
            "description": "Sort order (default: name.asc)",
            "name": "order",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Max results (default 100, max 1000)"
            },
            "required": false,
            "description": "Max results (default 100, max 1000)",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Pagination offset"
            },
            "required": false,
            "description": "Pagination offset",
            "name": "offset",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/get-competitor-people_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/create-competitor": {
      "post": {
        "summary": "Create a new competitor",
        "description": "Creates a competitor with normalized website URL, auto-creates a homepage page, and triggers startpage link check + KPI research in the background.",
        "tags": [
          "api"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/create-competitor_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/create-competitor_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/scrape-url": {
      "post": {
        "summary": "Scrape a URL",
        "description": "Scrapes a URL using the tiered fallback system (fetch → scrapfly → firecrawl). Supports async mode via responseURL and optional link extraction. Returns content, metadata, and scraper info.",
        "tags": [
          "api"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/scrape-url_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/scrape-url_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/activity-feed": {
      "post": {
        "summary": "Get activity feed",
        "description": "Returns activity items (page changes, news, hiring, social). Supports two modes: 'classic' (flat list with time-window cascade) and 'daily' (day-sliced with budget algorithm for balanced content distribution). Daily mode uses cursor-based pagination. RLS enforced.",
        "tags": [
          "app"
        ],
        "security": [
          {
            "supabaseAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/activity-feed_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/activity-feed_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/ads-overview": {
      "post": {
        "summary": "Get ad intelligence overview",
        "description": "Returns the data for the Competitive Ad Intelligence area in a single call: matrix (competitor × topic counts), ads (filtered LinkedIn ads with locale-merged headline/cta/description), momentum (weekly per-topic aggregation), and meta (self_competitor_id, filters echo). Self-tracking via is_self flag, not filtered. RLS enforced.",
        "tags": [
          "app"
        ],
        "security": [
          {
            "supabaseAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ads-overview_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ads-overview_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/activity-digest-preview": {
      "post": {
        "summary": "Preview activity digest email",
        "description": "Renders an activity digest email as HTML for preview. Can accept pre-built display items or fetch activities from the database. RLS enforced.",
        "tags": [
          "app"
        ],
        "security": [
          {
            "supabaseAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/activity-digest-preview_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/activity-digest-preview_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/invite-member": {
      "post": {
        "summary": "Invite member to organization",
        "description": "Invites a user to an organization. If the user doesn't exist, creates a Supabase invite and sends a styled email. If the user exists, adds them directly. Caller must be org member (dev accounts bypass).",
        "tags": [
          "app"
        ],
        "security": [
          {
            "supabaseAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/invite-member_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/invite-member_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/revoke-invitation": {
      "post": {
        "summary": "Revoke organization invitation",
        "description": "Deletes a pending invitation. Also deletes the auth.users record created as a side effect of generateLink({type:'invite'}) if the invitee never completed signup — otherwise subsequent re-invites for the same email silently skip the email send. Caller must be org member (dev accounts bypass).",
        "tags": [
          "app"
        ],
        "security": [
          {
            "supabaseAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/revoke-invitation_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/revoke-invitation_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/strategy-radar": {
      "get": {
        "summary": "Get strategy radar for a competitor",
        "description": "Returns the top 3 trend topics a competitor is investing in, based on new jobs and news articles from the last 4 weeks. Includes newcomer detection (first-time activity in a trend).",
        "tags": [
          "app"
        ],
        "security": [
          {
            "supabaseAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Competitor ID"
            },
            "required": true,
            "description": "Competitor ID",
            "name": "competitor_id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/strategy-radar_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin-email-templates": {
      "get": {
        "summary": "List email templates",
        "description": "Renders all email templates (activity-digest, invite, trend-newsletter) with mock data and returns HTML previews.",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "supabaseAdminAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/admin-email-templates_output"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden (not a dev account)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Send test email from template",
        "description": "Renders a specific email template and sends it to the authenticated user's email address.",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "supabaseAdminAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/admin-email-templates_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/admin-email-templates_output"
                    },
                    {
                      "properties": {
                        "sent": {
                          "type": "boolean"
                        },
                        "to": {
                          "type": "string",
                          "format": "email"
                        }
                      },
                      "required": [
                        "sent",
                        "to"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden (not a dev account)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin-llm-costs": {
      "get": {
        "summary": "Get LLM cost breakdown",
        "description": "Returns detailed LLM cost data from Langfuse: per-day-per-model, per-day-per-prompt, aggregate per-prompt, and average cost-per-competitor-per-pipeline.",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "supabaseAdminAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/admin-llm-costs_output"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden (not a dev account)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/trigger-queue": {
      "post": {
        "summary": "Trigger background queue",
        "description": "Manually triggers a background processing queue. Routes to BullMQ worker (if configured) or falls back to Netlify background functions.",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "supabaseAdminAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/trigger-queue_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/trigger-queue_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden (not a dev account)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/remap-competitor": {
      "post": {
        "summary": "Trigger website remapping",
        "description": "Enqueues a map-websites worker job for a specific competitor to re-discover and classify pages.",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "supabaseAdminAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/remap-competitor_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/remap-competitor_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden (not a dev account)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/dev-delete-user": {
      "post": {
        "summary": "Completely delete a user (dev only)",
        "description": "Hard-deletes a user from auth.users, cascading to profiles, organizations_users, and user_password_state. Also removes subscriptions and pending invitations by email, and nullifies system_alerts.acknowledged_by. Refuses if the user is creator of any organization.",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "supabaseAdminAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/dev-delete-user_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/dev-delete-user_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden (not a dev account)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/update-org-feed-variant": {
      "post": {
        "summary": "Set the Activity Feed variant for an organisation",
        "description": "Dev-only. Switches the algorithm used by the daily Activity Feed between 'default', 'chronological', and 'balanced_demo'.",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "supabaseAdminAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/update-org-feed-variant_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/update-org-feed-variant_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden (not a dev account)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/update-org-self-competitor": {
      "post": {
        "summary": "Set the self-competitor reference for an organisation",
        "description": "Points an organisation at its own competitor row so 'Your Company' columns in ad-intelligence views can be highlighted. Callable by org admins (or dev accounts). Pass null to unset.",
        "tags": [
          "organization"
        ],
        "security": [
          {
            "supabaseAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/update-org-self-competitor_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/update-org-self-competitor_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/delete-competitor": {
      "post": {
        "summary": "Delete a competitor and all related data",
        "description": "Cascading delete: removes page_changes, page_snapshots, scrape_run_pages, pages, jobs, aggregations, org links, subscriptions, news sources, and the competitor itself. Requires devaccount.",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "supabaseAdminAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/delete-competitor_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/delete-competitor_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden (not a dev account)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/send-activity-digest": {
      "post": {
        "summary": "Send activity digest emails",
        "description": "Sends activity digest emails to opted-in users. Supports targeting specific users/orgs, force-send (ignore schedule), and preview-only mode.",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "supabaseAdminAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/send-activity-digest_input"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/send-activity-digest_output"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden (not a dev account)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  }
}
