/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, then generate a workspace API key from Settings > API Keys. Set the API key in your environment asBLOCKS_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
- Create a session —
POST /rest/v1/sessions. The response includes_links.final_message.href, a pre-built URL for polling the assistant’s reply. - Poll the URL —
GETit untilitemsis 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
- 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. - 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.
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.

