---
date: 2026-02-27
version: "v2.4.0"
title: "Breaking: Webhook payload shape changed"
tags: ["Breaking"]
summary: "The spec.created webhook event now wraps the payload under a data key. Update your handler before Mar 14 to avoid disruption."
draft: false
---

We have changed the shape of the `spec.created` webhook payload. **If you use our webhooks, you must update your handler before March 14, 2026.** After that date, the old flat payload format will no longer be sent.

## What changed

**Before (v2.3.x and earlier):**

```json
{
  "event": "spec.created",
  "specId": "sp_01HZ7QX2Y",
  "title": "Homepage redesign",
  "createdAt": "2026-02-27T14:22:00Z"
}
```

**After (v2.4.0+):**

```json
{
  "event": "spec.created",
  "data": {
    "specId": "sp_01HZ7QX2Y",
    "title": "Homepage redesign",
    "createdAt": "2026-02-27T14:22:00Z"
  }
}
```

## Migration

Update your webhook handler to read from `payload.data` instead of the top-level payload object. The `event` field remains at the top level for quick routing.

Most handlers look like this after the change:

```javascript
app.post('/webhooks/inkframe', (req, res) => {
  const { event, data } = req.body;
  if (event === 'spec.created') {
    // Use data.specId, data.title, etc.
    console.log('New spec:', data.title);
  }
  res.sendStatus(200);
});
```

## Why we made this change

The old flat payload made it impossible to add top-level metadata (like signatures, retry counts, and API version) without colliding with event-specific fields. The `data` wrapper is the industry-standard pattern used by Stripe, GitHub, and Resend — it future-proofs the payload shape for all event types.

Full migration guide: see our [Webhooks documentation](#).
