# PagerDuty Client

Create incidents, manage services, and interact with PagerDuty's incident management platform.

## Methods

| Method                                   | Description                                           |
| ---------------------------------------- | ----------------------------------------------------- |
| `apiRequest(options, schema, metadata?)` | Make any PagerDuty API request with schema validation |

## Usage

### Create an Incident

```typescript
import { api, z, pagerDuty } from "@superblocksteam/sdk-api";

// Integration ID from the integrations panel
const PROD_PAGERDUTY = "a1b2c3d4-5678-90ab-cdef-pagerdu00001";

const IncidentSchema = z.object({
  id: z.string(),
  type: z.literal("incident"),
  self: z.string(),
  html_url: z.string(),
  incident_number: z.number(),
  title: z.string(),
  description: z.string().nullable(),
  status: z.string(), // triggered, acknowledged, resolved
  urgency: z.string(), // high, low
  created_at: z.string(),
  service: z.object({
    id: z.string(),
    type: z.string(),
    summary: z.string(),
  }),
  assignments: z.array(
    z.object({
      at: z.string(),
      assignee: z.object({
        id: z.string(),
        type: z.string(),
        summary: z.string(),
      }),
    }),
  ),
});

const CreateIncidentResponseSchema = z.object({
  incident: IncidentSchema,
});

export default api({
  integrations: {
    pagerduty: pagerDuty(PROD_PAGERDUTY),
  },
  name: "PagerDutyExample",
  input: z.object({
    title: z.string(),
    serviceId: z.string(),
    urgency: z.enum(["high", "low"]),
  }),
  output: z.object({
    incidentId: z.string(),
    incidentNumber: z.number(),
  }),
  async run(ctx, { title, serviceId, urgency }) {
    const result = await ctx.integrations.pagerduty.apiRequest(
      {
        method: "POST",
        path: "/incidents",
        body: {
          incident: {
            type: "incident",
            title: title,
            service: {
              id: serviceId,
              type: "service_reference",
            },
            urgency: urgency,
            body: {
              type: "incident_body",
              details: "Created via API",
            },
          },
        },
      },
      { response: CreateIncidentResponseSchema },
    );

    return {
      incidentId: result.incident.id,
      incidentNumber: result.incident.incident_number,
    };
  },
});
```

### List Incidents

```typescript
const ListIncidentsResponseSchema = z.object({
  incidents: z.array(IncidentSchema),
  limit: z.number(),
  offset: z.number(),
  total: z.number().nullable(),
  more: z.boolean(),
});

const result = await ctx.integrations.pagerduty.apiRequest(
  {
    method: "GET",
    path: "/incidents",
    params: {
      "statuses[]": "triggered,acknowledged",
      urgencies: "high",
      limit: 25,
      sort_by: "created_at:desc",
    },
  },
  { response: ListIncidentsResponseSchema },
);

result.incidents.forEach((incident) => {
  console.log(
    `#${incident.incident_number}: ${incident.title} (${incident.status})`,
  );
});
```

### Get an Incident

```typescript
const result = await ctx.integrations.pagerduty.apiRequest(
  {
    method: "GET",
    path: `/incidents/${incidentId}`,
  },
  { response: z.object({ incident: IncidentSchema }) },
);
```

### Update Incident Status

```typescript
await ctx.integrations.pagerduty.apiRequest(
  {
    method: "PUT",
    path: `/incidents/${incidentId}`,
    body: {
      incident: {
        type: "incident_reference",
        status: "acknowledged", // or "resolved"
      },
    },
  },
  { response: z.object({ incident: IncidentSchema }) },
);
```

### Add a Note to Incident

```typescript
const NoteSchema = z.object({
  id: z.string(),
  content: z.string(),
  created_at: z.string(),
  user: z.object({
    id: z.string(),
    summary: z.string(),
  }),
});

const result = await ctx.integrations.pagerduty.apiRequest(
  {
    method: "POST",
    path: `/incidents/${incidentId}/notes`,
    body: {
      note: {
        content: "Investigating the root cause",
      },
    },
  },
  { response: z.object({ note: NoteSchema }) },
);
```

### List Services

```typescript
const ServiceSchema = z.object({
  id: z.string(),
  type: z.literal("service"),
  self: z.string(),
  html_url: z.string(),
  name: z.string(),
  description: z.string().nullable(),
  status: z.string(), // active, warning, critical, maintenance, disabled
  created_at: z.string(),
});

const ListServicesResponseSchema = z.object({
  services: z.array(ServiceSchema),
  limit: z.number(),
  offset: z.number(),
  more: z.boolean(),
});

const result = await ctx.integrations.pagerduty.apiRequest(
  {
    method: "GET",
    path: "/services",
    params: {
      limit: 50,
      sort_by: "name",
    },
  },
  { response: ListServicesResponseSchema },
);
```

### Get On-Call Users

```typescript
const OnCallSchema = z.object({
  escalation_policy: z.object({
    id: z.string(),
    summary: z.string(),
  }),
  escalation_level: z.number(),
  schedule: z
    .object({
      id: z.string(),
      summary: z.string(),
    })
    .nullable(),
  user: z.object({
    id: z.string(),
    summary: z.string(),
    email: z.string(),
  }),
  start: z.string(),
  end: z.string(),
});

const ListOnCallsResponseSchema = z.object({
  oncalls: z.array(OnCallSchema),
});

const result = await ctx.integrations.pagerduty.apiRequest(
  {
    method: "GET",
    path: "/oncalls",
    params: {
      "escalation_policy_ids[]": policyId,
      include: "users",
    },
  },
  { response: ListOnCallsResponseSchema },
);

result.oncalls.forEach((oncall) => {
  console.log(`${oncall.user.summary} is on-call until ${oncall.end}`);
});
```

### Trigger Event via Events API

```typescript
const EventResponseSchema = z.object({
  status: z.string(),
  message: z.string(),
  dedup_key: z.string(),
});

// Note: Events API uses different base URL
const result = await ctx.integrations.pagerduty.apiRequest(
  {
    method: "POST",
    path: "/v2/enqueue",
    body: {
      routing_key: "your-integration-key",
      event_action: "trigger",
      dedup_key: "unique-incident-key",
      payload: {
        summary: "Server CPU above 90%",
        severity: "critical",
        source: "monitoring-system",
        timestamp: new Date().toISOString(),
        custom_details: {
          cpu_usage: 95,
          server: "web-01",
        },
      },
    },
  },
  { response: EventResponseSchema },
);
```

### Resolve via Events API

```typescript
await ctx.integrations.pagerduty.apiRequest(
  {
    method: "POST",
    path: "/v2/enqueue",
    body: {
      routing_key: "your-integration-key",
      event_action: "resolve",
      dedup_key: "unique-incident-key", // Same as trigger
    },
  },
  { response: EventResponseSchema },
);
```

## Trace Metadata

All methods accept an optional `metadata` parameter as the last argument for diagnostics labeling. See the [root SDK README](../../../README.md#trace-metadata) for details.

## Common Pitfalls

### No Specialized Methods

```typescript
// WRONG - These methods do not exist
await pagerduty.createIncident({ ... });
await pagerduty.listServices();

// CORRECT - Use apiRequest
await ctx.integrations.pagerduty.apiRequest(
  { method: "POST", path: "/incidents", body: { incident: { ... } } },
  { response: CreateIncidentResponseSchema }
);
```

### Wrap Objects in Parent Key

PagerDuty wraps objects:

```typescript
// WRONG
const body = {
  title: "Incident title",
  service: { ... },
};

// CORRECT
const body = {
  incident: {
    type: "incident",
    title: "Incident title",
    service: { ... },
  },
};
```

### Type References

Use `_reference` types for associations:

```typescript
const body = {
  incident: {
    type: "incident",
    title: "Server down",
    service: {
      id: "P123ABC",
      type: "service_reference", // Not "service"
    },
    escalation_policy: {
      id: "P456DEF",
      type: "escalation_policy_reference",
    },
  },
};
```

### Array Parameters

Array parameters need special syntax:

```typescript
// WRONG
params: { statuses: ["triggered", "acknowledged"] }

// CORRECT - Use [] suffix
params: { "statuses[]": "triggered,acknowledged" }

// Or multiple params
params: { "statuses[]": ["triggered", "acknowledged"] }
```

### Two APIs: REST and Events

PagerDuty has two different APIs:

```typescript
// REST API - For managing incidents, services, etc.
// Base URL: https://api.pagerduty.com
const path = "/incidents";

// Events API - For triggering/resolving from monitoring
// Base URL: https://events.pagerduty.com
const path = "/v2/enqueue";
```

### From Header Required

Some endpoints require a `From` header with user email:

```typescript
const result = await ctx.integrations.pagerduty.apiRequest(
  {
    method: "POST",
    path: "/incidents",
    body: { incident: { ... } },
    headers: {
      From: "user@example.com",  // Required for creating incidents
    },
  },
  { response: CreateIncidentResponseSchema }
);
```

### Pagination

PagerDuty uses offset-based pagination:

```typescript
async function getAllIncidents(pagerduty: PagerDutyClient) {
  const allIncidents: Incident[] = [];
  let offset = 0;
  const limit = 100;

  while (true) {
    const result = await ctx.integrations.pagerduty.apiRequest(
      {
        method: "GET",
        path: "/incidents",
        params: { limit, offset },
      },
      { response: ListIncidentsResponseSchema },
    );

    allIncidents.push(...result.incidents);
    if (!result.more) break;
    offset += limit;
  }

  return allIncidents;
}
```

## Error Handling

```typescript
import { RestApiValidationError } from "@superblocksteam/sdk-api";

try {
  const result = await ctx.integrations.pagerduty.apiRequest(
    { method: "POST", path: "/incidents", body: { ... } },
    { response: CreateIncidentResponseSchema }
  );
} catch (error) {
  if (error instanceof RestApiValidationError) {
    console.error("Validation failed:", error.details.zodError);
  }
}
```

## API Reference

- [PagerDuty REST API](https://developer.pagerduty.com/api-reference/)
- [Incidents](https://developer.pagerduty.com/api-reference/af59f6f7f6230-list-incidents)
- [Services](https://developer.pagerduty.com/api-reference/e960cca205c0f-list-services)
- [Events API v2](https://developer.pagerduty.com/api-reference/b3A6Mjc0ODI1Nw-send-an-event-to-pager-duty)
