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

Check out Port for yourself ➜ 

Approval Workflows for Service Deployment

Implement with AI

Send this guide to your coding agent.

Prerequisite: Install Port MCP

Open plan mode. Implement this Port guide in my org via MCP:

https://docs.port.io/guides/all/approval-workflow-for-gitlab-deployment/

Goal: get the guide's core flow working end-to-end in my org; adapting it to fit my existing setup takes priority over matching the guide 1:1.

Plan:
1. Confirm MCP is connected, in the right org, with sufficient permissions.
2. Diff the guide's data model (blueprints, properties, relations, actions, agents, automations, integrations, secrets) against mine.
3. Propose adaptations for gaps, reusing existing blueprints/relations over guide-named duplicates.
4. Flag what needs a UI click, credential, or secret from me, testing MCP capability empirically before ruling anything out.
5. Stop on any blocker and give me options. Approving this plan authorizes the writes it lists; pause only for writes beyond what's listed.

Build:
- Extend blueprint schema additively when upserting; don't remove or overwrite existing properties, and treat type conflicts as a blocker, not an auto-fix.
- List any mock data in the plan, minimal and labeled mock; once approved, seed it without re-asking, and tell me what you seeded.
- For anything the guide writes downstream (e.g. a webhook target), use a real entity, not a mock.
- For pages/widgets, use the real page identifier from the app URL, not a guessed slug.
- When you hit a UI step confirmed (not assumed) unsupported via MCP, pause, give exact clicks, then resume via MCP.
- Validate and give links after each meaningful step (only a tool-returned URL, no guessed paths); don't proceed if the last run wasn't a success.

Done:
- Confirm the guide's expected output exists and runs in Port.
- Summarize adaptations, seeded data, what was mocked or skipped, remaining UI steps, and how to verify.

This guide demonstrates how to build a single Port workflow that deploys a service through a GitLab pipeline and routes low-coverage deployments through a ServiceNow Change Request (CR) approval, using three entry points into the same workflow.

Workflow with a self-service deploy trigger and a self-service approval trigger both feeding GitLab pipeline webhooks, and an event trigger on change request approval re-triggering the pipeline
Open Beta

Port workflows are currently in open beta and available to all users. Workflows may undergo changes without prior notice.

Use-case​

  • A developer initiates a deployment via a self-service trigger, which starts a GitLab pipeline.
  • Pipeline stages: build, test, and deploy.
  • If code coverage in the test stage meets the threshold, deployment proceeds.
  • If coverage fails, a ServiceNow Change Request (CR) is created and the pipeline stops.
  • A Platform Engineer can approve or decline the CR from Port, or directly in the ServiceNow UI.
  • Either path updates the CR's approval property, which an event trigger in the same workflow picks up to re-trigger the GitLab pipeline with the decision.

Prerequisites​

  • Port account: If you don't have a Port account, you will need to create one.
  • Access to the workflows beta feature.
  • GitLab environment: This guide triggers a GitLab pipeline directly over its pipeline trigger API, so no GitLab integration installation is required.
  • ServiceNow instance access: You need admin or developer-level access.
  • Create the following GitLab CI/CD variables:
    • PORT_CLIENT_ID - Your Port client ID.
    • PORT_CLIENT_SECRET - Your Port client secret.
    • SERVICENOW_INSTANCE_URL - The ServiceNow instance URL. For example https://example-id.service-now.com.
    • SERVICENOW_API_TOKEN - A base64 encoded string of your ServiceNow credentials generated as <username>:<password>.
  • Create the following secrets in Port, used by the workflow's webhook nodes:
    • GITLAB_PROJECT_ID - The ID of your GitLab project.
    • GITLAB_TRIGGER_TOKEN - A pipeline trigger token for that project.
    • SERVICENOW_API_TOKEN - The same base64 encoded ServiceNow credentials as above.

Data Model​

Define a new blueprint in Port for the change request entity using the following JSON definition:

ServiceNow change request (click to expand)
{
"identifier": "servicenowChangeRequest",
"title": "Servicenow Change Request",
"icon": "Servicenow",
"schema": {
"properties": {
"number": {
"title": "Change Number",
"type": "string"
},
"description": {
"title": "Description",
"type": "string"
},
"isActive": {
"title": "Is active",
"type": "boolean"
},
"priority": {
"title": "Priority",
"type": "string"
},
"state": {
"icon": "DefaultProperty",
"title": "State",
"type": "string"
},
"createdOn": {
"title": "Created On",
"type": "string",
"format": "date-time"
},
"createdBy": {
"title": "Created By",
"type": "string"
},
"service": {
"type": "string",
"title": "Service"
},
"category": {
"type": "string",
"title": "Category"
},
"approval": {
"title": "Approval",
"icon": "DefaultProperty",
"type": "string",
"enum": [
"approved",
"not requested",
"requested",
"rejected"
],
"enumColors": {
"approved": "green",
"not requested": "turquoise",
"requested": "yellow",
"rejected": "pink"
}
},
"externalTags": {
"type": "string",
"title": "External Tags"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {}
}
Add a New Column to the Change Request Table

Add a new column named external_tags to your change_request table in ServiceNow to associate deployment requests with their corresponding Port context. Follow the ServiceNow documentation to complete this step.

The workflow​

Instead of chaining three separate self-service actions with two automations, this scenario fits into one workflow with three trigger nodes that share the same downstream logic:

TriggerFires whenLeads to
trigger_deploy (self-service)A developer requests a deployment from a service entity.Triggers the GitLab pipeline with APPROVAL_STATUS=pending.
trigger_approve (self-service)A Platform Engineer approves or rejects a CR from Port.Patches the decision into ServiceNow.
trigger_cr_updated (event)The CR's approval property changes to approved or rejected, whether that happened from the trigger above or directly in the ServiceNow UI.Re-triggers the GitLab pipeline with the decision.

Because the GitLab dispatch nodes are asynchronous webhooks, each one completes only once its own pipeline run finishes, reported back through Port's workflow node run API. trigger_pipeline's run reflects what that first pipeline invocation did: deployed directly, or paused and filed a change request. If a change request was filed, the servicenowChangeRequest entity is the source of truth for what happens next, its approval property drives trigger_cr_updated, and the follow-up deployment shows up as its own retrigger_pipeline run.

Approval workflow JSON (Click to expand)
Replace the placeholder
  • {SERVICENOW_INSTANCE} - your ServiceNow instance hostname.

GitLab's project ID and trigger token are read from Port secrets (see Configure the workflow below), so you don't need to edit the pasted JSON for those.

{
"identifier": "gitlab_deployment_approval_workflow",
"title": "Approval Workflow for Service Deployment",
"icon": "Deployment",
"description": "Deploy a service via GitLab, routing low-coverage deployments through a ServiceNow change request",
"nodes": [
{
"identifier": "trigger_deploy",
"title": "Deploy Service to Cluster",
"icon": "Deployment",
"config": {
"type": "SELF_SERVE_TRIGGER",
"contexts": [
{
"on": "ENTITY",
"userInput": "service"
}
],
"userInputs": {
"properties": {
"service": {
"type": "string",
"format": "entity",
"blueprint": "service",
"title": "Service"
},
"environment": {
"type": "string",
"title": "Environment",
"icon": "Environment",
"default": "Staging",
"enum": [
"Development",
"Staging",
"Production"
],
"enumColors": {
"Development": "lightGray",
"Staging": "lightGray",
"Production": "lightGray"
}
}
},
"required": [
"service"
],
"order": [
"service",
"environment"
]
}
}
},
{
"identifier": "trigger_pipeline",
"title": "Trigger GitLab Pipeline",
"icon": "GitLab",
"description": "Runs pre-deployment checks and the deploy pipeline; completes once this pipeline run finishes",
"config": {
"type": "WEBHOOK",
"url": "https://gitlab.com/api/v4/projects/{{ .secrets[\"GITLAB_PROJECT_ID\"] }}/ref/main/trigger/pipeline?token={{ .secrets[\"GITLAB_TRIGGER_TOKEN\"] }}",
"method": "POST",
"synchronized": false,
"onTimeout": "continue",
"body": {
"variables": {
"SERVICE": "{{ .outputs.trigger.service }}",
"ENVIRONMENT": "{{ .outputs.trigger.environment }}",
"APPROVAL_STATUS": "pending",
"NODE_RUN_ID": "{{ .workflowNodeRun.identifier }}"
}
}
}
},
{
"identifier": "trigger_approve",
"title": "Approve and Deploy Service",
"icon": "Deployment",
"config": {
"type": "SELF_SERVE_TRIGGER",
"contexts": [
{
"on": "ENTITY",
"userInput": "change_request"
}
],
"userInputs": {
"properties": {
"change_request": {
"type": "string",
"format": "entity",
"blueprint": "servicenowChangeRequest",
"title": "Change Request"
},
"approval_status": {
"icon": "DefaultProperty",
"title": "Decision",
"type": "string",
"default": "approved",
"enum": [
"approved",
"rejected"
],
"enumColors": {
"approved": "green",
"rejected": "pink"
}
},
"reason": {
"type": "string",
"title": "Reason"
}
},
"required": [
"change_request",
"approval_status"
],
"order": [
"change_request",
"approval_status",
"reason"
]
}
}
},
{
"identifier": "patch_servicenow",
"title": "Patch Change Request in ServiceNow",
"icon": "Servicenow",
"description": "Sends a PATCH request to ServiceNow when the admin approves or rejects the change request",
"config": {
"type": "WEBHOOK",
"url": "https://{SERVICENOW_INSTANCE}/api/now/table/change_request/{{ .outputs.trigger.change_request }}",
"method": "PATCH",
"synchronized": true,
"headers": {
"Authorization": "Basic {{ .secrets[\"SERVICENOW_API_TOKEN\"] }}"
},
"body": {
"approval": "{{ .outputs.trigger.approval_status }}"
}
}
},
{
"identifier": "trigger_cr_updated",
"title": "On Change Request Decision",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "servicenowChangeRequest"
},
"condition": {
"type": "JQ",
"expressions": [
".diff.after.properties.approval == \"approved\"",
".diff.after.properties.approval == \"rejected\""
],
"combinator": "or"
}
}
},
{
"identifier": "retrigger_pipeline",
"title": "Retrigger GitLab Pipeline with Decision",
"icon": "GitLab",
"description": "Resumes the deployment with the platform engineer's decision",
"config": {
"type": "WEBHOOK",
"url": "https://gitlab.com/api/v4/projects/{{ .secrets[\"GITLAB_PROJECT_ID\"] }}/ref/main/trigger/pipeline?token={{ .secrets[\"GITLAB_TRIGGER_TOKEN\"] }}",
"method": "POST",
"synchronized": false,
"onTimeout": "continue",
"body": {
"variables": {
"APPROVAL_STATUS": "{{ .outputs.trigger.diff.after.properties.approval }}",
"SYSTEM_ID": "{{ .outputs.trigger.diff.after.identifier }}",
"NODE_RUN_ID": "{{ .workflowNodeRun.identifier }}"
}
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger_deploy",
"targetIdentifier": "trigger_pipeline"
},
{
"sourceIdentifier": "trigger_approve",
"targetIdentifier": "patch_servicenow"
},
{
"sourceIdentifier": "trigger_cr_updated",
"targetIdentifier": "retrigger_pipeline"
}
]
}

Build the workflow​

  1. Go to the Workflows page in Port.
  2. Click on the + Workflow button in the top-right corner.
  3. Click on the {...} button in the top right corner.
  4. Copy and paste the workflow JSON above into the editor, replacing the example workflow.
  5. Click Save.

Configure the workflow​

After saving, replace the remaining placeholder and add the required secrets:

  • In patch_servicenow, replace {SERVICENOW_INSTANCE} with your ServiceNow instance hostname.
  • Add the following secrets in Port's Credentials settings:
    • GITLAB_PROJECT_ID - your GitLab project's ID.
    • GITLAB_TRIGGER_TOKEN - a pipeline trigger token for that project. To learn how to obtain these two values, see the GitLab backend documentation.
    • SERVICENOW_API_TOKEN - your base64 encoded ServiceNow credentials.
Secrets and integration actions

This workflow uses WEBHOOK nodes for GitLab, referencing GITLAB_PROJECT_ID and GITLAB_TRIGGER_TOKEN as secrets. Integration action nodes don't support secrets, so if your organization already has a GitLab integration installed, you can alternatively trigger the pipeline with an INTEGRATION_ACTION node, which authenticates through the integration's own stored credentials instead of these two secrets.

GitLab pipeline script​

This pipeline contains the logic for service deployment. It consists of three stages: build, test, and deploy. It reads its inputs as plain GitLab CI/CD variables (passed via the variables object in the webhook body above) instead of parsing a JSON payload file, and reports progress back to Port using the workflow node run API instead of the action-run log endpoints.

Each pipeline invocation reports back only to its own NODE_RUN_ID, the node run identifier of whichever webhook node triggered it. trigger_pipeline's run reflects the first invocation (deploy directly, or file a change request and stop); retrigger_pipeline's run reflects the follow-up invocation after a decision is made. There's no cross-run correlation to manage, the servicenowChangeRequest entity is what ties the two invocations together for anyone reading the catalog.

GitLab pipeline (click to expand)
stages:
- build
- test
- deploy

image: alpine:latest

variables:
PORT_CLIENT_ID: ${PORT_CLIENT_ID}
PORT_CLIENT_SECRET: ${PORT_CLIENT_SECRET}
PORT_API_URL: "https://api.port.io"
SERVICENOW_INSTANCE_URL: ${SERVICENOW_INSTANCE_URL}
SERVICENOW_API_TOKEN: ${SERVICENOW_API_TOKEN} # Base64 encoded version of SNOW username:password
APPROVAL_STATUS: "pending" # Default status for all pipelines


initialize-build:
stage: build
except:
- pushes
before_script:
- apk update
- apk add --upgrade curl jq -q
script:
- |
echo "Initiating build checks"
echo "Getting access token from Port API"
accessToken=$(curl -X POST \
-H 'Content-Type: application/json' \
-d '{"clientId": "'"$PORT_CLIENT_ID"'", "clientSecret": "'"$PORT_CLIENT_SECRET"'"}' \
-s "$PORT_API_URL/auth/access_token" | jq -r '.accessToken')

echo "ACCESS_TOKEN=$accessToken" >> data.env
echo "NODE_RUN_ID=$NODE_RUN_ID" >> data.env

if [ "$APPROVAL_STATUS" = "approved" ]; then
echo "Deployment approved by platform engineer. Proceeding directly."
curl -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $accessToken" \
-d '{"logs":[{"level":"INFO","message":"βœ… Deployment approved by platform engineer, proceeding with deployment"}]}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID/logs"

elif [ "$APPROVAL_STATUS" = "rejected" ]; then
echo "Deployment declined by platform engineer. Notifying developer."
curl -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $accessToken" \
-d '{"logs":[{"level":"ERROR","message":"❌ Deployment declined by platform engineer. Please review the change request and take necessary actions."}]}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID/logs"

curl -X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $accessToken" \
-d '{"status":"COMPLETED","result":"FAILED"}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID"
exit 1 # Exit pipeline due to declined approval
else
curl -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $accessToken" \
-d '{"logs":[{"level":"INFO","message":"πŸš€ Initiating deployment sequence, preparing environment and resources"}]}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID/logs"

curl -X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $accessToken" \
-d '{"status":"IN_PROGRESS","links":["'"$CI_PIPELINE_URL"'"]}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID"
fi
artifacts:
reports:
dotenv: data.env

run-tests:
stage: test
dependencies:
- initialize-build # Ensure the data.env artifact is downloaded
except:
- pushes
before_script:
- apk update
- apk add --upgrade curl jq -q
script:
- |
if [ "$APPROVAL_STATUS" = "approved" ]; then
echo "Skipping tests and threshold checks..."
echo "COVERAGE_MET=true" >> data.env
exit 0
fi

echo "πŸ§ͺ Running tests and checking thresholds..."
# Simulate threshold check (e.g., code coverage)
COVERAGE=65

if [ "$COVERAGE" -lt 70 ]; then
echo "⚠️ Coverage is below the 70% threshold, initiating ServiceNow change request..."
echo "COVERAGE_MET=false" >> data.env

echo "Creating a change request in ServiceNow"
changeRequestResponse=$(curl -X POST \
-H "Authorization: Basic $SERVICENOW_API_TOKEN" \
-H 'Content-Type: application/json' \
-d "{\"short_description\": \"Automated change request from GitLab CI/CD\", \"business_service\": \"$SERVICE\", \"priority\": \"1\", \"u_external_tag\": \"$NODE_RUN_ID\"}" \
-s "$SERVICENOW_INSTANCE_URL/api/now/table/change_request")

# Check if the request was successful and extract important details
if [[ $(echo "$changeRequestResponse" | jq -r '.result.number') != "null" ]]; then
changeNumber=$(echo "$changeRequestResponse" | jq -r '.result.number')
changeSysId=$(echo "$changeRequestResponse" | jq -r '.result.sys_id')
changeState=$(echo "$changeRequestResponse" | jq -r '.result.state')
changeCreatedOn=$(echo "$changeRequestResponse" | jq -r '.result.sys_created_on')
changeCreatedBy=$(echo "$changeRequestResponse" | jq -r '.result.sys_created_by')
changeCategory=$(echo "$changeRequestResponse" | jq -r '.result.category')
changePriority=$(echo "$changeRequestResponse" | jq -r '.result.priority')
changeDescription=$(echo "$changeRequestResponse" | jq -r '.result.short_description')
changeApproval=$(echo "$changeRequestResponse" | jq -r '.result.approval')
changeService=$(echo "$changeRequestResponse" | jq -r '.result.business_service.value')

echo "Change Request Created Successfully: Number: $changeNumber, Sys ID: $changeSysId, State: $changeState"
logMessage="⚠️ Pipeline did not meet the deployment checks, so a ServiceNow Change Request with Number $changeNumber and system ID $changeSysId has been created. An admin will need to review and approve this request before the deployment can proceed."

# Send the details to Port's workflow run logs
curl -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d "{\"logs\":[{\"level\":\"INFO\",\"message\":\"$logMessage\"}]}" \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID/logs"

# Create the change request in Port
portPayload="{
\"identifier\": \"$changeSysId\",
\"title\": \"$changeDescription\",
\"icon\": \"Servicenow\",
\"properties\": {
\"number\": \"$changeNumber\",
\"createdBy\": \"$changeCreatedBy\",
\"createdOn\": \"$changeCreatedOn\",
\"state\": \"$changeState\",
\"category\": \"$changeCategory\",
\"priority\": \"$changePriority\",
\"description\": \"$changeDescription\",
\"approval\": \"$changeApproval\",
\"externalTags\": \"$NODE_RUN_ID\",
\"service\": \"$changeService\"
},
\"relations\": {}
}"

# Send the change request data to Port API
curl -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d "$portPayload" \
"$PORT_API_URL/v1/blueprints/servicenowChangeRequest/entities"

else
echo "Failed to create ServiceNow Change Request: $changeRequestResponse"

# Send failure message to Port's workflow run logs
curl -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{"logs":[{"level":"ERROR","message":"❌ Failed to create ServiceNow Change Request with response: '"$changeRequestResponse"'"}]}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID/logs"

exit 1 # Exit with failure
fi
else
echo "βœ… Coverage is sufficient (β‰₯ 70%), no need to create a ServiceNow change request."
echo "COVERAGE_MET=true" >> data.env
fi
artifacts:
reports:
dotenv: data.env

# Deployment job
deploy-to-cloud:
stage: deploy
dependencies:
- run-tests
- initialize-build
except:
- pushes
before_script:
- apk update
- apk add --upgrade curl jq -q
script:
- |
# Check if APPROVAL_STATUS or COVERAGE_MET is true
if [ "$APPROVAL_STATUS" = "approved" ] || [ "$COVERAGE_MET" = "true" ]; then
echo "Conditions met, deploying service to the cluster..."
# HERE IS WHERE YOU CAN ADD YOUR DEPLOYMENT SCRIPT

curl -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{"logs":[{"level":"INFO","message":"βœ… Service has been successfully deployed to the cluster"}]}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID/logs"

curl -X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{"status":"COMPLETED","result":"SUCCESS"}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID"
else
echo "Conditions not met, change request is pending, ending this pipeline run"
curl -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{"logs":[{"level":"INFO","message":"⏸️ Deployment paused pending change request approval. Check the change request entity in Port for status."}]}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID/logs"

curl -X PATCH \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{"status":"COMPLETED","result":"SUCCESS"}' \
"$PORT_API_URL/v1/workflows/nodes/runs/$NODE_RUN_ID"
fi
Configure webhooks in ServiceNow

Follow our documentation to learn how to configure webhooks in ServiceNow so that a decision made directly in the ServiceNow UI syncs back into the servicenowChangeRequest entity and reaches the trigger_cr_updated event trigger.

Conclusion​

Once the blueprint and workflow above are created, you have everything you need to run the scenario described in the use-case: a developer-triggered deployment that pauses for a ServiceNow change request whenever code coverage falls short, and resumes automatically once a platform engineer or ServiceNow itself resolves the request.

Let's test it!​

Test 1: Coverage failure and approval

  1. Go to a service entity in Port and open its bolt (⚑) menu.
  2. Select Deploy Service to Cluster, choose an environment, and run it.
  3. The GitLab pipeline starts. Since the simulated coverage in the pipeline script is below the 70% threshold, it creates a ServiceNow change request and stops before deploying. The Deploy Service to Cluster run completes as SUCCESS, its job was to run pre-deployment checks and file the change request, which it did.
  4. In Port, open the servicenowChangeRequest entity that was just created, its bolt menu now offers Approve and Deploy Service.
  5. Run it with a decision of approved. This patches the ServiceNow record, which syncs back into Port and fires the trigger_cr_updated event trigger.
  6. The GitLab pipeline is re-triggered with the approval and deploys the service. The resulting retrigger_pipeline run completes as SUCCESS.

Test 2: Rejection

  1. Repeat steps 1 to 4 above.
  2. Run Approve and Deploy Service with a decision of rejected.
  3. Verify that the resulting retrigger_pipeline run completes as FAILED, and no deployment occurs.

Test 3: Decision made directly in ServiceNow

  1. Repeat steps 1 to 3 from Test 1 to get a pending change request.
  2. In the ServiceNow UI, approve or reject the change request directly.
  3. Once the ServiceNow-to-Port webhook syncs the approval property, verify that trigger_cr_updated fires and the GitLab pipeline is re-triggered exactly as it would be from Port's own approval trigger.

Extend the workflow​

  • Add a Slack or PagerDuty WEBHOOK node after patch_servicenow to notify the requesting developer directly, instead of relying solely on the workflow run's status.
  • Add a CONDITION node after trigger_cr_updated if you want different downstream behavior for approved versus rejected beyond what the pipeline script already handles.
  • Explore more workflow examples for inspiration.