---
title: "Create or replace project automation"
method: PUT
path: "/v1/project_automation"
tags: ["ProjectAutomations"]
---

# Create or replace project automation

`PUT /v1/project_automation`

## Request

- Base URL: `https://api.braintrust.dev`
- URL: `https://api.braintrust.dev/v1/project_automation`
- Auth: one of:
  - HTTP bearer
  - none

Create or replace project automation. If there is an existing project automation with the same name as the one specified in the request, will replace the existing project automation with the provided fields

## Request body

- CreateProjectAutomation
  - `project_id` string, uuid, required: Unique identifier for the project that the project automation belongs under
  - `name` string, required: Name of the project automation
  - `description` string, nullable: Textual description of the project automation
  - `config` union, required: The configuration for the automation rule
    - object
      - `event_type` 'logs', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `btql_filter` string, required: BTQL filter to identify rows for the automation rule
      - `interval_seconds` number, required: Perform the triggered action at most once in this interval of seconds
      - `action` union, required: The action to take when the automation rule is triggered
        - object
          - `type` 'webhook', required: The type of action to take
          - `url` string, required: The webhook URL to send the request to
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
        - object
          - `type` 'slack', required: The type of action to take
          - `workspace_id` string, required: The Slack workspace ID to post to
          - `channel` string, required: The Slack channel ID to post to
          - `message_template` string: Custom message template for the alert
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
        - object
          - `type` 'pagerduty', required: The type of action to take
          - `routing_key_secret_name` string, uuid, required: The data-plane secret containing the PagerDuty routing key
          - `severity` 'critical' | 'error' | 'warning' | 'info', required
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
    - object
      - `event_type` 'btql_export', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `export_definition` union, required: The definition of what to export
        - object
          - `type` 'log_traces', required
        - object
          - `type` 'log_spans', required
        - object
          - `type` 'btql_query', required
          - `btql_query` string, required: The BTQL query to export
      - `scope` union: Execution scope for export automation. Defaults to span-level execution.
        - SpanScope: Process individual spans
          - `type` 'span', required
        - TraceScope: Process entire traces (all spans sharing the same root_span_id)
          - `type` 'trace', required
          - `idle_seconds` number: Consider trace complete after this many seconds of inactivity (default: 30)
        - GroupScope: Process spans/traces grouped by a field (e.g., session_id)
          - `type` 'group', required
          - `group_by` string, required: Field path to group by, e.g. metadata.session_id
          - `interval_seconds` number: Maximum time range to include when constructing a group
          - `max_traces` integer: Maximum number of traces to include when constructing a group (default/max: 64)
          - `placement` 'first' | 'each', required: Which trace or traces to write grouped scorer results to
          - `idle_seconds` number: Optional: trigger after this many seconds of inactivity
      - `export_path` string, required: The path to export the results to. It should include the storage protocol and prefix, e.g. s3://bucket-name/path/to/export
      - `format` 'jsonl' | 'parquet', required: The format to export the results in
      - `interval_seconds` number, required: Perform the triggered action at most once in this interval of seconds
      - `credentials` union, required
        - object
          - `type` 'aws_iam', required
          - `role_arn` string, required: The ARN of the IAM role to use
          - `external_id` string, required: The automation-specific external id component (auto-generated by default)
        - object
          - `type` 'gcp_service_account', required
          - `service_account_email` string, required: The GCP service account email to impersonate
          - `credential_name` string: The name of a Google workload identity federation credential configured in this organization's AI providers. Supported data planes can use it regardless of hosting environment. If omitted, the data plane's GCP identity is used.
      - `batch_size` number, nullable: The number of rows to export in each batch
    - object
      - `event_type` 'async_query', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `created_by_user_id` string, uuid, required: The user who submitted the async query
      - `object_type` 'project_logs' | 'experiment' | 'dataset' | 'playground_logs', required: The source object type for the async query
      - `object_id` string, required: The source object ID for the async query
      - `query` string, required: The SQL query to execute asynchronously
      - `format` 'jsonl', required: The materialized result format
      - `batch_size` integer, nullable: The maximum number of result rows to write per async query batch
    - object
      - `event_type` 'retention', required: The type of automation.
      - `object_type` 'project_logs' | 'experiment' | 'dataset', required: The object type that the retention policy applies to
      - `retention_days` integer, required: The number of days to retain the object
    - object
      - `event_type` 'environment_update', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `environment_filter` string[]: Optional list of environment slugs to filter by
      - `action` union, required: The action to take when the automation rule is triggered
        - object
          - `type` 'webhook', required: The type of action to take
          - `url` string, required: The webhook URL to send the request to
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
        - object
          - `type` 'slack', required: The type of action to take
          - `workspace_id` string, required: The Slack workspace ID to post to
          - `channel` string, required: The Slack channel ID to post to
          - `message_template` string: Custom message template for the alert
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
        - object
          - `type` 'pagerduty', required: The type of action to take
          - `routing_key_secret_name` string, uuid, required: The data-plane secret containing the PagerDuty routing key
          - `severity` 'critical' | 'error' | 'warning' | 'info', required
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
    - WindowedAutomationConfig
      - `event_type` 'windowed', required: The type of automation.
      - `product_origin` 'patterns' | 'dashboard' | 'null', nullable: The product surface that created and manages the automation
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `continuous` boolean: Between scheduled runs, check for accumulated new work. The executor decides how to process it.
      - `threshold` object: Optional calculation and lifecycle policy that gate scheduled delivery
        - `calculation` object, required: The calculation evaluated for each window
          - `type` 'btql', required
          - `btql_query` string, required: A project-scoped BTQL or SQL query without runtime-owned evaluation time bounds
          - `output` object, required
            - `type` 'scalar', required
            - `value_column` string, required: The numeric result column produced by the query
        - `policy` object, required: The lifecycle policy applied to each calculation result
          - `condition` object, required
            - `type` 'threshold', required
            - `operator` 'lt' | 'lte' | 'gt' | 'gte' | 'eq' | 'neq', required
            - `threshold` number, required
          - `pending_seconds` integer, required: How long the condition must remain breached before firing
          - `no_data_behavior` 'keep_last' | 'resolve' | 'alert', required: How the lifecycle changes when the calculation returns no data
          - `renotify_interval_seconds` integer, nullable: Optional reminder interval while the automation is firing
          - `notify_on_recovery` boolean: Whether to deliver actions when a firing automation recovers
      - `window` object, required
        - `window_seconds` integer, required: How much recent data each scheduled run covers
        - `schedule` union, required: How often the windowed automation runs: at a fixed interval or on a cron schedule
          - object
            - `type` 'interval', required
            - `evaluation_interval_seconds` integer, required: How often the automation runs
          - object
            - `type` 'cron', required
            - `cron_expression` string, required: A standard five-field cron expression (minute hour day-of-month month day-of-week) controlling when the automation runs
            - `timezone` string, nullable: IANA timezone used to interpret the cron expression (defaults to UTC)
        - `evaluation_delay_seconds` integer, required: How far behind the present each evaluation window ends
      - `loop` object: Optional Loop agent to run for each triggered window
        - `prompt` string, required: Instructions for the Loop agent
        - `include_trigger_input` boolean: Whether to include the automation trigger payload as input
        - `agent_slug` string, required: The Loop agent to run
        - `auto_approve_tools` string[]: Write tools that may run without interactive approval
        - `harness` 'native' | 'codex' | 'claude-code' | 'pi': Required for an explicit model selection. Currently only Codex is supported.
        - `model` string: Omit harness, model, and endpoint_name to inherit the organization default provider and its recommended execution settings.
        - `endpoint_name` string
        - `reasoning_effort` 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
        - `target` object: The object this automation writes to or maintains
          - `type` 'dashboard', required
          - `id` union, required
            - string, uuid
            - 'overview'
      - `actions` union[]: Delivery actions exposed to Loop as tools, or run directly when Loop is not configured
        - union: A delivery action available to an automation
          - object
            - `type` 'webhook', required: The type of action to take
            - `url` string, required: The webhook URL to send the request to
            - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
          - object
            - `type` 'slack', required: The type of action to take
            - `workspace_id` string, required: The Slack workspace ID to post to
            - `channel` string, required: The Slack channel ID to post to
            - `message_template` string: Custom message template for the alert
            - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
          - object
            - `type` 'pagerduty', required: The type of action to take
            - `routing_key_secret_name` string, uuid, required: The data-plane secret containing the PagerDuty routing key
            - `severity` 'critical' | 'error' | 'warning' | 'info', required
            - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
    - TopicAutomationConfig
      - `event_type` 'topic', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `sampling_rate` number, required: The sampling rate for topic automation
      - `facet_model` 'brain-facet-latest' | 'brain-facet-1' | 'brain-facet-2' | 'null', nullable: Optional facet model override for topic automation
      - `facet_functions` union[], required: Facet functions used by the topic automation
        - union
          - object
            - `type` 'function', required
            - `id` string, required
            - `version` string: The version of the function
          - object
            - `type` 'global', required
            - `name` string, required
            - `function_type` 'llm' | 'scorer' | 'task' | 'tool' | 'custom_view' | 'preprocessor' | 'facet' | 'classifier' | 'tag' | 'parameters' | 'sandbox': The type of global function. Defaults to 'scorer'.
      - `topic_map_functions` TopicMapFunctionAutomation[], required: Topic map functions with optional per-topic-map filters
        - `function` union, required
          - object
            - `type` 'function', required
            - `id` string, required
            - `version` string: The version of the function
          - object
            - `type` 'global', required
            - `name` string, required
            - `function_type` 'llm' | 'scorer' | 'task' | 'tool' | 'custom_view' | 'preprocessor' | 'facet' | 'classifier' | 'tag' | 'parameters' | 'sandbox': The type of global function. Defaults to 'scorer'.
        - `btql_filter` string, nullable: Per-topic-map BTQL filter. For trace scope, a topic map runs when max(filter) over the trace is truthy. For span scope, it runs when the current span matches.
      - `scope` union: Execution scope for topic automation.
        - SpanScope: Process individual spans
          - `type` 'span', required
        - TraceScope: Process entire traces (all spans sharing the same root_span_id)
          - `type` 'trace', required
          - `idle_seconds` number: Consider trace complete after this many seconds of inactivity (default: 30)
        - GroupScope: Process spans/traces grouped by a field (e.g., session_id)
          - `type` 'group', required
          - `group_by` string, required: Field path to group by, e.g. metadata.session_id
          - `interval_seconds` number: Maximum time range to include when constructing a group
          - `max_traces` integer: Maximum number of traces to include when constructing a group (default/max: 64)
          - `placement` 'first' | 'each', required: Which trace or traces to write grouped scorer results to
          - `idle_seconds` number: Optional: trigger after this many seconds of inactivity
      - `data_scope` union: Optional data scope for topic automation.
        - object
          - `type` 'project_logs', required
        - object
          - `type` 'project_experiments', required
        - object
          - `type` 'experiment', required
          - `experiment_id` string, required
      - `btql_filter` string, nullable: Optional BTQL filter applied before topic automation.
      - `rerun_seconds` number, nullable: How often to recompute topic maps
      - `relabel_overlap_seconds` number, nullable: Deprecated. How much recent history to relabel after a new topic map version becomes active. Data planes v2.15.0 and later ignore this and apply a topic map version after the first only to traces classified once it is active.
      - `backfill_time_range` union: Topic window used for classification coverage and initial backfill.
        - string
        - object
          - `from` string, required
          - `to` string, required
    - TopicDigestAutomationConfig
      - `event_type` 'topic_digest', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `window_seconds` integer: How much recent history to include in each digest
      - `scheduled_time_minutes_utc` integer, required: Minutes after midnight UTC when the digest should be sent
      - `action` object, required: The Slack action to take when the digest is sent
        - `type` 'slack', required: The type of action to take
        - `workspace_id` string, required: The Slack workspace ID to post to
        - `channel` string, required: The Slack channel ID to post to
        - `message_template` string: Custom message template for the alert
        - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
      - `topic_map_function_ids` string[]: Optional topic map function IDs to include in the digest

## Response `200`

Returns the new project automation object

- ProjectAutomation
  - `id` string, uuid, required: Unique identifier for the project automation
  - `project_id` string, uuid, required: Unique identifier for the project that the project automation belongs under
  - `user_id` string, uuid, nullable: Identifies the user who created the project automation
  - `created` string, date-time, nullable: Date of project automation creation
  - `name` string, required: Name of the project automation
  - `description` string, nullable: Textual description of the project automation
  - `config` union, required: The configuration for the automation rule
    - object
      - `event_type` 'logs', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `btql_filter` string, required: BTQL filter to identify rows for the automation rule
      - `interval_seconds` number, required: Perform the triggered action at most once in this interval of seconds
      - `action` union, required: The action to take when the automation rule is triggered
        - object
          - `type` 'webhook', required: The type of action to take
          - `url` string, required: The webhook URL to send the request to
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
        - object
          - `type` 'slack', required: The type of action to take
          - `workspace_id` string, required: The Slack workspace ID to post to
          - `channel` string, required: The Slack channel ID to post to
          - `message_template` string: Custom message template for the alert
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
        - object
          - `type` 'pagerduty', required: The type of action to take
          - `routing_key_secret_name` string, uuid, required: The data-plane secret containing the PagerDuty routing key
          - `severity` 'critical' | 'error' | 'warning' | 'info', required
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
    - object
      - `event_type` 'btql_export', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `export_definition` union, required: The definition of what to export
        - object
          - `type` 'log_traces', required
        - object
          - `type` 'log_spans', required
        - object
          - `type` 'btql_query', required
          - `btql_query` string, required: The BTQL query to export
      - `scope` union: Execution scope for export automation. Defaults to span-level execution.
        - SpanScope: Process individual spans
          - `type` 'span', required
        - TraceScope: Process entire traces (all spans sharing the same root_span_id)
          - `type` 'trace', required
          - `idle_seconds` number: Consider trace complete after this many seconds of inactivity (default: 30)
        - GroupScope: Process spans/traces grouped by a field (e.g., session_id)
          - `type` 'group', required
          - `group_by` string, required: Field path to group by, e.g. metadata.session_id
          - `interval_seconds` number: Maximum time range to include when constructing a group
          - `max_traces` integer: Maximum number of traces to include when constructing a group (default/max: 64)
          - `placement` 'first' | 'each', required: Which trace or traces to write grouped scorer results to
          - `idle_seconds` number: Optional: trigger after this many seconds of inactivity
      - `export_path` string, required: The path to export the results to. It should include the storage protocol and prefix, e.g. s3://bucket-name/path/to/export
      - `format` 'jsonl' | 'parquet', required: The format to export the results in
      - `interval_seconds` number, required: Perform the triggered action at most once in this interval of seconds
      - `credentials` union, required
        - object
          - `type` 'aws_iam', required
          - `role_arn` string, required: The ARN of the IAM role to use
          - `external_id` string, required: The automation-specific external id component (auto-generated by default)
        - object
          - `type` 'gcp_service_account', required
          - `service_account_email` string, required: The GCP service account email to impersonate
          - `credential_name` string: The name of a Google workload identity federation credential configured in this organization's AI providers. Supported data planes can use it regardless of hosting environment. If omitted, the data plane's GCP identity is used.
      - `batch_size` number, nullable: The number of rows to export in each batch
    - object
      - `event_type` 'async_query', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `created_by_user_id` string, uuid, required: The user who submitted the async query
      - `object_type` 'project_logs' | 'experiment' | 'dataset' | 'playground_logs', required: The source object type for the async query
      - `object_id` string, required: The source object ID for the async query
      - `query` string, required: The SQL query to execute asynchronously
      - `format` 'jsonl', required: The materialized result format
      - `batch_size` integer, nullable: The maximum number of result rows to write per async query batch
    - object
      - `event_type` 'retention', required: The type of automation.
      - `object_type` 'project_logs' | 'experiment' | 'dataset', required: The object type that the retention policy applies to
      - `retention_days` integer, required: The number of days to retain the object
    - object
      - `event_type` 'environment_update', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `environment_filter` string[]: Optional list of environment slugs to filter by
      - `action` union, required: The action to take when the automation rule is triggered
        - object
          - `type` 'webhook', required: The type of action to take
          - `url` string, required: The webhook URL to send the request to
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
        - object
          - `type` 'slack', required: The type of action to take
          - `workspace_id` string, required: The Slack workspace ID to post to
          - `channel` string, required: The Slack channel ID to post to
          - `message_template` string: Custom message template for the alert
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
        - object
          - `type` 'pagerduty', required: The type of action to take
          - `routing_key_secret_name` string, uuid, required: The data-plane secret containing the PagerDuty routing key
          - `severity` 'critical' | 'error' | 'warning' | 'info', required
          - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
    - WindowedAutomationConfig
      - `event_type` 'windowed', required: The type of automation.
      - `product_origin` 'patterns' | 'dashboard' | 'null', nullable: The product surface that created and manages the automation
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `continuous` boolean: Between scheduled runs, check for accumulated new work. The executor decides how to process it.
      - `threshold` object: Optional calculation and lifecycle policy that gate scheduled delivery
        - `calculation` object, required: The calculation evaluated for each window
          - `type` 'btql', required
          - `btql_query` string, required: A project-scoped BTQL or SQL query without runtime-owned evaluation time bounds
          - `output` object, required
            - `type` 'scalar', required
            - `value_column` string, required: The numeric result column produced by the query
        - `policy` object, required: The lifecycle policy applied to each calculation result
          - `condition` object, required
            - `type` 'threshold', required
            - `operator` 'lt' | 'lte' | 'gt' | 'gte' | 'eq' | 'neq', required
            - `threshold` number, required
          - `pending_seconds` integer, required: How long the condition must remain breached before firing
          - `no_data_behavior` 'keep_last' | 'resolve' | 'alert', required: How the lifecycle changes when the calculation returns no data
          - `renotify_interval_seconds` integer, nullable: Optional reminder interval while the automation is firing
          - `notify_on_recovery` boolean: Whether to deliver actions when a firing automation recovers
      - `window` object, required
        - `window_seconds` integer, required: How much recent data each scheduled run covers
        - `schedule` union, required: How often the windowed automation runs: at a fixed interval or on a cron schedule
          - object
            - `type` 'interval', required
            - `evaluation_interval_seconds` integer, required: How often the automation runs
          - object
            - `type` 'cron', required
            - `cron_expression` string, required: A standard five-field cron expression (minute hour day-of-month month day-of-week) controlling when the automation runs
            - `timezone` string, nullable: IANA timezone used to interpret the cron expression (defaults to UTC)
        - `evaluation_delay_seconds` integer, required: How far behind the present each evaluation window ends
      - `loop` object: Optional Loop agent to run for each triggered window
        - `prompt` string, required: Instructions for the Loop agent
        - `include_trigger_input` boolean: Whether to include the automation trigger payload as input
        - `agent_slug` string, required: The Loop agent to run
        - `auto_approve_tools` string[]: Write tools that may run without interactive approval
        - `harness` 'native' | 'codex' | 'claude-code' | 'pi': Required for an explicit model selection. Currently only Codex is supported.
        - `model` string: Omit harness, model, and endpoint_name to inherit the organization default provider and its recommended execution settings.
        - `endpoint_name` string
        - `reasoning_effort` 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
        - `target` object: The object this automation writes to or maintains
          - `type` 'dashboard', required
          - `id` union, required
            - string, uuid
            - 'overview'
      - `actions` union[]: Delivery actions exposed to Loop as tools, or run directly when Loop is not configured
        - union: A delivery action available to an automation
          - object
            - `type` 'webhook', required: The type of action to take
            - `url` string, required: The webhook URL to send the request to
            - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
          - object
            - `type` 'slack', required: The type of action to take
            - `workspace_id` string, required: The Slack workspace ID to post to
            - `channel` string, required: The Slack channel ID to post to
            - `message_template` string: Custom message template for the alert
            - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
          - object
            - `type` 'pagerduty', required: The type of action to take
            - `routing_key_secret_name` string, uuid, required: The data-plane secret containing the PagerDuty routing key
            - `severity` 'critical' | 'error' | 'warning' | 'info', required
            - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
    - TopicAutomationConfig
      - `event_type` 'topic', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `sampling_rate` number, required: The sampling rate for topic automation
      - `facet_model` 'brain-facet-latest' | 'brain-facet-1' | 'brain-facet-2' | 'null', nullable: Optional facet model override for topic automation
      - `facet_functions` union[], required: Facet functions used by the topic automation
        - union
          - object
            - `type` 'function', required
            - `id` string, required
            - `version` string: The version of the function
          - object
            - `type` 'global', required
            - `name` string, required
            - `function_type` 'llm' | 'scorer' | 'task' | 'tool' | 'custom_view' | 'preprocessor' | 'facet' | 'classifier' | 'tag' | 'parameters' | 'sandbox': The type of global function. Defaults to 'scorer'.
      - `topic_map_functions` TopicMapFunctionAutomation[], required: Topic map functions with optional per-topic-map filters
        - `function` union, required
          - object
            - `type` 'function', required
            - `id` string, required
            - `version` string: The version of the function
          - object
            - `type` 'global', required
            - `name` string, required
            - `function_type` 'llm' | 'scorer' | 'task' | 'tool' | 'custom_view' | 'preprocessor' | 'facet' | 'classifier' | 'tag' | 'parameters' | 'sandbox': The type of global function. Defaults to 'scorer'.
        - `btql_filter` string, nullable: Per-topic-map BTQL filter. For trace scope, a topic map runs when max(filter) over the trace is truthy. For span scope, it runs when the current span matches.
      - `scope` union: Execution scope for topic automation.
        - SpanScope: Process individual spans
          - `type` 'span', required
        - TraceScope: Process entire traces (all spans sharing the same root_span_id)
          - `type` 'trace', required
          - `idle_seconds` number: Consider trace complete after this many seconds of inactivity (default: 30)
        - GroupScope: Process spans/traces grouped by a field (e.g., session_id)
          - `type` 'group', required
          - `group_by` string, required: Field path to group by, e.g. metadata.session_id
          - `interval_seconds` number: Maximum time range to include when constructing a group
          - `max_traces` integer: Maximum number of traces to include when constructing a group (default/max: 64)
          - `placement` 'first' | 'each', required: Which trace or traces to write grouped scorer results to
          - `idle_seconds` number: Optional: trigger after this many seconds of inactivity
      - `data_scope` union: Optional data scope for topic automation.
        - object
          - `type` 'project_logs', required
        - object
          - `type` 'project_experiments', required
        - object
          - `type` 'experiment', required
          - `experiment_id` string, required
      - `btql_filter` string, nullable: Optional BTQL filter applied before topic automation.
      - `rerun_seconds` number, nullable: How often to recompute topic maps
      - `relabel_overlap_seconds` number, nullable: Deprecated. How much recent history to relabel after a new topic map version becomes active. Data planes v2.15.0 and later ignore this and apply a topic map version after the first only to traces classified once it is active.
      - `backfill_time_range` union: Topic window used for classification coverage and initial backfill.
        - string
        - object
          - `from` string, required
          - `to` string, required
    - TopicDigestAutomationConfig
      - `event_type` 'topic_digest', required: The type of automation.
      - `status` 'active' | 'paused': Whether the automation is active or paused.
      - `window_seconds` integer: How much recent history to include in each digest
      - `scheduled_time_minutes_utc` integer, required: Minutes after midnight UTC when the digest should be sent
      - `action` object, required: The Slack action to take when the digest is sent
        - `type` 'slack', required: The type of action to take
        - `workspace_id` string, required: The Slack workspace ID to post to
        - `channel` string, required: The Slack channel ID to post to
        - `message_template` string: Custom message template for the alert
        - `formatting_prompt` string: Instructions for Loop to format content sent to this destination
      - `topic_map_function_ids` string[]: Optional topic map function IDs to include in the digest
  - `runtime_block` object
    - `reason` string, required
    - `model` string, required

## Other responses

- `400`: The request was unacceptable, often due to missing a required parameter
- `401`: No valid API key provided
- `403`: The API key doesn’t have permissions to perform the request
- `429`: Too many requests hit the API too quickly. We recommend an exponential backoff of your requests
- `500`: Something went wrong on Braintrust's end. (These are rare.)

## Changes

- **2026-10-30** `8f5039399f77`: 15 breaking, 6 info
  - the request property `config/oneOf[#/components/schemas/TopicAutomationConfig]/btql_filter` became not nullable
  - the request property `config/oneOf[#/components/schemas/TopicAutomationConfig]/facet_model` became not nullable
  - the request property `config/oneOf[#/components/schemas/TopicAutomationConfig]/relabel_overlap_seconds` became not nullable
  - the request property `config/oneOf[#/components/schemas/TopicAutomationConfig]/rerun_seconds` became not nullable
  - …17 more
- **2026-10-29** `6409b3f2bde5`: 2 breaking, 2 info
  - removed `subschema #1, subschema #5` from the `config` request property `oneOf` list
  - added `subschema #1, subschema #5` to the `config` response property `oneOf` list for the response status `200`
  - added `subschema #1, subschema #5` to the `config` request property `oneOf` list
  - removed `subschema #1, subschema #5` from the `config` response property `oneOf` list for the response status `200`
- **2026-10-22** `0426dee78e5b`: 2 info
  - added the new optional request property `config/oneOf[#/components/schemas/WindowedAutomationConfig]/continuous`
  - added the optional property `config/oneOf[#/components/schemas/WindowedAutomationConfig]/continuous` to the response with the `200` status
- …earlier changes not shown

[Operation history](https://skmtc.dev/braintrustdata/apis/braintrust-api/changes/v1/project_automation/put.md)

---

[API](https://skmtc.dev/braintrustdata/apis/braintrust-api.md) - [All operations](https://skmtc.dev/braintrustdata/apis/braintrust-api/llms.txt) - [OpenAPI document](https://skmtc.dev/braintrustdata/apis/braintrust-api/revisions/19184ec94018?raw)
