Magic Hour API Documentation Audit
One-line state: a feature-rich, multi-SDK API whose getting-started example, OpenAPI schemas, and upload guide have drifted out of sync — the reference says aspect_ratio, the quick-start says orientation, the upload flow hands you three different URLs, and the schemas mark fields required that they never define.
1. Quick-start's primary example uses a parameter the API doesn't have (critical)
Location: /get-started/quick-start
Problem: The first hands-on example calls client.v1.ai_image_generator.generate(... orientation="landscape" ...) with the note orientation: "landscape" - 16:9 aspect ratio. But the AI Image Generator schema at /api-reference/image-projects/ai-image-generator defines no orientation field at all — it uses aspect_ratio (enum 1:1/16:9/9:16) and resolution. The official x-codeSamples on that same reference page correctly use aspect_ratio="1:1".
Consequence: The very first call a developer (or an AI coding agent) copies from the quick-start fails or silently ignores the argument. An agent parsing the reference schema and the quick-start gets two different parameter names for the same concept and cannot reconcile them.
The fix: Replace orientation with aspect_ratio (and the matching enum values) in the quick-start example. Grep the docs for orientation= and purge any other occurrences.
2. OpenAPI required arrays reference fields that don't exist in properties (critical)
Location: /api-reference/video-projects/face-swap-video and /api-reference/video-projects/get-video-details
Problem: In Face Swap Video, the response required array lists estimated_frame_cost, but properties defines only id and credits_charged — the required field is never defined. In Get Video Details, the required array lists both downloads (plural) and download (singular) alongside total_frame_cost; carrying both a singular and plural spelling of the same field in required is a clear schema defect, and the download/total_frame_cost entries are not among the properties shown.
Consequence: Any OpenAPI-driven tool — codegen, SDK generators, agent toolchains, schema validators — either fails to build a valid type or generates a model whose required field can never be populated. This is exactly the class of bug that breaks agents silently: the spec promises a field that the response never contains.
The fix: Add estimated_frame_cost/total_frame_cost to properties with types (or remove them from required), and reconcile download vs downloads to one spelling in both required and properties.
3. Upload file_path has two contradictory formats across pages (critical)
Location: /api-reference/files/generate-asset-upload-urls vs /integration/inputs-and-outputs
Problem: The upload-urls API reference returns file_path with example video/id/1234.mp4. The inputs-and-outputs guide shows the same response field as "file_path": "mh://uploads/abc123/video.mp4" and states file_path: Reference to use in API calls (starts with mh://). Same field, two incompatible schemes.
Consequence: This field is the glue between the upload step and every downstream image_file_path/video_file_path/audio_file_path call. A developer who hardcodes the documented format from one page will pass a value the other page implies is wrong. An agent chaining upload → generate cannot tell which form the API actually returns.
The fix: Document the one true returned format and update the other page to match. If mh:// is the real scheme, fix the API reference example; if not, fix the guide's claim.
4. Canonical code sample recommends a deprecated value (significant)
Location: /api-reference/image-projects/ai-image-generator
Problem: The page's own x-codeSamples pass resolution="auto", while the resolution field description marks that exact value: auto — **Deprecated.** Mapped server-side from your subscription tier.... The recommended example demonstrates the deprecated path.
Consequence: auto still works (it maps server-side), so this isn't a hard failure — but developers and agents extract the official sample verbatim and build on a deprecated parameter value they'll later have to migrate off. The sample contradicts its own field docs.
The fix: Update the code sample to a non-deprecated resolution value (e.g. 1k/2k) and add a one-line note steering users off auto.
5. Upload URL appears with three different hosts/paths across pages (significant)
Location: /api-reference/files/generate-asset-upload-urls vs /integration/inputs-and-outputs
Problem: The same upload URL is documented three ways. The API reference's response example is upload_url: https://videos.magichour.ai/id/video.mp4?auth-value=1234567890, but the PUT curl on the same page targets a different path: https://videos.magichour.ai/api-assets/id/video.mp4. The inputs-and-outputs guide then shows upload_url: https://storage.magichour.ai/upload/abc123... — a different host entirely (storage. vs videos.).
Consequence: A developer wiring up the upload step can't tell which host/path is authoritative, and the response-example-vs-curl mismatch on a single page suggests at least one is wrong. Since the PUT target is supposed to come from the returned upload_url, the inconsistency undermines confidence in the whole upload flow.
The fix: Show one canonical upload_url host and path, and make the PUT example use the returned value verbatim rather than a hand-edited path.
6. Face Swap image_file_path is described as a video file (significant)
Location: /api-reference/video-projects/face-swap-video
Problem: The image_file_path field — the image whose face gets swapped in — is documented as: This value is either - a direct URL to the video file. That's a copy-paste error from the video field. The face_swap_mode enum description also contains a stray +- typo: +- **individual-faces** — specify exact mappings.
Consequence: Developers reading the field doc literally will try to supply a video URL to the image input and get unexpected failures, with no error-reference page to clarify.
The fix: Change the image_file_path description to "a direct URL to the image file," and remove the +- typo.
7. Webhook lifecycle references a pending status that isn't in the enum (significant)
Location: /integration/webhook/event-types vs /api-reference/video-projects/get-video-details
Problem: The webhook docs say video.started fires when a job moves from pending to rendering. But the Get Video Details status enum is draft | queued | rendering | complete | error | canceled — there is no pending. The video.started payload example also includes total_frame_cost, a field the Get Video Details required array names but properties doesn't surface (see issue 2).
Consequence: Developers writing state machines around webhook events will branch on a pending status that the API never emits. Agents mapping webhook payloads to the documented status enum hit a value with no definition.
The fix: Replace pending with the actual pre-render status (queued) and reconcile the total_frame_cost field with the Get Video Details schema.
8. Dead internal link to a non-existent Input Files Guide (significant)
Location: /api-reference/files/generate-asset-upload-urls
Problem: The page links [Input Files Guide](/integration/input-files) twice, but that page does not exist — the real guide is /integration/inputs-and-outputs. The Face Swap page similarly links .../generate-asset-upload-urls#input-file.
Consequence: Developers following the upload flow hit a 404 at the exact moment they need the guide that explains the file-path format (which is itself contradictory, per issue 3).
The fix: Repoint both links to /integration/inputs-and-outputs and verify the #input-file anchor exists.
9. download_directory="." contradicts the expected output and forces a manual mkdir (significant)
Location: /get-started/quick-start
Problem: The example sets download_directory="." with the comment save the outputs to the current directory, but the Expected Output shows Outputs are saved at ['outputs/output-1.png'], and the Troubleshooting section then documents FileNotFoundError: ... 'outputs/output-1.png' with the fix mkdir outputs. The code, the output, and the troubleshooting all disagree about where files land.
Consequence: Developers running the canonical first example hit a FileNotFoundError that the docs already anticipate but didn't fix in the example itself.
The fix: Make the example, the expected output, and the directory behavior consistent — either default to . and show ./output-1.png, or have the SDK create the outputs/ directory automatically.
10. Supported file formats conflict between the API reference and the guide (significant)
Location: /api-reference/files/generate-asset-upload-urls vs /integration/inputs-and-outputs
Problem: The upload-urls reference lists audio extensions including m4a, opus, ogg, amr and image extensions including heic, heif. The inputs-and-outputs guide's "Supported File Formats" lists audio as mp3, mpeg, wav, aac, aiff, flac (adds mpeg, omits m4a/opus/ogg/amr) and image without heic/heif. Two authoritative-looking format lists disagree.
Consequence: A developer who validates uploads against the guide will reject files the API accepts (or vice versa). Agents can't determine which extensions are actually supported.
The fix: Maintain one canonical format list (ideally generated from the API) and reference it from the guide rather than duplicating it.
11. Intro claims 22 APIs but lists 21 (significant)
Location: /introduction (and /get-started/quick-start)
Problem: The intro says Run sample code for all 22 APIs and the Colab link repeats sample code for all 22 APIs, but the "Available via API" list totals 21 tools (8 video + 12 image + 1 audio). The count is off by one, and the docs never identify which capability the 22nd entry refers to.
Consequence: An agent or developer indexing the intro to enumerate capabilities gets a count that doesn't match the list and can't tell whether a tool is missing from the table or the number is simply wrong.
The fix: Reconcile the count to the real number of documented tools, and audit the "Available via API" list against the API reference so every shipped endpoint appears in the table.
12. Embedded OpenAPI points to an off-site webhook doc URL and a second key-generation URL (significant)
Location: /api-reference/image-projects/ai-image-generator, /api-reference/video-projects/get-video-details
Problem: Embedded OpenAPI description blocks send users to https://magichour.ai/docs/webhook for webhook documentation, but the real webhook docs live at docs.magichour.ai/integration/webhook. The auth section in these specs also points key generation to https://magichour.ai/settings/developer, while the Authentication page sends users to the Developer Hub at magichour.ai/developer.
Consequence: Developers and agents following the spec's links land on the wrong (likely non-existent) page for webhooks and get two different URLs for creating API keys.
The fix: Update the OpenAPI descriptions to the canonical docs.magichour.ai/integration/webhook and settle on one Developer Hub URL across all pages.
13. No single error-code reference; error codes are scattered across pages (significant)
Location: /integration/webhook/event-types, /get-started/quick-start, /get-started/authentication
Problem: Application error codes (invalid_video_file, no_source_face, content_policy_violation, text_too_long, invalid_voice, insufficient_credits, processing_timeout, file_too_large, invalid_parameters, etc.) appear only in scattered webhook prose. The HTTP error table in the quick-start lists 400/401/402/500/502/503, while the Authentication page documents only 401. There is no consolidated reference tying HTTP statuses to application error codes.
Consequence: There is no single parseable error reference. Developers can't enumerate the failure modes they must handle, and agents can't build exhaustive error handling because codes are spread across pages with no contract.
The fix: Add one structured error reference (HTTP status codes plus application error codes, each with cause and remedy) and link it from auth, quick-start, and webhooks.
14. Discord invite on the auth page differs from every other page (minor)
Location: /get-started/authentication
Problem: The Authentication page links Discord as discord.gg/QNsVxKYz, while the introduction, quick-start, and API reference all use discord.gg/JX5rgsZaJp. One of these is stale and likely dead.
Consequence: Developers who need help and click the auth-page invite may hit an expired/invalid Discord link.
The fix: Standardize on the live invite across all pages.
15. API-key "Permissions" step appears undocumented (minor)
Location: /get-started/authentication
Problem: Key creation instructs users to Select the appropriate permissions for your use case, but the "Viewing API Keys" section only exposes Name / Last 4 Characters / Created — no permissions field, scopes, or options are described anywhere on the page.
Consequence: Developers look for a permissions control that the docs never explain — does it exist, and what scopes are available? — and have no way to know what they're selecting.
The fix: Either document the permissions/scopes UI and what each scope does, or remove the step from the instructions if keys aren't actually scoped.
16. PUT upload example ends with a stray ? (minor)
Location: /api-reference/files/generate-asset-upload-urls
Problem: The upload curl example ends with a dangling query delimiter: curl -X PUT --data '@/path/to/file/video.mp4' \ https://videos.magichour.ai/api-assets/id/video.mp4?. The trailing ? is not part of a real query string.
Consequence: Developers and agents copy-pasting the example carry a malformed-looking URL; at minimum it's confusing next to the response example, which shows a real ?auth-value=... query string.
The fix: Remove the trailing ?, or show the example with the actual signed query parameters as returned in upload_url.
What they do well
- Broad, multi-SDK coverage (Python, Node, Go, Rust) with a
.generate()helper and explicit SDK version gates (Python SDK v0.36.0+ / Node SDK v0.37.0+). - Webhook docs include real payload examples per event type, and the secure-handler page demonstrates correct constant-time verification in Python (
hmac.compare_digest) with a clear explanation of why it matters. - The OpenAPI reference documents deprecations inline (e.g.
seedream→seedream-v4), which is the right instinct — it just needs to stop demonstrating the deprecated values in its own samples.
Top 3 recommendations
- Reconcile examples with the schema. Purge
orientationin favor ofaspect_ratioin the quick-start, fix the deprecatedresolution="auto"sample, and fix the OpenAPIrequiredarrays that name undefined fields (estimated_frame_cost,downloadvsdownloads) — these break copy-paste and codegen for humans and agents alike. - Pick one truth for cross-cutting values. One
file_pathformat (mh://vsvideo/id/...), one upload-URL host/path, one supported-formats list, one webhook-doc URL, one Developer Hub URL, one Discord invite, one job-status vocabulary (droppending). - Ship a single structured error reference covering HTTP statuses and application error codes — currently they're scattered across the quick-start table, the auth page (401 only), and webhook prose with no consolidated contract.