openapi: 3.2.0 info: title: Plainrouter Creative API version: 0.5.0 description: 'Operations tagged Creative 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: Creative paths: /workspaces/{workspace}/admin/creatives: post: operationId: storeCreative description: Store a creative in the credential-bound workspace. Set generator, generator_job and parent_creative tags for output from an outside tool. summary: Upload a creative tags: - Creative parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer requestBody: required: true content: multipart/form-data: schema: type: object properties: file: type: string format: binary description: JPEG, PNG, MP4 or QuickTime bytes, at most 4 GiB. tags: type: object description: Optional flat map of at most 32 string tags; reserved keys count toward the cap. For a creative from an outside tool, set generator and generator_job, plus parent_creative for a variation. Values are stored unchanged. PlainRouter’s own creative tools will set these on every creative they make; intake performs no generation, rendering or scoring. properties: generator: type: string description: Lowercase slug naming the outside tool that made the creative. pattern: ^[a-z0-9-]{1,64}$ minLength: 1 maxLength: 64 generator_job: type: string description: Printable characters naming the tool’s job or render; control and other non-printable characters are refused. minLength: 1 maxLength: 255 parent_creative: type: string description: 'The id returned by the creative APIs: a lowercase ULID of an existing creative in the same workspace that this creative varies.' pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ additionalProperties: type: string maxLength: 255 source_ref: type: - string - 'null' maxLength: 255 required: - file responses: '201': description: Creative stored, or the existing creative returned for identical bytes in this workspace. content: application/json: schema: $ref: '#/components/schemas/CreativeIntakeRead' '401': description: A valid workspace-bound credential is required. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '403': description: The credential or workspace grant does not authorize creative intake. content: application/json: schema: anyOf: - $ref: '#/components/schemas/ErrorMessage' - $ref: '#/components/schemas/AgentCredentialError' '404': description: Creative intake is disabled, or the workspace is missing or inaccessible. content: application/json: schema: $ref: '#/components/schemas/ApiRouteNotFound' '422': description: A reserved tag that violates the provenance contract returns error.code creative_provenance_invalid and error.message naming the key. Other intake refusals return their stable error.code and error.message; request-shape errors return message plus field errors. content: application/json: schema: anyOf: - $ref: '#/components/schemas/CreativeIntakeRejected' - $ref: '#/components/schemas/ValidationError' '413': description: Creative bytes exceed the 4 GiB cap, or POST data exceeds the request limit. content: application/json: schema: anyOf: - $ref: '#/components/schemas/CreativeIntakeRejected' - $ref: '#/components/schemas/ErrorMessage' security: - creativeWriter: [] get: operationId: launch.creatives.index description: Read complete workspace creative rows within the shared MCP byte budget. Continue with next_cursor as cursor in the same workspace. summary: List workspace creatives tags: - Creative parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: cursor in: query description: Continue using next_cursor with the same filters; rule lists use an immutable rule ID, other lists an offset or object ID. schema: type: integer minimum: 0 - name: per_page in: query description: Maximum complete rows; the byte budget may return fewer. Defaults to 25. schema: type: integer default: 25 minimum: 1 maximum: 100 - name: page in: query description: Integer page from 1 through 10000000000000000; integer strings are accepted. Zero, negatives, fractions, non-numeric strings and booleans return 422 with a page field validation error. schema: type: integer default: 1 minimum: 1 maximum: 10000000000000000 responses: '401': description: Invalid or missing Server secret. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '200': description: Bounded rows; no nested history is included in rule summaries. Result projections share the 80,000-byte MCP JSON budget; defaults are bounded, next_cursor continues complete rows, and oversized single rows are explicitly refused. Plan creative continuation uses the read endpoint; proposal continuation uses the batch read endpoint. content: application/json: schema: type: object properties: creatives: 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: data: type: array items: type: object properties: id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ fingerprint: type: string type: type: string enum: - image - video mime_type: type: string byte_size: type: integer width: type: - integer - 'null' height: type: - integer - 'null' aspect_ratio: type: - number - 'null' duration_ms: type: - integer - 'null' original_filename: type: - string - 'null' source_ref: type: - string - 'null' tags: description: Stored tags, including provenance when supplied. An empty map is returned as an empty array. Existing creatives keep their original tags. anyOf: - type: object additionalProperties: type: string - type: array items: type: string maxItems: 0 status: type: string enum: - draft - approved - retired created_at: type: - string - 'null' updated_at: type: - string - 'null' required: - id - fingerprint - type - mime_type - byte_size - width - height - aspect_ratio - duration_ms - source_ref - tags - status - created_at - updated_at total: type: integer per_page: type: integer next_cursor: type: - integer - 'null' truncated: type: boolean continuation: type: string required: - creatives '404': description: A disabled Creative surface or missing workspace returns the api_route_not_found envelope with error, request and resources. 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' '403': description: The workspace grant does not authorize this operation, or the token cannot access this workspace. Returns message. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' servers: - url: https://plainrouter.com/api/v1 description: Production /workspaces/{workspace}/admin/creatives/from-url: post: operationId: storeCreativeFromUrl description: Store a creative in the credential-bound workspace. Set generator, generator_job and parent_creative tags for output from an outside tool. summary: Store a creative from a URL tags: - Creative parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer requestBody: required: true content: application/json: schema: type: object properties: url: type: string description: Vetted HTTPS URL to fetch; private addresses and unsafe redirects are refused. maxLength: 2048 tags: type: object description: Optional flat map of at most 32 string tags; reserved keys count toward the cap. For a creative from an outside tool, set generator and generator_job, plus parent_creative for a variation. Values are stored unchanged. PlainRouter’s own creative tools will set these on every creative they make; intake performs no generation, rendering or scoring. properties: generator: type: string description: Lowercase slug naming the outside tool that made the creative. pattern: ^[a-z0-9-]{1,64}$ minLength: 1 maxLength: 64 generator_job: type: string description: Printable characters naming the tool’s job or render; control and other non-printable characters are refused. minLength: 1 maxLength: 255 parent_creative: type: string description: 'The id returned by the creative APIs: a lowercase ULID of an existing creative in the same workspace that this creative varies.' pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ additionalProperties: type: string maxLength: 255 source_ref: type: - string - 'null' maxLength: 255 required: - url responses: '201': description: Creative stored, or the existing creative returned for identical bytes in this workspace. content: application/json: schema: $ref: '#/components/schemas/CreativeIntakeRead' '401': description: A valid workspace-bound credential is required. content: application/json: schema: $ref: '#/components/schemas/ErrorMessage' '403': description: The credential or workspace grant does not authorize creative intake. content: application/json: schema: anyOf: - $ref: '#/components/schemas/ErrorMessage' - $ref: '#/components/schemas/AgentCredentialError' '404': description: Creative intake is disabled, or the workspace is missing or inaccessible. content: application/json: schema: $ref: '#/components/schemas/ApiRouteNotFound' '422': description: A reserved tag that violates the provenance contract returns error.code creative_provenance_invalid and error.message naming the key. Other intake refusals return their stable error.code and error.message; request-shape errors return message plus field errors. content: application/json: schema: anyOf: - $ref: '#/components/schemas/CreativeIntakeRejected' - $ref: '#/components/schemas/ValidationError' '413': description: Creative bytes exceed the 4 GiB cap, or POST data exceeds the request limit. content: application/json: schema: anyOf: - $ref: '#/components/schemas/CreativeIntakeRejected' - $ref: '#/components/schemas/ErrorMessage' security: - creativeWriter: [] servers: - url: https://plainrouter.com/api/v1 description: Production /workspaces/{workspace}/admin/creatives/{creative}/status: patch: operationId: launch.creatives.status description: Read a creative or change its approval status within the authorized workspace. Creative content and provider submission remain separate operations. summary: Update a workspace creative status tags: - Creative parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: creative in: path required: true schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateCreativeStatusRequest' responses: '200': description: '' content: application/json: schema: type: object properties: creative: type: array items: {} required: - creative '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/creatives/{creative}: get: operationId: launch.creatives.show description: Read a creative or change its approval status within the authorized workspace. Creative content and provider submission remain separate operations. summary: Read a workspace creative tags: - Creative parameters: - name: workspace in: path required: true description: The workspace ID schema: type: integer - name: creative in: path required: true schema: type: string responses: '200': description: '' content: application/json: schema: type: object properties: creative: type: array items: {} required: - creative '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 components: schemas: CreativeIntakeRejected: type: object properties: error: type: object properties: code: type: string message: type: string required: - code - message required: - error title: CreativeIntakeRejected CreativeIntakeRead: type: object properties: creative: type: object properties: id: type: string pattern: ^[0-7][0-9a-hjkmnp-tv-z]{25}$ fingerprint: type: string type: type: string enum: - image - video mime_type: type: string byte_size: type: integer width: type: - integer - 'null' height: type: - integer - 'null' aspect_ratio: type: - number - 'null' duration_ms: type: - integer - 'null' original_filename: type: - string - 'null' source_ref: type: - string - 'null' tags: description: Stored tags, including provenance when supplied. An empty map is returned as an empty array. Existing creatives keep their original tags. anyOf: - type: object additionalProperties: type: string - type: array items: type: string maxItems: 0 status: type: string enum: - draft - approved - retired created_at: type: - string - 'null' updated_at: type: - string - 'null' required: - id - fingerprint - type - mime_type - byte_size - width - height - aspect_ratio - duration_ms - source_ref - tags - status - created_at - updated_at required: - creative title: CreativeIntakeRead AgentCredentialError: type: object properties: error: type: string enum: - invalid_token - insufficient_scope error_description: type: string required: - error - error_description title: AgentCredentialError 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 ErrorMessage: type: object properties: message: type: string required: - message title: ErrorMessage UpdateCreativeStatusRequest: type: object properties: status: type: string enum: - draft - approved - retired required: - status title: UpdateCreativeStatusRequest 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