New: AI-native post-mortems are here! Get a data-rich draft in minutes.

Every incident platform needs to know who owns what. Which team owns which service. Which backlog to send follow-ups to. Which escalation path to page when something breaks.
The problem is that most platforms encode this ownership logic separately in every configuration: alert routing, workflows, ITSM ticket syncing, and more. Each one maintains its own copy of the same information, in its own format. At scale, that can mean thousands of lines of hardcoded rules spread across dozens of places, all encoding the same underlying information.
And when organizations change, for example adding new services or creating teams, those changes cascade across all of those configurations. Miss one update, and the wrong team gets paged at 3am, follow-ups vanish into the wrong backlog, or reports misattribute incidents. All risk you can’t afford to take.
With incident.io Catalog, we take a different, and much simpler, approach. You model your organization once, and every part of the platform references that model dynamically. The rules and logic stay the same whether you have 5 services or 5,000. Simply sync the data from your source of truth and you’re done.
Let’s walk through a concrete example to see how this works in practice.
Imagine a (laughably simple!) engineering org. It has three teams, each of which owns services, has a Linear project for their backlog, and an escalation path so they can be paged:
| Team | Services | Linear Project | Escalation Path |
|---|---|---|---|
| Payments | payments-api, billing-worker | PAY | Payments On-Call |
| Search | search-api, recommendations-api | SEARCH | Search On-Call |
| Platform | auth-service | PLAT | Platform On-Call |
┌── Services: payments-api, billing-worker
Payments ───┼── Linear: PAY
└── Escalation: Payments On-Call
┌── Services: search-api, recommendations-api
Search ─────┼── Linear: SEARCH
└── Escalation: Search On-Call
┌── Services: auth-service
Platform ───┼── Linear: PLAT
└── Escalation: Platform On-Call
Now let's put it to work.
Now let's imagine the organization wants to configure a couple of simple incident management workflows:
1. Exporting follow-up actions to the right backlog When a follow-up action is created after an incident, send it to the owning team's Linear project.
2. Routing alerts to the right escalation path When an alert fires for a service, page the team that owns it.
On most platforms, you'd configure these with static conditional logic:
Follow-up export:
IF team = Payments
THEN export to PAY
ELSE IF team = Search
THEN export to SEARCH
ELSE IF team = Platform
THEN export to PLAT
Alert routing:
IF service = payments-api
THEN page Payments On-Call
ELSE IF service = billing-worker
THEN page Payments On-Call
ELSE IF service = search-api
THEN page Search On-Call
ELSE IF service = recommendations-api
THEN page Search On-Call
ELSE IF service = auth-service
THEN page Platform On-Call
Straightforward enough with 3 teams and 5 services. But things don't stay this simple.
Let's fast forward a few months, where our organization has shipped 3 new services and added a new team:
| Team | New Services |
|---|---|
| Payments | + checkout-api, + inventory-api |
| Search | + personalization-api |
| Data (new) | + data-pipeline |
And now, the owner of the incident management system has to make a bunch of changes to both automations keep things working:
Follow-up export:
IF team = Payments
THEN export to PAY
ELSE IF team = Search
THEN export to SEARCH
ELSE IF team = Platform
THEN export to PLAT
ELSE IF team = Data ← new
THEN export to DATA ← new
Alert routing:
IF service = payments-api
THEN page Payments On-Call
ELSE IF service = billing-worker
THEN page Payments On-Call
ELSE IF service = checkout-api ← new
THEN page Payments On-Call ← new
ELSE IF service = inventory-api ← new
THEN page Payments On-Call ← new
ELSE IF service = search-api
THEN page Search On-Call
ELSE IF service = recommendations-api
THEN page Search On-Call
ELSE IF service = personalization-api ← new
THEN page Search On-Call ← new
ELSE IF service = auth-service
THEN page Platform On-Call
ELSE IF service = data-pipeline ← new
THEN page Data On-Call ← new
Two automations. A heap of changes to logic, and a risk introduced that you've missed something along the way, and someone might not get paged when they need to.
But in reality, it's never just two automations. Ownership logic and other connectivity between things is embedded in:
That's a lot of configuration, each encoding ownership independently. Every new service, every new team, every reorg means updating all of them.
And real organizations aren't adding one service a quarter; they're shipping new services every week, creating and merging teams, moving people around.
The static logic grows and grows, and each update is a chance to miss something. A missed routing rule means the wrong team gets paged at 3am. A missed workflow means follow-ups disappear into the wrong backlog.
At incident.io, we've taken a different stance. Rather than repeating configurations all of the place, you replace all that static logic with two things: data in Catalog, and rules that operate across it.
Instead of hardcoded if/else chains, your automations just say:
Follow-up export:
When a follow-up is created
→ look up the person's team
→ find that team's Linear project
→ export it there
Alert routing:
When an alert arrives
→ extract the service from the alert
→ find the owning team in Catalog
→ look up that team's escalation path
→ page them
The rules don't reference any specific team, service, or escalation path. They reference the relationships between them.
When you add new services or a new team, the rules don't change. You just update the data in Catalog: add the service, set its owner, and everything flows through automatically.
Here's both scenarios side by side:
Original setup (3 teams, 5 services):
Static logic: │ Catalog rule:
│
IF svc = payments-api │ Alert arrives
THEN page Payments On-Call │ → find service owner
ELSE IF svc = billing-worker │ → look up escalation path
THEN page Payments On-Call │ → page them
ELSE IF svc = search-api │
THEN page Search On-Call │
ELSE IF svc = recos-api │
THEN page Search On-Call │
ELSE IF svc = auth-service │
THEN page Platform On-Call │
│
(10 lines of logic) │
After growth (4 teams, 9 services):
Static logic: │ Catalog rule:
│
IF svc = payments-api │ Alert arrives
THEN page Payments On-Call │ → find service owner
ELSE IF svc = billing-worker │ → look up escalation path
THEN page Payments On-Call │ → page them
ELSE IF svc = checkout-api │
THEN page Payments On-Call │ (the same 3 lines)
ELSE IF svc = inventory-api │
THEN page Payments On-Call │
ELSE IF svc = search-api │
THEN page Search On-Call │
ELSE IF svc = recos-api │
THEN page Search On-Call │
ELSE IF svc = personal-api │
THEN page Search On-Call │
ELSE IF svc = auth-service │
THEN page Platform On-Call │
ELSE IF svc = data-pipeline │
THEN page Data On-Call │
│
(18 lines of logic) │
The left side grows with every change. The right side never changes. And that means zero headaches for you, and high confidence that things will just work.
Keeping Catalog in sync
The obvious question: "Great, but now I need to keep Catalog up to date. Isn't that just moving the problem?"
We've thought a lot about this. Catalog integrates natively with the systems you're probably already maintaining:
You don't maintain Catalog by hand. You point it at your existing sources of truth and it stays in sync automatically. The ownership data you're already curating elsewhere flows straight in — no duplication, no drift.
The reality is that most organizations don't have 4 teams, and the don't just add one service every few months. The scale is high and the changes are constant. New services every sprint, and teams created to tackle new initiatives, merged in reorgs, or disbanded when priorities shift.
Imagine instead that we have 40 teams and 300 services.
Static logic:
IF service = service-1
THEN page Team A On-Call
ELSE IF service = service-2
THEN page Team A On-Call
ELSE IF service = service-3
THEN page Team B On-Call
ELSE IF service = service-4
THEN page Team B On-Call
ELSE IF service = service-5
THEN page Team C On-Call
...
... (600 lines)
...
ELSE IF service = service-300
THEN page Team ZZ On-Call
600 lines of conditional logic. In a single automation. Multiply that across 10+ configurations and you're maintaining thousands of lines of hardcoded rules that all encode the same ownership information. Every reorg, every new service, every team change cascades across all of them.
As a reminder, your Catalog-based logic still hasn't changed.
Alert arrives
→ find service owner
→ look up escalation path
→ page them
That’s 3 lines. Whether you have 5 services or 5,000.
Define your organization once. Use it everywhere. Nothing breaks as things change.

I'm one of the co-founders, and the Chief Product Officer here at incident.io.

Post-mortems are one of the most consistently underperforming rituals in software engineering. Most teams do them. Most teams know theirs aren't working. And most teams reach for the same diagnosis: the templates are too long, nobody has time, nobody reads them anyway.
incident.io
This is the story of how incident.io keeps its technology stack intentionally boring, scaling to thousands of customers with a lean platform team by relying on managed GCP services and a small set of well-chosen tools.
Matthew Barrington 
Blog about combining incident.io's incident context with Apono's dynamic provisioning, the new integration ensures secure, just-in-time access for on-call engineers, thereby speeding up incident response and enhancing security.
Brian HansonReady for modern incident management? Book a call with one of our experts today.
