What you will do
- An external trigger is a named entry point that lets another system start one specific pipeline. External API triggers documents every field, and External triggers API documents the call.
allowed_callersnames explicituser,service_account, orauth_teamcallers, and narrows an already-authorized set rather than granting access.variable_mappingbuilds run variables frompayload.<path>,event_type,variables.<name>, orliteral:<value>.payload_schemarejects a malformed call before a run starts;rate_limit.per_minutecaps invocations over the previous minute.- An
idempotency_keyis scoped by trigger and caller, so a retried call returns the original run instead of starting a second one. - Invocation history per trigger is the fastest way to see why a caller believes it is triggering runs but is being rejected.
Before you start
- Pipeline
- A saved pipeline to start, such as
first-pipeline - Caller identity
- A service account, or your own user, to name in
allowed_callers - Token
- A token for that caller, used when invoking
Steps
- 01
Define the trigger
The trigger fixes the pipeline, the scope, and the team that owns the runs it produces. The payload contract is part of the definition, not an afterthought.
External trigger definitionyaml name: start-first-pipeline pipeline: first-pipeline enabled: true scope: platform/production allowed_callers: - service_account: release-bot payload_schema: type: object required: [channel] properties: channel: { type: string } variable_mapping: RELEASE_CHANNEL: payload.channel TRIGGERED_BY: literal:external rate_limit: per_minute: 10 - 02
Create it
Create the trigger through the API or the External Triggers page. Keep the returned ID: invocation uses it.
Create and list triggersbash curl -sX POST http://localhost:8080/v1/external-triggers \ -H "Authorization: Bearer $NOPSAI_TOKEN" \ -H "Content-Type: application/json" \ --data @trigger.json | jq -r .id curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" http://localhost:8080/v1/external-triggers | jqVerify- The trigger is listed and enabled.
- 03
Invoke it
Call as the identity you allowed. The payload must satisfy the schema, and the mapped variables become run variables.
Invoke with an idempotency keybash curl -sX POST "http://localhost:8080/v1/external-triggers/$TRIGGER_ID/invoke" \ -H "Authorization: Bearer $CALLER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"idempotency_key":"first-1","payload":{"channel":"stable"}}' | jqExpected result- A run is started and returned. Repeating the same call with the same key returns that same run.
- 04
Check the invocation history
Every accepted and rejected call is recorded against the trigger, with the reason.
Read invocationsbash curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "http://localhost:8080/v1/external-triggers/$TRIGGER_ID/invocations" | jqVerify- The invocation appears with its caller, payload result, and the run it produced.
How it works
An external trigger is the right entry point for a change-management system, a chat command, or another platform. For a repository event, use a Git trigger manifest instead: it carries branch and path rules the external trigger has no opinion about.
For providers other than GitHub, a Git webhook source terminates the delivery: each source owns its signing secret, receives deliveries at POST /v1/git/webhooks/{sourceID}, verifies the signature, and normalises the payload into the same event model GitHub App events use — so trigger manifests stay provider-neutral.
Only the webhook ingress needs to be reachable by the provider. Nothing else about the install has to be exposed for Git-driven runs to work.
An empty allowed_callers list does not widen access: AAA still authorizes the caller against the trigger resource.
Implementation evidence
services/nopsai/external_triggers.goInvocation, idempotency, and rate-limit handling.
services/nopsai/external_triggers_gitops.goTrigger document schema.

