openapi: 3.2.0 info: title: Plainrouter Ad Test API version: 0.5.0 description: 'Operations tagged AdTestApi across 2 of this provider''s published API definitions: dev-plainrouter-openapi.json, plainrouter-openapi.json. Each path carries the servers of the definition it was published in.' servers: - url: https://plainrouter.com/api/v1 description: Production security: - workspaceSecret: [] tags: - name: AdTestApi paths: /agent/workspaces/{workspace}/tests: get: operationId: actions.tests.api.index description: Lists scoped Tests, their loser pause candidate status and reasons, and observed conversion event choices with last-30-day recorded counts. summary: List Tests and observed conversion event choices tags: - AdTestApi parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: page in: query schema: type: integer minimum: 1 - name: per_page in: query schema: type: integer minimum: 1 maximum: 100 responses: '200': description: Tests and observed conversion event choices. content: application/json: schema: $ref: '#/components/schemas/AdTestListRead' '401': description: An active workspace principal is required; credential refusals include an error code and description. content: application/json: schema: anyOf: - $ref: '#/components/schemas/AgentCredentialError' - $ref: '#/components/schemas/ErrorMessage' '403': description: The workspace principal lacks a matching plan grant or account binding. content: application/json: schema: anyOf: - $ref: '#/components/schemas/AgentCredentialError' - $ref: '#/components/schemas/ErrorMessage' '404': description: The scoped workspace, account, or Test is unavailable, or the API route is not found. content: application/json: schema: anyOf: - $ref: '#/components/schemas/ApiRouteNotFound' - $ref: '#/components/schemas/ErrorMessage' '422': description: Request validation failed, the event is not in the observed list, or selected ads are not members of the ad set. content: application/json: schema: $ref: '#/components/schemas/ValidationError' '429': description: The shared API rate limit was exceeded; Retry-After gives the seconds until another request is allowed. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' headers: Retry-After: description: Seconds until the shared API rate limit allows another request. required: true schema: type: integer security: - testReader: [] post: operationId: actions.tests.api.store description: Creates a judging Test with frozen event, settings, axis, and 2 to 4 ads from one mirrored ad set; it creates no proposal or Meta write. summary: Create a judging Test tags: - AdTestApi parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer requestBody: description: A Test using ads present in the selected mirrored Meta ad set. required: true content: application/json: schema: type: object properties: platform_ad_account_id: type: integer minimum: 1 ad_set_id: type: string pattern: ^[0-9]+$ maxLength: 128 member_ad_ids: type: array description: Two to four distinct Meta ad IDs shown in the selected ad set inventory. items: type: string pattern: ^[0-9]+$ maxLength: 128 minItems: 2 maxItems: 4 uniqueItems: true axis: type: string enum: - hook - angle - visual - offer - format - landing event_name: type: string description: An observed workspace conversion event other than PageView. minLength: 1 maxLength: 255 arrivals_floor: type: integer description: Optional override; default 50 counted arrivals per member. minimum: 50 minimum_conversions: type: integer description: Optional override; default 10 recorded conversions for a winner. minimum: 1 maximum_days: type: integer description: Optional override; default 14 complete account-local days. minimum: 3 maximum: 30 maximum_spend_minor: description: Optional stop limit in positive whole Meta minor units, using the same unit scale as Launch plan budget_amount_minor. anyOf: - type: string description: A positive whole number of Meta minor units in the selected account currency, using the same unit scale as Launch plan budget_amount_minor; the string form supports up to 18 digits. pattern: ^[1-9][0-9]{0,17}$ - type: integer description: A positive whole number of Meta minor units in the selected account currency, using the same unit scale as Launch plan budget_amount_minor. minimum: 1 maximum: 9223372036854775807 - type: 'null' required: - platform_ad_account_id - ad_set_id - member_ad_ids - axis - event_name responses: '201': description: The frozen judging Test. content: application/json: schema: $ref: '#/components/schemas/AdTestCreateRead' '401': description: An active workspace principal is required; credential refusals include an error code and description. content: application/json: schema: anyOf: - $ref: '#/components/schemas/AgentCredentialError' - $ref: '#/components/schemas/ErrorMessage' '403': description: The workspace principal lacks a matching plan grant or account binding. content: application/json: schema: anyOf: - $ref: '#/components/schemas/AgentCredentialError' - $ref: '#/components/schemas/ErrorMessage' '404': description: The scoped workspace, account, or Test is unavailable, or the API route is not found. content: application/json: schema: anyOf: - $ref: '#/components/schemas/ApiRouteNotFound' - $ref: '#/components/schemas/ErrorMessage' '422': description: Request validation failed, the event is not in the observed list, or selected ads are not members of the ad set. content: application/json: schema: $ref: '#/components/schemas/ValidationError' '429': description: The shared API rate limit was exceeded; Retry-After gives the seconds until another request is allowed. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' headers: Retry-After: description: Seconds until the shared API rate limit allows another request. required: true schema: type: integer security: - testWriter: [] servers: - url: https://plainrouter.com/api/v1 description: Production /agent/workspaces/{workspace}/tests/{ad_test}: get: operationId: actions.tests.api.show description: Reads a scoped Test, its stored daily facts and verdict, and loser pause candidate status and reason; recorded counts rank members only within this Test. summary: Read a Test, verdict, and pause candidates tags: - AdTestApi parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: ad_test in: path required: true schema: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 responses: '200': description: The Test and its stored verdict facts. content: application/json: schema: $ref: '#/components/schemas/AdTestShowRead' '401': description: An active workspace principal is required; credential refusals include an error code and description. content: application/json: schema: anyOf: - $ref: '#/components/schemas/AgentCredentialError' - $ref: '#/components/schemas/ErrorMessage' '403': description: The workspace principal lacks a matching plan grant or account binding. content: application/json: schema: anyOf: - $ref: '#/components/schemas/AgentCredentialError' - $ref: '#/components/schemas/ErrorMessage' '404': description: The scoped workspace, account, or Test is unavailable, or the API route is not found. content: application/json: schema: anyOf: - $ref: '#/components/schemas/ApiRouteNotFound' - $ref: '#/components/schemas/ErrorMessage' '429': description: The shared API rate limit was exceeded; Retry-After gives the seconds until another request is allowed. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' headers: Retry-After: description: Seconds until the shared API rate limit allows another request. required: true schema: type: integer security: - testReader: [] servers: - url: https://plainrouter.com/api/v1 description: Production components: schemas: AgentCredentialError: type: object properties: error: type: string enum: - invalid_token - insufficient_scope error_description: type: string required: - error - error_description title: AgentCredentialError AdTestListRead: type: object properties: tests: type: object properties: current_page: type: integer data: type: array items: type: object properties: id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 workspace_id: type: integer platform_ad_account_id: type: integer ad_set_id: type: string pattern: ^[0-9]+$ maxLength: 128 axis: type: string enum: - hook - angle - visual - offer - format - landing event_name: type: string arrivals_floor: type: integer count_health_click_threshold: type: - integer - 'null' description: Link-click threshold frozen at conclusion; null until concluded or for legacy Tests. minimum_conversions: type: integer maximum_days: type: integer maximum_spend_minor: type: - string - 'null' description: Optional maximum in positive whole Meta minor units, using the same unit scale as Launch plan budget_amount_minor. anomaly_threshold_percent: type: string pattern: ^[0-9]+(?:\.[0-9]+)?$ first_day: type: string format: date timezone: type: string currency: type: string pattern: ^[A-Z]{3}$ created_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 position: type: integer baseline_host: type: - string - 'null' description: Immutable normalized landing host once the member has a baseline. baseline_path: type: - string - 'null' description: Immutable normalized landing path once the member has a baseline. required: - ad_id - position pause_proposals: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 status: type: string enum: - pending - proposed - skipped - expired reason: type: - string - 'null' description: Stable reason code when the proposal was skipped or expired. enum: - already_paused - deleted - archived - campaign_paused - adset_paused - decision_window_elapsed - null decision_deadline_at: type: string format: date-time description: Immutable deadline 24 hours after the Test's candidates are created on the database clock. required: - ad_id - status - reason - decision_deadline_at verdict: type: - object - 'null' properties: outcome: type: - string - 'null' enum: - winner - no_clear_winner - not_judged - halted - null winner_ad_id: type: - string - 'null' pattern: ^[0-9]+$ maxLength: 128 reason: type: - string - 'null' halt_details: type: - object - 'null' properties: ad_id: type: string effective_status: type: string enum: - DISAPPROVED - WITH_ISSUES date: type: string format: date link_clicks: type: integer counted_arrivals: type: integer ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 baseline: type: object properties: host: type: string path: type: string required: - host - path observed_destinations: type: array items: type: object properties: host: type: string path: type: string counted_arrivals: type: integer required: - host - path - counted_arrivals window_start: type: - string - 'null' format: date window_end: type: - string - 'null' format: date concluded_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 state: type: string enum: - not_delivered - insufficient - ranked - facts_unavailable - halted rank: type: - integer - 'null' spend: type: - string - 'null' impressions: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' description: PlainRouter recorded consented conversions of this Test event. admitted_conversions: type: - integer - 'null' description: Admitted conversions are shown beside recorded conversions and are never substituted for them. spend_per_recorded_conversion: type: - string - 'null' description: Major-unit spend divided by PlainRouter recorded conversions; used only to compare members within this Test. covered_days: type: array items: type: string format: date required: - ad_id - state - rank - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions - spend_per_recorded_conversion - covered_days daily_facts: type: array items: type: object properties: date: type: string format: date ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 spend_minor: type: string description: Exact spend in Meta minor units for the account currency, including any stored fractional component. pattern: ^[0-9]+(?:\.[0-9]+)?$ spend: type: - string - 'null' description: Account-currency spend in major units. impressions: type: - integer - 'null' inline_link_clicks: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' admitted_conversions: type: - integer - 'null' required: - date - ad_test_member_id - ad_id - spend_minor - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions required: - outcome - winner_ad_id - reason - window_start - window_end - concluded_at - members - daily_facts required: - id - workspace_id - platform_ad_account_id - ad_set_id - axis - event_name - arrivals_floor - minimum_conversions - maximum_days - maximum_spend_minor - anomaly_threshold_percent - first_day - timezone - currency - created_at - members - pause_proposals - verdict first_page_url: type: - string - 'null' from: type: - integer - 'null' last_page: type: integer last_page_url: type: - string - 'null' links: type: array items: type: object properties: active: type: boolean label: type: string url: type: - string - 'null' required: - active - label - url next_page_url: type: - string - 'null' path: type: string per_page: type: integer prev_page_url: type: - string - 'null' to: type: - integer - 'null' total: type: integer required: - current_page - data - first_page_url - from - last_page - last_page_url - links - next_page_url - path - per_page - prev_page_url - to - total observed_conversion_events_available: type: boolean observed_conversion_events: type: array items: type: object properties: event_name: type: string recorded_count: type: integer required: - event_name - recorded_count required: - tests - observed_conversion_events_available - observed_conversion_events title: AdTestListRead ApiRouteNotFound: type: object properties: error: type: object properties: code: type: string enum: - api_route_not_found message: type: string resolution: type: string required: - code - message - resolution request: type: object properties: method: type: string path: type: string required: - method - path resources: type: object properties: documentation: type: string openapi: type: string required: - documentation - openapi required: - error - request - resources title: ApiRouteNotFound ValidationError: type: object properties: message: type: string errors: type: object additionalProperties: type: array items: type: string required: - message - errors title: ValidationError AdTestShowRead: type: object properties: test: type: object properties: id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 workspace_id: type: integer platform_ad_account_id: type: integer ad_set_id: type: string pattern: ^[0-9]+$ maxLength: 128 axis: type: string enum: - hook - angle - visual - offer - format - landing event_name: type: string arrivals_floor: type: integer count_health_click_threshold: type: - integer - 'null' description: Link-click threshold frozen at conclusion; null until concluded or for legacy Tests. minimum_conversions: type: integer maximum_days: type: integer maximum_spend_minor: type: - string - 'null' description: Optional maximum in positive whole Meta minor units, using the same unit scale as Launch plan budget_amount_minor. anomaly_threshold_percent: type: string pattern: ^[0-9]+(?:\.[0-9]+)?$ first_day: type: string format: date timezone: type: string currency: type: string pattern: ^[A-Z]{3}$ created_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 position: type: integer baseline_host: type: - string - 'null' description: Immutable normalized landing host once the member has a baseline. baseline_path: type: - string - 'null' description: Immutable normalized landing path once the member has a baseline. required: - ad_id - position pause_proposals: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 status: type: string enum: - pending - proposed - skipped - expired reason: type: - string - 'null' description: Stable reason code when the proposal was skipped or expired. enum: - already_paused - deleted - archived - campaign_paused - adset_paused - decision_window_elapsed - null decision_deadline_at: type: string format: date-time description: Immutable deadline 24 hours after the Test's candidates are created on the database clock. required: - ad_id - status - reason - decision_deadline_at verdict: type: - object - 'null' properties: outcome: type: - string - 'null' enum: - winner - no_clear_winner - not_judged - halted - null winner_ad_id: type: - string - 'null' pattern: ^[0-9]+$ maxLength: 128 reason: type: - string - 'null' halt_details: type: - object - 'null' properties: ad_id: type: string effective_status: type: string enum: - DISAPPROVED - WITH_ISSUES date: type: string format: date link_clicks: type: integer counted_arrivals: type: integer ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 baseline: type: object properties: host: type: string path: type: string required: - host - path observed_destinations: type: array items: type: object properties: host: type: string path: type: string counted_arrivals: type: integer required: - host - path - counted_arrivals window_start: type: - string - 'null' format: date window_end: type: - string - 'null' format: date concluded_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 state: type: string enum: - not_delivered - insufficient - ranked - facts_unavailable - halted rank: type: - integer - 'null' spend: type: - string - 'null' impressions: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' description: PlainRouter recorded consented conversions of this Test event. admitted_conversions: type: - integer - 'null' description: Admitted conversions are shown beside recorded conversions and are never substituted for them. spend_per_recorded_conversion: type: - string - 'null' description: Major-unit spend divided by PlainRouter recorded conversions; used only to compare members within this Test. covered_days: type: array items: type: string format: date required: - ad_id - state - rank - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions - spend_per_recorded_conversion - covered_days daily_facts: type: array items: type: object properties: date: type: string format: date ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 spend_minor: type: string description: Exact spend in Meta minor units for the account currency, including any stored fractional component. pattern: ^[0-9]+(?:\.[0-9]+)?$ spend: type: - string - 'null' description: Account-currency spend in major units. impressions: type: - integer - 'null' inline_link_clicks: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' admitted_conversions: type: - integer - 'null' required: - date - ad_test_member_id - ad_id - spend_minor - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions required: - outcome - winner_ad_id - reason - window_start - window_end - concluded_at - members - daily_facts required: - id - workspace_id - platform_ad_account_id - ad_set_id - axis - event_name - arrivals_floor - minimum_conversions - maximum_days - maximum_spend_minor - anomaly_threshold_percent - first_day - timezone - currency - created_at - members - pause_proposals - verdict required: - test title: AdTestShowRead AdTestCreateRead: type: object properties: test: type: object properties: id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 workspace_id: type: integer platform_ad_account_id: type: integer ad_set_id: type: string pattern: ^[0-9]+$ maxLength: 128 axis: type: string enum: - hook - angle - visual - offer - format - landing event_name: type: string arrivals_floor: type: integer count_health_click_threshold: type: - integer - 'null' description: Link-click threshold frozen at conclusion; null until concluded or for legacy Tests. minimum_conversions: type: integer maximum_days: type: integer maximum_spend_minor: type: - string - 'null' description: Optional maximum in positive whole Meta minor units, using the same unit scale as Launch plan budget_amount_minor. anomaly_threshold_percent: type: string pattern: ^[0-9]+(?:\.[0-9]+)?$ first_day: type: string format: date timezone: type: string currency: type: string pattern: ^[A-Z]{3}$ created_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 position: type: integer baseline_host: type: - string - 'null' description: Immutable normalized landing host once the member has a baseline. baseline_path: type: - string - 'null' description: Immutable normalized landing path once the member has a baseline. required: - ad_id - position pause_proposals: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 status: type: string enum: - pending - proposed - skipped - expired reason: type: - string - 'null' description: Stable reason code when the proposal was skipped or expired. enum: - already_paused - deleted - archived - campaign_paused - adset_paused - decision_window_elapsed - null decision_deadline_at: type: string format: date-time description: Immutable deadline 24 hours after the Test's candidates are created on the database clock. required: - ad_id - status - reason - decision_deadline_at verdict: type: - object - 'null' properties: outcome: type: - string - 'null' enum: - winner - no_clear_winner - not_judged - halted - null winner_ad_id: type: - string - 'null' pattern: ^[0-9]+$ maxLength: 128 reason: type: - string - 'null' halt_details: type: - object - 'null' properties: ad_id: type: string effective_status: type: string enum: - DISAPPROVED - WITH_ISSUES date: type: string format: date link_clicks: type: integer counted_arrivals: type: integer ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 baseline: type: object properties: host: type: string path: type: string required: - host - path observed_destinations: type: array items: type: object properties: host: type: string path: type: string counted_arrivals: type: integer required: - host - path - counted_arrivals window_start: type: - string - 'null' format: date window_end: type: - string - 'null' format: date concluded_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 state: type: string enum: - not_delivered - insufficient - ranked - facts_unavailable - halted rank: type: - integer - 'null' spend: type: - string - 'null' impressions: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' description: PlainRouter recorded consented conversions of this Test event. admitted_conversions: type: - integer - 'null' description: Admitted conversions are shown beside recorded conversions and are never substituted for them. spend_per_recorded_conversion: type: - string - 'null' description: Major-unit spend divided by PlainRouter recorded conversions; used only to compare members within this Test. covered_days: type: array items: type: string format: date required: - ad_id - state - rank - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions - spend_per_recorded_conversion - covered_days daily_facts: type: array items: type: object properties: date: type: string format: date ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 spend_minor: type: string description: Exact spend in Meta minor units for the account currency, including any stored fractional component. pattern: ^[0-9]+(?:\.[0-9]+)?$ spend: type: - string - 'null' description: Account-currency spend in major units. impressions: type: - integer - 'null' inline_link_clicks: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' admitted_conversions: type: - integer - 'null' required: - date - ad_test_member_id - ad_id - spend_minor - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions required: - outcome - winner_ad_id - reason - window_start - window_end - concluded_at - members - daily_facts required: - id - workspace_id - platform_ad_account_id - ad_set_id - axis - event_name - arrivals_floor - minimum_conversions - maximum_days - maximum_spend_minor - anomaly_threshold_percent - first_day - timezone - currency - created_at - members - pause_proposals - verdict required: - test title: AdTestCreateRead ErrorMessage: type: object properties: message: type: string required: - message title: ErrorMessage AdTestListRead_2: type: object properties: tests: type: object properties: current_page: type: integer data: type: array items: type: object properties: id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 workspace_id: type: integer platform_ad_account_id: type: integer ad_set_id: type: string pattern: ^[0-9]+$ maxLength: 128 axis: type: string enum: - hook - angle - visual - offer - format - landing event_name: type: - string - 'null' description: Legacy explicit event name; null for workspace-default mode. conversion_definition_version: type: integer enum: - 1 - 2 conversion_events: type: array description: Immutable canonical choices. Version 1 exposes its original event choice. items: type: string minItems: 1 exclude_subscription_payments: type: boolean description: Subscription-payment exclusion captured at creation; false for version 1. arrivals_floor: type: integer count_health_click_threshold: type: - integer - 'null' description: Link-click threshold frozen at conclusion; null until concluded or for legacy Tests. minimum_conversions: type: integer maximum_days: type: integer maximum_spend_minor: type: - string - 'null' description: Optional maximum in positive whole Meta minor units, using the same unit scale as Launch plan budget_amount_minor. anomaly_threshold_percent: type: string pattern: ^[0-9]+(?:\.[0-9]+)?$ first_day: type: string format: date timezone: type: string currency: type: string pattern: ^[A-Z]{3}$ created_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 position: type: integer baseline_host: type: - string - 'null' description: Immutable normalized landing host once the member has a baseline. baseline_path: type: - string - 'null' description: Immutable normalized landing path once the member has a baseline. required: - ad_id - position pause_proposals: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 status: type: string enum: - pending - proposed - skipped - expired reason: type: - string - 'null' description: Stable reason code when the proposal was skipped or expired. enum: - already_paused - deleted - archived - campaign_paused - adset_paused - decision_window_elapsed - null decision_deadline_at: type: string format: date-time description: Immutable deadline 24 hours after the Test's candidates are created on the database clock. required: - ad_id - status - reason - decision_deadline_at verdict: type: - object - 'null' properties: outcome: type: - string - 'null' enum: - winner - no_clear_winner - not_judged - halted - null winner_ad_id: type: - string - 'null' pattern: ^[0-9]+$ maxLength: 128 reason: type: - string - 'null' halt_details: type: - object - 'null' properties: ad_id: type: string effective_status: type: string enum: - DISAPPROVED - WITH_ISSUES date: type: string format: date link_clicks: type: integer counted_arrivals: type: integer ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 baseline: type: object properties: host: type: string path: type: string required: - host - path observed_destinations: type: array items: type: object properties: host: type: string path: type: string counted_arrivals: type: integer required: - host - path - counted_arrivals window_start: type: - string - 'null' format: date window_end: type: - string - 'null' format: date concluded_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 state: type: string enum: - not_delivered - insufficient - ranked - facts_unavailable - halted rank: type: - integer - 'null' spend: type: - string - 'null' impressions: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' description: PlainRouter recorded conversions under this Test’s frozen definition. Version 1 retains its original recorded-count semantics. admitted_conversions: type: - integer - 'null' description: Admitted conversions are shown beside recorded conversions and are never substituted for them. spend_per_recorded_conversion: type: - string - 'null' description: Major-unit spend divided by PlainRouter recorded conversions; used only to compare members within this Test. covered_days: type: array items: type: string format: date required: - ad_id - state - rank - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions - spend_per_recorded_conversion - covered_days daily_facts: type: array items: type: object properties: date: type: string format: date ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 spend_minor: type: string description: Exact spend in Meta minor units for the account currency, including any stored fractional component. pattern: ^[0-9]+(?:\.[0-9]+)?$ spend: type: - string - 'null' description: Account-currency spend in major units. impressions: type: - integer - 'null' inline_link_clicks: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' admitted_conversions: type: - integer - 'null' required: - date - ad_test_member_id - ad_id - spend_minor - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions required: - outcome - winner_ad_id - reason - window_start - window_end - concluded_at - members - daily_facts required: - id - workspace_id - platform_ad_account_id - ad_set_id - axis - event_name - conversion_definition_version - conversion_events - exclude_subscription_payments - arrivals_floor - minimum_conversions - maximum_days - maximum_spend_minor - anomaly_threshold_percent - first_day - timezone - currency - created_at - members - pause_proposals - verdict first_page_url: type: - string - 'null' from: type: - integer - 'null' last_page: type: integer last_page_url: type: - string - 'null' links: type: array items: type: object properties: active: type: boolean label: type: string url: type: - string - 'null' required: - active - label - url next_page_url: type: - string - 'null' path: type: string per_page: type: integer prev_page_url: type: - string - 'null' to: type: - integer - 'null' total: type: integer required: - current_page - data - first_page_url - from - last_page - last_page_url - links - next_page_url - path - per_page - prev_page_url - to - total observed_conversion_events_available: type: boolean observed_conversion_events: type: array items: type: object properties: event_name: type: string recorded_count: type: integer required: - event_name - recorded_count required: - tests - observed_conversion_events_available - observed_conversion_events title: AdTestListRead AdTestShowRead_2: type: object properties: test: type: object properties: id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 workspace_id: type: integer platform_ad_account_id: type: integer ad_set_id: type: string pattern: ^[0-9]+$ maxLength: 128 axis: type: string enum: - hook - angle - visual - offer - format - landing event_name: type: - string - 'null' description: Legacy explicit event name; null for workspace-default mode. conversion_definition_version: type: integer enum: - 1 - 2 conversion_events: type: array description: Immutable canonical choices. Version 1 exposes its original event choice. items: type: string minItems: 1 exclude_subscription_payments: type: boolean description: Subscription-payment exclusion captured at creation; false for version 1. arrivals_floor: type: integer count_health_click_threshold: type: - integer - 'null' description: Link-click threshold frozen at conclusion; null until concluded or for legacy Tests. minimum_conversions: type: integer maximum_days: type: integer maximum_spend_minor: type: - string - 'null' description: Optional maximum in positive whole Meta minor units, using the same unit scale as Launch plan budget_amount_minor. anomaly_threshold_percent: type: string pattern: ^[0-9]+(?:\.[0-9]+)?$ first_day: type: string format: date timezone: type: string currency: type: string pattern: ^[A-Z]{3}$ created_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 position: type: integer baseline_host: type: - string - 'null' description: Immutable normalized landing host once the member has a baseline. baseline_path: type: - string - 'null' description: Immutable normalized landing path once the member has a baseline. required: - ad_id - position pause_proposals: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 status: type: string enum: - pending - proposed - skipped - expired reason: type: - string - 'null' description: Stable reason code when the proposal was skipped or expired. enum: - already_paused - deleted - archived - campaign_paused - adset_paused - decision_window_elapsed - null decision_deadline_at: type: string format: date-time description: Immutable deadline 24 hours after the Test's candidates are created on the database clock. required: - ad_id - status - reason - decision_deadline_at verdict: type: - object - 'null' properties: outcome: type: - string - 'null' enum: - winner - no_clear_winner - not_judged - halted - null winner_ad_id: type: - string - 'null' pattern: ^[0-9]+$ maxLength: 128 reason: type: - string - 'null' halt_details: type: - object - 'null' properties: ad_id: type: string effective_status: type: string enum: - DISAPPROVED - WITH_ISSUES date: type: string format: date link_clicks: type: integer counted_arrivals: type: integer ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 baseline: type: object properties: host: type: string path: type: string required: - host - path observed_destinations: type: array items: type: object properties: host: type: string path: type: string counted_arrivals: type: integer required: - host - path - counted_arrivals window_start: type: - string - 'null' format: date window_end: type: - string - 'null' format: date concluded_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 state: type: string enum: - not_delivered - insufficient - ranked - facts_unavailable - halted rank: type: - integer - 'null' spend: type: - string - 'null' impressions: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' description: PlainRouter recorded conversions under this Test’s frozen definition. Version 1 retains its original recorded-count semantics. admitted_conversions: type: - integer - 'null' description: Admitted conversions are shown beside recorded conversions and are never substituted for them. spend_per_recorded_conversion: type: - string - 'null' description: Major-unit spend divided by PlainRouter recorded conversions; used only to compare members within this Test. covered_days: type: array items: type: string format: date required: - ad_id - state - rank - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions - spend_per_recorded_conversion - covered_days daily_facts: type: array items: type: object properties: date: type: string format: date ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 spend_minor: type: string description: Exact spend in Meta minor units for the account currency, including any stored fractional component. pattern: ^[0-9]+(?:\.[0-9]+)?$ spend: type: - string - 'null' description: Account-currency spend in major units. impressions: type: - integer - 'null' inline_link_clicks: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' admitted_conversions: type: - integer - 'null' required: - date - ad_test_member_id - ad_id - spend_minor - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions required: - outcome - winner_ad_id - reason - window_start - window_end - concluded_at - members - daily_facts required: - id - workspace_id - platform_ad_account_id - ad_set_id - axis - event_name - conversion_definition_version - conversion_events - exclude_subscription_payments - arrivals_floor - minimum_conversions - maximum_days - maximum_spend_minor - anomaly_threshold_percent - first_day - timezone - currency - created_at - members - pause_proposals - verdict required: - test title: AdTestShowRead AdTestCreateRead_2: type: object properties: test: type: object properties: id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 workspace_id: type: integer platform_ad_account_id: type: integer ad_set_id: type: string pattern: ^[0-9]+$ maxLength: 128 axis: type: string enum: - hook - angle - visual - offer - format - landing event_name: type: - string - 'null' description: Legacy explicit event name; null for workspace-default mode. conversion_definition_version: type: integer enum: - 1 - 2 conversion_events: type: array description: Immutable canonical choices. Version 1 exposes its original event choice. items: type: string minItems: 1 exclude_subscription_payments: type: boolean description: Subscription-payment exclusion captured at creation; false for version 1. arrivals_floor: type: integer count_health_click_threshold: type: - integer - 'null' description: Link-click threshold frozen at conclusion; null until concluded or for legacy Tests. minimum_conversions: type: integer maximum_days: type: integer maximum_spend_minor: type: - string - 'null' description: Optional maximum in positive whole Meta minor units, using the same unit scale as Launch plan budget_amount_minor. anomaly_threshold_percent: type: string pattern: ^[0-9]+(?:\.[0-9]+)?$ first_day: type: string format: date timezone: type: string currency: type: string pattern: ^[A-Z]{3}$ created_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 position: type: integer baseline_host: type: - string - 'null' description: Immutable normalized landing host once the member has a baseline. baseline_path: type: - string - 'null' description: Immutable normalized landing path once the member has a baseline. required: - ad_id - position pause_proposals: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 status: type: string enum: - pending - proposed - skipped - expired reason: type: - string - 'null' description: Stable reason code when the proposal was skipped or expired. enum: - already_paused - deleted - archived - campaign_paused - adset_paused - decision_window_elapsed - null decision_deadline_at: type: string format: date-time description: Immutable deadline 24 hours after the Test's candidates are created on the database clock. required: - ad_id - status - reason - decision_deadline_at verdict: type: - object - 'null' properties: outcome: type: - string - 'null' enum: - winner - no_clear_winner - not_judged - halted - null winner_ad_id: type: - string - 'null' pattern: ^[0-9]+$ maxLength: 128 reason: type: - string - 'null' halt_details: type: - object - 'null' properties: ad_id: type: string effective_status: type: string enum: - DISAPPROVED - WITH_ISSUES date: type: string format: date link_clicks: type: integer counted_arrivals: type: integer ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 baseline: type: object properties: host: type: string path: type: string required: - host - path observed_destinations: type: array items: type: object properties: host: type: string path: type: string counted_arrivals: type: integer required: - host - path - counted_arrivals window_start: type: - string - 'null' format: date window_end: type: - string - 'null' format: date concluded_at: type: - string - 'null' format: date-time members: type: array items: type: object properties: ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 state: type: string enum: - not_delivered - insufficient - ranked - facts_unavailable - halted rank: type: - integer - 'null' spend: type: - string - 'null' impressions: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' description: PlainRouter recorded conversions under this Test’s frozen definition. Version 1 retains its original recorded-count semantics. admitted_conversions: type: - integer - 'null' description: Admitted conversions are shown beside recorded conversions and are never substituted for them. spend_per_recorded_conversion: type: - string - 'null' description: Major-unit spend divided by PlainRouter recorded conversions; used only to compare members within this Test. covered_days: type: array items: type: string format: date required: - ad_id - state - rank - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions - spend_per_recorded_conversion - covered_days daily_facts: type: array items: type: object properties: date: type: string format: date ad_test_member_id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ minLength: 26 maxLength: 26 ad_id: type: string pattern: ^[0-9]+$ maxLength: 128 spend_minor: type: string description: Exact spend in Meta minor units for the account currency, including any stored fractional component. pattern: ^[0-9]+(?:\.[0-9]+)?$ spend: type: - string - 'null' description: Account-currency spend in major units. impressions: type: - integer - 'null' inline_link_clicks: type: - integer - 'null' counted_arrivals: type: - integer - 'null' recorded_conversions: type: - integer - 'null' admitted_conversions: type: - integer - 'null' required: - date - ad_test_member_id - ad_id - spend_minor - spend - impressions - counted_arrivals - recorded_conversions - admitted_conversions required: - outcome - winner_ad_id - reason - window_start - window_end - concluded_at - members - daily_facts required: - id - workspace_id - platform_ad_account_id - ad_set_id - axis - event_name - conversion_definition_version - conversion_events - exclude_subscription_payments - arrivals_floor - minimum_conversions - maximum_days - maximum_spend_minor - anomaly_threshold_percent - first_day - timezone - currency - created_at - members - pause_proposals - verdict required: - test title: AdTestCreateRead securitySchemes: workspaceSecret: type: http description: Per-workspace Server secret. It authenticates only the Signals workspace represented by the credential. scheme: bearer sandboxCredential: type: http description: Short-lived Sandbox key issued by POST /sandbox/keys. scheme: bearer workspaceActionKey: type: http description: Workspace key with read-actions for reads or propose-actions for writes. Unbound keys select an available advertising account with account_id. scheme: bearer planWriter: type: http description: An OAuth agent or account-bound Workspace Key with write access and the plan-create grant. scheme: bearer workspaceInventoryKey: type: http description: Workspace read key with the get_inventory_metrics grant. Account-bound keys can read only their bound account. scheme: bearer planReader: type: http description: An OAuth agent or account-bound Workspace Key with read access to the approved ad account. scheme: bearer creativeWriter: type: http description: Workspace-bound write credential with a grant authorizing creative intake. scheme: bearer testReader: type: http description: A workspace principal with read tier and the launch.plans.index and launch.plans.show grants for this account. scheme: bearer testWriter: type: http description: A workspace principal with write tier and the launch.plans.store grant for this account. scheme: bearer ruleReader: type: http description: 'A workspace-scoped key with read ability and actions.rules.index or actions.rules.show access to the bound account. Send Accept: application/json.' scheme: bearer ruleWriter: type: http description: 'A workspace-scoped key with write ability and the actions.rules.store or actions.rules.update grant. Send Accept: application/json.' scheme: bearer agentContextKey: type: http description: Workspace key with read ability, ad-account.read permission and the agent.context grant. scheme: bearer agentContextOAuth: type: oauth2 description: OAuth access token for an active approved agent principal with mcp:use, ad-account.read and agent.context authorization. Authorization code with PKCE and device authorization tokens are accepted. This scheme applies only to agent context. flows: authorizationCode: authorizationUrl: https://plainrouter.com/oauth2/authorize tokenUrl: https://plainrouter.com/oauth2/token refreshUrl: https://plainrouter.com/oauth2/token scopes: mcp:use: Use the approved agent principal. x-refined-from: - dev-plainrouter-openapi.json - plainrouter-openapi.json x-action-types: - increase_budget - decrease_budget - pause - resume - replace_creative - shift_spend - rollback - create_campaign - create_ad_set - create_ad - upload_asset x-signed: true