---
title: "Create a book record"
method: POST
path: "/api/v1/books"
tags: ["Books"]
---

# Create a book record

`POST /api/v1/books`

## Request

- Base URL: `https://beyourcover.com`
- URL: `https://beyourcover.com/api/v1/books`
- Auth: HTTP bearer

Creates a book that covers can be generated against. `author` is required unless `genre` is the planner genre (`Planner & Journal` / `planner-journal`). When `subgenre` is omitted for a high-variance genre, the server auto-detects one from the book details (an explicit `subgenre` always wins; detection failure leaves it null). An explicit `subgenre` must be one of the genre's values, else `400 invalid_subgenre`. Supports idempotent replay via the `Idempotency-Key` header: a repeated key returns `200` with the previously created book instead of `201`.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `title` string, required: Book title. Required. Rendered on the cover, so Arabic and Hindi/Devanagari scripts are rejected (`unsupported_characters`).
  - `author` string: Author name. Required unless `genre` is `Planner & Journal` / `planner-journal`. Same script restrictions as `title`.
  - `subtitle` string: Optional subtitle. Same script restrictions as `title`.
  - `genre` string, required: Required. Accepts either a kebab-case genre slug (e.g. `science-fiction`, `business-economics`, `religion-spirituality`, `non-fiction`, `fantasy`, `romance`, `thriller`, `mystery`, `horror`, `historical-fiction`, `health-fitness`, `contemporary-fiction`, `young-adult`, `childrens`, `self-help`, `biography-memoir`, `poetry`, `planner-journal`, `other`) or the canonical genre value (e.g. `Science Fiction`). Unrecognized values return `invalid_genre`.
  - `subgenre` string: Optional subgenre value. Must be one of the genre's values, exactly as written (see `GET /api/v1/capabilities`, `genres[].subgenres[].value`). Any other value returns `invalid_subgenre`, and the message lists the valid values. A genre with no subgenres (`other`) takes none. If omitted for genres that have subgenres, the server auto-detects one.
  - `summary` string: Optional plot/content summary used to inform generation.
  - `target_audience` 'children' | 'middle-grade' | 'young-adult' | 'new-adult' | 'adult': Optional. Must be one of the predefined audience values.
  - `external_ref` string: Optional caller-supplied reference. Echoed back in the response (not stored on the book).
  - `planner_year` string: Optional. Only meaningful for the planner genre. Must be a string if provided (type-checked; no length limit is enforced in code).

## Response `200`

Idempotent replay: a book with the same `Idempotency-Key` already exists for this user. `external_ref` is echoed from the current request, not the original one.

- BookCreated
  - `bookId` string, uuid, required
  - `subgenre` string, nullable, required: The stored subgenre: the caller-provided value, an auto-detected one, or null.
  - `external_ref` string, nullable, required: Echo of the request's external_ref (not persisted on the book).
  - `created_at` string, date-time, required

## Other responses

- `201`: Book created.
- `400`: Validation or moderation failure. `code` is one of: `missing_required_fields`, `invalid_field_type`, `field_too_long`, `unsupported_characters`, `invalid_field_value`, `content_policy_violation`, `invalid_genre`, `invalid_subgenre`.
- `401`: Missing/malformed API key, unknown or inactive key, or inactive subscription detected during credit checks. `code`: `unauthorized`.
- `403`: The API key is valid but its owner has no active enterprise subscription. `code`: `forbidden`.
- `500`: Server error. `code` is `book_creation_failed` (insert failed) or `internal_error`.

---

[API](https://skmtc.dev/beyourcover/apis/beyourcover-partner-api.md) · [All operations](https://skmtc.dev/beyourcover/apis/beyourcover-partner-api/llms.txt) · [OpenAPI document](https://skmtc.dev/beyourcover/apis/beyourcover-partner-api/revisions/82ad2c7c8ede?raw)
