Skip to content

Connector triggers ​

A connector trigger is a trigger of type connector: it starts an agent when a connector reports an event. A pull request opened on GitHub, a pipeline that failed on GitLab, a build that completed in Azure DevOps, an issue created in Jira. The connector's package declares what can be listened to; the trigger on the agent says which connector, which of its triggers, with which parameters, and what to do with the event. Each event that passes starts an ordinary agent run.

Enterprise module

Connector triggers need the enterprise Connectors module and a connector in the same project whose package declares triggers. Without the module they can be read and deleted but not created (see Without the Connectors module).

When to use it. The service has a connector package with the events you need. Compared with a webhook trigger, a connector trigger registers the hook at the service for you when the connected account may do that, checks the service's own signature or token, and hands the agent the package's tidy event data instead of the vendor's raw request body. For any other sender, or an event no package declares, use a webhook trigger.

How it works ​

A package trigger is one of two kinds:

KindHow events arriveSetup
WebhookThe service pushes each event to a callback URL of Console. Console checks the delivery's signature or token (as the package declares it) before any package code runs.Automatic when the package can register the hook with the connector's account; otherwise manual: the trigger shows the Callback URL, a Signing secret (once) and the steps to enter them at the service.
PollConsole asks the service on a cron schedule what is new.None. The first check only notes where the service stands, so enabling a poll never replays history.

Triggers that listen to the same connector, package trigger and parameters share one subscription: one hook at the service (or one poll) serves all of them, and each agent trigger applies its own events, filter and prompt.

Create one ​

  1. Make sure the project has a connector of a package with triggers (see Packages with triggers) and that its account is connected. Without a working account the trigger is saved but waits (see The subscription).
  2. Open Build → Agents → your agent → Ship → Triggers → Add trigger and choose the Connector type.
  3. Pick the Connector and its Trigger, then the Parameters the trigger needs (a repository, a project). The lists are read with the connector's account; while it has no working account the field says Connect an account to pick from a list, or type the value. and takes the value as typed.
  4. Pick the Events (empty: every event of the trigger) and, for a poll, the Schedule.
  5. Set the filter, Include the event payload, the prompt and the variables, as for a webhook trigger.
  6. Save. A trigger whose hook cannot be registered for you shows the callback URL, the secret and the setup steps on its Settings tab. Copy the secret now: it is shown once.

A saved connector trigger has the tabs Settings (the subscription: its state, the callback URL and secret, the setup steps, Retry and Rotate secret), Test (a sample event: Preview shows what it would do, Run starts the trigger with it) and Deliveries (the events it received in the last 7 days). In the trigger list it shows the connector and the trigger name, with Connector · the service pushes events (webhook) or Connector · checked on a schedule (poll).

The connector's own page has a read-only Triggers tab that lists the package's triggers and the subscriptions made from them.

Over the API:

bash
curl -X POST "$CONSOLE_URL/api/client/v1/agents/code-reviewer/triggers" \
  -H "Authorization: Bearer $COGNIPEER_API_TOKEN" -H "Content-Type: application/json" \
  -d '{
    "name": "Review pull requests",
    "type": "connector",
    "connector": {
      "connector_id": "6720b4c3d2e1f0a9b8c7d601",
      "trigger": "pull_request",
      "params": { "repository": "acme/api" },
      "events": ["pull_request.opened", "pull_request.synchronize", "pull_request.ready_for_review"],
      "filter": [{ "path": "payload.draft", "op": "eq", "value": false }],
      "include_payload": true
    },
    "prompt": "Review pull request {{payload.number}} in {{payload.repository}}: {{payload.title}}.",
    "class": "deferred"
  }'

The answer is the trigger with its connector settings and a connector_subscription that says how events arrive. The fields are in the Triggers API.

The event data ​

The event is the package's, not the vendor's. A package shapes what it reports into flat data. The GitHub pull_request event is { action, repository, number, title, body, state, draft, merged, url, author, baseBranch, headBranch, headSha, labels, … }, with repository as the text owner/name; it is not GitHub's own request body, so payload.pull_request.draft or payload.repository.full_name do not exist. The Test tab of a saved trigger shows the package's sample event: write the filter and the prompt against it.

A path the event does not carry is absent, and an absent path matches only not_exists: a filter on a wrong name lets nothing through. A connector event has no headers, and its preset is always connector.

Packages with triggers ​

These are the triggers of the packages shipped with this release. The Triggers tab of a connector, and the trigger editor, show what your Console has. name.{a, b} stands for the event types name.a and name.b.

PackageTriggerKindParametersEvent types
GitHubpull_requestWebhookRepositorypull_request.{opened, edited, closed, reopened, synchronize, ready_for_review, converted_to_draft, labeled, unlabeled, assigned, unassigned, review_requested, review_request_removed}
issuesWebhookRepositoryissues.{opened, edited, closed, reopened, labeled, unlabeled, assigned, unassigned, deleted, transferred}
issue_commentWebhookRepositoryissue_comment.{created, edited, deleted}
pushWebhookRepositorypush
workflow_runWebhookRepositoryworkflow_run.{requested, in_progress, completed}
GitLabmerge_requestWebhookProjectmerge_request.{open, update, close, reopen, merge, approved, unapproved, approval, unapproval}
pushWebhookProjectpush
pipelineWebhookProjectpipeline.{created, pending, running, success, failed, canceled, skipped, manual}
noteWebhookProjectnote (comments)
Azure DevOpspull_request_createdWebhookProject, Repository (optional)git.pullrequest.created
pull_request_updatedWebhookProject, Repository (optional)git.pullrequest.updated
build_completedWebhookProjectbuild.complete
code_pushedWebhookProject, Repository (optional)git.push
Jiraissue_createdPoll, every 5 minutes by defaultProjectissue.created
issue_updatedPoll, every 5 minutes by defaultProjectissue.updated
Earthquakesnew_earthquakePoll, every 5 minutes by defaultFeed, Smallest magnitudeOne event per new earthquake

What the connected account needs to register a hook is on each package's section of SaaS app connectors. Without it, the trigger falls back to the manual setup.

Events the vendor connectors cover ​

The GitHub, GitLab and Azure DevOps packages replace the vendor presets that webhook triggers used to have (retired). A retired preset took any event its vendor sent; a connector trigger takes only the events its package declares, in the table above. Any other event of these vendors (for example GitHub's pull_request_review, check_run or release, GitLab's issue and tag_push, or the work item events of Azure DevOps) has no connector trigger yet: it cannot be picked, and naming it in connector.events is 400.

To move a trigger saved with a retired preset, use Convert to connector trigger in its row of the trigger list. The editor opens with the prompt, variables, filter and payload switch carried over and marks the filter rows that read headers or preset, which a connector event does not have. Rewrite the paths for the package's event data, then use Preview on the Test tab.

The subscription ​

The connector_subscription of a trigger, and its Settings tab, say how events arrive.

StatusStudioMeaning
activeActiveThe hook is registered, or the poll runs.
pendingPendingThe hook is being registered. One still pending after five minutes turns failed.
manualNeeds setupSet the hook up by hand at the service with the Callback URL and the Signing secret; the steps are on the tab.
pausedPausedThe connector is disabled, or its account is disconnected and the subscription needs it. It resumes when the connector does.
failedFailedRegistering again after a rotation failed, or the trigger could not be bound. Retry tries again.

The reason is a short sentence on the tab, and a stable reason_code on the API:

reason_codeMeaning
no_subscribeThe package cannot register the hook itself. Set it up by hand.
subscribe_failedThe service refused the registration, or could not be reached. Set it up by hand, or Retry.
unreachableConsole's address is not reachable from the internet (its APP_URL is a local address), so a service cannot post to it. Set APP_URL to a public address, or use a poll trigger.
accountThe connector has no working account. Connecting it again tries the registration again.
connector_disabledThe connector is disabled. Enabling it resumes the trigger.
package_missingThe connector's package is no longer installed.
rotate_failedThe hook was removed at the service and registering it with the new secret failed. Retry.
attach_failedThe trigger is saved, but the connection to the connector could not be set up. Retry.

Saving never fails because of the service. A failed registration leaves the trigger saved, as manual with the reason and the setup steps. Retry registers it again (not for no_subscribe and account: connect the account for the latter). A poll that fails stays active with last_error and backs off, so look at last_error and the last poll time too.

The signing secret. For a hook set up by hand, the full secret is in the answer that created or rotated it, and nowhere else; later reads show its last four characters. Rotate secret makes a new one: for a hook set up by hand you can keep the old one working for a grace period (1 hour or 24 hours) while you change it at the service; a hook Console registered is registered again with the new secret, and the old one stops at once.

A hook set up by hand needs no account to receive, so it keeps receiving when the connector's account is disconnected. Polls and hooks Console registered pause instead, and resume when the account is back. A webhook trigger saved while its connector is disabled waits until the connector is enabled; when Console can register the hook then, it shows no callback URL or secret until that time, so nobody sets up a second hook by hand.

Removing the last agent trigger of a subscription removes the hook at the service (or stops the poll). Changing a trigger's connector, trigger or parameters moves it to another subscription.

Poll schedule ​

A poll runs on the package's default schedule, in UTC, unless the trigger gives its own schedule (cron and an IANA timezone, default UTC).

The cron has five fields: minute, hour, day of month, month, day of week. Each field takes *, numbers, ranges (9-17), lists (1,15) and steps (*/10); the month and the day of week also take names (jan, mon-fri), and 7 is Sunday as well as 0. Not accepted: L, W, # and ?, macros such as @daily, and a seconds field. The schedule may not run more often than the package allows. A cron that breaks a rule is 400 on connector.schedule.cron with a sentence that says what is wrong, and Studio shows the same while you type.

  • The first check is a baseline. It notes where the service stands and starts nothing. After that each check reports what happened since the previous one, oldest first.
  • Editing only the schedule carries on from where the poll was. Changing the connector, the trigger or its parameters starts over with a new baseline.
  • Switching off. A disabled trigger ignores events. When a scheduled check finds every trigger on the poll disabled, it calls nothing and forgets its place; a trigger enabled after that starts with a new baseline, so nothing from the time it was off is reported. A poll that another trigger still has enabled keeps its place.
  • Clearing the schedule (schedule: null on the API, an empty field in Studio) returns the poll to the package's schedule, in UTC. On the API a schedule object replaces the stored one as a whole: send the timezone again when you change only the cron.

What the agent receives ​

The run's message is built as for a webhook trigger: a fixed first line that names the package, the trigger and the event and says that everything from the event is external data, then your prompt rendered as a template, then (with Include the event payload, the default) the event data as JSON of at most 32 KiB, then the mapped variables, then End of external data.

The template names are {{payload.…}} (the event data), {{event}}, {{delivery_id}}, {{received_at}}, {{connector.name}}, {{connector.key}}, {{connector.package}}, {{connector.trigger}}, {{trigger.name}} and {{agent.key}}. Every value is inserted as JSON. Values taken from the event reach the model only in the message, never in the agent's system prompt.

The run has trigger: "connector", channel: "trigger" and the trigger's trigger_id, the metadata labels connector_id, connector_trigger, event_type and event_id, and a new session. With Include the event payload on, the run keeps the event. On an agent that runs a flow, the fire starts the agent's flow, which reads the event as {{ trigger.* }}.

Unattended. A connector event starts an unattended run: nobody is there to approve a tool. A tool that would ask follows the agent's When nobody can answer setting, denied by default (approvals). Set a tool to Never ask to let it run on its own, and keep a filter on who or what may start a run: events and filters choose what is worth a run, they are not access control.

Duplicates and limits ​

  • Each event runs once per trigger. An event has an id (the service's own event id); the same event reported twice, by a redelivery or an overlapping poll, starts one run. A redelivery of an event whose run failed or was cancelled runs again, a few times at most, as for webhooks.
  • Run rate. The same per-trigger run rate as webhook triggers applies (AGENT_WEBHOOK_RATE_LIMIT_PER_MINUTE, 60 by default), and so do the agent's run, spend and quota limits.
  • A poll over a limit loses nothing. It stays on the same page of events, tries again after a back-off and starts only what the trigger has not taken yet. When several triggers share the poll and only one is over its limit, the poll moves on once the others are done with the page; the trigger over its limit misses those events, and the subscription's last_error says so.
  • The service always gets 2xx once a delivery's signature is right, whatever becomes of the events, so it does not switch the hook off because of a refusal.

Try it ​

The Test tab, or POST …/triggers/{id}/run with a sample: payload is the event data, event its type, and dry_run: true only shows what would happen. Without payload the package's sample event is used. Events and filter are not applied and nothing is de-duplicated.

bash
curl -X POST "$CONSOLE_URL/api/client/v1/agents/code-reviewer/triggers/$TRIGGER_ID/run" \
  -H "Authorization: Bearer $COGNIPEER_API_TOKEN" -H "Content-Type: application/json" \
  -d '{ "dry_run": true, "event": "pull_request.opened",
        "payload": { "action": "opened", "repository": "acme/api", "number": 42, "title": "Add retry", "draft": false } }'

The answer has matched, reason, event, prompt, variables and errors, as for a webhook trigger. Deliveries lists what arrived, with the same outcomes as for a webhook trigger.

Recipes ​

Pull request review on GitHub ​

The agent. A reviewer with a GitHub tool that can read a pull request's diff and post a comment. The GitHub connector's own tool does both (list_pull_request_files with the diff, comment_on_pr, with Read only off): bind its managed tool to the agent. The event carries the number, title, branches and links, not the diff.

The trigger. Connector: your GitHub connector. Trigger: pull_request, Repository: the one to review. Events pull_request.opened, pull_request.reopened, pull_request.synchronize and pull_request.ready_for_review. Filter payload.draft eq false. Class deferred. Prompt:

text
Review pull request {{payload.number}} in {{payload.repository}}: {{payload.title}}.
Author: {{payload.author}}. Merging {{payload.headBranch}} into {{payload.baseBranch}}. URL: {{payload.url}}.
Read the diff with your GitHub tool and post one review comment with concrete findings.

On a public repository anyone can open a pull request: add a rule on payload.author if only some people should start a review. Each push to the branch is a new synchronize event and a new review.

Pull request review on Azure DevOps ​

Connector: your Azure DevOps connector. Trigger pull_request_created (add a second trigger with pull_request_updated to review again after pushes; that event also fires for votes and status changes, so filter it), Project and optionally Repository. Filter payload.targetBranch eq main. The event carries pullRequestId, title, description, sourceBranch, targetBranch, repository, project and url.

Failed build triage ​

GitHub: trigger workflow_run, event workflow_run.completed, filter payload.conclusion in [failure, timed_out]. GitLab: trigger pipeline, event pipeline.failed. Azure DevOps: trigger build_completed, filter payload.result eq failed. The event names the run, branch and commit, not the log: the agent needs a tool that fetches the log. Deliver the diagnosis with a callback or let the agent post it.

New Jira issues ​

Trigger issue_created (a poll) for a Project. The first check is the baseline, so only issues created after the trigger was enabled start a run.

Templates ​

The Pull Request Reviewer and CI Failure Triage agent templates suggest connector triggers on the GitHub package: a pull request opened, reopened, updated or marked ready for review, and a workflow run that completed with failure or timed_out. When you use the template:

  • The form lists the project's active GitHub connectors. With exactly one it is picked for you; with several you choose.
  • It asks for the Repository, which no template can know. Leave it empty and that trigger is not created.
  • With no GitHub connector, or without the Connectors module, the agent is created without the connector triggers, and the result screen names the triggers left out and why. Add a connector and then the trigger from the agent's Triggers panel, or use the template again.
  • Connector triggers are created disabled, like every template trigger. Check the subscription on the trigger, then enable it.

Without the Connectors module ​

On a Console without the Connectors module, creating or changing a connector trigger is 400 invalid_trigger on type. A stored one is listed with Needs Connectors, does not fire, and Run now answers 409 connector_triggers_unavailable.

While the Connectors licence or switch, or the Agents service, is off for the workspace, a connector trigger can be read, renamed, switched on or off and deleted, but nothing new is bound: changing its connector, trigger, parameters or schedule is 400, Retry and Rotate secret are 402 or 403 service_unavailable, polls do not run and the callback URL answers 404. Everything goes on when it is back.

Networking ​

A webhook kind needs the service to reach Console: the callback URL (/api/public/connector-hooks/{tenantId}/{hookId}) is built from APP_URL and needs no API token, as every delivery proves the subscription's secret. For an on-premises Console route /api/public/connector-hooks/ through your ingress. A poll needs only Console to reach the service, so it also works when Console cannot be reached from outside.

API ​

MethodPath
GET, POST, PATCH, DELETE/api/client/v1/agents/{agentKey}/triggers…As for every trigger, with type: "connector" and a connector object
POST/api/client/v1/agents/{agentKey}/triggers/{triggerId}/connector/retryRegister the hook again, resume a paused poll, or bind a trigger whose subscription is gone
POST/api/client/v1/agents/{agentKey}/triggers/{triggerId}/webhook/rotate-secretA new signing secret for a webhook kind
POST/api/client/v1/agents/{agentKey}/triggers/{triggerId}/runRun now with a sample event, or a dry run
GET/api/client/v1/agents/{agentKey}/triggers/{triggerId}/deliveriesThe delivery log (7 days)
GET/api/connectors/trigger-catalogDashboard: the project's connectors whose packages declare triggers, with their triggers
GET/api/connectors/{id}/triggersDashboard: a connector's triggers and the subscriptions made from them
POST/api/public/connector-hooks/{tenantId}/{hookId}The callback URL services post to (no API token)

Field-level reference: Triggers API. Package authors declare triggers in the package manifest.

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub