Skip to main content
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 URLhttps://api.blocks.team
Auth headerAuthorization: ApiKey <YOUR_API_KEY>
Rate limit100 requests / minute / API key

1. Create a Blocks account and get an API key

Create a Blocks account, 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:
export BLOCKS_API_KEY="your_api_key_here"

2. Create a session and poll for the first reply

  1. Create a sessionPOST /rest/v1/sessions. The response includes _links.final_message.href, a pre-built URL for polling the assistant’s reply.
  2. Poll the URLGET it until items is non-empty.
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));
}
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)
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
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);
}
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
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)
	}
}

3. Send a follow-up

  1. Send a follow-upPOST /rest/v1/sessions/{session_id}/messages. The response returns its own _links.final_message.href, the same shortcut as create.
  2. Poll the new thread’s URL — same pattern as above.
Follow-ups can be sent at any time — including while the agent is still working — and will interrupt the in-flight turn.
The snippets below assume session, headers, and BASE_URL are still in scope from the previous step.
// 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));
}
# 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)
# 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
// 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);
}
# 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
// 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)
}

Next steps

Create Session

Full request and response schema for POST /rest/v1/sessions.

Get Session Messages

List, filter, and poll messages on a session or a single thread.

Send Messages

Post follow-ups — they interrupt an in-flight session.

Get Session

Look up a single session by ID.