> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blocks.team/llms.txt
> Use this file to discover all available pages before exploring further.

# Fetch Plans & Files

> Download a plan or file the agent produced in a session.

```http theme={null}
GET /rest/v1/sessions/{session_id}/artifacts/{artifact_id}
```

Returns a download link for one artifact the agent produced in a session. Artifact IDs come from the session response's `plan_artifact_ids` (see [`Get Session`](/rest-api/sessions/get)), which lists the latest artifact for each plan, oldest plan first — so the last entry is the most recent plan.

`kind` is `plan` or `file`. Iterations of the same plan share a `plan_id`; older iterations stay downloadable by their own ID. `url` is a presigned download link valid for 6 days — fetch it **without** the `Authorization` header.

## Request

<CodeGroup>
  ```javascript JavaScript theme={null}
  const artifact = await fetch(
    `https://api.blocks.team/rest/v1/sessions/${sessionId}/artifacts/${artifactId}`,
    { headers: { Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}` } },
  ).then((r) => r.json());
  ```

  ```python Python theme={null}
  import os, requests

  artifact = requests.get(
      f"https://api.blocks.team/rest/v1/sessions/{session_id}/artifacts/{artifact_id}",
      headers={"Authorization": f"ApiKey {os.environ['BLOCKS_API_KEY']}"},
  ).json()
  ```

  ```bash cURL theme={null}
  curl "https://api.blocks.team/rest/v1/sessions/$SESSION_ID/artifacts/$ARTIFACT_ID" \
    -H "Authorization: ApiKey $BLOCKS_API_KEY"
  ```

  ```java Java theme={null}
  import java.net.URI;
  import java.net.http.*;

  HttpResponse<String> res = HttpClient.newHttpClient().send(
      HttpRequest.newBuilder(URI.create(
          "https://api.blocks.team/rest/v1/sessions/" + sessionId + "/artifacts/" + artifactId))
          .header("Authorization", "ApiKey " + System.getenv("BLOCKS_API_KEY"))
          .build(),
      HttpResponse.BodyHandlers.ofString());
  ```

  ```ruby Ruby theme={null}
  require "net/http"
  require "json"

  artifact = JSON.parse(Net::HTTP.get(
    URI("https://api.blocks.team/rest/v1/sessions/#{session_id}/artifacts/#{artifact_id}"),
    { "Authorization" => "ApiKey #{ENV['BLOCKS_API_KEY']}" },
  ))
  ```

  ```go Go theme={null}
  req, _ := http.NewRequest("GET",
      "https://api.blocks.team/rest/v1/sessions/"+sessionID+"/artifacts/"+artifactID, nil)
  req.Header.Set("Authorization", "ApiKey "+os.Getenv("BLOCKS_API_KEY"))
  res, _ := http.DefaultClient.Do(req)
  ```
</CodeGroup>

### Path parameters

<ParamField path="session_id" type="string (uuid)" required>
  The session the artifact belongs to.
</ParamField>

<ParamField path="artifact_id" type="string (uuid)" required>
  The artifact ID, taken from the session's `plan_artifact_ids`.
</ParamField>

## Response

```json theme={null}
{
  "id": "c7d3a1b2-5e6f-4a8b-9c0d-1e2f3a4b5c6d",
  "url": "https://blocks-bundles.s3.us-east-1.amazonaws.com/…/artifacts/c7d3a1b2-…?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=518400&…",
  "file_name": "plan.md",
  "file_path": "/home/user/workspace/plan.md",
  "file_size_in_kb": 4.2,
  "mime_type": "text/markdown",
  "kind": "plan",
  "plan_id": "0b9e8d7c-6f5a-4e3d-8c2b-1a0f9e8d7c6b",
  "created_at": "2026-04-30T18:24:10.000Z"
}
```

<ResponseField name="id" type="string (uuid)">
  Artifact ID.
</ResponseField>

<ResponseField name="url" type="string">
  Presigned download URL, valid for 6 days. Fetch it directly, without the `Authorization` header.
</ResponseField>

<ResponseField name="file_name" type="string">
  Base name of the file, e.g. `plan.md`.
</ResponseField>

<ResponseField name="file_path" type="string">
  Absolute path of the file inside the agent's sandbox when it was captured.
</ResponseField>

<ResponseField name="file_size_in_kb" type="number">
  File size in kilobytes.
</ResponseField>

<ResponseField name="mime_type" type="string">
  MIME type the download is served with, e.g. `text/markdown`.
</ResponseField>

<ResponseField name="kind" type="string">
  `plan` for a plan the agent wrote, `file` for any other captured file.
</ResponseField>

<ResponseField name="plan_id" type="string (uuid) | null">
  Groups iterations of the same plan. `null` when `kind` is `file`.
</ResponseField>

<ResponseField name="created_at" type="string (ISO 8601)">
  When the artifact was captured.
</ResponseField>

## Download the latest plan

Fetch the session, take the last entry of `plan_artifact_ids`, resolve it to a download URL, then fetch the URL. Iterate the array instead of taking the last entry to download every plan.

<CodeGroup>
  ```javascript JavaScript theme={null}
  const BASE_URL = "https://api.blocks.team";
  const headers = { Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}` };

  const session = await fetch(`${BASE_URL}/rest/v1/sessions/${sessionId}`, { headers }).then((r) => r.json());
  const planArtifactId = session.plan_artifact_ids.at(-1); // latest plan; iterate the array for all plans
  const artifact = await fetch(`${BASE_URL}/rest/v1/sessions/${sessionId}/artifacts/${planArtifactId}`, { headers }).then((r) => r.json());
  const plan = await fetch(artifact.url).then((r) => r.text()); // presigned — no Authorization header
  ```

  ```python Python theme={null}
  import os, requests

  BASE_URL = "https://api.blocks.team"
  HEADERS = {"Authorization": f"ApiKey {os.environ['BLOCKS_API_KEY']}"}

  session = requests.get(f"{BASE_URL}/rest/v1/sessions/{session_id}", headers=HEADERS).json()
  plan_artifact_id = session["plan_artifact_ids"][-1]  # latest plan; iterate the list for all plans
  artifact = requests.get(f"{BASE_URL}/rest/v1/sessions/{session_id}/artifacts/{plan_artifact_id}", headers=HEADERS).json()
  plan = requests.get(artifact["url"]).text  # presigned — no Authorization header
  ```

  ```bash cURL theme={null}
  BASE_URL="https://api.blocks.team"
  AUTH="Authorization: ApiKey $BLOCKS_API_KEY"

  PLAN_ARTIFACT_ID=$(curl -s -H "$AUTH" "$BASE_URL/rest/v1/sessions/$SESSION_ID" | jq -r '.plan_artifact_ids[-1]')
  PLAN_URL=$(curl -s -H "$AUTH" "$BASE_URL/rest/v1/sessions/$SESSION_ID/artifacts/$PLAN_ARTIFACT_ID" | jq -r '.url')
  curl -s "$PLAN_URL"   # presigned — no Authorization header
  ```

  ```java Java theme={null}
  import java.net.URI;
  import java.net.http.*;
  import com.fasterxml.jackson.databind.*;

  String BASE_URL = "https://api.blocks.team";
  String AUTH = "ApiKey " + System.getenv("BLOCKS_API_KEY");
  HttpClient http = HttpClient.newHttpClient();
  ObjectMapper json = new ObjectMapper();

  JsonNode session = json.readTree(http.send(
      HttpRequest.newBuilder(URI.create(BASE_URL + "/rest/v1/sessions/" + sessionId))
          .header("Authorization", AUTH).build(),
      HttpResponse.BodyHandlers.ofString()).body());
  JsonNode planIds = session.get("plan_artifact_ids");
  String planArtifactId = planIds.get(planIds.size() - 1).asText(); // latest plan

  JsonNode artifact = json.readTree(http.send(
      HttpRequest.newBuilder(URI.create(BASE_URL + "/rest/v1/sessions/" + sessionId + "/artifacts/" + planArtifactId))
          .header("Authorization", AUTH).build(),
      HttpResponse.BodyHandlers.ofString()).body());

  String plan = http.send(
      HttpRequest.newBuilder(URI.create(artifact.get("url").asText())).build(), // presigned — no Authorization header
      HttpResponse.BodyHandlers.ofString()).body();
  ```

  ```ruby Ruby theme={null}
  require "net/http"
  require "json"

  BASE_URL = "https://api.blocks.team"
  HEADERS = { "Authorization" => "ApiKey #{ENV['BLOCKS_API_KEY']}" }

  session = JSON.parse(Net::HTTP.get(URI("#{BASE_URL}/rest/v1/sessions/#{session_id}"), HEADERS))
  plan_artifact_id = session["plan_artifact_ids"].last # latest plan; iterate the array for all plans
  artifact = JSON.parse(Net::HTTP.get(URI("#{BASE_URL}/rest/v1/sessions/#{session_id}/artifacts/#{plan_artifact_id}"), HEADERS))
  plan = Net::HTTP.get(URI(artifact["url"])) # presigned — no Authorization header
  ```

  ```go Go theme={null}
  auth := "ApiKey " + os.Getenv("BLOCKS_API_KEY")

  req, _ := http.NewRequest("GET", BaseURL+"/rest/v1/sessions/"+sessionID, nil)
  req.Header.Set("Authorization", auth)
  res, _ := http.DefaultClient.Do(req)
  var session struct {
  	PlanArtifactIDs []string `json:"plan_artifact_ids"`
  }
  json.NewDecoder(res.Body).Decode(&session)
  res.Body.Close()
  planArtifactID := session.PlanArtifactIDs[len(session.PlanArtifactIDs)-1] // latest plan

  req, _ = http.NewRequest("GET", BaseURL+"/rest/v1/sessions/"+sessionID+"/artifacts/"+planArtifactID, nil)
  req.Header.Set("Authorization", auth)
  res, _ = http.DefaultClient.Do(req)
  var artifact struct {
  	URL string `json:"url"`
  }
  json.NewDecoder(res.Body).Decode(&artifact)
  res.Body.Close()

  res, _ = http.Get(artifact.URL) // presigned — no Authorization header
  plan, _ := io.ReadAll(res.Body)
  res.Body.Close()
  ```
</CodeGroup>

## Errors

| Status | Code | Reason |
| - | - | - |
| `404` | `NOT_FOUND` | Session does not exist or belongs to a different workspace. |
| `404` | `NOT_FOUND` | `Artifact not found` — the artifact ID is unknown or is attached to a different session. |
| `422` | `VALIDATION` | `session_id` or `artifact_id` is not a UUID. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.