Layer tools
A Layer tool is one job from the Layer app’s tools menu, such as upscaling an image, making a seamless texture or reframing a video. Each has a key, such as image.upscale, and runs on any base model in its pool. A tool asks only for the fields its job needs and fixes the rest: Create texture always draws a tileable image, so its form has no tileability field. The asset a tool run makes reopens in that tool in the Layer app.
Reach for a tool when the job is one of these. To set every parameter a model takes yourself, run the base model instead. Tools are on /v2 only.
A tool run is three calls: list the tools, read the schema of the one you want on a model, then run it with that schema’s fields.
Listing tools
Section titled “Listing tools”GET /v2/workspaces/{workspace_id}/inference-tools lists the tools you can run in the workspace, in the order the Layer app’s tools menu shows them. It’s paginated and takes these filters:
| Filter | Purpose |
|---|---|
modality |
Only the tools that make this modality, such as image or video. |
group |
generate for the tools that make something new, edit for those that change an asset. |
project_id, session_id |
Answer for the project the work lands in. See The default model. |
curl "https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/inference-tools?group=edit&modality=image" \ -H "Authorization: Bearer $LAYER_TOKEN"{ "inference_tools": [ { "key": "image.upscale", "name": "Upscale image", "description": "Add resolution and detail.", "group": "edit", "modality": "image", "source": { "modality": "image", "takes_mask": false }, "batches_outputs": true, "base_model_ids": ["clarity-upscaler", "esrgan-upscaler", "topaz-wonder-3.5-upscale-image", "…"], "default_base_model_id": "topaz-wonder-3.5-upscale-image", "concept": "upscale", "prompt_placeholder": "Describe the detail to add", "primary": true, "keywords": ["enhance", "super resolution", "hd", "4k"] } // … ], "pagination": { "next_cursor": "…", "has_more_results": true, "total_count": 23 }}| Field | Meaning |
|---|---|
key |
The tool’s key, used in the tool’s paths and as inference_tool_key on a model run. |
group, modality |
Whether the tool makes something new or changes an asset, and the type of asset it makes. |
source |
What an edit works on (modality) and whether it can keep its edit to an area a mask marks (takes_mask). null for a tool that makes something new. |
base_model_ids |
The models you can run the tool on, in catalog order. |
default_base_model_id |
The model a run takes when it names none. |
batches_outputs |
Whether a run can make more than one output (batch_size above 1). |
name, description, concept, prompt_placeholder, primary, keywords |
What the Layer app shows for the tool: its name and blurb, its icon by concept name, the hint in its empty prompt, whether it’s an edit offered first on its asset type, and the extra terms a search matches it by. |
A tool none of whose models you can run is left out of the list.
The default model
Section titled “The default model”A run that names no base_model_id takes the tool’s default_base_model_id, picked in this order:
- The tool’s own default, where it has one.
- Else the first model you pinned in the Layer app that the tool runs on. Pins are per member, so two members can get different defaults for the same tool.
- Else the model Layer recommends for the tool’s job.
Only models you can run count. base_model_ids and the default follow the AI policy of the workspace, or of the project the work lands in: a project can allow fewer models than the workspace, or set other defaults. Pass the session_id you’ll run in, or the project_id of its project, and the list and the schema answer for that project. A run given that session_id takes the default the same way, and its response’s base_model_id names the model it took. A run given only a session_name picks its model under the workspace’s policy, since the session it names is found only once the run is accepted: when a project’s default matters, run with its session_id.
Reading a tool’s schema
Section titled “Reading a tool’s schema”GET /v2/workspaces/{workspace_id}/inference-tools/{inference_tool_key}/inference-schema returns the JSON Schema of what a run of the tool takes on one model: the one in base_model_id (by ID, slug, name or alias), or the default without it. It takes project_id and session_id as the list does.
curl "https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/inference-tools/image.upscale/inference-schema?base_model_id=topaz-wonder-3.5-upscale-image" \ -H "Authorization: Bearer $LAYER_TOKEN"{ "title": "Upscale image with Topaz Wonder 3.5", "type": "object", "additionalProperties": false, "properties": { "upscale_factor": { "type": "string", "default": "2", "title": "Upscale", "description": "How many times larger the output is than the source.", "x-layer": { "label": "Upscale", "order": 0, "options": [ { "label": "2×", "value": "2" }, { "label": "4×", "value": "4" } ] // … } }, "creativity": { /* … */ }, "face_enhancement": { /* … */ } // … }, "x-inference-tool-key": "image.upscale", "x-slug": "topaz-wonder-3.5-upscale-image", "x-source": { "modality": "image", "required": true, "takes_mask": false }}propertiesare the fields the tool shows on that model, ordered byx-layer.order. The fields the tool hides or fixes are left out, andadditionalProperties: falserefuses them. A field withx-layer.advancedis one the Layer app keeps under advanced settings.x-sourcedescribes an edit’s source, which is not a field:modalityis the type of asset it works on,requiredwhether this model needs one, andtakes_maskwhether a run on this model takes a mask. A tool that makes something new has nox-source.- A file field takes each file as
{ "file_id": "…", "weight": 1.0 }: the ID of a file in the workspace or of a public file, never a URL. Upload files first; see Files & uploads. - The fields differ from model to model, so read the schema again when the model changes.
An unknown key, or a tool none of whose models you can run, is a 404 with INFERENCE_TOOL_NOT_FOUND. A model you can’t run is a 404 with BASE_MODEL_NOT_FOUND, and one you can run that the tool doesn’t is a 422 with INFERENCE_TOOL_MODEL_NOT_IN_POOL.
Running a tool
Section titled “Running a tool”POST /v2/workspaces/{workspace_id}/inference-tools/{inference_tool_key}/inferences starts a run. To upscale an image four times:
# One key per run: reuse it for every retry of this run.IDEMPOTENCY_KEY="$(uuidgen)"
curl -X POST "https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/inference-tools/image.upscale/inferences" \ -H "Authorization: Bearer $LAYER_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $IDEMPOTENCY_KEY" \ -d '{ "base_model_id": "topaz-wonder-3.5-upscale-image", "source_file_id": "FILE_ID", "input": { "upscale_factor": "4" }, "session_name": "Upscales" }'# → { "inference_id": "…", "status": "in_progress", "base_model_id": "topaz-wonder-3.5-upscale-image",# "poll_interval_seconds": 10, "session_id": "…", "expected_events": [], … }The run is asynchronous: poll GET /v2/workspaces/{workspace_id}/inferences/{inference_id} until it ends, or pass a webhook. The Idempotency-Key header makes a retry that sends the same key return the first run instead of starting a second one; see Idempotency keys.
| Field | Meaning |
|---|---|
base_model_id |
One of the tool’s base_model_ids, by ID, slug, name or alias. Without it, the run takes the default. |
input |
The fields of the tool’s schema on that model. Send every field the schema lists in required; a field you leave out takes its default, and the tool sets the values it fixes. |
source_file_id |
The file an edit works on. Required where the schema says x-source.required; a tool that makes something new takes none. |
mask_file_id |
Only where the schema says x-source.takes_mask: an image the size of the source, white where the edit may change it and black where the source stays as it is. |
batch_size |
How many outputs the run makes, 1 to 16. More than 1 needs a tool with batches_outputs and a model that batches. |
reference_sets |
Reference sets to apply, as on POST .../inferences. A model that needs one, such as an ElevenLabs voice changer, refuses a run without it with REFERENCE_SET_REQUIRED. |
session_id, session_name |
The session the run lands in. The policy of its project decides the models the run may take. |
webhook |
Where to deliver the run’s terminal event. See Webhooks. |
An edit that sets no size keeps its source’s shape. The response adds base_model_id, the model the run takes, and reference_set_contributions, what each reference set contributed, to what a run of a base model returns.
A run the tool can’t make is refused before it starts. A model outside the tool’s pool is a 422 with INFERENCE_TOOL_MODEL_NOT_IN_POOL. A source the run needs or can’t take, a mask the model takes none of, or a batch where one output is made is a 422 with INVALID_INPUTS, its detail naming the field. See Error codes.
Pricing a run first
Section titled “Pricing a run first”POST /v2/workspaces/{workspace_id}/inference-tools/{inference_tool_key}/inferences/estimate prices a run without starting it. Send the body of the run without session_name and webhook. The estimate builds the run as the run does, so send the same source_file_id and session_id too: an upscale is priced from the size of its source, and a project’s policy can change the default model, and with it the price.
curl -X POST "https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/inference-tools/image.upscale/inferences/estimate" \ -H "Authorization: Bearer $LAYER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "base_model_id": "topaz-wonder-3.5-upscale-image", "source_file_id": "FILE_ID", "input": { "upscale_factor": "4" } }'| Field | Meaning |
|---|---|
estimated_price_creative_units |
What the run costs. |
workspace_balance_creative_units |
The workspace’s available balance. |
estimated_remaining_balance_creative_units |
The balance after the run. Can be negative. |
has_sufficient_creative_units |
Whether the workspace can afford the run. |
base_model_id |
The model the run takes. |
reference_set_contributions, reference_sets_degraded, reference_sets_warning |
What each reference set would contribute, and which would not. |
A body the run would refuse is refused here with the same status and error_code, so an estimate also checks a run before anything is spent. What the workspace has left is not checked here. Your rate limit and usage limit are checked when you submit the run, and the balance only as the run spends: a run accepted with a 202 can still end failed with error_code INSUFFICIENT_BALANCE or USAGE_LIMIT_EXCEEDED. Check has_sufficient_creative_units before you run, and handle those failures when you poll the run or read its webhook. See Creative Units & billing.
Webhooks
Section titled “Webhooks”A run takes a webhook, as POST .../inferences does, so its terminal event goes to your URL instead of you polling for it. The response’s expected_events lists the events that can arrive: of inference.completed, inference.failed and inference.cancelled, only the one that ends the run, plus the scoring events where the scope scores automatically. The delivery’s data is the body of GET .../inferences/{inference_id}, so it carries the run’s inference_tool_key and base_model_id. See Webhooks for the payload, signing and retries.
One run per file
Section titled “One run per file”A file slot whose schema carries x-layer.batchable: true, such as the reference images of image.edit and video.edit, is one the tool can run once per file, as the Layer app runs it. The API doesn’t fan a run out: a run takes every file you give it, together. For one run per file, send one run per file.
This is a different thing from batch_size, which asks one run for several outputs from the same inputs.
A tool key on a base model run
Section titled “A tool key on a base model run”A run of a base model can name the tool it is, so its asset reopens in that tool in the Layer app too. Reach for this when you already build runs from a model’s full form:
POST /v2/workspaces/{workspace_id}/base-models/{slug}/inferences?inference_tool_key=image.upscalevalidates the body against the model’s form for the tool’s job. A field you leave out takes the value the tool’s form sets (a value it fixes, or the default of a field it shows), except the prompt, which you always write.POST /v2/workspaces/{workspace_id}/inferencestakesinference_tool_keyin its body and runs the body as you send it. It must carry each value the tool fixes, and a reframe tool’s run must ask for the reframe.
Either way the run must be one the tool makes: a base_model_id from the tool’s base_model_ids, with every value the tool fixes left as the tool sets it. A run that isn’t is refused with a 422: UNKNOWN_INFERENCE_TOOL for a key that’s not a tool, INFERENCE_TOOL_MODEL_NOT_IN_POOL for another model or none, INFERENCE_TOOL_PIN_MISMATCH, its detail naming the field, for a changed or missing fixed value, and the other INFERENCE_TOOL_* codes in Error codes. The tool route above sets the fixed values for you, so prefer it when you start from the tool.
Reopening a past run in its tool
Section titled “Reopening a past run in its tool”GET /v2/workspaces/{workspace_id}/inferences/{inference_id} returns inference_tool_key, the tool the run was made with, beside the base_model_id it ran on. To reopen the run in its tool, read the tool’s schema on that model:
curl "https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/inference-tools/$INFERENCE_TOOL_KEY/inference-schema?base_model_id=$BASE_MODEL_ID" \ -H "Authorization: Bearer $LAYER_TOKEN"inference_tool_key is null for a run made without a tool, which reopens on its model alone.
A past run’s tool or model need not be one you can run now. Your policy may allow none of the tool’s models, another member may have made the run under a looser policy, the tool may have left the catalog, or the model may be disabled. The schema read says which, and what to show instead:
| Schema read | Why | What to do |
|---|---|---|
200 |
You can run the tool on the run’s model. | Open the tool on that model. |
404 INFERENCE_TOOL_NOT_FOUND |
No tool has the key any more, or you can run none of its models. | Fall back to the model alone: when GET .../base-models/{base_model_id} answers, open the model’s own form. Else say the tool is unavailable. |
404 BASE_MODEL_NOT_FOUND |
You can’t run the run’s model: it’s disabled or retired, or your policy doesn’t allow it. | Open the tool on your default (read the schema without base_model_id) and say the run’s model is unavailable. |
422 INFERENCE_TOOL_MODEL_NOT_IN_POOL |
The tool no longer runs on that model. | The same: open the tool on your default. |
Over MCP
Section titled “Over MCP”The MCP server runs the same tools, and your agent picks them on its own when a request is one of their jobs, such as “upscale this image 4x”:
list_inference_toolslists the tools, with the same fields and filters as the REST list.get_inference_toolreturns a tool’s summary, thebase_model_idits fields are for, and the JSON Schema ofinputon that model asinput_schema.run_inference_toolruns it, with the fields of the REST run exceptwebhook. The run lands in a session: it takessession_id,session_nameorproject_id.get_forge_runpolls the run, and returns itsinference_tool_key.
execute_forge takes an optional inference_tool_key as the flat REST run does, and checks the run the same way. The MCP server has no estimate for a tool run: price it over REST.