Step-by-step migration from API v1 to v2 - pagination, error shape, webhook signatures, and the cutover checklist.
v1 reaches end-of-life on 2027-03-31. This guide walks the migration in order of effort - most integrations complete it in a day. The five real differences:
| Area | v1 | v2 |
|---|---|---|
| Pagination | page/per_page + total | cursor/limit + has_more |
| Errors | { "error": "string" } | Structured error object + code |
| Webhook signature | X-Meridian-Signature (HMAC of body) | Meridian-Signature (timestamped) |
| Environments | ?env= query param | Determined by the key itself |
| Updates | PUT (full replace) | PATCH (partial) |
/v1 → /v2. If you use an SDK, upgrade to the current major version instead - it handles everything below.
curl "https://api.meridian.dev/v1/projects?env=test"curl "https://api.meridian.dev/v2/projects"v1 loops over page/per_page; v2 walks a cursor. If you paginate manually, port to this pattern:
let cursor;
do {
const page = await client.projects.listPage({ limit: 100, cursor });
process(page.data);
cursor = page.nextCursor; // null when done
} while (cursor);total is gone - if you displayed counts, fetch once to count or drop it.
v1 gave you a string; v2 gives { error: { type, code, message, request_id } }. Branch on code, never on the message text.
const code = typeof body.error === "object"
? body.error.code // v2
: `v1_http_${status}`; // v1 fallbackThe signature scheme gained a timestamp (replay protection) and moved headers:
X-Meridian-Signature → Meridian-Signaturebody → HMAC over timestamp.bodyUse the SDK verifier (verifyWebhookSignature) rather than porting your own - see Webhooks.
?env=test is gone - the key chooses the environment (mrnd_test_ vs mrnd_live_). Swap PUT calls to PATCH and send only changed fields.
v1 and v2 resources are separate namespaces: a prj_ created via v1 doesn't appear in v2 lists, and vice versa. Plan accordingly:
/v2 paths immediately.