Get started by cloning the custom-integrations-starter-kit template repo.

Git sync is the recommended way to sync custom integrations into Tracecat in production. For development,

Tracecat uses YAML to define inputs and configurations for actions and workflows. YAML is a human-readable configuration language that is easy to write and read. It is also more concise than JSON and more customizable than HTML forms.

If you’re new to YAML, you can learn more here. YAML is also widely used in DevOps tools like Ansible, GitHub Actions, and Docker Compose.

By the end of this tutorial, you’ll learn how to:

  • Sync custom integrations into Tracecat
  • Build custom integrations in YAML and Python

Prerequisites

  • Access to a private Git repository (e.g. GitHub, GitLab)
  • Basic knowledge of Python, pip, and YAML

Sync remote git repository

In this section, we’ll walk through the process of securely syncing custom integrations into Tracecat.

1

Create new repo

The first step is to create a new repo from the custom-integrations-starter-kit template.

2

Configure remote repo URL

Go to the Git Repository section in Organization settings and configure the remote repo URL to point to your private repository.

3

GitHub SSH deploy key

Create a SSH public / private key pair (without a passphrase) and store the public key in GitHub. Store the public key in GitHub.

4

Private key in Tracecat

Go to the SSH Keys section in Organizations settings and add the private key with the name github-ssh-key.

Do not change the key name. The github-ssh-key key name works for both GitHub and GitLab.

5

Refresh repos in Tracecat

Go to the Repositories section in the Registry page and refresh the repos. Your custom repo should now be visible.

6

Sync custom repo

In the same Repositories section, select your custom repo and press the Sync from remote. Your custom integrations should now be visible in your Tracecat instance’s registry.

7

View custom integrations

Go to the Actions view in the Registry page and filter by Origin to view synced actions.

8

🎉 That's it!

Feel free to add your own custom Python UDFs and YAML templates to the custom integrations repo.

Just push to your changes to the git repo and press sync in Tracecat Registry. Updates and new actions will show up immediately.

Action Templates

If you find yourself using a generic action (e.g. core.http_request) with the same parameters in multiple workflows, we recommend creating a YAML action template.

Check out Tracecat’s open source Action Templates on GitHub for examples. More information on when and how to build effective templates can be found in the integrations best practices.

Action Templates are custom integrations built on Tracecat’s YAML-based DSL (domain specific language). A template comprises of the following elements:

  • A unique title, description, and display group
  • A namespace (e.g. tools.falconpy) and name (e.g. call_command) for the action
  • Inputs defined in the expects section
  • Steps defined in the steps section
  • An optional secrets section

Steps consist of 1-2 commonly used actions, such as core.http_request, configured to call an external API. These steps are also parameterized with the inputs defined in the expects section.

Only data specified in the returns field of the template is logged in Tracecat workflows. Outputs between steps are not logged unless specified in returns.

Every template follows the same structure:

type: action
definition:
  title: <title>
  description: <description>
  display_group: <display_group>
  doc_url: <doc_url>
  namespace: <namespace>
  name: <name>
  secrets:  # Secrets are optional
    - name: <secret_name>
      keys:
        - <key_name>
      optional_keys:
        - <key_name>
  expects:
    <param_name>:
      type: <type>
      description: <description>
      default: <default_value>  # Optional
  steps:
    - ref: <step_id>
      action: <action_namespace>.<action_name>
      args:
        <param_name>: <param_value>
  returns: <returns>

Actions in templates

Templates support all actions defined in the Tracecat Registry. Actions, such as core.http_request and tools.falconpy.call_command, can all be used in templates by specifying the steps.action field.

Expressions in templates

Templates support the following expressions:

  • inputs: Reference inputs into the action as defined in the expects section.
  • steps: Reference results from previous steps in the same template.
  • SECRETS: Reference secrets.
  • FN: Reference functions.

inputs and steps are expressions specific to templates. SECRETS and FN are used in the same way as in workflows.

This means that templates have full support for Tracecat’s powerful inline functions. For example:

${{ FN.to_isoformat(inputs.start_time) }}
${{ FN.strip(FN.to_base64url(inputs.url), "=") }}

Secrets in templates

Secrets used in templates must be defined in the secrets section. You must specify the secret’s name, key, and (if applicable) optional_keys.

Secrets stored in the secrets manager can be accessed using the SECRETS context: ${{ SECRETS.<name>.<key> }}.

For example:

${{ SECRETS.virustotal.VIRUSTOTAL_API_KEY }}

Tracecat will automatically replace the expression with the secret value at runtime. Retrieved secrets are removed from memory, i.e. garbage collected, after the action is executed.

Python UDFs

Check out Tracecat’s open source core actions and Python integrations on GitHub for examples.

Tracecat makes it easy to turn your Python scripts into no-code workflow actions, which we call user-defined functions (UDFs). All you need is a:

  • Single Python decorator
  • from typing import Annotated
  • from typing_extensions import Doc

Secrets used in UDFs must be defined as RegistrySecret objects in the secrets argument of the @registry.register decorator. They are retrieved from Tracecat’s secrets manager at runtime and garbage collected after the action is executed.

For example:

from typing import Annotated
from typing_extensions import Doc

from tracecat_registry import registry, RegistrySecret, secrets


# (Optional) Define secrets used in the function
secret_name = RegistrySecret(
  name="secret_name",
  keys=["SECRET_NAME"],
  optional_keys=["OPTIONAL_SECRET_NAME"],
)


# Register the function as a Tracecat UDF
@registry.register(
    default_title="Say Goodbye Secretly",
    description="This is a function that says goodbye",
    display_group="Greetings",
    namespace="integrations.greetings",
    # (Optional) Define secrets used in the function
    secrets=[secret_name],
)
def say_goodbye_secretly(
    name: Annotated[str, Doc("The name to say goodbye to")],
):
    secret = secrets.get("SECRET_NAME")
    # We're returning the secret for demonstration only.
    # Do not do this in your own functions!
    return {"message": f"Goodbye, {name}! Secret: {secret}"}

Next steps