PagerDuty Provider

PagerDuty Provider

The PagerDuty provider integrates with PagerDuty for incident management, alerting, and on-call scheduling.

Installation

go get github.com/resolute/resolute/providers/pagerduty

Configuration

PagerDutyConfig

type PagerDutyConfig struct {
    APIKey     string // PagerDuty API key
    ServiceKey string // Default service integration key (optional)
}

Environment Variables

VariableDescriptionRequired
PAGERDUTY_API_KEYPagerDuty API keyYes
PAGERDUTY_SERVICE_KEYDefault service integration keyNo

Provider Constructor

NewProvider

func NewProvider(cfg PagerDutyConfig) *PagerDutyProvider

Creates a new PagerDuty provider.

Parameters:

  • cfg - PagerDuty configuration

Returns: *PagerDutyProvider implementing core.Provider

Example:

provider := pagerduty.NewProvider(pagerduty.PagerDutyConfig{
    APIKey:     os.Getenv("PAGERDUTY_API_KEY"),
    ServiceKey: os.Getenv("PAGERDUTY_SERVICE_KEY"),
})

Types

Incident

type Incident struct {
    ID               string           `json:"id"`
    IncidentNumber   int              `json:"incident_number"`
    Title            string           `json:"title"`
    Description      string           `json:"description"`
    Status           string           `json:"status"` // "triggered", "acknowledged", "resolved"
    Urgency          string           `json:"urgency"` // "high", "low"
    Priority         *Priority        `json:"priority"`
    Service          ServiceRef       `json:"service"`
    Assignments      []Assignment     `json:"assignments"`
    EscalationPolicy EscalationRef    `json:"escalation_policy"`
    CreatedAt        time.Time        `json:"created_at"`
    LastStatusChange time.Time        `json:"last_status_change_at"`
    ResolvedAt       *time.Time       `json:"resolved_at"`
}

type ServiceRef struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

type EscalationRef struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

type Assignment struct {
    At       time.Time `json:"at"`
    Assignee UserRef   `json:"assignee"`
}

type UserRef struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

Priority

type Priority struct {
    ID          string `json:"id"`
    Name        string `json:"name"`
    Description string `json:"description"`
    Order       int    `json:"order"`
}

Event

type Event struct {
    RoutingKey  string            `json:"routing_key"`
    EventAction string            `json:"event_action"` // "trigger", "acknowledge", "resolve"
    DedupKey    string            `json:"dedup_key"`
    Payload     EventPayload      `json:"payload"`
    Links       []EventLink       `json:"links"`
    Images      []EventImage      `json:"images"`
}

type EventPayload struct {
    Summary       string            `json:"summary"`
    Source        string            `json:"source"`
    Severity      string            `json:"severity"` // "critical", "error", "warning", "info"
    Timestamp     string            `json:"timestamp"`
    Component     string            `json:"component"`
    Group         string            `json:"group"`
    Class         string            `json:"class"`
    CustomDetails map[string]any    `json:"custom_details"`
}

type EventLink struct {
    Href string `json:"href"`
    Text string `json:"text"`
}

type EventImage struct {
    Src  string `json:"src"`
    Href string `json:"href"`
    Alt  string `json:"alt"`
}

OnCall

type OnCall struct {
    User             UserRef       `json:"user"`
    Schedule         ScheduleRef   `json:"schedule"`
    EscalationPolicy EscalationRef `json:"escalation_policy"`
    EscalationLevel  int           `json:"escalation_level"`
    Start            time.Time     `json:"start"`
    End              time.Time     `json:"end"`
}

type ScheduleRef struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

Activities

TriggerEvent

Triggers a new event (creates an incident).

Input:

type TriggerEventInput struct {
    RoutingKey  string            `json:"routing_key"`
    Summary     string            `json:"summary"`
    Source      string            `json:"source"`
    Severity    string            `json:"severity"`
    DedupKey    string            `json:"dedup_key"`
    Component   string            `json:"component"`
    Group       string            `json:"group"`
    Class       string            `json:"class"`
    CustomDetails map[string]any  `json:"custom_details"`
    Links       []EventLink       `json:"links"`
}

Output:

type TriggerEventOutput struct {
    Status   string `json:"status"`
    Message  string `json:"message"`
    DedupKey string `json:"dedup_key"`
}

Node Factory:

func TriggerEvent(input TriggerEventInput) *core.Node[TriggerEventInput, TriggerEventOutput]

Example:

triggerNode := pagerduty.TriggerEvent(pagerduty.TriggerEventInput{
    RoutingKey: os.Getenv("PAGERDUTY_ROUTING_KEY"),
    Summary:    "Database connection pool exhausted",
    Source:     "monitoring-service",
    Severity:   "critical",
    DedupKey:   "db-pool-exhausted",
    Component:  "database",
    CustomDetails: map[string]any{
        "pool_size":  100,
        "active":     100,
        "waiting":    50,
    },
})

AcknowledgeEvent

Acknowledges an existing event.

Input:

type AcknowledgeEventInput struct {
    RoutingKey string `json:"routing_key"`
    DedupKey   string `json:"dedup_key"`
}

Output:

type AcknowledgeEventOutput struct {
    Status  string `json:"status"`
    Message string `json:"message"`
}

Node Factory:

func AcknowledgeEvent(input AcknowledgeEventInput) *core.Node[AcknowledgeEventInput, AcknowledgeEventOutput]

ResolveEvent

Resolves an existing event.

Input:

type ResolveEventInput struct {
    RoutingKey string `json:"routing_key"`
    DedupKey   string `json:"dedup_key"`
}

Output:

type ResolveEventOutput struct {
    Status  string `json:"status"`
    Message string `json:"message"`
}

Node Factory:

func ResolveEvent(input ResolveEventInput) *core.Node[ResolveEventInput, ResolveEventOutput]

Example:

resolveNode := pagerduty.ResolveEvent(pagerduty.ResolveEventInput{
    RoutingKey: os.Getenv("PAGERDUTY_ROUTING_KEY"),
    DedupKey:   "db-pool-exhausted",
})

GetIncident

Gets incident details by ID.

Input:

type GetIncidentInput struct {
    ID string `json:"id"`
}

Output:

type GetIncidentOutput struct {
    Incident Incident `json:"incident"`
}

Node Factory:

func GetIncident(input GetIncidentInput) *core.Node[GetIncidentInput, GetIncidentOutput]

ListIncidents

Lists incidents with filtering options.

Input:

type ListIncidentsInput struct {
    Statuses   []string  `json:"statuses"`   // Filter by status
    ServiceIDs []string  `json:"service_ids"` // Filter by service
    Since      time.Time `json:"since"`
    Until      time.Time `json:"until"`
    Urgencies  []string  `json:"urgencies"`
    Limit      int       `json:"limit"`
    Offset     int       `json:"offset"`
}

Output:

type ListIncidentsOutput struct {
    Incidents []Incident `json:"incidents"`
    Total     int        `json:"total"`
    More      bool       `json:"more"`
}

Node Factory:

func ListIncidents(input ListIncidentsInput) *core.Node[ListIncidentsInput, ListIncidentsOutput]

Example:

listNode := pagerduty.ListIncidents(pagerduty.ListIncidentsInput{
    Statuses:   []string{"triggered", "acknowledged"},
    Urgencies:  []string{"high"},
    Since:      time.Now().Add(-24 * time.Hour),
    Limit:      50,
})

UpdateIncident

Updates an incident’s status or properties.

Input:

type UpdateIncidentInput struct {
    ID         string `json:"id"`
    Status     string `json:"status"`
    Resolution string `json:"resolution"`
    Title      string `json:"title"`
    Urgency    string `json:"urgency"`
    PriorityID string `json:"priority_id"`
}

Output:

type UpdateIncidentOutput struct {
    Incident Incident `json:"incident"`
}

Node Factory:

func UpdateIncident(input UpdateIncidentInput) *core.Node[UpdateIncidentInput, UpdateIncidentOutput]

AddNote

Adds a note to an incident.

Input:

type AddNoteInput struct {
    IncidentID string `json:"incident_id"`
    Content    string `json:"content"`
}

Output:

type AddNoteOutput struct {
    NoteID string `json:"note_id"`
}

Node Factory:

func AddNote(input AddNoteInput) *core.Node[AddNoteInput, AddNoteOutput]

GetOnCalls

Gets current on-call users.

Input:

type GetOnCallsInput struct {
    ScheduleIDs        []string  `json:"schedule_ids"`
    EscalationPolicyIDs []string `json:"escalation_policy_ids"`
    Since              time.Time `json:"since"`
    Until              time.Time `json:"until"`
}

Output:

type GetOnCallsOutput struct {
    OnCalls []OnCall `json:"oncalls"`
}

Node Factory:

func GetOnCalls(input GetOnCallsInput) *core.Node[GetOnCallsInput, GetOnCallsOutput]

Example:

onCallNode := pagerduty.GetOnCalls(pagerduty.GetOnCallsInput{
    EscalationPolicyIDs: []string{"POLICY123"},
    Since:               time.Now(),
    Until:               time.Now().Add(24 * time.Hour),
})

ListServices

Lists PagerDuty services.

Input:

type ListServicesInput struct {
    Query  string `json:"query"`
    Limit  int    `json:"limit"`
    Offset int    `json:"offset"`
}

Output:

type ListServicesOutput struct {
    Services []ServiceRef `json:"services"`
    Total    int          `json:"total"`
}

Node Factory:

func ListServices(input ListServicesInput) *core.Node[ListServicesInput, ListServicesOutput]

Usage Patterns

Automated Alerting Flow

flow := core.NewFlow("database-monitor").
    TriggeredBy(core.Schedule("*/5 * * * *")).
    Then(checkDatabaseHealthNode.As("health")).
    When(func(s *core.FlowState) bool {
        health := core.Get[HealthCheckOutput](s, "health")
        return health.Status == "critical"
    }).
        Then(pagerduty.TriggerEvent(pagerduty.TriggerEventInput{
            RoutingKey: os.Getenv("PAGERDUTY_ROUTING_KEY"),
            Summary:    core.Output("health.message"),
            Source:     "database-monitor",
            Severity:   "critical",
            DedupKey:   "db-health-check",
        })).
    EndWhen().
    Build()

Incident Response Automation

flow := core.NewFlow("incident-response").
    TriggeredBy(core.Webhook("/pagerduty/webhook")).
    Then(parseWebhookNode.As("event")).
    When(func(s *core.FlowState) bool {
        event := core.Get[WebhookEvent](s, "event")
        return event.Type == "incident.triggered"
    }).
        Then(gatherDiagnosticsNode.As("diagnostics")).
        Then(pagerduty.AddNote(pagerduty.AddNoteInput{
            IncidentID: core.Output("event.incident_id"),
            Content:    core.Output("diagnostics.summary"),
        })).
        Then(notifySlackNode).
    EndWhen().
    Build()

Auto-Resolution Flow

flow := core.NewFlow("auto-resolve").
    TriggeredBy(core.Schedule("*/5 * * * *")).
    Then(checkServiceHealthNode.As("health")).
    When(func(s *core.FlowState) bool {
        health := core.Get[HealthOutput](s, "health")
        return health.Status == "healthy"
    }).
        Then(pagerduty.ResolveEvent(pagerduty.ResolveEventInput{
            RoutingKey: os.Getenv("PAGERDUTY_ROUTING_KEY"),
            DedupKey:   "service-health-check",
        })).
    EndWhen().
    Build()

On-Call Notification Flow

flow := core.NewFlow("on-call-notify").
    TriggeredBy(core.Manual("notify")).
    Then(pagerduty.GetOnCalls(pagerduty.GetOnCallsInput{
        EscalationPolicyIDs: []string{os.Getenv("ESCALATION_POLICY_ID")},
        Since:               time.Now(),
        Until:               time.Now().Add(time.Hour),
    }).As("oncall")).
    Then(sendDirectMessageNode).
    Build()

Incident Metrics Flow

flow := core.NewFlow("incident-metrics").
    TriggeredBy(core.Schedule("0 0 * * *")).
    Then(pagerduty.ListIncidents(pagerduty.ListIncidentsInput{
        Statuses: []string{"resolved"},
        Since:    time.Now().Add(-24 * time.Hour),
        Until:    time.Now(),
        Limit:    100,
    }).As("incidents")).
    Then(calculateMetricsNode.As("metrics")).
    Then(publishMetricsNode).
    Build()

Complete Example

package main

import (
    "os"
    "time"

    "github.com/resolute/resolute/core"
    "github.com/resolute/resolute/providers/pagerduty"
)

func main() {
    // Configure provider
    pdProvider := pagerduty.NewProvider(pagerduty.PagerDutyConfig{
        APIKey: os.Getenv("PAGERDUTY_API_KEY"),
    })

    // Build intelligent alerting flow
    flow := core.NewFlow("smart-alerting").
        TriggeredBy(core.Schedule("*/5 * * * *")).
        // Check system health
        Then(checkSystemHealthNode.As("health")).
        // Alert on critical issues
        When(func(s *core.FlowState) bool {
            health := core.Get[HealthOutput](s, "health")
            return health.Status == "critical"
        }).
            Then(pagerduty.TriggerEvent(pagerduty.TriggerEventInput{
                RoutingKey: os.Getenv("PAGERDUTY_ROUTING_KEY"),
                Summary:    core.Output("health.message"),
                Source:     "health-monitor",
                Severity:   "critical",
                DedupKey:   "system-health",
                Component:  core.Output("health.component"),
                CustomDetails: map[string]any{
                    "metrics": core.Output("health.metrics"),
                },
            })).
            Then(pagerduty.GetOnCalls(pagerduty.GetOnCallsInput{
                EscalationPolicyIDs: []string{os.Getenv("ESCALATION_POLICY")},
            }).As("oncall")).
            Then(notifyOnCallSlackNode).
        EndWhen().
        // Auto-resolve when healthy
        When(func(s *core.FlowState) bool {
            health := core.Get[HealthOutput](s, "health")
            return health.Status == "healthy" && health.WasCritical
        }).
            Then(pagerduty.ResolveEvent(pagerduty.ResolveEventInput{
                RoutingKey: os.Getenv("PAGERDUTY_ROUTING_KEY"),
                DedupKey:   "system-health",
            })).
        EndWhen().
        Build()

    // Run worker
    err := core.NewWorker().
        WithConfig(core.WorkerConfig{
            TaskQueue: "alerting",
        }).
        WithFlow(flow).
        WithProviders(pdProvider).
        Run()

    if err != nil {
        panic(err)
    }
}

See Also