#323

Linear: webhook deliveries omit `Linear-Timestamp`, Issue payloads lack `state`, `updatedFrom` holds the whole issue

Openjcardonneopened 21d ago

Problem

Three differences between the Linear emulator's webhook deliveries and Linear's documented payloads make a standard receiver reject or misread them.

  1. No Linear-Timestamp header. dispatchLinearWebhook (packages/@emulators/linear/src/webhooks.ts) sends Linear-Delivery, Linear-Event and Linear-Signature, but not Linear-Timestamp. Linear documents that header ("The Unix timestamp, in milliseconds, when the webhook was sent"), and the official SDK still reads it (LINEAR_WEBHOOK_TS_HEADER = "linear-timestamp" in packages/sdk/src/webhooks/client.ts). A receiver whose replay guard reads the header refuses every emulated delivery. The body's webhookTimestamp is already present.
  2. Issue payloads have stateId but no state. issueWebhookPayload (routes/graphql.ts) returns stateId only. Linear's schema declares state: WorkflowStateChildWebhookPayload! (id, name, type, color) on IssueWebhookPayload (packages/sdk/src/schema.graphql in linear/linear), and receivers use state.type == "completed" to detect a closed issue.
  3. updatedFrom contains the whole previous issue. On issueUpdate the emulator passes updatedFrom: before, where before = issueWebhookPayload(context, issue). Linear documents updatedFrom as "an object containing the previous values of all updated properties". Every update therefore looks like a change to every field (for example a status change on a title edit).

Reproduced on published emulate 0.12.2. The code is unchanged on main at 4c0422e.

Reproduction

  1. Seed a team with workflow states, an issue, and a webhook with a secret pointing at a local receiver.
  2. issueUpdate(id, input: { stateId: <completed state> }).
  3. Inspect the delivery.

Actual: no Linear-Timestamp header, data.state absent, updatedFrom lists every issue field.

Expected (per https://linear.app/developers/webhooks): a Linear-Timestamp header equal to webhookTimestamp, data.state with id, name, type, color, and updatedFrom limited to the fields the mutation changed (here stateId and updatedAt).

Activity

0

No activity yet.