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

Check out Port for yourself ➜ 

Sync Port services to incident.io

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/sync-service-entities-to-incident-io/

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 sync service entities from Port into incident.io's catalog, ensuring better visibility and context for your services during incident management.

Choose a synchronization method

You can use either of these methods:

  • catalog-importer (recommended): incident.io's official CLI tool, designed to run as part of CI/CD or on a cronjob. It syncs structured data from Port (and other sources) into the incident.io catalog.
  • Custom GitHub workflow: A GitHub Actions workflow that fetches entities from Port and pushes them to incident.io via their API.

Prerequisites

Admin access required

You need admin access to your incident.io organization to create API keys. If you don't have admin access, contact your incident.io administrator to create the API key for you.

Setup

The catalog-importer is incident.io's official tool for syncing catalog data. It is designed to run as part of CI/CD or on a cronjob and supports multiple data sources, including Port.

Install catalog-importer

macOS (Homebrew):

brew tap incident-io/homebrew-taps
brew install catalog-importer

Other platforms:

Install from the releases page or run:

go install -v github.com/incident-io/catalog-importer/v2/cmd/catalog-importer@latest

Set up API keys

Create an incident.io API key with permissions to view and manage catalog types and entries. Set:

export INCIDENT_API_KEY="your-incident-io-api-key"

For Port, use your Port credentials:

export PORT_CLIENT_ID="your-port-client-id"
export PORT_CLIENT_SECRET="your-port-client-secret"
Selecting a Port API URL by account region

The port_region, port.baseUrl, portBaseUrl, port_base_url and OCEAN__PORT__BASE_URL parameters select which Port API instance to use:

Create the fetch script

Create fetch-from-port.sh to fetch entities from Port and output JSON for the catalog-importer:

#!/bin/bash
set -e

PORT_BASE_URL="${PORT_BASE_URL:-https://api.port.io}"
BLUEPRINT_ID="${BLUEPRINT_ID:-service}"

TOKEN=$(curl -s -X POST "${PORT_BASE_URL}/v1/auth/access_token" \
-H "Content-Type: application/json" \
-d "{\"clientId\": \"$PORT_CLIENT_ID\", \"clientSecret\": \"$PORT_CLIENT_SECRET\"}" \
| jq -r '.accessToken')

curl -s -X GET "${PORT_BASE_URL}/v1/blueprints/${BLUEPRINT_ID}/entities" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
| jq '[.entities[] | {
external_id: .identifier,
name: .title,
url: .properties.url // "",
readme: .properties.readme // "",
language: .properties.language // "",
lifecycle: .properties.lifecycle // "",
type: .properties.type // ""
}]'

Create the importer config

Create importer.jsonnet. The catalog-importer creates the catalog type automatically on first sync.

Blueprint schema matching

Ensure the attribute IDs in the config (url, readme, etc.) match the property names in your Port Service blueprint.

{
sync_id: 'port-services',

pipelines: [
{
sources: [
{
exec: {
command: ['bash', 'fetch-from-port.sh'],
},
},
],
outputs: [
{
name: 'Port Services',
description: 'Services entities synced from Port',
type_name: 'Custom["PortServices"]',
categories: ['service'],
source: {
external_id: '$.external_id',
name: '$.name',
},
attributes: [
{ id: 'url', name: 'URL', type: 'String', source: '$.url' },
{ id: 'readme', name: 'Readme', type: 'Text', source: '$.readme' },
{ id: 'language', name: 'Language', type: 'String', source: '$.language' },
{ id: 'lifecycle', name: 'Lifecycle', type: 'String', source: '$.lifecycle' },
{ id: 'type', name: 'Type', type: 'String', source: '$.type' },
],
},
],
},
],
}

For full configuration options, see the catalog-importer config reference.

Validate and sync

catalog-importer validate --config=importer.jsonnet
catalog-importer sync --config=importer.jsonnet --dry-run
catalog-importer sync --config=importer.jsonnet

Run in CI/CD

You can run the catalog-importer in GitHub Actions using the Docker image:

name: Sync Port to incident.io

on:
schedule:
- cron: "0 */2 * * *"
workflow_dispatch:

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Sync catalog
run: |
docker run --rm \
-v ${{ github.workspace }}/incident-io:/config \
-w /config \
-e INCIDENT_API_KEY=${{ secrets.INCIDENT_API_KEY }} \
-e PORT_CLIENT_ID=${{ secrets.PORT_CLIENT_ID }} \
-e PORT_CLIENT_SECRET=${{ secrets.PORT_CLIENT_SECRET }} \
incidentio/catalog-importer:latest \
sync --config importer.jsonnet

Store your config and fetch-from-port.sh in an incident-io directory in your repo. For Port's EU region, add -e PORT_BASE_URL=https://api.port.io to the docker run command.

For more details, see the catalog-importer documentation.

Limitations

Note that incident.io can currently ingest up to 50,000 catalog items. Keep this limit in mind when scaling your service catalog.