Event trigger
Event triggers allow workflows to run automatically in response to changes in your software catalog. When an entity is created, updated, or deleted, the workflow can be triggered to perform automated actions.
Trigger types
You can configure the following event types:
| Event type | Description |
|---|---|
ENTITY_CREATED | Triggered when a new entity is created in the specified blueprint |
ENTITY_UPDATED | Triggered when an existing entity is updated |
ENTITY_DELETED | Triggered when an entity is deleted |
ANY_ENTITY_CHANGE | Triggered on any change (create, update, or delete) |
TIMER_EXPIRED | Triggered when a timer property expires |
Configuration
Here's how to configure each event trigger type:
- Entity created
- Entity updated
- Entity deleted
- Any entity change
- Timer expired
{
"identifier": "trigger",
"title": "On Service Created",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_CREATED",
"blueprintIdentifier": "service"
}
}
}
{
"identifier": "trigger",
"title": "On Service Updated",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "service"
}
}
}
{
"identifier": "trigger",
"title": "On Service Deleted",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_DELETED",
"blueprintIdentifier": "service"
}
}
}
{
"identifier": "trigger",
"title": "On Any Service Change",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ANY_ENTITY_CHANGE",
"blueprintIdentifier": "service"
}
}
}
Triggered when a timer property on an entity expires:
{
"identifier": "trigger",
"title": "On TTL Expired",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "TIMER_EXPIRED",
"blueprintIdentifier": "environment",
"propertyIdentifier": "ttl"
}
}
}
Published
The published field controls whether the trigger is active and listening for events. When set to false, the trigger will not respond to any events.
| Property | Type | Default | Description |
|---|---|---|---|
published | boolean | true | When `true`, the trigger is active and will respond to events. When `false`, the trigger is disabled. |
This is useful for temporarily disabling a workflow without deleting it, or for creating workflows that are not yet ready for production use.
Conditions
Add conditions to filter which events trigger the workflow using JQ expressions:
{
"identifier": "trigger",
"title": "On Production Service Updated",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "service"
},
"condition": {
"type": "JQ",
"expressions": [
".diff.after.properties.environment == \"production\""
],
"combinator": "and"
}
}
}
Condition properties
| Property | Description |
|---|---|
type | Must be "JQ" |
expressions | Array of JQ expressions that must evaluate to true |
combinator | How to combine expressions: "and" (all must match) or "or" (any must match) |
Common condition patterns
- Property changed
- Property equals value
- Property changed to value
- Has relation
Check if a property value has changed:
{
"expressions": [
".diff.before.properties.status != .diff.after.properties.status"
]
}
Filter for entities with a specific property value:
{
"expressions": [
".diff.after.properties.tier == \"critical\""
]
}
Check if a property changed from any value to a specific value:
{
"expressions": [
".diff.before.properties.status != \"deployed\"",
".diff.after.properties.status == \"deployed\""
],
"combinator": "and"
}
Check if an entity has a specific relation set:
{
"expressions": [
".diff.after.relations.team != null"
]
}
Outputs
Event triggers store the full event object as outputs that can be referenced in subsequent nodes. Outputs are accessed using bracket notation .outputs["<trigger-node-identifier>"].<field>.
| Output | Description |
|---|---|
.outputs["<trigger-node-identifier>"].diff.before | The entity state before the change (null for created events) |
.outputs["<trigger-node-identifier>"].diff.after | The entity state after the change (null for deleted events) |
.outputs["<trigger-node-identifier>"].action | The action type: "CREATE", "UPDATE", or "DELETE" |
.outputs["<trigger-node-identifier>"].diff.after.blueprint | The blueprint identifier of the entity |
.outputs["<trigger-node-identifier>"].diff.after.identifier | The identifier of the entity |
.outputs["<trigger-node-identifier>"].impactedEntity | The entity evaluated by the scorecard rule. Only available for triggers on the _rule_result blueprint. See scorecard rule results |
Entity structure
The diff.before and diff.after objects have the following structure:
{
"identifier": "my-service",
"title": "My Service",
"blueprint": "service",
"properties": {
"language": "python",
"status": "running"
},
"relations": {
"team": "backend-team"
},
"team": ["backend-team"],
"icon": "Service"
}
Using outputs in action nodes
This example shows how to reference event trigger outputs in an action node. Since the trigger node's identifier is trigger, outputs are accessed via .outputs["trigger"]:
{
"config": {
"type": "WEBHOOK",
"url": "https://api.example.com/notify",
"body": {
"entityIdentifier": "{{ .outputs.trigger.diff.after.identifier }}",
"entityTitle": "{{ .outputs.trigger.diff.after.title }}",
"newStatus": "{{ .outputs.trigger.diff.after.properties.status }}",
"previousStatus": "{{ .outputs.trigger.diff.before.properties.status }}",
"action": "{{ .outputs.trigger.action }}"
}
}
}
Scorecard rule results
Each scorecard rule evaluation is stored as an entity of the _rule_result system blueprint. When an event trigger listens to _rule_result, the event's diff holds the rule result itself, not the entity the rule evaluated. To save you from fetching that entity yourself, Port adds an impactedEntity field to the trigger's outputs.
How the impacted entity is resolved
Port reads properties.blueprint and properties.entity from the rule result (from diff.after, falling back to diff.before for deleted rule results) and fetches the current state of that entity from your catalog when the event is processed.
If Port cannot resolve the entity (for example, if those properties are missing or the entity no longer exists), impactedEntity is set to null. The workflow still runs, and an error is written to the trigger node's logs.
Use the impacted entity in conditions
You can reference .impactedEntity directly in trigger conditions, alongside .diff. For example, the following trigger only fires when a rule starts failing for a production service:
{
"identifier": "trigger",
"title": "On Production Rule Failure",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "_rule_result"
},
"condition": {
"type": "JQ",
"expressions": [
".diff.before.properties.result == \"Passed\"",
".diff.after.properties.result == \"Not passed\"",
".impactedEntity.properties.environment == \"production\""
],
"combinator": "and"
}
}
}
Port fetches the impacted entity for condition evaluation only when an expression contains .impactedEntity. Use dot notation (.impactedEntity.properties.<property>) rather than bracket notation (.["impactedEntity"]) in conditions.
Use the impacted entity in subsequent nodes
The impacted entity is available in subsequent nodes at .outputs["<trigger-node-identifier>"].impactedEntity. It has the same structure as diff.before and diff.after:
{
"config": {
"type": "WEBHOOK",
"url": "https://api.example.com/notify",
"body": {
"ruleResult": "{{ .outputs.trigger.diff.after.identifier }}",
"result": "{{ .outputs.trigger.diff.after.properties.result }}",
"entityIdentifier": "{{ .outputs.trigger.impactedEntity.identifier }}",
"entityTitle": "{{ .outputs.trigger.impactedEntity.title }}",
"owningTeam": "{{ .outputs.trigger.impactedEntity.team }}"
}
}
}
Examples
Notify on critical service status change
This workflow monitors critical services and sends a Slack notification when their status changes. It uses conditions to trigger only for services with tier set to critical.
Workflow example (click to expand)
{
"identifier": "notify-critical-status",
"title": "Notify on Critical Service Status Change",
"nodes": [
{
"identifier": "trigger",
"title": "On Status Change",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "service"
},
"condition": {
"type": "JQ",
"expressions": [
".diff.after.properties.tier == \"critical\"",
".diff.before.properties.status != .diff.after.properties.status"
],
"combinator": "and"
}
}
},
{
"identifier": "send-notification",
"title": "Send Slack Notification",
"config": {
"type": "WEBHOOK",
"url": "https://hooks.slack.com/services/xxx",
"method": "POST",
"body": {
"text": "Critical service {{ .outputs.trigger.diff.after.title }} status changed from {{ .outputs.trigger.diff.before.properties.status }} to {{ .outputs.trigger.diff.after.properties.status }}"
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "send-notification"
}
]
}
Clean up resources on environment deletion
This workflow triggers when an environment entity is deleted and calls an external API to clean up the associated cloud resources.
Workflow example (click to expand)
{
"identifier": "cleanup-environment",
"title": "Cleanup on Environment Deletion",
"nodes": [
{
"identifier": "trigger",
"title": "On Environment Deleted",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_DELETED",
"blueprintIdentifier": "environment"
}
}
},
{
"identifier": "cleanup",
"title": "Trigger Cleanup",
"config": {
"type": "WEBHOOK",
"url": "https://api.example.com/cleanup",
"method": "POST",
"body": {
"environmentId": "{{ .outputs.trigger.diff.before.identifier }}",
"environmentName": "{{ .outputs.trigger.diff.before.title }}",
"cloudProvider": "{{ .outputs.trigger.diff.before.properties.cloudProvider }}"
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "cleanup"
}
]
}
Auto-expire TTL environments
This workflow uses a timer trigger to automatically mark environments as expired when their TTL property expires, then notifies the owner.
Workflow example (click to expand)
{
"identifier": "expire-environment",
"title": "Expire Environment on TTL",
"nodes": [
{
"identifier": "trigger",
"title": "On TTL Expired",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "TIMER_EXPIRED",
"blueprintIdentifier": "environment",
"propertyIdentifier": "ttl"
}
}
},
{
"identifier": "update-status",
"title": "Mark as Expired",
"config": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "environment",
"mapping": {
"identifier": "{{ .outputs.trigger.diff.after.identifier }}",
"properties": {
"status": "expired"
}
}
}
},
{
"identifier": "notify",
"title": "Notify Owner",
"config": {
"type": "WEBHOOK",
"url": "https://api.example.com/notify",
"body": {
"message": "Environment {{ .outputs.trigger.diff.after.title }} has expired",
"owner": "{{ .outputs.trigger.diff.after.relations.owner }}"
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "update-status"
},
{
"sourceIdentifier": "update-status",
"targetIdentifier": "notify"
}
]
}