> For the complete documentation index, see llms.txt.
Skip to main content

Check out Port for yourself ➜ 

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 githubPullRequest blueprint includes the status, isStale, repositoryFullName, and link properties.
  • Each pull request has a creator relation to Port's system _user blueprint.
  • Port's Slack Ocean custom integration is installed and configured to ingest slack-user entities.
  • The slack-user blueprint has a port_user relation that maps Slack users to entities from the system _user blueprint.
  • Your Port secrets include SLACK_BOT_TOKEN.
  • You replace YOUR_ORG/YOUR_REPOSITORY with 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"
}
]
}
Set up and resolve Slack users

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"
}
]
}