Lists the files the agent wrote into a thread, so you can find them without watching the stream
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
Lists the files this thread's turns wrote into its workspace. Every entry names a file the read endpoint accepts.
Use this when you did not watch the stream. create_artifact writes a workspace file and emits no artifact SSE frame — only save_artifact_to_project does. So a client that reconnected, polled for the result instead of streaming, or ran the turn from a script has no filename to work from otherwise.
This is not the project artifact list. That one returns the project library — registered artifacts with numeric ids and versions, shared across every thread. This one returns ephemeral per-thread files addressed by name. A turn that only called create_artifact appears here and not there.
An empty array is a normal answer. A thread whose turns produced no files returns
{"files": []}— not a404, and not an error. So is a project library with nothing promoted into it. Neither means anything is misconfigured.
Basic Usage
curl --request GET \
--url 'https://cloud.getwren.ai/api/v2/projects/1/threads/5/workspace' \
--header 'Authorization: Bearer <API_KEY>'{
"files": [
{
"filename": "q3-revenue.html",
"contentType": "text/html; charset=utf-8",
"sizeBytes": 18422,
"lastModified": "2026-08-04T09:14:31.000Z"
},
{
"filename": "revenue-by-product.svg",
"contentType": "image/svg+xml",
"sizeBytes": 4096,
"lastModified": "2026-08-04T09:14:29.000Z"
}
]
}Response fields
| Field | Type | Description |
|---|---|---|
filename | string | Pass this to the read endpoint to fetch the bytes. |
contentType | string | Inferred from the extension, matching what the read endpoint will serve. application/octet-stream when unrecognised. |
sizeBytes | integer | Size on disk. Omitted when the store does not report it. |
lastModified | string | When the file was last written. Omitted when the store does not report it. |
contentType is resolved with the same rule the read endpoint uses, so you can decide how to render a file — or whether you want it at all — before spending a request on the bytes.
Directories are not listed. The read endpoint addresses a single name and rejects separators, so a nested directory would not be fetchable anyway.
Reading the files back
Combine threadId from the turn's init frame with each filename:
// 1. Run the turn. You only need the thread id — not every frame.
const { threadId } = await runTurn({
projectId: 1,
question: 'Build me an HTML summary of Q3 revenue by product line.',
});
// 2. Ask what it produced.
const { files } = await getJson(`/v2/projects/1/threads/${threadId}/workspace`);
// 3. Fetch the ones you want to show.
const rendered = await Promise.all(
files
.filter((f) => f.contentType.startsWith('text/html'))
.map(async (f) => ({
...f,
body: await getText(
`/v2/projects/1/threads/${threadId}/workspace/${encodeURIComponent(f.filename)}`,
),
})),
);Collecting filenames from create_artifact tool results as the turn streams still works and saves a request — see Artifacts. This endpoint is the recovery path for when you did not, or could not.
Error handling
| Status | When |
|---|---|
400 | Invalid project_id or thread_id. |
401 | Missing or invalid API key. |
404 | The project or the thread does not exist. A thread belonging to another project is reported the same way. |
405 | Method not allowed — this endpoint is GET only. |
501 | The artifact service is not configured on this deployment. |
A thread that produced nothing is not a
404. Only a thread that does not exist, or belongs to another project, is.
See Artifacts for how workspace files compare with the project library, and a worked example of both.
