openapi: 3.2.0 info: title: Plainrouter Deployment Plan API version: 0.5.0 description: 'Operations tagged DeploymentPlan 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: DeploymentPlan paths: /workspaces/{workspace}/admin/plans/{deployment_plan}/copy: post: operationId: launch.plans.copy description: Copy failed plan content and stored integer budgets into a fresh draft. History stays unchanged; validate budgets and submit again with a new intent. summary: Copy a failed plan to a new draft tags: - DeploymentPlan parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: deployment_plan in: path required: true schema: type: string responses: '201': description: A new draft was created. content: application/json: schema: $ref: '#/components/schemas/PlanCopyRead' '401': description: Missing, invalid, revoked or expired credentials, a missing active principal, missing read scope or lost issuer authority return message. No error code is returned. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '403': description: Missing workspace, account or write access returns error.code insufficient_scope and error.message. A revoked grant returns message without a code. content: application/json: schema: anyOf: - $ref: '#/components/schemas/PlanCopyForbidden' - $ref: '#/components/schemas/ErrorMessage' '404': description: An inaccessible or missing plan returns error.code plan_not_found and error.message. A missing workspace or disabled Plan surface returns the api_route_not_found envelope with error, request and resources. content: application/json: schema: anyOf: - $ref: '#/components/schemas/PlanCopyNotFound' - $ref: '#/components/schemas/ApiRouteNotFound' '422': description: error.code plan_not_failed when the source is not failed; platform_ad_account_unavailable when its account becomes unavailable after authorization; plan_not_found if the source disappears after lookup. Each includes error.message. content: application/json: schema: $ref: '#/components/schemas/PlanCopyRejected' '413': description: Oversized POST data returns message "The POST data is too large." before route authorization. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '429': description: The shared API rate limit is exceeded; message is "Too Many Attempts." Retry-After gives seconds until a retry 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 '503': description: 'The bounded workspace transaction timed out: error.code workspace_lock_timeout and error.message "The workspace is busy. Retry the operation shortly." Retry-After is 1 second.' content: application/json: schema: $ref: '#/components/schemas/WorkspaceLockTimeout' '500': description: An unexpected server or provider-read fault returns message "Server Error" when debug is disabled. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' security: - planWriter: [] servers: - url: https://plainrouter.com/api/v1 description: Production /workspaces/{workspace}/admin/plans/{deployment_plan}/execute: post: operationId: launch.plans.execute description: Create or replay a governed Launch intent using the reviewed plan's review_version. Changed versions are refused before intent creation or staging. summary: Execute a deployment plan tags: - DeploymentPlan parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: deployment_plan in: path required: true schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ExecuteDeploymentPlanRequest' responses: '200': description: Created or replayed Launch intent, including a blocked intent under the kill switch. content: application/json: schema: $ref: '#/components/schemas/LaunchIntentRead' '401': description: Missing, invalid, revoked or expired credentials, a missing active principal, missing read scope or lost issuer authority return message. No error code is returned. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '403': description: Missing workspace, account, write access, execution scope or workspace grant returns message without a code. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '404': description: A missing or inaccessible workspace or plan, or a disabled Plan surface, returns error.code api_route_not_found with error.message, error.resolution, request and resources. content: application/json: schema: $ref: '#/components/schemas/ApiRouteNotFound' '422': description: 'Request validation returns message plus errors (including intent_key and review_version). Domain refusals return error.code and error.message: plan_changed_since_review ("This plan changed since you reviewed it. Reload and review the latest plan before submitting."), deployment_plan_not_executable, creative_not_ready, creative_bytes_unavailable, ad_set_daily_budget_invalid, ad_set_bid_amount_invalid, currency_mismatch, currency_unsupported, budget_invalid, deployment_plan_has_no_actions. Proposal input or evidence validation returns message plus field errors.' content: application/json: schema: anyOf: - $ref: '#/components/schemas/ValidationError' - $ref: '#/components/schemas/PlanExecuteRejected' '409': description: An intent key bound to another plan returns error.code launch_submission_key_plan_mismatch and error.message. content: application/json: schema: $ref: '#/components/schemas/PlanExecuteConflict' '413': description: Oversized POST data returns message "The POST data is too large." before route authorization. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '429': description: The shared API rate limit is exceeded; message is "Too Many Attempts." Retry-After gives seconds until a retry 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 '503': description: 'The bounded workspace transaction timed out: error.code workspace_lock_timeout and error.message "The workspace is busy. Retry the operation shortly." Retry-After is 1 second.' content: application/json: schema: $ref: '#/components/schemas/WorkspaceLockTimeout' '500': description: An unexpected server or provider-read fault returns message "Server Error" when debug is disabled. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' security: - planWriter: [] servers: - url: https://plainrouter.com/api/v1 description: Production /workspaces/{workspace}/admin/plans: get: operationId: launch.plans.index description: Read plans for the approved ad account with their current review_version. Pass the version from the plan you reviewed when executing. summary: List deployment plans tags: - DeploymentPlan parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: per_page in: query schema: type: integer default: 25 responses: '200': description: Plans with their current review version. content: application/json: schema: type: object properties: plans: type: object properties: data: type: array items: $ref: '#/components/schemas/DeploymentPlanRead' current_page: type: integer per_page: type: integer total: type: integer required: - data - current_page - per_page - total required: - plans '401': description: An active principal is required. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '403': description: The principal lacks read access to this account. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '404': description: Workspace or plan not found. content: application/json: schema: $ref: '#/components/schemas/ApiRouteNotFound' security: - planReader: [] post: operationId: launch.plans.store description: Create or edit a draft deployment plan for the authorized advertising account. Review and validate the plan before submitting any provider writes. summary: Create a deployment plan tags: - DeploymentPlan parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/StoreDeploymentPlanRequest' responses: '201': description: '' content: application/json: schema: type: object properties: plan: type: array items: {} required: - plan '401': description: Invalid or missing Server secret. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '404': description: A missing workspace returns the api_route_not_found envelope. content: application/json: schema: $ref: '#/components/schemas/ApiRouteNotFound' '422': description: JSON booleans in integer inputs, including workspace selectors, return message plus errors mapping field names to arrays of messages. Numeric strings retain their existing validation and coercion. content: application/json: schema: $ref: '#/components/schemas/ValidationError' servers: - url: https://plainrouter.com/api/v1 description: Production /workspaces/{workspace}/admin/plans/{deployment_plan}: get: operationId: launch.plans.show description: Read plans for the approved ad account with their current review_version. Pass the version from the plan you reviewed when executing. summary: Read a deployment plan tags: - DeploymentPlan parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: deployment_plan in: path required: true schema: type: string responses: '200': description: Plans with their current review version. content: application/json: schema: type: object properties: plan: $ref: '#/components/schemas/DeploymentPlanRead' required: - plan '401': description: An active principal is required. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '403': description: The principal lacks read access to this account. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '404': description: Workspace or plan not found. content: application/json: schema: $ref: '#/components/schemas/ApiRouteNotFound' security: - planReader: [] patch: operationId: launch.plans.update description: Create or edit a draft deployment plan for the authorized advertising account. Review and validate the plan before submitting any provider writes. summary: Update a deployment plan tags: - DeploymentPlan parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: deployment_plan in: path required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/UpdateDeploymentPlanRequest' responses: '200': description: '' content: application/json: schema: type: object properties: plan: type: array items: {} required: - plan '401': description: Invalid or missing Server secret. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '404': description: A missing workspace returns the api_route_not_found envelope. content: application/json: schema: $ref: '#/components/schemas/ApiRouteNotFound' '422': description: JSON booleans in integer inputs, including workspace selectors, return message plus errors mapping field names to arrays of messages. Numeric strings retain their existing validation and coercion. content: application/json: schema: $ref: '#/components/schemas/ValidationError' servers: - url: https://plainrouter.com/api/v1 description: Production /workspaces/{workspace}/admin/plans/{deployment_plan}/validate: post: operationId: launch.plans.validate description: Validate a plan without provider writes. Reuse a ready Meta video in the same ad account; plans needing a new video upload are refused. summary: Validate a deployment plan tags: - DeploymentPlan parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: deployment_plan in: path required: true schema: type: string responses: '401': description: Invalid or missing Server secret. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '404': description: A missing workspace returns the api_route_not_found envelope. content: application/json: schema: $ref: '#/components/schemas/ApiRouteNotFound' '200': description: The plan and its validation result. content: application/json: schema: type: object properties: plan: $ref: '#/components/schemas/DeploymentPlanRead_2' required: - plan '422': description: 'Request and integer selector validation returns message plus field errors. Domain refusals return error.code and error.message, including launch_video_upload_unavailable: New video uploads to Meta are unavailable. Use a video already uploaded to this Meta ad account or choose an image.' content: application/json: schema: anyOf: - $ref: '#/components/schemas/ValidationError' - $ref: '#/components/schemas/PlanValidateRejected' security: - planWriter: [] servers: - url: https://plainrouter.com/api/v1 description: Production components: schemas: PlanCopyNotFound: type: object properties: error: type: object properties: code: type: string enum: - plan_not_found message: type: string required: - code - message required: - error title: PlanCopyNotFound LaunchIntentRead: type: object properties: intent: type: object properties: id: type: string status: type: string enum: - approval_required - approved - executing - verified - failed - drifted - partial - blocked action_batch_id: type: - string - 'null' policy_reasons: type: array items: type: string provider_object_ids: {} campaign_id: type: - string - 'null' ad_set_id: type: - string - 'null' currency: type: - string - 'null' actions: type: array items: type: object properties: id: type: string type: type: string enum: - increase_budget - decrease_budget - pause - resume - replace_creative - shift_spend - rollback - create_campaign - create_ad_set - create_ad - upload_asset - adjust_budget - set_status - duplicate_ad_with_creative - duplicate_adset - duplicate_ad_with_creative_v2 verification_result: type: - string - 'null' external_ids: {} required: - id - type - verification_result - external_ids required: - id - status - action_batch_id - policy_reasons - provider_object_ids - campaign_id - ad_set_id - currency - actions required: - intent title: LaunchIntentRead DeploymentPlanRead: type: object properties: name: type: - string - 'null' id: type: string platform_ad_account_id: type: integer review_version: type: string description: Current review version. Pass this value when executing the plan you reviewed. pattern: ^[a-f0-9]{64}$ copy: type: - object - 'null' properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta landing_page: type: string creatives: type: array items: type: object properties: id: type: string position: type: integer copy: type: - object - 'null' description: Optional complete per-ad copy override; null uses the plan copy. properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta landing_page: type: - string - 'null' description: Optional per-ad HTTPS landing URL override; null uses the plan landing URL. maxLength: 2048 resolved_copy: type: - object - 'null' properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta resolved_landing_page: type: string copy_differs: type: boolean landing_page_differs: type: boolean required: - id - position - copy - landing_page - resolved_copy - resolved_landing_page - copy_differs - landing_page_differs required: - id - platform_ad_account_id - review_version title: DeploymentPlanRead PlanCopyRejected: type: object properties: error: type: object properties: code: type: string enum: - plan_not_failed - platform_ad_account_unavailable - plan_not_found message: type: string required: - code - message required: - error title: PlanCopyRejected PlanExecuteConflict: type: object properties: error: type: object properties: code: type: string enum: - launch_submission_key_plan_mismatch message: type: string required: - code - message required: - error title: PlanExecuteConflict PlanCopyRead: type: object properties: plan: type: object properties: id: type: string platform_ad_account_id: type: integer status: type: string enum: - draft budget_amount_minor: type: integer validation_result: type: 'null' validated_at: type: 'null' approval_id: type: 'null' required: - id - platform_ad_account_id - status - budget_amount_minor - validation_result - validated_at - approval_id required: - plan title: PlanCopyRead 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 PlanCopyForbidden: type: object properties: error: type: object properties: code: type: string enum: - insufficient_scope message: type: string required: - code - message required: - error title: PlanCopyForbidden PlanExecuteRejected: type: object properties: error: type: object properties: code: type: string enum: - plan_changed_since_review - deployment_plan_not_executable - creative_not_ready - creative_bytes_unavailable - ad_set_daily_budget_invalid - ad_set_bid_amount_invalid - currency_mismatch - currency_unsupported - budget_invalid - deployment_plan_has_no_actions message: type: string required: - code - message required: - error title: PlanExecuteRejected ErrorMessage: type: object properties: message: type: string required: - message title: ErrorMessage ExecuteDeploymentPlanRequest: type: object properties: review_version: type: string pattern: ^[a-f0-9]{64}$ intent_key: type: - string - 'null' maxLength: 240 required: - review_version title: ExecuteDeploymentPlanRequest WorkspaceLockTimeout: type: object properties: error: type: object properties: code: type: string enum: - workspace_lock_timeout message: type: string required: - code - message required: - error title: WorkspaceLockTimeout DeploymentPlanRead_2: type: object description: Complete projection rows are paged within 80,000 JSON bytes, accounting for both MCP representations. Defaults are bounded. Follow next_cursor with the same scope and filters. One oversized row is explicitly refused; fields are never silently clipped. properties: name: type: - string - 'null' id: type: string platform_ad_account_id: type: integer review_version: type: string description: Current review version. Pass this value when executing the plan you reviewed. pattern: ^[a-f0-9]{64}$ copy: type: - object - 'null' properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta landing_page: type: string creatives: type: array items: type: object properties: id: type: string position: type: integer copy: type: - object - 'null' description: Optional complete per-ad copy override; null uses the plan copy. properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta landing_page: type: - string - 'null' description: Optional per-ad HTTPS landing URL override; null uses the plan landing URL. maxLength: 2048 resolved_copy: type: - object - 'null' properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta resolved_landing_page: type: string copy_differs: type: boolean landing_page_differs: type: boolean required: - id - position - copy - landing_page - resolved_copy - resolved_landing_page - copy_differs - landing_page_differs next_cursor: type: - integer - 'null' truncated: type: boolean continuation: type: string creative_count: type: integer required: - id - platform_ad_account_id - review_version title: DeploymentPlanRead ProposalUnavailable: type: object properties: error: type: object properties: code: type: string enum: - workspace_lock_timeout - proposal_not_recorded message: type: string required: - code - message required: - error title: ProposalUnavailable StoreDeploymentPlanRequest: type: object properties: name: type: - string - 'null' maxLength: 255 platform_ad_account_id: type: integer minimum: 1 platform: type: string enum: - google - meta - microsoft campaign_ref: type: - string - 'null' maxLength: 255 campaign_spec: type: - array - 'null' items: type: string ad_set_ref: type: - string - 'null' maxLength: 255 ad_set_spec: type: - object - 'null' description: Budget fields use positive whole Meta minor units. JSON booleans return 422 with field errors; digit-only strings retain their existing normalization. properties: daily_budget: type: integer minimum: 1 bid_amount: type: integer minimum: 1 lifetime_budget: type: - integer - 'null' minimum: 1 additionalProperties: {} budget_type: type: string enum: - daily - lifetime budget_amount_minor: type: integer minimum: 1 currency: type: string minLength: 3 maxLength: 3 copy: type: object properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta landing_page: type: string format: uri maxLength: 2048 utm_policy_name: type: - string - 'null' maxLength: 255 naming_policy_name: type: - string - 'null' maxLength: 255 creative_ids: type: array items: type: string creative_overrides: type: array items: type: object properties: creative_id: type: string copy: type: object properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta landing_page: type: - string - 'null' format: uri maxLength: 2048 required: - creative_id - copy - landing_page uniqueItems: true required: - platform_ad_account_id - platform - budget_type - budget_amount_minor - currency - copy - landing_page title: StoreDeploymentPlanRequest PlanExecuteRejected_2: type: object properties: error: type: object properties: code: type: string enum: - plan_changed_since_review - deployment_plan_not_executable - creative_not_ready - creative_bytes_unavailable - launch_video_upload_unavailable - ad_set_daily_budget_invalid - ad_set_bid_amount_invalid - currency_mismatch - currency_unsupported - budget_invalid - deployment_plan_has_no_actions message: type: string required: - code - message required: - error title: PlanExecuteRejected PlanValidateRejected: type: object properties: error: type: object properties: code: type: string description: Launch plan refusal code, including launch_video_upload_unavailable when a new Meta video upload would be required. message: type: string required: - code - message required: - error title: PlanValidateRejected UpdateDeploymentPlanRequest: type: object properties: name: type: - string - 'null' maxLength: 255 platform_ad_account_id: type: integer minimum: 1 platform: type: string enum: - google - meta - microsoft campaign_ref: type: - string - 'null' maxLength: 255 campaign_spec: type: - array - 'null' items: type: string ad_set_ref: type: - string - 'null' maxLength: 255 ad_set_spec: type: - object - 'null' description: Budget fields use positive whole Meta minor units. JSON booleans return 422 with field errors; digit-only strings retain their existing normalization. properties: daily_budget: type: integer minimum: 1 bid_amount: type: integer minimum: 1 lifetime_budget: type: - integer - 'null' minimum: 1 additionalProperties: {} budget_type: type: string enum: - daily - lifetime budget_amount_minor: type: integer minimum: 1 currency: type: string minLength: 3 maxLength: 3 copy: type: object properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta landing_page: type: string format: uri maxLength: 2048 utm_policy_name: type: - string - 'null' maxLength: 255 naming_policy_name: type: - string - 'null' maxLength: 255 creative_ids: type: array items: type: string creative_overrides: type: array items: type: object properties: creative_id: type: string copy: type: object properties: primary_text: type: string headline: type: string description: type: string cta: type: string enum: - learn_more - shop_now - sign_up required: - primary_text - headline - description - cta landing_page: type: - string - 'null' format: uri maxLength: 2048 required: - creative_id - copy - landing_page uniqueItems: true title: UpdateDeploymentPlanRequest 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