> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracecat.com/llms.txt
> Use this file to discover all available pages before exploring further.

# YAML templates

> Build custom YAML template actions for Tracecat: compose existing actions and expressions into reusable, versioned building blocks for workflows and agents.

Use a YAML action template to compose existing actions into a reusable action.

## Template shape

Create a `.yml` file in your registry templates directory. Define inputs in `expects`, run actions in `steps`, and return the final value from `returns`.

```yaml theme={null}
type: action
definition:
  title: Post message
  description: Post a message to a Slack channel.
  display_group: Slack
  namespace: tools.slack
  name: post_message
  expects:
    channel:
      type: str
      description: The Slack channel ID.
    text:
      type: str
      description: The message text.
  steps:
    - ref: post_message
      action: tools.slack_sdk.call_method
      args:
        sdk_method: chat_postMessage
        params:
          channel: ${{ inputs.channel }}
          text: ${{ inputs.text }}
  returns: ${{ steps.post_message.result }}
```

* Start every file with `type: action`.
* Use `tools.<integration>` for the namespace.
* Set `title`, `description`, `display_group`, `namespace`, and `name` in `definition`.
* Define inputs in `expects`.
* Build the action in `steps`.
* Return the final value from `returns`.

## Expressions

Use `${{ }}` for expressions.

Use `inputs` to read the inputs you defined in `expects`.

Use `steps` to read the result of an earlier step. After a step runs, its output is available at `steps.<ref>.result`.

```yaml Read an input theme={null}
args:
  channel: ${{ inputs.channel }}
  text: ${{ inputs.text }}
```

```yaml Read an earlier step result theme={null}
returns: ${{ steps.post_message.result }}
```

```yaml Build one step from another theme={null}
steps:
  - ref: lookup_user
    action: tools.slack.users.lookup_user_by_email
    args:
      email: ${{ inputs.email }}
  - ref: post_message
    action: tools.slack.chat.post_message
    args:
      channel: ${{ steps.lookup_user.result.id }}
      text: Hello
```

You can use:

* `inputs`
* `steps`
* `SECRETS`
* `VARS`
* `FN.*`

## Secrets

Declare what each template needs under `definition.secrets` (API keys, structured types, or OAuth). That list drives which credentials must exist for the action when the workflow runs. Use the same `${{ SECRETS... }}` paths in `args` as in any other expression.

Use the same `${{ SECRETS... }}` syntax as workflow actions.

Custom secrets (API keys, SSH, mTLS, CA bundles, and so on):

```yaml theme={null}
${{ SECRETS.<secret_name>.<KEY> }}
```

OAuth tokens live under `<provider_id>_oauth`. The key is the provider ID in uppercase plus `_USER_TOKEN` or `_SERVICE_TOKEN`:

* `authorization_code`: `${{ SECRETS.<provider_id>_oauth.<PROVIDER_ID_UPPER>_USER_TOKEN }}`
* `client_credentials`: `${{ SECRETS.<provider_id>_oauth.<PROVIDER_ID_UPPER>_SERVICE_TOKEN }}`

```yaml theme={null}
${{ SECRETS.slack_oauth.SLACK_USER_TOKEN }}
${{ SECRETS.google_docs_oauth.GOOGLE_DOCS_SERVICE_TOKEN }}
```

Optional fallback when either grant type is allowed:

```yaml theme={null}
${{ SECRETS.microsoft_sentinel_oauth.MICROSOFT_SENTINEL_USER_TOKEN || SECRETS.microsoft_sentinel_oauth.MICROSOFT_SENTINEL_SERVICE_TOKEN }}
```

## Python UDFs

Declare credentials in `@registry.register(..., secrets=[...])`.

* `RegistrySecret`: static keys; set `secret_type` to `ssh_key`, `mtls`, or `ca_cert` when needed ([secret types](/automations/core-concepts/secrets)). Optional: `optional`, `optional_keys`.
* `RegistryOAuthSecret`: declares a dependency on an existing OAuth integration. Pass the `provider_id` and `grant_type` that match the integration in your workspace (a mismatch — for example declaring `authorization_code` when the integration uses `client_credentials` — will not resolve).

Read values with `secrets.get("<KEY>")` (key only, e.g. `GOOGLE_DRIVE_USER_TOKEN`), not a `SECRETS.` path.

```python theme={null}
from tracecat_registry import RegistryOAuthSecret, RegistrySecret, registry, secrets

api = RegistrySecret(name="exa", keys=["EXA_API_KEY"])

oauth = RegistryOAuthSecret(
    provider_id="google_drive",
    grant_type="authorization_code",
)
```

## YAML

Under `definition`, set a `secrets` list (same validator as Python).

Custom (optional `secret_type` for structured secrets):

```yaml theme={null}
definition:
  secrets:
    - name: exa
      keys: ["EXA_API_KEY"]
    - name: ansible
      keys: ["PRIVATE_KEY"]
      secret_type: ssh_key
```

OAuth:

```yaml theme={null}
definition:
  secrets:
    - type: oauth
      provider_id: microsoft_teams
      grant_type: authorization_code
```

The `provider_id` and `grant_type` must match an integration already configured in your workspace. Use `${{ SECRETS.<provider_id>_oauth.<TOKEN_KEY> }}` in `args`. Set `optional: true` on an OAuth entry when it is not always required.

## Example providers

See [`integrations/google_drive.py`](https://github.com/TracecatHQ/tracecat/blob/main/packages/tracecat-registry/tracecat_registry/integrations/google_drive.py) and [`integrations/slack_sdk.py`](https://github.com/TracecatHQ/tracecat/blob/main/packages/tracecat-registry/tracecat_registry/integrations/slack_sdk.py) for UDFs that use OAuth credentials. See [`tracecat_registry/core/ssh.py`](https://github.com/TracecatHQ/tracecat/blob/main/packages/tracecat-registry/tracecat_registry/core/ssh.py) for an `ssh_key` secret example.

For YAML templates, see [`microsoft_teams/send_message.yml`](https://github.com/TracecatHQ/tracecat/blob/main/packages/tracecat-registry/tracecat_registry/templates/tools/microsoft_teams/send_message.yml) (`authorization_code`), [`google_docs/create_document.yml`](https://github.com/TracecatHQ/tracecat/blob/main/packages/tracecat-registry/tracecat_registry/templates/tools/google_docs/create_document.yml) (`client_credentials`), and [`microsoft_sentinel/.../get_alert_rule_template.yml`](https://github.com/TracecatHQ/tracecat/blob/main/packages/tracecat-registry/tracecat_registry/templates/tools/microsoft_sentinel/alert_rules/get_alert_rule_template.yml) (optional dual grant). See [`templates/tools/exa/search.yml`](https://github.com/TracecatHQ/tracecat/blob/main/packages/tracecat-registry/tracecat_registry/templates/tools/exa/search.yml) for a simple API key template.

For the full list of built-in provider IDs, see [`tracecat/integrations/providers/__init__.py`](https://github.com/TracecatHQ/tracecat/blob/main/tracecat/integrations/providers/__init__.py).

## Limitations

* Template steps only support `ref`, `action`, and `args`.
* Template steps run in order.
* Template steps do not support `run_if`, `for_each`, `join_strategy`, `start_delay`, `timeout`, or `max_attempts`.
* Templates can call tools actions, other templates, and `core.script.run_python`. Other platform actions are not supported inside templates.
* If a later step fails, you do not get a final result with earlier step outputs. Keep templates short. In practice, do not build templates with more than 2 steps.

## Example templates

All integrations in Tracecat are open source. Browse [template actions](https://github.com/TracecatHQ/tracecat/tree/main/packages/tracecat-registry/tracecat_registry/templates/tools) for more examples.
For concrete examples, browse the [Slack templates](https://github.com/TracecatHQ/tracecat/tree/main/packages/tracecat-registry/tracecat_registry/templates/tools/slack) and [CrowdStrike templates](https://github.com/TracecatHQ/tracecat/tree/main/packages/tracecat-registry/tracecat_registry/templates/tools/crowdstrike).
