---
title: "Export a file/dir to a download URL"
method: POST
path: "/preview/nodes/{name}/exports"
tags: ["nodes"]
---

# Export a file/dir to a download URL

`POST /preview/nodes/{name}/exports`

## Request

- Base URL: `https://autoresearch.sfcompute.com`
- URL: `https://autoresearch.sfcompute.com/preview/nodes/{name}/exports`
- Auth: HTTP bearer

Export a file or directory from a node to a download URL: the way to get results (an optimized model, a checkpoint, generated data) OUT of a node when your sandbox is HTTPS-only. Returns {artifact_id, state: "ready", size_bytes, sha256, download: {url, expires_at}}: fetch the url with plain curl before it expires (~7 days; the stored object itself lives ~30 days) and verify the sha256. The url points at the object store's own hostname (currently fly.storage.tigris.dev), NOT the API's - a sandbox egress allowlist must permit that host too, or the fetch 403s at your own proxy with the credential fine. A directory arrives as a tar of its contents; extract with `tar -xf`. No credential enters the node and nothing rides through MCP: the transfer is streamed to object storage by the host. The node must be running. On a stopped node this call refuses and does not wake the node. A run_command wakes it, and the wake bills, so wake it only when you need the file now. Large paths take a while: over MCP the call waits about 45 seconds (under your client's own per-call deadline), and if the export is still running then it returns {artifact_id, state: "pending", next_step}, which is not an error. The export keeps running; call get_artifact(artifact_id) until state is "ready" to get the download. If you lose the artifact_id, list_artifacts(node) lists your recent exports. Exports are capped in size (5 GiB; export_data uploads bigger paths in parts) and rate per customer. Every refusal carries a stable error code in structuredContent {error: {code, message, retryable}} (TOO_LARGE, NOT_FOUND, RATE_LIMITED, ...; the docs list them all). This URL is PRIVATE and machine-shaped; to put the file in front of a human (Slack, a doc, <img src>), follow with share_artifact(artifact_id) for a clean public embeddable link. get_artifact(artifact_id) re-mints this download later.

## Path parameters

- `name` string, required: Node name (from `list_nodes`).

## Query parameters

- `workspace` string

## Request body

- object
  - `mission` string, nullable: Attribute the artifact to a mission (from open_mission); it shows on the mission's page. Omit and it joins the node's own mission, which is usually what you want.
  - `path` string, required: The path to export, absolute or ~-relative (e.g. "~/out/model.gguf"). A regular file streams as-is; a directory is tarred (its contents; extract with `tar -xf`).

## Response `200`

The capability's JSON result.

- Result: The capability's result: the same object the MCP tool returns as structuredContent. Resources carry an `object` type discriminator and an immutable id; money is integer micro-dollars in `*_usd_micros` fields.
  - `object` string: The resource type, when the result is one resource.

## Other responses

- `400`: Malformed request: bad JSON, a parameter that does not parse, or a value outside its range. Code `invalid`.
- `401`: No bearer token, or one this API cannot verify. Code `unauthenticated`.
- `403`: Authenticated, but the token's scopes or the account's role do not reach this operation. The message names the scope word to request. Code `forbidden`.
- `404`: No such route. Code `not_found`; the body names the OpenAPI document.
- `409`: The node is not in a state this export can read: stopped, still coming up, lost, or changing state mid-call. Code `NODE_NOT_RUNNING`.
- `413`: The path is over the export size cap. `export_file` stops at the object store's single-PUT bound and no raise exceeds it; send a larger path with `export_data`. Code `TOO_LARGE`.
- `422`: A well-formed request the platform declined: no such node, a cap reached, a state that forbids the move. The message says which, and what to do instead. Code `refused`.
- `429`: Over this door's per-customer budget. Wait `Retry-After` seconds. Code `rate_limited`.
- `500`: Our fault. Retry; if it persists, quote the request-id. Code `internal`.
- `503`: The node's host did not answer, went away mid-export, or export is not configured here. Ours, not yours: retry. Codes `HOST_UNAVAILABLE`, `NOT_ENABLED`.
- `504`: copy_file's export leg did not finish inside the call's wait. It is still running and may land: read the artifact by id before copying again. Code `TIMEOUT`. (export_file answers a wait that ends with 200 and `state: pending` instead.)

---

[API](https://skmtc.dev/givemeanode/apis/autoresearch-customer-api.md) · [All operations](https://skmtc.dev/givemeanode/apis/autoresearch-customer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/givemeanode/apis/autoresearch-customer-api/revisions/f88dcb70ac6c?raw)
