> ## 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.

# Quick Start

> Create an agent session, stream the first response, and send a follow-up.

The Sessions API lets you start a conversation with a Blocks agent and stream its responses over plain HTTP. Every endpoint lives under `/rest/v1`, accepts and returns JSON, and authenticates with a workspace-scoped API key.

|                 |                                        |
| --------------- | -------------------------------------- |
| **Base URL**    | `https://api.blocks.team`              |
| **Auth header** | `Authorization: ApiKey <YOUR_API_KEY>` |
| **Rate limit**  | 100 requests / minute / API key        |

## 1. Create a Blocks account and get an API key

Create a [Blocks account](https://blocks.team/signup), then generate a workspace API key from **Settings** > **API Keys**. Set the API key in your environment as `BLOCKS_API_KEY` before running the snippets below. For example:

```bash theme={null}
export BLOCKS_API_KEY="your_api_key_here"
```

## 2. Create a session and poll for the first reply

1. **Create a session** — `POST /rest/v1/sessions`. The response includes `_links.final_message.href`, a pre-built URL for polling the assistant's reply.
2. **Poll the URL** — `GET` it until `items` is non-empty.

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

  // 1. Create a session.
  const session = await fetch(`${BASE_URL}/rest/v1/sessions`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      agent_name: "claude",
      message: "Say hello and tell me a one-sentence fun fact about octopuses.",
    }),
  }).then((r) => r.json());

  // 2. Poll for the assistant's final message.
  while (true) {
    const page = await fetch(session._links.final_message.href, { headers }).then((r) => r.json());
    if (page.items.length > 0) {
      console.log(page.items[0].message);
      break;
    }
    await new Promise((r) => setTimeout(r, 5000));
  }
  ```

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

  BASE_URL = "https://api.blocks.team"
  HEADERS = {
      "Authorization": f"ApiKey {os.environ['BLOCKS_API_KEY']}",
      "Content-Type": "application/json",
  }

  # 1. Create a session.
  session = requests.post(f"{BASE_URL}/rest/v1/sessions", headers=HEADERS, json={
      "agent_name": "claude",
      "message": "Say hello and tell me a one-sentence fun fact about octopuses.",
  }).json()

  # 2. Poll for the assistant's final message.
  while True:
      page = requests.get(session["_links"]["final_message"]["href"], headers=HEADERS).json()
      if page["items"]:
          print(page["items"][0]["message"])
          break
      time.sleep(5)
  ```

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

  # 1. Create a session.
  SESSION=$(curl -s -X POST "$BASE_URL/rest/v1/sessions" \
    -H "$AUTH" -H "Content-Type: application/json" \
    -d '{"agent_name":"claude","message":"Say hello and tell me a one-sentence fun fact about octopuses."}')
  SESSION_ID=$(echo "$SESSION" | jq -r '.id')
  FINAL_URL=$(echo "$SESSION" | jq -r '._links.final_message.href')

  # 2. Poll for the assistant's final message.
  while :; do
    PAGE=$(curl -s -H "$AUTH" "$FINAL_URL")
    if [ "$(echo "$PAGE" | jq '.items | length')" -gt 0 ]; then
      echo "$PAGE" | jq -r '.items[0].message'
      break
    fi
    sleep 5
  done
  ```

  ```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();

  // 1. Create a session.
  JsonNode session = json.readTree(http.send(
      HttpRequest.newBuilder(URI.create(BASE_URL + "/rest/v1/sessions"))
          .header("Authorization", AUTH).header("Content-Type", "application/json")
          .POST(HttpRequest.BodyPublishers.ofString(
              "{\"agent_name\":\"claude\",\"message\":\"Say hello and tell me a one-sentence fun fact about octopuses.\"}"))
          .build(),
      HttpResponse.BodyHandlers.ofString()).body());
  String sessionId = session.get("id").asText();
  String finalUrl = session.at("/_links/final_message/href").asText();

  // 2. Poll for the assistant's final message.
  while (true) {
      JsonNode page = json.readTree(http.send(
          HttpRequest.newBuilder(URI.create(finalUrl)).header("Authorization", AUTH).build(),
          HttpResponse.BodyHandlers.ofString()).body());
      if (page.get("items").size() > 0) {
          System.out.println(page.at("/items/0/message").asText());
          break;
      }
      Thread.sleep(5000);
  }
  ```

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

  BASE_URL = "https://api.blocks.team"
  HEADERS = {
    "Authorization" => "ApiKey #{ENV['BLOCKS_API_KEY']}",
    "Content-Type" => "application/json",
  }

  # 1. Create a session.
  session = JSON.parse(Net::HTTP.post(
    URI("#{BASE_URL}/rest/v1/sessions"),
    JSON.generate({
      agent_name: "claude",
      message: "Say hello and tell me a one-sentence fun fact about octopuses.",
    }),
    HEADERS,
  ).body)

  # 2. Poll for the assistant's final message.
  loop do
    page = JSON.parse(Net::HTTP.get(URI(session["_links"]["final_message"]["href"]), HEADERS))
    break puts(page["items"][0]["message"]) if page["items"].any?
    sleep 5
  end
  ```

  ```go Go theme={null}
  package main

  import (
  	"bytes"
  	"encoding/json"
  	"fmt"
  	"net/http"
  	"os"
  	"time"
  )

  const BaseURL = "https://api.blocks.team"

  func main() {
  	auth := "ApiKey " + os.Getenv("BLOCKS_API_KEY")

  	// 1. Create a session.
  	body, _ := json.Marshal(map[string]string{
  		"agent_name": "claude",
  		"message":    "Say hello and tell me a one-sentence fun fact about octopuses.",
  	})
  	req, _ := http.NewRequest("POST", BaseURL+"/rest/v1/sessions", bytes.NewReader(body))
  	req.Header.Set("Authorization", auth)
  	req.Header.Set("Content-Type", "application/json")
  	res, _ := http.DefaultClient.Do(req)
  	var session struct {
  		ID    string `json:"id"`
  		Links struct {
  			FinalMessage struct{ Href string } `json:"final_message"`
  		} `json:"_links"`
  	}
  	json.NewDecoder(res.Body).Decode(&session)
  	res.Body.Close()

  	// 2. Poll for the assistant's final message.
  	for {
  		req, _ := http.NewRequest("GET", session.Links.FinalMessage.Href, nil)
  		req.Header.Set("Authorization", auth)
  		res, _ := http.DefaultClient.Do(req)
  		var page struct {
  			Items []struct{ Message string } `json:"items"`
  		}
  		json.NewDecoder(res.Body).Decode(&page)
  		res.Body.Close()
  		if len(page.Items) > 0 {
  			fmt.Println(page.Items[0].Message)
  			break
  		}
  		time.Sleep(5 * time.Second)
  	}
  }
  ```
</CodeGroup>

## 3. Send a follow-up

3. **Send a follow-up** — `POST /rest/v1/sessions/{session_id}/messages`. The response returns its own `_links.final_message.href`, the same shortcut as create.
4. **Poll the new thread's URL** — same pattern as above.

<Note>
  Follow-ups can be sent at any time — including while the agent is still working — and will interrupt the in-flight turn.
</Note>

The snippets below assume `session`, `headers`, and `BASE_URL` are still in scope from the previous step.

<CodeGroup>
  ```javascript JavaScript theme={null}
  // 3. Send a follow-up.
  const followup = await fetch(`${BASE_URL}/rest/v1/sessions/${session.id}/messages`, {
    method: "POST",
    headers,
    body: JSON.stringify({ message: "Cool — now tell me one about cuttlefish." }),
  }).then((r) => r.json());

  // 4. Poll the follow-up's thread.
  while (true) {
    const page = await fetch(followup._links.final_message.href, { headers }).then((r) => r.json());
    if (page.items.length > 0) {
      console.log(page.items[0].message);
      break;
    }
    await new Promise((r) => setTimeout(r, 5000));
  }
  ```

  ```python Python theme={null}
  # 3. Send a follow-up.
  followup = requests.post(
      f"{BASE_URL}/rest/v1/sessions/{session['id']}/messages",
      headers=HEADERS,
      json={"message": "Cool — now tell me one about cuttlefish."},
  ).json()

  # 4. Poll the follow-up's thread.
  while True:
      page = requests.get(followup["_links"]["final_message"]["href"], headers=HEADERS).json()
      if page["items"]:
          print(page["items"][0]["message"])
          break
      time.sleep(5)
  ```

  ```bash cURL theme={null}
  # 3. Send a follow-up.
  FOLLOWUP=$(curl -s -X POST "$BASE_URL/rest/v1/sessions/$SESSION_ID/messages" \
    -H "$AUTH" -H "Content-Type: application/json" \
    -d '{"message":"Cool — now tell me one about cuttlefish."}')
  FOLLOW_URL=$(echo "$FOLLOWUP" | jq -r '._links.final_message.href')

  # 4. Poll the follow-up's thread.
  while :; do
    PAGE=$(curl -s -H "$AUTH" "$FOLLOW_URL")
    if [ "$(echo "$PAGE" | jq '.items | length')" -gt 0 ]; then
      echo "$PAGE" | jq -r '.items[0].message'
      break
    fi
    sleep 5
  done
  ```

  ```java Java theme={null}
  // 3. Send a follow-up.
  JsonNode followup = json.readTree(http.send(
      HttpRequest.newBuilder(URI.create(BASE_URL + "/rest/v1/sessions/" + sessionId + "/messages"))
          .header("Authorization", AUTH).header("Content-Type", "application/json")
          .POST(HttpRequest.BodyPublishers.ofString(
              "{\"message\":\"Cool — now tell me one about cuttlefish.\"}"))
          .build(),
      HttpResponse.BodyHandlers.ofString()).body());
  String followUrl = followup.at("/_links/final_message/href").asText();

  // 4. Poll the follow-up's thread.
  while (true) {
      JsonNode page = json.readTree(http.send(
          HttpRequest.newBuilder(URI.create(followUrl)).header("Authorization", AUTH).build(),
          HttpResponse.BodyHandlers.ofString()).body());
      if (page.get("items").size() > 0) {
          System.out.println(page.at("/items/0/message").asText());
          break;
      }
      Thread.sleep(5000);
  }
  ```

  ```ruby Ruby theme={null}
  # 3. Send a follow-up.
  followup = JSON.parse(Net::HTTP.post(
    URI("#{BASE_URL}/rest/v1/sessions/#{session['id']}/messages"),
    JSON.generate({ message: "Cool — now tell me one about cuttlefish." }),
    HEADERS,
  ).body)

  # 4. Poll the follow-up's thread.
  loop do
    page = JSON.parse(Net::HTTP.get(URI(followup["_links"]["final_message"]["href"]), HEADERS))
    break puts(page["items"][0]["message"]) if page["items"].any?
    sleep 5
  end
  ```

  ```go Go theme={null}
  // 3. Send a follow-up.
  body, _ := json.Marshal(map[string]string{
  	"message": "Cool — now tell me one about cuttlefish.",
  })
  req, _ := http.NewRequest("POST", BaseURL+"/rest/v1/sessions/"+session.ID+"/messages", bytes.NewReader(body))
  req.Header.Set("Authorization", auth)
  req.Header.Set("Content-Type", "application/json")
  res, _ := http.DefaultClient.Do(req)
  var followup struct {
  	Links struct {
  		FinalMessage struct{ Href string } `json:"final_message"`
  	} `json:"_links"`
  }
  json.NewDecoder(res.Body).Decode(&followup)
  res.Body.Close()

  // 4. Poll the follow-up's thread.
  for {
  	req, _ := http.NewRequest("GET", followup.Links.FinalMessage.Href, nil)
  	req.Header.Set("Authorization", auth)
  	res, _ := http.DefaultClient.Do(req)
  	var page struct {
  		Items []struct{ Message string } `json:"items"`
  	}
  	json.NewDecoder(res.Body).Decode(&page)
  	res.Body.Close()
  	if len(page.Items) > 0 {
  		fmt.Println(page.Items[0].Message)
  		break
  	}
  	time.Sleep(5 * time.Second)
  }
  ```
</CodeGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Create Session" icon="plus" href="/rest-api/sessions/create">
    Full request and response schema for `POST /rest/v1/sessions`.
  </Card>

  <Card title="Get Session Messages" icon="list" href="/rest-api/sessions/messages">
    List, filter, and poll messages on a session or a single thread.
  </Card>

  <Card title="Send Messages" icon="paper-plane" href="/rest-api/sessions/follow-up">
    Post follow-ups — they interrupt an in-flight session.
  </Card>

  <Card title="Get Session" icon="magnifying-glass" href="/rest-api/sessions/get">
    Look up a single session by ID.
  </Card>
</CardGroup>
