Skip to main content
Tracecat makes it easy to turn your Python scripts into actions for agents and workflows. All you need is:
  • A single Python decorator, @registry.register
  • from typing import Annotated
  • from typing_extensions import Doc

Quick start

  1. Add a Python file to your custom registry package.
  2. Register one async function with @registry.register(...).
  3. Define typed inputs, read secrets with secrets.get(...), and return JSON-serializable data.
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],
)
async 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}"}

What to define

  • Use tools.<integration> for the namespace.
  • Set default_title, description, display_group, doc_url, and namespace in @registry.register(...).
  • Define action inputs with type hints and Field(..., description=...).
  • Define required credentials with RegistrySecret(...) and pass them in secrets=[...].
  • Read secret values with secrets.get(...).
  • Return plain dicts, lists, strings, numbers, or booleans.

Declaring secret types

By default, secrets are custom (arbitrary key-value). If your action requires an SSH key, mTLS certificate, or CA certificate, set secret_type so the credentials UI renders the correct form.
secret_typeRequired keysDescription
"custom"AnyGeneric key-value pairs (default)
"ssh_key"["PRIVATE_KEY"]SSH private key (PEM)
"mtls"["TLS_CERTIFICATE", "TLS_PRIVATE_KEY"]mTLS client certificate and key
"ca_cert"["CA_CERTIFICATE"]CA certificate bundle

Examples

# SSH key secret — renders an SSH private key textarea in the UI
ssh_secret = RegistrySecret(
    name="ansible",
    keys=["PRIVATE_KEY"],
    secret_type="ssh_key",
)

# mTLS secret — renders certificate + key textareas
mtls_secret = RegistrySecret(
    name="mtls",
    keys=["TLS_CERTIFICATE", "TLS_PRIVATE_KEY"],
    secret_type="mtls",
    optional=True,
)

# CA certificate secret — renders a certificate textarea
ca_cert_secret = RegistrySecret(
    name="ca_cert",
    keys=["CA_CERTIFICATE"],
    secret_type="ca_cert",
    optional=True,
)

# Custom secret (default) — renders key-value inputs
api_secret = RegistrySecret(
    name="slack",
    keys=["SLACK_BOT_TOKEN"],
)
secret_type uses snake_case everywhere — Python declarations, the API, and the UI all use the same values (ssh_key, ca_cert, mtls, custom). Use the same ${{ SECRETS... }} syntax as workflow actions. Custom secrets (API keys, SSH, mTLS, CA bundles, and so on):
${{ 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 }}
${{ SECRETS.slack_oauth.SLACK_USER_TOKEN }}
${{ SECRETS.google_docs_oauth.GOOGLE_DOCS_SERVICE_TOKEN }}
Optional fallback when either grant type is allowed:
${{ 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). 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.
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):
definition:
  secrets:
    - name: exa
      keys: ["EXA_API_KEY"]
    - name: ansible
      keys: ["PRIVATE_KEY"]
      secret_type: ssh_key
OAuth:
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 and integrations/slack_sdk.py for UDFs that use OAuth credentials. See tracecat_registry/core/ssh.py for an ssh_key secret example. For YAML templates, see microsoft_teams/send_message.yml (authorization_code), google_docs/create_document.yml (client_credentials), and microsoft_sentinel/.../get_alert_rule_template.yml (optional dual grant). See 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.

Example actions

You can browse all open-source integrations in Tracecat in tools actions. Use template actions to see how actions are composed, and use core actions to inspect built-in implementations. For concrete examples, browse the Slack, AWS Boto3, and FalconPy integrations.