> ## 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.

# Content API v1 → v3 Migration Guide

This document compares Content API v1 and v3 and provides a practical migration guide for integrators. The v3 feed is designed for speed, bandwidth efficiency, and flexibility via cursor pagination and response shaping (fields and includes).

**Related Documentation:**

- [Content API v3 Overview](./content-v3-overview.md)

## Endpoint Mapping

### Content CRUD

| Operation   | v1 Endpoint                                        | v3 Endpoint                   |
| ----------- | -------------------------------------------------- | ----------------------------- |
| List        | `GET /integration-api/v1/dashboard/issues`         | `GET /api/v3/content`         |
| Get single  | `GET /integration-api/v1/dashboard/issues/{id}`    | `GET /api/v3/content/{id}`    |
| Create      | `POST /integration-api/v1/dashboard/issues`        | `POST /api/v3/content`        |
| Bulk create | N/A                                                | `POST /api/v3/content/bulk`   |
| Update      | `PUT /integration-api/v1/dashboard/issues/{id}`    | `PUT /api/v3/content/{id}`    |
| Delete      | `DELETE /integration-api/v1/dashboard/issues/{id}` | `DELETE /api/v3/content/{id}` |

### Audiobook Tracks

| Operation      | v1 Endpoint                                                       | v3 Endpoint                                  |
| -------------- | ----------------------------------------------------------------- | -------------------------------------------- |
| List tracks    | `GET /integration-api/v1/dashboard/issues/{id}/tracks`            | `GET /api/v3/content/{id}/tracks`            |
| Add track      | `POST /integration-api/v1/dashboard/issues/{id}/tracks`           | `POST /api/v3/content/{id}/tracks`           |
| Update track   | `PUT /integration-api/v1/dashboard/issues/{id}/tracks/{track}`    | `PUT /api/v3/content/{id}/tracks/{track}`    |
| Reorder tracks | `PUT /integration-api/v1/dashboard/issues/{id}/tracks-orders`     | `PUT /api/v3/content/{id}/tracks/order`      |
| Delete track   | `DELETE /integration-api/v1/dashboard/issues/{id}/tracks/{track}` | `DELETE /api/v3/content/{id}/tracks/{track}` |

---

## At-a-Glance Comparison

| Topic          | v1                                                         | v3                                                                                                     |
| -------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Base path      | `/integration-api/v1/dashboard/issues`                     | `/api/v3/content`                                                                                      |
| Auth           | API token in query string                                  | API token in `X-User-Token` header or request body (no query string)                                   |
| Pagination     | Page-based paginator (`page`, `last_page`, etc.)           | Cursor pagination with `links.next`/`links.prev` and `meta.has_more`                                   |
| Search/filter  | Overloaded `query` param (state, name, id)                 | Structured filters: `filter[query]`, `filter[external_id]`, `filter[created_at]`, `filter[updated_at]` |
| Date filters   | N/A                                                        | Strict ranges, 1-month span rules, format `YYYY-MM-DD HH:mm:ss`                                        |
| Sorting        | Not standardized                                           | `sort` on `created_at`/`updated_at`                                                                    |
| Shaping        | Not supported                                              | `include` blocks (`prices`, `description`, `metadata`) + `fields` selection                            |
| Prices shape   | Object keyed by currency (e.g., `{ "USD": 25 }`)           | Array of objects (`[{ "currency_id": "USD", "amount": 25 }]`)                                          |
| Timestamps     | Mixed structures (e.g., `published_at.timestamp` + `date`) | ISO 8601 strings (e.g., `published_at: "2019-09-13T00:00:00Z"`)                                        |
| Cover URL      | `cover_image_path`                                         | `cover_url`                                                                                            |
| Error handling | JSON with `CODE` and payload                               | Same `CODE` with improved message                                                                      |

## Breaking Changes for Integrators

1. Endpoint change: `GET /integration-api/v1/dashboard/issues` → `GET /api/v3/content`.
2. Pagination: switch from page-based to cursor-based.
   - No numeric `page` parameter.
   - Use `links.next` token and check `meta.has_more`.
3. Filters: the overloaded `query` parameter is deprecated in v3.
   - Use `filter[query]` for free-text search by title/name.
   - Use `filter[external_id]` for exact ISBN/external ID matches.
   - Use `filter[updated_at][from|to]` or `filter[created_at][from|to]` with strict 1-month span rules and `YYYY-MM-DD HH:mm:ss` format.
4. Sorting: v3 introduces `sort` with allowed fields `created_at`, `updated_at` (prefix with `-` for descending).
5. Response shaping:
   - Use `include=prices,description,metadata` to request optional blocks.
   - Use `fields=` to trim the returned attributes for bandwidth efficiency.
6. Schema differences:
   - `cover_image_path` (v1) → `cover_url` (v3).
   - `published_at` structure (v1 object) → ISO 8601 string (v3).
   - `prices` (v1 object by currency) → `prices` (v3 array of `{currency_id, amount}`).
   - `free` (v1 boolean) → `free` (v3 object: `{enabled, until, require_login}`).
   - `stock` (v1 boolean, physical content) → `stock_quantity` (v3 integer): `false` becomes `0`, `true` becomes the count you actually hold.
   - New optional blocks: `description`, `metadata` (with `publisher`, `author`, `bisac`, `keywords`, `country`, `edition`, `narrator`, `category`, `collection`, `metrics`), `geographic_restrictions`.
7. Validation: invalid includes or parameters return 422 with detailed error messages in v3.

## Response Differences

### v1 (example excerpt)

```json
{
  "CODE": "success",
  "data": {
    "paginator": {
      "current_page": 1,
      "data": [
        {
          "id": 468166,
          "external_id": "9781496822482",
          "name": "Conversations with Donald Hall",
          "cover_image_path": "https://.../cover.jpg",
          "free": false,
          "prices": { "USD": 25 }
        }
      ],
      "last_page": 6
    }
  }
}
```

### v3 (example excerpt)

```json
{
  "data": [
    {
      "id": "468166",
      "external_id": "9781496822482",
      "name": "Conversations with Donald Hall",
      "file_type": "epub",
      "cover_url": "https://.../468166.jpg",
      "published_at": "2019-09-13T00:00:00Z",
      "license": "retail",
      "free": {
        "enabled": false,
        "until": "2025-12-12T00:00:00Z",
        "require_login": false
      },
      "preview": { "enabled": true, "require_login": false }
    }
  ],
  "links": {
    "next": "https://.../content?cursor=abc123&per_page=100",
    "prev": null
  },
  "meta": { "has_more": true }
}
```

### Prices shape

```json
// v1
{"prices": {"USD": 9.99, "EUR": 8.50}}

// v3
{"prices": [{"currency_id": "USD", "amount": 9.99}, {"currency_id": "EUR", "amount": 8.50}]}
```

## Migration Checklist

1. Authentication
   - Keep using your store API token.
   - For v3, send it in the `X-User-Token` header over HTTPS.
2. Endpoints
   - Replace v1 listing endpoint with `GET /api/v3/content`.
3. Pagination
   - Implement cursor pagination.
   - Use `links.next` for the next request and `meta.has_more` to detect completion.
4. Filters
   - Replace `query` with `filter[query]`.
   - Use `filter[external_id]` for ISBN/external ID lookups.
   - Add date-windowed sync via `filter[updated_at]` or `filter[created_at]` with the 1‑month rules.
5. Sorting
   - Add `sort` when you need ordering other than the default (`published_at` desc).
6. Response shaping
   - Add `include=` for optional blocks (`prices`, `description`, `metadata`).
   - Trim with `fields=` to reduce payloads.
7. Schema updates
   - Map `cover_image_path` → `cover_url`.
   - Map `prices` object → `prices` array.
   - Update timestamp parsing to ISO 8601 strings.
   - Handle `free` and `preview` objects.
8. Error handling
   - Handle 422 validation errors (e.g., invalid includes, date formats, ranges) and 401 for auth.
9. Rollout
   - Run v1 and v3 in parallel behind a feature flag.
   - Monitor logs and payload sizes; switch traffic when stable.

## Practical Examples

### Minimal listing with search (v3)

```http
GET /api/v3/content?filter[query]=harry&per_page=100 HTTP/1.1
Host: {store_final_domain}
X-User-Token: sk_live_xxx
```

### Incremental sync by update date (v3)

```http
GET /api/v3/content?filter[updated_at][from]=2024-12-25 00:00:00&filter[updated_at][to]=2024-12-31 23:59:59&sort=-updated_at HTTP/1.1
Host: {store_final_domain}
X-User-Token: sk_live_xxx
```

### Shape response with fields and includes (v3)

```http
GET /api/v3/content?include=prices,description&fields=id,name,description,prices&per_page=100 HTTP/1.1
Host: {store_final_domain}
X-User-Token: sk_live_xxx
```

### Fetch by ISBN/external_id (v3)

```http
GET /api/v3/content?filter[external_id]=9781496822482 HTTP/1.1
Host: {store_final_domain}
X-User-Token: sk_live_xxx
```

## Troubleshooting FAQ

**How do I request a single item in v3?**
Use `GET /api/v3/content/{id}` for direct lookup. Supports both internal and external IDs via the `id_type` parameter.

**What's the difference between filter[external_id] and id_type=external?**

- `filter[external_id]` on list endpoint returns items matching that ID
- `id_type=external` on single-item endpoints (`GET`, `PUT`, `DELETE`) allows using external ID in the path

**What page size should I use?**
v3 supports `per_page` from 1 to 500 (default 100). Use higher values for faster syncs when appropriate.

**Why am I getting a 422 error?**
Check date formats/ranges, allowed includes (`prices`, `description`, `metadata`, `geographic_restrictions`), and `per_page` bounds.

**Do I have to include metadata?**
No. Only request `include=metadata` when you need it. Trim with `fields` to keep responses small.

**How do I migrate bulk imports to v3?**
Use `POST /api/v3/content/bulk` with up to 100 items per request. The response includes partial success handling with detailed errors for any failed items.

**Can I still use the old v1 endpoints?**
Yes, v1 endpoints remain available during the migration period. We recommend starting with v3 for new integrations.

**What about audiobook track endpoints?**
Track endpoints have moved from `/issues/{id}/tracks-orders` to `/content/{id}/tracks/order`. The structure is otherwise similar.

---

## See Also

- [Content API v3 Overview](./content-v3-overview.md)
- [List Content](./content-v3-list.md)
- [Get Content](./content-v3-get.md)
- [Create Content](./content-v3-create.md)
- [Bulk Operations](./content-v3-bulk.md)
- [Update Content](./content-v3-update.md)
- [Delete Content](./content-v3-delete.md)
- [Audiobook Tracks](./content-v3-tracks.md)

---
