Atlassian stopped selling Opsgenie to new customers on 4 June 2025. Support ends on 5 April 2027. On that date access shuts off and unmigrated data is deleted. You have a hard deadline to move schedules, escalations, users, and integrations to a new system.
The migration is not automatic, whatever the destination. Six things do not survive the move intact: rotation layers, time restrictions, overrides, escalation delays, heartbeats, and email senders. You must export the current state, map the logic, and repoint the sources.
This checklist covers the export commands, the six manual steps, and the cutover order. It works whether you move to Jira Service Management, another pager, or fold paging into your monitoring. The export takes ten minutes, so do it this week rather than in the last quarter.
The deadline and the API key
Atlassian recommends Jira Service Management as the destination. It absorbed Opsgenie’s alerting and on-call features and includes an automated migration for schedules, escalation policies, and integrations. This fits Jira-native teams. It is a full service-management suite priced per agent, a much bigger product than a pager. Even with the automated path, the six items below need checking by hand.
The Opsgenie REST API works until the deadline. Every export below is a GET request; nothing is modified.
Settings -> App settings -> API key management -> Add new API key.
Keys have four rights: Read, Create/Update, Delete, Configuration Access. Schedules, escalations, users, and integrations need Configuration Access. A Read-only key returns HTTP 403 on those endpoints. Tick Read and Configuration Access only.
Do not use an integration API key from inside an “API” integration. These are often restricted to alert data. A team API key only sees that team’s objects, so integrations created by people who left can hide in a team nobody looks at. Use the global key.
Header: Authorization: GenieKey <key>. US accounts use https://api.opsgenie.com, EU accounts https://api.eu.opsgenie.com. Wrong host returns 401.
Export schedules, escalations, and users
The whole export takes about ten minutes.
Start with users. The API returns 100 records per page. Paginate with the offset parameter.
curl -s 'https://api.opsgenie.com/v2/users?limit=100&offset=0' \
-H 'Authorization: GenieKey YOUR_KEY' > users-0.json
Enter fullscreen mode Exit fullscreen mode
Save the output. The fields are id, username (the email), fullName, role, timeZone, blocked, verified, and createdAt. A user with blocked=true can no longer log in but still appears in active schedules. Do not assume blocked users are inactive.
Next, export schedules. This endpoint does not paginate. You get everything in one call.
curl -s 'https://api.opsgenie.com/v2/schedules?expand=rotation' \
-H 'Authorization: GenieKey YOUR_KEY' > schedules.json
Enter fullscreen mode Exit fullscreen mode
The expand=rotation flag is mandatory. Without it, the rotation data is omitted. The response includes id, name, timezone (IANA format like Europe/London), enabled status, ownerTeam, and rotations.
Then pull escalations and integrations.
curl -s 'https://api.opsgenie.com/v2/escalations' \
-H 'Authorization: GenieKey YOUR_KEY' > escalations.json
Enter fullscreen mode Exit fullscreen mode
curl -s 'https://api.opsgenie.com/v2/integrations' \
-H 'Authorization: GenieKey YOUR_KEY' > integrations.json
Enter fullscreen mode Exit fullscreen mode
Run sanity checks immediately. Verify the user list is not empty and matches your roster.
jq -r '.data[] | [.username, .fullName, .blocked] | @tsv' users-0.json
Enter fullscreen mode Exit fullscreen mode
Check escalation rules to see the delay structure.
jq -r '.data[] | .name + ": " + ([.rules[] | (.delay.timeAmount|tostring) + "m " + .recipient.type] | join(", "))' escalations.json
Enter fullscreen mode Exit fullscreen mode
List only enabled integrations to see what is actually live.
jq -r '.data[] | select(.enabled) | .type + "\t" + .name' integrations.json
Enter fullscreen mode Exit fullscreen mode
These files are what you rebuild from. The API returns raw data. It does not calculate effective rotation orders or resolve team memberships. You must interpret the structure in the next steps. Keep these JSON files with your runbooks, not in a personal download folder. They are the only record of your current configuration.
Rotation layers, time restrictions, and overrides
Opsgenie schedules support multiple rotation layers. The API returns raw layers in order, but it does not calculate the effective order where they overlap. The last rotation in the list wins. Write down which layer is on top for each schedule.
Rotation fields include name, type (daily, weekly, hourly), length, startDate, and participants. Length is a hand-off multiplier. A weekly rotation with length 2 hands off every two weeks. Participants can be users, teams, escalations, or none. A none participant is a deliberate gap in coverage.
Rotations can carry a timeRestriction. The type is time-of-day or weekday-and-time-of-day. Fields include startDay, startHour, startMin, endDay, endHour, and endMin. These define business-hours and weekday-only rules. Many tools model these differently or not at all. Map these constraints to the new system manually.
Overrides are not in the schedules payload at all. They live in the schedule timeline: GET /v2/schedules/{id}/timeline. Overrides are temporary, so most teams recreate the current ones by hand in the new tool once the base rotations are in place. Get the base rotations right first.
Check the output for overlapping layers and note who is covering each time slot, along with the time restrictions on each rotation. None of this migrates automatically, so it is what you rebuild by hand in the destination tool.
Escalation delays are absolute
Opsgenie escalation delays are absolute, counted from alert creation, not intervals between levels. Each rule in an escalation chain has a delay object with timeAmount and timeUnit (minutes). The delay counts from the moment the alert was created, not from the time the previous rule failed to resolve it.
This is where copied numbers go wrong. A chain with delays of 0, 10, and 25 minutes fires at 0, 10, and 25 minutes after creation. A destination tool that implements delays as “wait X minutes after the previous step” produces a different timeline if you copy the numbers across: 0, 10, and 35 minutes after creation. The values that tool needs are 0, 10, and 15. Adjust the delay values in the new system to match the absolute timing you need.
Each rule also carries a condition (if-not-acked or if-not-closed), a notifyType, and a recipient of type user, schedule, or team. Review the repeat object on each policy too. It contains waitInterval, count, resetRecipientStates, and closeAlertAfterAll. Not every destination tool supports all these fields. Map the repeat behavior explicitly or accept that the retry logic will change.
Write down the absolute delay for every rule, work out what the new tool needs to fire at the same times, and build the new policies with those values. Then test the chain with a real alert while the old system is still active, and disable the old escalation only after the page arrives when expected.
Heartbeats and email senders
Opsgenie heartbeats work backwards from most integrations. The platform expects a periodic ping and pages when the ping stops. The new tool needs a heartbeat monitor, and the cron jobs that send the pings must be updated. If the cron job still points at the old endpoint, the new monitor sees silence and pages.
Email integrations are often invisible. An email integration means something sends mail to an Opsgenie address. The sender is frequently a cron job or legacy script with no other output and no clear owner. When Opsgenie shuts off, these alerts go nowhere.
Find the sender by tracing mail headers. Look for the source IP or script path in the email metadata. If you cannot find the owner, the alert dies on shutdown day.
List every heartbeat and email sender, then update the cron jobs to point at the new destination. Before disabling the old heartbeat, watch the new monitor receive a ping and stay quiet.
Inventory integrations and alert history
Every enabled integration is a webhook, email address, or API key that some tool sends alerts to. All of them break at once on shutdown day. Enabled is only configuration. Check what actually fires:
curl -s 'https://api.opsgenie.com/v2/alerts?limit=100&sort=createdAt&order=desc' \
-H 'Authorization: GenieKey YOUR_KEY' | jq -r '.data[] | [.createdAt, .integration.type, .integration.name, .source] | @tsv'
Enter fullscreen mode Exit fullscreen mode
An enabled integration with no alert in 90 days is either very quiet or dead. Ask the owner. A backup job that fires twice a year is quiet, not dead.
Classify the sources. Webhooks from Prometheus Alertmanager, Grafana, Datadog, CloudWatch, Sentry, or uptime checkers are repointed by changing the destination URL in the source tool. API scripts require searching the codebase for api.opsgenie.com and GenieKey. Chat and ticketing integrations like Slack or Jira are outbound and are not part of the ingestion surface. Status pages, iCal feeds, and mobile app links point at Opsgenie and break on shutdown day too.
Alert history is lost on shutdown day. Save a copy. Disabled integrations do not need migration. Keep the file anyway; it is the only record of what was ever wired in.
The output of the inventory is a short table: tool, integration type, last alert, owner, new destination, done.
Cutover and decommissioning
Stand up the destination first. Repoint the lowest-stakes source first, confirm an alert arrives and pages the right person, then move to the next. Leave the Opsgenie integration enabled until the new path has fired for real at least once, then disable it in Opsgenie. Disabled is reversible; do not delete.
Send a test page through every escalation level at least once on the new system while the old one still works. The only pager test that counts is a page that arrives.
Keep a log of each switch with the old integration id, the new destination, and the time.
Timeline: export now (an hour, and it de-risks everything after); pick and stand up the destination next quarter; run both and cut over channel by channel the quarter after; decommission well before the deadline. Nobody wants to switch pagers during a bad week, and there is never a good week.
Disclosure: I build AlertKick, which has an Opsgenie importer. Everything above applies whatever you move to. The longer write-ups this checklist condenses are on the AlertKick blog: exporting with the API and the integration inventory.