Face Transform (Ace Data Cloud)
Swap, age and beautify a face without touching your GPU
- status
- task_id
- media_urls_json
- first_media_url
- result_json
- trace_id
What this node is actually for
Face work in local ComfyUI is a chore. A swap means ReActor and InsightFace, a recognition stack with non-commercial models that spent years as one of the worst installs in the ecosystem. Identity conditioning means IP-Adapter FaceID, InstantID or PuLID, plus the right LoRAs.
AceFaceTransformFaceTransform skips all of it. It runs nothing locally, needs no checkpoint, and works without a GPU, because it isn't generating - it posts your image to api.acedata.cloud and hands back whatever the service did to it. Swap, beautify, age, gender, cartoon, plus two analysis actions. Paid per call in Ace Credits.
Which makes it part of the API-wrapper category, with the usual tradeoffs: your photo leaves the machine, the vendor's moderation applies, every run is metered. The part I'd actually weigh is that Ace Data Cloud has essentially no community footprint - searching the r/comfyui + r/StableDiffusion + r/civitai corpus for "acedata", "ace data cloud" and "facedify" returns zero threads. Nobody has vetted this pack for you, and a node that carries your key and phones out by design is the exact shape that got weaponized once already (ComfyUI_LLMVISION). Treat it as a vendor SDK: keep the key out of workflow JSON, don't send anything private.
How it works
One Dify tool, face_transform, seven endpoints, picked by the action dropdown: keypoints → /face/analyze (landmarks, analysis only), beautify → /face/beautify, age → /face/change-age, gender → /face/change-gender, swap → /face/swap, cartoon → /face/cartoon, liveness → /face/detect-live.
Before the request goes out, your parameters_json is validated with jsonschema against a pinned copy of the published API contract - a misspelled key or a string where a number belongs fails locally, for free, instead of as a paid 400. Widget values are coerced to the contract's types, so 70 typed into a STRING box still sends a number.
The inputs that matter
parameters_json (default {}) is the honest way to drive this node - it takes the API's fields verbatim. action is the selector, and its first entry, (from JSON/default), defers to action inside your JSON or to the API's own default (keypoints).
Fourteen STRING widgets exist to spare you typing JSON, and only some apply per action: smoothing, whitening, face_lifting, eye_enlarging for beautify; age_infos for age (an array, [{"age": 70}]); gender_infos for gender ([{"gender": 1}]); source_image_url and target_image_url for swap, plus optional timeout and callback_url; and image_url, mode, face_model_version, need_rotate_detection on the analysis side. A nonempty widget overrides the matching JSON field.
Where people get burned: every image input here is a URL string, not an IMAGE. There is no upload anywhere in the node. Your photo has to already sit at a public https address that Ace's servers can fetch, and wiring a Load Image node into that widget is a type error, not a shortcut. Same for swap's source and target.
{"image_url": "https://example.com/portrait.jpg", "smoothing": 0.6, "whitening": 0.3, "face_lifting": 0.4, "eye_enlarging": 0.2}
Outputs, and what they wire into
Six STRING outputs: status, task_id, media_urls_json, first_media_url, result_json, trace_id, plus a text panel on the node. It's an output node, so it runs on Queue whether or not anything is downstream.
Read status first - succeeded, pending, partial or failed. For keypoints and liveness there may be no media URL at all; the answer is in result_json. swap is submitted with async forced on, so expect pending and a task_id rather than an image - and this pack ships no task-retrieve node, so you're reading that ID back through the Ace platform. Don't reflexively re-run: paid calls are never retried after a connection failure, and a second run is a second charge.
Your result comes back as a URL, so first_media_url → AceFaceTransformLoadImage → an IMAGE tensor you can preview, save, upscale or detail.
Install
Manager: search Face Transform, confirm publisher acedatacloud (Registry id face-transform), install, restart. Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/AceDataCloud/FaceTransformComfyUI
cd FaceTransformComfyUI && pip install -r requirements.txt
Dependencies are requests and jsonschema - no models, no CUDA, nothing big to download. The README asks for ComfyUI 0.39.0+ for its native media loaders. examples/01-configure-and-run.json is worth importing (File → Open) for the shape; it's a config template and won't submit a paid request by itself.
Then the box people forget - the key lives in the ComfyUI process environment, and you restart the app, not the page:
export ACEDATACLOUD_API_KEY="your-key-here" # or ACEDATACLOUD_API_TOKEN
Common issues
Set ACEDATACLOUD_API_KEY in the ComfyUI process environment and restart. The variable wasn't visible to the process. Export it in the shell, service file or launcher that starts ComfyUI, then restart ComfyUI - reloading the browser tab does nothing.
Invalid image_url: does not meet the API's required constraint. Contract validation fired before the request, and it's naming what's missing or mistyped. For age and gender that's usually the age_infos / gender_infos array, which the contract requires as JSON.
Nothing to load into the loader. Check status first - a pending task has no URL, and the loader only accepts Ace CDN hosts anyway.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| parameters_json | STRING | {} | — |
| action | COMBO | (from JSON/default) | 8 options: (from JSON/default), keypoints, beautify, age, gender, swap, +2 |
| mode | STRING | — | |
| image_url | STRING | — | |
| face_model_version | STRING | — | |
| need_rotate_detection | STRING | — | |
| smoothing | STRING | — | |
| whitening | STRING | — | |
| face_lifting | STRING | — | |
| eye_enlarging | STRING | — | |
| age_infos | STRING | — | |
| gender_infos | STRING | — | |
| timeout | STRING | — | |
| callback_url | STRING | — | |
| source_image_url | STRING | — | |
| target_image_url | STRING | — |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| status | STRING | — |
| task_id | STRING | — |
| media_urls_json | STRING | — |
| first_media_url | STRING | — |
| result_json | STRING | — |
| trace_id | STRING | — |