Skip to main content
Response Agents as Code lets you define and manage your response agents as YAML files in your own GitHub repository. Cotool polls the repo on a schedule, reconciles the files into your agents, and records every synced change in the agent’s version history. This gives you a GitOps workflow for agents: code review on pull requests, change history in Git, and the ability to roll out the same agents across environments by editing files instead of clicking through the UI.
Managed vs. manual agents: An agent synced from a repo is engine-owned — it is read-only in the Cotool UI. You change a managed agent by editing its YAML and letting the next sync apply it. Agents created in the UI are unaffected; the two can coexist in the same organization.

How It Works

1

You keep agent YAML in a Git repo

Each agent is one YAML file in a folder you choose (default cotool/agents). The file describes the whole agent: prompt, model, tools, skills, inputs, triggers, and more.
2

Cotool polls and reconciles

Roughly every 5 minutes, Cotool fetches the configured branch and makes your agents match the files — creating new agents, updating changed ones, and removing agents whose file disappeared. A commit-SHA check skips the fetch entirely when the branch hasn’t moved.
3

Every change is versioned

Each synced create or update appends an entry to the agent’s version history, tagged with the source commit. An unchanged re-sync adds nothing.

Setup

Response Agents as Code syncs from the GitHub integration. Connect GitHub first, then configure the sync.
1

Connect GitHub

Go to Settings > Integrations and connect GitHub. Install the GitHub App on the organization and repositories you want to sync from.
2

Open the Response Agents as Code card

On the GitHub tool page, find the Response Agents as Code card. (You need the tool.manage permission to configure it.)
3

Choose repo, branch, and path

Pick the repository, branch, and the folder that contains your agent YAML files. The dropdowns are populated from the repos the GitHub App can access. The path defaults to cotool/agents.
4

Enable and save

Turn on Enable sync and click Save config. While enabled, the periodic sync includes your organization; while off, it skips it.
5

Sync now (optional)

Click Sync now to run a sync immediately instead of waiting for the next cycle. The card shows last-sync status and a per-file table with the synced agent or any error.

File Format

Each file defines one response agent. The format is intentionally distinct from the UI’s bulk export/import format — here it’s one agent per file, with the system prompt either inline or in a separate .md file.
apiVersion: cotool.ai/v1
kind: ResponseAgent
metadata:
  sync_key: triage-suspicious-login
  name: Triage Suspicious Login
  description: First-pass triage for IdP suspicious-login alerts.
  tags:
    - auth
    - p1
spec:
  modelAlias: anthropic:chat:sonnet-4.6
  planningMode: auto
  toolNames:
    - slack
  inputs:
    - name: alertId
      type: text
      description: The IdP alert ID to triage.
      required: true
  acceptanceCriteria:
    - Determines whether the login is expected travel or genuinely anomalous.
  triggers:
    - source: cron
      name: Hourly sweep
      schedule: "0 * * * *"
  systemPrompt:
    inline: |
      # Triage Suspicious Login

      You are a first-pass triage agent for identity-provider
      "suspicious login" alerts. Classify each as Expected or Anomalous.

Top-level fields

FieldRequiredNotes
apiVersionoptionalSchema version, e.g. cotool.ai/v1.
kindoptionalMust be ResponseAgent when present.
metadatarequiredIdentity and human-readable info (see below).
specrequiredThe agent definition (see below).

metadata

FieldRequiredNotes
sync_keyoptionalStable, org-unique identity for the agent (see Identity).
namerequiredHuman-readable name. Can change freely — it is not the identity.
descriptionoptionalWhat the agent does.
tagsoptionalFree-text tags for categorizing the agent.

spec

FieldRequiredNotes
modelAliasrequiredModel alias, e.g. anthropic:chat:sonnet-4.6.
systemPromptrequiredEither { inline: "..." } or { file: "prompt.md" } (path relative to the YAML file).
planningModeoptionalauto (default), never, or always. See Planning Modes.
agentFilesystemoptionalMount a persistent Agent Filesystem across runs (default false).
toolNamesoptionalNon-agent tool ids the agent may use.
agentToolsoptionalOther managed agents this agent may call, by sync_key (see Agent-to-agent).
cliNamesoptionalCLI integration ids the agent may use.
skillsoptionalAttached skills by name (org-scoped, tracks the latest version).
inputsoptionalDeclared agent inputs.
acceptanceCriteriaoptionalAcceptance criteria evaluated for each run.
triggersoptionalInline triggers (see Triggers).
structuredOutputSchemaoptionalJSON schema for structured output.
contextDocsoptionalContext documents attached to the agent.
The YAML is strict — unknown or misspelled keys (for example syncKey instead of sync_key) are rejected with a validation error for that file rather than being silently ignored.

Identity

Each file maps to exactly one agent. Cotool decides which agent a file represents by:
  1. metadata.sync_key — an author-chosen identity, unique within your organization. Recommended. Because identity is independent of file content and location, you can rename the agent or move/rename the file without churning the agent or losing its triggers and history.
  2. File path — used when no sync_key is set. Renaming or moving the file then changes its identity (Cotool treats it as a new agent).
Set a sync_key for any agent you expect to rename or reorganize. It’s the only way to keep a webhook trigger’s minted secret and URL stable across a rename.

Triggers

Declare triggers inline under spec.triggers. The sync engine materializes them as engine-owned, read-only triggers on the agent. Supported sources: cron, webhook, jira, jira-automation, slack, linear, and email.
spec:
  triggers:
    - source: cron
      name: Daily report
      schedule: "0 9 * * *"
    - source: webhook
      name: Inbound events
      sync_key: inbound          # stable id; preserves the minted secret across renames
    - source: jira
      name: New security issues
      events:
        - jira:issue_created
      jqlFilter: project = SEC
    - source: slack
      name: Security channel
      allowedChannelIds:
        - C0123456789
Secrets never live in YAML. Webhook secrets and inbound URLs are minted server-side; Slack and Linear bind to your organization’s connected integration at sync time. If the required integration isn’t connected, that trigger is skipped with a diagnostic — the agent itself still syncs, and the file’s status shows integration not connected rather than failing.
A trigger’s sync_key is its stable identity within the agent (it defaults to a slug of name). Set it explicitly to rename a trigger without recreating its row — which, for webhook-family triggers, is what preserves the minted secret and URL. For the full set of fields per source, see Triggering Agents.

Agent-to-Agent References

A YAML agent can call another managed agent as a tool by referencing its sync_key:
spec:
  toolNames:
    - slack
  agentTools:
    - sync_key: enrich-alert
    - sync_key: notify-oncall
References are resolved in a second pass after all agents exist, so forward references work — the order of files doesn’t matter. Always reference other agents via agentTools and sync_key, not via toolNames.

Reconcile Behavior

Each sync makes your agents match the files:
  • New file → a new managed agent is created.
  • Changed file → the managed agent is updated, and the change is recorded as a version.
  • Removed file → the agent is gently soft-deleted. This keeps its triggers and tags and is reversible: if the file returns, the same agent is resurrected (not a fresh copy).
  • Invalid file → the last good version of that agent is kept. The error is recorded against that file and never blocks the others, so one bad file can’t take down the rest of your agents.
The card’s per-file status table shows ok, integration not connected, or a parse/validation error for each file, with a link to the file at the synced commit.

Validate Before Syncing

POST /api/agent-sync/validate runs the exact same parse, schema, and cross-file graph validation as the sync engine, statelessly, over files you post — so it cannot drift from real sync behavior. It’s the engine behind the CI Action below, and you can call it directly:
curl -X POST https://app.cotool.ai/api/agent-sync/validate \
  -H "Authorization: Bearer $COTOOL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "files": [
      { "path": "cotool/agents/triage.yaml", "content": "apiVersion: cotool.ai/v1\nkind: ResponseAgent\n..." }
    ]
  }'
The response carries a result per YAML file with errors (would block sync) and warnings, plus an overall valid flag. Warnings are checks that depend on live org state the request can’t see (an unprovided systemPrompt.file, or an agentTools sync_key not in the posted set) and never fail the check. See the API Reference for the full schema.
Prefer GitHub OIDC over an API key. The $COTOOL_API_KEY above is shown for direct or manual calls and for CI outside GitHub. For GitHub Actions, use the OIDC-based Action below instead — it mints a short-lived, repo-scoped token, stores no secret, and authorizes as a validate-only principal.A Cotool API key, by contrast, is long-lived and carries the full permissions of the user who created it — it is not narrowed to validation, even though the endpoint only needs tool.manage — so a leaked key is as powerful as that user. If you must use one (non-GitHub or self-hosted CI), create it under a dedicated, least-privilege service account, keep it in a secrets manager, and rotate it regularly.

Validate in CI

The cotool/validate-agents GitHub Action validates your agent YAML on every pull request and annotates problems inline. The Action authenticates only via GitHub OIDC — there is no API-key input, and it is the recommended way to validate in CI. The job mints a short-lived, repo-scoped OIDC token; Cotool verifies it and maps the repository to your org via your GitOps sync configuration. Nothing to store or rotate, and the token grants validate-only access — never your broader API-key permissions.
# .github/workflows/validate-agents.yml
on:
  pull_request:
    paths: ['cotool/agents/**']
permissions:
  contents: read
  id-token: write          # lets the job mint the OIDC token
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: cotool/validate-agents@v1
        with:
          dir: cotool/agents
OIDC requires that this repository is configured as your org’s agents source under Settings → Agents → GitOps sync — that registration is what authorizes the repo. To validate before configuring sync, or outside GitHub CI, call the endpoint directly with an API key (the curl example above) rather than the Action — subject to the API-key trade-offs noted above.

Inputs

InputDefaultDescription
dircotool/agentsDirectory to scan for agent YAML (and referenced prompt .md).
fileComma-separated explicit file list; overrides directory discovery.
recursivetrueRecurse into subdirectories of dir.
audiencecotool-validateOIDC audience requested from GitHub and verified by the Cotool API.
api_urlhttps://app.cotool.aiBase URL of the Cotool API.
fail_on_warningsfalseFail the check on warnings (errors always fail).

How validation gates a merge — and how syncing stays safe

The Action (and the /validate endpoint behind it) is advisory by default: it reports problems on the pull request, but on its own it does not stop anything from syncing. Cotool’s sync engine polls whatever is on your configured branch and reconciles it — it does not know or care whether a CI check ran or passed. Two separate mechanisms turn that into a safe GitOps workflow:
  1. Branch protection makes validation a merge gate (recommended). In your repository settings, mark the validation check as a required status check on the branch you sync from. GitHub then blocks any pull request from merging until validation passes, so only validated YAML ever reaches the branch Cotool polls. This is the piece that actually gates syncing, and it lives in your GitHub settings — not in Cotool.
  2. The sync engine re-validates as a backstop. Independently of CI, every sync runs the exact same parse, schema, and cross-file graph checks before applying a file. An invalid file is skipped per-file — its last good version is kept and the error is recorded against that file — so a bad file that slips past CI never corrupts your live agents (see Reconcile Behavior).
1

Open a pull request

An author edits agent YAML under your synced path and opens a PR. The validate-agents Action runs against the PR’s files and annotates any errors or warnings inline.
2

The required check gates the merge

With branch protection enabled on your sync branch, the PR cannot merge until the validation check is green. Reviewers only ever merge validated YAML; broken YAML is blocked at the door.
3

Merge to the sync branch

Once approved and green, the PR merges into the branch you configured under Settings → Agents → GitOps sync (commonly your default branch).
4

Cotool polls and reconciles

Within ~5 minutes the sync engine fetches the branch HEAD, re-validates, and applies the changes — recording each as a version. Anything still invalid is skipped per-file, never blocking the rest.
Cotool never reads your CI status — branch protection is what ensures only validated YAML lands on the sync branch. If you sync from an unprotected branch (or commit directly to it), the Action becomes a non-blocking signal and the server-side backstop is your only guard against invalid files.

Exporting an Existing Agent

To move a UI-built agent into Git, open the agent, use the version dropdown, and choose Export as YAML. The export emits the canonical sync shape — inline prompt as a literal block, stable sync_keys for agent-tool references, and triggers without any server-owned secrets — ready to commit to your repo.

Recovery

If the backing GitHub integration is disconnected, synced agents keep running — only syncing pauses. The card surfaces a Reconnect GitHub prompt. If you need to edit a managed agent while GitHub is disconnected, use convert to manual: Cotool makes an editable copy, moves the triggers to it, and parks the managed original (recoverable). The copy is an ordinary UI-owned agent from then on.

Agent Versioning — how synced and manual changes share one timeline

Triggering Agents — all trigger types and their fields

GitHub Integration — connect GitHub and configure repository sync

Creating Agents — building agents in the UI

Skills — reusable instructions referenced by name