openapi: 3.1.0
info:
  title: blogAIzer API
  version: 1.0.0
  description: |
    AI content pipeline as a service. Generate research-backed, EEAT-compliant blog posts
    with featured images via API. From topic to published article in ~45 seconds.
  contact:
    email: api@blogaizer.com
    url: https://blogaizer.com/docs
  termsOfService: https://blogaizer.com/terms
  license:
    name: Proprietary

servers:
  - url: https://blogaizer.com/api/v1
    description: Production
  - url: https://blogaizer.com/api/v1
    description: Test (use sk_test_ key)

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: |
        API key from blogaizer.com/dashboard.
        Live keys start with `sk_live_`, test keys with `sk_test_`.

  schemas:
    Error:
      type: object
      required: [error, message]
      properties:
        error:
          type: string
          description: Machine-readable error code
        message:
          type: string
          description: Human-readable description
        details:
          type: object
          description: Optional context

    AccountRegistration:
      type: object
      required: [email, company_name]
      properties:
        email:
          type: string
          format: email
        company_name:
          type: string

    AccountRegistrationResponse:
      type: object
      properties:
        account_id:
          type: string
        api_key_live:
          type: string
        api_key_test:
          type: string
        plan:
          type: string
        credits_remaining:
          type: integer
        message:
          type: string

    UsageResponse:
      type: object
      properties:
        plan:
          type: string
        credits_total:
          type: integer
        credits_used:
          type: integer
        credits_remaining:
          type: integer
        period_ends:
          type: string
          format: date
        usage_by_type:
          type: object
          properties:
            articles:
              type: integer
            ideas:
              type: integer
            analytics:
              type: integer
            research:
              type: integer

    SubscribeRequest:
      type: object
      required: [plan]
      properties:
        plan:
          type: string
          enum: [api_starter, api_growth, api_scale]

    SubscribeResponse:
      type: object
      properties:
        checkout_url:
          type: string
          format: uri

    BrandConfig:
      type: object
      required: [name, industry, brand_voice]
      properties:
        name:
          type: string
        industry:
          type: string
        website:
          type: string
          format: uri
        brand_voice:
          type: string
        image_style:
          type: object
          properties:
            colors:
              type: array
              items:
                type: string
            adjectives:
              type: array
              items:
                type: string
            mood:
              type: string
            avoid:
              type: array
              items:
                type: string

    BrandConfigResponse:
      type: object
      properties:
        brand_id:
          type: string
        status:
          type: string

    ProductConfig:
      type: object
      required: [brand_id, products]
      properties:
        brand_id:
          type: string
        products:
          type: array
          items:
            type: object
            required: [name, tagline, description]
            properties:
              name:
                type: string
              tagline:
                type: string
              description:
                type: string
              keywords:
                type: array
                items:
                  type: string
              cta_text:
                type: string
              cta_url:
                type: string
                format: uri
              competitors:
                type: array
                items:
                  type: object
                  properties:
                    name:
                      type: string
                    url:
                      type: string
                      format: uri
              themes:
                type: array
                items:
                  type: string

    ProductConfigResponse:
      type: object
      properties:
        products_configured:
          type: integer
        product_ids:
          type: array
          items:
            type: string

    PersonaConfig:
      type: object
      required: [brand_id, personas]
      properties:
        brand_id:
          type: string
        personas:
          type: array
          items:
            type: object
            required: [name, role, tone, language]
            properties:
              name:
                type: string
              role:
                type: string
              tone:
                type: string
              language:
                type: string
              product_contexts:
                type: array
                items:
                  type: object
                  properties:
                    product_id:
                      type: string
                    pain_points:
                      type: array
                      items:
                        type: string
                    goals:
                      type: array
                      items:
                        type: string

    PersonaConfigResponse:
      type: object
      properties:
        personas_configured:
          type: integer

    PublishTargetConfig:
      type: object
      required: [brand_id, targets]
      properties:
        brand_id:
          type: string
        targets:
          type: array
          items:
            type: object
            required: [type, name, url]
            properties:
              type:
                type: string
                enum: [wordpress, webhook, nextjs_api]
              name:
                type: string
              url:
                type: string
                format: uri
              username:
                type: string
              app_password:
                type: string
              seo_plugin:
                type: string
                enum: [rankmath, yoast]
              auth_token:
                type: string

    PublishTargetResponse:
      type: object
      properties:
        targets_configured:
          type: integer

    ArticleRequest:
      type: object
      required: [topic]
      properties:
        brand_id:
          type: string
        product_id:
          type: string
          description: Optional, auto-detected if not set
        persona_id:
          type: string
        topic:
          type: string
        keywords:
          type: array
          items:
            type: string
          description: Optional, AI suggests if empty
        word_count:
          type: integer
          default: 1500
        outputs:
          type: array
          items:
            type: string
            enum: [html, markdown, social, featured_image]
        publish_to:
          type: string
          description: Target ID for auto-publish
        webhook_url:
          type: string
          format: uri
          description: Override default webhook

    ArticleJobResponse:
      type: object
      properties:
        job_id:
          type: string
        status:
          type: string
          enum: [queued]
        estimated_seconds:
          type: integer
        credits_charged:
          type: integer
        poll_url:
          type: string
        message:
          type: string

    ArticleStatusProcessing:
      type: object
      properties:
        job_id:
          type: string
        status:
          type: string
          enum: [processing]
        step:
          type: string
          enum: [scan, research, writing, audit, formatting, image, social]
        progress:
          type: integer
          minimum: 0
          maximum: 100
        estimated_seconds_remaining:
          type: integer

    ArticleStatusComplete:
      type: object
      properties:
        job_id:
          type: string
        status:
          type: string
          enum: [complete]
        article:
          type: object
          properties:
            title:
              type: string
            html:
              type: string
            markdown:
              type: string
            meta_title:
              type: string
            meta_description:
              type: string
            focus_keyword:
              type: string
            word_count:
              type: integer
            eeat_score:
              type: integer
              minimum: 0
              maximum: 50
            research_sources:
              type: array
              items:
                type: object
                properties:
                  title:
                    type: string
                  url:
                    type: string
                    format: uri
                  used_for:
                    type: string
            internal_link_suggestions:
              type: array
              items:
                type: object
                properties:
                  anchor_text:
                    type: string
                  target_url:
                    type: string
                    format: uri
                  relevance:
                    type: number
        featured_image:
          type: object
          properties:
            url:
              type: string
              format: uri
            alt_text:
              type: string
            title:
              type: string
            width:
              type: integer
            height:
              type: integer
        social_posts:
          type: object
          properties:
            linkedin:
              type: array
              items:
                type: object
                properties:
                  variant:
                    type: string
                  text:
                    type: string
                  first_comment:
                    type: string
            x:
              type: array
              items:
                type: object
                properties:
                  variant:
                    type: string
                  text:
                    type: string
        published_to:
          type: object
          properties:
            target:
              type: string
            url:
              type: string
              format: uri
            post_id:
              type: integer
        credits_used:
          type: integer

    IdeasRequest:
      type: object
      required: [brand_id]
      properties:
        brand_id:
          type: string
        product_id:
          type: string
        count:
          type: integer
          maximum: 10
          default: 10
        consider:
          type: object
          properties:
            trends:
              type: boolean
              default: true
            competitors:
              type: boolean
              default: true
            linkedin:
              type: boolean
              default: true
            existing_posts:
              type: boolean
              default: true

    IdeasResponse:
      type: object
      properties:
        ideas:
          type: array
          items:
            type: object
            properties:
              title:
                type: string
              brief:
                type: string
              type:
                type: string
                enum: [trend, competitor_gap, content_gap, seasonal, follow_up]
              source:
                type: string
              suggested_keywords:
                type: array
                items:
                  type: string
              estimated_search_volume:
                type: string
                enum: [low, medium, high]
              theme:
                type: string
              relevance_score:
                type: integer
                minimum: 1
                maximum: 10
        credits_used:
          type: integer
        intelligence_summary:
          type: string

    AnalyticsRequest:
      type: object
      required: [brand_id]
      properties:
        brand_id:
          type: string
        date_range:
          type: string
          enum: ["7d", "30d", "90d"]
          default: "30d"

    AnalyticsResponse:
      type: object
      properties:
        top_performing:
          type: array
          items:
            type: object
            properties:
              title:
                type: string
              url:
                type: string
                format: uri
              pageviews:
                type: integer
              avg_read_time:
                type: string
              trend:
                type: string
        declining:
          type: array
          items:
            type: object
            properties:
              title:
                type: string
              url:
                type: string
                format: uri
              pageviews:
                type: integer
              drop_percent:
                type: integer
              suggested_action:
                type: string
        best_publish_time:
          type: object
          properties:
            day:
              type: string
            hour:
              type: integer
            confidence:
              type: number
        best_performing_theme:
          type: string
        worst_performing_theme:
          type: string
        keyword_wins:
          type: array
          items:
            type: object
            properties:
              keyword:
                type: string
              position:
                type: integer
              trend:
                type: string
        keyword_losses:
          type: array
          items:
            type: object
            properties:
              keyword:
                type: string
              position:
                type: integer
              previous:
                type: integer
              action:
                type: string
        recommendations:
          type: array
          items:
            type: string
        credits_used:
          type: integer

    FollowUpsRequest:
      type: object
      required: [brand_id]
      properties:
        brand_id:
          type: string
        post_url:
          type: string
          format: uri
        auto_select:
          type: boolean

    FollowUpsResponse:
      type: object
      properties:
        original_post:
          type: object
          properties:
            title:
              type: string
            pageviews:
              type: integer
            avg_read_time:
              type: string
        follow_ups:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
                enum: [sequel, simplify, advanced, adjacent, refresh]
              title:
                type: string
              reason:
                type: string
        credits_used:
          type: integer

  headers:
    X-RateLimit-Limit:
      schema:
        type: integer
      description: Request limit per minute for current plan
    X-RateLimit-Remaining:
      schema:
        type: integer
      description: Remaining requests in current window
    X-RateLimit-Reset:
      schema:
        type: integer
      description: Unix timestamp when the rate limit resets
    X-Credits-Remaining:
      schema:
        type: integer
      description: Credits remaining in current billing period

  responses:
    Unauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: invalid_api_key
            message: Bad or missing API key

    CreditsExhausted:
      description: No credits remaining
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Error"
              - type: object
                properties:
                  credits_remaining:
                    type: integer
                  upgrade_url:
                    type: string
          example:
            error: credits_exhausted
            message: No credits remaining
            credits_remaining: 0
            upgrade_url: https://blogaizer.com/dashboard

    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Error"
              - type: object
                properties:
                  retry_after_seconds:
                    type: integer
          example:
            error: rate_limit_exceeded
            message: Too many requests
            retry_after_seconds: 12

    NotConfigured:
      description: Brand or product not set up
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: not_configured
            message: Brand not configured yet

paths:
  /account/register:
    post:
      operationId: registerAccount
      summary: Create a new API account
      description: Register a new account and receive API keys with 5 free trial credits.
      tags: [Account]
      security: []
      x-credits-cost: 0
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccountRegistration"
            example:
              email: bot@company.com
              company_name: Acme Corp
      responses:
        "201":
          description: Account created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountRegistrationResponse"
              example:
                account_id: acc_abc123
                api_key_live: sk_live_...
                api_key_test: sk_test_...
                plan: free_trial
                credits_remaining: 5
                message: 5 free credits to test. Subscribe for more.

  /account/usage:
    get:
      operationId: getUsage
      summary: Check credits and usage
      description: Returns current plan, credit balance, and usage breakdown by type.
      tags: [Account]
      x-credits-cost: 0
      responses:
        "200":
          description: Usage data
          headers:
            X-Credits-Remaining:
              $ref: "#/components/headers/X-Credits-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /account/subscribe:
    post:
      operationId: subscribe
      summary: Subscribe to a plan
      description: Creates a Stripe Checkout session for the selected plan.
      tags: [Account]
      x-credits-cost: 0
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubscribeRequest"
      responses:
        "200":
          description: Checkout URL
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscribeResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /config/brand:
    post:
      operationId: configureBrand
      summary: Configure brand identity
      description: Set up or update brand name, voice, industry, and image style. Called once, updated as needed.
      tags: [Configuration]
      x-credits-cost: 0
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BrandConfig"
      responses:
        "200":
          description: Brand configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BrandConfigResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /config/products:
    post:
      operationId: configureProducts
      summary: Configure products
      description: Add products with competitors, keywords, and themes for targeted content generation.
      tags: [Configuration]
      x-credits-cost: 0
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductConfig"
      responses:
        "200":
          description: Products configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductConfigResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /config/personas:
    post:
      operationId: configurePersonas
      summary: Configure target personas
      description: Define target audiences with product-specific pain points and goals.
      tags: [Configuration]
      x-credits-cost: 0
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PersonaConfig"
      responses:
        "200":
          description: Personas configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PersonaConfigResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /config/publish-targets:
    post:
      operationId: configurePublishTargets
      summary: Configure publish targets
      description: Set up WordPress, webhook, or Next.js API delivery for finished content.
      tags: [Configuration]
      x-credits-cost: 0
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PublishTargetConfig"
      responses:
        "200":
          description: Targets configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublishTargetResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /content/articles:
    post:
      operationId: generateArticle
      summary: Generate article (async)
      description: |
        Submit a topic for article generation. Returns immediately with a job ID.
        Poll the status endpoint or configure a webhook for delivery.
        Pipeline: scan → research → writing → audit → formatting → image → social.
        Takes ~45 seconds for a complete article.
      tags: [Content]
      x-credits-cost: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ArticleRequest"
      responses:
        "202":
          description: Job queued
          headers:
            X-Credits-Remaining:
              $ref: "#/components/headers/X-Credits-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ArticleJobResponse"
        "400":
          $ref: "#/components/responses/NotConfigured"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/CreditsExhausted"
        "429":
          $ref: "#/components/responses/RateLimited"

  /content/articles/{job_id}:
    get:
      operationId: getArticleStatus
      summary: Poll article status
      description: Check the status of an article generation job. Returns progress while processing, full result when complete.
      tags: [Content]
      x-credits-cost: 0
      parameters:
        - name: job_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Job status (processing or complete)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ArticleStatusProcessing"
                  - $ref: "#/components/schemas/ArticleStatusComplete"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: Job not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: job_not_found
                message: Invalid job_id

  /intelligence/ideas:
    post:
      operationId: generateIdeas
      summary: Generate curated content ideas
      description: |
        Generate 10 curated content ideas based on trends, competitors, analytics,
        and existing content. Uses the full blogAIzer intelligence engine.
      tags: [Intelligence]
      x-credits-cost: 2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IdeasRequest"
      responses:
        "200":
          description: Ideas generated
          headers:
            X-Credits-Remaining:
              $ref: "#/components/headers/X-Credits-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IdeasResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/CreditsExhausted"

  /intelligence/analytics:
    post:
      operationId: getAnalytics
      summary: AI-powered performance analysis
      description: |
        Get AI analysis of published content performance. Requires GA4 connected.
        Returns top/declining posts, best publish times, keyword tracking, and recommendations.
      tags: [Intelligence]
      x-credits-cost: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AnalyticsRequest"
      responses:
        "200":
          description: Analytics report
          headers:
            X-Credits-Remaining:
              $ref: "#/components/headers/X-Credits-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AnalyticsResponse"
        "400":
          description: GA4 not connected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: ga4_not_connected
                message: Analytics endpoint requires GA4 configured
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/CreditsExhausted"

  /intelligence/follow-ups:
    post:
      operationId: generateFollowUps
      summary: Generate follow-up ideas
      description: |
        Generate follow-up content ideas for top-performing posts.
        Analyzes read time, bounce rate, search queries, and exit pages to suggest sequels,
        simplified versions, advanced deep-dives, and content refreshes.
      tags: [Intelligence]
      x-credits-cost: 2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FollowUpsRequest"
      responses:
        "200":
          description: Follow-up ideas generated
          headers:
            X-Credits-Remaining:
              $ref: "#/components/headers/X-Credits-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FollowUpsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/CreditsExhausted"

tags:
  - name: Account
    description: Account registration, subscription, and usage tracking
  - name: Configuration
    description: Brand, product, persona, and publish target setup
  - name: Content
    description: Async article generation and status polling
  - name: Intelligence
    description: AI-powered ideas, analytics, and follow-up suggestions (premium)
