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

# eBooks over API

> Learn how to manage eBooks digital product over APIs

Fermion offers an easy-to-use API for creating and managing eBook digital products. This guide will help you upload, encrypt, and publish eBooks via API step by step.

## Overview

With Fermion’s eBook API, you can:

* Create eBook products through code
* Securely upload PDF files to our cloud storage
* Apply automatic DRM protection and encryption
* Configure watermarks and control download permissions
* Update eBook metadata and descriptions
* Set up and edit pricing plans
* Manage multiple versions of any eBook

## Prerequisites

Before you begin, make sure you have:

* A [valid Fermion API key](/api-guide/introduction#how-to-get-your-api-key)
* A PDF file ready for upload (your eBook)
* Basic familiarity with HTTP requests and file uploads

<Steps>
  <Step title="Create an eBook Product">
    Create a new eBook product using the `create-ebook-product` endpoint.

    Refer to the [create-ebook endpoint](/api-reference/product/create-ebook-product) for call details.

    **Sample Response:**

    ```json theme={null}
    {
    	"status": "ok",
    	"data": {
    		"fermionDigitalProductId": "prod_abc123xyz",
    		"slug": "xyz789"
    	}
    }
    ```

    <Note>
      Save the `slug` value—it's a unique, URL-friendly ID you’ll use for all later API calls.
    </Note>
  </Step>

  <Step title="Get a Presigned Upload URL">
    See the [get-presigned-url-for-ebook-upload](/api-reference/product/get-presigned-url-for-ebook-upload) for details.

    **Sample Response:**

    ```json theme={null}
    {
    	"status": "ok",
    	"data": {
    		"r2KeyForOriginalEbookPdf": "private-cached-ebook-unencrypted/prod_abc123xyz/file_unique_id",
    		"presignedUrl": "https://storage.example.com/upload?signature=..."
    	}
    }
    ```

    <Warning>
      The presigned upload URL will expire in **1 hour**. Be sure to upload your PDF before then.
      If it expires, just request a new upload URL.
    </Warning>

    **Key Points:**

    * Save `r2KeyForOriginalEbookPdf`—you’ll need it next
    * The upload link is one-time and time-limited for security
    * Requesting a new URL allows you to upload a new version
  </Step>

  <Step title="Upload the PDF via Presigned URL">
    Upload your PDF using a HTTP PUT request to the URL from Step 2.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X PUT \
        -H "Content-Type: application/pdf" \
        --data-binary @your-ebook.pdf \
        "https://storage.example.com/upload?signature=..."
      ```

      ```javascript JavaScript (Node.js) theme={null}
      const fs = require('fs')

      const pdfBuffer = fs.readFileSync('your-ebook.pdf')

      await fetch(presignedUrl, {
      	method: 'PUT',
      	headers: {
      		'Content-Type': 'application/pdf'
      	},
      	body: pdfBuffer
      })
      ```

      ```python Python theme={null}
      import requests

      with open('your-ebook.pdf', 'rb') as f:
          pdf_data = f.read()

      response = requests.put(
          presigned_url,
          headers={'Content-Type': 'application/pdf'},
          data=pdf_data
      )
      ```
    </CodeGroup>

    <Tip>
      Use the HTTP PUT method (not POST) and make sure <code>Content-Type</code> is set to <code>application/pdf</code> for correct uploads.
    </Tip>
  </Step>

  <Step title="Process and Encrypt the Uploaded eBook">
    After uploading your PDF, trigger processing and encryption.

    See the [process-uploaded-ebook](/api-reference/product/process-uploaded-ebook).

    **Sample Response:**

    ```json theme={null}
    {
    	"status": "ok",
    	"data": {
    		"ebookVersionIndex": 0
    	}
    }
    ```

    **What Processing Does:**

    1. Downloads your PDF from storage
    2. Encrypts the file with AES-128-CBC
    3. Generates encryption keys and initialization vectors
    4. Saves the encrypted eBook securely
    5. Logs encryption metadata to your product

    <Info>
      Save the `ebookVersionIndex`—you’ll need it to activate (publish) this version.
    </Info>
  </Step>

  <Step title="Update eBook Metadata and Reader Settings">
    See API docs: [update-ebook-metadata](/api-reference/product/update-ebook-metadata)

    **Sample Request Body:**

    ```json theme={null}
    {
    	"productSlug": "xyz789",
    	"title": "Introduction to Web Development",
    	"shortDescription": "Learn HTML, CSS, and JavaScript fundamentals",
    	"longDescription": "This comprehensive guide covers everything you need to know about modern web development...",
    	"activeVersionIndex": 0,
    	"isBookDownloadable": false,
    	"shouldEnableCenterWatermark": true,
    	"shouldEnableHeaderWatermark": true
    }
    ```

    **Field Breakdown:**

    * `title` (optional): eBook display title
    * `shortDescription` (optional): Summary for listings
    * `longDescription` (optional): Main description on product page
    * `activeVersionIndex` (optional): Which uploaded version readers see
    * `isBookDownloadable` (optional): Allow download of PDF file
    * `shouldEnableCenterWatermark` (optional): Show watermark in middle of pages
    * `shouldEnableHeaderWatermark` (optional): Show watermark in headers

    <Warning>
      **Important:** Set `activeVersionIndex` to the version index returned from Step 4 to activate your upload.
      Only processed eBook versions (`uploaded-and-processed`) can be marked active.
    </Warning>

    ### Watermark Options

    Watermarks protect your content from unauthorized sharing:

    * **Center Watermark**: User’s email/ID in the center of every page
    * **Header Watermark**: User info shown in each PDF header
    * Watermarks are personalized for each downloader to trace misuse

    ### Controlling Download Access

    * <code>isBookDownloadable: true</code> — Allow PDF downloads for offline reading
    * <code>isBookDownloadable: false</code> — Restrict reading to browser-only
  </Step>

  <Step title="Add Pricing (Optional)">
    To set up paid access, use the digital product pricing API.

    See docs: [create-custom-pricing-plan-for-a-product](/api-reference/product/create-custom-pricing-plan-for-a-product)
  </Step>

  <Step title="Publish & Share">
    After completing the above, your eBook is ready to be published by setting it to "Active."
  </Step>
</Steps>

## Complete Example: JavaScript/Node.js Workflow

Below is a full eBook upload example using Node.js:

```javascript theme={null}
const API_KEY = 'your-api-key'
const BASE_URL = 'https://your-school.fermion.app/api'

// Step 1: Create the eBook product
const createResponse = await fetch(`${BASE_URL}/public/create-ebook-product`, {
	method: 'POST',
	headers: {
		'fermion-api-key': API_KEY,
		'Content-Type': 'application/json'
	},
	body: JSON.stringify({
		ebookName: 'My Amazing eBook',
		shortDescription: 'Learn something awesome',
		status: 'Draft'
	})
})

const { slug, fermionDigitalProductId } = (await createResponse.json()).data

// Step 2: Request presigned upload URL
const uploadUrlResponse = await fetch(`${BASE_URL}/public/get-presigned-url-for-ebook-upload`, {
	method: 'POST',
	headers: {
		'fermion-api-key': API_KEY,
		'Content-Type': 'application/json'
	},
	body: JSON.stringify({ productSlug: slug })
})

const { presignedUrl, r2KeyForOriginalEbookPdf } = (await uploadUrlResponse.json()).data

// Step 3: Upload the PDF
const pdfBuffer = fs.readFileSync('./my-ebook.pdf')
await fetch(presignedUrl, {
	method: 'PUT',
	headers: { 'Content-Type': 'application/pdf' },
	body: pdfBuffer
})

// Step 4: Process and encrypt the PDF
const processResponse = await fetch(`${BASE_URL}/public/process-uploaded-ebook`, {
	method: 'POST',
	headers: {
		'fermion-api-key': API_KEY,
		'Content-Type': 'application/json'
	},
	body: JSON.stringify({
		productSlug: slug,
		r2KeyForOriginalEbookPdf
	})
})

const { ebookVersionIndex } = (await processResponse.json()).data

// Step 5: Update metadata and activate version
await fetch(`${BASE_URL}/public/update-ebook-metadata`, {
	method: 'POST',
	headers: {
		'fermion-api-key': API_KEY,
		'Content-Type': 'application/json'
	},
	body: JSON.stringify({
		productSlug: slug,
		title: 'My Amazing eBook',
		longDescription: 'This eBook will teach you...',
		activeVersionIndex: ebookVersionIndex,
		isBookDownloadable: false,
		shouldEnableCenterWatermark: true,
		shouldEnableHeaderWatermark: true
	})
})

console.log(`eBook created successfully! Product ID: ${fermionDigitalProductId}`)
```

## Managing Multiple eBook Versions

You can upload new versions (such as updates or corrections) for the same eBook:

<Steps>
  <Step title="Get New Presigned Upload URL">
    Call <code>get-presigned-url-for-ebook-upload</code> again to get a fresh upload URL.
  </Step>

  <Step title="Upload New PDF">
    Upload your new PDF via the new URL.
  </Step>

  <Step title="Process and Encrypt">
    Process and encrypt the new version.
  </Step>

  <Step title="Update Version Index">
    Update <code>activeVersionIndex</code> via the metadata API to switch to the new version.
  </Step>
</Steps>

<Tip>
  Don't delete past versions—you can switch between them any time by changing <code>activeVersionIndex</code>.
</Tip>

## Best Practices

### Security

* Always use HTTPS for every API request and upload
* Never expose your API key in frontend or client code
* Enable watermarks to discourage piracy for premium content
* Disable file downloads (`isBookDownloadable: false`) for sensitive material

### Performance

* Upload large PDFs directly to storage with the presigned URL (not through your own server)
* If the upload fails or times out, request a new presigned URL and re-upload (do not reuse old links)
* Integrate uploads asynchronously if building user-facing dashboards

### Content Organization

* Set clear and descriptive `ebookName` values for easy identification
* Keep products in `Draft` mode during setup and review
* Review the eBook reader experience before publishing as “Active”
* Use `shortDescription` for SEO/discovery and `longDescription` for full details

## Troubleshooting

### "Uploaded eBook not found" Error

You may see this in Step 4 if:

* The PDF failed to upload, or upload was incomplete
* The `r2KeyForOriginalEbookPdf` you used does not match the uploaded file
* The presigned upload URL expired

**Fix:** Request a new presigned URL and upload your PDF again.

### "You can only mark an uploaded book as active" Error

This occurs if you try to activate a version that hasn't been processed.

**Fix:** Complete Step 4 (process-uploaded-ebook), then set `activeVersionIndex`.

### Upload Timed Out

Large PDFs can sometimes time out or stall.

**Fixes:**

* Use a stable and fast internet connection
* Adjust timeout settings in your HTTP client or tool
* Compress or optimize PDFs before uploading

### Expired Upload URL

**Fix:** Simply request a new presigned URL using the API—there’s no limit to how many times you can do this.

## Getting Help

If you get stuck or have an edge-case issue:

* See the [API Reference](https://docs.fermion.app/api-reference) for endpoint details
* Check the [Webhooks Guide](https://docs.fermion.app/webhooks) to track eBook purchases
* For product-specific issues, contact support and include your `fermionDigitalProductId`

## Next Steps

* [Set up pricing plans](https://docs.fermion.app/api-reference/product/create-custom-pricing-plan-for-a-product)
* [Create coupons for your eBooks](https://docs.fermion.app/api-reference/product/create-digital-product-coupon)
* [Enroll users programmatically](https://docs.fermion.app/api-reference/user/enroll-user-into-digital-product)
* [Configure webhooks](https://docs.fermion.app/webhooks) to track purchases and enrollments
