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

# Delete an eligible local article

> Never silently unpublishes its remote CMS copy.



## OpenAPI

````yaml /api-reference/openapi.yaml delete /profiles/{profile_id}/articles/{id}
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}/articles/{id}:
    delete:
      tags:
        - Articles
      summary: Delete an eligible local article
      description: Never silently unpublishes its remote CMS copy.
      operationId: deleteArticle
      parameters:
        - $ref: '#/components/parameters/ProfileID'
        - $ref: '#/components/parameters/ResourceID'
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          $ref: '#/components/responses/Deletion'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
      security:
        - OAuth2:
            - articles:delete
        - 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'
    ResourceID:
      name: id
      in: path
      required: true
      description: >-
        Resource UUID, scoped by the path profile or persisted operation
        receipt.
      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: ^[!-~]+$
  responses:
    Deletion:
      description: Explicit local deletion result and durable receipt.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DeletionEnvelope'
    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'
  schemas:
    UUID:
      type: string
      format: uuid
    DeletionEnvelope:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          $ref: '#/components/schemas/DeletionData'
        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
    DeletionData:
      type: object
      additionalProperties: false
      required:
        - deleted
        - resource_id
        - receipt
      properties:
        deleted:
          type: boolean
        resource_id:
          $ref: '#/components/schemas/UUID'
        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.
    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'
  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.