Create and Manage Statuspage Incidents
Overviewโ
This self-service guide will show you how to seamlessly create and update incidents on your Atlassian Statuspage directly through Port. You'll also learn how to automatically notify your team about incident changes using Port's notification system.
Prerequisitesโ
- Get your Status page API Key and Page ID:
-
In your GitHub repository, go to Settings > Secrets and add the following secrets:
STATUSPAGE_PAGE_ID
- Your Statuspage IdSTATUSPAGE_API_KEY
- Statuspage API key. Create the API KeyPORT_CLIENT_ID
- Your portclient id
How to get the credentials.PORT_CLIENT_SECRET
- Your portclient secret
How to get the credentials.
-
Create the blueprints in Port.
Statuspage Blueprint
{
"identifier": "statuspage",
"title": "Status Page",
"description": "A Statuspage for communicating service status, maintenance and incidents",
"schema": {
"properties": {
"page_description": {
"type": "string",
"title": "Page Description",
"description": "Description of the page (optional)"
},
"headline": {
"type": "string",
"title": "Headline",
"description": "A short headline for the Statuspage (optional)"
},
"branding": {
"type": "string",
"title": "Branding",
"description": "Branding level of the Statuspage (e.g., 'basic')"
},
"status_indicator": {
"type": "string",
"title": "Status Indicator",
"enum": ["none", "minor", "major", "critical"],
"enumColors": {
"none": "green",
"minor": "yellow",
"major": "orange",
"critical": "red"
},
"description": "The current status of the page"
},
"status_description": {
"type": "string",
"title": "Status Description",
"description": "Description of the current status (optional)"
},
"subdomain": {
"type": "string",
"title": "Subdomain",
"description": "The subdomain used for the Statuspage URL (e.g., 'acme')"
},
"domain": {
"type": "string",
"title": "Custom Domain",
"description": "Custom domain name for the Statuspage (optional)"
},
"url": {
"type": "string",
"format": "url",
"title": "Statuspage URL",
"description": "Full URL of the Statuspage (optional)"
},
"allow_page_subscribers": {
"type": "boolean",
"title": "Allow Page Subscribers",
"description": "Whether to allow users to subscribe to page updates"
},
"allow_incident_subscribers": {
"type": "boolean",
"title": "Allow Incident Subscribers",
"description": "Whether to allow users to subscribe to specific incidents"
},
"allow_email_subscribers": {
"type": "boolean",
"title": "Allow Email Subscribers",
"description": "Whether to allow email subscriptions"
},
"allow_sms_subscribers": {
"type": "boolean",
"title": "Allow SMS Subscribers",
"description": "Whether to allow SMS subscriptions"
},
"allow_rss_atom_feeds": {
"type": "boolean",
"title": "Allow RSS/Atom Feeds",
"description": "Whether to allow RSS or Atom feeds"
},
"allow_webhook_subscribers": {
"type": "boolean",
"title": "Allow Webhook Subscribers",
"description": "Whether to allow webhook subscriptions"
},
"time_zone": {
"type": "string",
"title": "Time Zone",
"description": "The time zone used for the Statuspage"
},
"createdAt": {
"type": "string",
"format": "date-time",
"title": "Created At",
"description": "When the Statuspage was created"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"title": "Last Updated At",
"description": "When the Statuspage was last updated"
}
}
},
"calculationProperties": {},
"aggregationProperties": {}
}
Statuspage Incident Blueprint
{
"identifier": "statuspageIncident",
"title": "Statuspage Incident",
"icon": "Alert",
"description": "An incident reported on a Statuspage",
"schema": {
"properties": {
"status": {
"type": "string",
"title": "Current Status",
"enum": [
"investigating",
"identified",
"monitoring",
"resolved",
"postmortem",
"scheduled",
"in_progress",
"verifying",
"completed"
],
"enumColors": {
"investigating": "blue",
"identified": "orange",
"monitoring": "yellow",
"resolved": "green",
"postmortem": "purple",
"scheduled": "lightGray",
"in_progress": "blue",
"verifying": "yellow",
"completed": "green"
},
"description": "Current status of the incident"
},
"impact": {
"type": "string",
"title": "Impact",
"enum": ["none", "minor", "major", "critical"],
"enumColors": {
"none": "green",
"minor": "yellow",
"major": "orange",
"critical": "red"
},
"description": "The impact level of the incident"
},
"createdAt": {
"type": "string",
"format": "date-time",
"title": "Created At",
"description": "When the incident was first reported"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"title": "Last Updated At",
"description": "When the incident was last updated"
},
"startedAt": {
"type": "string",
"format": "date-time",
"title": "Started At",
"description": "When the incident actually began"
},
"resolvedAt": {
"type": "string",
"format": "date-time",
"title": "Resolved At",
"description": "When the incident was resolved (null if ongoing)"
},
"shortlink": {
"type": "string",
"format": "url",
"title": "Short Link",
"description": "A shortened URL for sharing the incident status page"
},
"postmortemPublishedAt": {
"type": "string",
"format": "date-time",
"title": "Postmortem Published At",
"description": "When the postmortem analysis was published (null if not yet published)"
},
"scheduled_for": {
"type": "string",
"format": "date-time",
"title": "Scheduled For",
"description": "Start time for a scheduled incident (null if not scheduled)"
},
"scheduled_until": {
"type": "string",
"format": "date-time",
"title": "Scheduled Until",
"description": "End time for a scheduled incident (null if not scheduled)"
},
"scheduled_remind_prior": {
"type": "boolean",
"title": "Scheduled Remind Prior",
"description": "Whether to send a reminder before a scheduled incident"
},
"scheduled_reminded_at": {
"type": "string",
"format": "date-time",
"title": "Scheduled Reminded At",
"description": "When the reminder for a scheduled incident was sent (null if not applicable)"
},
"scheduled_auto_in_progress": {
"type": "boolean",
"title": "Scheduled Auto In Progress",
"description": "Whether to automatically transition the incident to 'in progress'"
},
"scheduled_auto_completed": {
"type": "boolean",
"title": "Scheduled Auto Completed",
"description": "Whether to automatically mark the incident as completed"
},
"metadata": {
"type": "object",
"title": "Metadata",
"description": "Custom metadata associated with the incident"
},
"reminder_intervals": {
"type": "string",
"title": "Reminder Intervals",
"description": "Intervals for sending reminders for a scheduled incident (null if not applicable)"
},
"postmortem_body": {
"type": "string",
"format": "markdown",
"title": "Postmortem",
"description": "The content of the postmortem analysis"
},
"postmortem_body_last_updated_at": {
"type": "string",
"format": "date-time",
"title": "Postmortem Last Updated At",
"description": "When the postmortem body was last updated (null if not applicable)"
},
"postmortem_ignored": {
"type": "boolean",
"title": "Postmortem Ignored",
"description": "Whether the postmortem has been ignored"
},
"postmortem_published_at": {
"type": "string",
"format": "date-time",
"title": "Postmortem Published At",
"description": "When the postmortem was published (null if not yet published)"
},
"postmortem_notified_subscribers": {
"type": "boolean",
"title": "Postmortem Notified Subscribers",
"description": "Whether subscribers were notified about the postmortem"
},
"postmortem_notified_twitter": {
"type": "boolean",
"title": "Postmortem Notified Twitter",
"description": "Whether the postmortem was announced on Twitter"
}
}
},
"calculationProperties": {
"category": {
"title": "Category",
"description": "Category of the incident",
"calculation": ".properties | .status as $status | if ($status | IN(\"scheduled\", \"in_progress\", \"verifying\", \"completed\")) then \"maintainance\" else \"incident\" end",
"type": "string",
"colorized": true,
"colors": {
"maintainance": "bronze",
"incident": "red"
}
}
},
"relations": {
"statuspage": {
"target": "statuspage",
"required": false,
"title": "Status Page",
"many": false
}
}
}
GitHub Workflowโ
We recommend creating a dedicated repository for the workflows that are used by Port actions.
- Create the file
create-statuspage-incident.yml
in the.github/workflows
folder of your repository.
create-statuspage-incident
GitHub Workflow
name: Create Statuspage Incident and Notify Port
on:
workflow_dispatch:
inputs:
incident_title:
description: "Title of the incident"
required: true
incident_message:
description: "Description of the incident"
required: true
incident_severity:
description: "Severity: none, minor, major, critical"
required: true
type: choice
options:
- none
- minor
- major
- critical
incident_status:
description: "Status: investigating, identified, monitoring, resolved"
required: true
type: choice
options:
- investigating
- identified
- monitoring
- resolved
status_page:
description: "Statuspage page ID"
required: false
port_context:
description: "Port context"
required: true
env:
STATUSPAGE_PAGE_ID: ${{ inputs.status_page || secrets.STATUSPAGE_PAGE_ID }}
jobs:
create_statuspage_incident:
runs-on: ubuntu-latest
steps:
- name: Notify Port (Initial)
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
baseUrl: https://api.getport.io
operation: PATCH_RUN
runId: ${{ fromJson(inputs.port_context).runId }}
logMessage: |
Creating Statuspage incident: ${{ inputs.incident_title }}
- name: Create Statuspage Incident
id: create_incident
run: |
echo "Creating Statuspage incident: ${{ inputs.incident_title }} in status page: ${{ env.STATUSPAGE_PAGE_ID }}"
payload='{
"incident": {
"name": "${{ inputs.incident_title }}",
"body": "${{ inputs.incident_message }}",
"status": "${{ inputs.incident_status }}",
"impact_override": "${{ inputs.incident_severity }}",
"deliver_notifications": true,
"metadata": {
"port": {
"runId": "${{ fromJson(inputs.port_context).runId }}",
"triggeredBy": "${{ fromJson(inputs.port_context).trigger.by.user.email }}"
}
}
}
}'
# Add scheduled fields only if the incident is scheduled
if [[ "${{ inputs.incident_status }}" =~ ^(scheduled|in_progress|verifying|completed)$ ]]; then
# Calculate default start and end times for scheduled maintenance (next hour, 3 hours duration)
start_time=$(date -u -d "+1 hour" +%Y-%m-%dT%H:%M:%S.000Z)
end_time=$(date -u -d "+4 hours" +%Y-%m-%dT%H:%M:%S.000Z)
payload=$(echo "$payload" | jq --arg start_time "$start_time" --arg end_time "$end_time" '.incident += {"scheduled_for": $start_time, "scheduled_until": $end_time, "scheduled_remind_prior": true, "scheduled_auto_in_progress": true, "scheduled_auto_completed": true}')
fi
echo "$payload"
response=$(curl -s -X POST "https://api.statuspage.io/v1/pages/${{ env.STATUSPAGE_PAGE_ID }}/incidents" \
-H "Authorization: OAuth ${{ secrets.STATUSPAGE_API_KEY }}" \
-H "Content-Type: application/json" \
-d "$payload")
echo "$response"
echo "INCIDENT=$response" >> $GITHUB_ENV
echo "$response" > response.json
echo "incident_id=$(jq -r '.id' response.json)" >> $GITHUB_OUTPUT
echo "name=$(jq -r '.name' response.json)" >> $GITHUB_OUTPUT
echo "status=$(jq -r '.status' response.json)" >> $GITHUB_OUTPUT
echo "impact=$(jq -r '.impact' response.json)" >> $GITHUB_OUTPUT
echo "created_at=$(jq -r '.created_at' response.json)" >> $GITHUB_OUTPUT
echo "updated_at=$(jq -r '.updated_at' response.json)" >> $GITHUB_OUTPUT
- name: Upsert Entity in Port
uses: port-labs/port-github-action@v1
env:
INCIDENT_ID: ${{ steps.create_incident.outputs.incident_id }}
STATUS: ${{ steps.create_incident.outputs.status }}
IMPACT: ${{ steps.create_incident.outputs.impact }}
CREATED_AT: ${{ steps.create_incident.outputs.created_at }}
UPDATED_AT: ${{ steps.create_incident.outputs.updated_at }}
with:
identifier: ${{ env.INCIDENT_ID }}
title: ${{ inputs.incident_title }}
team: "[]"
icon: DefaultBlueprint
blueprint: ${{ fromJson(inputs.port_context).blueprint }}
properties: |-
{
"impact": "${{ env.IMPACT }}",
"status": "${{ env.STATUS }}",
"createdAt": "${{ env.CREATED_AT }}",
"updatedAt": "${{ env.UPDATED_AT }}",
"pageId": "${{ env.STATUSPAGE_PAGE_ID }}"
}
relations: |-
{
"statuspage": "${{ env.STATUSPAGE_PAGE_ID }}"
}
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
baseUrl: https://api.getport.io
operation: UPSERT
runId: ${{ fromJson(inputs.port_context).runId }}
- name: Notify Port (Initial)
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
baseUrl: https://api.getport.io
operation: PATCH_RUN
runId: ${{ fromJson(inputs.port_context).runId }}
status: "SUCCESS"
logMessage: |
Finished creating Statuspage incident: ${{ inputs.incident_title }}
- Create the file
update-statuspage-incident.yml
in the.github/workflows
folder of your repository.
update-statuspage-incident
GitHub Workflow
name: Update Statuspage Incident
on:
workflow_dispatch:
inputs:
incident_id:
description: 'ID of the Statuspage incident to update'
required: true
new_incident_status:
description: 'New status of the incident'
required: true
update_message:
description: 'message to include in the update'
required: false
port_context:
description: "Port context"
required: true
env:
STATUSPAGE_PAGE_ID: ${{ fromJson(inputs.port_context).entity.relations.statuspage || secrets.STATUSPAGE_PAGE_ID }}
INCIDENT_ID: ${{ fromJson(inputs.port_context).entity.identifier }}
jobs:
update_incident:
runs-on: ubuntu-latest
steps:
- name: Notify Port (Initial)
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
baseUrl: https://api.getport.io
operation: PATCH_RUN
runId: ${{ fromJson(inputs.port_context).runId }}
logMessage: |
Creating Statuspage incident: ${{ inputs.incident_id }} in status page: ${{ fromJson(inputs.port_context).entity.title }}
- name: Update Statuspage Incident
run: |
curl -X PUT https://api.statuspage.io/v1/pages/${{ env.STATUSPAGE_PAGE_ID }}/incidents/${{ env.INCIDENT_ID }} \
-H 'Authorization: OAuth ${{ secrets.STATUSPAGE_API_KEY }}' \
-H 'Content-Type: application/json' \
-d '{
"incident": {
"body": "${{ inputs.update_message }}",
"status": "${{ inputs.new_incident_status }}",
"metadata": {
"port": {
"runId": "${{ fromJson(inputs.port_context).runId }}",
"trigger": "${{ fromJson(inputs.port_context).trigger }}"
}
}
}
}'
- name: Upsert Entity in Port
uses: port-labs/port-github-action@v1
with:
identifier: ${{ fromJson(inputs.port_context).entity.identifier }}
title: ${{ fromJson(inputs.port_context).entity.title }}
team: "[]"
icon: DefaultBlueprint
blueprint: ${{ fromJson(inputs.port_context).blueprint }}
properties: |-
{
"status": "${{ inputs.new_incident_status }}"
}
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
operation: UPSERT
runId: ${{ fromJson(inputs.port_context).runId }}
- name: Notify Port about Update
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
baseUrl: https://api.getport.io
operation: PATCH_RUN
status: "SUCCESS"
runId: ${{ fromJson(inputs.port_context).runId }}
logMessage: |
Updated Statuspage incident (${{ inputs.incident_id }}) to status: ${{ inputs.new_incident_status }}
- Customize Your Workflow. Refer to the Statuspage API docs to tailor the data fields sent within your workflow to align perfectly with your needs.
The baseUrl
, port_region
, port.baseUrl
, portBaseUrl
, port_base_url
and OCEAN__PORT__BASE_URL
parameters are used to select which instance or Port API will be used.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.getport.io), your API URL is
https://api.getport.io
. - If you use the US region of Port (https://app.us.getport.io), your API URL is
https://api.us.getport.io
.
Port Configurationโ
Create two self service actions using the following JSON configuration.
Create Statuspage Incident (click to expand)
Make sure to replace <GITHUB_ORG>
and <GITHUB_REPO>
with your GitHub organization and repository names respectively.
{
"identifier": "create_statuspage_incident",
"title": "Create Statuspage Incident",
"icon": "Alert",
"description": "Create a Statuspage incident",
"trigger": {
"type": "self-service",
"operation": "CREATE",
"userInputs": {
"properties": {
"incident_title": {
"type": "string",
"title": "Incident Title",
"description": "Concise title for the incident"
},
"incident_message": {
"type": "string",
"title": "Incident Description",
"description": "Detailed description of the incident"
},
"incident_severity": {
"type": "string",
"title": "Incident Severity",
"enum": [
"none",
"minor",
"major",
"critical"
]
},
"incident_status": {
"type": "string",
"title": "Incident Status",
"enum": [
"investigating",
"identified",
"monitoring",
"resolved"
]
},
"status_page": {
"type": "string",
"title": "Status Page",
"blueprint": "statuspage",
"description": "The status page",
"format": "entity"
}
},
"required": [
"incident_title",
"incident_message",
"incident_severity",
"incident_status"
],
"order": [
"incident_title",
"incident_message",
"incident_severity",
"incident_status",
"status_page"
]
},
"blueprintIdentifier": "statuspageIncident"
},
"invocationMethod": {
"type": "GITHUB",
"org": "<GITHUB_ORG>",
"repo": "<GITHUB_REPO>",
"workflow": "create-statuspage-incident.yml",
"workflowInputs": {
"incident_title": "{{ .inputs.\"incident_title\" }}",
"incident_message": "{{ .inputs.\"incident_message\" }}",
"incident_severity": "{{ .inputs.\"incident_severity\" }}",
"incident_status": "{{ .inputs.\"incident_status\" }}",
"port_context": {
"entity": "{{.entity}}",
"blueprint": "{{.action.blueprint}}",
"runId": "{{.run.id}}",
"trigger": "{{ .trigger }}"
}
},
"reportWorkflowStatus": true
},
"requiredApproval": false
}
Update Statuspage Incident (click to expand)
Make sure to replace <GITHUB_ORG>
and <GITHUB_REPO>
with your GitHub organization and repository names respectively.
{
"identifier": "update_statuspage_incident",
"title": "Update Statuspage Incident",
"icon": "Alert",
"description": "Update the status of an existing Statuspage incident",
"trigger": {
"type": "self-service",
"operation": "DAY-2",
"userInputs": {
"properties": {
"new_incident_status": {
"type": "string",
"title": "New Incident Status",
"enum": [
"investigating",
"identified",
"monitoring",
"resolved"
]
},
"update_message": {
"type": "string",
"title": "Update Message (Optional)",
"description": "Additional information about the status update"
}
},
"required": [
"new_incident_status"
],
"order": [
"new_incident_status",
"update_message"
]
},
"blueprintIdentifier": "statuspageIncident"
},
"invocationMethod": {
"type": "GITHUB",
"org": "<GITHUB_ORG>",
"repo": "<GITHUB_REPO>",
"workflow": "update-statuspage-incident.yml",
"workflowInputs": {
"incident_id": "{{ .entity.identifier }}",
"new_incident_status": "{{ .inputs.\"new_incident_status\" }}",
"update_message": "{{ .inputs.\"update_message\" }}",
"port_context": {
"entity": "{{.entity}}",
"blueprint": "{{.action.blueprint}}",
"runId": "{{.run.id}}",
"trigger": "{{ .trigger }}"
}
},
"reportWorkflowStatus": true
},
"requiredApproval": false
}
Now you should see the Create Statuspage Incident
and Update Statuspage Incident
actions in the self-service page.
Let's test it!โ
Prerequisitesโ
To test the new actions, you need to make sure that your Port catalog contains your Statuspage entities, or that you added your Statuspage ID as a secret to your GitHub repository (under the STATUSPAGE_PAGE_ID
secret in GitHub)
To find your Statuspage ID:
- Browse to Statuspage
- Select the page you want to create an incident for from the sidebar on the left side
- The Statuspage ID will appear in the URL. for example, given the URL:
https://manage.statuspage.io/pages/aaaaaaaaaaaa/incidents
, the Statuspage ID isaaaaaaaaaaaa
Creating an Incidentโ
- Go to the self-service hub in Port.
- Click the Create Statuspage Incident action.
- Fill in the title, description, severity, and status.
- Click Execute.
Updating an Incidentโ
- Go to the self-service hub in Port.
- Click the Update Statuspage Incident action.
- Select the incident you want to update.
- Choose the new status and add an optional update message.
- Click Execute.
Congrats ๐ You can now create and update a Statuspage incident in Port ๐ฅ