Endpoints
The Weave API is available to customers on the Weave Enterprise plan only
The following sections list the endpoints that are available for the Weave API. All of the endpoints use the same base URL and are versioned under /v1.
Discovery endpoints
GET tools
Lists the tools available to the token's workspace. Use it to discover the tool_id values you pass to the other endpoints. Only published shared tools are listed — unpublished workflows and tools kept in a member's personal space are not.
HTTP endpoint
GET https://developer.api.weavy.ai/v1/tools
Headers
X-Figma-Token: [WEAVE API TOKEN]
Example response body
{
"tools": [
{
"id": "wf_xyz",
"name": "Hero image generator"
},
{
"id": "wf_abc",
"name": "Product shot upscaler"
}
]
}
Response fields
| Field | Description |
|---|---|
| tools |
|
Tool fields
| Field | Description |
|---|---|
| id |
|
| name |
|
GET inspect
Describes a tool's inputs and outputs. Call this endpoint before building a POST runs request: it tells you which id values are inputs, what each input accepts, and what outputs to expect.
HTTP endpoint
GET https://developer.api.weavy.ai/v1/tools/:tool_id/inspect
:tool_id is a required path parameter, the ID of the tool to inspect.
Query parameters
| Parameter | Description |
|---|---|
| tool_version |
|
GET https://developer.api.weavy.ai/v1/tools/:tool_id/inspect?tool_version=12
Headers
X-Figma-Token: [WEAVE API TOKEN]
Example response body
{
"tool_version": 12,
"inputs": [
{
"id": "b97a8c27-2dbe-4159-b5d5-33df6a329ea3",
"type": "string",
"name": "Prompt",
"default_value": { "type": "string", "value": "a red panda in space" }
},
{
"id": "3f2504e0-4f89-4d3a-9a0c-0305e82c3301",
"type": "integer",
"name": "Strength",
"number_range": { "min": 0, "max": 100 },
"default_value": { "type": "integer", "value": 60 }
},
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"type": "seed",
"name": "Seed",
"default_value": { "type": "seed", "is_random": true }
},
{
"id": "1ff8a7b5-2c3d-4e6f-8a1b-9c0d2e3f4a5b",
"type": "selector",
"name": "Style",
"values": ["bold", "muted"],
"default_value": { "type": "selector", "value": "bold" }
},
{
"id": "6fa459ea-ee8a-4ca4-894e-db77e160355e",
"type": "iterator",
"name": "Colors",
"default_value": { "type": "iterator", "value": ["red", "green", "blue"] }
},
{
"id": "2d8e4f6a-9b1c-4d3e-8f5a-7c6b0e9d2a14",
"type": "media",
"name": "Reference image",
"default_value": null
}
],
"outputs": [
{
"id": "b3f9c2a1-7d4e-4f86-a0b5-2e8c91d4f7a6",
"name": "Hero image",
"output_type": "image",
"input_ids": [
"b97a8c27-2dbe-4159-b5d5-33df6a329ea3",
"3f2504e0-4f89-4d3a-9a0c-0305e82c3301",
"2d8e4f6a-9b1c-4d3e-8f5a-7c6b0e9d2a14"
]
}
]
}
Response fields
| Field | Description |
|---|---|
| tool_version |
|
| inputs | |
| outputs |
|
Input fields
| Field | Description |
|---|---|
| id |
|
| type |
|
| name |
|
| description |
|
| default_value |
|
| values |
|
| number_range |
|
Output fields
| Field | Description |
|---|---|
| id |
|
| name |
|
| output_type |
|
| input_ids |
|
Runs endpoints
The following endpoints are used to start, monitor, cancel, and price runs of a tool.
POST runs
Starts one or more runs of a tool. Execution is asynchronous: the endpoint returns 202 Accepted with the created runs, and you poll GET runs for progress and results.
HTTP endpoint
POST https://developer.api.weavy.ai/v1/tools/:tool_id/runs
:tool_id is a required path parameter, the ID of the tool to run.
Headers
X-Figma-Token: [WEAVE API TOKEN]
Content-Type: application/json
Parameters
| Body parameter | Description |
|---|---|
| tool_version |
|
| count |
|
| inputs |
|
| webhook |
|
Using published defaults
Each entry in inputs sets its input in one of two ways:
| Field | Description |
|---|---|
| id |
|
| value |
|
| use_default |
|
Supply exactly one of value or use_default per entry. Sending both is a 400, and so is omitting both, since every tool input must be given a value.
The default used is the default_value reported by GET inspect for the version you are running, so inspect that version to see what a defaulted input will run with. An input whose default_value is null has nothing saved to fall back to: use_default on it returns 400, and you have to send an explicit value.
The snippet below shows the two forms side by side and omits the tool's other inputs for brevity; a real request has to carry every one of them.
{
"inputs": [
{
"id": "b97a8c27-2dbe-4159-b5d5-33df6a329ea3",
"value": { "type": "string", "value": "a red panda in space" }
},
{
"id": "3f2504e0-4f89-4d3a-9a0c-0305e82c3301",
"use_default": true
}
]
}
Input types
Every value object carries a type field that matches the input's type from GET inspect, along with the fields listed below.
| Input type | Value fields |
|---|---|
| string | value (string) |
| integer | value (number) |
| boolean | value (boolean) |
| seed | is_random, value (number, used when is_random is not true) |
| media | value (asset URL or data URI) or asset_id, kind (image, video, audio, or 3d) |
| selector | value (string or number, one of the node values) |
| iterator | value (string array, max 100), run_mode |
| media_iterator | value (array of { "url" or "asset_id", "kind" }, max 100), run_mode |
| array | value (string array) |
A media input takes the file either as value — a URL the API can fetch, or a base64 data URI — or as asset_id, the handle for a file you uploaded through the assets endpoints. Provide exactly one of value or asset_id. Sending both or neither results in a 400. Entries of a media_iterator value take url or asset_id the same way.
kind is one of image, video, audio, or 3d, and is optional in both cases, but it resolves differently. With value, it is inferred from the URL's extension, and a URL it cannot be inferred from is a 400 — so pass kind explicitly for URLs without one. With asset_id, it comes from the content_type declared when the file was uploaded, so it never needs to be sent. An explicit kind always wins.
For iterator and media_iterator inputs, value is the list to iterate over and the run fans out per entry. run_mode is either "parallel" or "matrix", but it is not yet honored: the iterator mode is fixed by the tool. See the examples for how iterators shape the run's outputs.
Example request body
{
"tool_version": 12,
"count": 3,
"inputs": [
{
"id": "b97a8c27-2dbe-4159-b5d5-33df6a329ea3",
"value": { "type": "string", "value": "a red panda in space" }
},
{
"id": "3f2504e0-4f89-4d3a-9a0c-0305e82c3301",
"use_default": true
},
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"value": { "type": "seed", "is_random": true }
},
{
"id": "1ff8a7b5-2c3d-4e6f-8a1b-9c0d2e3f4a5b",
"value": { "type": "selector", "value": "bold" }
},
{
"id": "6fa459ea-ee8a-4ca4-894e-db77e160355e",
"value": { "type": "iterator", "value": ["red", "green", "blue"] }
},
{
"id": "2d8e4f6a-9b1c-4d3e-8f5a-7c6b0e9d2a14",
"value": { "type": "media", "value": "https://example.com/panda.png", "kind": "image" }
}
],
"webhook": { "url": "https://example.com/weave-callback" }
}
Example response body
202 Accepted
{
"runs": [
{
"id": "run_abc123",
"tool_id": "wf_xyz",
"status": "running"
},
{
"id": "run_abc124",
"tool_id": "wf_xyz",
"status": "running"
}
]
}
This request sets all six inputs the GET inspect example reports: five explicitly, and Strength with use_default, which runs it at the value the tool was published with.
The response is always { "runs": [...] }, even when count is 1. Each run starts asynchronously, so status is always running here. Poll GET runs for progress and results.
GET runs
Returns the status of one or more runs, including their outputs once a run completes.
Any API token in a workspace can retrieve runs started with an API token in that workspace. Runs started by users in the Weave app are not visible to the Weave API.
HTTP endpoint
GET https://developer.api.weavy.ai/v1/tools/:tool_id/runs?run_ids=a,b
run_ids is a required, comma-separated list of run IDs as returned by POST runs.
Headers
X-Figma-Token: [WEAVE API TOKEN]
Example response body
{
"runs": [
{
"id": "run_abc123",
"tool_id": "wf_xyz",
"status": "completed",
"created_at": "2026-05-06T10:00:00Z",
"completed_at": "2026-05-06T10:00:42Z",
"outputs": [
{
"id": "b3f9c2a1-7d4e-4f86-a0b5-2e8c91d4f7a6",
"name": "Hero image",
"assets": [
{ "type": "image", "url": "https://cdn.figma.com/..." }
]
},
{
"id": "4a7d1e93-6c82-4f15-b0a9-3e5d8c2b7f60",
"name": "Caption",
"assets": [
{ "type": "text", "text": "A red panda floating in space." }
]
}
]
}
],
"workspace_credits_remaining": 850
}
Response fields
| Field | Description |
|---|---|
| runs |
|
| workspace_credits_remaining |
|
Run fields
| Field | Description |
|---|---|
| id |
|
| tool_id |
|
| status |
|
| progress_percentage |
|
| created_at |
|
| completed_at |
|
| canceled_at |
|
| outputs |
|
| error |
|
Output fields
Each entry in outputs represents one output node of the tool. When no iterator is involved, there is one entry per output node. When an iterator is involved, there is one entry per output node per iteration. See the examples for iterator output shapes.
| Field | Description |
|---|---|
| id |
|
| name |
|
| iteration_index |
|
| assets |
|
Webhooks
Instead of polling GET runs, you can supply webhook.url on POST runs to be notified when a run finishes. Weave sends one POST to that URL for each run as it reaches a terminal state (completed or failed). The url must be HTTPS. Delivery is best-effort — retried a few times on failure, with redirects not followed — so treat it as a prompt to fetch results, not a guaranteed, exactly-once signal.
The callback is not signed, so a receiver cannot distinguish it from a forged request. Treat its body as untrusted: use it as a signal to call GET runs with the run IDs you started, and take the results from that response.
Request
The callback is a JSON POST. Weave sets the following headers:
Content-Type: application/json
X-Weavy-Delivery-Id: [UNIQUE DELIVERY ID]
X-Weavy-Timestamp: [ISO 8601 TIMESTAMP]
Example request body
{
"event": "run.completed",
"delivery_id": "whd_abc123",
"run": {
"id": "run_abc123",
"tool_id": "wf_xyz",
"status": "completed",
"created_at": "2026-05-06T10:00:00Z",
"completed_at": "2026-05-06T10:00:42Z",
"outputs": [
{
"id": "b3f9c2a1-7d4e-4f86-a0b5-2e8c91d4f7a6",
"name": "Hero image",
"assets": [
{ "type": "image", "url": "https://cdn.figma.com/..." }
]
}
]
}
}
Body fields
| Field | Description |
|---|---|
| event |
|
| delivery_id |
|
| run |
|
POST cancel runs
Cancels one or more runs. run_ids is a comma-separated list, mirroring GET runs.
HTTP endpoint
POST https://developer.api.weavy.ai/v1/tools/:tool_id/runs/cancel?run_ids=a,b
Headers
X-Figma-Token: [WEAVE API TOKEN]
Example response body
The response uses the same shape as GET runs: each requested run is returned with its current status.
{
"runs": [
{
"id": "run_abc123",
"tool_id": "wf_xyz",
"status": "canceled",
"created_at": "2026-05-06T10:00:00Z",
"canceled_at": "2026-05-06T10:00:05Z"
},
{
"id": "run_abc124",
"tool_id": "wf_xyz",
"status": "completed",
"created_at": "2026-05-06T10:00:00Z",
"completed_at": "2026-05-06T10:00:42Z",
"outputs": [
{
"id": "b3f9c2a1-7d4e-4f86-a0b5-2e8c91d4f7a6",
"name": "Hero image",
"assets": [
{ "type": "image", "url": "https://cdn.figma.com/..." }
]
}
]
}
],
"workspace_credits_remaining": 820
}
A run that has already reached a terminal status can't be canceled. It is returned with its existing status rather than canceled.
POST cost
Returns the estimated credit cost of running a tool, without starting anything. It takes the same body as POST runs, so you can price the exact request you're about to send. No run is created and no credits are spent.
HTTP endpoint
POST https://developer.api.weavy.ai/v1/tools/:tool_id/cost
:tool_id is a required path parameter, the ID of the tool to price.
Headers
X-Figma-Token: [WEAVE API TOKEN]
Content-Type: application/json
Parameters
| Body parameter | Description |
|---|---|
| tool_version |
|
| inputs |
|
The cost endpoint shares the same input validation rules as the POST runs endpoint. As a result, unrunnable requests cannot be priced: the API returns a 400 Bad Request.
Example request body
{
"tool_version": 3,
"inputs": [
{
"id": "b97a8c27-2dbe-4159-b5d5-33df6a329ea3",
"value": { "type": "string", "value": "a red panda in space" }
},
{
"id": "3f2504e0-4f89-4d3a-9a0c-0305e82c3301",
"use_default": true
}
]
}
Example response body
200 OK
{
"estimated_credit_cost": 12
}
Response fields
| Field | Description |
|---|---|
| estimated_credit_cost |
|
A null cost is not an error: the request is priceable only once the run is underway. Treat it as "unknown before the run", not as "free".
Assets endpoints
A media input can take a file you upload to Weave instead of a URL the API fetches. Uploading is a three-step flow:
- POST uploads to declare the file and get a single-use write URL.
PUTthe file to that URL.- POST commit to make the asset usable.
The asset_id from step 1 is only accepted as a run input once step 3 succeeds.
POST uploads
Declares a file and returns the URL to write it to. The upload URL is signed for the exact file_size and content_type you declare here, so both must match the file you send.
HTTP endpoint
POST https://developer.api.weavy.ai/v1/assets/uploads
Headers
X-Figma-Token: [WEAVE API TOKEN]
Content-Type: application/json
Example request body
{
"file_name": "panda.png",
"file_size": 182734,
"content_type": "image/png"
}
Request fields
| Field | Description |
|---|---|
| file_name |
|
| file_size |
|
| content_type |
|
Example response body
{
"asset_id": "9c85f3a1-2d47-4b6e-8f10-5a3c7d9e1b24",
"upload_url": "https://storage.figma.com/weave-uploads/...",
"content_type": "image/png",
"expires_at": "2026-05-06T22:00:00Z"
}
Response fields
| Field | Description |
|---|---|
| asset_id |
|
| upload_url |
|
| content_type |
|
| expires_at |
|
Uploading the file
PUT the file body to upload_url. The URL is signed, so it carries its own authorization — do not send your X-Figma-Token with it.
Headers
Both of these headers are required on the PUT. The upload is rejected if either is missing or does not match.
Content-Type: [CONTENT TYPE FROM THE INITIATE RESPONSE]
If-None-Match: *
| Header | Description |
|---|---|
| Content-Type |
|
| If-None-Match |
|
Example request
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/png" \
-H "If-None-Match: *" \
--data-binary @panda.png
The body must also be exactly file_size bytes, and the URL works only once — a second PUT to the same URL fails.
Accepted content types
content_type is the media type you send, not the file's extension. Each accepted type below lists the file format it corresponds to, so an MP3 is uploaded as audio/mpeg, a QuickTime movie as video/quicktime, and a glTF binary as model/gltf-binary. A file whose format is not listed cannot be uploaded — pass it to a media input as a URL instead.
| Content type | File format | Extension | Input kind |
|---|---|---|---|
image/png | PNG | .png | image |
image/jpeg | JPEG | .jpg, .jpeg | image |
image/webp | WebP | .webp | image |
video/mp4 | MPEG-4 | .mp4 | video |
video/quicktime | QuickTime | .mov | video |
video/webm | WebM | .webm | video |
audio/mpeg | MP3 | .mp3 | audio |
audio/wav | WAV | .wav | audio |
model/gltf-binary | glTF binary | .glb | 3d |
model/obj | Wavefront OBJ | .obj | 3d |
model/fbx | FBX | .fbx | 3d |
model/ply | PLY | .ply | 3d |
image/jpg is accepted as an alias for image/jpeg. The Input kind column is the kind the file resolves to on a media input, which is why kind never has to be sent alongside an asset_id. Note that .m4v is not accepted, even though it is an MPEG-4 container — upload it as .mp4.
POST commit
Finishes an upload and makes the asset usable as a media input. Call it once the PUT to upload_url has succeeded. It verifies that what landed matches what was declared, then marks the asset committed.
HTTP endpoint
POST https://developer.api.weavy.ai/v1/assets/uploads/:asset_id/commit
:asset_id is a required path parameter, the asset_id returned by POST uploads.
Headers
X-Figma-Token: [WEAVE API TOKEN]
There is no request body.
Example response body
{
"asset_id": "9c85f3a1-2d47-4b6e-8f10-5a3c7d9e1b24",
"status": "committed"
}
Response fields
| Field | Description |
|---|---|
| asset_id |
|
| status |
|
The call is idempotent: committing an already-committed asset returns the same response. It returns 409 if the file has not been uploaded yet — retry once the PUT succeeds — or if the asset was rejected because what landed did not match what was declared, in which case start a new upload.
Using the asset on a run
Only the media input is shown; a real request also sets the tool's other inputs.
{
"inputs": [
{
"id": "2d8e4f6a-9b1c-4d3e-8f5a-7c6b0e9d2a14",
"value": { "type": "media", "asset_id": "9c85f3a1-2d47-4b6e-8f10-5a3c7d9e1b24", "kind": "image" }
}
]
}
Errors
When a request can't be completed, the Weave API returns an HTTP error status with a JSON body describing the problem. This response covers the request itself, such as an invalid token or a tool that can't be found. It is separate from the run-level error field in the GET runs response, which reports a run that started and then failed.
Status codes
| Error codes | Description |
|---|---|
| 400 | The request body or query parameters failed validation. GET tools does not return this status, as it takes no body or query parameters. On POST commit it means :asset_id is not a valid UUID. |
| 401 | The X-Figma-Token header is missing or the token is invalid. |
| 404 | No tool with the given ID is accessible to the token, or the workflow is not configured as a tool. On POST commit it means no asset with the given ID belongs to the token's workspace. GET tools and POST uploads do not return this status, as they take no ID. |
| 409 | The asset can't be committed yet: the file has not been uploaded, or the upload was rejected because what landed did not match what was declared. Only POST commit returns this status. |
| 429 | The token has exceeded its rate limit for this endpoint. Retry after the number of seconds in the Retry-After header. |
| 500 | An unexpected error occurred on the server. |
Error response body
Example response body
400 Bad Request
{
"statusCode": 400,
"internalErrorCode": 4001,
"message": "The request body failed validation.",
"fieldsErrors": [
"inputs.0.value.value must be a string"
],
"timestamp": "2026-05-06T10:00:00Z",
"correlationId": "req_8f2c1a"
}
Response fields
| Field | Description |
|---|---|
| statusCode |
|
| internalErrorCode |
|
| message |
|
| fieldsErrors |
|
| timestamp |
|
| correlationId |
|