Skip to content

Artifacts

An artifact is a file that an agent produced for your workspace, for example a PDF brochure written by a workflow’s pdf_export node. Most artifacts are written by the platform while a workflow runs; you read them and download their files. The one way to create an artifact yourself is createTextArtifact, for a workspace owner. There is no update or delete operation.

OperationKindWhat it does
artifact(id)queryOne artifact: kind, title, status, size.
artifacts(...)queryYour artifacts, newest first, optionally for one session.
artifactDownloadUrl(id)mutationA fresh download link, valid for 5 minutes.
createTextArtifact(...)mutationOwner only: store an HTML page or a Markdown document.

Each operation accepts either of two credentials, with the x-huat-platform: customer header:

  • A dashboard JWT (Authorization: Bearer <jwt>, from login): any artifact of your workspace.
  • The avatarToken from sdkStart (Authorization: Bearer <avatarToken>): only the artifacts of the session that token belongs to. This is what lets an end user in your chat UI download a document their own conversation produced, without a dashboard login.

An API key is not accepted. With an avatarToken, an artifact of another session answers exactly like one that does not exist (NOT_FOUND), and passing another session’s id to artifacts(sessionId:) is FORBIDDEN. See Authentication for the three credential types.

FieldTypeDescription
idInt!Artifact id.
kindString!What produced it, a lower-case slug: pdf_document (a pdf_export node), or pdf_document_draft (the same node in a dashboard draft test run), an image, a voice-over (audio_speech, WAV), a piece of music (audio_music, MP3), a narrated explainer (video_script, a JSON manifest played by the app) or a document from a generate_artifact node (off by default), html_page or markdown (from createTextArtifact). An open set: new kinds can appear without a schema change.
statusArtifactStatus!PENDING, READY or FAILED (below).
titleString!Display title, usable as a file name (for a PDF, the node’s rendered titleTemplate).
mimeTypeString!Media type of the file, for example application/pdf.
sizeBytesFloatFile size in bytes once READY; null before.
errorCodeStringWhy it FAILED: one of RENDER_FAILED, UPLOAD_FAILED, TIMEOUT, TOO_LARGE, INVALID_INPUT, BLOCKED (the model declined the request), INTERNAL. null otherwise. Never raw error text.
sessionIdIntThe conversation (session) that produced it; null for runs with no session.
messageIdIntThe message it belongs to, when the producer recorded one.
workflowRunIdIntThe workflow run that produced it, readable with workflowRun.
agentIdIntThe AI employee (agent) that generated it; null for owner-written files and older rows.
generatorStringThe model that generated it; null when a person wrote it or it was rendered from a template.
hasThumbnailBoolean!true when a small preview image exists: call artifactDownloadUrl(id, thumbnail: true) for its link.
isTestBoolean!true for output of a draft or test run. Hidden from artifacts unless a workspace owner passes includeTests.
visibilityStringReserved (PRIVATE or TENANT) and not enforced yet: every artifact follows the session and AI employee that made it, and no value makes a file public. null for every row today.
createdAtDateTime!When it was requested.
updatedAtDateTime!When it last changed, for example when it became READY.

The storage location of the file is deliberately not a field. To download, mint a link with artifactDownloadUrl.

StatusMeaning
PENDINGBeing produced (rendering or uploading). Poll artifact(id) until it is READY or FAILED.
READYStored. Call artifactDownloadUrl(id) for a link.
FAILEDNot produced; errorCode says why. There is no file.

The pdf_export node explains each errorCode. A pdf_export node finishes its artifact before the workflow moves on, so by the time a later node (or your app) has the artifactId, the artifact is normally READY.

artifact(id: Int!): ArtifactDto!

One artifact. NOT_FOUND for an id that does not exist, belongs to another workspace, or (with an avatarToken) belongs to another session.

query GetArtifact($id: Int!) {
artifact(id: $id) {
id
kind
status
title
mimeType
sizeBytes
errorCode
sessionId
}
}
{ "id": 123 }
{
"data": {
"artifact": {
"id": 123,
"kind": "pdf_document",
"status": "READY",
"title": "5-day plan for Lisbon",
"mimeType": "application/pdf",
"sizeBytes": 48211,
"errorCode": null,
"sessionId": 9001
}
}
}
artifacts(sessionId: Int, kind: String, includeTests: Boolean, first: Int, after: String): ArtifactConnection!

Your artifacts, newest first, any status. Download links are not included: call artifactDownloadUrl per file.

ArgumentTypeDescription
sessionIdIntOnly artifacts of this session. With an avatarToken, omit it or pass the token’s own session; any other session is FORBIDDEN.
kindStringOnly this kind, for example pdf_document (which leaves out draft-test output). A malformed slug is BAD_REQUEST.
includeTestsBooleanWorkspace owners only: also list output of draft or test runs (hidden by default). Ignored for members. Default false.
firstIntPage size, default 20, at most 100.
afterStringA previous page’s pageInfo.endCursor; walks to older artifacts. A malformed cursor is BAD_REQUEST.

With an avatarToken, the list always holds only that session’s artifacts, whether or not you pass sessionId.

Returns ArtifactConnection: edges { cursor node { ...ArtifactDto } } and pageInfo { hasNextPage endCursor } (endCursor is null for an empty page).

query SessionFiles($sessionId: Int, $first: Int, $after: String) {
artifacts(
sessionId: $sessionId
kind: "pdf_document"
first: $first
after: $after
) {
edges {
node {
id
title
status
createdAt
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
{ "sessionId": 9001, "first": 10 }
artifactDownloadUrl(id: Int!, thumbnail: Boolean): ArtifactDownloadUrlDto!

Mints a fresh signed HTTPS link to a READY artifact’s file, valid for 5 minutes. It is a mutation, not a query, so no client cache can replay an expired link: call it when the user clicks download. Pass thumbnail: true to get the link of the small JPEG preview instead of the full file; it works only for an artifact with hasThumbnail: true.

FieldTypeDescription
artifactIdInt!The artifact the link is for.
urlString!The signed link. Valid until expiresAt.
expiresAtDateTime!When the link stops working.
mimeTypeString!Media type of the file.
titleString!Display title, usable as a file name.
mutation DownloadArtifact($id: Int!) {
artifactDownloadUrl(id: $id) {
url
expiresAt
mimeType
title
}
}
{ "id": 123 }
CodeWhen
CONFLICTThe artifact is PENDING (not finished yet) or FAILED (there is no file). Only READY has a link.
NOT_FOUNDSame scoping as artifact(id): no such artifact in your workspace, or (with an avatarToken) not in that token’s session.
INTERNAL_SERVER_ERRORThe link could not be signed; try again.

The mutation is rate-limited to 60 calls per minute per client IP address.

Available in production since 2026-10-29.

createTextArtifact(kind: String!, title: String!, content: String!): ArtifactDto!

Stores a page or document you wrote as a READY artifact in one call. Workspace owner only (dashboard JWT): a member and an SDK avatarToken get FORBIDDEN.

ArgumentNotes
kindhtml_page (a web page as HTML) or markdown. Any other value is BAD_REQUEST.
title1 to 200 characters.
contentThe text, at most 262144 bytes of UTF-8 (the limit counts bytes, not characters). Empty or larger content is BAD_REQUEST.
mutation CreateText($kind: String!, $title: String!, $content: String!) {
createTextArtifact(kind: $kind, title: $title, content: $content) {
id
kind
status
mimeType
sizeBytes
}
}
  • Stored as plain text. The file is never stored as a web page, so a storage or download link can never render it as one. Do not write the content into your own page. Show it through a sanitising, sandboxed preview, and remove scripts, forms, frames, external resources and navigation: none of those are supported in a stored page.
  • Rate limit. Throttled per client IP.
  • No delete yet. There is no mutation to delete or edit an artifact. An unfinished artifact is cleaned up automatically after 7 days, and others follow the workspace’s retention rules.
  • Read it back with artifact and download it with artifactDownloadUrl, like any other artifact.