Artifacts

An artifact is a file a run produces and keeps: a generated report, a rendered image, a diff, a CSV, a build output. The run's working directory is scratch and disappears when the run ends, so anything you want to hand back to a person or a downstream system becomes an artifact, stored durably and attached to the run.

What an artifact is

Each artifact has a name, a content type, a size, optional metadata you attach, and an expiry. It belongs to the run that wrote it and, through it, to your org; like everything else, it is tenant-isolated. Artifacts are listed on the run page and reachable over the REST API, the CLI, and the MCP server.

Writing one

From the program, call artifacts.write. Pass text or raw bytes; it stores the file and resolves to a reference with a download URL:

import { artifacts, agent, output } from "@boardwalk-labs/workflow";

const report = await agent("Write the weekly metrics report as Markdown: ...");

const ref = await artifacts.write(
  "weekly-report.md",
  "text/markdown",
  report,
  { week: "2026-W24" }, // optional metadata, yours to read back later
);

output({ report: ref.url }); // ref = { id, name, url }

Binary is the same call with a Uint8Array body, so a generated PNG or a zipped bundle works exactly like text.

From an agent

The artifacts built-in tool is on by default, so an agent can persist its own output without you wiring anything: ask it to save its work and it writes the artifact, then you read the reference back. The program around it stays in control of what counts as a deliverable.

Retrieving artifacts

List a run's artifacts, then fetch one:

  • Dashboard: the run page lists every artifact with a download link.
  • CLI:the run's output and artifacts show up alongside its logs.
  • API: GET /v1/orgs/:slug/runs/:id/artifacts to list, then GET /v1/artifacts/:id/download (a 302 to a signed URL) or /download-url(the signed URL as JSON, for clients that can't attach a bearer token across a redirect).
  • MCP: list_artifacts then get_artifact_download_url.

Signed URLs & lifetime

Downloads go through short-lived signed URLs, so the bytes are served by the CDN without exposing the storage bucket, and a link you mint is only good for a window. Request a custom window with ttlSeconds when you mint one (up to a day). The artifact record itself persists with the run; the URL is the thing that expires, so mint a fresh one when you need it rather than storing it.

Artifacts vs. output

Reach for an artifact when the thing is a file: something to download, large, or binary. Reach for output() when the thing is the run's result: a JSON value a person reads on the run page, a notification carries, or a calling workflow consumes. They compose well, return a small JSON summary from output() that points at the heavier artifacts by URL.