diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 56f7366..bb5e142 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,13 +2,14 @@ repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
     rev: v4.5.0
     hooks:
+      - id: check-ast
       - id: trailing-whitespace
         args: [--markdown-linebreak-ext=md]
       - id: end-of-file-fixer
       - id: mixed-line-ending
 
   - repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: v0.1.11
+    rev: v0.1.14
     hooks:
       - id: ruff
         args: [--fix, --exit-non-zero-on-fix]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1513491..d109778 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## 2024-01-23
+
+- v24.1.2
+- controlnet 모델에 `Passthrough` 옵션 추가. 입력으로 들어온 컨트롤넷 옵션을 그대로 사용
+- fastapi 엔드포인트 추가
+
 ## 2024-01-10
 
 - v24.1.1
diff --git a/adetailer/__version__.py b/adetailer/__version__.py
index 9696595..869aead 100644
--- a/adetailer/__version__.py
+++ b/adetailer/__version__.py
@@ -1 +1 @@
-__version__ = "24.1.1"
+__version__ = "24.1.2"
diff --git a/adetailer/args.py b/adetailer/args.py
index f205347..130bdea 100644
--- a/adetailer/args.py
+++ b/adetailer/args.py
@@ -18,9 +18,6 @@
     validator,
 )
 
-cn_model_regex = r".*(inpaint|tile|scribble|lineart|openpose|depth).*|^None$"
-cn_module_regex = r".*(inpaint|tile|pidi|lineart|openpose|depth).*|^None$"
-
 
 @dataclass
 class SkipImg2ImgOrig:
@@ -79,8 +76,8 @@ class ADetailerArgs(BaseModel, extra=Extra.forbid):
     ad_use_clip_skip: bool = False
     ad_clip_skip: conint(ge=1, le=12) = 1
     ad_restore_face: bool = False
-    ad_controlnet_model: constr(regex=cn_model_regex) = "None"
-    ad_controlnet_module: constr(regex=cn_module_regex) = "None"
+    ad_controlnet_model: str = "None"
+    ad_controlnet_module: str = "None"
     ad_controlnet_weight: confloat(ge=0.0, le=1.0) = 1.0
     ad_controlnet_guidance_start: confloat(ge=0.0, le=1.0) = 0.0
     ad_controlnet_guidance_end: confloat(ge=0.0, le=1.0) = 1.0
diff --git a/adetailer/ui.py b/adetailer/ui.py
index 9733aed..b6318ff 100644
--- a/adetailer/ui.py
+++ b/adetailer/ui.py
@@ -574,7 +574,7 @@ def inpainting(w: Widgets, n: int, is_img2img: bool, webui_info: WebuiInfo):
 
 def controlnet(w: Widgets, n: int, is_img2img: bool):
     eid = partial(elem_id, n=n, is_img2img=is_img2img)
-    cn_models = ["None", *get_cn_models()]
+    cn_models = ["None", "Passthrough", *get_cn_models()]
 
     with gr.Row(variant="panel"):
         with gr.Column(variant="compact"):
diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py
index eb37a7d..6cc5110 100644
--- a/scripts/!adetailer.py
+++ b/scripts/!adetailer.py
@@ -10,7 +10,7 @@
 from functools import partial
 from pathlib import Path
 from textwrap import dedent
-from typing import Any, NamedTuple
+from typing import TYPE_CHECKING, Any, NamedTuple
 
 import gradio as gr
 import torch
@@ -52,6 +52,9 @@
 from modules.sd_samplers import all_samplers
 from modules.shared import cmd_opts, opts, state
 
+if TYPE_CHECKING:
+    from fastapi import FastAPI
+
 no_huggingface = getattr(cmd_opts, "ad_no_huggingface", False)
 adetailer_dir = Path(paths.models_path, "adetailer")
 extra_models_dir = shared.opts.data.get("ad_extra_models_dir", "")
@@ -424,7 +427,6 @@ def script_args_copy(script_args):
     def script_filter(self, p, args: ADetailerArgs):
         script_runner = copy(p.scripts)
         script_args = self.script_args_copy(p.script_args)
-        self.disable_controlnet_units(script_args)
 
         ad_only_seleted_scripts = opts.data.get("ad_only_seleted_scripts", True)
         if not ad_only_seleted_scripts:
@@ -515,9 +517,12 @@ def get_i2i_p(self, p, args: ADetailerArgs, image):
         i2i._ad_disabled = True
         i2i._ad_inner = True
 
-        if args.ad_controlnet_model != "None":
+        if args.ad_controlnet_model != "Passthrough":
+            self.disable_controlnet_units(i2i.script_args)
+
+        if args.ad_controlnet_model not in ["None", "Passthrough"]:
             self.update_controlnet_args(i2i, args)
-        else:
+        elif args.ad_controlnet_model != "Passthrough":
             i2i.control_net_enabled = False
 
         return i2i
@@ -972,6 +977,24 @@ def on_before_ui():
         )
 
 
+# api
+
+
+def add_api_endpoints(_: gr.Blocks, app: FastAPI):
+    @app.get("/adetailer/v1/version")
+    def version():
+        return {"version": __version__}
+
+    @app.get("/adetailer/v1/schema")
+    def schema():
+        return ADetailerArgs.schema()
+
+    @app.get("/adetailer/v1/ad_model")
+    def ad_model():
+        return {"ad_model": list(model_mapping)}
+
+
 script_callbacks.on_ui_settings(on_ui_settings)
 script_callbacks.on_after_component(on_after_component)
+script_callbacks.on_app_started(add_api_endpoints)
 script_callbacks.on_before_ui(on_before_ui)