API reference
Webhook Events
Tasks
A POST with a JSON body for each event a webhook subscribes to, narrowed by its spaces and query. A delivery is tried up to three times; a 4xx other than 429 isn't retried.
Headers
x-issues-eventThe event, such as
issue.created.x-issues-deliveryThe delivery's id, the same on each retry, so a delivery you've had can be skipped.
x-issues-signaturet=<unix seconds>,v1=<hex>: an HMAC-SHA256 of<t>.<raw body>keyed with the webhook's secret.
Check the signature
import { createHmac, timingSafeEqual } from "node:crypto"
// body: the raw request body as it arrived; header: x-issues-signature
export function verify(body, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")))
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false
const expected = createHmac("sha256", secret).update(`${parts.t}.${body}`).digest("hex")
return expected.length === parts.v1?.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
}Body
{
id: string
event: "issue.created" | "issue.updated" | "issue.transitioned" | "issue.deleted" | "issue.moved" | "issue.sla_breached" | "comment.created" | "comment.updated" | "comment.deleted" | "worklog.created" | "attachment.created" | "link.created" | "link.deleted" | "sprint.started" | "sprint.completed" | "version.released"
at: string
actor: {
type: "user" | "key" | "system" | "automation"
id: string
label?: string
}
issue: null | {
id: string
key: string
summary: string
type?: string
status?: string
statusCategory: "done" | "todo" | "inprogress"
priority?: string
resolution?: string
assignee: null | {
id: string
email?: string
name?: string
}
reporter: null | {
id: string
email?: string
name?: string
}
labels: string[]
dueDate?: string
storyPoints?: number
fields: {
[key: string]: boolean | null | string | number | string[] | {
parent: string
child?: string
}
}
created: string
updated: string
}
changes?: {
field: string
from?: unknown
to?: unknown
fromText?: string
toText?: string
}[]
transition?: {
id: string
name: string
from: string
to: string
}
commentId?: string
sprintId?: string
versionId?: string
from?: string
}issue.createdAn issue was created.
issue.updatedAn issue's fields changed:
changeslists each field with its value before and after.issue.transitionedAn issue moved through its workflow:
transitionnames the transition and the statuses before and after.issue.deletedAn issue was deleted:
issueis how it was.issue.movedAn issue moved to another space and got a new key:
fromis the key it had.issue.sla_breachedAn issue missed one of its space's SLAs.
comment.createdA comment was added:
commentIdnames it.comment.updatedA comment was edited.
comment.deletedA comment was deleted.
worklog.createdTime was logged on an issue.
attachment.createdA file was attached to an issue.
link.createdTwo issues were linked.
link.deletedA link between two issues was removed.
sprint.startedA sprint started:
sprintIdnames it, andissueis null.sprint.completedA sprint was completed:
sprintIdnames it, andissueis null.version.releasedA version was released:
versionIdnames it, andissueis null.
Mirage apps
Events an app turns on are POSTed to its interactions endpoint as type 0, signed like interactions: x-signature-ed25519 over x-signature-timestamp and the body, with the app's public key.
Body
{
id: string
applicationId: string
type: 0
version: 1
timestamp: string
event:
| { type: "message_mention"; serverId: string; channelId: string; message: Message }
| { type: "member_join"; serverId: string; member: { userId: string; name: string; joinedAt: number } }
}message_mentionA message mentions the app's bot or replies to one of its messages, in a channel the bot can read. Messages from bots never send it.
member_joinSomeone joined a server the app's bot is in.