Use-cases & examples
This page provides real-world examples of workflows for common use cases.
Self-service examples
Scaffold a new microservice
Create a new service repository and register it in Port:
Workflow example (click to expand)
{
"identifier": "scaffold-service",
"title": "Scaffold New Service",
"icon": "Microservice",
"description": "Create a new microservice from a template",
"nodes": [
{
"identifier": "trigger",
"title": "Service Details",
"config": {
"type": "SELF_SERVE_TRIGGER",
"userInputs": {
"properties": {
"serviceName": {
"type": "string",
"title": "Service Name",
"description": "Name for the new service (lowercase, no spaces)"
},
"language": {
"type": "string",
"title": "Language",
"enum": ["python", "nodejs", "go", "java"],
"enumColors": {
"python": "blue",
"nodejs": "green",
"go": "turquoise",
"java": "orange"
}
},
"team": {
"type": "string",
"format": "team",
"title": "Owning Team"
},
"description": {
"type": "string",
"title": "Description"
}
},
"required": ["serviceName", "language", "team"]
}
}
},
{
"identifier": "create-repo",
"title": "Create Repository",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "gh-integration-123",
"integrationProvider": "github-ocean",
"integrationInvocationType": "dispatch_workflow",
"integrationActionExecutionProperties": {
"org": "my-org",
"repo": "platform-templates",
"workflow": "scaffold-service.yml",
"workflowInputs": {
"service_name": "{{ .outputs.trigger.serviceName }}",
"language": "{{ .outputs.trigger.language }}",
"description": "{{ .outputs.trigger.description }}"
},
"reportWorkflowStatus": true
}
}
},
{
"identifier": "create-entity",
"title": "Register in Port",
"config": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "service",
"mapping": {
"identifier": "{{ .outputs.trigger.serviceName }}",
"title": "{{ .outputs.trigger.serviceName }}",
"team": "{{ .outputs.trigger.team }}",
"properties": {
"language": "{{ .outputs.trigger.language }}",
"description": "{{ .outputs.trigger.description }}",
"repository": "https://github.com/my-org/{{ .outputs.trigger.serviceName }}",
"status": "development",
"createdAt": "{{ now | todateiso8601 }}"
}
}
}
},
{
"identifier": "notify",
"title": "Send Notification",
"config": {
"type": "WEBHOOK",
"url": "https://hooks.slack.com/services/xxx",
"method": "POST",
"body": {
"text": "New service *{{ .outputs.trigger.serviceName }}* created",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "New service *{{ .outputs.trigger.serviceName }}* has been scaffolded:\n• Language: {{ .outputs.trigger.language }}\n• Team: {{ .outputs.trigger.team }}\n• Repository: https://github.com/my-org/{{ .outputs.trigger.serviceName }}"
}
}
]
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "create-repo"
},
{
"sourceIdentifier": "create-repo",
"targetIdentifier": "create-entity"
},
{
"sourceIdentifier": "create-entity",
"targetIdentifier": "notify"
}
]
}
Deploy with condition based on environment
Route deployments through different paths based on the target environment, with production deployments requiring additional approval steps.
Workflow example (click to expand)
{
"identifier": "deploy-service",
"title": "Deploy Service",
"icon": "Deployment",
"nodes": [
{
"identifier": "trigger",
"title": "Deployment Request",
"config": {
"type": "SELF_SERVE_TRIGGER",
"userInputs": {
"properties": {
"service": {
"type": "string",
"format": "entity",
"blueprint": "service",
"title": "Service"
},
"version": {
"type": "string",
"title": "Version"
},
"environment": {
"type": "string",
"title": "Environment",
"enum": ["development", "staging", "production"]
}
},
"required": ["service", "version", "environment"]
}
}
},
{
"identifier": "check-env",
"title": "Check Environment",
"config": {
"type": "CONDITION",
"outlets": [
{
"identifier": "production",
"title": "Production",
"expression": ".outputs.trigger.environment == \"production\""
},
{
"identifier": "non-production",
"title": "Non-Production",
"expression": ".outputs.trigger.environment != \"production\""
}
]
}
},
{
"identifier": "deploy-prod",
"title": "Deploy to Production",
"config": {
"type": "WEBHOOK",
"url": "https://api.example.com/deploy/production",
"method": "POST",
"body": {
"service": "{{ .outputs.trigger.service }}",
"version": "{{ .outputs.trigger.version }}",
"requiresApproval": true
}
}
},
{
"identifier": "deploy-nonprod",
"title": "Deploy to Non-Production",
"config": {
"type": "WEBHOOK",
"url": "https://api.example.com/deploy",
"method": "POST",
"body": {
"service": "{{ .outputs.trigger.service }}",
"version": "{{ .outputs.trigger.version }}",
"environment": "{{ .outputs.trigger.environment }}"
}
}
},
{
"identifier": "update-status",
"title": "Update Deployment Status",
"config": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "service",
"mapping": {
"identifier": "{{ .outputs.trigger.service }}",
"properties": {
"lastDeployedVersion": "{{ .outputs.trigger.version }}",
"lastDeployedAt": "{{ now | todateiso8601 }}"
}
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "check-env"
},
{
"sourceIdentifier": "check-env",
"targetIdentifier": "deploy-prod",
"sourceOutletIdentifier": "production"
},
{
"sourceIdentifier": "check-env",
"targetIdentifier": "deploy-nonprod",
"sourceOutletIdentifier": "non-production"
},
{
"sourceIdentifier": "deploy-prod",
"targetIdentifier": "update-status"
},
{
"sourceIdentifier": "deploy-nonprod",
"targetIdentifier": "update-status"
}
]
}
Analyze service with AI agent
Use an AI agent to analyze a service and post the results to Slack. The AI node pauses the workflow until the analysis is complete, then passes the response to the notification step.
Workflow example (click to expand)
{
"identifier": "ai-service-analysis",
"title": "Analyze Service with AI",
"icon": "CPU",
"description": "Invoke an AI agent to analyze a service and share the results",
"nodes": [
{
"identifier": "trigger",
"title": "Select Service",
"config": {
"type": "SELF_SERVE_TRIGGER",
"userInputs": {
"properties": {
"service": {
"type": "string",
"format": "entity",
"blueprint": "service",
"title": "Service to Analyze"
},
"analysisType": {
"type": "string",
"title": "Analysis Type",
"enum": ["health", "security", "performance"],
"enumColors": {
"health": "green",
"security": "red",
"performance": "blue"
}
}
},
"required": ["service", "analysisType"]
}
}
},
{
"identifier": "ai-analyze",
"title": "Run AI Analysis",
"config": {
"type": "AI_AGENT",
"agentIdentifier": "service-analyzer",
"userPrompt": "Perform a {{ .outputs.trigger.analysisType }} analysis of service {{ .outputs.trigger.service }}. Include current status, recent changes, and actionable recommendations."
}
},
{
"identifier": "notify",
"title": "Share Results",
"config": {
"type": "WEBHOOK",
"url": "https://hooks.slack.com/services/xxx",
"method": "POST",
"body": {
"text": "AI {{ .outputs.trigger.analysisType }} analysis complete for *{{ .outputs.trigger.service }}*",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "{{ .outputs[\"ai-analyze\"].response }}"
}
}
]
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "ai-analyze"
},
{
"sourceIdentifier": "ai-analyze",
"targetIdentifier": "notify"
}
]
}
Agent-triggered examples
Roll back a degraded deployment
A monitoring agent detects that a service's health status degraded after a deployment and invokes a rollback workflow through the Port MCP server, no human in the loop until the result is reported. The workflow definition is identical to a self-service workflow - a SELF_SERVE_TRIGGER - but its description and input descriptions are written as instructions to the agent, and permissions restrict invocation to the monitoring agent's identity.
Workflow example (click to expand)
{
"identifier": "rollback-degraded-service",
"title": "Roll back degraded service",
"icon": "Rollback",
"description": "Roll back a service to a previous version. Use this tool when a service's health status degrades after a deployment and a rollback is the appropriate remediation. Requires the service identifier, the version to roll back to, and a reason for the rollback.",
"nodes": [
{
"identifier": "trigger",
"title": "Rollback Request",
"config": {
"type": "SELF_SERVE_TRIGGER",
"userInputs": {
"properties": {
"service": {
"type": "string",
"format": "entity",
"blueprint": "service",
"title": "Service",
"description": "The degraded service to roll back. Use the service's catalog identifier."
},
"toVersion": {
"type": "string",
"title": "Rollback Version",
"description": "The last known-good version to roll back to. Use the service's previously deployed version from the catalog."
},
"reason": {
"type": "string",
"title": "Reason",
"description": "Why the rollback was triggered, for example the health check or metric that degraded."
}
},
"required": ["service", "toVersion", "reason"]
},
"permissions": {
"users": ["monitoring-agent"],
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "user", "property": "type" },
"operator": "=",
"value": "machine"
}
]
}
}
}
},
{
"identifier": "rollback",
"title": "Trigger Rollback",
"config": {
"type": "WEBHOOK",
"url": "https://ci.example.com/api/rollback",
"method": "POST",
"body": {
"service": "{{ .outputs.trigger.service }}",
"version": "{{ .outputs.trigger.toVersion }}"
}
}
},
{
"identifier": "update-status",
"title": "Update Service Status",
"config": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "service",
"mapping": {
"identifier": "{{ .outputs.trigger.service }}",
"properties": {
"lastDeployedVersion": "{{ .outputs.trigger.toVersion }}",
"healthStatus": "recovering"
}
}
}
},
{
"identifier": "notify",
"title": "Notify On-call",
"config": {
"type": "WEBHOOK",
"url": "https://hooks.slack.com/services/xxx",
"method": "POST",
"body": {
"text": "Agent rolled back *{{ .outputs.trigger.service }}* to {{ .outputs.trigger.toVersion }}. Reason: {{ .outputs.trigger.reason }}"
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "rollback"
},
{
"sourceIdentifier": "rollback",
"targetIdentifier": "update-status"
},
{
"sourceIdentifier": "update-status",
"targetIdentifier": "notify"
}
]
}
Automation examples
Auto-assign team on service creation
Automatically assign a default team to newly created services that don't have an owner, ensuring no service is left unmanaged.
Workflow example (click to expand)
{
"identifier": "auto-assign-team",
"title": "Auto-assign Team on Service Creation",
"nodes": [
{
"identifier": "trigger",
"title": "On Service Created",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_CREATED",
"blueprintIdentifier": "service"
},
"condition": {
"type": "JQ",
"expressions": [".diff.after.team == null or .diff.after.team == []"]
}
}
},
{
"identifier": "assign-team",
"title": "Assign Default Team",
"config": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "service",
"mapping": {
"identifier": "{{ .outputs.trigger.diff.after.identifier }}",
"team": "platform-team"
}
}
},
{
"identifier": "notify",
"title": "Notify Platform Team",
"config": {
"type": "WEBHOOK",
"url": "https://hooks.slack.com/services/xxx",
"method": "POST",
"body": {
"text": "Service *{{ .outputs.trigger.diff.after.title }}* was created without a team and has been assigned to Platform Team"
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "assign-team"
},
{
"sourceIdentifier": "assign-team",
"targetIdentifier": "notify"
}
]
}
Notify creators about stale pull requests in Slack
Notify pull request creators in Slack when the isStale property changes to true without closing the pull request.
Before using this workflow, ensure that:
- Your
githubPullRequestblueprint includes thestatus,isStale,repositoryFullName, andlinkproperties. - Each pull request has a
creatorrelation to Port's system_userblueprint. - Port's Slack Ocean custom integration is installed and configured to ingest
slack-userentities. - The
slack-userblueprint has aport_userrelation that maps Slack users to entities from the system_userblueprint. - Your Port secrets include
SLACK_BOT_TOKEN. - You replace
YOUR_ORG/YOUR_REPOSITORYwith the repository that the workflow should monitor.
Workflow example (Click to expand)
{
"identifier": "notify-stale-pull-request-creators",
"title": "Notify creators about stale pull requests",
"icon": "Github",
"description": "Notify pull request creators in Slack when their pull requests are marked as stale",
"nodes": [
{
"identifier": "stale-pr-trigger",
"title": "When a pull request becomes stale",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "githubPullRequest"
},
"condition": {
"type": "JQ",
"expressions": [
".diff.before.properties.isStale != true",
".diff.after.properties.isStale == true",
".diff.after.properties.status == \"open\"",
".diff.after.properties.repositoryFullName == \"YOUR_ORG/YOUR_REPOSITORY\"",
".diff.after.relations.creator != null"
],
"combinator": "and"
}
}
},
{
"identifier": "fetch-slack-user",
"title": "Find the creator's Slack user",
"config": {
"type": "WEBHOOK",
"url": "https://api.port.io/v1/blueprints/slack-user/entities/search",
"method": "POST",
"synchronized": true,
"headers": {
"Content-Type": "application/json"
},
"body": {
"query": {
"combinator": "and",
"rules": [
{
"relation": "port_user",
"targetProperty": "$identifier",
"operator": "=",
"value": "{{ .outputs[\"stale-pr-trigger\"].diff.after.relations.creator }}"
}
]
},
"include": [
"$identifier",
"$title"
],
"limit": 2
}
},
"variables": {
"matchCount": "{{ .result.response.data.entities | length }}",
"slackUserId": "{{ .result.response.data.entities[0].identifier }}"
}
},
{
"identifier": "check-slack-user",
"title": "Confirm one Slack user",
"config": {
"type": "CONDITION",
"outlets": [
{
"identifier": "matched",
"title": "One user found",
"expression": ".outputs[\"fetch-slack-user\"].matchCount == 1"
}
]
}
},
{
"identifier": "send-slack-message",
"title": "Notify the creator in Slack",
"config": {
"type": "WEBHOOK",
"url": "https://slack.com/api/chat.postMessage",
"method": "POST",
"synchronized": true,
"headers": {
"Authorization": "Bearer {{ .secrets.SLACK_BOT_TOKEN }}",
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"channel": "{{ .outputs[\"fetch-slack-user\"].slackUserId }}",
"text": "Your pull request {{ .outputs[\"stale-pr-trigger\"].diff.after.title }} has been marked as stale.",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Your pull request <{{ .outputs[\"stale-pr-trigger\"].diff.after.properties.link }}|{{ .outputs[\"stale-pr-trigger\"].diff.after.title }}> has been marked as *stale*.\n\nPlease update or close it if it is no longer needed."
}
}
]
}
}
}
],
"connections": [
{
"sourceIdentifier": "stale-pr-trigger",
"targetIdentifier": "fetch-slack-user"
},
{
"sourceIdentifier": "fetch-slack-user",
"targetIdentifier": "check-slack-user"
},
{
"sourceIdentifier": "check-slack-user",
"sourceOutletIdentifier": "matched",
"targetIdentifier": "send-slack-message"
}
]
}
The slack-user blueprint is not a system blueprint. Create and populate it using Port's Slack Ocean custom integration. The integration's configuration maps each Slack member ID to the entity's $identifier.
Add a port_user relation to the slack-user blueprint that targets _user, then populate it by matching the Slack profile email to the corresponding Port user. You can use the mapping configuration methods to define a search-based relation for this mapping.
At runtime, the workflow reads the _user entity identifier from the pull request's creator relation. It searches for the slack-user entity whose port_user relation points to that user, then passes the matching entity's $identifier to chat.postMessage as the Slack user ID.
Notify on critical service degradation
Send alerts to Slack and create a PagerDuty incident when a critical service's health status changes from healthy to degraded.
Workflow example (click to expand)
{
"identifier": "notify-degradation",
"title": "Notify on Service Degradation",
"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.healthStatus == \"healthy\"",
".diff.after.properties.healthStatus != \"healthy\""
],
"combinator": "and"
}
}
},
{
"identifier": "notify-slack",
"title": "Alert Slack",
"config": {
"type": "WEBHOOK",
"url": "https://hooks.slack.com/services/xxx",
"method": "POST",
"body": {
"text": "ALERT: Critical service {{ .outputs.trigger.diff.after.title }} health status changed from {{ .outputs.trigger.diff.before.properties.healthStatus }} to {{ .outputs.trigger.diff.after.properties.healthStatus }}",
"attachments": [
{
"color": "danger",
"fields": [
{
"title": "Service",
"value": "{{ .outputs.trigger.diff.after.title }}",
"short": true
},
{
"title": "Previous Status",
"value": "{{ .outputs.trigger.diff.before.properties.healthStatus }}",
"short": true
},
{
"title": "Current Status",
"value": "{{ .outputs.trigger.diff.after.properties.healthStatus }}",
"short": true
}
]
}
]
}
}
},
{
"identifier": "create-incident",
"title": "Create PagerDuty Incident",
"config": {
"type": "WEBHOOK",
"url": "https://events.pagerduty.com/v2/enqueue",
"method": "POST",
"body": {
"routing_key": "{{ .secrets[\"pagerduty-key\"] }}",
"event_action": "trigger",
"payload": {
"summary": "Critical service {{ .outputs.trigger.diff.after.title }} is degraded",
"severity": "critical",
"source": "port-workflows",
"custom_details": {
"service": "{{ .outputs.trigger.diff.after.identifier }}",
"previous_status": "{{ .outputs.trigger.diff.before.properties.healthStatus }}",
"current_status": "{{ .outputs.trigger.diff.after.properties.healthStatus }}"
}
}
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "notify-slack"
},
{
"sourceIdentifier": "notify-slack",
"targetIdentifier": "create-incident"
}
]
}
TTL-based environment cleanup
Automatically clean up temporary environments when their TTL expires, triggering infrastructure teardown and notifying the environment owner.
Workflow example (click to expand)
{
"identifier": "cleanup-expired-env",
"title": "Cleanup Expired Environments",
"nodes": [
{
"identifier": "trigger",
"title": "On TTL Expired",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "TIMER_EXPIRED",
"blueprintIdentifier": "environment",
"propertyIdentifier": "ttl"
}
}
},
{
"identifier": "mark-expired",
"title": "Mark as Expired",
"config": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "environment",
"mapping": {
"identifier": "{{ .outputs.trigger.diff.after.identifier }}",
"properties": {
"status": "expired"
}
}
}
},
{
"identifier": "trigger-cleanup",
"title": "Trigger Infrastructure Cleanup",
"config": {
"type": "WEBHOOK",
"url": "https://api.example.com/cleanup",
"method": "POST",
"body": {
"environmentId": "{{ .outputs.trigger.diff.after.identifier }}",
"cloudProvider": "{{ .outputs.trigger.diff.after.properties.cloudProvider }}",
"region": "{{ .outputs.trigger.diff.after.properties.region }}"
}
}
},
{
"identifier": "notify-owner",
"title": "Notify Owner",
"config": {
"type": "WEBHOOK",
"url": "https://hooks.slack.com/services/xxx",
"method": "POST",
"body": {
"text": "Environment *{{ .outputs.trigger.diff.after.title }}* has expired and is being cleaned up.",
"channel": "{{ .outputs.trigger.diff.after.properties.slackChannel }}"
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "mark-expired"
},
{
"sourceIdentifier": "mark-expired",
"targetIdentifier": "trigger-cleanup"
},
{
"sourceIdentifier": "trigger-cleanup",
"targetIdentifier": "notify-owner"
}
]
}