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

# Create Monitor

> Create a monitoring alert for your campaigns. Checked daily.

**Supported metrics:** roas, ctr, cpc, cpa, cpm, cpv, spend, conversions, impressions, clicks, cost_per_lead, conversion_rate, budget_utilization, video_views, engagement_rate

**Operators:** less_than, greater_than, less_than_or_equal, greater_than_or_equal, changes_by

**Advanced features:**
- **Multiple conditions with AND/OR:** Use `conditions` array with `conditions_logic: "AND"` or `"OR"`
- **Consecutive days:** Add `consecutive_days: 3` to only trigger after 3 days in a row
- **Relative thresholds:** Set `threshold_type: "relative"` with `threshold_multiplier: 1.5` to mean "50% above average"
- **% change detection:** Use `operator: "changes_by"` with `direction: "decrease"` and `threshold: 30` for "dropped 30%"
- **Campaign targeting:** Use `campaign_ids` to monitor specific campaigns only
- **Auto-actions (coming soon):** `auto_action: "pause_campaign"` or `"increase_budget"` with `auto_action_value: 20`

**Examples:**

1. Simple: "Alert me if ROAS drops below 2"
→ metric: "roas", operator: "less_than", threshold: 2.0

2. Consecutive days: "Alert if CPA exceeds $50 for 3 days straight"
→ metric: "cpa", operator: "greater_than", threshold: 50, consecutive_days: 3

3. Compound AND: "Alert if CTR `<` 0.8% AND spend > $200"
→ conditions: [
    `{metric: "ctr", operator: "less_than", threshold: 0.8}`,
    `{metric: "spend", operator: "greater_than", threshold: 200}`
  ], conditions_logic: "AND"

4. Relative: "Alert if CPA goes 50% above my 30-day average"
→ metric: "cpa", operator: "greater_than", threshold_type: "relative", threshold_multiplier: 1.5, threshold_timeframe: "last_30d"

5. % drop: "Notify me if cost per lead drops 30% in a day"
→ metric: "cost_per_lead", operator: "changes_by", threshold: 30, direction: "decrease", timeframe: "vs_previous_day"

**IMPORTANT:**
- If user doesn't specify an email for alerts, ASK them. Do not guess.
- If user doesn't specify platforms, default to all their connected platforms.
- If user doesn't specify campaigns, default to all campaigns.
- Do not assume timeframe — ask if unclear.
- Do not assume threshold values — ask if the user doesn't specify a number.
- ROAS monitors are skipped on traffic/awareness/engagement campaigns automatically.
- CPA monitors are skipped on campaigns with zero conversions automatically.



## OpenAPI

````yaml /api-reference/openapi.json post /api/v1/tools/create_monitor/execute
openapi: 3.1.0
info:
  contact:
    email: support@adspirer.com
    name: Adspirer Support
  description: >-
    REST endpoints for every Adspirer tool. Same surface as the MCP server, over
    plain HTTP for consumers that can't speak SSE (n8n, Zapier, Make, curl, any
    language's HTTP client).


    ## Envelope

    Every request wraps tool-specific input in an `arguments` object:

    ```json

    { "arguments": { <tool-specific fields> } }

    ```

    Every response wraps the result in either a success envelope (`success:
    true`, `data: {...}`) or an error envelope (`success: false`, `error:
    "..."`, `is_error: true`).


    ## Authentication

    Pass your API key as `Authorization: Bearer sk_live_...` on every request.
    Generate keys at https://adspirer.ai/keys.


    ## Quota & billing

    Every successful billable call decrements your monthly tool-call allowance.
    The current counter is attached to every 200 response under `data.quota`:

    ```json

    "quota": { "used": 42, "limit": 150, "tier": "plus", "period_end":
    "2026-05-01" }

    ```

    When the limit is hit, the API returns HTTP 402 with a full `quota` block
    including `upgrade_url`. Read-only diagnostic tools (`get_usage_status`,
    `list_connected_accounts`, `get_connections_status`) are exempt and never
    consume quota.


    ## Idempotency

    Write operations accept an `Idempotency-Key: <uuid>` header. A repeated call
    with the same key returns the cached result rather than executing twice.
    **Strongly recommended for n8n, Zapier, and any retry-prone client** — it
    prevents duplicate campaigns when networks misbehave. Generate a fresh UUID
    per logical operation (not per retry).


    ## Multi-account users

    Customers with multiple connected accounts on the same platform (e.g. an
    agency with 10 Meta ad accounts) must specify which account to use via
    `ad_account_id`, `customer_id`, `advertiser_id`, or `account_id` depending
    on the platform. Omitting it returns HTTP 400 with a list of valid account
    IDs. See `list_connected_accounts` to discover available IDs.


    ## HTTP status codes

    - `200` — success (parse `data`)

    - `400` — tool-level error (surface `error` to users)

    - `401` — bad/missing API key

    - `402` — Adspirer quota exhausted

    - `404` — unknown tool name

    - `429` — upstream ad platform rate-limited us (Meta/Google/etc.) — retry
    with backoff

    - `500` — server error, report to support


    ## Streaming

    This endpoint is plain request-response JSON. There is **no SSE, no chunked
    streaming**. Safe to use from n8n Cloud's HTTP Request node, Zapier
    Webhooks, Make HTTP module, curl, and every mainstream HTTP library.
  title: Adspirer REST API
  version: 1.0.0
servers:
  - description: Production
    url: https://api.adspirer.ai
security: []
tags:
  - description: Audit tools
    name: audit
  - description: General (Account Management) tools
    name: general
  - description: Google Ads tools
    name: google-ads
  - description: LinkedIn Ads tools
    name: linkedin-ads
  - description: Meta Ads tools
    name: meta-ads
  - description: Monitoring & Reporting tools
    name: monitoring
  - description: TikTok Ads tools
    name: tiktok-ads
paths:
  /api/v1/tools/create_monitor/execute:
    post:
      tags:
        - monitoring
      summary: Create Monitor
      description: >-
        Create a monitoring alert for your campaigns. Checked daily.


        **Supported metrics:** roas, ctr, cpc, cpa, cpm, cpv, spend,
        conversions, impressions, clicks, cost_per_lead, conversion_rate,
        budget_utilization, video_views, engagement_rate


        **Operators:** less_than, greater_than, less_than_or_equal,
        greater_than_or_equal, changes_by


        **Advanced features:**

        - **Multiple conditions with AND/OR:** Use `conditions` array with
        `conditions_logic: "AND"` or `"OR"`

        - **Consecutive days:** Add `consecutive_days: 3` to only trigger after
        3 days in a row

        - **Relative thresholds:** Set `threshold_type: "relative"` with
        `threshold_multiplier: 1.5` to mean "50% above average"

        - **% change detection:** Use `operator: "changes_by"` with `direction:
        "decrease"` and `threshold: 30` for "dropped 30%"

        - **Campaign targeting:** Use `campaign_ids` to monitor specific
        campaigns only

        - **Auto-actions (coming soon):** `auto_action: "pause_campaign"` or
        `"increase_budget"` with `auto_action_value: 20`


        **Examples:**


        1. Simple: "Alert me if ROAS drops below 2"

        → metric: "roas", operator: "less_than", threshold: 2.0


        2. Consecutive days: "Alert if CPA exceeds $50 for 3 days straight"

        → metric: "cpa", operator: "greater_than", threshold: 50,
        consecutive_days: 3


        3. Compound AND: "Alert if CTR `<` 0.8% AND spend > $200"

        → conditions: [
            `{metric: "ctr", operator: "less_than", threshold: 0.8}`,
            `{metric: "spend", operator: "greater_than", threshold: 200}`
          ], conditions_logic: "AND"

        4. Relative: "Alert if CPA goes 50% above my 30-day average"

        → metric: "cpa", operator: "greater_than", threshold_type: "relative",
        threshold_multiplier: 1.5, threshold_timeframe: "last_30d"


        5. % drop: "Notify me if cost per lead drops 30% in a day"

        → metric: "cost_per_lead", operator: "changes_by", threshold: 30,
        direction: "decrease", timeframe: "vs_previous_day"


        **IMPORTANT:**

        - If user doesn't specify an email for alerts, ASK them. Do not guess.

        - If user doesn't specify platforms, default to all their connected
        platforms.

        - If user doesn't specify campaigns, default to all campaigns.

        - Do not assume timeframe — ask if unclear.

        - Do not assume threshold values — ask if the user doesn't specify a
        number.

        - ROAS monitors are skipped on traffic/awareness/engagement campaigns
        automatically.

        - CPA monitors are skipped on campaigns with zero conversions
        automatically.
      operationId: execute_create_monitor
      parameters:
        - description: >-
            Client-generated UUID to make writes idempotent. Strongly
            recommended for write tools. A repeat call with the same key returns
            the cached result instead of re-executing. Example:
            550e8400-e29b-41d4-a716-446655440000
          example: 550e8400-e29b-41d4-a716-446655440000
          in: header
          name: Idempotency-Key
          required: false
          schema:
            format: uuid
            type: string
      requestBody:
        content:
          application/json:
            example:
              arguments:
                conditions:
                  - consecutive_days: 1
                    direction: string
                    metric: string
                    operator: string
                    threshold: 1
                    threshold_multiplier: 1
                    threshold_timeframe: string
                    threshold_type: string
                    timeframe: string
                conditions_logic: AND
                metric: string
                monitor_type: performance_drop
                name: string
                operator: string
                threshold: 1
            schema:
              properties:
                arguments:
                  description: Input for creating a monitoring alert.
                  properties:
                    alert_destination:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: null
                      description: >-
                        Where to send alerts: email address, Slack webhook URL,
                        etc. If not provided, uses the user's account email.
                      title: Alert Destination
                    alert_method:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: email
                      description: >-
                        How to notify: 'email', 'slack', 'webhook'. Default:
                        email.
                      title: Alert Method
                    auto_action:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: null
                      description: >-
                        Automatic action when triggered: 'pause_campaign',
                        'increase_budget', 'decrease_budget', 'notify_only'.
                        Default: notify_only (no automatic changes).
                      title: Auto Action
                    auto_action_value:
                      anyOf:
                        - type: number
                        - type: 'null'
                      default: null
                      description: >-
                        Value for auto-action: percentage for budget changes
                        (e.g., 20 for +20%), ignored for pause/notify.
                      title: Auto Action Value
                    campaign_ids:
                      anyOf:
                        - items:
                            type: string
                          type: array
                        - type: 'null'
                      default: null
                      description: >-
                        Specific campaign IDs to monitor. Default: all
                        campaigns. Get IDs from
                        list_campaigns/list_meta_campaigns tools.
                      title: Campaign Ids
                    check_frequency:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: daily
                      description: >-
                        How often to check: 'daily' — checks once per day using
                        data through yesterday. This is the only available
                        frequency currently.
                      title: Check Frequency
                    conditions:
                      anyOf:
                        - items:
                            $ref: >-
                              #/components/schemas/execute_create_monitor_MonitorConditionInput
                          type: array
                        - type: 'null'
                      default: null
                      description: >-
                        List of conditions. Multiple conditions are evaluated
                        with AND logic by default. For OR logic, set
                        conditions_logic='OR'. If not provided, use the
                        single-condition fields (metric, operator, threshold)
                        below.
                      title: Conditions
                    conditions_logic:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: AND
                      description: >-
                        How multiple conditions combine: 'AND' (all must be
                        true) or 'OR' (any must be true). Default: 'AND'.
                      title: Conditions Logic
                    consecutive_days:
                      anyOf:
                        - type: integer
                        - type: 'null'
                      default: null
                      description: Single condition consecutive days (shorthand).
                      title: Consecutive Days
                    metric:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: null
                      description: >-
                        Single condition metric (shorthand). Use 'conditions'
                        array for multiple conditions.
                      title: Metric
                    monitor_type:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: performance_drop
                      description: >-
                        Type: 'performance_drop', 'budget_pace',
                        'anomaly_detection', 'spend_threshold',
                        'conversion_tracking'
                      title: Monitor Type
                    name:
                      description: >-
                        Name for this alert (e.g., 'High CPA Auto-Pause', 'ROAS
                        Scaling Rule')
                      title: Name
                      type: string
                    operator:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: null
                      description: Single condition operator (shorthand).
                      title: Operator
                    platforms:
                      anyOf:
                        - items:
                            type: string
                          type: array
                        - type: 'null'
                      default: null
                      description: >-
                        Platforms to monitor: ['google_ads'], ['meta_ads'],
                        ['google_ads', 'meta_ads'], etc. Default: all connected
                        platforms.
                      title: Platforms
                    scope_level:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: campaign
                      description: >-
                        What level to monitor: 'campaign' (default), 'ad_set'
                        (ad group level), or 'ad' (individual ad/creative
                        level).
                      title: Scope Level
                    threshold:
                      anyOf:
                        - type: number
                        - type: 'null'
                      default: null
                      description: Single condition threshold (shorthand).
                      title: Threshold
                    timeframe:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: null
                      description: Single condition timeframe (shorthand).
                      title: Timeframe
                  required:
                    - name
                  type: object
              required:
                - arguments
              type: object
        description: >-
          All tool arguments are wrapped in an `arguments` object. The fields
          accepted inside `arguments` are listed below — required fields are
          marked with a red asterisk.
        required: true
      responses:
        '200':
          content:
            application/json:
              example:
                data:
                  quota:
                    limit: 150
                    period_end: '2026-05-01'
                    tier: plus
                    used: 42
                  text: (tool-specific textual output for create_monitor)
                success: true
                tool: create_monitor
              schema:
                $ref: '#/components/schemas/SuccessResponse'
          description: >-
            Tool executed successfully. `data.text` carries the human-readable
            result (markdown-friendly). `data.quota` shows your current usage
            against the plan limit. `data.structured` appears when the tool
            emits machine-parseable structured content. `data.content` appears
            for tools that return non-text blocks (images, resources).
        '400':
          content:
            application/json:
              example:
                error: >-
                  You have 25 meta_ads accounts connected. Please specify which
                  account to use by passing the ad_account_id parameter:
                    - Acme Holdings (ad_account_id="act_123456789")
                    - Acme EU (ad_account_id="act_987654321")
                is_error: true
                success: false
                tool: create_monitor
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: >-
            Tool-level error. The `error` string is safe to surface to end
            users. Common causes: missing required argument, multi-account user
            didn't specify which account, upstream platform validation failure.
        '401':
          content:
            application/json:
              example:
                error: >-
                  Not authenticated. Please connect your Adspirer account first
                  at https://adspirer.ai
                is_error: true
                success: false
                tool: create_monitor
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Invalid, expired, or revoked API key.
        '402':
          content:
            application/json:
              example:
                error: |-
                  🚨 Monthly limit reached (150/150 tool calls on Plus tier).
                  Upgrade to Pro at https://adspirer.ai to keep building.
                is_error: true
                quota:
                  limit: 150
                  period_end: '2026-05-01'
                  tier: plus
                  upgrade_url: https://adspirer.ai
                  used: 150
                success: false
                tool: create_monitor
              schema:
                $ref: '#/components/schemas/QuotaErrorResponse'
          description: >-
            Quota exhausted for the current billing period. The response
            includes a `quota` block with the current used/limit/tier values and
            an `upgrade_url` to move up a tier.
        '404':
          content:
            application/json:
              example:
                error: 'Tool not found: create_monitor'
                is_error: true
                success: false
                tool: create_monitor
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Unknown tool_name. Check /openapi.json for the full catalog.
        '429':
          content:
            application/json:
              example:
                error: >-
                  Upstream platform rate limit hit (Meta Business Use Case
                  throttle at 95%). Retry after 60 seconds.
                is_error: true
                success: false
                tool: create_monitor
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: >-
            Rate-limited by the upstream ad platform (Meta, Google, LinkedIn,
            TikTok). Retry with exponential backoff. Not the same as Adspirer's
            own quota — that returns 402.
        '500':
          content:
            application/json:
              example:
                error: 'Internal error: RuntimeError'
                is_error: true
                success: false
                tool: create_monitor
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: >-
            Unexpected server error. Report to support@adspirer.com with
            request_id.
      security:
        - ApiKeyAuth: []
components:
  schemas:
    execute_create_monitor_MonitorConditionInput:
      description: A single monitoring condition.
      properties:
        consecutive_days:
          anyOf:
            - type: integer
            - type: 'null'
          default: null
          description: >-
            Only trigger if condition is true for this many consecutive days.
            E.g., 3 means 'true for 3 days in a row'. Min 1, max 30.
          title: Consecutive Days
        direction:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: >-
            For 'changes_by' operator: 'increase', 'decrease', or 'any'. E.g.,
            'decrease' with threshold 30 means 'metric dropped by 30%'.
          title: Direction
        metric:
          description: >-
            Metric to check: 'roas', 'ctr', 'cpc', 'cpa', 'cpm', 'cpv', 'spend',
            'conversions', 'impressions', 'clicks', 'cost_per_lead',
            'conversion_rate', 'budget_utilization', 'video_views',
            'engagement_rate'
          title: Metric
          type: string
        operator:
          description: >-
            Comparison: 'less_than', 'greater_than', 'less_than_or_equal',
            'greater_than_or_equal', 'changes_by'
          title: Operator
          type: string
        threshold:
          anyOf:
            - type: number
            - type: 'null'
          default: null
          description: >-
            Threshold value for static comparisons (e.g., 2.0 for ROAS, 50 for
            CPA). Required unless threshold_type is 'relative'.
          title: Threshold
        threshold_multiplier:
          anyOf:
            - type: number
            - type: 'null'
          default: null
          description: >-
            For relative thresholds: multiplier applied to baseline. E.g., 1.5
            means '50% above baseline'. Required when threshold_type='relative'.
          title: Threshold Multiplier
        threshold_timeframe:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: >-
            For relative thresholds: timeframe for the baseline average. E.g.,
            'last_30d' means compare to 30-day average. Default: 'last_30d'.
          title: Threshold Timeframe
        threshold_type:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: >-
            'static' (default) = compare to fixed threshold value. 'relative' =
            compare to baseline metric × multiplier (e.g., 'CPA > 30-day average
            × 1.5').
          title: Threshold Type
        timeframe:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: >-
            Data window: 'last_24h', 'last_7d', 'last_14d', 'last_30d', 'today',
            'yesterday', 'vs_previous_day'. If not specified, uses the lookback
            period of available data.
          title: Timeframe
      required:
        - metric
        - operator
      title: MonitorConditionInput
      type: object
    SuccessResponse:
      description: >-
        Returned on HTTP 200. `data.text` is the primary human-readable output.
        `data.quota` is always present for billable calls. `data.structured` is
        set only when the tool emits machine-parseable structured content.
        `data.content` is set only when the tool emits non-text content blocks.
      properties:
        data:
          properties:
            content:
              description: Non-text content blocks (images, resources).
              items:
                $ref: '#/components/schemas/ContentBlock'
              type: array
            quota:
              $ref: '#/components/schemas/QuotaBlock'
            structured:
              additionalProperties: true
              description: >-
                Machine-parseable structured content when the tool provides it
                (e.g. account selection prompts, widget payloads).
              type: object
            text:
              description: >-
                Human-readable output. Markdown-friendly for tools that emit
                formatted lists, tables, or recommendations.
              type: string
          required:
            - text
          type: object
        success:
          const: true
          type: boolean
        tool:
          description: Echoed tool_name from the request URL.
          type: string
      required:
        - success
        - data
        - tool
      type: object
    ErrorResponse:
      description: >-
        Returned on HTTP 4xx / 5xx (except 402 which uses `QuotaErrorResponse`).
        `error` is always a human-readable string safe to surface to end users.
      properties:
        error:
          description: Human-readable error message.
          type: string
        is_error:
          const: true
          type: boolean
        structured_content:
          additionalProperties: true
          description: >-
            Present when the underlying tool attached structured metadata to the
            error (e.g. account-selection lists).
          type: object
        success:
          const: false
          type: boolean
        tool:
          type: string
      required:
        - success
        - error
        - tool
      type: object
    QuotaErrorResponse:
      description: >-
        Returned on HTTP 402 when the monthly quota is exhausted. Identical to
        `ErrorResponse` but with an additional `quota` block.
      properties:
        error:
          type: string
        is_error:
          const: true
          type: boolean
        quota:
          $ref: '#/components/schemas/QuotaBlock'
        success:
          const: false
          type: boolean
        tool:
          type: string
      required:
        - success
        - error
        - tool
        - quota
      type: object
    ContentBlock:
      additionalProperties: true
      description: >-
        A non-text content block. Rare today; tools that attach images,
        resources, or widget payloads populate `data.content` with blocks of
        this shape.
      properties:
        type:
          example: image
          type: string
      required:
        - type
      type: object
    QuotaBlock:
      description: >-
        Current quota state for the API key owner. Attached to every successful
        billable response under `data.quota`, and to 402 errors under `quota`
        (plus `upgrade_url`).
      properties:
        limit:
          description: Tool call allowance for the current tier.
          example: 150
          type: integer
        period_end:
          description: When the monthly counter resets (ISO date).
          example: '2026-05-01'
          format: date
          type: string
        tier:
          description: Subscription tier of the account that owns the API key.
          enum:
            - free
            - plus
            - pro
            - max
          example: plus
          type: string
        upgrade_url:
          description: Upgrade link (present only on 402 quota errors).
          format: uri
          type: string
        used:
          description: Tool calls consumed this billing period.
          example: 42
          type: integer
      required:
        - used
        - limit
        - tier
        - period_end
      type: object
  securitySchemes:
    ApiKeyAuth:
      bearerFormat: API Key (sk_live_...)
      description: >-
        API key from https://adspirer.ai/keys. Prefix `sk_live_`. Treat as a
        secret — never commit.
      scheme: bearer
      type: http

````