{
  "openapi": "3.1.0",
  "paths": {
    "/video.started": {
      "post": {
        "description": "The request example illustrates what your webhook endpoint should expect.",
        "summary": "Video Started",
        "tags": ["Video Events"],
        "parameters": [
          {
            "name": "magic-hour-event-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.",
              "example": "3e771b50c..."
            }
          },
          {
            "name": "magic-hour-event-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.",
              "example": "1730742038"
            }
          }
        ],
        "operationId": "video.started",
        "requestBody": {
          "description": "Body",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "enum": ["video.started"] },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "cuid-example",
                        "description": "Unique ID of the video. Use it with the [Get video Project API](https://docs.magichour.ai/api-reference/video-projects/get-video-details) to fetch status and downloads."
                      },
                      "name": {
                        "type": "string",
                        "nullable": true,
                        "description": "The name of the video.",
                        "example": "Example Name"
                      },
                      "status": {
                        "type": "string",
                        "enum": ["draft", "queued", "rendering", "complete", "error", "canceled"],
                        "description": "The status of the video.\n\n- `draft` - the project was created but has not been submitted for rendering\n- `queued` - the job is waiting for an available server\n- `rendering` - the job is being processed; the `video.started` webhook event fires when rendering begins\n- `complete` - the job finished successfully; fires `video.completed`\n- `error` - the job failed during processing; fires `video.errored`\n- `canceled` - the job was manually canceled (for example from the Magic Hour web app)\n\n**Note:** `rendering`, `complete`, and `error` have matching webhook events; `canceled` does not - a canceled job emits no webhook event, so poll this endpoint to detect cancellation.",
                        "example": "complete"
                      },
                      "type": {
                        "type": "string",
                        "description": "The type of the video project. Possible values are ANIMATION, AUTO_SUBTITLE, VIDEO_TO_VIDEO, FACE_SWAP, TEXT_TO_VIDEO, IMAGE_TO_VIDEO, LIP_SYNC, TALKING_PHOTO, AVATAR, VIDEO_UPSCALER, VIDEO_EDITOR, CHARACTER_REPLACE, VIDEO_COLORIZER, VIDEO_WATERMARK_REMOVER, VIDEO_COLOR_GRADER, VIDEO_TRANSLATOR, MUSIC_VIDEO, EXTEND, AUDIO_TO_VIDEO, VIDEO_EXPANDER, UGC_AD",
                        "example": "FACE_SWAP"
                      },
                      "created_at": { "type": "string", "format": "date-time" },
                      "width": {
                        "type": "integer",
                        "description": "The width of the final output video. A value of -1 indicates the width can be ignored.",
                        "example": 512
                      },
                      "height": {
                        "type": "integer",
                        "description": "The height of the final output video. A value of -1 indicates the height can be ignored.",
                        "example": 960
                      },
                      "enabled": {
                        "type": "boolean",
                        "description": "Whether this resource is active. If false, it is deleted."
                      },
                      "start_seconds": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Start time of your clip (seconds). Must be ≥ 0.",
                        "format": "float",
                        "example": 0
                      },
                      "end_seconds": {
                        "type": "number",
                        "minimum": 0.1,
                        "description": "End time of your clip (seconds). Must be greater than start_seconds.",
                        "format": "float",
                        "example": 5
                      },
                      "credits_charged": {
                        "type": "integer",
                        "description": "The amount of credits deducted from your account to generate the video. If the status is not 'complete', this value is an estimate and may be adjusted upon completion based on the actual FPS of the output video. \n\nIf video generation fails, credits will be refunded, and this field will be updated to include the refund.",
                        "example": 450
                      },
                      "fps": {
                        "type": "number",
                        "description": "Frame rate of the video. If the status is not 'complete', the frame rate is an estimate and will be adjusted when the video completes.",
                        "example": 30
                      },
                      "error": {
                        "type": "object",
                        "properties": {
                          "message": {
                            "type": "string",
                            "description": "Details on the reason why a failure happened.",
                            "example": "Please use an image with a detectable face"
                          },
                          "code": {
                            "type": "string",
                            "example": "no_source_face",
                            "description": "An error code to indicate why a failure happened."
                          }
                        },
                        "required": ["message", "code"],
                        "nullable": true,
                        "description": "In the case of an error, this object will contain the error encountered during video render",
                        "example": null
                      },
                      "downloads": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://videos.magichour.ai/id/output.mp4"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "example": "2024-10-19T05:16:19.027Z"
                            }
                          },
                          "required": ["url", "expires_at"],
                          "description": "The download url and expiration date of the image project"
                        }
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "status",
                      "type",
                      "created_at",
                      "width",
                      "height",
                      "enabled",
                      "start_seconds",
                      "end_seconds",
                      "credits_charged",
                      "fps",
                      "error",
                      "downloads"
                    ],
                    "description": "Success"
                  }
                },
                "required": ["type", "payload"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Success message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Success"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          }
        }
      }
    },
    "/video.completed": {
      "post": {
        "description": "The request example illustrates what your webhook endpoint should expect.",
        "summary": "Video Completed",
        "tags": ["Video Events"],
        "parameters": [
          {
            "name": "magic-hour-event-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.",
              "example": "3e771b50c..."
            }
          },
          {
            "name": "magic-hour-event-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.",
              "example": "1730742038"
            }
          }
        ],
        "operationId": "video.completed",
        "requestBody": {
          "description": "Body",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "enum": ["video.completed"] },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "cuid-example",
                        "description": "Unique ID of the video. Use it with the [Get video Project API](https://docs.magichour.ai/api-reference/video-projects/get-video-details) to fetch status and downloads."
                      },
                      "name": {
                        "type": "string",
                        "nullable": true,
                        "description": "The name of the video.",
                        "example": "Example Name"
                      },
                      "status": {
                        "type": "string",
                        "enum": ["draft", "queued", "rendering", "complete", "error", "canceled"],
                        "description": "The status of the video.\n\n- `draft` - the project was created but has not been submitted for rendering\n- `queued` - the job is waiting for an available server\n- `rendering` - the job is being processed; the `video.started` webhook event fires when rendering begins\n- `complete` - the job finished successfully; fires `video.completed`\n- `error` - the job failed during processing; fires `video.errored`\n- `canceled` - the job was manually canceled (for example from the Magic Hour web app)\n\n**Note:** `rendering`, `complete`, and `error` have matching webhook events; `canceled` does not - a canceled job emits no webhook event, so poll this endpoint to detect cancellation.",
                        "example": "complete"
                      },
                      "type": {
                        "type": "string",
                        "description": "The type of the video project. Possible values are ANIMATION, AUTO_SUBTITLE, VIDEO_TO_VIDEO, FACE_SWAP, TEXT_TO_VIDEO, IMAGE_TO_VIDEO, LIP_SYNC, TALKING_PHOTO, AVATAR, VIDEO_UPSCALER, VIDEO_EDITOR, CHARACTER_REPLACE, VIDEO_COLORIZER, VIDEO_WATERMARK_REMOVER, VIDEO_COLOR_GRADER, VIDEO_TRANSLATOR, MUSIC_VIDEO, EXTEND, AUDIO_TO_VIDEO, VIDEO_EXPANDER, UGC_AD",
                        "example": "FACE_SWAP"
                      },
                      "created_at": { "type": "string", "format": "date-time" },
                      "width": {
                        "type": "integer",
                        "description": "The width of the final output video. A value of -1 indicates the width can be ignored.",
                        "example": 512
                      },
                      "height": {
                        "type": "integer",
                        "description": "The height of the final output video. A value of -1 indicates the height can be ignored.",
                        "example": 960
                      },
                      "enabled": {
                        "type": "boolean",
                        "description": "Whether this resource is active. If false, it is deleted."
                      },
                      "start_seconds": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Start time of your clip (seconds). Must be ≥ 0.",
                        "format": "float",
                        "example": 0
                      },
                      "end_seconds": {
                        "type": "number",
                        "minimum": 0.1,
                        "description": "End time of your clip (seconds). Must be greater than start_seconds.",
                        "format": "float",
                        "example": 5
                      },
                      "credits_charged": {
                        "type": "integer",
                        "description": "The amount of credits deducted from your account to generate the video. If the status is not 'complete', this value is an estimate and may be adjusted upon completion based on the actual FPS of the output video. \n\nIf video generation fails, credits will be refunded, and this field will be updated to include the refund.",
                        "example": 450
                      },
                      "fps": {
                        "type": "number",
                        "description": "Frame rate of the video. If the status is not 'complete', the frame rate is an estimate and will be adjusted when the video completes.",
                        "example": 30
                      },
                      "error": {
                        "type": "object",
                        "properties": {
                          "message": {
                            "type": "string",
                            "description": "Details on the reason why a failure happened.",
                            "example": "Please use an image with a detectable face"
                          },
                          "code": {
                            "type": "string",
                            "example": "no_source_face",
                            "description": "An error code to indicate why a failure happened."
                          }
                        },
                        "required": ["message", "code"],
                        "nullable": true,
                        "description": "In the case of an error, this object will contain the error encountered during video render",
                        "example": null
                      },
                      "downloads": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://videos.magichour.ai/id/output.mp4"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "example": "2024-10-19T05:16:19.027Z"
                            }
                          },
                          "required": ["url", "expires_at"],
                          "description": "The download url and expiration date of the image project"
                        }
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "status",
                      "type",
                      "created_at",
                      "width",
                      "height",
                      "enabled",
                      "start_seconds",
                      "end_seconds",
                      "credits_charged",
                      "fps",
                      "error",
                      "downloads"
                    ],
                    "description": "Success"
                  }
                },
                "required": ["type", "payload"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Success message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Success"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          }
        }
      }
    },
    "/video.errored": {
      "post": {
        "description": "The request example illustrates what your webhook endpoint should expect.",
        "summary": "Video Errored",
        "tags": ["Video Events"],
        "parameters": [
          {
            "name": "magic-hour-event-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.",
              "example": "3e771b50c..."
            }
          },
          {
            "name": "magic-hour-event-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.",
              "example": "1730742038"
            }
          }
        ],
        "operationId": "video.errored",
        "requestBody": {
          "description": "Body",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "enum": ["video.errored"] },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "cuid-example",
                        "description": "Unique ID of the video. Use it with the [Get video Project API](https://docs.magichour.ai/api-reference/video-projects/get-video-details) to fetch status and downloads."
                      },
                      "name": {
                        "type": "string",
                        "nullable": true,
                        "description": "The name of the video.",
                        "example": "Example Name"
                      },
                      "status": {
                        "type": "string",
                        "enum": ["draft", "queued", "rendering", "complete", "error", "canceled"],
                        "description": "The status of the video.\n\n- `draft` - the project was created but has not been submitted for rendering\n- `queued` - the job is waiting for an available server\n- `rendering` - the job is being processed; the `video.started` webhook event fires when rendering begins\n- `complete` - the job finished successfully; fires `video.completed`\n- `error` - the job failed during processing; fires `video.errored`\n- `canceled` - the job was manually canceled (for example from the Magic Hour web app)\n\n**Note:** `rendering`, `complete`, and `error` have matching webhook events; `canceled` does not - a canceled job emits no webhook event, so poll this endpoint to detect cancellation.",
                        "example": "complete"
                      },
                      "type": {
                        "type": "string",
                        "description": "The type of the video project. Possible values are ANIMATION, AUTO_SUBTITLE, VIDEO_TO_VIDEO, FACE_SWAP, TEXT_TO_VIDEO, IMAGE_TO_VIDEO, LIP_SYNC, TALKING_PHOTO, AVATAR, VIDEO_UPSCALER, VIDEO_EDITOR, CHARACTER_REPLACE, VIDEO_COLORIZER, VIDEO_WATERMARK_REMOVER, VIDEO_COLOR_GRADER, VIDEO_TRANSLATOR, MUSIC_VIDEO, EXTEND, AUDIO_TO_VIDEO, VIDEO_EXPANDER, UGC_AD",
                        "example": "FACE_SWAP"
                      },
                      "created_at": { "type": "string", "format": "date-time" },
                      "width": {
                        "type": "integer",
                        "description": "The width of the final output video. A value of -1 indicates the width can be ignored.",
                        "example": 512
                      },
                      "height": {
                        "type": "integer",
                        "description": "The height of the final output video. A value of -1 indicates the height can be ignored.",
                        "example": 960
                      },
                      "enabled": {
                        "type": "boolean",
                        "description": "Whether this resource is active. If false, it is deleted."
                      },
                      "start_seconds": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Start time of your clip (seconds). Must be ≥ 0.",
                        "format": "float",
                        "example": 0
                      },
                      "end_seconds": {
                        "type": "number",
                        "minimum": 0.1,
                        "description": "End time of your clip (seconds). Must be greater than start_seconds.",
                        "format": "float",
                        "example": 5
                      },
                      "credits_charged": {
                        "type": "integer",
                        "description": "The amount of credits deducted from your account to generate the video. If the status is not 'complete', this value is an estimate and may be adjusted upon completion based on the actual FPS of the output video. \n\nIf video generation fails, credits will be refunded, and this field will be updated to include the refund.",
                        "example": 450
                      },
                      "fps": {
                        "type": "number",
                        "description": "Frame rate of the video. If the status is not 'complete', the frame rate is an estimate and will be adjusted when the video completes.",
                        "example": 30
                      },
                      "error": {
                        "type": "object",
                        "properties": {
                          "message": {
                            "type": "string",
                            "description": "Details on the reason why a failure happened.",
                            "example": "Please use an image with a detectable face"
                          },
                          "code": {
                            "type": "string",
                            "example": "no_source_face",
                            "description": "An error code to indicate why a failure happened."
                          }
                        },
                        "required": ["message", "code"],
                        "nullable": true,
                        "description": "In the case of an error, this object will contain the error encountered during video render",
                        "example": null
                      },
                      "downloads": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://videos.magichour.ai/id/output.mp4"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "example": "2024-10-19T05:16:19.027Z"
                            }
                          },
                          "required": ["url", "expires_at"],
                          "description": "The download url and expiration date of the image project"
                        }
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "status",
                      "type",
                      "created_at",
                      "width",
                      "height",
                      "enabled",
                      "start_seconds",
                      "end_seconds",
                      "credits_charged",
                      "fps",
                      "error",
                      "downloads"
                    ],
                    "description": "Success"
                  }
                },
                "required": ["type", "payload"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Success message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Success"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          }
        }
      }
    },
    "/image.started": {
      "post": {
        "description": "The request example illustrates what your webhook endpoint should expect.",
        "summary": "Image Started",
        "tags": ["Image Events"],
        "parameters": [
          {
            "name": "magic-hour-event-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.",
              "example": "3e771b50c..."
            }
          },
          {
            "name": "magic-hour-event-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.",
              "example": "1730742038"
            }
          }
        ],
        "operationId": "image.started",
        "requestBody": {
          "description": "Body",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "enum": ["image.started"] },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "cuid-example",
                        "description": "Unique ID of the image. Use it with the [Get image Project API](https://docs.magichour.ai/api-reference/image-projects/get-image-details) to fetch status and downloads."
                      },
                      "name": {
                        "type": "string",
                        "nullable": true,
                        "description": "The name of the image.",
                        "example": "Example Name"
                      },
                      "status": {
                        "type": "string",
                        "enum": ["draft", "queued", "rendering", "complete", "error", "canceled"],
                        "description": "The status of the image.\n\n- `draft` - the project was created but has not been submitted for rendering\n- `queued` - the job is waiting for an available server\n- `rendering` - the job is being processed; the `image.started` webhook event fires when rendering begins\n- `complete` - the job finished successfully; fires `image.completed`\n- `error` - the job failed during processing; fires `image.errored`\n- `canceled` - the job was manually canceled (for example from the Magic Hour web app)\n\n**Note:** `rendering`, `complete`, and `error` have matching webhook events; `canceled` does not - a canceled job emits no webhook event, so poll this endpoint to detect cancellation.",
                        "example": "complete"
                      },
                      "image_count": {
                        "type": "integer",
                        "description": "Number of images generated",
                        "example": 1
                      },
                      "type": {
                        "type": "string",
                        "description": "The type of the image project. Possible values are FACE_EDITOR, AI_IMAGE_EDITOR, GENERATIVE_FILL, AI_SELFIE, AI_HEADSHOT, AI_INFLUENCER, AI_IMAGE, AI_MEME, CLOTHES_CHANGER, BACKGROUND_REMOVER, FACE_SWAP, IMAGE_UPSCALER, IMAGE_ENHANCER, AI_GIF, QR_CODE, PHOTO_EDITOR, PHOTO_COLORIZER, IMAGE_COLOR_GRADER, HEAD_SWAP, BODY_SWAP, STORYBOARD, IMAGE_EXPANDER",
                        "example": "AI_IMAGE"
                      },
                      "created_at": { "type": "string", "format": "date-time" },
                      "enabled": {
                        "type": "boolean",
                        "description": "Whether this resource is active. If false, it is deleted."
                      },
                      "credits_charged": {
                        "type": "integer",
                        "example": 5,
                        "description": "The amount of credits deducted from your account to generate the image. We charge credits right when the request is made. \n\nIf an error occurred while generating the image(s), credits will be refunded and this field will be updated to include the refund."
                      },
                      "downloads": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://videos.magichour.ai/id/output.png"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "example": "2024-10-19T05:16:19.027Z"
                            }
                          },
                          "required": ["url", "expires_at"],
                          "description": "The download url and expiration date of the image project"
                        }
                      },
                      "error": {
                        "type": "object",
                        "properties": {
                          "message": {
                            "type": "string",
                            "description": "Details on the reason why a failure happened.",
                            "example": "Please use an image with a detectable face"
                          },
                          "code": {
                            "type": "string",
                            "example": "no_source_face",
                            "description": "An error code to indicate why a failure happened."
                          }
                        },
                        "required": ["message", "code"],
                        "nullable": true,
                        "description": "In the case of an error, this object will contain the error encountered during video render",
                        "example": null
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "status",
                      "image_count",
                      "type",
                      "created_at",
                      "enabled",
                      "credits_charged",
                      "downloads",
                      "error"
                    ],
                    "description": "Success"
                  }
                },
                "required": ["type", "payload"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Success message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Success"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          }
        }
      }
    },
    "/image.completed": {
      "post": {
        "description": "The request example illustrates what your webhook endpoint should expect.",
        "summary": "Image Completed",
        "tags": ["Image Events"],
        "parameters": [
          {
            "name": "magic-hour-event-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.",
              "example": "3e771b50c..."
            }
          },
          {
            "name": "magic-hour-event-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.",
              "example": "1730742038"
            }
          }
        ],
        "operationId": "image.completed",
        "requestBody": {
          "description": "Body",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "enum": ["image.completed"] },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "cuid-example",
                        "description": "Unique ID of the image. Use it with the [Get image Project API](https://docs.magichour.ai/api-reference/image-projects/get-image-details) to fetch status and downloads."
                      },
                      "name": {
                        "type": "string",
                        "nullable": true,
                        "description": "The name of the image.",
                        "example": "Example Name"
                      },
                      "status": {
                        "type": "string",
                        "enum": ["draft", "queued", "rendering", "complete", "error", "canceled"],
                        "description": "The status of the image.\n\n- `draft` - the project was created but has not been submitted for rendering\n- `queued` - the job is waiting for an available server\n- `rendering` - the job is being processed; the `image.started` webhook event fires when rendering begins\n- `complete` - the job finished successfully; fires `image.completed`\n- `error` - the job failed during processing; fires `image.errored`\n- `canceled` - the job was manually canceled (for example from the Magic Hour web app)\n\n**Note:** `rendering`, `complete`, and `error` have matching webhook events; `canceled` does not - a canceled job emits no webhook event, so poll this endpoint to detect cancellation.",
                        "example": "complete"
                      },
                      "image_count": {
                        "type": "integer",
                        "description": "Number of images generated",
                        "example": 1
                      },
                      "type": {
                        "type": "string",
                        "description": "The type of the image project. Possible values are FACE_EDITOR, AI_IMAGE_EDITOR, GENERATIVE_FILL, AI_SELFIE, AI_HEADSHOT, AI_INFLUENCER, AI_IMAGE, AI_MEME, CLOTHES_CHANGER, BACKGROUND_REMOVER, FACE_SWAP, IMAGE_UPSCALER, IMAGE_ENHANCER, AI_GIF, QR_CODE, PHOTO_EDITOR, PHOTO_COLORIZER, IMAGE_COLOR_GRADER, HEAD_SWAP, BODY_SWAP, STORYBOARD, IMAGE_EXPANDER",
                        "example": "AI_IMAGE"
                      },
                      "created_at": { "type": "string", "format": "date-time" },
                      "enabled": {
                        "type": "boolean",
                        "description": "Whether this resource is active. If false, it is deleted."
                      },
                      "credits_charged": {
                        "type": "integer",
                        "example": 5,
                        "description": "The amount of credits deducted from your account to generate the image. We charge credits right when the request is made. \n\nIf an error occurred while generating the image(s), credits will be refunded and this field will be updated to include the refund."
                      },
                      "downloads": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://videos.magichour.ai/id/output.png"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "example": "2024-10-19T05:16:19.027Z"
                            }
                          },
                          "required": ["url", "expires_at"],
                          "description": "The download url and expiration date of the image project"
                        }
                      },
                      "error": {
                        "type": "object",
                        "properties": {
                          "message": {
                            "type": "string",
                            "description": "Details on the reason why a failure happened.",
                            "example": "Please use an image with a detectable face"
                          },
                          "code": {
                            "type": "string",
                            "example": "no_source_face",
                            "description": "An error code to indicate why a failure happened."
                          }
                        },
                        "required": ["message", "code"],
                        "nullable": true,
                        "description": "In the case of an error, this object will contain the error encountered during video render",
                        "example": null
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "status",
                      "image_count",
                      "type",
                      "created_at",
                      "enabled",
                      "credits_charged",
                      "downloads",
                      "error"
                    ],
                    "description": "Success"
                  }
                },
                "required": ["type", "payload"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Success message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Success"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          }
        }
      }
    },
    "/image.errored": {
      "post": {
        "description": "The request example illustrates what your webhook endpoint should expect.",
        "summary": "Image Errored",
        "tags": ["Image Events"],
        "parameters": [
          {
            "name": "magic-hour-event-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.",
              "example": "3e771b50c..."
            }
          },
          {
            "name": "magic-hour-event-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.",
              "example": "1730742038"
            }
          }
        ],
        "operationId": "image.errored",
        "requestBody": {
          "description": "Body",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "enum": ["image.errored"] },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "cuid-example",
                        "description": "Unique ID of the image. Use it with the [Get image Project API](https://docs.magichour.ai/api-reference/image-projects/get-image-details) to fetch status and downloads."
                      },
                      "name": {
                        "type": "string",
                        "nullable": true,
                        "description": "The name of the image.",
                        "example": "Example Name"
                      },
                      "status": {
                        "type": "string",
                        "enum": ["draft", "queued", "rendering", "complete", "error", "canceled"],
                        "description": "The status of the image.\n\n- `draft` - the project was created but has not been submitted for rendering\n- `queued` - the job is waiting for an available server\n- `rendering` - the job is being processed; the `image.started` webhook event fires when rendering begins\n- `complete` - the job finished successfully; fires `image.completed`\n- `error` - the job failed during processing; fires `image.errored`\n- `canceled` - the job was manually canceled (for example from the Magic Hour web app)\n\n**Note:** `rendering`, `complete`, and `error` have matching webhook events; `canceled` does not - a canceled job emits no webhook event, so poll this endpoint to detect cancellation.",
                        "example": "complete"
                      },
                      "image_count": {
                        "type": "integer",
                        "description": "Number of images generated",
                        "example": 1
                      },
                      "type": {
                        "type": "string",
                        "description": "The type of the image project. Possible values are FACE_EDITOR, AI_IMAGE_EDITOR, GENERATIVE_FILL, AI_SELFIE, AI_HEADSHOT, AI_INFLUENCER, AI_IMAGE, AI_MEME, CLOTHES_CHANGER, BACKGROUND_REMOVER, FACE_SWAP, IMAGE_UPSCALER, IMAGE_ENHANCER, AI_GIF, QR_CODE, PHOTO_EDITOR, PHOTO_COLORIZER, IMAGE_COLOR_GRADER, HEAD_SWAP, BODY_SWAP, STORYBOARD, IMAGE_EXPANDER",
                        "example": "AI_IMAGE"
                      },
                      "created_at": { "type": "string", "format": "date-time" },
                      "enabled": {
                        "type": "boolean",
                        "description": "Whether this resource is active. If false, it is deleted."
                      },
                      "credits_charged": {
                        "type": "integer",
                        "example": 5,
                        "description": "The amount of credits deducted from your account to generate the image. We charge credits right when the request is made. \n\nIf an error occurred while generating the image(s), credits will be refunded and this field will be updated to include the refund."
                      },
                      "downloads": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://videos.magichour.ai/id/output.png"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "example": "2024-10-19T05:16:19.027Z"
                            }
                          },
                          "required": ["url", "expires_at"],
                          "description": "The download url and expiration date of the image project"
                        }
                      },
                      "error": {
                        "type": "object",
                        "properties": {
                          "message": {
                            "type": "string",
                            "description": "Details on the reason why a failure happened.",
                            "example": "Please use an image with a detectable face"
                          },
                          "code": {
                            "type": "string",
                            "example": "no_source_face",
                            "description": "An error code to indicate why a failure happened."
                          }
                        },
                        "required": ["message", "code"],
                        "nullable": true,
                        "description": "In the case of an error, this object will contain the error encountered during video render",
                        "example": null
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "status",
                      "image_count",
                      "type",
                      "created_at",
                      "enabled",
                      "credits_charged",
                      "downloads",
                      "error"
                    ],
                    "description": "Success"
                  }
                },
                "required": ["type", "payload"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Success message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Success"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          }
        }
      }
    },
    "/audio.started": {
      "post": {
        "description": "The request example illustrates what your webhook endpoint should expect.",
        "summary": "Audio Started",
        "tags": ["Audio Events"],
        "parameters": [
          {
            "name": "magic-hour-event-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.",
              "example": "3e771b50c..."
            }
          },
          {
            "name": "magic-hour-event-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.",
              "example": "1730742038"
            }
          }
        ],
        "operationId": "audio.started",
        "requestBody": {
          "description": "Body",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "enum": ["audio.started"] },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "cuid-example",
                        "description": "Unique ID of the audio. Use it with the [Get audio Project API](https://docs.magichour.ai/api-reference/audio-projects/get-audio-details) to fetch status and downloads."
                      },
                      "name": {
                        "type": "string",
                        "nullable": true,
                        "description": "The name of the audio.",
                        "example": "Example Name"
                      },
                      "status": {
                        "type": "string",
                        "enum": ["draft", "queued", "rendering", "complete", "error", "canceled"],
                        "description": "The status of the audio.\n\n- `draft` - the project was created but has not been submitted for rendering\n- `queued` - the job is waiting for an available server\n- `rendering` - the job is being processed; the `audio.started` webhook event fires when rendering begins\n- `complete` - the job finished successfully; fires `audio.completed`\n- `error` - the job failed during processing; fires `audio.errored`\n- `canceled` - the job was manually canceled (for example from the Magic Hour web app)\n\n**Note:** `rendering`, `complete`, and `error` have matching webhook events; `canceled` does not - a canceled job emits no webhook event, so poll this endpoint to detect cancellation.",
                        "example": "complete"
                      },
                      "type": {
                        "type": "string",
                        "description": "The type of the audio project. Possible values are AUDIO_TRANSLATOR, VOICE_GENERATOR, VOICE_CHANGER, VOICE_CLONER, VIDEO_TO_AUDIO, MUSIC_GENERATOR, SOUND_EFFECT_GENERATOR",
                        "example": "VOICE_GENERATOR"
                      },
                      "created_at": { "type": "string", "format": "date-time" },
                      "enabled": {
                        "type": "boolean",
                        "description": "Whether this resource is active. If false, it is deleted."
                      },
                      "credits_charged": {
                        "type": "integer",
                        "example": 2,
                        "description": "The amount of credits deducted from your account to generate the audio. We charge credits right when the request is made. \n\nIf an error occurred while generating the audio, credits will be refunded and this field will be updated to include the refund."
                      },
                      "downloads": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://videos.magichour.ai/id/output.wav"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "example": "2024-10-19T05:16:19.027Z"
                            }
                          },
                          "required": ["url", "expires_at"],
                          "description": "The download url and expiration date of the audio project"
                        }
                      },
                      "error": {
                        "type": "object",
                        "properties": {
                          "message": {
                            "type": "string",
                            "description": "Details on the reason why a failure happened.",
                            "example": "Please use an image with a detectable face"
                          },
                          "code": {
                            "type": "string",
                            "example": "no_source_face",
                            "description": "An error code to indicate why a failure happened."
                          }
                        },
                        "required": ["message", "code"],
                        "nullable": true,
                        "description": "In the case of an error, this object will contain the error encountered during video render",
                        "example": null
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "status",
                      "type",
                      "created_at",
                      "enabled",
                      "credits_charged",
                      "downloads",
                      "error"
                    ],
                    "description": "Success"
                  }
                },
                "required": ["type", "payload"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Success message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Success"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          }
        }
      }
    },
    "/audio.completed": {
      "post": {
        "description": "The request example illustrates what your webhook endpoint should expect.",
        "summary": "Audio Completed",
        "tags": ["Audio Events"],
        "parameters": [
          {
            "name": "magic-hour-event-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.",
              "example": "3e771b50c..."
            }
          },
          {
            "name": "magic-hour-event-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.",
              "example": "1730742038"
            }
          }
        ],
        "operationId": "audio.completed",
        "requestBody": {
          "description": "Body",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "enum": ["audio.completed"] },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "cuid-example",
                        "description": "Unique ID of the audio. Use it with the [Get audio Project API](https://docs.magichour.ai/api-reference/audio-projects/get-audio-details) to fetch status and downloads."
                      },
                      "name": {
                        "type": "string",
                        "nullable": true,
                        "description": "The name of the audio.",
                        "example": "Example Name"
                      },
                      "status": {
                        "type": "string",
                        "enum": ["draft", "queued", "rendering", "complete", "error", "canceled"],
                        "description": "The status of the audio.\n\n- `draft` - the project was created but has not been submitted for rendering\n- `queued` - the job is waiting for an available server\n- `rendering` - the job is being processed; the `audio.started` webhook event fires when rendering begins\n- `complete` - the job finished successfully; fires `audio.completed`\n- `error` - the job failed during processing; fires `audio.errored`\n- `canceled` - the job was manually canceled (for example from the Magic Hour web app)\n\n**Note:** `rendering`, `complete`, and `error` have matching webhook events; `canceled` does not - a canceled job emits no webhook event, so poll this endpoint to detect cancellation.",
                        "example": "complete"
                      },
                      "type": {
                        "type": "string",
                        "description": "The type of the audio project. Possible values are AUDIO_TRANSLATOR, VOICE_GENERATOR, VOICE_CHANGER, VOICE_CLONER, VIDEO_TO_AUDIO, MUSIC_GENERATOR, SOUND_EFFECT_GENERATOR",
                        "example": "VOICE_GENERATOR"
                      },
                      "created_at": { "type": "string", "format": "date-time" },
                      "enabled": {
                        "type": "boolean",
                        "description": "Whether this resource is active. If false, it is deleted."
                      },
                      "credits_charged": {
                        "type": "integer",
                        "example": 2,
                        "description": "The amount of credits deducted from your account to generate the audio. We charge credits right when the request is made. \n\nIf an error occurred while generating the audio, credits will be refunded and this field will be updated to include the refund."
                      },
                      "downloads": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://videos.magichour.ai/id/output.wav"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "example": "2024-10-19T05:16:19.027Z"
                            }
                          },
                          "required": ["url", "expires_at"],
                          "description": "The download url and expiration date of the audio project"
                        }
                      },
                      "error": {
                        "type": "object",
                        "properties": {
                          "message": {
                            "type": "string",
                            "description": "Details on the reason why a failure happened.",
                            "example": "Please use an image with a detectable face"
                          },
                          "code": {
                            "type": "string",
                            "example": "no_source_face",
                            "description": "An error code to indicate why a failure happened."
                          }
                        },
                        "required": ["message", "code"],
                        "nullable": true,
                        "description": "In the case of an error, this object will contain the error encountered during video render",
                        "example": null
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "status",
                      "type",
                      "created_at",
                      "enabled",
                      "credits_charged",
                      "downloads",
                      "error"
                    ],
                    "description": "Success"
                  }
                },
                "required": ["type", "payload"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Success message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Success"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          }
        }
      }
    },
    "/audio.errored": {
      "post": {
        "description": "The request example illustrates what your webhook endpoint should expect.",
        "summary": "Audio Errored",
        "tags": ["Audio Events"],
        "parameters": [
          {
            "name": "magic-hour-event-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.",
              "example": "3e771b50c..."
            }
          },
          {
            "name": "magic-hour-event-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.",
              "example": "1730742038"
            }
          }
        ],
        "operationId": "audio.errored",
        "requestBody": {
          "description": "Body",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "enum": ["audio.errored"] },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "cuid-example",
                        "description": "Unique ID of the audio. Use it with the [Get audio Project API](https://docs.magichour.ai/api-reference/audio-projects/get-audio-details) to fetch status and downloads."
                      },
                      "name": {
                        "type": "string",
                        "nullable": true,
                        "description": "The name of the audio.",
                        "example": "Example Name"
                      },
                      "status": {
                        "type": "string",
                        "enum": ["draft", "queued", "rendering", "complete", "error", "canceled"],
                        "description": "The status of the audio.\n\n- `draft` - the project was created but has not been submitted for rendering\n- `queued` - the job is waiting for an available server\n- `rendering` - the job is being processed; the `audio.started` webhook event fires when rendering begins\n- `complete` - the job finished successfully; fires `audio.completed`\n- `error` - the job failed during processing; fires `audio.errored`\n- `canceled` - the job was manually canceled (for example from the Magic Hour web app)\n\n**Note:** `rendering`, `complete`, and `error` have matching webhook events; `canceled` does not - a canceled job emits no webhook event, so poll this endpoint to detect cancellation.",
                        "example": "complete"
                      },
                      "type": {
                        "type": "string",
                        "description": "The type of the audio project. Possible values are AUDIO_TRANSLATOR, VOICE_GENERATOR, VOICE_CHANGER, VOICE_CLONER, VIDEO_TO_AUDIO, MUSIC_GENERATOR, SOUND_EFFECT_GENERATOR",
                        "example": "VOICE_GENERATOR"
                      },
                      "created_at": { "type": "string", "format": "date-time" },
                      "enabled": {
                        "type": "boolean",
                        "description": "Whether this resource is active. If false, it is deleted."
                      },
                      "credits_charged": {
                        "type": "integer",
                        "example": 2,
                        "description": "The amount of credits deducted from your account to generate the audio. We charge credits right when the request is made. \n\nIf an error occurred while generating the audio, credits will be refunded and this field will be updated to include the refund."
                      },
                      "downloads": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri",
                              "example": "https://videos.magichour.ai/id/output.wav"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "example": "2024-10-19T05:16:19.027Z"
                            }
                          },
                          "required": ["url", "expires_at"],
                          "description": "The download url and expiration date of the audio project"
                        }
                      },
                      "error": {
                        "type": "object",
                        "properties": {
                          "message": {
                            "type": "string",
                            "description": "Details on the reason why a failure happened.",
                            "example": "Please use an image with a detectable face"
                          },
                          "code": {
                            "type": "string",
                            "example": "no_source_face",
                            "description": "An error code to indicate why a failure happened."
                          }
                        },
                        "required": ["message", "code"],
                        "nullable": true,
                        "description": "In the case of an error, this object will contain the error encountered during video render",
                        "example": null
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "status",
                      "type",
                      "created_at",
                      "enabled",
                      "credits_charged",
                      "downloads",
                      "error"
                    ],
                    "description": "Success"
                  }
                },
                "required": ["type", "payload"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Success message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Success"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string", "example": "Error message from your endpoint" }
                  },
                  "required": ["message"],
                  "description": "Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "info": {
    "title": "Magic Hour Webhook",
    "version": "beta",
    "description": "## Introduction \n\nConfiguring a webhook allow Magic Hour to notify you whenever an event occurs in your account. To get started, create a webhook [here](https://magichour.ai/developer?tab=webhooks&utm_source=docs&utm_medium=referral&utm_campaign=webhook-reference).\n\nAPI documentation can be found [here](https://docs.magichour.ai/webhook-reference).\n\nIf you have any questions, please reach out to us via [discord](https://discord.gg/JX5rgsZaJp).\n\n## Securing your endpoint\n\nEvents includes two headers to help verify the authenticity of the payload. \n- `magic-hour-event-signature` - A 64-character hexadecimal HMAC-SHA256 signature. Use the complete webhook secret, not an API key, to sign the timestamp header, a period, and the exact raw request body.\n- `magic-hour-event-timestamp` - Unix time in seconds for this delivery attempt. Reject missing or invalid timestamps; a five-minute window before or after the current time is recommended to limit replay.\n\nVerify the signature and timestamp before accepting production events. Read the entire raw request body before parsing JSON.\n\nTo compute the signature to verify:\n1. Create a `signed_payload` by concatenating:\n      - the timestamp header exactly as received\n      - the character `.`\n      - the exact raw UTF-8 request body, including both `type` and `payload`\n\nDo not parse and re-stringify the body or sign `payload` alone.\n\n2. Compute HMAC-SHA256 with the complete webhook secret as the key and `signed_payload` as the message. Do not strip the secret prefix or use your API key.\n3. Compare its hexadecimal digest with `magic-hour-event-signature` using a constant-time comparison. Reject missing or invalid headers, mismatches, and timestamps outside your configured past/future tolerance. See the [secure-handler guide](https://docs.magichour.ai/integration/webhook/secure-handler) for complete Python and Node examples.\n\n## Retry behavior\n\nFailed deliveries, including timeouts and non-`2XX` responses, are scheduled after 1, 2, 4, 8... minutes, capped at 60 minutes, until the event is more than 24 hours old. These are scheduled delays, not guaranteed delivery times. Verify and durably accept the event before returning `2XX`; acknowledge promptly. Timestamp and signature are regenerated for every delivery attempt.\n\nDisabled webhooks do not receive pending deliveries. Those events remain subject to the same expiry; re-enabling a webhook allows pending events to resume before expiry.\n",
    "termsOfService": "https://magichour.ai/terms-of-service"
  },
  "servers": [{ "url": "https://your-webhook.com" }],
  "tags": [
    { "name": "Image Events", "description": "Events related to image" },
    { "name": "Video Events", "description": "Events related to video" },
    { "name": "Audio Events", "description": "Events related to audio" }
  ]
}
