Skip to content

Relay Rules

Relay rules define how alerts are processed and routed. Rules execute in order based on their order property.

The group field controls whether rules run simultaneously or sequentially:

  • Same group = sequential (order 1, then 2, then 3)
  • Different groups = parallel (run at the same time)

See Best Practices - Parallel vs Sequential for examples.

Set externalKey on relay rules to make configuration idempotent and safe to re-run. See Best Practices.

Notify team members based on a schedule:

const rule = await client.relay.rules.create(relayId, {
name: "Notify on-call engineer",
ruleType: "schedule_notify",
order: 1,
config: {
scheduleId: "sch_xyz123",
},
enabled: true,
});

Call an external webhook:

const rule = await client.relay.rules.create(relayId, {
name: "Post to Slack",
ruleType: "webhook",
order: 2,
config: {
endpoint: "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
method: "POST",
headers: {
"Content-Type": "application/json",
},
payload: {
text: "Alert received",
severity: "high",
},
timeout: 10000, // Optional: timeout in ms (default 30000)
},
});

Invoke an AI agent to handle the alert:

const rule = await client.relay.rules.create(relayId, {
name: "Invoke Devin agent",
ruleType: "agent",
order: 1,
config: {
agentType: "devin", // or "cursor"
integrationId: "int_abc123", // References stored credentials
endpoint: "https://api.devin.ai/v3/organizations/sessions", // Optional override
config: {
// Agent-specific configuration
priority: "high",
},
pollInterval: 30000, // Check status every 30s (default)
maxPollAttempts: 120, // Give up after 120 attempts (default)
},
});

Cursor requires a repository URL and can auto-create PRs:

const cursorRule = await client.relay.rules.create(relayId, {
name: "Cursor Auto-Fix",
ruleType: "agent",
order: 1,
config: {
agentType: "cursor",
integrationId: "int_cursor123",
repository: "https://github.com/org/repo", // Required for Cursor
autoCreatePr: true, // Default: true
autoBranch: true, // Let Cursor auto-name the branch
pollInterval: 30000,
maxPollAttempts: 120,
},
});
// List all rules
const allRules = await client.relay.rules.list(relayId);
// Filter by enabled status
const enabledRules = await client.relay.rules.list(relayId, {
enabled: true,
});
// Filter by rule type
const webhookRules = await client.relay.rules.list(relayId, {
ruleType: "webhook",
});
const rule = await client.relay.rules.get(relayId, ruleId);
console.log(`Rule: ${rule.name}, Order: ${rule.order}`);
const updatedRule = await client.relay.rules.update(relayId, ruleId, {
name: "Updated rule name",
enabled: false,
config: {
// Updated config
},
});
await client.relay.rules.delete(relayId, ruleId);
console.log("Rule deleted successfully");
const reorderedRules = await client.relay.rules.reorder(relayId, {
rules: [
{ id: "rul_first", order: 1 },
{ id: "rul_second", order: 2 },
{ id: "rul_third", order: 3 },
],
});
reorderedRules.forEach((rule) => {
console.log(`${rule.order}: ${rule.name}`);
});

Organize related rules using the group property:

await client.relay.rules.create(relayId, {
name: "Primary notification",
ruleType: "schedule_notify",
group: "primary",
order: 1,
config: { scheduleId: "sch_primary" },
});
await client.relay.rules.create(relayId, {
name: "Backup notification",
ruleType: "schedule_notify",
group: "backup",
order: 2,
config: { scheduleId: "sch_backup" },
});
type CreateRelayRuleInput = {
externalKey?: string;
name: string;
ruleType: RelayRuleType;
group?: string;
order?: number;
config: RelayRuleConfig;
enabled?: boolean;
};
type RelayRule = {
id: string;
organizationId: string;
relayId: string;
group: string;
externalKey: string | null;
name: string;
order: number;
ruleType: RelayRuleType;
config: Record<string, unknown>;
enabled: boolean;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
};

See Best Practices for relay rule configuration guidelines.