---
title: "job.completed"
method: POST
path: "jobCompleted"
---

# job.completed

`POST jobCompleted` (webhook)

Sent to the job's `webhookUrl` (falling back to the API key's default webhook URL) when a job completes. For `generate` jobs the payload has NO `operation` field and `data` is `{ bookId, covers[] }`; for cover-operation jobs (`variation`, `edit_image`, `upscale`, `audiobook_cover`) the payload includes `operation` and `data` is the operation's result (`sourceCoverId` + `covers[]` for variation/edit_image; `covers[]` with an `asset` field for upscale/audiobook_cover). Internal `_storagePath` fields are stripped. Signed with the API key's webhook secret (see partner webhook docs).

## Payload

- WebhookJobCompleted
  - `event` 'job.completed', required
  - `jobId` string, uuid, required
  - `external_ref` string, nullable, required
  - `status` 'completed', required
  - `operation` 'variation' | 'edit_image' | 'upscale' | 'audiobook_cover' | 'remove_text' | 'generate_reference' | 'wrap' | 'wrap_extend_front': Present on every job's webhook except `generate`, whose webhooks omit this field.
  - `timestamp` string, date-time, required
  - `data` union, required: The job's result payload with internal `_storagePath` fields stripped. Note: `covers[].signedUrl` in the webhook is the URL signed at completion time (1-hour expiry); poll GET /api/v1/jobs/{jobId} for a fresh URL.
    - GenerateJobResult: Result payload for `generate` jobs. Only successfully generated covers are included; `credits_used` reflects the successful count.
      - `bookId` string, uuid, required
      - `covers` GeneratedCover[], required
        - `coverId` string, uuid, required: Public id of the cover. Use with the /api/v1/covers/{coverId} endpoints.
        - `signedUrl` string, uri, required: Time-limited signed URL for the preview image (1 hour).
        - `expiresAt` string, date-time, required
        - `coverStyle` string: Template/style id used to render the cover (e.g. a V2 template id).
        - `prompt` string: The generation prompt stored for this cover.
    - CoverOperationJobResult: Result payload for `variation` and `edit_image` jobs. Each entry is a NEW child cover.
      - `sourceCoverId` string, uuid, required: Public id of the cover the operation was applied to.
      - `covers` GeneratedCover[], required
        - `coverId` string, uuid, required: Public id of the cover. Use with the /api/v1/covers/{coverId} endpoints.
        - `signedUrl` string, uri, required: Time-limited signed URL for the preview image (1 hour).
        - `expiresAt` string, date-time, required
        - `coverStyle` string: Template/style id used to render the cover (e.g. a V2 template id).
        - `prompt` string: The generation prompt stored for this cover.
    - AssetJobResult: Result payload for `upscale` and `audiobook_cover` jobs. The new asset is attached to the SAME cover (coverId is the source cover's public id).
      - `covers` CoverAssetResult[], required
        - `coverId` string, uuid, required: Public id of the (source) cover the asset belongs to.
        - `asset` 'full' | 'audiobook_preview' | 'audiobook_full' | 'no_text' | 'no_text_full', required: Which asset was produced: `full` (upscale of preview), `audiobook_full` (upscale of audiobook_preview), `audiobook_preview` (audiobook_cover jobs), `no_text` (remove_text jobs), or `no_text_full` (upscale of no_text).
        - `signedUrl` string, uri, required
        - `expiresAt` string, date-time, required
    - WrapJobResult: Result payload for a `wrap` or `wrap_extend_front` job. Unlike cover operations this carries an `assets` map, not a `covers` array — a wrap is several renditions of ONE design. For `wrap_extend_front`, `designStyle` and `preset` are omitted (the wrap's stored layout is reused).
      - `wrapId` string, uuid, required: Pass to GET /api/v1/wraps/{wrapId} and the render endpoint.
      - `bookId` string, uuid
      - `coverId` string, uuid: Public id of the cover used as the front panel.
      - `designStyle` 'solid' | 'textured' | 'continuation' | 'complementary'
      - `preset` 'standard' | 'blurb-only' | 'minimal'
      - `dims` WrapDimensions: Print geometry at 300 DPI. All inch values are the finished canvas including bleed.
        - `spineWidth` number: Spine width in inches: derived from pageCount and paperType on a KDP wrap, entered by hand on a Barnes & Noble design from the editor.
        - `fullWidth` number: Full wrap width in inches (back + spine + front + bleed).
        - `fullHeight` number: Full wrap height in inches (trim height + bleed).
        - `pixelWidth` integer: fullWidth x dpi, rounded.
        - `pixelHeight` integer: fullHeight x dpi, rounded.
        - `dpi` integer: Always 300.
        - `bleed` number: Bleed in inches per edge (0 for hardcover printed case).
        - `safeTextMargin` number: Minimum inches between text and the trim edge.
        - `minFontSize` number: Smallest point size the layout engine will emit.
        - `canHaveSpineText` boolean: False when the spine is too thin for lettering (under 79 pages on KDP); spine text is dropped with a warning.
        - `barcodeZone` object: Rectangle in inches, relative to the BACK COVER origin, that the printer covers with the barcode. The layout engine keeps text out of it.
          - `x` number
          - `y` number
          - `width` number
          - `height` number
        - `sections` object: Panel boundaries in inches from the left edge of the full wrap.
        - `bindingType` 'paperback' | 'hardcover' | 'dust-jacket': `dust-jacket` (with flaps) appears only on Barnes & Noble designs made in the browser editor.
      - `layout_report` WrapLayoutReport: What the compositor actually drew, after auto-fit and guardrails.
        - `width` integer
        - `height` integer
        - `dpi` integer
        - `blocks` object[]
          - `id` string
          - `fontSize` number: Point size after auto-fit, which may be smaller than requested.
          - `heightPx` integer
          - `overflowsPanel` boolean
        - `warnings` WrapWarning[]
          - `code` 'text_too_long_for_band' | 'text_too_long_for_block' | 'preset_not_applied' | 'text_overflows_panel' | 'clamped_to_safe_area' | 'moved_clear_of_barcode' | 'barcode_zone_unavoidable' | 'font_size_raised_to_minimum' | 'spine_too_thin_for_text' | 'italic_not_available' | 'bold_not_available' | 'image_block_not_rendered' | 'spine_image_not_allowed' | 'front_outpaint_trimmed' | 'front_outpaint_incomplete', required
          - `blockId` string: The text or image block the warning applies to, when it is block-specific.
          - `message` string, required
      - `warnings` WrapWarning[]
        - `code` 'text_too_long_for_band' | 'text_too_long_for_block' | 'preset_not_applied' | 'text_overflows_panel' | 'clamped_to_safe_area' | 'moved_clear_of_barcode' | 'barcode_zone_unavoidable' | 'font_size_raised_to_minimum' | 'spine_too_thin_for_text' | 'italic_not_available' | 'bold_not_available' | 'image_block_not_rendered' | 'spine_image_not_allowed' | 'front_outpaint_trimmed' | 'front_outpaint_incomplete', required
        - `blockId` string: The text or image block the warning applies to, when it is block-specific.
        - `message` string, required
      - `assets` WrapAssets, required: Renditions of one wrap design. Re-signed on every poll/read.
        - `wrap_preview` WrapAsset
          - `signedUrl` string, uri, required
          - `expiresAt` string, date-time, required
        - `wrap_flat` WrapAsset
          - `signedUrl` string, uri, required
          - `expiresAt` string, date-time, required
        - `wrap_pdf` WrapAsset
          - `signedUrl` string, uri, required
          - `expiresAt` string, date-time, required
        - `back_art` WrapAsset
          - `signedUrl` string, uri, required
          - `expiresAt` string, date-time, required

## Acknowledgement `200`

Return any 2xx to acknowledge receipt.

---

[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)
