BeYourCover Book Cover Generation API

Generate, edit, and download professional AI book covers programmatically. Built for publishers, self-publishing platforms, and developer teams who need automated book cover design at scale.

REST APIMCP ServerWebhook SupportPrint-Ready PDFs

Who Uses the Book Cover API?

Self-Publishing Platforms

Offer built-in cover generation to your authors. Integrate with a few API calls and let users create covers without leaving your platform.

Publishing Houses & Agencies

Generate hundreds of cover variations for your catalog. Use bulk generation to A/B test covers and find what converts best.

Author Tools & SaaS Products

Add professional cover design to your writing, formatting, or marketing tool. White-label ready with full control over the generation pipeline.

How It Works

1

Create a Book

Send title, author, genre, and a summary to POST /books. The summary helps the AI generate relevant imagery.

2

Generate Covers

Call POST /generate with your book ID. Get up to 5 AI-generated covers per request, delivered via polling or webhooks.

3

Edit, Upscale & Download

Refine covers with AI edits, create variations, generate audiobook versions, and upscale to high resolution — all through the API.

API Documentation

Generate book covers programmatically with the BeYourCover REST API.

Quick Start

Generate your first cover with 4 API calls and the bare minimum data. Grab your API key from the Authentication section, then run the commands below.

  1. Create a book — only title, author, and genre are required.
  2. Generate a cover — pass the bookId. Returns a job ID.
  3. Poll the job until status is completed.
  4. Download the cover from the asset download endpoint.
End-to-end minimum (curl)
# 1. Create a book (only title, author, genre required)
curl -X POST https://beyourcover.com/api/v1/books \
  -H "Authorization: Bearer byc_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"title":"My Novel","author":"Jane Doe","genre":"fantasy"}'
# -> { "bookId": "uuid", ... }

# 2. Generate a cover (only bookId required)
curl -X POST https://beyourcover.com/api/v1/covers/generate \
  -H "Authorization: Bearer byc_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"bookId":"<bookId>"}'
# -> { "jobId": "uuid", "poll_url": "/api/v1/jobs/uuid", ... }

# 3. Poll until status === "completed"
curl https://beyourcover.com/api/v1/jobs/<jobId> \
  -H "Authorization: Bearer byc_sk_your_key"
# -> { "status": "completed", "result": { "covers": [{ "coverId": "uuid", ... }] } }

# 4. Download the cover
curl -L https://beyourcover.com/api/v1/covers/<coverId>/assets/preview/download \
  -H "Authorization: Bearer byc_sk_your_key" \
  -o cover.png

Tip: Adding a summary to the book dramatically improves cover quality — the AI uses it to pick imagery and color. See the full endpoint reference for count, templates, webhooks, and more.

Authentication

All API requests require a Bearer token in the Authorization header. Get your API key from the Account Dashboard. Don't have an account yet? Choose an API plan to get started.

Header
Authorization: Bearer byc_sk_your_api_key_here

Rate Limits

PlanCredits/moConcurrent Jobs
Starter1505
Growth50010
Scale1,50020

See full plan details and pricing on the Enterprise Plans page.

Core endpoints

These eight calls cover most integrations: create a book, make covers, wait for them, and download the files. Every other endpoint is in the Advanced reference. Lists return the newest items first. When a list response has a next_cursor, send it back as ?cursor= to get the next page; null means there are no more.

POST/api/v1/books

Create a book record. Required before generating covers.

FieldRequiredMaxDescription
titlerequired100Book title
authorrequired*100Author name. *Optional when genre is planner-journal — planner covers typically lead with a brand wordmark and omit the author.
genrerequired—Genre slug — see Genres section
subtitleoptional300Book subtitle
subgenreoptional100Subgenre (kebab-case — see Genres section). For genres that use subgenres, omitting this auto-detects one from your title and summary; an explicit value always wins. It must be one of the genre's values — any other value returns 400 invalid_subgenre with the valid values.
summaryrecommended1000Book synopsis — strongly recommended, used by the AI to create relevant imagery
target_audienceoptional—Restricted values: children, middle-grade, young-adult, new-adult, adult
external_refoptional200Your internal ID — echoed back in responses
planner_yearoptional—Free-form year label rendered prominently on the cover (typically foil-stamped). Only relevant when genre is planner-journal. Any string is accepted — e.g. 2026, 2026-2027, FY26.

Tip: Always provide a summary. The AI uses it to select visual metaphors, color palettes, and imagery that match your book. Without it, the cover will be based on title and genre alone, which produces more generic results.

Note: Title, author, and subtitle do not support Arabic or Hindi (Devanagari) scripts. Most other scripts including Latin, CJK, Cyrillic, and accented characters are supported.

Planner & Journal covers: When genre is planner-journal, the author field becomes optional (most planners lead with a brand wordmark and have no author). Use title for the brand/wordmark, subtitle for the tagline, and planner_year for a prominent foil-stamped year on the cover.

Subgenre auto-detection: For genres that use subgenres (Romance, Fantasy, Thriller, Mystery, Science Fiction, Horror, Contemporary Fiction, Biography & Memoir, Planner & Journal, Children's, Young Adult), omitting subgenre auto-detects one from your title and summary; the resolved value is returned as subgenre in the response. A few genres (Self-Help, Business, Religion, Health & Fitness, Historical Fiction, Non-Fiction, Poetry, Other) need no subgenre at all. An explicit subgenre always takes precedence, and must be one of the genre's values: an unknown value returns 400 invalid_subgenre instead of creating the book.

Example Request
{
  "title": "Scale Without Limits",
  "author": "Alex Carter",
  "genre": "business-economics",
  "subtitle": "Systems and Strategies for Explosive Business Growth",
  "summary": "A practical guide for entrepreneurs and executives on building scalable systems, creating predictable growth, and leading high-performing organizations in competitive markets.",
  "target_audience": "adult",
  "external_ref": "book-123"
}
Example Request — Planner & Journal (no author)
{
  "title": "The Daily Momentum Planner",
  "subtitle": "Master Your Work Hours and Reclaim Your Time",
  "genre": "planner-journal",
  "subgenre": "productivity",
  "planner_year": "2026",
  "summary": "A weekly planner for entrepreneurs focused on quarterly goal tracking, time blocking, and reflection prompts.",
  "external_ref": "planner-001"
}
Headers (optional)
Idempotency-Key: unique-request-id
Response 201 (or 200 if idempotent duplicate)
{
  "bookId": "uuid",
  "subgenre": null,
  "external_ref": "book-123",
  "created_at": "2025-01-15T10:30:00Z"
}

POST/api/v1/covers/generate

Start an async cover generation job. Returns 202 immediately.

FieldRequiredMaxDescription
bookIdrequired—UUID of the book created via POST /api/v1/books
countoptional—Number of covers to generate. Integer 1–5, default 1. Each cover costs 1 credit.
templateIdoptional—Template ID from GET /templates. If omitted (and no other template control is sent), the app's art director designs the covers for the book, at the same price. An unrecognized id returns 400 invalid_template_id.
promptoptional1500Your instructions for the cover, in plain words: colours, mood, what it shows. They are added to the design. To send a complete prompt of your own instead, see the advanced options.
external_refoptional200Your internal ID — echoed back in responses and webhooks

Supported genres & credit safety: Every genre is supported. A book in a genre that uses subgenres but has none set gets one before its first render, as in the app: the detected subgenre when the detection is sure enough, else the genre's default. Letting POST /books auto-detect the subgenre, or passing one, picks the book's own shelf. All synchronous rejections (404, 422, 400) happen before credits are reserved, so no credits are charged for them.

More options — your own complete prompt, style choices for a pinned template, the story motif, several directions in one call, and a webhook per job — are in Advanced options for generation.

Example Request
{
  "bookId": "550e8400-e29b-41d4-a716-446655440000",
  "count": 3,
  "prompt": "Muted colours and a calm, intellectual mood"
}
Headers (optional)
Idempotency-Key: unique-request-id
Response 202
{
  "jobId": "uuid",
  "status": "queued",
  "external_ref": "gen-001",
  "created_at": "2025-01-15T10:30:00Z",
  "poll_url": "/api/v1/jobs/uuid"
}

GET/api/v1/jobs/:jobId

Poll job status. Each call returns freshly signed URLs valid for 1 hour. The result shape varies by operation — see each endpoint for details. A job with "source": "web" ran in the web app: you get its status and credits, and its result is always null.

Response 200 (completed — generate/variation)
{
  "jobId": "uuid",
  "status": "completed",
  "operation": "generate",
  "source": "api",
  "external_ref": "gen-001",
  "credits_used": 2,
  "created_at": "...",
  "started_at": "...",
  "completed_at": "...",
  "result": {
    "bookId": "uuid",
    "covers": [
      {
        "coverId": "uuid",
        "signedUrl": "https://...",
        "expiresAt": "2025-01-15T11:30:00.000Z"
      }
    ]
  },
  "error": null
}

Important: Signed URLs expire after 1 hour. Download or serve images on your infrastructure before the expiresAt timestamp. You can re-poll the job at any time to get fresh URLs.

GET/api/v1/covers/:coverId/assets/:assetType/download

Download a cover asset. Returns a 302 redirect to a signed URL valid for 1 hour. No credit charge.

ParamInDescription
coverIdpathUUID of the cover
assetTypepathpreview (the cover), full (print resolution, after an upscale), audiobook_preview, audiobook_full, no_text, no_text_full (text-free artwork)
Example
curl -L https://beyourcover.com/api/v1/covers/<coverId>/assets/preview/download \
  -H "Authorization: Bearer byc_sk_your_key" \
  -o cover.png

GET/api/v1/books

List the books on your account, newest first, so you can come back to a book without creating it again. (The POST on this path creates a book.) Hidden books stay out unless you ask for them, as on the dashboard shelf.

Query Parameters
?limit=20             // optional, 1–100 (default 20)
?cursor=<opaque>      // optional, from a previous next_cursor
?q=lantern            // optional, title or author contains (max 100 chars)
?include_hidden=true  // optional, also list hidden books
?source_type=premade  // optional: generated | external_wrap | premade
?client_id=<uuid>     // optional, one agency client's books
Response 200
{
  "books": [
    {
      "bookId": "uuid",
      "title": "My Novel",
      "author": "Jane Doe",
      "subtitle": null,
      "genre": "Contemporary Fiction",   // canonical value
      "subgenre": "book-club",
      "summary": "...",
      "target_audience": null,
      "planner_year": null,
      "visibility": "default",           // or "hidden"
      "source_type": "generated",
      "thumbnail_cover_id": "uuid",      // newest favorite, else newest cover
      "covers_count": 4,                 // hidden covers included
      "created_at": "..."
    }
  ],
  "next_cursor": null
}

GET/api/v1/books/:bookId/covers

List every cover generated for a book (newest first). Each entry exposes its public coverId plus which files it has. To get a file, use the download endpoint.

Query Parameters
?limit=100            // optional, 1–200 (default 100)
?include_hidden=true  // optional, also list hidden covers
Response 200
{
  "bookId": "uuid",
  "covers": [
    {
      "coverId": "uuid",                  // cover's public_id
      "coverStyle": "v2-thriller-house-bigtype",
      "visibility": "default",            // default | favorite | hidden
      "created_at": "...",
      "assets": {
        "preview":           { "available": true },
        "full":              { "available": true },   // present after upscale
        "audiobook_preview": { "available": false },
        "audiobook_full":    { "available": false }
      }
    }
  ],
  "total": 1
}

Errors: 404 book_not_found (missing or not owned by your key), 400 invalid_limit, 400 invalid_include_hidden.

GET/api/v1/credits

Current credit balance, plan tier, and concurrency limit for the authenticated subscription. Check this before large batches. Insufficient-credit errors on generation also carry error.details (balance, required, billing_url).

Response 200
{
  "tier": "growth",
  "credits_remaining": 120,   // unused monthly credits
  "credits_monthly": 500,     // monthly allowance at renewal
  "overage_credits": 30,      // purchased overage
  "total_available": 150,     // credits_remaining + overage_credits
  "concurrent_limit": 10,     // max queued/processing jobs at once
  "is_active": true,
  "billing_url": "https://beyourcover.com/enterprise-dashboard",
  "marketing_kits": {
    "period_key": "2026-10",          // billing period
    "kits_left": 3, "kits_total": 5, "kits_used": 2,
    "books": [                        // kits open this period
      {
        "bookId": "uuid",
        "title": "My Novel",
        // the first wrap / audiobook / text-free render is included
        "included_formats_left": ["audiobook", "text_free"]
      }
    ]                                 // null = meter unreadable
  }
}

GET/api/v1/templates

List available cover templates. Authentication required. No credits are consumed. Use the same genre values from the Genres & Subgenres section to filter.

Query Parameters
?genre=fantasy                          // optional, any genre slug
?genre=science-fiction
?genre=thriller&subgenre=cozy           // optional; subgenre requires genre
?genre=romance&subgenre=dark-romance
?genre=fantasy&subgenre=romantasy

subgenre uses the kebab-case values from the Genres & Subgenres table (e.g. cozy, dark-romance, epic-fantasy). With genre and subgenre, the response lists the templates that subgenre renders from; each one carries subgenreMatch: true, kept for older clients. With genre alone, it returns every template available across that genre's subgenres.

The list comes from the same template catalog that generation checks templateId against, so every listed id can be pinned. Each template carries its previewImage URL and its variants: style axes such as fontStyle and titleColor, each with the options the engine picks one from when it renders that template. Pass your choice as variant_overrides on POST /covers/generate with the same templateId.

Errors: 400 invalid_genre, 400 invalid_subgenre (lists valid values), 400 missing_genre_for_subgenre (subgenre sent without genre).

Response 200
{
  "templates": [
    {
      "id": "v2-thriller-bigtype-house",
      "name": "Big Type Over House",
      "family": "thriller-bigtype-house",
      "templateType": "photographic",
      "genreBuckets": ["Thriller & Mystery:domestic", "Thriller & Mystery:psychological", "Thriller & Mystery:rural"],
      "previewImage": "https://images.beyourcover.com/previews/v2/thriller-bigtype-house.png",
      "variants": {
        "fontStyle": ["massive-bold-sans-caps", "heavy-condensed-grotesque-caps", "chunky-distressed-sans-caps"],
        "titleColor": ["bone-white", "acid-yellow", "hot-magenta", "toxic-green"]
        // … one entry per style axis
      },
      "subgenreMatch": true    // only present when subgenre is in the query
    }
  ],
  "total": 7
}

Genres & Subgenres

Use these values for the genre and subgenre fields when creating a book. Genre is required; subgenre is optional — if you omit it for a genre that uses subgenres, one is auto-detected from your title and summary when the book is created (see POST /books). All genre and subgenre values are case-sensitive and must be sent exactly as shown.

Available Genres

Value (slug)Description
business-economicsBusiness & Economics
religion-spiritualityReligion & Spirituality
non-fictionNon-Fiction
fantasyFantasy
romanceRomance
thrillerThriller
mysteryMystery
science-fictionScience Fiction
horrorHorror
historical-fictionHistorical Fiction
health-fitnessHealth & Fitness
contemporary-fictionContemporary Fiction
young-adultYoung Adult
childrensChildren's
self-helpSelf-Help & Personal Development
biography-memoirBiography & Memoir
poetryPoetry
planner-journalPlanner & Journal

Subgenres

romance subgenres
ValueLabel
contemporaryContemporary Romance
romantasyRomantasy
new-adultNew Adult Romance
dark-romanceDark Romance
sportsSports Romance
romantic-suspenseRomantic Suspense
eroticErotic Romance
paranormal-monsterParanormal & Monster Romance
historical-romanceHistorical Romance
small-townSmall Town Romance
clean-sweetClean & Sweet Romance
romcomRomantic Comedy
fantasy subgenres
ValueLabel
romantasyRomantasy
epic-fantasyEpic & High Fantasy
adventureAdventure Fantasy
dystopianDystopian
dark-fantasyDark Fantasy & Horror
cozy-fantasyCozy Fantasy
grimdarkGrimdark
litrpgLitRPG & Progression
dark-academiaDark Academia Fantasy
myth-folkloreMyth & Folklore Retellings
contemporary-urbanContemporary & Urban Fantasy
grounded-lowGrounded & Low Fantasy
children's subgenres
ValueLabel
picture-bookPicture Book
early-readerEarly Reader
chapter-bookChapter Book
middle-gradeMiddle Grade
otherOther
thriller subgenres
ValueLabel
cozyCozy Mystery
domesticDomestic Thriller
psychologicalPsychological Thriller
legalLegal Thriller
politicalPolitical Thriller
proceduralPolice Procedural
dark-academiaDark Academia Thriller
ruralRural / Small-Town
true-crimeTrue Crime Fiction
technoTechno-Thriller
espionageEspionage / Spy
mystery subgenres
ValueLabel
cozyCozy Mystery
domesticDomestic Thriller
psychologicalPsychological Thriller
legalLegal Thriller
politicalPolitical Thriller
proceduralPolice Procedural
dark-academiaDark Academia Thriller
ruralRural / Small-Town
true-crimeTrue Crime Fiction
technoTechno-Thriller
espionageEspionage / Spy
biography & memoir subgenres
ValueLabel
literaryLiterary Memoir
historicalHistorical Biography
inspirationalInspirational & Recovery
culturalCultural & Travel
contemporary fiction subgenres
ValueLabel
book-clubBook Club / Upmarket
womens-fictionWomen's Fiction
family-sagaFamily Saga
humorousHumorous & Satirical
historical fiction subgenres
ValueLabel
historical-fictionHistorical Fiction
planner & journal subgenres
ValueLabel
productivityProductivity & Business
wellnessWellness & Gratitude
budgetBudget & Finance
fitnessFitness & Health
minimalistMinimalist Notebook
feminineFeminine Lifestyle
faithFaith & Christian
goal-settingGoal-Setting & Vision
science fiction subgenres
ValueLabel
space-operaSpace Opera
dystopianDystopian / Post-Apocalyptic
hard-sfHard Sci-Fi
litrpgLitRPG / Progression
cyberpunkCyberpunk / Cli-Fi
horror subgenres
ValueLabel
gothicGothic Horror
cosmicCosmic / Lovecraftian
supernaturalSupernatural / Ghost Story
slasherSlasher / Splatterpunk
psychologicalPsychological Horror
folkFolk Horror
bodyBody Horror
dark-fantasyDark Fantasy Horror
self-help & personal development subgenres
ValueLabel
self-helpSelf-Help
business & economics subgenres
ValueLabel
businessBusiness
religion & spirituality subgenres
ValueLabel
religionReligion & Spirituality
health & fitness subgenres
ValueLabel
health-fitnessHealth & Fitness
non-fiction subgenres
ValueLabel
non-fictionNon-Fiction
poetry subgenres
ValueLabel
poetryPoetry
young adult subgenres
ValueLabel
ya-contemporaryYA Contemporary
ya-fantasyYA Fantasy
ya-romanceYA Romance
ya-dystopianYA Dystopian
ya-sci-fiYA Science Fiction
ya-thrillerYA Thriller & Mystery
ya-horrorYA Horror
Example: Book with subgenre
{
  "title": "Crown of Shadows",
  "author": "Elena Blackwood",
  "genre": "fantasy",
  "subgenre": "dark-fantasy",
  "summary": "A disgraced knight seeks redemption..."
}

Webhooks

Configure a webhook URL in your dashboard to receive job completion notifications. You can also override the URL per-job via the webhookUrl field in the generate request (see advanced options). Each payload is signed, so you can check that it came from us.

Webhook Header
X-BYC-Signature: sha256=<hmac_hex_digest>
Verification (Node.js)
import crypto from 'crypto';

function verifyWebhook(body, signature, secret) {
  const expected = 'sha256=' +
    crypto.createHmac('sha256', secret)
      .update(body)
      .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
Webhook Payload (generate / variation)
{
  "event": "job.completed",
  "jobId": "uuid",
  "external_ref": "gen-001",
  "status": "completed",
  "timestamp": "2025-01-15T10:32:00Z",
  "data": {
    "bookId": "uuid",
    "covers": [
      {
        "coverId": "uuid",
        "signedUrl": "https://...",
        "expiresAt": "2025-01-15T11:32:00.000Z"
      }
    ]
  }
}
Webhook Payload (edit_image)
{
  "event": "job.completed",
  "jobId": "uuid",
  "status": "completed",
  "timestamp": "2025-01-15T10:32:00Z",
  "data": {
    "sourceCoverId": "uuid",
    "covers": [
      {
        "coverId": "uuid",
        "signedUrl": "https://...",
        "expiresAt": "2025-01-15T11:32:00.000Z"
      }
    ]
  }
}
Webhook Payload (upscale / audiobook_cover)
{
  "event": "job.completed",
  "jobId": "uuid",
  "status": "completed",
  "timestamp": "2025-01-15T10:32:00Z",
  "data": {
    "covers": [
      {
        "coverId": "uuid",
        "asset": "full",
        "signedUrl": "https://...",
        "expiresAt": "2025-01-15T11:32:00.000Z"
      }
    ]
  }
}

Important: Webhook image URLs also expire after 1 hour. Download or cache images on your servers as soon as you receive the webhook. If a URL has expired, poll the job endpoint to get a fresh one.

Errors

All errors return a structured JSON object with a code and message.

Error Response Format
{
  "error": {
    "code": "insufficient_credits",
    "message": "Insufficient credits. Need 3, have 1 (0 monthly + 1 overage)."
  }
}
StatusCodeMeaning
400missing_required_fieldsRequired fields are missing from the request body
400missing_book_idbookId is required for cover generation
400invalid_countcount must be an integer between 1 and 5
400invalid_genreGenre value not recognized
400invalid_subgenreSubgenre is not one of the genre's values (the message lists them)
400invalid_template_idtemplateId is not a valid template — see GET /templates
400unsupported_charactersText fields contain unsupported characters (Arabic, Hindi)
400field_too_longOne or more fields exceed their maximum length
400invalid_field_typeA field has the wrong type (expected string)
400invalid_field_valueA field value is not in the allowed set (e.g. target_audience)
400content_policy_violationContent violates the content policy
400missing_promptEdit image requires a non-empty prompt
400missing_cover_imageCover has no preview image (required for edit/audiobook)
400invalid_source_assetsourceAsset must be "preview", "audiobook_preview" or "no_text"
401unauthorizedInvalid or missing API key
402insufficient_creditsNot enough credits for this operation
403forbiddenNo active enterprise subscription for this key
404book_not_foundBook not found or doesn't belong to your account
404cover_not_foundCover not found or doesn't belong to your account
404asset_not_availableThe requested asset has not been generated yet (download or upscale of a missing asset)
404job_not_foundJob not found or doesn't belong to your account
409asset_already_existsThe upscaled version already exists for this cover
422unsupported_genreThe book's genre/subgenre has no cover route. Every current genre has one, so this is a safety net rather than an expected response. No credits are charged.
429concurrency_limitToo many concurrent jobs running
500internal_errorInternal server error

Full Example

End-to-end flow (curl)
# 1. Create a book
curl -X POST https://beyourcover.com/api/v1/books \
  -H "Authorization: Bearer byc_sk_your_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: book-001" \
  -d '{"title":"My Novel","author":"Jane","genre":"fantasy"}'

# 2. Generate covers
curl -X POST https://beyourcover.com/api/v1/covers/generate \
  -H "Authorization: Bearer byc_sk_your_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: req-001" \
  -d '{"bookId":"<bookId>","count":3}'

# 3. Poll for results
curl https://beyourcover.com/api/v1/jobs/<jobId> \
  -H "Authorization: Bearer byc_sk_your_key"

# 4. List the book's covers
curl https://beyourcover.com/api/v1/books/<bookId>/covers \
  -H "Authorization: Bearer byc_sk_your_key"

# 5. Edit a cover
curl -X POST https://beyourcover.com/api/v1/covers/<coverId>/edit-image \
  -H "Authorization: Bearer byc_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Make the sky more dramatic"}'

# 6. Upscale to high resolution
curl -X POST https://beyourcover.com/api/v1/covers/<coverId>/upscale \
  -H "Authorization: Bearer byc_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"sourceAsset":"preview"}'

# 7. Download the upscaled image
curl -L https://beyourcover.com/api/v1/covers/<coverId>/assets/full/download \
  -H "Authorization: Bearer byc_sk_your_key" \
  -o cover-hd.png

Advanced reference

Every other endpoint, grouped by task. Open a group to see its endpoints. You do not need any of them to make and download covers.

More control over generationYour own complete prompt, template style choices, the story motif, several directions in one call

POST/api/v1/covers/generate — advanced options

Extra fields for POST /covers/generate. Send them with the core fields. Each one is optional.

FieldRequiredMaxDescription
promptoptional1500 / 6000Free-text prompt or instructions for the cover. With prompt_mode note (up to 1500 characters) it is merged into the template's prompt. With verbatim (up to 6000) it is the whole cover prompt.
prompt_modeoptional—note (default) or verbatim. verbatim renders prompt exactly as written, with no template, like the app's Prompt mode. Do not send templateId, variant_overrides or motif_id with it (400 prompt_mode_conflict).
variant_overridesoptional—Style choices for the pinned templateId, as axis → option, e.g. {"titleColor": "bone-white"}. The axes and options are the template's variants from GET /templates; an axis you leave out is picked by the engine. Errors: variant_overrides_requires_template, invalid_variant_axis, invalid_variant_value (all 400).
motif_idoptional—The motif the cover shows, from GET /books/:bookId/motifs. Applies to templates that render a motif. Errors: 404 motif_not_found, 400 motif_not_used (the pinned template renders no motif).
directionsoptional—1–5 cover directions. The art director designs one new cover from each, as the app's Pick several directions; 1 credit each. Leave count out. It takes no templateId, variant_overrides, motif_id or verbatim prompt. Errors: directions_conflict, invalid_directions, direction_too_long, count_directions_mismatch (all 400).
webhookUrloptional500Per-job webhook override. Falls back to the URL configured in your dashboard.
Example Request — several directions
{
      "bookId": "550e8400-e29b-41d4-a716-446655440000",
      "directions": [
        "A lone lighthouse in a storm, cold blue light",
        "A close-up of an old brass key on dark wood"
      ]
    }

GET/api/v1/books/:bookId/motifs

List the book's motif candidates: the story objects, symbols and places the engine took from its summary, plus any added in the app. Pass one id as motif_id on POST /covers/generate. A new book has none until its first generation. Free.

Response 200
{
      "bookId": "uuid",
      "motifs": [
        {
          "id": "uuid",                 // pass as motif_id
          "label": "A brass key",
          "description": "an old brass key hanging on a nail",
          "type": "object",             // a kind or a composition role
          "user_added": false
        }
      ],
      "allowed_types": ["object", "place", "symbol"]
    }

Errors: 404 book_not_found.

Change a finished coverVariations, AI edits, text removal, a new person on the cover, the audiobook version

POST/api/v1/covers/:coverId/variations

Generate new cover variations using the same prompt and style as the source cover. Async job — returns 202. The variations render in parallel, and each one is a new cover of the same book. The MCP tool create_cover_variations runs the same operation.

FieldRequiredDescription
coverIdpathUUID of the source cover
countoptionalNumber of variations (1–5, default 1). Each costs 1 credit.
external_refoptionalYour internal ID
webhookUrloptionalPer-job webhook override
Example Request
curl -X POST https://beyourcover.com/api/v1/covers/<coverId>/variations \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{"count": 3}'
Headers (optional)
Idempotency-Key: unique-request-id
Response 202
{
      "jobId": "uuid",
      "status": "queued",
      "operation": "variation",
      "external_ref": null,
      "created_at": "2025-01-15T10:30:00Z",
      "poll_url": "/api/v1/jobs/uuid"
    }
Job Result (when completed)
{
      "sourceCoverId": "uuid",
      "covers": [
        {
          "coverId": "uuid",
          "signedUrl": "https://...",
          "expiresAt": "2025-01-15T11:30:00.000Z",
          "coverStyle": "cinematic-drama"
        }
      ]
    }

POST/api/v1/covers/:coverId/edit-image

Edit a cover image using a text prompt. Creates a modified version of the existing cover. Async job — returns 202. Costs 1 credit.

FieldRequiredMaxDescription
coverIdpath—UUID of the cover to edit
promptrequired1000Description of the changes to make (e.g. "make the sky more dramatic")
external_refoptional—Your internal ID
webhookUrloptional—Per-job webhook override
Example Request
curl -X POST https://beyourcover.com/api/v1/covers/<coverId>/edit-image \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{"prompt": "Make the background darker and add more stars"}'
Headers (optional)
Idempotency-Key: unique-request-id
Response 202
{
      "jobId": "uuid",
      "status": "queued",
      "operation": "edit_image",
      "credits_reserved": 1,
      "included_format": false,
      "external_ref": null,
      "created_at": "2025-01-15T10:30:00Z",
      "poll_url": "/api/v1/jobs/uuid"
    }
Job Result (when completed)
{
      "sourceCoverId": "uuid",
      "covers": [
        {
          "coverId": "uuid",
          "signedUrl": "https://...",
          "expiresAt": "2025-01-15T11:30:00.000Z",
          "coverStyle": "cinematic-drama"
        }
      ]
    }

POST/api/v1/covers/:coverId/remove-text

Remove all rendered text from a cover — title, author, and every other word — producing the text-free no_text asset on the same cover. The original preview is left untouched. Async job — returns 202. Costs 1 credit, or 0 for the book's first text-free cover when its marketing kit was opened this month: the 202 then says credits_reserved: 0 and included_format: true, and a failed job does not give the included render back. Returns 409 asset_already_exists if the no_text asset already exists, or 409 operation_in_progress if a job for this cover is still running — poll that job rather than starting a second one, which would charge again and overwrite the first result. Upscale the result via the upscale endpoint with sourceAsset: "no_text".

FieldRequiredDescription
coverIdpathUUID of the cover
regenerateoptionaltrue replaces an existing text-free asset with a new run, charged like any run. Without it, an existing asset returns 409 asset_already_exists.
external_refoptionalYour internal ID
webhookUrloptionalPer-job webhook override
Example Request
curl -X POST https://beyourcover.com/api/v1/covers/<coverId>/remove-text \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{}'
Headers (optional)
Idempotency-Key: unique-request-id
Response 202
{
      "jobId": "uuid",
      "status": "queued",
      "operation": "remove_text",
      "credits_reserved": 1,
      "included_format": false,
      "external_ref": null,
      "created_at": "2025-01-15T10:30:00Z",
      "poll_url": "/api/v1/jobs/uuid"
    }
Job Result (when completed)
{
      "covers": [
        {
          "coverId": "uuid",
          "asset": "no_text",
          "signedUrl": "https://...",
          "expiresAt": "2025-01-15T11:30:00.000Z"
        }
      ]
    }

POST/api/v1/covers/:coverId/replace-person

Replace a person on a cover with the person in an uploaded photo. Two image models each make one new cover. Async job — returns 202. Costs 1 credit for the call, refunded only when both models fail.

Consent on every call. Send person_consent: true on every call. By sending it you confirm that you are the person in the photo or have that person's permission, and that the person is an adult. Without it the call returns 400 person_consent_required before any upload is read or any credit is reserved. We store each confirmation with the API key and the workspace.

Upload the photo with POST /api/v1/uploads first. The photo is checked before any credit is reserved: content policy, one person, no likely minor and no public figure. A refused photo returns person_photo_refused with a reason and is deleted.

FieldRequiredMaxDescription
photo_upload_idrequired—The upload_id of the person photo
person_consentrequired—Must be true on every call (see above)
targetoptional120Which person to replace (e.g. "the woman on the left"). Defaults to the main person.
external_refoptional—Your internal ID
webhookUrloptional—Per-job webhook override
Example Request
curl -X POST https://beyourcover.com/api/v1/covers/<coverId>/replace-person \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{"photo_upload_id": "<upload_id>", "person_consent": true, "target": "the man on the right"}'
Job Result (when completed)
{
      "sourceCoverId": "uuid",
      "covers": [
        { "coverId": "uuid", "signedUrl": "https://...", "model": "GPT Image 2.5" },
        { "coverId": "uuid", "signedUrl": "https://...", "model": "Nano banana 2.1" }
      ],
      "failed_models": []
    }

POST/api/v1/covers/:coverId/audiobook-cover

Generate a square (1:1) audiobook version of a cover. Async job — returns 202. Costs 1 credit, or 0 for the book's first audiobook cover when its marketing kit was opened this month: the 202 then says credits_reserved: 0 and included_format: true, and a failed job does not give the included render back. Requires the cover to have a preview image.

FieldRequiredDescription
coverIdpathUUID of the cover
regenerateoptionaltrue replaces an existing audiobook asset with a new run, charged like any run. Without it, an existing asset returns 409 asset_already_exists.
external_refoptionalYour internal ID
webhookUrloptionalPer-job webhook override
Example Request
curl -X POST https://beyourcover.com/api/v1/covers/<coverId>/audiobook-cover \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{}'
Headers (optional)
Idempotency-Key: unique-request-id
Response 202
{
      "jobId": "uuid",
      "status": "queued",
      "operation": "audiobook_cover",
      "credits_reserved": 1,
      "included_format": false,
      "external_ref": null,
      "created_at": "2025-01-15T10:30:00Z",
      "poll_url": "/api/v1/jobs/uuid"
    }
Job Result (when completed)
{
      "covers": [
        {
          "coverId": "uuid",
          "asset": "audiobook_preview",
          "signedUrl": "https://...",
          "expiresAt": "2025-01-15T11:30:00.000Z"
        }
      ]
    }
Print-quality filesUpscale to print resolution, or export at an exact size and DPI

POST/api/v1/covers/:coverId/upscale

Upscale a cover asset to high resolution. Async job — returns 202. Free — consumes no credits. Returns 409 if the upscaled version already exists.

FieldRequiredDescription
coverIdpathUUID of the cover
sourceAssetrequiredWhich asset to upscale: preview → full, audiobook_preview → audiobook_full, or no_text → no_text_full
external_refoptionalYour internal ID
webhookUrloptionalPer-job webhook override
Example Request
curl -X POST https://beyourcover.com/api/v1/covers/<coverId>/upscale \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{"sourceAsset": "preview"}'
Headers (optional)
Idempotency-Key: unique-request-id
Response 202
{
      "jobId": "uuid",
      "status": "queued",
      "operation": "upscale",
      "credits_reserved": 0,
      "included_format": false,
      "external_ref": null,
      "created_at": "2025-01-15T10:30:00Z",
      "poll_url": "/api/v1/jobs/uuid"
    }
Job Result (when completed)
{
      "covers": [
        {
          "coverId": "uuid",
          "asset": "full",
          "signedUrl": "https://...",
          "expiresAt": "2025-01-15T11:30:00.000Z"
        }
      ]
    }

POST/api/v1/covers/:coverId/exports

Advanced export, as in the app: a JPG or PNG at an exact pixel width or print size, with the DPI written into the file. Synchronous — returns 200 with a signed link (1 hour) that downloads under a file name. Free — consumes no credits. The first export of a cover builds a master about 3200 px wide with the AI upscaler, which can take a minute; later exports reuse it. Returns 409 text_layout_not_supported for a cover whose text was set in the app's text editor. A trial workspace gets a watermarked file.

FieldRequiredDescription
coverIdpathUUID of the cover
formatrequiredjpg or png
variantoptionalfull (default) or no_text, after remove-text
shapeoptionaloriginal (default, the cover's 5:8) or 2:3, widened at both sides. Sizes are those of the 2:3 file.
width_pxoptionalExact width in pixels, 200–8192. Send this or print.
dpioptionalWith width_px: DPI written into the file (72, 150, 300 or 600; default 300)
printoptional{ width_in, height_in?, dpi? } in inches (1–24). With height_in the cover fits inside the box at its own aspect ratio.
Example Request
curl -X POST https://beyourcover.com/api/v1/covers/<coverId>/exports \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{"format": "jpg", "print": {"width_in": 6.125, "dpi": 300}}'
Response 200
{
      "coverId": "uuid",
      "variant": "full",
      "format": "jpg",
      "widthPx": 1838,
      "heightPx": 2941,
      "dpi": 300,
      "printWidthIn": 6.13,
      "printHeightIn": 9.8,
      "isUpscale": false,
      "clamped": false,
      "watermarked": false,
      "masterCreated": true,
      "fileName": "My_Book_cover_1838x2941.jpg",
      "signedUrl": "https://...",
      "expiresAt": "2025-01-15T11:30:00.000Z"
    }
Print wraps for paperbacksBack cover, spine and front as one print-ready PDF for Amazon KDP

GET/api/v1/wraps/calculate

Print geometry for an Amazon KDP wrap: spine width, the full canvas in inches and pixels at 300 DPI, safe margins, the barcode zone, and whether the spine is thick enough for text. Free, no credits, no side effects — call it before creating a wrap so you know the numbers KDP will check on upload. POST /wraps and the MCP calculate_kdp_wrap tool apply the same trim, page and paper rules, so a size this refuses cannot be created either.

Query paramRequiredDescription
trimWidth, trimHeightrequiredTrim size in inches, e.g. 6 × 9
pageCountrequiredInterior pages — this drives spine width
paperTyperequiredwhite | cream | premium_color | standard_color
bindingTypeoptionalpaperback (default) | hardcover
Example Request
curl "https://beyourcover.com/api/v1/wraps/calculate?trimWidth=6&trimHeight=9&pageCount=300&paperType=white" \
      -H "Authorization: Bearer byc_sk_your_key"
Response 200
{
      "spineWidth": 0.6756,
      "fullWidth": 12.9256,
      "fullHeight": 9.25,
      "pixelWidth": 3878,
      "pixelHeight": 2775,
      "dpi": 300,
      "bleed": 0.125,
      "safeTextMargin": 0.125,
      "minFontSize": 7,
      "canHaveSpineText": true,
      "barcodeZone": { "x": 3.75, "y": 7.55, "width": 2, "height": 1.2 }
    }

POST/api/v1/wraps

Create a full print wrap (back + spine + front) from an existing cover. Generates back-cover artwork, extends your cover art out to the full front print panel, lays your back-cover copy out on the back, and returns a composited flat, a preview, and a print-ready PDF. Async job — returns 202. Costs 2 credits for a solid back and 4 for the AI art styles. If the job fails before the wrap is created, the full amount is refunded. If it fails after — the wrap is already in your account, listable and free to re-render — the credits stand. The book's first wrap costs 0 credits when its marketing kit was opened this month: the 202 then says credits_reserved: 0 and included_format: true, and a failed job does not give the included wrap back. Amazon KDP only.

You supply text, not coordinates: a preset places the blocks, auto-fit sizes them to the space available, and the result is checked against the safe margin and the barcode zone. Anything the layout engine had to change comes back in warnings. The layout is saved in the same schema the browser wrap editor uses, so the design opens there for hand adjustment afterwards.

FieldRequiredDescription
coverIdrequiredThe cover to use as the front panel
trimWidth, trimHeight, pageCount, paperTyperequiredSame values as /wraps/calculate — validated identically
designStyleoptionalsolid (2 credits, flat colour, no AI) | textured | continuation (panoramic wrap-around) | complementary (default)
presetoptionalstandard (default: tagline + blurb + bio) | blurb-only | minimal
fontFamilyoptionalFont for the back-cover text and the spine lettering, from the wrap editor's list: Arimo | Playfair Display | Merriweather | Montserrat | Dancing Script | Roboto Slab | Open Sans | Source Code Pro | Poppins | Cinzel | Cormorant Garamond | Great Vibes | Abril Fatface | Libre Baskerville. Default Arimo. Auto-fit measures the text in this font.
tagline, blurb, authorBiooptionalBack-cover copy (200 / 2000 / 600 chars). Blocks with no text are omitted.
spineTitle, spineAuthoroptionalSpine lettering. Dropped with a warning when the page count makes the spine too thin.
solidColor, spineColoroptionalHex colours. Omitted, both are sampled from the artwork.
customInstructionsoptionalExtra art direction for the back cover (max 500 chars). Ignored for solid.
bindingTypeoptionalpaperback (default) | hardcover
Example Request
curl -X POST https://beyourcover.com/api/v1/wraps \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{
        "coverId": "<coverId>",
        "trimWidth": 6,
        "trimHeight": 9,
        "pageCount": 300,
        "paperType": "white",
        "designStyle": "complementary",
        "preset": "standard",
        "fontFamily": "Playfair Display",
        "tagline": "One town. One lie.",
        "blurb": "When the river froze in November...",
        "spineTitle": "THE THAW",
        "spineAuthor": "M. R. VALE"
      }'
Response 202
{
      "jobId": "uuid",
      "status": "queued",
      "operation": "wrap",
      "credits_reserved": 4,
      "included_format": false,
      "external_ref": null,
      "created_at": "2025-01-15T10:30:00Z",
      "poll_url": "/api/v1/jobs/uuid"
    }
Job Result (when completed)
{
      "wrapId": "uuid",
      "bookId": "uuid",
      "coverId": "uuid",
      "designStyle": "complementary",
      "preset": "standard",
      "dims": { "spineWidth": 0.6756, "fullWidth": 12.9256, "fullHeight": 9.25, "...": "..." },
      "layout_report": {
        "blocks": [
          { "id": "tagline", "fontSize": 18, "heightPx": 113, "overflowsPanel": false }
        ],
        "warnings": []
      },
      "warnings": [],
      "assets": {
        "wrap_preview": { "signedUrl": "https://...", "expiresAt": "..." },
        "wrap_flat":    { "signedUrl": "https://...", "expiresAt": "..." },
        "wrap_pdf":     { "signedUrl": "https://...", "expiresAt": "..." }
      }
    }

GET/api/v1/wraps

List the wraps on your account, newest first: all of them, one book's (bookId), or the ones built from one cover (coverId). Free. Wraps made in the browser wrap editor are listed too, Amazon KDP and Barnes & Noble designs alike, so you can find one and re-render it here. Metadata only — GET /wraps/{wrapId} returns a wrap's text and signed links. Paginate with limit (default 20, max 100) and cursor (the previous page's next_cursor).

Example Request
curl "https://beyourcover.com/api/v1/wraps?bookId=<bookId>" \
      -H "Authorization: Bearer byc_sk_your_key"
Response 200
{
      "wraps": [
        {
          "wrapId": "uuid",
          "bookId": "uuid",
          "coverId": "uuid",
          "designName": "Design (Dust jacket)",
          "designStyle": "complementary",
          "printProvider": "bn",
          "bindingType": "dust-jacket",
          "trimWidth": 6,
          "trimHeight": 9,
          "pageCount": null,
          "paperType": null,
          "spineWidth": 1,
          "flapWidth": 3.5,
          "created_at": "2025-01-15T10:30:00Z",
          "updated_at": "2025-01-16T09:00:00Z"
        }
      ],
      "next_cursor": null
    }

GET/api/v1/wraps/{wrapId}

Fetch a wrap with fresh one-hour signed URLs on every asset, plus its layout_state and the font its text is set in. Wraps created in the browser wrap editor are readable here too — there is one wrap per design, whichever surface made it. A Barnes & Noble design has no page count: its spine width was entered by hand.

Response 200
{
      "wrapId": "uuid",
      "bookId": "uuid",
      "designName": "API",
      "designStyle": "complementary",
      "printProvider": "kdp",
      "bindingType": "paperback",
      "trimWidth": 6,
      "trimHeight": 9,
      "pageCount": 300,
      "spineWidth": 0.6756,
      "flapWidth": null,
      "fontFamily": "Playfair Display",
      "layout_state": { "textBlocks": [ ... ], "spineRotation": 90, "...": "..." },
      "assets": {
        "wrap_preview": { "signedUrl": "https://...", "expiresAt": "..." },
        "wrap_flat":    { "signedUrl": "https://...", "expiresAt": "..." },
        "wrap_pdf":     { "signedUrl": "https://...", "expiresAt": "..." },
        "back_art":     { "signedUrl": "https://...", "expiresAt": "..." }
      }
    }

POST/api/v1/wraps/{wrapId}/render

Re-typeset an existing wrap on the artwork it already has. Free and synchronous — no model runs, so it returns 200 with the new assets instead of a job to poll. Fixing a typo in a blurb should not cost a second wrap.

Send only the fields you want to change. The stored layout is patched in place: nothing moves, and every field you leave out keeps its current text. Send fontFamily to set the tagline, blurb, author bio and spine in another of the editor's fonts; left out, the wrap keeps its font, and text blocks the author added in the editor keep theirs. To lay the text out again, name a preset. A wrap with text blocks or images that the preset does not own — one laid out in the wrap editor — is patched instead, and the response carries a preset_not_applied warning. To render a layout you positioned yourself, send a complete layoutState (as returned by GET /wraps/{wrapId}); it takes precedence over preset, fontFamily and the text fields. Its image layers follow the editor's rules: at most 5 per design (else too_many_image_layers), and each new image is moderated (else content_policy_violation). The wrap_pdf link downloads the file under the editor's export name instead of opening it in a tab.

Works on every wrap on your account, including Barnes & Noble designs made in the editor: the wrap is drawn at its own print size, with its hand-entered spine and a dust jacket's flaps.

Example Request
curl -X POST https://beyourcover.com/api/v1/wraps/<wrapId>/render \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{
        "blurb": "A tighter, shorter blurb.",
        "fontFamily": "Cormorant Garamond",
        "spineTitle": "THE THAW"
      }'
Response 200
{
      "wrapId": "uuid",
      "bookId": "uuid",
      "dims": { "...": "..." },
      "layout_report": { "blocks": [ ... ], "warnings": [] },
      "warnings": [],
      "assets": {
        "wrap_preview": { "signedUrl": "https://...", "expiresAt": "..." },
        "wrap_flat":    { "signedUrl": "https://...", "expiresAt": "..." },
        "wrap_pdf":     { "signedUrl": "https://...", "expiresAt": "..." }
      }
    }

POST/api/v1/wraps/{wrapId}/extend-front

Re-run the AI extension of the front panel and recompose the wrap with its existing back artwork and layout. Free — the wrap was already paid for — but it runs an AI chain, so it returns 202 with a job to poll. Use it when the extended front came back with a visible seam, a framed-picture look, or an unfilled band (the wrap job reports the last of those as a front_outpaint_incomplete warning). Each run is a fresh roll of the model, exactly like the wrap editor's "re-extend front". A Barnes & Noble design from the editor is extended to its own geometry, a dust jacket's front flap included.

Example Request
curl -X POST https://beyourcover.com/api/v1/wraps/<wrapId>/extend-front \
      -H "Authorization: Bearer byc_sk_your_key"
Response 202
{
      "jobId": "uuid",
      "status": "queued",
      "operation": "wrap_extend_front",
      "credits_reserved": 0,
      "poll_url": "/api/v1/jobs/uuid"
    }
Start from your own imagesUpload an image, design a new cover from references, or import a finished cover

POST/api/v1/uploads

Presign an image upload. Free — uses no credits. Returns an upload_id and a 5-minute upload_url; PUT the bytes to that URL with the same Content-Type, then pass the upload_id to generate-reference, or, with purpose: "cover_import", to cover import. The storage key is server-generated, and size, dimension, and content-policy checks all run when the upload is consumed — before any credit is reserved.

FieldRequiredDescription
content_typerequiredimage/jpeg, image/png, or image/webp
size_bytesoptionalDeclared size; rejected early when over the limit
purposeoptionalreference (default, up to 5 MB) or cover_import (a finished cover for cover import, up to 10 MB)
Example Request
curl -X POST https://beyourcover.com/api/v1/uploads \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{"content_type": "image/jpeg"}'

    # then upload the bytes to the returned URL
    curl -X PUT "<upload_url>" \
      -H "Content-Type: image/jpeg" \
      --data-binary @reference.jpg
Response 200
{
      "upload_id": "<user-id>/api-uploads/ref-<uuid>.jpg",
      "upload_url": "https://...",
      "expires_at": "2025-01-15T10:35:00.000Z",
      "max_bytes": 5242880,
      "allowed_types": ["image/jpeg", "image/png", "image/webp"]
    }

POST/api/v1/covers/generate-reference

Generate a NEW cover from 1–4 reference images plus art direction. Async job — returns 202. Costs 1 credit. A vision model reads the references and writes a self-contained prompt which the text-to-image chain renders — the image model never receives the reference images, so the result is an original cover in the referenced style. Covers made this way have a null coverStyle (they sit outside the template system).

FieldRequiredDescription
bookIdrequiredThe book the new cover belongs to
instructionsrequiredArt direction (max 1000 chars): what to take from the references, what to change
reference_upload_idsoptionalupload_ids from /api/v1/uploads (1–4 refs total)
reference_cover_idsoptionalcoverIds of existing covers to use as references
external_refoptionalYour internal ID
webhookUrloptionalPer-job webhook override
Example Request
curl -X POST https://beyourcover.com/api/v1/covers/generate-reference \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{
        "bookId": "<bookId>",
        "instructions": "This palette and the hand-lettered title, but a winter scene",
        "reference_upload_ids": ["<upload_id>"]
      }'
Headers (optional)
Idempotency-Key: unique-request-id
Response 202
{
      "jobId": "uuid",
      "status": "queued",
      "operation": "generate_reference",
      "external_ref": null,
      "created_at": "2025-01-15T10:30:00Z",
      "poll_url": "/api/v1/jobs/uuid"
    }
Job Result (when completed)
{
      "bookId": "uuid",
      "covers": [
        {
          "coverId": "uuid",
          "coverStyle": null,
          "prompt": "...",
          "signedUrl": "https://...",
          "expiresAt": "2025-01-15T11:30:00.000Z"
        }
      ]
    }

POST/api/v1/covers/import

Import a finished front cover the workspace owns, as the app's External Cover Studio does. Free for a cover that is already 5:8 (a crop). 1 credit for any other shape: an AI outpaint extends it to 5:8, and the credit is refunded if that fails. A short balance answers 402 before anything is saved. Upload it first with purpose: "cover_import". The image (JPEG, PNG or WebP, 256–4096 px per side) and the book text are checked and moderated before anything is saved, and the cover is adapted to the 5:8 working format. Synchronous: the response carries the new coverId, which upscale, remove-text, audiobook-cover, exports, replace-person and wraps take like any other. Admins and editors only.

FieldRequiredDescription
upload_idrequiredA cover_import upload, after the bytes were PUT
rights_confirmedrequiredtrue: you have the rights to the image and will review every output before publishing
book_id or bookone ofAn existing book, or the fields of a new one as POST /api/v1/books takes them (title, author, genre, …)
Example Request
curl -X POST https://beyourcover.com/api/v1/uploads \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{"content_type": "image/png", "purpose": "cover_import"}'

    curl -X PUT "<upload_url>" -H "Content-Type: image/png" --data-binary @cover.png

    curl -X POST https://beyourcover.com/api/v1/covers/import \
      -H "Authorization: Bearer byc_sk_your_key" \
      -H "Content-Type: application/json" \
      -d '{"upload_id": "<upload_id>", "rights_confirmed": true,
           "book": {"title": "The Salt Road", "author": "Ana Vela", "genre": "fantasy"}}'
Response 201
{
      "coverId": "c3d4e5f6-...",
      "bookId": "a1b2c3d4-...",
      "jobId": "e5f6a7b8-...",
      "book_created": true,
      "normalized": true,
      "outpainted": false,
      "image": { "width": 1600, "height": 2560 },
      "credits_charged": 0,
      "credits_refunded": 0
    }
Agency clientsGroup books by the client you design them for

GET/api/v1/clients

List your workspace's agency clients, newest first, with the number of books filed under each. These are the same clients as the client panel in the app. Archived clients appear only with ?include_archived=true. Free. To list one client's books, send ?client_id= to GET /books.

Response 200
{
      "clients": [
        {
          "clientId": "uuid",
          "name": "Northwind Press",
          "slug": "northwind-press",
          "notes": null,
          "archived": false,
          "book_count": 12,
          "created_at": "2026-10-01T09:00:00Z"
        }
      ]
    }

POST/api/v1/clients

Create an agency client. Free. Admins and editors can create clients; a reviewer gets 403 forbidden_role. Returns 201 with the client in the list shape above. Add a logo in the app.

FieldRequiredMaxDescription
namerequired120The client's name
notesoptional2000Your own notes about the client
Example Request
{ "name": "Northwind Press", "notes": "Fantasy imprint" }

PUT/api/v1/books/:bookId/client

File a book under an agency client, or send null to move it back to Unassigned. Free. Works on any book in the workspace, as in the app.

Example Request
{ "clientId": "uuid" }   // or { "clientId": null }
Response 200
{ "bookId": "uuid", "clientId": "uuid" }

Errors: 400 invalid_body, 404 book_not_found, 404 client_not_found.

Manage your libraryEdit a book, hide or favourite a cover, list jobs, read one cover

PATCH/api/v1/books/:bookId

Edit a book's details, as the edit form in the app does, or hide it from the shelf. Free. Send only the fields to change; null clears an optional field. A title is required, and an author is required except for Planner & Journal books. A new genre keeps the stored subgenre only when it belongs to that genre. Covers you generate later use the new details; existing covers do not change. Returns the book in the list shape above.

Request Body
{
      "title": "The Lamp",          // any of: title, author, subtitle, summary,
      "subgenre": "epic-fantasy",   // genre, subgenre, target_audience,
      "visibility": "hidden"        // planner_year, visibility (default | hidden)
    }

Errors: 404 book_not_found, 400 unknown_fields, no_changes, missing_required_fields, invalid_genre, invalid_subgenre, content_policy_violation and the field checks of POST /api/v1/books.

PATCH/api/v1/covers/:coverId

Hide a cover or favorite it, as the eye and heart buttons do in the app. Free. favorite makes the cover represent its book on the shelf (the newest favorite wins); hidden takes it out of the covers list (nothing is deleted); default undoes either.

Request Body
{ "visibility": "favorite" }   // default | favorite | hidden
Response 200
{ "coverId": "uuid", "bookId": "uuid", "visibility": "favorite" }

Errors: 404 cover_not_found, 400 invalid_visibility.

GET/api/v1/jobs

List your jobs, newest first. Result payloads are omitted here — poll GET /api/v1/jobs/:jobId for signed cover URLs. Optional status, operation and source filters.

The list also shows the jobs your team runs in the web app, because they spend the same credits. Each job's source is api (this API or the MCP server) or web. Pass source=api to list only the jobs you started here. The MCP tool list_jobs reads the same list, with the same filters and cursor.

Query Parameters
?status=queued|processing|completed|failed   // optional
    ?operation=generate|variation|edit_image|upscale|audiobook_cover|remove_text|generate_reference|replace_person|wrap|wrap_extend_front|import_cover|generate_single|edit_font|convert_style|layerize_text|remove_text_variants|audiobook_generate|marketing_resource|kit_regeneration|brand_kit_extract|series_next_book|wrap_cover_generate|wrap_cover_regenerate|audiobook_upscale|refine_cover|wrap_back_cover_generate|wrap_cover_export   // optional
    ?source=api|web   // optional
    ?limit=20      // optional, 1–100 (default 20)
    ?cursor=<opaque>   // optional, from a previous next_cursor
Response 200
{
      "jobs": [
        {
          "jobId": "uuid",
          "status": "completed",
          "operation": "generate",
          "source": "api",
          "external_ref": "gen-001",
          "credits_used": 2,
          "bookId": "uuid",
          "error": null,
          "created_at": "...",
          "started_at": "...",
          "completed_at": "...",
          "poll_url": "/api/v1/jobs/uuid"
        }
      ],
      "next_cursor": "eyJjIjoi…"   // null on the last page
    }

Errors: 400 invalid_status_filter, 400 invalid_operation_filter, 400 invalid_source_filter, 400 invalid_limit, 400 invalid_cursor.

GET/api/v1/covers/:coverId

Get cover metadata and asset availability. Returns fresh signed URLs for any available assets as a convenience. For stable retrieval, use GET /.../download. No credit charge.

ParamInDescription
coverIdpathUUID of the cover (from generate or variation results)
Response 200
{
      "coverId": "uuid",
      "bookId": "uuid",
      "coverStyle": "cinematic-drama",
      "assets": {
        "preview": {
          "available": true,
          "signedUrl": "https://...",
          "expiresAt": "2025-01-15T11:30:00.000Z"
        },
        "full": {
          "available": false
        },
        "audiobook_preview": {
          "available": false
        },
        "audiobook_full": {
          "available": false
        }
      }
    }

Tip: Use this endpoint to inspect which assets exist on a cover and retrieve fresh temporary URLs. For a stable retrieval path, use the asset download endpoint.

Asset types: preview is the standard cover image, full is the upscaled version, audiobook_preview is the square audiobook cover, audiobook_full is the upscaled audiobook cover, no_text is the text-free artwork, and no_text_full is its upscaled version.

For developers & AI agents

Besides this REST API, BeYourCover runs a remote Model Context Protocol server, so Claude and other MCP-capable agents can generate covers through the same pipeline, spending the same credits. It is served over Streamable HTTP at https://beyourcover.com/api/mcp and accepts either a byc_sk_ API key or an OAuth 2.1 token, so a connector can configure itself with nothing pre-shared.

Full documentation — connection snippets for each client, all 32 tools with their credit costs, the async job model and how print wraps work — is on the MCP server page.

The machine-readable files are listed on the MCP page: the OpenAPI 3.1 spec, this reference as Markdown, the API catalog, the agent auth guide, Markdown copies of public pages, and the health check.

Generation coverage for agentsGET /api/v1/capabilities

GET/api/v1/capabilities

Machine-readable generation coverage and pricing. The genres array reports, per genre and subgenre, exactly what the generator will accept — today every genre and subgenre. A pair marked unsupported would return 422 unsupported_genre. The coverage is derived from the same routing logic the generate endpoint uses, so it never over-promises.

Response 200 (abridged)
{
      "api_version": "v1",
      "generation_engine": "v2",
      "genres": [
        {
          "genre": "Romance",
          "slug": "romance",
          "supported": true,
          "subgenre_auto_detected": true,   // detected from title/summary if omitted
          "subgenres": [
            { "value": "contemporary", "label": "Contemporary Romance", "supported": true },
            { "value": "romantasy",    "label": "Romantasy",            "supported": true }
          ]
        },
        {
          "genre": "Poetry",
          "slug": "poetry",
          "supported": true,
          "subgenre_auto_detected": false,   // one implicit subgenre, nothing to detect
          "subgenres": [{ "value": "poetry", "label": "Poetry", "supported": true }]
        }
      ],
      "operations": {
        "generate":        { "credits_per_image": 1, "description": "..." },
        "variation":       { "credits_per_image": 1, "description": "..." },
        "edit_image":      { "credits_per_image": 1, "description": "..." },
        "audiobook_cover": { "credits_per_image": 1, "description": "..." },
        "upscale":         { "credits_per_image": 0, "description": "Free." }
      },
      "limits": { "max_covers_per_generate": 5 },
      "coverage_note": "..."
    }

Frequently Asked Questions

What is the BeYourCover Book Cover Generation API?

The BeYourCover API is a REST API that lets you generate professional AI book covers programmatically. You send book details (title, author, genre, summary) and receive high-quality cover images. It supports generation (also from reference images you upload), variations, editing, replacing a person on a cover with an uploaded photo, text-free covers, upscaling, exports as JPG or PNG at an exact pixel or print size with the DPI written into the file, audiobook covers, and full print wraps for Amazon KDP with a print-ready PDF.

What programming languages can I use with the API?

The API uses standard REST conventions with JSON payloads, so it works with any language that can make HTTP requests — Python, JavaScript/Node.js, Ruby, PHP, Go, Java, C#, and more. All you need is an HTTP client and your API key.

How does API pricing work?

API access is on every Enterprise plan: Starter (150 credits a month), Growth (500) and Scale (1,500). Prices are on the Enterprise Plans page, in your currency. Credits per image: cover generation 1, variation 1, edit 1, audiobook cover 1, text removal 1, replace person 1, reference-image generation 1, cover import with an AI outpaint to 5:8 1, upscale free, cover import (already 5:8) free, re-run of a wrap's front extension free. Replace person is priced per call: one call returns two versions. A full print wrap costs 2 credits with a solid back cover and 4 with AI artwork; re-typesetting it later is free. Overage credits are available on all plans. The same plans include the marketing kit (a launch campaign built from a book's cover, with brand kits and client review links); it runs in the browser only and is not available through the API or the MCP server. A book opened in the marketing kit includes one audiobook cover, one text-free cover and one print wrap a month, and the API and the MCP server honour them: the book's first of each costs 0 credits.

Can I generate book covers in bulk?

Yes. The API supports concurrent requests (5–20 depending on your plan) and asynchronous job processing. You can submit multiple generation requests and poll or use webhooks to collect results. This makes it ideal for batch processing entire book catalogs.

Can I control how each cover looks through the API?

Yes. By default the art director that designs your first covers in the app designs them for each book. For a fixed look, use the same controls as the app's Create tab: pin a template, then choose its style options (title colour, font style and the other variants the template list returns). Pick the story motif the cover shows from the book's motif list. Or send your own complete prompt with prompt_mode "verbatim" (up to 6,000 characters) and it renders as written, with no template. The REST API and the MCP server both take these controls.

Can I ask the API for specific cover ideas?

Yes. Send 1 to 5 directions (for example "a lighthouse in a storm, painterly") and the app's art director designs one cover from scratch for each, as Pick several directions does in the app. Each cover costs 1 credit, and the covers render in parallel. The REST API and the MCP server both offer it.

What book cover formats and sizes does the API support?

The API generates standard ebook covers (vertical aspect ratio) and square audiobook covers (1:1). You can also create a text-free version of any cover (all rendered text removed — useful for audiobook platforms and A+ content) and a full print wrap for Amazon KDP: back cover, spine and front in one design. A cover, its text-free version and its audiobook version can each be upscaled to print resolution. To replace a text-free or audiobook version, ask again with regenerate set to true; it is charged as a new run. Every file downloads through a signed URL: the cover images, and for a print wrap a preview, a full-resolution flat image and a KDP-ready PDF.

How do I get started with the API?

Sign up for an API plan on the Enterprise Plans page, get your API key from the Account Dashboard, and make your first request. The documentation on this page covers authentication, all 30 endpoints, webhooks, and includes a complete curl example.

Can AI agents like Claude use the API?

Yes. BeYourCover runs a remote MCP (Model Context Protocol) server at https://beyourcover.com/api/mcp over Streamable HTTP. Agents connect with the same Bearer API keys, or sign in with OAuth 2.1 in clients that support custom connectors. It exposes 32 task-shaped tools: create_book_and_generate_covers, generate_covers_for_book, generate_covers_from_directions, get_generation_status, list_templates_for_genre, get_credit_balance, list_books, list_covers_for_book, list_jobs, list_book_motifs, list_clients, create_client, assign_book_to_client, update_book, set_cover_visibility, download_cover, export_cover, upscale_cover, edit_cover, create_cover_variations, replace_person_on_cover, remove_cover_text, upload_reference_image, import_cover, generate_cover_from_reference, create_audiobook_cover, list_wraps, get_wrap, create_full_wrap, update_wrap_text, extend_wrap_front and calculate_kdp_wrap. With them an agent can create books, generate covers (also from reference images), make variations of them, edit them, remove their text, upscale them to print resolution, make audiobook covers, and build a complete print wrap — back artwork, back-cover copy typeset in any of the wrap editor's fonts, spine and extended front panel — that comes back as a KDP-ready PDF, so MCP-capable agents can take a paperback from idea to upload without opening the editor. Agents can also list the wraps on the account, including ones made in the editor, read their layout, and re-typeset them or move their text and image blocks for free. They can manage the shelf as the app does: search and filter the books, edit a book's details, hide a book or a cover, and favorite the cover that represents a book — all free — and list the workspace's jobs. get_credit_balance also shows which books still have included marketing-kit renders. There is also a machine-readable capabilities endpoint, an OpenAPI 3.1 spec, an llms.txt, an RFC 9727 API catalog at /.well-known/api-catalog that indexes every API here with its spec, docs and health endpoint, and an auth.md guide at /auth.md that walks an agent through obtaining a credential. Every public page also does Markdown content negotiation: send Accept: text/markdown and you get the page as clean Markdown with frontmatter instead of HTML, so an agent reading the blog, the product guides, the genre pages or the marketplace never has to scrape a layout.

Can the API put a real person's photo on a cover?

Yes. POST /api/v1/covers/{coverId}/replace-person (MCP: replace_person_on_cover) replaces a person on a cover with the person in a photo you upload, and returns two versions for 1 credit. The caller must confirm consent on every call with person_consent: true, which states that the caller is the person in the photo or has that person's permission, and that the person is an adult. A call without it is refused before anything is read or charged. We store each confirmation with the API key or signed-in member and the workspace. Before any credit is spent, the photo is checked: one adult, no public figure, and our content policy.

Can I import a cover I already have through the API?

Yes. POST /api/v1/covers/import (MCP: import_cover) imports a finished front cover you own, as the External Cover Studio does in the app. It is free when the cover is already 5:8, and costs 1 credit when an AI outpaint has to extend it to that shape; the credit is refunded if the outpaint fails. Upload the file with POST /api/v1/uploads and purpose "cover_import" (JPEG, PNG or WebP, 256 to 4096 pixels per side, up to 10 MB), then import it into an existing book or a new one, with rights_confirmed: true. The image and the book text are moderated before anything is saved, and a 402 for a short balance comes before anything is saved. The imported cover then works with every cover endpoint: print wraps, audiobook covers, text-free versions, upscale and export.

Do you support webhooks?

Yes. The API sends HMAC-SHA256 signed webhook events when jobs complete, so you don't need to poll. You can configure a default webhook URL in your dashboard and optionally override it per-job.

Can I use the API for a white-label product?

Yes. The API gives you control over the generation pipeline so you can integrate cover generation into your own product. You remain responsible for reviewing outputs, rights clearance, and any claims you make to your end users about the final files.

Start Building with the Book Cover API

Get your API key and generate your first cover in minutes. Plans start at $79 a month with 150 credits.