openapi: "3.0.3"
info:
  title: Seamline Public API
  version: "1.0"
  description: |
    Public discovery endpoints for Seamline creator hubs.

    Seamline is creator portfolio infrastructure — a public hub at
    seamline.now/@handle listing a creator's projects, bio, and email capture.
    Every hub embeds JSON-LD structured data (ProfilePage + Person + CreativeWork[]).

    **Privacy:** Subscriber emails and lead data are never available through any
    public endpoint. Only creator-authored project descriptions and public hub
    content are accessible.
  contact:
    email: support@seamline.now
  license:
    name: All rights reserved — VaultSpark Studios LLC

servers:
  - url: https://seamline.now
    description: Production

paths:
  /@{handle}:
    get:
      operationId: getCreatorHub
      summary: Get a creator's public hub
      description: |
        Returns the creator's public portfolio hub — projects, bio, and email
        capture widget. The response HTML includes machine-readable JSON-LD:
        ProfilePage (root), Person (mainEntity), and CreativeWork[] (hasPart).
        Parse the JSON-LD script tag for structured data; do not scrape layout.
      parameters:
        - name: handle
          in: path
          required: true
          description: Creator handle (without the @ prefix)
          schema:
            type: string
            example: janedoe
      responses:
        "200":
          description: Creator hub HTML with embedded JSON-LD structured data
          content:
            text/html:
              schema:
                type: string
        "404":
          description: No creator with this handle

  /@{handle}/persona.json:
    get:
      operationId: getCreatorPersona
      summary: Structured JSON creator profile purpose-built for agent consumption
      description: |
        Machine-readable creator profile — display name, bio, creator type, a
        voice-profile summary, live/coming-soon/building projects, and active
        magnet count. No subscriber PII. Cached 1 hour at the edge. Prefer this
        over scraping the hub HTML when only structured fields are needed.
      parameters:
        - name: handle
          in: path
          required: true
          description: Creator handle (without the @ prefix)
          schema:
            type: string
            example: janedoe
      responses:
        "200":
          description: Creator persona JSON
          content:
            application/json:
              schema:
                type: object
                properties:
                  name: { type: string }
                  handle: { type: string }
                  creatorType: { type: string, nullable: true }
                  bio: { type: string, nullable: true }
                  hubUrl: { type: string, format: uri }
                  projects:
                    type: array
                    items:
                      type: object
                      properties:
                        name: { type: string }
                        status: { type: string, enum: [live, coming_soon, building] }
                        url: { type: string, nullable: true }
                  activeMagnets: { type: integer }
                  voiceSummary: { type: string, nullable: true }
                  capabilities:
                    type: array
                    items: { type: string }
                  badgeUrl: { type: string, format: uri }
                  schema: { type: string }
        "404":
          description: No creator with this handle

  /sitemap.xml:
    get:
      operationId: listCreators
      summary: List all public creator hubs
      description: |
        XML sitemap enumerating all public creator hub URLs.
        Use to discover creator handles or build a creator index.
      responses:
        "200":
          description: XML sitemap
          content:
            application/xml:
              schema:
                type: string

  /agents.json:
    get:
      operationId: getAgentManifest
      summary: Machine-readable product capability contract
      description: |
        Returns the Seamline agent manifest (schemaVersion 2.0) — full capability
        matrix, pricing tiers, intent types, and agent guidance. Read this before
        building any agent integration against Seamline.
      responses:
        "200":
          description: Agent manifest JSON
          content:
            application/json:
              schema:
                type: object

  /.well-known/llms.txt:
    get:
      operationId: getLlmsGuide
      summary: LLM discovery guide
      description: |
        Plain-text guide for large language models — capability matrix, privacy
        posture, recommended actions, and do-not-do list. Read before answering
        questions about Seamline on behalf of a user.
      responses:
        "200":
          description: LLM-readable plain text guide
          content:
            text/plain:
              schema:
                type: string

  /api/public/subscriber-badge/{handle}:
    get:
      operationId: getSubscriberBadge
      summary: Public subscriber count badge for a creator
      description: |
        Returns public subscriber count data for a creator hub.
        Only aggregate counts are exposed — no individual subscriber data.
      parameters:
        - name: handle
          in: path
          required: true
          description: Creator handle (without the @ prefix)
          schema:
            type: string
            example: janedoe
      responses:
        "200":
          description: Subscriber badge data
          content:
            application/json:
              schema:
                type: object
                properties:
                  handle:
                    type: string
                  count:
                    type: integer
                    description: Total public subscriber count
                  label:
                    type: string
                    description: Human-readable label e.g. "412 subscribers"
        "404":
          description: No creator with this handle

  /api/health:
    get:
      operationId: healthCheck
      summary: API health check
      description: Returns OK when the Seamline API is reachable.
      responses:
        "200":
          description: API is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok

  # ── Authenticated creator endpoints ──────────────────────────────────────
  # Require: Authorization: Bearer <supabase-access-token>
  # Obtain a token: supabase.auth.getSession() → session.access_token

  /api/me:
    get:
      operationId: getMe
      summary: Get the authenticated creator's profile
      description: |
        Returns the handle and display name of the authenticated creator.
        Use this to confirm a token is valid and resolve the @handle
        before making other authenticated requests.
      security:
        - supabaseAuth: []
      responses:
        "200":
          description: Creator profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  handle:
                    type: string
                    nullable: true
                    description: Creator handle (without @ prefix)
                    example: janedoe
                  displayName:
                    type: string
                    nullable: true
                    description: Creator display name
                    example: Jane Doe
        "401":
          description: Missing or invalid token

  /api/projects/effectiveness:
    get:
      operationId: listProjectEffectiveness
      summary: Per-project top-performing magnet stats
      description: |
        Returns the top-performing magnet (by subscriber capture count) for each
        of the authenticated creator's projects. All aggregate — no subscriber PII.
        Use to identify which projects are driving the most audience growth.
      security:
        - supabaseAuth: []
      responses:
        "200":
          description: Per-project effectiveness array
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    project_id:
                      type: string
                      format: uuid
                    top_magnet_headline:
                      type: string
                      nullable: true
                    top_magnet_id:
                      type: string
                      format: uuid
                      nullable: true
                    capture_count:
                      type: integer
        "401":
          description: Missing or invalid token

  /api/sequences:
    get:
      operationId: listSequenceSteps
      summary: Get sequence steps for a magnet
      description: |
        Returns the stored welcome-sequence steps (subject lines and step metadata)
        for a published magnet owned by the authenticated creator. Steps are ordered
        by step_number (1–5). Use step_number + magnet_id to identify slots for
        upsert-step calls.
      security:
        - supabaseAuth: []
      parameters:
        - name: magnet_id
          in: query
          required: true
          description: UUID of the magnet whose sequence steps to retrieve
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Sequence steps
          content:
            application/json:
              schema:
                type: object
                properties:
                  sequences:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        step_number:
                          type: integer
                          minimum: 1
                          maximum: 5
                        subject:
                          type: string
        "400":
          description: Missing magnet_id parameter
        "401":
          description: Missing or invalid token

  /api/sequences/upsert-step:
    post:
      operationId: upsertSequenceStep
      summary: Insert or update a sequence step
      description: |
        Inserts or updates a welcome-sequence step (subject + body) at the given
        step_number for a published magnet owned by the authenticated creator.
        Uses upsert on (magnet_id, step_number) — safe to call multiple times.

        step_number must be between 1 and 5 (enforced by DB constraint).
        Body supports {{first_name}} and {{creator_name}} merge tags.

        Privacy: never include subscriber emails or lead PII in the body.
      security:
        - supabaseAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - magnet_id
                - step_number
                - subject
                - body
              properties:
                magnet_id:
                  type: string
                  format: uuid
                  description: UUID of the published magnet
                step_number:
                  type: integer
                  minimum: 1
                  maximum: 5
                  description: Sequence position (1 = first email, 5 = last)
                subject:
                  type: string
                  maxLength: 200
                  description: Email subject line
                body:
                  type: string
                  maxLength: 8000
                  description: Email body. Supports {{first_name}} and {{creator_name}} merge tags.
                delay_hours:
                  type: integer
                  minimum: 0
                  maximum: 720
                  default: 72
                  description: Hours after previous step before this email sends
            example:
              magnet_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
              step_number: 5
              subject: "Someone I want you to meet: @partner"
              body: "Hey {{first_name}},\n\nI've been collaborating with someone whose work I think you'll love..."
              delay_hours: 96
      responses:
        "200":
          description: Step upserted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    example: true
                  sequence:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      step_number:
                        type: integer
        "400":
          description: Validation error or magnet not found / not owned by creator
        "401":
          description: Missing or invalid token
        "429":
          description: Rate limit exceeded

components:
  securitySchemes:
    supabaseAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Supabase access token (JWT). Obtain via:
          const { data: { session } } = await supabase.auth.getSession()
          Authorization: Bearer <session.access_token>
        Tokens expire in 1 hour; refresh via supabase.auth.refreshSession().
