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

# Explicitly replace eligible future calendar work

> Generated, published and in-flight slots remain protected.



## OpenAPI

````yaml /api-reference/openapi.yaml post /profiles/{profile_id}/calendar/replacements
openapi: 3.1.0
info:
  title: SORANK Public API
  version: 1.1.0
  description: >
    Versioned REST contract for user-delegated SORANK access, served under

    `/api/public/v1`. Contract 1.1 adds products, Search Console and AI traffic

    analytics, extended GEO reads and site settings to the same version path.

    Availability of each operation depends on the deployed backend version and

    the public API feature flag.

    OAuth tokens are resource-bound to the REST API. Personal API keys may use

    REST only when their transport grant permits it. Browser session cookies and

    Supabase tokens are not credentials for this surface.


    Every request is checked against the credential's exact scopes, explicit

    profile allowlist and the actor's current profile access. The resource owner

    and billable account are resolved server-side. A revoked or expired
    credential

    is rejected even on idempotent replay and operation polling.


    Eligibility: the user must have completed Sorank onboarding and the accessed

    site must have a valid subscription (the owner's subscription for an agency

    collaborator; a site without a subscription is not eligible). Eligibility is

    checked at OAuth consent, at personal key creation and replacement, and
    again

    on every REST request and MCP tool call, including idempotent replays and

    operation polling. An incomplete onboarding returns 403

    `onboarding_required`; an ineligible site returns 402
    `subscription_required`

    with `details.reason`. `listProfiles` returns only the eligible sites of the

    grant; operations without a profile require at least one eligible site.


    All mutations require `Idempotency-Key`. Repeating an identical command with

    the same key returns its original receipt after fresh authorization; a
    changed

    command with the same key returns 409. An accepted asynchronous command may

    still fail later. Do not treat an accepted batch as wholly complete: an

    article generation batch returns HTTP 202 with a receipt already `succeeded`

    (the acceptance is the command's outcome), and each generated article is
    then

    followed through `getArticle` or `listArticles` from the receipt's accepted
    IDs.

    Settings commands return the resulting settings and the asynchronous

    `effects` they queued.


    Errors use one envelope everywhere, including authentication and rate-limit

    middlewares: `{"error": {"code", "message", "request_id", "retryable",

    "details"?}}`. MCP tool errors carry the same `code`, `message`,

    `request_id`, `retryable` and `details` in the tool error result. Codes are

    stable snake_case identifiers; each operation lists the codes it can return

    in `x-error-codes`, and REST and MCP return the same code for the same
    cause.


    Error statuses: 400 invalid request (`invalid_request`, `invalid_id`,

    `invalid_idempotency_key` and field-specific validation codes such as

    `invalid_limit`, `invalid_range`, `field_not_writable` for a read-only field

    like `website_url`, `invalid_market`, `invalid_competitor_selection`,

    `too_many_competitors` or `invalid_url`); 401

    missing/invalid/expired/revoked credential (`invalid_credential`, with a

    `WWW-Authenticate` challenge carrying `resource_metadata`); 402

    `subscription_required` (with `details.reason` among `paused`, `past_due`,

    `incomplete`, `inactive` when known) or `insufficient_quota` for business

    quotas; 403 `missing_scope` or `onboarding_required`; 404 `not_found` for an

    inaccessible or unknown resource; 409 conflicts: `idempotency_conflict` (the

    key belongs to a different command), `calendar_busy` (retryable),

    `calendar_changed` (read the current calendar before retrying),

    `publication_busy` (retryable), `resource_busy` (retryable),

    `keyword_already_published`, `article_identity_conflict`,

    `slug_already_exists`, `product_catalog_cursor_stale` (restart the

    product list from the first page), `business_brief_revision_conflict`

    (read the settings again), `calendar_required`,

    `youtube_source_replacement_required` (send `replace: true`),

    `youtube_source_purge_in_progress` (retryable, with `Retry-After`);

    `gsc_not_connected`, `ga_not_connected` (connect Google in Sorank); 422
    unsupported provider capability or state

    (`unsupported_capability`, `unsupported_category`, `cms_not_connected`,

    `publication_state_invalid`, `youtube_channel_unsupported`,

    `youtube_channel_not_found`, ...); 403 `google_access_lost` (with

    `details.reason` among `scope_missing`, `property_access_denied`,

    `token_expired`, `ga_permissions_lost`, `ga_property_unavailable`;

    reconnect Google in Sorank); 429 `rate_limited` or `youtube_rate_limited`

    (retryable, with `Retry-After` and `details.retry_after_seconds` when a

    delay is known); 500 `internal_error` for unexpected failures only; 503

    `auth_state_unavailable` or `rate_limit_unavailable` (retryable), with

    access denied until the state recovers, `google_unavailable` or

    `youtube_unavailable` (retryable), or `youtube_feature_disabled` (not

    retryable: the integration is disabled on this deployment); 504 `ga_timeout`
    (retryable with a shorter range).


    Each operation has an MCP tool named by `x-mcp-tool`. Tool arguments are

    flat: `profile_id` and `id` for the path parameters, `idempotency_key` for

    the header, plus the query or body fields of the operation; list and batch

    bounds follow the deployment configuration. A tool result carries exactly

    the REST response body (`data` and `meta`).
servers:
  - url: /api/public/v1
security:
  - OAuth2: []
  - PersonalAPIKey: []
tags:
  - name: Profiles
    description: Accessible SEO profiles, capabilities and business quotas.
  - name: Keywords and GEO
    description: >-
      Existing keyword suggestions, GEO analysis results and GEO aggregates as
      shown by the Sorank dashboard.
  - name: Products
    description: Product catalog discovered on the profile's site.
  - name: Analytics
    description: |
      Live Google Search Console and Google Analytics 4 reads, as shown by the
      Sorank dashboard. Each call queries Google; it may refresh and store the
      site's Google access token, never forces a cache refresh, and is charged
      to a dedicated analytics rate budget lower than other reads. Google
      Analytics exposure is limited to traffic coming from AI assistants.
  - name: Settings
    description: |
      Site settings as the Sorank settings pages show them to the same user,
      published as an allowlist: no owner identifier, homepage screenshot,
      connection field, storage path, signed URL or technical error.
  - name: Articles
    description: Local article reading, editing and generation.
  - name: Calendar
    description: Automatic editorial calendar and slot management.
  - name: Publication
    description: CMS publication, updates and scheduling.
  - name: Operations
    description: Durable command receipts and outcome polling.
paths:
  /profiles/{profile_id}/calendar/replacements:
    post:
      tags:
        - Calendar
      summary: Explicitly replace eligible future calendar work
      description: Generated, published and in-flight slots remain protected.
      operationId: replaceCalendar
      parameters:
        - $ref: '#/components/parameters/ProfileID'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReplaceCalendarRequest'
      responses:
        '200':
          $ref: '#/components/responses/CalendarMutation'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
      security:
        - OAuth2:
            - calendar:write
            - articles:generate
            - articles:publish
        - PersonalAPIKey: []
components:
  parameters:
    ProfileID:
      name: profile_id
      in: path
      required: true
      description: >-
        Profile UUID; must be in the grant allowlist and currently accessible to
        the actor.
      schema:
        $ref: '#/components/schemas/UUID'
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: >-
        Opaque client command key of 1 to 128 visible ASCII characters. UUID
        recommended; reuse only for the same command.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[!-~]+$
  schemas:
    ReplaceCalendarRequest:
      type: object
      additionalProperties: false
      required:
        - replace_slot_ids
        - settings
        - start_date
        - end_date
      oneOf:
        - required:
            - keyword_ids
        - required:
            - subjects
      properties:
        replace_slot_ids:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items:
            $ref: '#/components/schemas/UUID'
        settings:
          $ref: '#/components/schemas/CalendarSettingsInput'
        start_date:
          type: string
          format: date
          description: >-
            Local start date; the selected local_time must be in the future and
            the planner starts here.
        end_date:
          type: string
          format: date
          description: >-
            Inclusive local end date; the command rolls back if any planned slot
            falls later.
        keyword_ids:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items:
            $ref: '#/components/schemas/UUID'
        subjects:
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/CalendarSeed'
    UUID:
      type: string
      format: uuid
    CalendarSettingsInput:
      type: object
      additionalProperties: false
      required:
        - timezone
        - local_time
      properties:
        timezone:
          type: string
          minLength: 1
          description: IANA timezone such as Europe/Paris.
        local_time:
          type: string
          pattern: ^([01][0-9]|2[0-3]):[0-5][0-9]$
          description: Local wall-clock time in the named timezone.
        instructions:
          type: string
    CalendarSeed:
      type: object
      additionalProperties: false
      required:
        - subject
      properties:
        subject:
          type: string
          minLength: 1
          maxLength: 500
          description: Maximum 500 UTF-8 bytes after trimming.
        keyword_id:
          $ref: '#/components/schemas/UUID'
          description: >-
            Existing suggestion in the same profile; subject must equal its
            canonical label.
        instructions:
          type: string
          maxLength: 4000
          description: Per-slot instructions; maximum 4000 UTF-8 bytes.
    CalendarMutationEnvelope:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          $ref: '#/components/schemas/CalendarMutationData'
        meta:
          $ref: '#/components/schemas/MutationMeta'
    ErrorEnvelope:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/ErrorBody'
      examples:
        - error:
            code: insufficient_quota
            message: The current article generation quota is exhausted.
            request_id: 33333333-3333-4333-8333-333333333333
            retryable: false
    CalendarMutationData:
      type: object
      additionalProperties: false
      required:
        - calendar
        - receipt
      properties:
        calendar:
          $ref: '#/components/schemas/CalendarData'
        receipt:
          $ref: '#/components/schemas/CommandReceipt'
    MutationMeta:
      type: object
      additionalProperties: false
      required:
        - request_id
        - operation_id
      properties:
        request_id:
          type: string
          minLength: 1
        operation_id:
          $ref: '#/components/schemas/UUID'
    ErrorBody:
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - request_id
        - retryable
      properties:
        code:
          type: string
          description: Stable public error code; never a raw SQL, CMS or provider error.
          examples:
            - insufficient_quota
            - unsupported_capability
        message:
          type: string
        request_id:
          $ref: '#/components/schemas/UUID'
        retryable:
          type: boolean
          description: |
            True when repeating the same request later, unchanged and with the
            same Idempotency-Key, may succeed (transient busy state, rate limit,
            unavailable authorization or rate-limit state). False when the
            request or the resource state must change first.
        details:
          type: object
          additionalProperties: true
          description: >-
            Safe structured details only (for example `reason` for
            `subscription_required`); no tokens, prompts or internal commands.
    CalendarData:
      type: object
      additionalProperties: false
      required:
        - exists
        - profile_id
      examples:
        - exists: false
          profile_id: 22222222-2222-4222-8222-222222222222
      properties:
        exists:
          type: boolean
        profile_id:
          $ref: '#/components/schemas/UUID'
        id:
          $ref: '#/components/schemas/UUID'
        status:
          type: string
          enum:
            - active
            - paused
        settings:
          $ref: '#/components/schemas/CalendarSettingsData'
        slots:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/CalendarSlotData'
      description: When exists is false, only exists and profile_id are emitted.
    CommandReceipt:
      type: object
      additionalProperties: false
      description: |
        Durable receipt of a command; poll getOperation with operation_id until
        the status is terminal (`succeeded` or `failed`; `verification_pending`
        means the CMS outcome is still being verified). An article generation
        receipt is `succeeded` as soon as the batch is accepted.
      required:
        - operation_id
        - status
        - resource_id
      properties:
        operation_id:
          $ref: '#/components/schemas/UUID'
        status:
          type: string
          enum:
            - accepted
            - running
            - succeeded
            - failed
            - verification_pending
        resource_id:
          $ref: '#/components/schemas/UUID'
    CalendarSettingsData:
      type: object
      additionalProperties: false
      required:
        - timezone
        - local_time
      properties:
        timezone:
          type: string
        local_time:
          type: string
          pattern: ^([01][0-9]|2[0-3]):[0-5][0-9]$
          description: >-
            Local wall-clock publication time HH:MM, the same form as
            CalendarSettingsInput.
        instructions:
          type:
            - string
            - 'null'
    CalendarSlotData:
      type: object
      additionalProperties: false
      required:
        - id
        - subject
        - scheduled_at
        - status
      properties:
        id:
          $ref: '#/components/schemas/UUID'
        subject:
          type: string
        scheduled_at:
          $ref: '#/components/schemas/Timestamp'
        status:
          type: string
        article_id:
          type:
            - string
            - 'null'
          format: uuid
        instructions:
          type:
            - string
            - 'null'
    Timestamp:
      type: string
      format: date-time
      description: RFC3339 timestamp with an explicit UTC offset.
  responses:
    CalendarMutation:
      description: Calendar state, warnings and durable receipt.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/CalendarMutationEnvelope'
    Unauthorized:
      description: Credential missing, invalid, expired or revoked.
      headers:
        WWW-Authenticate:
          description: Bearer challenge with configured resource metadata where applicable.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Error:
      description: Error response; see status-to-code mapping in API description.
      headers:
        Retry-After:
          description: >-
            Seconds until retry when HTTP 429 is returned, or with 409
            youtube_source_purge_in_progress.
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
  securitySchemes:
    OAuth2:
      type: oauth2
      description: |
        Authorization-code grant with PKCE S256, exact scopes, explicit resource
        audience and a persisted consent grant. Obtain endpoint URLs from server
        discovery in the target environment. Refresh tokens remain client-bound.
      flows:
        authorizationCode:
          authorizationUrl: /oauth/authorize
          tokenUrl: /oauth/token
          refreshUrl: /oauth/token
          scopes:
            profiles:read: Read authorized profiles and CMS capabilities.
            quotas:read: Read business quotas.
            keywords:read: Read existing keyword suggestions.
            geo:read: Read existing GEO analyses.
            articles:read: Read articles and generation results.
            articles:write: Edit allowed local article fields.
            articles:generate: Generate articles.
            articles:delete: Delete eligible local articles.
            calendar:read: Read calendar and slots.
            calendar:write: Manage calendar and slots.
            articles:publish: Schedule and manage CMS publication.
            products:read: Read the product catalog.
            analytics:read: Read Search Console and AI traffic analytics.
            settings:read: Read site, article, visual and YouTube settings.
            settings:write: >-
              Change site, business, competitor, article, visual and YouTube
              settings. Some changes queue asynchronous regeneration or
              synchronization.
    PersonalAPIKey:
      type: http
      scheme: bearer
      bearerFormat: Personal API key
      description: >
        Send the one-time personal API key in Authorization: Bearer. Exact
        scopes,

        explicit profiles, expiry, revocation and transport are verified
        server-side.

        This is not an OAuth access token or a browser session.

````

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