# Publishing API v2 migration guide

Status: public beta. Scoped API keys may use the v2 lifecycle routes in production. Existing integrations may continue using `POST /api/v1/convert` and `/openapi.json` without changes.

## Compatibility

API v2 is additive. No v1 removal is scheduled. Before any v1 removal, the service will observe v2 for at least 180 days, publish a 90-day deprecation notice, and verify that active clients have migrated. Existing v1 request bodies, PDF responses, status codes, and accounting remain supported.

## Resource model

1. Create a managed document and first immutable revision with `POST /api/v2/documents`.
2. Add later immutable revisions under `/api/v2/documents/{documentId}/revisions`.
3. Request a free, short-lived preview under `/previews`, or a final publication under `/publishes`.
4. Treat `200` as completed within the synchronous threshold and `202` as accepted work. Poll the returned job link until it reaches `succeeded`, `failed`, or `cancelled`.
5. Mint a private artifact link or use the authorized download redirect.
6. Delete the managed document to cancel active work and schedule object-first cleanup for sources and artifacts.

## Authentication and scopes

Use an environment-bound bearer API key. Each operation declares its exact `x-required-scopes` value in `/openapi-v2.json`:

- `documents.preview`
- `documents.render`
- `documents.read`
- `documents.delete`
- `templates.read`
- `webhooks.read`
- `webhooks.write`

Create a scoped key in Dashboard → API Keys. A normal publishing integration needs only `documents.render` and `documents.read`; use separate keys for source deletion, previews, or webhook administration.

## Idempotency

Send an opaque `Idempotency-Key` of 8 to 200 characters for document creation, revision creation, preview, final publish, and cancellation. The identity is scoped to the authenticated principal and operation.

- Repeat the same logical request with the same key and byte-equivalent canonical body.
- A replay returns the original resource identities and `Idempotency-Replayed: true`.
- Reusing a key with a different canonical request returns typed `409 idempotency_conflict`.
- `GET` is naturally safe. Document `DELETE` is resource-idempotent. Artifact-link creation is uncharged and non-persisting, so a retry may return a different short-lived URL.

Webhook creation and secret rotation also require `Idempotency-Key`. Replaying the identical operation returns the original secret instead of creating another version.

## Signed webhooks

Create a public HTTPS subscription with `POST /api/v2/webhooks`. Subscribe to `job.succeeded`, `job.failed`, and/or `job.cancelled`. The create response returns a `whsec_` signing secret; list responses expose only its fingerprint.

Verify `X-MD2PDF-Signature` against the exact raw body as `HMAC-SHA256(secret, "<timestamp>.<raw-body>")`. Reject timestamps more than five minutes from local time. Deduplicate `X-MD2PDF-Delivery` before applying side effects because delivery is at least once.

Retryable endpoint responses are `408`, `409`, `425`, `429`, and `5xx`. Delivery uses bounded exponential backoff for at most 12 attempts or 24 hours. Rendering, artifact finalization, and charging never wait for or roll back because of a webhook endpoint.

Rotate a secret with `POST /api/v2/webhooks/{webhookId}/rotate-secret`. Existing delivery records remain pinned to their original secret version. `DELETE /api/v2/webhooks/{webhookId}` permanently disables the subscription and cancels undelivered work.

## Retention and charging

Every content-bearing response carries a `retention` object and explicit `expiresAt`.

- Managed encrypted source and revisions: 30 days after document activity, unless deleted first.
- Preview artifact: 15 minutes. Preview does not consume trial, subscription, API-call, or credit-pack publication units.
- Final artifact: 7 days after finalization.
- Job and typed failure detail: 30 days.
- Download link: at most 5 minutes and never persisted.

A final unit is reserved at admission and commits only after a final artifact is successfully finalized. Failure or cancellation releases the reservation.

## Errors and retries

All JSON errors use `publishing.lifecycle.v1` and include a stable category, code, HTTP status, safe message, and `retryable` flag. They never include Markdown, private URLs, credentials, or cross-tenant resource details.

- Retry transport failures, `429`, and `503` only when the response or operation contract permits it.
- Honor `Retry-After` and use exponential backoff with full jitter.
- Reuse the identical `Idempotency-Key` and request body for retried mutations.
- Do not retry validation, authentication, authorization, quota, not-found, expired-artifact, or idempotency-conflict errors without changing the underlying condition.

## Client generation

Generate a client from `https://markdowntopdfconverter.com/openapi-v2.json`. Keep beta usage behind your own integration flag until general availability. The repository verifies the contract with an independent OpenAPI parser and compiles every operation through an `openapi-typescript` plus `openapi-fetch` fixture.

## Reviewed automation paths

- n8n: `https://markdowntopdfconverter.com/downloads/integrations/n8n`
- Make: `https://markdowntopdfconverter.com/downloads/integrations/make`
- Zapier: `https://markdowntopdfconverter.com/downloads/integrations/zapier`

These downloads contain no credentials. Bind a dedicated platform credential after import, and never forward it to the short-lived signed artifact URL.
