> ## Documentation Index
> Fetch the complete documentation index at: https://devs.surferseo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Content Editor

> Creates a new Content Editor. The operation is asynchronous — the response returns immediately
with the Content Editor in `scheduled` state.

`use_brand_knowledge` defaults to `true`. When enabled, workspace brand knowledge is used where available; set `false` for unbranded output.

Supports `Idempotency-Key` header.

**Keywords:** `main_keyword` (required) is the primary keyword that drives the SERP analysis.
Add up to 19 `secondary_keywords` (20 keywords total including the main keyword); exceeding the
limit returns `400` with a `secondary_keywords` / `too_many` detail.

**Locations:** `location` is a country or region display name (e.g. `"United States"`, the default
when omitted). Fetch the full list of supported values from `GET /api/v1/locations` (no auth
required). `device` selects the SERP device profile — `mobile` (default) or `desktop`.

**Templates:** `surfer_template` and `custom_template_id` are mutually exclusive. When neither
is provided, Surfer asynchronously preselects a template — the workspace's default custom
template if configured, otherwise a Surfer template inferred from the SERP. Read the final
values after `state` becomes `completed`. On a completed Content Editor, both fields `null`
represents the "SERP-based" template: the article structure follows the SERP analysis rather
than a preset.

**Custom voice:** when `custom_voice_id` is omitted, the workspace's default custom voice (if any)
is used. Pass `null` explicitly to opt out.

**Webhooks:** `content_editor.initialization.completed` / `content_editor.initialization.failed`
fire when initialization settles. During initialization the AI Search score is computed, firing
`content_editor.ai_search_score.calculated` / `.failed` when it lands (no event when the score ends
`unavailable`). The SEO score computed for imported content at creation sends no event;
`content_editor.seo_score.calculated` / `.failed` and `content_editor.content_score.recalculated`
fire after content updates made through the API. As a fallback, poll `GET .../content_editors/:id`.

**Cost:** each successful request consumes one Content Editor credit.

**Rate limit**: 10 requests/sec




## OpenAPI

````yaml /api/openapi-v2.json post /api/v2/workspaces/{workspace_id}/content_editors
openapi: 3.0.0
info:
  description: >+
    Audit, SERP Analyzer, AI Detector, and Humanizer are not yet available in v2
    — see the v1 API docs.


    Building an LLM or agent integration?
    [/llms.txt](https://app.surferseo.com/llms.txt) is a plain-text,
    machine-readable index of this API.


    ## Authentication


    Most API requests require an `API-KEY` header with a valid API key.
    Operations that do not require authentication state that explicitly.



    ## Workspaces


    Most v2 resource endpoints are scoped to a workspace via the URL path:
    `/api/v2/workspaces/:workspace_id/...`.


    The API key must belong to an organization that owns the workspace.
    Attempting to access another organization's workspace returns `404 Not
    Found` (not `403`) to avoid leaking resource existence.



    ## Pagination


    List endpoints whose reference page documents `page` and `page_size` return
    paginated responses; the other

    list endpoints return every item with `meta.total` plus any
    endpoint-specific metadata their reference page documents. Use query
    parameters to control pagination:


    | Parameter   | Default | Max | Description                |

    |-------------|---------|-----|----------------------------|

    | `page`      | 1       | —   | Page number (1-indexed)    |

    | `page_size` | 25      | 100 | Number of items per page   |


    Response includes a `meta` object with pagination details:


    ```json

    {
      "data": [...],
      "meta": {
        "total": 156,
        "page": 1,
        "page_size": 25,
        "total_pages": 7
      }
    }

    ```



    ## Filtering


    List endpoints support filtering via bracket-notation query parameters:


    ```

    GET
    /api/v2/workspaces/123/content_editors?filter[state]=completed&filter[inserted_after]=2024-04-12T15:23:45Z

    ```


    Supported filters are documented per endpoint. Unknown filter keys are
    silently ignored. Applied filters are reflected in the response
    `meta.filters` object.



    ## Ordering


    List endpoints support ordering via `sort` and `order` query parameters:


    ```

    GET /api/v2/workspaces/123/content_editors?sort=inserted_at&order=desc

    ```


    - `sort` — field to sort by. Supported fields are documented per endpoint.
    Defaults to `inserted_at`.

    - `order` — `asc` or `desc`. Defaults to `desc`.



    ## Idempotency


    Non-idempotent operations (`POST` requests that create resources) support an
    `Idempotency-Key` header:


    ```

    POST /api/v2/workspaces/123/content_editors

    Idempotency-Key: client-generated-uuid-here

    ```


    If a request with the same key was already processed, the API answers with
    the original status code and the resource as it is now, without running the
    action again. Keys expire after 24 hours. Using the same key with a
    different request path returns `422`, a request still in progress under the
    same key returns `409`, and a failed request does not store its key, so the
    same key can retry it.



    ## Rate Limiting


    Rate limits are enforced per API key, per endpoint. When exceeded, the API
    returns `429` with a `Retry-After` header. Per-endpoint limits are
    documented on each operation.


    v2 responses additionally include `x-ratelimit-limit`,
    `x-ratelimit-remaining`, and `x-ratelimit-reset` (Unix timestamp, seconds)
    headers describing the current window state.



    ## Errors


    Newer v2 endpoints return errors in this envelope:


    ```json

    {
      "error": {
        "reason": "validation_error",
        "message": "Request validation failed",
        "details": [
          { "field": "keywords", "reason": "required" }
        ]
      }
    }

    ```


    | HTTP Status | Reason                   |
    Description                                      |

    |-------------|--------------------------|--------------------------------------------------|

    | 400         | `validation_error`       | Invalid request
    parameters                       |

    | 401         | `unauthorized`           | Missing or invalid API
    key                       |

    | 403         | `permission_denied`      | Valid key but no access,
    including plan/billing  |

    | 404         | `not_found`              | Resource does not
    exist                          |

    | 406         | `not_acceptable`         | Requested response format
    unavailable            |

    | 409         | `conflict`               | Wrong resource state or
    idempotency key in use   |

    | 413         | `content_too_large`      | Request body too
    large                           |

    | 415         | `unsupported_media_type` | Unsupported request content
    type                 |

    | 422         | `unprocessable_entity`   | Request understood but cannot be
    processed       |

    | 422         | `quota_exceeded`         | Feature enabled but credit/quota
    limit reached   |

    | 429         | `rate_limit_exceeded`    | Too many
    requests                                |

    | 500         | `internal_error`         | Unexpected server
    error                          |



    ## Webhooks


    Some asynchronous operations emit webhook events. Operations that emit
    events list the event names and payload notes in their descriptions.


    Webhook setup is organization-level. Configure an HTTPS endpoint that
    accepts POST requests and responds with a `2xx` status, then contact Surfer
    support to register the endpoint and receive a verification key.


    Surfer sends webhook requests with a `Verification-Key` header and a JSON
    body.



    ## Webhook Events


    - `content_editor.ai_article.completed`

    - `content_editor.ai_article.failed`

    - `content_editor.ai_article.waiting_for_user_input`

    - `content_editor.ai_search_score.calculated`

    - `content_editor.ai_search_score.failed`

    - `content_editor.seo_score.calculated`

    - `content_editor.seo_score.failed`

    - `content_editor.content_score.recalculated`

    - `content_editor.auto_optimize.completed`

    - `content_editor.auto_optimize.failed`

    - `content_editor.auto_optimize.cancelled`

    - `content_editor.initialization.completed`

    - `content_editor.initialization.failed`

    - `content_editor.seo_guidelines.competitors.load_more.completed`

    - `content_editor.seo_guidelines.competitors.load_more.failed`

    - `content_editor.outline.completed`

    - `content_editor.outline.failed`

  title: Surfer API
  version: v2.0
servers:
  - url: https://app.surferseo.com
    variables: {}
security: []
tags:
  - name: Workspaces
    x-displayName: Workspaces
  - description: >-
      Maintaining a legacy integration? The v1 Content Editor endpoints remain
      available.
    name: Content Editors
    x-displayName: Content Editors
  - name: AI Articles
    x-displayName: AI Articles
  - name: Auto-Optimize
    x-displayName: Auto-Optimize
  - name: SEO Guidelines
    x-displayName: SEO Guidelines
  - name: AI Search
    x-displayName: AI Search
  - name: Outline
    x-displayName: Outline
  - name: Permalinks
    x-displayName: Permalinks
  - name: Content Templates
    x-displayName: Content Templates
  - name: Custom Voices
    x-displayName: Custom Voices
  - name: Surfer Content Templates
    x-displayName: Content Template Presets
    x-group: Content Template Presets
  - description: Available in v1 only. See the v1 API docs.
    name: Audit
    x-displayName: Audit
  - description: Available in v1 only. See the v1 API docs.
    name: SERP Analyzer
    x-displayName: SERP Analyzer
  - description: Available in v1 only. See the v1 API docs.
    name: AI Detector
    x-displayName: AI Detector
  - description: Available in v1 only. See the v1 API docs.
    name: Humanizer
    x-displayName: Humanizer
paths:
  /api/v2/workspaces/{workspace_id}/content_editors:
    post:
      tags:
        - Content Editors
      summary: Create Content Editor
      description: >
        Creates a new Content Editor. The operation is asynchronous — the
        response returns immediately

        with the Content Editor in `scheduled` state.


        `use_brand_knowledge` defaults to `true`. When enabled, workspace brand
        knowledge is used where available; set `false` for unbranded output.


        Supports `Idempotency-Key` header.


        **Keywords:** `main_keyword` (required) is the primary keyword that
        drives the SERP analysis.

        Add up to 19 `secondary_keywords` (20 keywords total including the main
        keyword); exceeding the

        limit returns `400` with a `secondary_keywords` / `too_many` detail.


        **Locations:** `location` is a country or region display name (e.g.
        `"United States"`, the default

        when omitted). Fetch the full list of supported values from `GET
        /api/v1/locations` (no auth

        required). `device` selects the SERP device profile — `mobile` (default)
        or `desktop`.


        **Templates:** `surfer_template` and `custom_template_id` are mutually
        exclusive. When neither

        is provided, Surfer asynchronously preselects a template — the
        workspace's default custom

        template if configured, otherwise a Surfer template inferred from the
        SERP. Read the final

        values after `state` becomes `completed`. On a completed Content Editor,
        both fields `null`

        represents the "SERP-based" template: the article structure follows the
        SERP analysis rather

        than a preset.


        **Custom voice:** when `custom_voice_id` is omitted, the workspace's
        default custom voice (if any)

        is used. Pass `null` explicitly to opt out.


        **Webhooks:** `content_editor.initialization.completed` /
        `content_editor.initialization.failed`

        fire when initialization settles. During initialization the AI Search
        score is computed, firing

        `content_editor.ai_search_score.calculated` / `.failed` when it lands
        (no event when the score ends

        `unavailable`). The SEO score computed for imported content at creation
        sends no event;

        `content_editor.seo_score.calculated` / `.failed` and
        `content_editor.content_score.recalculated`

        fire after content updates made through the API. As a fallback, poll
        `GET .../content_editors/:id`.


        **Cost:** each successful request consumes one Content Editor credit.


        **Rate limit**: 10 requests/sec
      operationId: KSeoWeb.Api.V2.ContentEditors.Controller.create
      parameters:
        - description: ''
          in: path
          name: workspace_id
          required: true
          schema:
            $ref: '#/components/schemas/EntityId'
        - description: >-
            Client-generated key for safely retrying create requests. Reusing
            the same key for the same path answers with the original status code
            and the resource as it is now; reusing it for a different path
            returns 422. Keys expire after 24 hours.
          in: header
          name: Idempotency-Key
          required: false
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContentEditorParams'
        description: Content Editor params
        required: false
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreatedContentEditorDetail'
          description: Created Content Editor
          headers:
            x-ratelimit-limit:
              description: >-
                Maximum number of requests allowed in the current window for
                this endpoint and API key.
              schema:
                example: 10
                type: integer
              style: simple
            x-ratelimit-remaining:
              description: >-
                Requests remaining in the current window. `0` means the next
                request will be rate-limited.
              schema:
                example: 7
                type: integer
              style: simple
            x-ratelimit-reset:
              description: Unix timestamp (seconds) at which the current window resets.
              schema:
                example: 1714838400
                type: integer
              style: simple
        '400':
          content:
            application/json:
              example:
                error:
                  details:
                    - field: keywords
                      reason: required
                  reason: validation_error
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Validation error
          headers:
            x-ratelimit-limit:
              description: >-
                Maximum number of requests allowed in the current window for
                this endpoint and API key.
              schema:
                example: 10
                type: integer
              style: simple
            x-ratelimit-remaining:
              description: >-
                Requests remaining in the current window. `0` means the next
                request will be rate-limited.
              schema:
                example: 7
                type: integer
              style: simple
            x-ratelimit-reset:
              description: Unix timestamp (seconds) at which the current window resets.
              schema:
                example: 1714838400
                type: integer
              style: simple
        '401':
          content:
            application/json:
              example:
                error:
                  details: []
                  reason: unauthorized
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Unauthorized
          headers:
            x-ratelimit-limit:
              description: >-
                Maximum number of requests allowed in the current window for
                this endpoint and API key.
              schema:
                example: 10
                type: integer
              style: simple
            x-ratelimit-remaining:
              description: >-
                Requests remaining in the current window. `0` means the next
                request will be rate-limited.
              schema:
                example: 7
                type: integer
              style: simple
            x-ratelimit-reset:
              description: Unix timestamp (seconds) at which the current window resets.
              schema:
                example: 1714838400
                type: integer
              style: simple
        '403':
          content:
            application/json:
              example:
                error:
                  details: []
                  reason: permission_denied
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Forbidden
          headers:
            x-ratelimit-limit:
              description: >-
                Maximum number of requests allowed in the current window for
                this endpoint and API key.
              schema:
                example: 10
                type: integer
              style: simple
            x-ratelimit-remaining:
              description: >-
                Requests remaining in the current window. `0` means the next
                request will be rate-limited.
              schema:
                example: 7
                type: integer
              style: simple
            x-ratelimit-reset:
              description: Unix timestamp (seconds) at which the current window resets.
              schema:
                example: 1714838400
                type: integer
              style: simple
        '404':
          content:
            application/json:
              example:
                error:
                  details: []
                  reason: not_found
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Workspace not found
          headers:
            x-ratelimit-limit:
              description: >-
                Maximum number of requests allowed in the current window for
                this endpoint and API key.
              schema:
                example: 10
                type: integer
              style: simple
            x-ratelimit-remaining:
              description: >-
                Requests remaining in the current window. `0` means the next
                request will be rate-limited.
              schema:
                example: 7
                type: integer
              style: simple
            x-ratelimit-reset:
              description: Unix timestamp (seconds) at which the current window resets.
              schema:
                example: 1714838400
                type: integer
              style: simple
        '406':
          content:
            application/json:
              example:
                error:
                  details:
                    - allowed:
                        - application/json
                      field: accept
                      reason: unsupported
                  message: 'Accept must be one of: application/json'
                  reason: not_acceptable
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Not acceptable
          headers:
            x-ratelimit-limit:
              description: >-
                Maximum number of requests allowed in the current window for
                this endpoint and API key.
              schema:
                example: 10
                type: integer
              style: simple
            x-ratelimit-remaining:
              description: >-
                Requests remaining in the current window. `0` means the next
                request will be rate-limited.
              schema:
                example: 7
                type: integer
              style: simple
            x-ratelimit-reset:
              description: Unix timestamp (seconds) at which the current window resets.
              schema:
                example: 1714838400
                type: integer
              style: simple
        '415':
          content:
            application/json:
              example:
                error:
                  details: []
                  message: 'Set Content-Type: application/json.'
                  reason: unsupported_media_type
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Unsupported media type
          headers:
            x-ratelimit-limit:
              description: >-
                Maximum number of requests allowed in the current window for
                this endpoint and API key.
              schema:
                example: 10
                type: integer
              style: simple
            x-ratelimit-remaining:
              description: >-
                Requests remaining in the current window. `0` means the next
                request will be rate-limited.
              schema:
                example: 7
                type: integer
              style: simple
            x-ratelimit-reset:
              description: Unix timestamp (seconds) at which the current window resets.
              schema:
                example: 1714838400
                type: integer
              style: simple
        '422':
          content:
            application/json:
              example:
                error:
                  details: []
                  message: The request was understood but could not be processed.
                  reason: unprocessable_entity
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Quota exceeded
          headers:
            x-ratelimit-limit:
              description: >-
                Maximum number of requests allowed in the current window for
                this endpoint and API key.
              schema:
                example: 10
                type: integer
              style: simple
            x-ratelimit-remaining:
              description: >-
                Requests remaining in the current window. `0` means the next
                request will be rate-limited.
              schema:
                example: 7
                type: integer
              style: simple
            x-ratelimit-reset:
              description: Unix timestamp (seconds) at which the current window resets.
              schema:
                example: 1714838400
                type: integer
              style: simple
        '429':
          content:
            application/json:
              example:
                error:
                  details:
                    - retry_after: 30
                  reason: rate_limit_exceeded
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Rate limit exceeded
          headers:
            Retry-After:
              description: Seconds the client should wait before retrying the request.
              schema:
                example: 30
                type: integer
              style: simple
            x-ratelimit-limit:
              description: >-
                Maximum number of requests allowed in the current window for
                this endpoint and API key.
              schema:
                example: 10
                type: integer
              style: simple
            x-ratelimit-remaining:
              description: >-
                Requests remaining in the current window. `0` means the next
                request will be rate-limited.
              schema:
                example: 7
                type: integer
              style: simple
            x-ratelimit-reset:
              description: Unix timestamp (seconds) at which the current window resets.
              schema:
                example: 1714838400
                type: integer
              style: simple
      callbacks: {}
      security:
        - API Key: []
components:
  schemas:
    EntityId:
      example: 123
      minimum: 1
      title: EntityId
      type: integer
    CreateContentEditorParams:
      additionalProperties: false
      properties:
        custom_instructions:
          example: Mention product X, write from perspective of Y
          nullable: true
          type: string
        custom_template_id:
          allOf:
            - $ref: '#/components/schemas/CustomTemplateId'
          nullable: true
        custom_voice_id:
          allOf:
            - $ref: '#/components/schemas/EntityId'
          nullable: true
        device:
          default: mobile
          enum:
            - mobile
            - desktop
          example: mobile
          type: string
        import_content_url:
          example: https://example.com/article
          format: uri
          nullable: true
          type: string
        location:
          $ref: '#/components/schemas/Location'
        main_keyword:
          description: Primary keyword for SERP analysis
          example: seo tools
          maxLength: 700
          type: string
        notes:
          example: Draft for Q2 campaign
          nullable: true
          type: string
        secondary_keywords:
          default: []
          description: Up to 19 secondary keywords (20 total including main keyword).
          example:
            - keyword research
            - best seo software
          items:
            maxLength: 700
            type: string
          maxItems: 19
          type: array
        surfer_template:
          allOf:
            - $ref: '#/components/schemas/SurferContentTemplate'
          nullable: true
        target_word_count:
          example: 2000
          minimum: 1
          nullable: true
          type: integer
        use_brand_knowledge:
          default: true
          description: >-
            Defaults to true. When enabled, workspace brand knowledge is used
            where available; set false for unbranded output.
          example: true
          type: boolean
      required:
        - main_keyword
      title: CreateContentEditorParams
      type: object
    CreatedContentEditorDetail:
      allOf:
        - $ref: '#/components/schemas/ContentEditorDetail'
      properties:
        state:
          enum:
            - completed
            - executing
            - failed
            - scheduled
          example: scheduled
          type: string
      title: CreatedContentEditorDetail
      type: object
    ErrorResponse:
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetails'
      required:
        - error
      title: ErrorResponse
      type: object
    CustomTemplateId:
      description: >-
        Workspace-level custom template ID. Mutually exclusive with
        surfer_template.
      minimum: 1
      title: CustomTemplateId
      type: integer
    Location:
      description: >-
        Location; get the list of supported locations from `/api/v1/locations`
        (no auth required)
      example: United States
      title: Location
      type: string
    SurferContentTemplate:
      description: Surfer template preset. Mutually exclusive with custom_template_id.
      enum:
        - blog_post
        - category_description
        - product_description
        - comparison
        - listicle
        - landing_page
        - local_business
        - llm_optimized_article
      example: blog_post
      title: SurferContentTemplate
      type: string
    ContentEditorDetail:
      allOf:
        - $ref: '#/components/schemas/ContentEditorSummary'
      properties:
        custom_instructions:
          example: Write from perspective of a senior developer
          nullable: true
          type: string
        meta_description:
          description: Draft meta description.
          example: A curated list of the top SEO tools for 2025.
          nullable: true
          type: string
        meta_title:
          description: Draft meta title.
          example: Best SEO Tools in 2025
          nullable: true
          type: string
        notes:
          example: Draft for Q2 campaign
          nullable: true
          type: string
      title: ContentEditorDetail
      type: object
    ErrorDetails:
      properties:
        details:
          description: Structured details; shape depends on `reason`.
          items:
            type: object
          type: array
        message:
          description: Optional human-readable context.
          type: string
        reason:
          description: Machine-readable error key.
          type: string
      required:
        - reason
        - details
      title: ErrorDetails
      type: object
    ContentEditorSummary:
      allOf:
        - $ref: '#/components/schemas/V2.Timestamps'
      properties:
        ai_article:
          allOf:
            - $ref: '#/components/schemas/AiArticle'
          nullable: true
        ai_search_guidelines:
          allOf:
            - $ref: '#/components/schemas/AiSearchGuidelinesStatus'
          nullable: true
        auto_optimize:
          allOf:
            - $ref: '#/components/schemas/AutoOptimizeStatus'
          nullable: true
        content_score:
          $ref: '#/components/schemas/ContentScore'
        created_by:
          example: user@example.com
          nullable: true
          type: string
        custom_template_id:
          allOf:
            - $ref: '#/components/schemas/CustomTemplateId'
          nullable: true
        custom_voice_id:
          allOf:
            - $ref: '#/components/schemas/EntityId'
          nullable: true
        device:
          enum:
            - mobile
            - desktop
          example: mobile
          type: string
        error:
          allOf:
            - $ref: '#/components/schemas/ErrorDetails'
          nullable: true
        id:
          $ref: '#/components/schemas/EntityId'
        import_content_url:
          example: https://example.com/article
          nullable: true
          type: string
        location:
          $ref: '#/components/schemas/Location'
        main_keyword:
          example: seo tools
          type: string
        outline:
          allOf:
            - $ref: '#/components/schemas/OutlineStatus'
          nullable: true
        permalinks:
          items:
            $ref: '#/components/schemas/Permalink'
          type: array
        secondary_keywords:
          example:
            - keyword research
            - ai visibility
          items:
            type: string
          type: array
        seo_guidelines:
          allOf:
            - $ref: '#/components/schemas/SeoGuidelinesStatus'
          nullable: true
        state:
          enum:
            - completed
            - executing
            - failed
            - scheduled
          example: completed
          type: string
        surfer_template:
          allOf:
            - $ref: '#/components/schemas/SurferContentTemplate'
          nullable: true
        target_word_count:
          example: 2000
          nullable: true
          type: integer
        use_brand_knowledge:
          description: >-
            Whether workspace brand knowledge is enabled for this Content
            Editor.
          example: true
          type: boolean
        workspace_id:
          $ref: '#/components/schemas/EntityId'
      required:
        - id
        - workspace_id
        - permalinks
        - state
        - main_keyword
        - secondary_keywords
        - location
        - device
        - content_score
        - outline
        - seo_guidelines
        - ai_search_guidelines
        - auto_optimize
        - ai_article
        - created_by
      title: ContentEditorSummary
      type: object
    V2.Timestamps:
      properties:
        inserted_at:
          example: '2024-04-12T15:23:45Z'
          format: date-time
          type: string
        updated_at:
          example: '2024-06-01T09:17:33Z'
          format: date-time
          type: string
      required:
        - inserted_at
      title: V2.Timestamps
      type: object
    AiArticle:
      description: >-
        Active AI article. `null` if no AI article has been generated for the
        Content Editor.
      properties:
        id:
          $ref: '#/components/schemas/EntityId'
        status:
          enum:
            - new
            - analyzing
            - waiting_for_user_input
            - writing
            - completed
            - failed
          example: completed
          type: string
      required:
        - id
        - status
      title: AiArticle
      type: object
    AiSearchGuidelinesStatus:
      description: >-
        AI Search guidelines state. `null` when no AI Search guidelines exist
        for the Content Editor (e.g. created before the workflow existed) —
        distinct from `{status: "executing"}` (generation in progress).
      properties:
        status:
          enum:
            - executing
            - completed
            - failed
          example: completed
          type: string
      required:
        - status
      title: AiSearchGuidelinesStatus
      type: object
    AutoOptimizeStatus:
      description: >-
        Latest Auto-Optimize job state. `null` when no Auto-Optimize has ever
        been run on this Content Editor.
      properties:
        status:
          enum:
            - executing
            - completed
            - failed
          example: completed
          type: string
      required:
        - status
      title: AutoOptimizeStatus
      type: object
    ContentScore:
      properties:
        ai_search:
          example: 62
          nullable: true
          type: integer
        seo:
          example: 75
          nullable: true
          type: integer
        total:
          example: 75
          nullable: true
          type: integer
      required:
        - seo
        - ai_search
        - total
      title: ContentScore
      type: object
    OutlineStatus:
      description: >-
        Outline state. `null` when no outline exists for the Content Editor —
        distinct from `{status: "scheduled"}` (generation in progress).
      properties:
        status:
          enum:
            - scheduled
            - completed
            - failed
          example: completed
          type: string
      required:
        - status
      title: OutlineStatus
      type: object
    Permalink:
      properties:
        hash:
          example: abc123xyz
          type: string
        type:
          enum:
            - edit
            - comment
          example: edit
          type: string
        url:
          example: https://app.surferseo.com/drafts/s/abc123xyz
          format: uri
          type: string
      required:
        - type
        - hash
        - url
      title: Permalink
      type: object
    SeoGuidelinesStatus:
      description: >-
        SEO guidelines state. `null` when no SEO guidelines exist for the
        Content Editor — distinct from `{status: "executing"}` (generation in
        progress).
      properties:
        status:
          enum:
            - executing
            - completed
            - failed
          example: completed
          type: string
      required:
        - status
      title: SeoGuidelinesStatus
      type: object
  securitySchemes:
    API Key:
      in: header
      name: API-KEY
      type: apiKey

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.