---
title: "Policy Scan Auto Fix Automation"
method: POST
path: "/v1/ai/policy_scan_auto_fix_automation"
tags: ["ai"]
---

# Policy Scan Auto Fix Automation

`POST /v1/ai/policy_scan_auto_fix_automation`

## Request

- Base URL: `https://api.linktodm.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.linktodm.com/v1/ai/policy_scan_auto_fix_automation`
- Auth: HTTP bearer

Auto-fix policy violations on a draft AutomationPayload.

Backend rewrites flagged content + applies static fixes, then
re-scans. If still flagged, retries up to MAX_FIX_ATTEMPTS times
until verdict=allow OR the model stops making progress. The
response carries `attempts` and `verified_clean` so the UI knows
whether the result was self-verified or hit the cap.

## Query parameters

- `exclude_automation_id` integer, nullable: When fixing an EDIT, pass the current automation id so the post-fix re-scan's keyword-reuse + content-reuse checks don't flag the automation's own keywords/text against itself.

## Request body

- PolicyFixAutomationPayload: Body for POST /api/v1/ai/policy_scan_auto_fix_automation. Carries the full draft AutomationPayload + the latest scan result so the backend knows exactly which fields to rewrite and why. Typed end to end — FastAPI rejects malformed bodies at the boundary instead of surfacing a KeyError mid-fix.
  - `payload` AutomationPayload, required
    - `name` string, required
    - `type` 'Auto DM Link' | 'Auto DM Products' | 'Button Text' | 'Attachment', required: Stub schema.
    - `facebook_account_id` string, nullable
    - `instagram_business_account_id` string, nullable
    - `instagram_user_id` string, nullable
    - `media_type` 'IMAGE' | 'VIDEO' | 'CAROUSEL_ALBUM'
    - `media_url` string, nullable
    - `media_id` string, nullable
    - `keywords` string[], nullable
    - `dm_message` string, nullable
    - `attachment_id` integer, nullable
    - `disable_auto_reply` boolean, nullable
    - `auto_reply_messages` string[], nullable
    - `auto_reply_mode` 'MANUAL' | 'AI'
    - `auto_reply_ai_prompt` string, nullable
    - `auto_reply_limit` integer, nullable
    - `product_links` ProductLink[], nullable
      - `url` string, required
      - `image_url` string, nullable
      - `title` string, nullable
      - `url_label` string, nullable
      - `addition_buttons` AdditionButtonSchema[], nullable
        - `url` string, required
        - `url_label` string, nullable
    - `addition_buttons` AdditionButtonSchema[], nullable
      - `url` string, required
      - `url_label` string, nullable
    - `dm_quick_replies` QuickReplySchema[], nullable
      - `id` string
      - `label` string, required
      - `action` 'SEND_MESSAGE' | 'CONNECT_AUTOMATION' | 'NOTHING', required: What tapping a quick-reply chip does.
      - `message_type` 'Auto DM Link' | 'Auto DM Products' | 'Button Text' | 'Attachment': Stub schema.
      - `dm_message` string, nullable
      - `automation_id` integer, nullable
      - `flow_automation_id` integer, nullable
    - `ask_to_follow` boolean, nullable
    - `extra_data` AutomationExtraData
      - `ask_to_follow_title` string, nullable
      - `ask_to_follow_button_label` string, nullable
      - `ask_to_follow_error_title` string, nullable
      - `ask_to_follow_error_button_label` string, nullable
      - `ask_to_follow_visit_profile_button_label` string, nullable
      - `opener_message_type` 'Button Text' | 'Quick Reply Text'
      - `opener_message` string, nullable
      - `opener_message_button_label` string, nullable
      - `opener_message_additional_buttons` AdditionButtonSchema[], nullable
        - `url` string, required
        - `url_label` string, nullable
    - `step_delays` AutomationStepDelays: Optional per-step delays. A missing/None entry means the step runs immediately after the previous one. Each delay is relative to the previous step, so the chain is: trigger -> comment -> opener -> ask_to_follow -> dm.
      - `comment` StepDelay: Wait applied before a step runs, relative to the previous step.
        - `delay_in_seconds` integer, required
        - `delay_unit` 'SECONDS' | 'MINUTES' | 'HOURS' | 'DAYS': Unit a user entered a wait duration in. Shared by the legacy automation step delays (`StepDelay.delay_unit`) and the flow builder's DELAY node / comment auto-reply delay. The backend always schedules with the canonical seconds value; this is what the UI round-trips so the editor can show the delay the way it was typed.
      - `opener` StepDelay: Wait applied before a step runs, relative to the previous step.
        - `delay_in_seconds` integer, required
        - `delay_unit` 'SECONDS' | 'MINUTES' | 'HOURS' | 'DAYS': Unit a user entered a wait duration in. Shared by the legacy automation step delays (`StepDelay.delay_unit`) and the flow builder's DELAY node / comment auto-reply delay. The backend always schedules with the canonical seconds value; this is what the UI round-trips so the editor can show the delay the way it was typed.
      - `ask_to_follow` StepDelay: Wait applied before a step runs, relative to the previous step.
        - `delay_in_seconds` integer, required
        - `delay_unit` 'SECONDS' | 'MINUTES' | 'HOURS' | 'DAYS': Unit a user entered a wait duration in. Shared by the legacy automation step delays (`StepDelay.delay_unit`) and the flow builder's DELAY node / comment auto-reply delay. The backend always schedules with the canonical seconds value; this is what the UI round-trips so the editor can show the delay the way it was typed.
      - `dm` StepDelay: Wait applied before a step runs, relative to the previous step.
        - `delay_in_seconds` integer, required
        - `delay_unit` 'SECONDS' | 'MINUTES' | 'HOURS' | 'DAYS': Unit a user entered a wait duration in. Shared by the legacy automation step delays (`StepDelay.delay_unit`) and the flow builder's DELAY node / comment auto-reply delay. The backend always schedules with the canonical seconds value; this is what the UI round-trips so the editor can show the delay the way it was typed.
    - `is_active` boolean
    - `is_draft` boolean
    - `is_story` boolean, nullable
    - `is_universal` boolean, nullable
    - `trigger_type` 'STORY_MENTION' | 'CURRENT_MEDIA' | 'AD_MEDIA'
    - `disable_url_tracking` boolean, nullable
    - `disable_dm` boolean, nullable
    - `one_dm_per_user` boolean, nullable
    - `one_dm_per_user_window_hours` integer, nullable
    - `share_media_for_dm` boolean
    - `enable_opener_message` boolean, nullable
    - `keyword_trigger_type` 'ANY' | 'EXACT_MATCH' | 'EXCLUDE' | 'AI_INTENT'
    - `media_source` 'INSTAGRAM' | 'FACEBOOK'
    - `trigger_ai_prompt` string, nullable
    - `use_next_post` boolean, nullable
    - `use_next_story` boolean, nullable
    - `use_rewind` boolean, nullable
    - `follow_up_messages` FollowUpMessageSchema[], nullable
      - `follow_up_type` 'IF_NOT_CLICKED_LINK' | 'IF_CLICKED_LINK' | 'IF_NO_READ_MESSAGE' | 'IF_UNREACTED_TO_MESSAGE' | 'IF_NO_REPLY' | 'IF_REPLIED_TO_MESSAGE' | 'IF_SEEN_MESSAGE' | 'IF_UNSEEN_MESSAGE' | 'IF_REACTED_TO_MESSAGE_WITH_EMOJI' | 'IF_USER_FOLLOWED_ME' | 'IF_USER_UNFOLLOWED_ME' | 'ALWAYS', required
      - `message_type` 'TEXT' | 'IMAGE_BUTTON' | 'BUTTON', required
      - `dm_message` string, nullable
      - `delay_in_seconds` integer, nullable
      - `reaction_emoji` string, nullable
    - `collect_info_items` CollectInfoItem[], nullable
      - `message` string, required
      - `error_message` string, nullable
      - `type` 'EMAIL' | 'PHONE' | 'ANY', required
    - `dm_variations` AutomationDmVariationSchema[], nullable
      - `dm_message` string, nullable
      - `product_links` ProductLink[], nullable
        - `url` string, required
        - `image_url` string, nullable
        - `title` string, nullable
        - `url_label` string, nullable
        - `addition_buttons` AdditionButtonSchema[], nullable
          - `url` string, required
          - `url_label` string, nullable
      - `addition_buttons` AdditionButtonSchema[], nullable
        - `url` string, required
        - `url_label` string, nullable
      - `attachment_id` integer, nullable
      - `attachment_data` AttachmentItem
        - `id` integer, required
        - `meta_attachment_id` string, required
        - `file_url` string, required
        - `type` 'image' | 'video' | 'audio' | 'file', required
        - `filename` string, nullable
        - `content_type` string, nullable
        - `file_size` integer, nullable
        - `created_at` string, date-time, required
  - `scan_result` PolicyScanResponse, required: Response body for the policy_scan_automation / policy_scan_flow_automation endpoints. Pre-creation scans, so no automation_id is present. Note: model name is intentionally NOT exposed in the response. It's captured in Axiom logs for ops/cost monitoring but kept private — callers shouldn't depend on it.
    - `automation_type` 'automation' | 'flow_automation', required
    - `scanned_at` string, required
    - `policy_version` string, required
    - `verdict` 'allow' | 'warn' | 'block', required
    - `verdict_message` string, required
    - `verdict_category` string, nullable
    - `totals` ScanTotals, required
      - `items_total` integer, required
      - `items_scanned` integer, required
      - `items_failed` integer, required
      - `items_with_violations` integer, required
    - `highest_severity` 'none' | 'low' | 'medium' | 'high', required
    - `items` ScanItemResult[], required
      - `item_id` string, required
      - `item_type` string, required
      - `content_preview` string, required
      - `status` 'scanned' | 'scan_failed', required
      - `highest_severity` 'none' | 'low' | 'medium' | 'high', required
      - `violations` Violation[]
        - `category` string, required: Key of the Meta policy category. Must match a key from app.api.v1.ai.policy_scan.meta_policies.
        - `severity` 'low' | 'medium' | 'high', required
        - `evidence` string, required: Verbatim quote from the content showing the violation.
        - `explanation` string, required: One-sentence mapping of the evidence to the named category.
        - `policy_url` string, nullable

## Response `200`

Successful Response

- PolicyFixAutomationResponse: Response body for POST /api/v1/ai/policy_scan_auto_fix_automation. Typed payload — frontend can rely on the rewritten data matching the AutomationPayload shape without runtime coercion.
  - `payload` AutomationPayload, required
    - `name` string, required
    - `type` 'Auto DM Link' | 'Auto DM Products' | 'Button Text' | 'Attachment', required: Stub schema.
    - `facebook_account_id` string, nullable
    - `instagram_business_account_id` string, nullable
    - `instagram_user_id` string, nullable
    - `media_type` 'IMAGE' | 'VIDEO' | 'CAROUSEL_ALBUM'
    - `media_url` string, nullable
    - `media_id` string, nullable
    - `keywords` string[], nullable
    - `dm_message` string, nullable
    - `attachment_id` integer, nullable
    - `disable_auto_reply` boolean, nullable
    - `auto_reply_messages` string[], nullable
    - `auto_reply_mode` 'MANUAL' | 'AI'
    - `auto_reply_ai_prompt` string, nullable
    - `auto_reply_limit` integer, nullable
    - `product_links` ProductLink[], nullable
      - `url` string, required
      - `image_url` string, nullable
      - `title` string, nullable
      - `url_label` string, nullable
      - `addition_buttons` AdditionButtonSchema[], nullable
        - `url` string, required
        - `url_label` string, nullable
    - `addition_buttons` AdditionButtonSchema[], nullable
      - `url` string, required
      - `url_label` string, nullable
    - `dm_quick_replies` QuickReplySchema[], nullable
      - `id` string
      - `label` string, required
      - `action` 'SEND_MESSAGE' | 'CONNECT_AUTOMATION' | 'NOTHING', required: What tapping a quick-reply chip does.
      - `message_type` 'Auto DM Link' | 'Auto DM Products' | 'Button Text' | 'Attachment': Stub schema.
      - `dm_message` string, nullable
      - `automation_id` integer, nullable
      - `flow_automation_id` integer, nullable
    - `ask_to_follow` boolean, nullable
    - `extra_data` AutomationExtraData
      - `ask_to_follow_title` string, nullable
      - `ask_to_follow_button_label` string, nullable
      - `ask_to_follow_error_title` string, nullable
      - `ask_to_follow_error_button_label` string, nullable
      - `ask_to_follow_visit_profile_button_label` string, nullable
      - `opener_message_type` 'Button Text' | 'Quick Reply Text'
      - `opener_message` string, nullable
      - `opener_message_button_label` string, nullable
      - `opener_message_additional_buttons` AdditionButtonSchema[], nullable
        - `url` string, required
        - `url_label` string, nullable
    - `step_delays` AutomationStepDelays: Optional per-step delays. A missing/None entry means the step runs immediately after the previous one. Each delay is relative to the previous step, so the chain is: trigger -> comment -> opener -> ask_to_follow -> dm.
      - `comment` StepDelay: Wait applied before a step runs, relative to the previous step.
        - `delay_in_seconds` integer, required
        - `delay_unit` 'SECONDS' | 'MINUTES' | 'HOURS' | 'DAYS': Unit a user entered a wait duration in. Shared by the legacy automation step delays (`StepDelay.delay_unit`) and the flow builder's DELAY node / comment auto-reply delay. The backend always schedules with the canonical seconds value; this is what the UI round-trips so the editor can show the delay the way it was typed.
      - `opener` StepDelay: Wait applied before a step runs, relative to the previous step.
        - `delay_in_seconds` integer, required
        - `delay_unit` 'SECONDS' | 'MINUTES' | 'HOURS' | 'DAYS': Unit a user entered a wait duration in. Shared by the legacy automation step delays (`StepDelay.delay_unit`) and the flow builder's DELAY node / comment auto-reply delay. The backend always schedules with the canonical seconds value; this is what the UI round-trips so the editor can show the delay the way it was typed.
      - `ask_to_follow` StepDelay: Wait applied before a step runs, relative to the previous step.
        - `delay_in_seconds` integer, required
        - `delay_unit` 'SECONDS' | 'MINUTES' | 'HOURS' | 'DAYS': Unit a user entered a wait duration in. Shared by the legacy automation step delays (`StepDelay.delay_unit`) and the flow builder's DELAY node / comment auto-reply delay. The backend always schedules with the canonical seconds value; this is what the UI round-trips so the editor can show the delay the way it was typed.
      - `dm` StepDelay: Wait applied before a step runs, relative to the previous step.
        - `delay_in_seconds` integer, required
        - `delay_unit` 'SECONDS' | 'MINUTES' | 'HOURS' | 'DAYS': Unit a user entered a wait duration in. Shared by the legacy automation step delays (`StepDelay.delay_unit`) and the flow builder's DELAY node / comment auto-reply delay. The backend always schedules with the canonical seconds value; this is what the UI round-trips so the editor can show the delay the way it was typed.
    - `is_active` boolean
    - `is_draft` boolean
    - `is_story` boolean, nullable
    - `is_universal` boolean, nullable
    - `trigger_type` 'STORY_MENTION' | 'CURRENT_MEDIA' | 'AD_MEDIA'
    - `disable_url_tracking` boolean, nullable
    - `disable_dm` boolean, nullable
    - `one_dm_per_user` boolean, nullable
    - `one_dm_per_user_window_hours` integer, nullable
    - `share_media_for_dm` boolean
    - `enable_opener_message` boolean, nullable
    - `keyword_trigger_type` 'ANY' | 'EXACT_MATCH' | 'EXCLUDE' | 'AI_INTENT'
    - `media_source` 'INSTAGRAM' | 'FACEBOOK'
    - `trigger_ai_prompt` string, nullable
    - `use_next_post` boolean, nullable
    - `use_next_story` boolean, nullable
    - `use_rewind` boolean, nullable
    - `follow_up_messages` FollowUpMessageSchema[], nullable
      - `follow_up_type` 'IF_NOT_CLICKED_LINK' | 'IF_CLICKED_LINK' | 'IF_NO_READ_MESSAGE' | 'IF_UNREACTED_TO_MESSAGE' | 'IF_NO_REPLY' | 'IF_REPLIED_TO_MESSAGE' | 'IF_SEEN_MESSAGE' | 'IF_UNSEEN_MESSAGE' | 'IF_REACTED_TO_MESSAGE_WITH_EMOJI' | 'IF_USER_FOLLOWED_ME' | 'IF_USER_UNFOLLOWED_ME' | 'ALWAYS', required
      - `message_type` 'TEXT' | 'IMAGE_BUTTON' | 'BUTTON', required
      - `dm_message` string, nullable
      - `delay_in_seconds` integer, nullable
      - `reaction_emoji` string, nullable
    - `collect_info_items` CollectInfoItem[], nullable
      - `message` string, required
      - `error_message` string, nullable
      - `type` 'EMAIL' | 'PHONE' | 'ANY', required
    - `dm_variations` AutomationDmVariationSchema[], nullable
      - `dm_message` string, nullable
      - `product_links` ProductLink[], nullable
        - `url` string, required
        - `image_url` string, nullable
        - `title` string, nullable
        - `url_label` string, nullable
        - `addition_buttons` AdditionButtonSchema[], nullable
          - `url` string, required
          - `url_label` string, nullable
      - `addition_buttons` AdditionButtonSchema[], nullable
        - `url` string, required
        - `url_label` string, nullable
      - `attachment_id` integer, nullable
      - `attachment_data` AttachmentItem
        - `id` integer, required
        - `meta_attachment_id` string, required
        - `file_url` string, required
        - `type` 'image' | 'video' | 'audio' | 'file', required
        - `filename` string, nullable
        - `content_type` string, nullable
        - `file_size` integer, nullable
        - `created_at` string, date-time, required
  - `changes` RawFix[], required
    - `item_id` string, required
    - `action` 'rewrite' | 'remove', required
    - `new_content` string, nullable
    - `rationale` string, nullable
  - `static_fixes_applied` string[]: Deterministic fixes applied without LLM (e.g. capping auto_reply_limit, generating extra variations). Each entry is a human-readable summary.
  - `attempts` integer: How many fix → re-scan cycles the service ran before returning. 1 = first attempt produced a clean result. Higher values mean the model needed more rounds; if it equals MAX_FIX_ATTEMPTS the response may still contain unresolved violations (see static_fixes_applied for the 'reached max' note).
  - `verified_clean` boolean: True if the final re-scan returned verdict=allow. False means we hit the attempt cap with violations still present — the frontend should surface a manual-review hint to the user.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/linktodm/apis/salesmorph.md) - [All operations](https://skmtc.dev/linktodm/apis/salesmorph/llms.txt) - [OpenAPI document](https://skmtc.dev/linktodm/apis/salesmorph/revisions/4af0ad5dbe92?raw)
