Frase Webhooks API

Webhook subscriptions for event notifications

GitHub OpenAPI

Operations 5

GET /webhooks List webhooks #
POST /webhooks Create webhook #
GET /webhooks/{id} Get webhook #
PATCH /webhooks/{id} Update webhook #
DELETE /webhooks/{id} Delete webhook #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/frase-io:frase-io-webhooks-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

frase-io-webhooks-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Frase Webhooks API
  version: 1.0.0
  description: 'The Frase API provides programmatic access to your SEO content platform. Use it to manage content, research, audits, AI visibility tracking, and more. ## Authentication


    All API requests require authentication using an API key. Include your key in the `X-API-KEY` header:


    ```

    X-API-KEY: sk_live_your_api_key_here

    ```


    For legacy compatibility, the `token` header is also supported. ## Rate Limits


    Three independent limits apply, each with its own headers and error code. Headers are returned on `429` responses, not on successful ones. **Per-minute burst** - 60/min on trial and Starter, 120 Professional, 300 Scale, 1,000 Enterprise. Error code `rate_limit_exceeded`. - `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (epoch seconds)


    **Sustained hourly ceiling** - 2,000/hour trial, 3,000 Starter, 5,000 Professional, 15,000 Scale, 50,000 Enterprise. Error code `hourly_limit_exceeded`. - `X-RateLimit-Hourly-Limit`, `X-RateLimit-Hourly-Remaining`, `X-RateLimit-Hourly-Reset` (epoch seconds)


    **Monthly request budget** - error code `monthly_budget_exceeded`, headers `X-Monthly-Limit`, `X-Monthly-Used`, `X-Monthly-Remaining`. Every `429` carries `Retry-After` in seconds. On the hourly ceiling this can be up to 3,600, so clients should honour it rather than retrying on a short fixed backoff - a retry sooner than `Retry-After` cannot succeed. ## Errors


    The API uses standard HTTP status codes and returns errors in a consistent format:


    ```json

    {

    "error": {

    "type": "authentication_error",

    "code": "invalid_api_key",

    "message": "Invalid API key provided.",

    "doc_url": "https://docs.frase.io/api/errors#invalid_api_key"

    }

    }

    ```


    ## Changelog


    Breaking changes are listed here, newest first. "Breaking" means a field that was always present with a value can now be `null`, or an existing value now means something different — not that a field was removed or renamed. Additive fields are not listed. ### 2026-08-31 — Fact `sources[]` may omit `url`, and minted facts default to `approved`


    - `GET /briefs/{id}/facts`, `PATCH /briefs/{id}/facts/{factId}` → a `sources[]` entry may now carry only `title`, with no `url` or `domain`. Research names a publisher far more often than it captures a link (793 of 801 plan-evidence items in production), and those names were previously discarded rather than returned. Read `url` defensively. - `GET /briefs/{id}/facts` → `approval` on a newly minted fact is now `approved` rather than `pending`. A client polling for `pending` facts to review will see far fewer; rejection is unchanged and remains permanent. Facts the writer asserted in its own output (`provenance: generated`) are still minted `pending`. ### 2026-08-24 — Content Guard''s hasPublishedUrl is now recoverability, not the stored URL


    - `GET /content/{id}/monitoring` → `hasPublishedUrl`: was a judgment on the STORED URL (false for a relative path even when the page was live). It now answers "could Guard watch this article right now" — the stored URL when absolute, or an address the server recovers (CMS inventory; FraseCMS re-derivation from current settings when a live published post exists). `true` no longer implies the stored URL itself is absolute, and a FraseCMS article published before domain setup flips to `true` when setup finishes, with no re-publish. It remains exactly the condition enabling enforces. - `publishedUrlIssue` is now the RESIDUAL after recovery failed, not a raw-column classification — "not_absolute" can also mean the FraseCMS post is not live or FraseCMS is disabled for the site. - `POST /content/{id}/publish` → `url` (frase_cms): now derived from the site''s current CMS settings rather than the adapter''s host-of-the-moment value; the same URL is stored on the content row and sent in the content.published webhook.'
  contact:
    name: Frase Support
    url: https://frase.io
    email: support@frase.io
  license:
    name: Proprietary
    url: https://www.frase.io/company/terms
servers:
- url: https://next.frase.io/api/v1
  description: Production
- url: /api/v1
  description: This host
security:
- ApiKeyAuth: []
- BearerAuth: []
tags:
- name: Webhooks
  description: Webhook subscriptions for event notifications
paths:
  /webhooks:
    get:
      operationId: listWebhooks
      summary: List webhooks
      description: List all webhook subscriptions.
      tags:
      - Webhooks
      parameters:
      - name: page
        in: query
        schema:
          type: integer
          default: 1
      - name: page_size
        in: query
        schema:
          type: integer
          default: 20
      responses:
        '200':
          description: List of webhooks
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        name:
                          type: string
                        url:
                          type: string
                        events:
                          type: array
                          items:
                            type: string
                        is_active:
                          type: boolean
                        stats:
                          type: object
                          properties:
                            success_count:
                              type: integer
                            failure_count:
                              type: integer
                            last_delivery_at:
                              type: string
                              format: date-time
                              nullable: true
                            last_success_at:
                              type: string
                              format: date-time
                              nullable: true
                            last_failure_at:
                              type: string
                              format: date-time
                              nullable: true
                            last_failure_reason:
                              type: string
                              nullable: true
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                          nullable: true
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                  request_id:
                    type: string
    post:
      operationId: createWebhook
      summary: Create webhook
      description: 'Create a new webhook subscription. The secret is only returned once.


        ## Delivery


        Every delivery is an HTTPS `POST` to your URL (port 443, public address, no redirects) with `Content-Type: application/json` and these headers:


        - `X-Webhook-Event`: the event name, e.g. `content.ready`. The same name is also in the body as `event`, so it is covered by the signature; prefer the body value.

        - `X-Webhook-Id`: stable per event. Every retry of one event to one webhook carries the same value, so drop any delivery whose id you have already processed.

        - `X-Webhook-Timestamp`: unix seconds

        - `X-Webhook-Signature`: `v1=." using the webhook secret>`


        Any non-2xx response is retried up to three more times. Ten consecutive failed events pause the subscription.


        ### 2026-10-15 — Citation Engine approval lifecycle v2


        BREAKING for Citation Engine webhook consumers: Citation Engine payloads now carry `schemaVersion: 2`. Staff approval emits `content.ready` (`stage: ready`, `readyAt`). `content.approved` (`stage: approved`, `approvedAt`) means every assigned client approved this exact revision, or staff explicitly approved it under current account-wide delegation. It no longer means staff review alone. Unmanaged content generation events are unchanged.


        Before rollout, subscribe to `content.ready` for review intake; retain `content.approved` for delivery automation. Do not publish a `content.ready` document automatically. Read `approvals.mode`, revision fingerprint/version, authenticated document links and `nextAction`. Assign client approvers in each account''s existing Delivery → Client profile configuration, or explicitly grant staff delegation there. Empty approver lists require configuration and do not imply consent.


        Already delivered revisions retain their receipts. Undelivered historical approvals require explicit client consent or a fresh staff approval after delegation; they are never silently backfilled as delegated. Legacy reviewed fixes can continue into the checked review flow without re-generating their accepted text. Event names retain their subscription and signature envelope; only Citation Engine payload/approval semantics are versioned. See the Citation approval rollout guide in the repository for the operator preflight and recovery procedure.'
      tags:
      - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              - url
              - events
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 100
                url:
                  type: string
                  format: uri
                  description: Must be HTTPS
                events:
                  type: array
                  items:
                    type: string
                    enum:
                    - content.ready
                    - content.approved
                    - content.published
                    - brief.ready
                    - research.completed
                    - audit.completed
                    - optimization.ready
                    - ai_visibility.alert
                    - performance.alert
                    - atomization.ready
                is_active:
                  type: boolean
                  default: true
      responses:
        '201':
          description: Webhook created with secret
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookWithSecret'
  /webhooks/{id}:
    get:
      operationId: getWebhook
      summary: Get webhook
      description: Get webhook details (secret is not included).
      tags:
      - Webhooks
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Webhook details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      name:
                        type: string
                      url:
                        type: string
                      events:
                        type: array
                        items:
                          type: string
                      is_active:
                        type: boolean
                      config:
                        type: object
                        properties:
                          max_retries:
                            type: integer
                          retry_delay_seconds:
                            type: integer
                      stats:
                        type: object
                        properties:
                          success_count:
                            type: integer
                          failure_count:
                            type: integer
                          last_delivery_at:
                            type: string
                            format: date-time
                            nullable: true
                          last_success_at:
                            type: string
                            format: date-time
                            nullable: true
                          last_failure_at:
                            type: string
                            format: date-time
                            nullable: true
                          last_failure_reason:
                            type: string
                            nullable: true
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                        nullable: true
                  request_id:
                    type: string
    patch:
      operationId: updateWebhook
      summary: Update webhook
      description: Update webhook settings.
      tags:
      - Webhooks
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                is_active:
                  type: boolean
      responses:
        '200':
          description: Webhook updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      name:
                        type: string
                      url:
                        type: string
                      events:
                        type: array
                        items:
                          type: string
                      is_active:
                        type: boolean
                      updated_at:
                        type: string
                        format: date-time
                      message:
                        type: string
                      warnings:
                        type: array
                        items:
                          type: string
                  request_id:
                    type: string
    delete:
      operationId: deleteWebhook
      summary: Delete webhook
      description: Delete a webhook subscription.
      tags:
      - Webhooks
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Webhook deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      name:
                        type: string
                      deleted:
                        type: boolean
                      message:
                        type: string
                  request_id:
                    type: string
components:
  schemas:
    WebhookWithSecret:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        url:
          type: string
        events:
          type: array
          items:
            type: string
        is_active:
          type: boolean
        secret:
          type: string
          description: Only returned on creation. Store securely.
        created_at:
          type: string
          format: date-time
    Pagination:
      type: object
      properties:
        page:
          type: integer
        page_size:
          type: integer
        total:
          type: integer
        has_more:
          type: boolean
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-KEY
      description: 'API key for authentication. Get your key from Settings > API Keys. You may also send the same key as `Authorization: Bearer <key>` — see BearerAuth.'
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Frase API key (sk_live_* / sk_test_*) or MCP access token (mcp_at_*)
      description: 'Send either your API key or an MCP access token as `Authorization: Bearer <token>`. An API key sent this way is exactly equivalent to sending it in X-API-KEY — same identity, same scopes, same rate limits — so use whichever your HTTP client makes natural. X-API-KEY takes precedence if you somehow send both. Cookie/session auth is intentionally NOT supported on /api/v1/* endpoints.'