Sync Port Services to incident.io
This guide demonstrates how to set up a GitHub workflow that periodically syncs service entities from Port into incident.io's catalog, ensuring better visibility and context for your services during incident management.
Prerequisitesโ
- You need a Port account and should have completed the onboarding process
- A GitHub repository where you can trigger a workflow for this guide
- An incident.io account and an API key
Set up required secrets and permissionsโ
- In your GitHub repository, go to Settings > Secrets and add the following secrets:
- PORT_CLIENT_ID- Port Client ID learn more
- PORT_CLIENT_SECRET- Port Client Secret learn more
- INCIDENT_IO_API_KEY- API key from your incident.io dashboard with the following permissions:- Can manage catalog types and edit catalog data
- Can view catalog types and entries
 
- INCIDENT_IO_CATALOG_TYPE_ID- Incident.io catalog type ID. To be created in this section
 
Below is the JSON for the Service blueprint that represents the service entities you will sync from Port to incident.io:
Service blueprint (click to expand)
{
  "identifier": "service",
  "title": "Service",
  "icon": "Github",
  "schema": {
    "properties": {
      "readme": {
        "title": "README",
        "type": "string",
        "format": "markdown",
        "icon": "Book"
      },
      "url": {
        "title": "URL",
        "format": "url",
        "type": "string",
        "icon": "Link"
      },
      "language": {
        "icon": "Git",
        "type": "string",
        "title": "Language",
        "enum": [
          "GO",
          "Python",
          "Node",
          "React"
        ],
        "enumColors": {
          "GO": "red",
          "Python": "green",
          "Node": "blue",
          "React": "yellow"
        }
      },
      "type": {
        "title": "Type",
        "description": "This service's type",
        "type": "string",
        "enum": [
          "Backend",
          "Frontend",
          "Library"
        ],
        "enumColors": {
          "Backend": "purple",
          "Frontend": "pink",
          "Library": "green"
        },
        "icon": "DefaultProperty"
      },
      "lifecycle": {
        "title": "Lifecycle",
        "type": "string",
        "enum": [
          "Production",
          "Experimental",
          "Deprecated"
        ],
        "enumColors": {
          "Production": "green",
          "Experimental": "yellow",
          "Deprecated": "red"
        },
        "icon": "DefaultProperty"
      }
    },
    "required": []
  },
  "mirrorProperties": {},
  "calculationProperties": {},
  "aggregationProperties": {},
  "relations": {}
}
While this example shows a standard service schema, you can easily add new properties to the blueprint JSON schema to represent additional details about your services and sync them to incident.io.
Create a catalog type in incident.ioโ
To sync your Port services into incident.io, you first need to create a catalog type in incident.io. Follow the steps below:
- 
Login to your incident.io account 
- 
Click Catalog on the left navigation bar 
- 
Click on add a custom type 
- 
Fill in the following details: - Name: a suitable name such as Port Services
- Description: Provide a description for the catalog type
- Under Categories, select Servicesfrom the list
- Under Attributes, add the following columns to reflect the properties of your Port Serviceblueprint:- url: Select- stringas the data type
- readme: Select- rich textas the data type
- language: Select- stringas the data type
- lifecycle: Select- stringas the data type
- type: Select- stringas the data type
 
 
- Name: a suitable name such as 
- 
Once successful, click on the newly created type and take note of the ID from the browser's URL. For example, the ID might be something like 01J5RB95K5NNDE1CRQ7ZQ24YH5for this browser URL (https://app.incident.io/organization/catalog/01J5RB95K5NNDE1CRQ7ZQ24YH5). Create a GitHub secret (INCIDENT_IO_CATALOG_TYPE_ID) in the repository with the value of this ID.
Create GitHub workflowโ
Let's go ahead and create a GitHub workflow file in a GitHub repository meant for the incident.io integration:
- Create a GitHub repository (or use an existing one)
- Create a .githubdirectory andworkflowssub directory
Inside the .github/workflows directory create a file called sync-port-services-to-incident-io.yml with the following content:
GitHub workflow configuration (click to expand)
The default syncing interval is set to 2 hours. Adjust it to suit your use case
name: Sync Data to incident.io
on:
  schedule:
    - cron: "0 */2 * * *" # every two hours. Adjust this value
jobs:
  sync-data:
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repository
        uses: actions/checkout@v4
        
      - name: Get Port Access Token
        id: get_token
        run: |
          access_token=$(curl --location --request POST 'https://api.getport.io/v1/auth/access_token' \
          --header 'Content-Type: application/json' \
          --data-raw '{
              "clientId": "${{ secrets.PORT_CLIENT_ID }}",
              "clientSecret": "${{ secrets.PORT_CLIENT_SECRET }}"
          }' | jq '.accessToken' | sed 's/"//g')
          echo "access_token=$access_token" >> $GITHUB_ENV
      - name: Get Service Entities from Port
        id: get_entities
        run: |
          response=$(curl -X GET "https://api.getport.io/v1/blueprints/service/entities" \
              -H "Authorization: Bearer ${{ env.access_token }}" \
              -H "Content-Type: application/json")
          
          # Check if response is empty or if an error occurred
          if [ -z "$response" ]; then
            echo "No response received from Port API."
            exit 1
          else
            echo "Port Service Entities Response:"
            echo "$response"
          fi
          
          # Save response to file and environment variable
          echo "$response" > response.json
      - name: Get incident.io Schema
        id: get_schema
        run: |
          schema_response=$(curl --location --request GET 'https://api.incident.io/v2/catalog_types/${{ secrets.INCIDENT_IO_CATALOG_TYPE_ID }}' \
          -H "Authorization: Bearer ${{ secrets.INCIDENT_IO_API_KEY }}" \
          -H "Content-Type: application/json")
          echo "$schema_response" > schema.json
          
      - name: Map and Send Data to incident.io
        run: |
          schema=$(jq '.catalog_type.schema.attributes' schema.json)
          # Extract IDs of incident.io catalog attributes. Note that additional properties can be added if needed
          url_id=$(echo "$schema" | jq -r '.[] | select(.name == "url") | .id')
          readme_id=$(echo "$schema" | jq -r '.[] | select(.name == "readme") | .id')
          language_id=$(echo "$schema" | jq -r '.[] | select(.name == "language") | .id')
          lifecycle_id=$(echo "$schema" | jq -r '.[] | select(.name == "lifecycle") | .id')
          type_id=$(echo "$schema" | jq -r '.[] | select(.name == "type") | .id')
          # Read entities as a JSON array, and use `jq` to iterate correctly
          entities=$(jq -c '.entities[]' response.json)
          echo "$entities" | while IFS= read -r entity; do
            name=$(echo "$entity" | jq -r '.title // empty')
            if [ -z "$name" ]; then
              echo "Error: 'name' field is required but is empty. Skipping this entity."
              continue
            fi
            data=$(jq -n \
              --arg url_id "$url_id" \
              --arg url "$(echo "$entity" | jq -r '.properties.url // empty')" \
              --arg readme_id "$readme_id" \
              --arg readme "$(echo "$entity" | jq -r '.properties.readme // empty')" \
              --arg language_id "$language_id" \
              --arg language "$(echo "$entity" | jq -r '.properties.language // empty')" \
              --arg lifecycle_id "$lifecycle_id" \
              --arg lifecycle "$(echo "$entity" | jq -r '.properties.lifecycle // empty')" \
              --arg type_id "$type_id" \
              --arg type "$(echo "$entity" | jq -r '.properties.type // empty')" \
              --arg external_id "$(echo "$entity" | jq -r '.identifier')" \
              --arg name "$name" \
              --arg catalog_type_id "${{ secrets.INCIDENT_IO_CATALOG_TYPE_ID }}" \
              '{
                "aliases": [],
                "attribute_values": {
                  ($url_id): {"value": {"literal": $url}},
                  ($readme_id): {"value": {"literal": $readme}},
                  ($language_id): {"value": {"literal": $language}},
                  ($lifecycle_id): {"value": {"literal": $lifecycle}},
                  ($type_id): {"value": {"literal": $type}}
                },
                "catalog_type_id": $catalog_type_id,
                "external_id": $external_id,
                "name": $name
              }')
            echo "Sending data to API for entity $name"
            response=$(curl -i -X POST "https://api.incident.io/v2/catalog_entries" \
              -H "Authorization: Bearer ${{ secrets.INCIDENT_IO_API_KEY }}" \
              -H "Content-Type: application/json" \
              -d "$data")
            echo "Response from Incident.io API for entity $name"
          done
The port_region, port.baseUrl, portBaseUrl, port_base_url and OCEAN__PORT__BASE_URL parameters are used to select which instance of 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.port.io), your API URL is https://api.port.io.
- If you use the US region of Port (https://app.us.port.io), your API URL is https://api.us.port.io.
Resultsโ
Once the scheduled GitHub workflow is triggered, the service entities from Port will be automatically synced into your incident.io catalog. To view the ingested catalog items:
- Log in to your incident.io account.
- Navigate to the Catalog section in the left navigation bar.
- Search for the Port Servicescatalog type or any custom name you provided.
- You should now see the synced service entities from Port listed under this catalog type, giving you improved visibility and context for managing incidents.
 
With this integration in place, youโll have real-time access to your Port services within incident.io, streamlining your incident response and management processes.
Limitationsโ
Note that incident.io can currently ingest up to 50,000 catalog items, keep this limit in mind when scaling your service catalog.