{
  "openapi": "3.1.0",
  "info": {
    "title": "DeepSwapAI Face Swap API",
    "version": "2026-08-14",
    "description": "The current DeepSwapAI.com API for photo, batch photo, mapped group-photo, video, and GIF face swap tasks. Submit multipart media, receive a taskId, and poll the same workflow endpoint for completion.",
    "termsOfService": "https://deepswapai.com/terms",
    "contact": {
      "name": "DeepSwapAI Support",
      "url": "https://deepswapai.com/contact",
      "email": "support@deepswapai.com"
    },
    "x-last-verified": "2026-08-14"
  },
  "externalDocs": {
    "description": "Human-readable API documentation",
    "url": "https://deepswapai.com/face-swap-api"
  },
  "x-postman-collection": "https://deepswapai.com/deepswapai-face-swap-api.postman_collection.json",
  "servers": [
    {
      "url": "https://deepswapai.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Photo",
      "description": "One face reference and one target photo."
    },
    {
      "name": "Batch photo",
      "description": "One face to multiple photos, or multiple source faces to one target photo."
    },
    {
      "name": "Group photo",
      "description": "Map up to ten replacement identities into one group photo."
    },
    {
      "name": "Video",
      "description": "One face reference and one MP4, MOV, or WebM target."
    },
    {
      "name": "GIF",
      "description": "One face reference and one GIF or short MP4/WebM clip."
    }
  ],
  "paths": {
    "/api/ai-tasks": {
      "post": {
        "tags": [
          "Photo"
        ],
        "summary": "Create a photo face swap task",
        "operationId": "createPhotoFaceSwap",
        "description": "Costs 3 credits. Each image can be up to 30 MB. Accepted inputs are JPEG/JPG, PNG, WebP, GIF, BMP, HEIC, or HEIF with validated image content.",
        "x-credit-cost": {
          "credits": 3,
          "unit": "task"
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "sourceImage",
                  "targetImage"
                ],
                "properties": {
                  "sourceImage": {
                    "type": "string",
                    "format": "binary",
                    "description": "Face reference image; maximum 30 MB."
                  },
                  "targetImage": {
                    "type": "string",
                    "format": "binary",
                    "description": "Target scene image; maximum 30 MB."
                  },
                  "faceRestoreVisibility": {
                    "type": "number",
                    "default": 1,
                    "description": "Optional face restoration visibility value."
                  },
                  "restoreVisibility": {
                    "type": "number",
                    "default": 1,
                    "description": "Optional restoration visibility value."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task accepted for asynchronous processing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "get": {
        "tags": [
          "Photo"
        ],
        "summary": "Get a photo face swap task",
        "operationId": "getPhotoFaceSwap",
        "description": "Poll this endpoint until status is COMPLETED, FAILED, or CANCELLED. Polling is the currently supported completion mechanism; webhook callbacks are not available.",
        "parameters": [
          {
            "name": "taskId",
            "in": "query",
            "required": true,
            "description": "The taskId returned by the matching POST request.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current task state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/ai-tasks/batch-face-swap": {
      "post": {
        "tags": [
          "Batch photo"
        ],
        "summary": "Create a batch photo face swap task",
        "operationId": "createBatchPhotoFaceSwap",
        "description": "Costs 3 credits per generated output. A request can contain at most 20 images, 30 MB per image, and 95 MB combined. JPEG, PNG, WebP, AVIF, HEIC, and HEIF are accepted.",
        "x-credit-cost": {
          "credits": 3,
          "unit": "generated output"
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "mode",
                  "sourceImages",
                  "targetImages"
                ],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "singleToMulti",
                      "multiToSingle"
                    ],
                    "description": "singleToMulti requires one source image and one or more targets. multiToSingle requires one or more source images and one target."
                  },
                  "sourceImages": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 19,
                    "items": {
                      "type": "string",
                      "format": "binary",
                      "description": "Source face image."
                    }
                  },
                  "targetImages": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 19,
                    "items": {
                      "type": "string",
                      "format": "binary",
                      "description": "Target image."
                    }
                  }
                }
              },
              "encoding": {
                "sourceImages": {
                  "style": "form",
                  "explode": true
                },
                "targetImages": {
                  "style": "form",
                  "explode": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task accepted for asynchronous processing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "get": {
        "tags": [
          "Batch photo"
        ],
        "summary": "Get a batch photo face swap task",
        "operationId": "getBatchPhotoFaceSwap",
        "description": "Poll this endpoint until status is COMPLETED, FAILED, or CANCELLED. Polling is the currently supported completion mechanism; webhook callbacks are not available.",
        "parameters": [
          {
            "name": "taskId",
            "in": "query",
            "required": true,
            "description": "The taskId returned by the matching POST request.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current task state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/ai-tasks/multi-face-swap": {
      "post": {
        "tags": [
          "Group photo"
        ],
        "summary": "Create a mapped group-photo face swap task",
        "operationId": "createGroupPhotoFaceSwap",
        "description": "Costs 3 credits per replacement face. Supply one group photo, contiguous targetFace0 through targetFace9 fields, and one corresponding faceBox mapping for every replacement. The total request can be up to 95 MB.",
        "x-credit-cost": {
          "credits": 3,
          "unit": "replacement face",
          "maximumReplacementFaces": 10
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "sourceImage",
                  "targetFace0",
                  "faceBox0"
                ],
                "properties": {
                  "sourceImage": {
                    "type": "string",
                    "format": "binary",
                    "description": "Group photo containing the people to replace. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "targetFace0": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 0. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox0": {
                    "type": "string",
                    "description": "Required when targetFace0 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  },
                  "targetFace1": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 1. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox1": {
                    "type": "string",
                    "description": "Required when targetFace1 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  },
                  "targetFace2": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 2. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox2": {
                    "type": "string",
                    "description": "Required when targetFace2 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  },
                  "targetFace3": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 3. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox3": {
                    "type": "string",
                    "description": "Required when targetFace3 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  },
                  "targetFace4": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 4. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox4": {
                    "type": "string",
                    "description": "Required when targetFace4 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  },
                  "targetFace5": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 5. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox5": {
                    "type": "string",
                    "description": "Required when targetFace5 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  },
                  "targetFace6": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 6. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox6": {
                    "type": "string",
                    "description": "Required when targetFace6 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  },
                  "targetFace7": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 7. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox7": {
                    "type": "string",
                    "description": "Required when targetFace7 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  },
                  "targetFace8": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 8. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox8": {
                    "type": "string",
                    "description": "Required when targetFace8 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  },
                  "targetFace9": {
                    "type": "string",
                    "format": "binary",
                    "description": "Replacement identity image 9. Numbered fields must be contiguous from targetFace0. JPEG, PNG, or WebP; maximum 30 MB."
                  },
                  "faceBox9": {
                    "type": "string",
                    "description": "Required when targetFace9 is supplied. JSON face mapping: {\"faceIndex\":number,\"x\":number,\"y\":number,\"width\":number,\"height\":number}. faceIndex is the optional 0-based global detected-face index; include it when leaving other detected faces unchanged. Supply exactly one mapping for every replacement face.",
                    "example": "{\"faceIndex\":0,\"x\":120,\"y\":80,\"width\":180,\"height\":180}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task accepted for asynchronous processing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "get": {
        "tags": [
          "Group photo"
        ],
        "summary": "Get a mapped group-photo task",
        "operationId": "getGroupPhotoFaceSwap",
        "description": "Poll this endpoint until status is COMPLETED, FAILED, or CANCELLED. Polling is the currently supported completion mechanism; webhook callbacks are not available.",
        "parameters": [
          {
            "name": "taskId",
            "in": "query",
            "required": true,
            "description": "The taskId returned by the matching POST request.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current task state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/ai-tasks/video": {
      "post": {
        "tags": [
          "Video"
        ],
        "summary": "Create a video face swap task",
        "operationId": "createVideoFaceSwap",
        "description": "The face image can be up to 30 MB. The MP4, MOV, or WebM target can be up to 95 MB and 300 seconds, and both uploaded files must be 95 MB or smaller combined. One face reference is applied to the primary on-camera person; separate reference mapping for multiple people in one video is not supported. The workflow changes the face only and preserves the original body, clothing, hands, products, text, logos, framing, and scene. Duration is rounded up. Processing costs 2 credits per second with a 6-credit minimum at either input tier.",
        "x-credit-cost": {
          "faceOnly": {
            "720p": {
              "creditsPerRoundedUpSecond": 2,
              "minimumCredits": 6
            },
            "1080p": {
              "creditsPerRoundedUpSecond": 2,
              "minimumCredits": 6
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "sourceImage",
                  "targetVideo"
                ],
                "properties": {
                  "sourceImage": {
                    "type": "string",
                    "format": "binary",
                    "description": "Face reference image; JPEG, PNG, WebP, HEIC, or HEIF; maximum 30 MB."
                  },
                  "targetVideo": {
                    "type": "string",
                    "format": "binary",
                    "description": "Target MP4, MOV, or WebM video; maximum 95 MB and 300 seconds."
                  },
                  "quality": {
                    "type": "string",
                    "enum": [
                      "720p",
                      "1080p"
                    ],
                    "default": "1080p"
                  },
                  "preserveScene": {
                    "type": "boolean",
                    "const": true,
                    "default": true,
                    "description": "Must be true. The current video workflow changes the face only and preserves the original body, clothing, hands, products, text, logos, framing, and scene."
                  },
                  "faceRestoreVisibility": {
                    "type": "number",
                    "default": 0.85,
                    "description": "Optional face restoration visibility value."
                  },
                  "restoreVisibility": {
                    "type": "number",
                    "default": 0.7,
                    "description": "Optional restoration visibility value."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task accepted for asynchronous processing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "get": {
        "tags": [
          "Video"
        ],
        "summary": "Get a video face swap task",
        "operationId": "getVideoFaceSwap",
        "description": "Poll this endpoint until status is COMPLETED, FAILED, or CANCELLED. Polling is the currently supported completion mechanism; webhook callbacks are not available.",
        "parameters": [
          {
            "name": "taskId",
            "in": "query",
            "required": true,
            "description": "The taskId returned by the matching POST request.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current task state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/ai-tasks/gif": {
      "post": {
        "tags": [
          "GIF"
        ],
        "summary": "Create a GIF or short-clip face swap task",
        "operationId": "createGifFaceSwap",
        "description": "The face image can be up to 30 MB. The GIF, MP4, or WebM target can be up to 95 MB and 30 seconds, and both uploaded files must be 95 MB or smaller combined. Duration is rounded up. Cost is 3 credits per second with a 6-credit minimum.",
        "x-credit-cost": {
          "creditsPerRoundedUpSecond": 3,
          "minimumCredits": 6
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "sourceImage",
                  "targetGif"
                ],
                "properties": {
                  "sourceImage": {
                    "type": "string",
                    "format": "binary",
                    "description": "Face reference image; JPEG, PNG, WebP, HEIC, or HEIF; maximum 30 MB."
                  },
                  "targetGif": {
                    "type": "string",
                    "format": "binary",
                    "description": "Target GIF, MP4, or WebM animation; maximum 95 MB and 30 seconds. Target and face-reference uploads must be 95 MB or smaller combined."
                  },
                  "faceRestoreVisibility": {
                    "type": "number",
                    "default": 0.85,
                    "description": "Optional face restoration visibility value."
                  },
                  "restoreVisibility": {
                    "type": "number",
                    "default": 0.7,
                    "description": "Optional restoration visibility value."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task accepted for asynchronous processing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/EmailNotVerified"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "get": {
        "tags": [
          "GIF"
        ],
        "summary": "Get a GIF or short-clip face swap task",
        "operationId": "getGifFaceSwap",
        "description": "Poll this endpoint until status is COMPLETED, FAILED, or CANCELLED. Polling is the currently supported completion mechanism; webhook callbacks are not available.",
        "parameters": [
          {
            "name": "taskId",
            "in": "query",
            "required": true,
            "description": "The taskId returned by the matching POST request.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current task state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "DeepSwapAI API key",
        "description": "Send the key once in Authorization: Bearer YOUR_API_KEY. Keys are shown once when created and stored by DeepSwapAI only as SHA-256 hashes."
      }
    },
    "schemas": {
      "TaskStatus": {
        "type": "string",
        "enum": [
          "PENDING",
          "PROCESSING",
          "COMPLETED",
          "FAILED",
          "CANCELLED"
        ]
      },
      "TaskAccepted": {
        "type": "object",
        "required": [
          "taskId",
          "status"
        ],
        "properties": {
          "taskId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/TaskStatus"
          },
          "requiredCredits": {
            "type": "integer",
            "minimum": 0,
            "description": "Included by workflows that return the calculated credit requirement."
          },
          "estimatedCredits": {
            "type": "integer",
            "minimum": 0,
            "description": "Included by the GIF workflow."
          },
          "quality": {
            "type": "string",
            "enum": [
              "720p",
              "1080p"
            ]
          }
        }
      },
      "Task": {
        "type": "object",
        "required": [
          "id",
          "taskType",
          "status",
          "costCredits",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "taskType": {
            "type": "string",
            "enum": [
              "IMAGE_SWAP",
              "BATCH_FACE_SWAP",
              "MULTI_FACE_SWAP",
              "VIDEO_SWAP",
              "GIF_FACE_SWAP"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/TaskStatus"
          },
          "costCredits": {
            "type": "integer",
            "minimum": 0
          },
          "parameters": {
            "type": "object",
            "additionalProperties": true
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "required": {
            "type": "integer"
          },
          "available": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "resetSeconds": {
            "type": "integer"
          }
        }
      }
    },
    "responses": {
      "ValidationError": {
        "description": "A field, file type, size, duration, mapping, or taskId is invalid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The Bearer API key is missing or invalid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InsufficientCredits": {
        "description": "The account does not have enough credits for the task.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "EmailNotVerified": {
        "description": "The account email must be verified before creating a task.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No task with that taskId belongs to the authenticated account.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "The API-key rate limit was exceeded or another generation is already in progress for the account.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait when the API-key limit is exceeded.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Limit": {
            "description": "Current per-key request limit when supplied.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "description": "Remaining requests in the current window when supplied.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServerError": {
        "description": "The task could not be accepted or read because of a server error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
