> ## Documentation Index
> Fetch the complete documentation index at: https://docs.publica.la/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating from `create-article`

The legacy `POST /integration-api/v1/create-article` endpoint is **deprecated and will be sunset**. The Add-ons API v3 replaces it with a complete resource: listing, creation (single and bulk), correction, deletion and a review lifecycle.

## At-a-Glance Comparison

| Feature               | v1 `create-article`                       | Add-ons API v3                                              |
| --------------------- | ----------------------------------------- | ----------------------------------------------------------- |
| **Base path**         | `POST /integration-api/v1/create-article` | `/api/v3/content/{content}/add-ons`                         |
| **Authentication**    | `X-User-Token` header                     | `X-User-Token` header (unchanged)                           |
| **Operations**        | Create only                               | List, create, bulk create, update, delete                   |
| **Product ref**       | `issue_id` in the body                    | `{content}` in the path (internal or external id)           |
| **Review lifecycle**  | None. Always visible immediately          | `draft` (default) / `published`                             |
| **Provenance**        | Not recorded                              | Stamped internally from the creation channel                |
| **Body sanitization** | Stored as sent                            | Sanitized to a fixed HTML subset; removals reported         |
| **Bulk**              | Sequential requests                       | Dedicated `bulk` endpoint, up to 100 items (without images) |
| **Marker position**   | Accepted without validation               | Validated `{x: 0-100, y: 0-100}`; malformed input rejected  |
| **Image input**       | `image_from_url`, unvalidated download    | `image_from_url`, validated (JPEG/PNG/GIF/WebP, max 10 MB)  |
| **Ownership check**   | None                                      | Only the content owner manages add-ons                      |
| **Response**          | `{ "CODE": "success", "data": {…} }`      | Standard v3 envelope `{ "data": {…} }`                      |

---

## Breaking Changes

### 1. The product moves from body to path

```diff
- POST /integration-api/v1/create-article
-   { "issue_id": 123, "title": "…", … }
+ POST /api/v3/content/123/add-ons
+   { "title": "…", … }
```

External identifiers work in the path with `?id_type=external`.

### 2. New add-ons default to draft

`create-article` made every article visible to readers instantly. The v3 resource creates **drafts** by default. To keep the old publish-immediately behavior, pass:

```json
{ "status": "published" }
```

### 3. Marker positions are validated

`location_in_start_page` was stored as-is, and malformed values put the marker at the page border instead of where intended. v3 rejects anything that is not `{ "x": 0-100, "y": 0-100 }` with `422`.

### 4. Images are validated on download

URLs that do not point to a real JPEG, PNG, GIF or WebP under 10 MB are rejected with `422` instead of failing later in the image pipeline.

### 5. Response envelope

```diff
- { "CODE": "success", "data": { … } }
+ { "data": { … } }
```

The v3 add-on object exposes new fields (`status`, `content_id`) and ISO 8601 timestamps. See [the add-on object](./add-ons-v3-overview.md#the-add-on-object).

### 6. PDF and EPUB products only

`create-article` accepted any `issue_id`. v3 creates add-ons on PDF and EPUB products that are numbered (a PDF by its finished conversion, an EPUB by its page markers), because every position is validated against the product's page count (`422` otherwise, with the reason under `errors.content`). An EPUB is addressed with an [anchor](./add-ons-v3-overview.md#anchoring); `create-article` has no way to send one, so it stays a PDF-only path.

### 7. Feature flag and administrator token

The v3 resource requires the **Add-ons API** feature enabled for your store and a token belonging to an **administrator** user (`403` otherwise). The feature gates only this API; managing add-ons manually from the dashboard never requires it. Contact support before migrating.

### 8. Bodies are sanitized

`create-article` stores `body` as sent. v3 always runs it through the [rich text sanitizer](./add-ons-v3-overview.md#rich-text-bodies): event handlers, inline styles, scripts, tables and unknown tags are removed, headings are flattened to paragraphs, and the response lists every removal under `sanitization.discarded`. If your articles rely on `<h2>`–`<h4>` structure, expect it to come back as paragraphs.

---

## Field Mapping

| v1 field                  | v3 field                  | Notes                                                                                                                                      |
| ------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `issue_id`                | `{content}` path segment  | Or an external identifier with `id_type=external`                                                                                          |
| `title`                   | `title`                   | Unchanged                                                                                                                                  |
| `pretitle`                | `pretitle`                | Unchanged                                                                                                                                  |
| `body`                    | `body`                    | Same length limit; now sanitized on save                                                                                                   |
| `author`                  | `author`                  | Unchanged                                                                                                                                  |
| `by_line`                 | `by_line`                 | Now validated (max 1,000)                                                                                                                  |
| `type`                    | `type`                    | Same values, defaults to `article`                                                                                                         |
| `start_page` / `end_page` | `start_page` / `end_page` | Unchanged semantics                                                                                                                        |
| `location_in_start_page`  | `location_in_start_page`  | Now validated                                                                                                                              |
| (none)                    | `anchor`                  | New. Derived from the pair above when you send the legacy fields, so a PDF migration needs no change; required to place a piece in an EPUB |
| `image_from_url`          | `image_from_url`          | Now validated                                                                                                                              |
| `image_caption`           | `image_caption`           | Now validated (max 2,000)                                                                                                                  |
| (none)                    | `status`                  | New: `draft` (default) or `published`                                                                                                      |
| (none)                    | `source`                  | Not part of the contract: provenance is stamped internally from the creation channel                                                       |
| (none)                    | `body_format`             | New, read-only: `html` once a body went through the sanitizer                                                                              |

## Side-by-Side Example

**v1:**

```bash
curl -X POST "https://yourstore.publica.la/integration-api/v1/create-article" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "issue_id": 123,
    "title": "Interview with the author",
    "body": "<p>…</p>",
    "start_page": 12
  }'
```

**v3:**

```bash
curl -X POST "https://yourstore.publica.la/api/v3/content/123/add-ons" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Interview with the author",
    "body": "<p>…</p>",
    "start_page": 12,
    "status": "published"
  }'
```
