{"openapi":"3.1.0","info":{"title":"FixMyScan API","summary":"Fix scanned books for e-readers.","description":"Turn scanned books (PDF or DjVu, up to 1,000 pages) into clean, searchable\nPDFs for e-readers: pages cropped to the text, straightened, sharp black and white\nor gentle greyscale, two-page spreads split, searchable text added.\n\n**Keys.** Make one on your FixMyScan Account page and send it as\n`Authorization: Bearer fms_live_...`. It is shown once.\n\n**Flow.** `POST /uploads`, PUT the file, then `POST /books` with the `upload_id`\n(or `POST /books` with a `source_url`). Poll `GET /books/{id}` or give a\n`webhook_url`. Files are deleted 24 hours after a book is ready.\n\n**Webhooks.** One POST when a book ends, `book.ready` or `book.failed`, with the\nbook as `data`. Check `FixMyScan-Signature: t=<time>,v1=<hex>`: HMAC-SHA256 of\n`\"<t>.<raw body>\"` with the webhook secret shown beside your key. Retried after\n30 s, 2 min, 10 min, 1 h and 6 h until your endpoint answers 2xx.\n\n**Errors.** `{\"error\": {\"code\": \"...\", \"message\": \"...\"}}`; a 402 adds `upgrade_url`,\nand `allowance` (`pages_left`, `renews_at`) when free pages come back on a date.\n","version":"1.0.0"},"servers":[{"url":"https://app.fixmyscan.com/api"}],"paths":{"/v1/public/uploads":{"post":{"tags":["books"],"summary":"Get an upload URL","description":"Make a book and a presigned upload for its file (PDF or DjVu, up to 1 GB\nand 1,000 pages). PUT the bytes to `upload.url` with `upload.headers`, then\nstart it with POST /v1/public/books and the `upload_id`.","operationId":"create_upload_v1_public_uploads_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}}},"401":{"description":"No API key, or one that was deleted."},"402":{"description":"The book cannot start: no free pages left and no plan. See upgrade_url, and allowance.renews_at for when free pages come back."},"404":{"description":"No such book for this key's account."},"409":{"description":"The book is in a state that does not allow this."},"422":{"description":"The request is not valid."},"429":{"description":"Too many requests for this key. Wait for Retry-After seconds."}}}},"/v1/public/books":{"post":{"tags":["books"],"summary":"Start a book","description":"Convert a scan from `source_url` or an `upload_id`. Answers at once; the\nbook converts in the background (minutes). Poll GET /v1/public/books/{id},\nor give a `webhook_url`. Without a plan, a book converts from the account's\nfree allowance, and a book longer than what is left converts its first pages\n(`first_pages_only`). Free pages come back every 30 days. With none left the\nanswer is 402 with `upgrade_url` and `allowance.renews_at`.","operationId":"create_book_v1_public_books_post","parameters":[{"name":"idempotency-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Repeat it on a retry so a book starts once.","title":"Idempotency-Key"},"description":"Repeat it on a retry so a book starts once."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Book"}}}},"401":{"description":"No API key, or one that was deleted."},"402":{"description":"The book cannot start: no free pages left and no plan. See upgrade_url, and allowance.renews_at for when free pages come back."},"404":{"description":"No such book for this key's account."},"409":{"description":"The book is in a state that does not allow this."},"422":{"description":"The request is not valid."},"429":{"description":"Too many requests for this key. Wait for Retry-After seconds."}}},"get":{"tags":["books"],"summary":"List books","description":"The account's books from the last 24 hours, newest first (up to 20).","operationId":"list_books_v1_public_books_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookList"}}}},"401":{"description":"No API key, or one that was deleted."},"402":{"description":"The book cannot start: no free pages left and no plan. See upgrade_url, and allowance.renews_at for when free pages come back."},"404":{"description":"No such book for this key's account."},"409":{"description":"The book is in a state that does not allow this."},"422":{"description":"The request is not valid."},"429":{"description":"Too many requests for this key. Wait for Retry-After seconds."}}}},"/v1/public/previews":{"post":{"tags":["books"],"summary":"Preview a book","description":"Before and after of 3 of the book's pages, before converting. Free.\nThe book's `preview` fills in within seconds; start the whole book later\nwith POST /v1/public/books and `upload_id` set to its id.","operationId":"create_preview_v1_public_previews_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Book"}}}},"401":{"description":"No API key, or one that was deleted."},"402":{"description":"The book cannot start: no free pages left and no plan. See upgrade_url, and allowance.renews_at for when free pages come back."},"404":{"description":"No such book for this key's account."},"409":{"description":"The book is in a state that does not allow this."},"422":{"description":"The request is not valid."},"429":{"description":"Too many requests for this key. Wait for Retry-After seconds."}}}},"/v1/public/books/{book_id}":{"get":{"tags":["books"],"summary":"Get a book","description":"State, progress, the preview when there is one, and `download_url` once ready.","operationId":"get_book_v1_public_books__book_id__get","parameters":[{"name":"book_id","in":"path","required":true,"schema":{"type":"string","title":"Book Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Book"}}}},"401":{"description":"No API key, or one that was deleted."},"402":{"description":"The book cannot start: no free pages left and no plan. See upgrade_url, and allowance.renews_at for when free pages come back."},"404":{"description":"No such book for this key's account."},"409":{"description":"The book is in a state that does not allow this."},"422":{"description":"The request is not valid."},"429":{"description":"Too many requests for this key. Wait for Retry-After seconds."}}},"delete":{"tags":["books"],"summary":"Delete a book","description":"Delete the upload and every file made from it now, rather than after 24 hours.","operationId":"delete_book_v1_public_books__book_id__delete","parameters":[{"name":"book_id","in":"path","required":true,"schema":{"type":"string","title":"Book Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Done"}}}},"401":{"description":"No API key, or one that was deleted."},"402":{"description":"The book cannot start: no free pages left and no plan. See upgrade_url, and allowance.renews_at for when free pages come back."},"404":{"description":"No such book for this key's account."},"409":{"description":"The book is in a state that does not allow this."},"422":{"description":"The request is not valid."},"429":{"description":"Too many requests for this key. Wait for Retry-After seconds."}}}},"/v1/public/books/{book_id}/cancel":{"post":{"tags":["books"],"summary":"Cancel a book","description":"Stop a book that is converting or waiting. It does not count as used.","operationId":"cancel_book_v1_public_books__book_id__cancel_post","parameters":[{"name":"book_id","in":"path","required":true,"schema":{"type":"string","title":"Book Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Done"}}}},"401":{"description":"No API key, or one that was deleted."},"402":{"description":"The book cannot start: no free pages left and no plan. See upgrade_url, and allowance.renews_at for when free pages come back."},"404":{"description":"No such book for this key's account."},"409":{"description":"The book is in a state that does not allow this."},"422":{"description":"The request is not valid."},"429":{"description":"Too many requests for this key. Wait for Retry-After seconds."}}}}},"components":{"schemas":{"Book":{"properties":{"id":{"type":"string","title":"Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"state":{"type":"string","title":"State","description":"awaiting_upload, inspecting, ready_for_crop, awaiting_authorization, queued, processing, validating, ready, failed, refused, cancelled or expired."},"message":{"type":"string","title":"Message","description":"One plain sentence about the book, for people."},"pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pages"},"pages_done":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pages Done"},"pages_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pages Total","description":"Pages this conversion makes (fewer than pages for a first-pages book)."},"first_pages_only":{"type":"boolean","title":"First Pages Only","description":"True when only the book's first pages are converted, from the free pages; pages_total says how many."},"allowance":{"anyOf":[{"$ref":"#/components/schemas/FreePages"},{"type":"null"}],"description":"The account's free pages in the rolling 30 days, once they are counting; null with a plan."},"download_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Download Url","description":"The finished PDF while it exists; the link works for an hour."},"preview":{"anyOf":[{"items":{"$ref":"#/components/schemas/PreviewPage"},"type":"array"},{"type":"null"}],"title":"Preview"},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorInfo"},{"type":"null"}]},"upgrade_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upgrade Url","description":"Where a plan can be bought, when the book was refused."},"created_at":{"type":"string","title":"Created At"},"expires_at":{"type":"string","title":"Expires At","description":"When the files are deleted."}},"type":"object","required":["id","title","state","message","pages","pages_done","pages_total","first_pages_only","download_url","preview","error","created_at","expires_at"],"title":"Book","examples":[{"created_at":"2026-11-06T10:12:03+00:00","expires_at":"2026-11-07T10:12:03+00:00","first_pages_only":false,"id":"job_4mNq2c9WkqfK8xY0Tt3bPLh1sR4","message":"Adding searchable text: page 120 of 312.","pages":312,"pages_done":120,"pages_total":312,"state":"processing","title":"old book"}]},"BookList":{"properties":{"books":{"items":{"$ref":"#/components/schemas/Book"},"type":"array","title":"Books"}},"type":"object","required":["books"],"title":"BookList"},"BookRequest":{"properties":{"trim_margins":{"type":"boolean","title":"Trim Margins","description":"Crop each page to its text block. Covers and picture pages stay whole.","default":true},"split_spreads":{"type":"boolean","title":"Split Spreads","description":"Split scans of two facing pages into single pages.","default":true},"output":{"type":"string","enum":["bw","grey"],"title":"Output","description":"bw: sharp black and white, best on e-ink. grey: gentle greyscale.","default":"bw"},"language":{"type":"string","title":"Language","description":"Tesseract code of the book's language (eng, deu, lat...), or auto to detect it.","default":"auto"},"source_url":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"title":"Source Url","description":"A public https link to the PDF or DjVu file."},"upload_id":{"anyOf":[{"type":"string","pattern":"^job_[A-Za-z0-9_-]{1,60}$"},{"type":"null"}],"title":"Upload Id","description":"From POST /v1/public/uploads, after the PUT."},"webhook_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Webhook Url","description":"https URL posted once when the book is ready or failed."}},"additionalProperties":false,"type":"object","title":"BookRequest","examples":[{"source_url":"https://example.org/scans/old-book.pdf","webhook_url":"https://example.com/hooks/fixmyscan"},{"language":"deu","output":"grey","upload_id":"job_4mNq2c9WkqfK8xY0Tt3bPLh1sR4"}]},"Done":{"properties":{"id":{"type":"string","title":"Id"},"state":{"type":"string","title":"State"},"message":{"type":"string","title":"Message"}},"type":"object","required":["id","state","message"],"title":"Done"},"ErrorInfo":{"properties":{"code":{"type":"string","title":"Code"}},"type":"object","required":["code"],"title":"ErrorInfo"},"FreePages":{"properties":{"pages_left":{"type":"integer","title":"Pages Left","description":"Free pages left in the rolling 30 days."},"renews_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Renews At","description":"When the oldest counted pages come back; null while none are used."}},"type":"object","required":["pages_left","renews_at"],"title":"FreePages"},"PreviewPage":{"properties":{"page":{"type":"integer","title":"Page"},"images":{"additionalProperties":{"type":"string"},"type":"object","title":"Images","description":"before and after: JPEG links that work for an hour."}},"type":"object","required":["page","images"],"title":"PreviewPage"},"PreviewRequest":{"properties":{"source_url":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"title":"Source Url"},"upload_id":{"anyOf":[{"type":"string","pattern":"^job_[A-Za-z0-9_-]{1,60}$"},{"type":"null"}],"title":"Upload Id"},"trim_margins":{"type":"boolean","title":"Trim Margins","default":true},"split_spreads":{"type":"boolean","title":"Split Spreads","default":true},"output":{"type":"string","enum":["bw","grey"],"title":"Output","default":"bw"}},"additionalProperties":false,"type":"object","title":"PreviewRequest","examples":[{"upload_id":"job_4mNq2c9WkqfK8xY0Tt3bPLh1sR4"}]},"Transfer":{"properties":{"method":{"type":"string","title":"Method"},"url":{"type":"string","title":"Url"},"headers":{"additionalProperties":{"type":"string"},"type":"object","title":"Headers"},"expires_at":{"type":"string","title":"Expires At"}},"type":"object","required":["method","url","headers","expires_at"],"title":"Transfer"},"Upload":{"properties":{"upload_id":{"type":"string","title":"Upload Id","description":"Pass it to POST /v1/public/books (or /previews) after the PUT."},"upload":{"$ref":"#/components/schemas/Transfer","description":"PUT the file's bytes to url with these headers, within the hour."}},"type":"object","required":["upload_id","upload"],"title":"Upload"},"UploadRequest":{"properties":{"filename":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Filename"},"size":{"type":"integer","minimum":1.0,"title":"Size","description":"The file's exact size in bytes; the upload URL is signed for it."}},"additionalProperties":false,"type":"object","required":["size"],"title":"UploadRequest","examples":[{"filename":"old-book.pdf","size":48213504}]}},"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"fms_live_..."}}},"security":[{"apiKey":[]}]}