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

# Update Content

Modify existing content items. Update metadata, pricing, access settings, files, or any other content attributes.

---

## Endpoint

```
PUT /api/v3/content/{content_id}
```

---

## Path Parameters

| Parameter    | Type   | Description                        |
| ------------ | ------ | ---------------------------------- |
| `content_id` | string | Content internal ID or external ID |

---

## Query Parameters

| Parameter | Type   | Default    | Description                                                                     |
| --------- | ------ | ---------- | ------------------------------------------------------------------------------- |
| `id_type` | string | `internal` | `internal` for numeric ID, `external` for external ID                           |
| `include` | string | -          | Comma-separated: `prices`, `description`, `metadata`, `geographic_restrictions` |
| `fields`  | string | -          | Comma-separated list of fields to return                                        |

---

## Request Body

All fields are optional. Only include fields you want to update. Omitted fields are left unchanged.

:::warning Array Replacement Behavior
All array and object fields use **full replacement** semantics. When you include an array field in the request, it **replaces all existing values** — including when you send an empty array `[]`, which **removes all existing values** for that field.

For example, sending `"keywords": []` will clear all keywords from the content item. Only include array fields you intend to update.
:::

### Core Fields

| Field               | Type    | Description                             |
| ------------------- | ------- | --------------------------------------- |
| `name`              | string  | Content title (max 255 chars)           |
| `description`       | string  | Full content description (HTML allowed) |
| `published_at`      | string  | Publication date (YYYY-MM-DD only)      |
| `lang`              | string  | Language code                           |
| `audience`          | string  | Target audience                         |
| `subtitle`          | string  | Content subtitle (max 255 chars)        |
| `publication_place` | string  | Place of publication (max 255 chars)    |
| `edition_year`      | integer | Edition year (4 digits, e.g., `2025`)   |

### File Update

| Field       | Type   | Description                                                           |
| ----------- | ------ | --------------------------------------------------------------------- |
| `file`      | string | Storage path to new content file (mutually exclusive with `file_url`) |
| `file_url`  | string | External URL for new content file (mutually exclusive with `file`)    |
| `cover`     | string | Storage path to new cover image (mutually exclusive with `cover_url`) |
| `cover_url` | string | External URL for new cover (mutually exclusive with `cover`)          |

:::tip File Upload Options

- **`file_url` / `cover_url`**: Provide a publicly accessible URL. The platform will download the file automatically.
- **`file` / `cover`**: Provide a storage path from SFTP upload. Contact support to enable SFTP access for your account.
  :::

:::note File Replacement Behavior
When you update a content file, the platform creates a **new version** of the file. Previous versions are retained internally. The content will be re-processed after the file update, and the `conversion_status` will change to reflect the processing state. Only **PDF** and **EPUB** content types support file updates. For audiobooks, use the [Audiobook Tracks API](./content-v3-tracks.md) to manage audio files.
:::

:::warning File Type Constraint
The replacement file must match the original content type. For example, you cannot replace a PDF with an EPUB. To change the content type, delete and recreate the content item.
:::

### Pricing

| Field    | Type  | Description                                |
| -------- | ----- | ------------------------------------------ |
| `prices` | array | Complete price array (replaces all prices) |

:::note Price Replacement
The `prices` array replaces all existing prices. Include all currencies you want to keep. Sending an empty array `[]` removes all prices.
:::

:::tip Scheduled Pricing
Prices support `starts_at` and `ends_at` fields for scheduled pricing: `[{currency_id, amount, starts_at, ends_at}]`. Dates should be in `YYYY-MM-DD` format.
:::

### Access Settings

| Field                   | Type         | Description                                           |
| ----------------------- | ------------ | ----------------------------------------------------- |
| `free`                  | boolean      | Enable/disable free access                            |
| `free_until`            | string\|null | Free access expiration (YYYY-MM-DD or null to remove) |
| `require_login`         | boolean      | Require login for free access                         |
| `preview`               | boolean      | Enable/disable preview                                |
| `preview_require_login` | boolean      | Require login for preview                             |

### DRM Print Quota

| Field                           | Type          | Description                                                                                        |
| ------------------------------- | ------------- | -------------------------------------------------------------------------------------------------- |
| `drm_print_limit`               | integer\|null | Maximum number of pages a user can print in a single print job (0 to 100)                          |
| `drm_print_lifetime_percentage` | integer\|null | Maximum percentage of the book's pages a user can print in total, across all print jobs (0 to 100) |

See [DRM Print Quota](./content-v3-create.md#drm-print-quota) for the full semantics: the quota applies to `pdf` and `epub` content, and printing stays disabled unless both values are positive.

:::note Updating the Print Quota
Each field follows partial-update semantics: an omitted field keeps its current value. Send both fields as `null` to remove the quota and disable printing. Quota changes take effect immediately and are not validated against pages users have already printed.
:::

### Subscribers-Only Access

| Field              | Type    | Description                                    |
| ------------------ | ------- | ---------------------------------------------- |
| `subscribers_only` | boolean | Restrict purchase to users with an active plan |

See [Subscribers-Only Access](./content-v3-create.md#subscribers-only-access) for rules on the `retail` license interaction.

### Marketplace

| Field                 | Type    | Description                                                                                                                                                                              |
| --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `show_in_marketplace` | boolean | Make content available in the Publica.la marketplace. When `true`, the content is listed for other tenants to discover and add to their stores. Send `false` to remove from marketplace. |

### Metadata

| Field                    | Type    | Description                                                                                                                                                             |
| ------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `author`                 | array   | Author names: `[{first_name, last_name?, title?}]` (replaces all). See [Author names](./content-v3-create.md#author-names)                                              |
| `publisher`              | array   | Publisher names (replaces all, max 200 chars each)                                                                                                                      |
| `keywords`               | array   | Keywords (replaces all, max 200 chars each)                                                                                                                             |
| `bisac`                  | array   | BISAC codes (replaces all, max 4 codes)                                                                                                                                 |
| `thema`                  | array   | Thema subject codes (replaces all, strings max 200 chars each)                                                                                                          |
| `category`               | array   | Category names (replaces all, max 200 chars each)                                                                                                                       |
| `collection`             | array   | Collection names (replaces all, max 200 chars each)                                                                                                                     |
| `series`                 | array   | Series names (replaces all, max 200 chars each)                                                                                                                         |
| `country`                | array   | Country names (replaces all, max 200 chars each)                                                                                                                        |
| `edition`                | array   | Edition names (replaces all, max 200 chars each)                                                                                                                        |
| `narrator`               | array   | Narrator names (replaces all, max 200 chars each)                                                                                                                       |
| `publishing_group`       | array   | Publishing group names (replaces all, max 200 chars each)                                                                                                               |
| `accessibility_features` | array   | Accessibility features (replaces all): `[{code: "04", description?: "..."}]` (code max 3 chars, description max 5000 chars)                                             |
| `custom_metadata`        | object  | Custom taxonomy groups: `{"group_slug": ["value1"]}`. Sending `{"group_slug": []}` clears that group                                                                    |
| `share_with_tenants_ids` | array   | Tenant IDs (strings) to share this content with. See [Content Sharing](./content-v3-sharing.md)                                                                         |
| `format_group_ids`       | array   | Format group IDs (strings) to associate this content with                                                                                                               |
| `pages`                  | integer | Number of pages (min: 0). Applies to any `file_type`. For PDF and audio content the value is derived from the file during processing; input is ignored for those types. |

### Geographic Restrictions

| Field                     | Type         | Description                                            |
| ------------------------- | ------------ | ------------------------------------------------------ |
| `geographic_restrictions` | object\|null | `{included: [...], excluded: [...]}` or null to remove |

### Identifiers

| Field                      | Type    | Description                                                                           |
| -------------------------- | ------- | ------------------------------------------------------------------------------------- |
| `identifiers`              | array   | Array of identifier objects (replaces all, max 20)                                    |
| `identifiers[].type`       | string  | Identifier type: `isbn_digital`, `isbn_printed`, `uuid`, `ddc`, `external_id`         |
| `identifiers[].value`      | string  | Identifier value (max 255 chars). Validated per type (e.g., ISBN-10/ISBN-13 checksum) |
| `identifiers[].is_primary` | boolean | Mark as primary identifier (optional, defaults to `false`)                            |

:::warning Identifier Replacement Behavior
The `identifiers` array uses **full replacement** semantics, just like other array fields. Sending `"identifiers": []` will remove all identifiers and clear `external_id`. Only include identifiers you want to keep. Omitting `identifiers` from the request preserves existing identifiers unchanged.
:::

:::info Uniqueness
Types `isbn_digital`, `isbn_printed`, `uuid`, and `external_id` require **tenant-level uniqueness** — two content items cannot share the same identifier of these types, regardless of which is primary. Only one identifier can be marked as primary. If none is marked, the first one is auto-promoted.
:::

### Physical Product Fields

| Field              | Type    | Description                                                                       |
| ------------------ | ------- | --------------------------------------------------------------------------------- |
| `binding_type`     | string  | Binding type                                                                      |
| `height`           | float   | Height in cm                                                                      |
| `width`            | float   | Width in cm                                                                       |
| `weight`           | float   | Weight in grams                                                                   |
| `stock_quantity`   | integer | Units available for sale (min: 0). `0` sells the product out and blocks purchases |
| `editing_location` | string  | Editing/printing location                                                         |
| `thickness`        | float   | Thickness in cm (min: 0)                                                          |

A product with no quantity is untracked: it stays on sale and nothing is enforced. Sending `null`, or omitting the field, leaves the product as it was, so this API can start tracking a product but cannot stop.

---

## Request Examples

### Update Basic Information

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Updated Book Title",
    "description": "<p>Updated description with more details...</p>"
  }'
```

### Update by External ID

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/9781234567890?id_type=external" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Revised Edition",
    "published_at": "2025-01-15"
  }'
```

### Update Pricing

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "prices": [
      { "currency_id": "USD", "amount": 24.99 },
      { "currency_id": "EUR", "amount": 21.99 },
      { "currency_id": "GBP", "amount": 19.99 }
    ]
  }'
```

### Enable Free Access

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "free": true,
    "free_until": "2025-06-30",
    "require_login": true
  }'
```

### Disable Free Access

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "free": false,
    "free_until": null
  }'
```

### Set or Update the Print Quota

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "drm_print_limit": 15,
    "drm_print_lifetime_percentage": 80
  }'
```

### Remove the Print Quota

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "drm_print_limit": null,
    "drm_print_lifetime_percentage": null
  }'
```

### Update Metadata

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "author": [
      { "first_name": "Jane", "last_name": "Smith" },
      { "first_name": "John", "last_name": "Doe" }
    ],
    "publisher": ["New Publisher Name"],
    "keywords": ["updated", "keywords", "list"],
    "bisac": [
      { "code": "FIC000000" },
      { "code": "FIC019000" }
    ]
  }'
```

### Update Cover Image

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "cover_url": "https://example.com/new-cover.jpg"
  }'
```

### Replace Content File

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "file_url": "https://example.com/files/updated-book-v2.pdf"
  }'
```

### Replace Content File via SFTP

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "file": "uploads/updated-book-v2.pdf"
  }'
```

### Update Identifiers

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "identifiers": [
      { "type": "isbn_digital", "value": "978-0-306-40615-7", "is_primary": true },
      { "type": "ddc", "value": "823.914" }
    ]
  }'
```

### Update Geographic Restrictions

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "geographic_restrictions": {
      "included": ["US", "CA", "GB"],
      "excluded": []
    }
  }'
```

### Remove Geographic Restrictions

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "geographic_restrictions": null
  }'
```

### Update Physical Product

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "stock_quantity": 48,
    "pages": 380,
    "weight": 520
  }'
```

### Sell a Physical Product Out

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "stock_quantity": 0
  }'
```

### Combined Update

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166?include=prices,metadata" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Complete Updated Title",
    "description": "<p>Fully revised and updated edition...</p>",
    "published_at": "2025-02-01",
    "prices": [
      { "currency_id": "USD", "amount": 29.99 }
    ],
    "author": [{ "first_name": "Updated", "last_name": "Author" }],
    "keywords": ["revised", "updated", "2025"],
    "preview": true
  }'
```

---

## Response

### Success Response (200 OK)

```json
{
  "data": {
    "id": "468166",
    "external_id": "9781234567890",
    "name": "Updated Book Title",
    "slug": "updated-book-title",
    "lang": "en",
    "file_type": "pdf",
    "audience": "adult",
    "subtitle": null,
    "publication_place": null,
    "edition_year": 2025,
    "cover_url": "https://cdn.publica.la/covers/468166.jpg",
    "reader_url": "https://yourstore.publica.la/reader/updated-book-title",
    "product_url": "https://yourstore.publica.la/library/publication/updated-book-title",
    "created_at": "2021-03-19T00:00:00.000000Z",
    "updated_at": "2025-12-23T14:30:00.000000Z",
    "published_at": "2025-01-15T00:00:00.000000Z",
    "license": "retail",
    "free": {
      "enabled": false,
      "until": null,
      "require_login": false
    },
    "preview": {
      "enabled": true,
      "require_login": false
    },
    "subscribers_only": false,
    "conversion_status": "done",
    "identifiers": []
  }
}
```

:::note Conversion Status After File Update
When updating content files, the response will show an updated `conversion_status`. Use [Get Content](./content-v3-get.md) to poll for processing completion, just as you would after creating new content.
:::

### Response with Includes

```json
{
  "data": {
    "id": "468166",
    "external_id": "9781234567890",
    "name": "Updated Book Title",
    "slug": "updated-book-title",
    "lang": "en",
    "file_type": "pdf",
    "audience": "adult",
    "subtitle": null,
    "publication_place": null,
    "edition_year": 2025,
    "cover_url": "https://cdn.publica.la/covers/468166.jpg",
    "reader_url": "https://yourstore.publica.la/reader/updated-book-title",
    "product_url": "https://yourstore.publica.la/library/publication/updated-book-title",
    "created_at": "2021-03-19T00:00:00.000000Z",
    "updated_at": "2025-12-23T14:30:00.000000Z",
    "published_at": "2025-01-15T00:00:00.000000Z",
    "license": "retail",
    "free": {
      "enabled": false,
      "until": null,
      "require_login": false
    },
    "preview": {
      "enabled": true,
      "require_login": false
    },
    "subscribers_only": false,
    "conversion_status": "done",
    "prices": [{ "currency_id": "USD", "amount": 29.99 }],
    "description": "<p>Fully revised and updated edition...</p>",
    "publisher": ["New Publisher Name"],
    "author": ["Updated Author"],
    "bisac": [{ "code": "FIC000000", "label": "Fiction > General" }],
    "keywords": ["revised", "updated", "2025"],
    "country": [],
    "edition": [],
    "narrator": [],
    "publishing_group": [],
    "thema": [],
    "series": [],
    "category": [],
    "collection": [],
    "metrics": {
      "total_pages": 320,
      "total_words": 85000,
      "total_seconds": 0
    },
    "custom_metadata": {},
    "geographic_restrictions": null,
    "identifiers": [
      {
        "type": "isbn_digital",
        "value": "978-0-306-40615-7",
        "is_primary": true
      }
    ]
  }
}
```

---

## Error Handling

### Content Not Found (404)

```json
{
  "message": "Content not found."
}
```

### Validation Errors (422)

```json
{
  "message": "The given data was invalid.",
  "errors": {
    "name": ["The name may not be greater than 255 characters."],
    "prices.0.currency_id": ["The selected prices.0.currency_id is invalid."]
  }
}
```

### Duplicate Identifier (422)

```json
{
  "message": "The given data was invalid.",
  "errors": {
    "identifiers.0.value": [
      "This isbn_digital identifier is already in use as primary for another content."
    ]
  }
}
```

### Authentication Error (401)

```json
{
  "message": "Unauthenticated."
}
```

---

## Common Error Causes

| Error                   | Cause                                                      |
| ----------------------- | ---------------------------------------------------------- |
| Content not found       | Invalid ID or wrong `id_type`                              |
| Duplicate identifier    | Another content has this identifier as primary             |
| Invalid currency        | Currency code not in system                                |
| Invalid BISAC code      | BISAC code doesn't exist                                   |
| File URL inaccessible   | Cannot download from provided URL                          |
| Invalid date format     | Date not in YYYY-MM-DD or ISO 8601 format                  |
| File update not allowed | File updates only supported for PDF and EPUB content types |

---

## Best Practices

### 1. Use Partial Updates

Only include fields you're changing:

```json
// Good - minimal payload
{ "name": "New Title" }

// Avoid - unnecessary fields
{
  "name": "New Title",
  "description": "Same description",
  "prices": [...],
  "author": [...]
}
```

### 2. Retrieve Before Update

For complex updates, get current state first:

```bash
# Get current prices
curl -X GET "https://yourstore.publica.la/api/v3/content/468166?include=prices"

# Then update with new price added
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166" \
  -d '{ "prices": [existing_prices + new_price] }'
```

### 3. Use Include for Verification

Request includes in response to verify update:

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/content/468166?include=prices,metadata" \
  -d '{ "prices": [...], "author": [...] }'
```

---

## See Also

- [Get Content](./content-v3-get.md) - Retrieve content details
- [Delete Content](./content-v3-delete.md) - Remove content
- [Create Content](./content-v3-create.md) - Add new content
- [Overview](./content-v3-overview.md) - API overview

---
