20minds Border Collie – Open Responses SDK
This document shows how to call the OpenAI Python SDK Responses API for the Border Collie server.
Prerequisites
- Sign up for a 20minds account
- Create an API key in settings
- Install Python 3.10+ and OpenAI Python SDK (tested with
2.15.0)
Install the SDK:
pip install openai==2.15.0
Configure the client
Border Collie uses an API key passed via the OpenAI SDK
api_key field.from openai import OpenAI
TWENTYMINDS_BASE_URL = "https://api.20minds.ai/v1/border-collie"
TWENTYMINDS_API_KEY = "your-api-key"
client = OpenAI(
base_url=TWENTYMINDS_BASE_URL,
api_key=TWENTYMINDS_API_KEY,
)
Create an agent task
task_mode:
- agent: tool-executing mode (similar to border_collie_stream.py)
- coding: reasoning-first mode (similar to border_collie.py)
response = client.responses.create(
input=[
{
"role": "user",
"content": [
{"type": "input_text",
"text": "What is the central dogma of molecular biology?"}
],
}
],
extra_body={
"task_mode": "agent",
},
)
print(response.id)
print(response.status)
print(response.metadata.get("task_url"))
Poll for completion
import time
resp = client.responses.retrieve(response_id=response.id)
while resp.status == "running":
time.sleep(5)
resp = client.responses.retrieve(response_id=response.id)
print(resp.status)
print(resp.output)
Continue a conversation
Use either
previous_response_id or extra_body.task_id (not both).followup = client.responses.create(
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": "Explain it simply."}
],
}
],
previous_response_id=response.id,
)
Delete a task
client.responses.delete(response_id=response.id)
Response shape (summary)
A completed response contains the full conversation in
output:for msg in resp.output:
print(msg["role"], msg["content"]) # content is a list of blocks
Notes
- Images can be sent using
input_imagewith a public URL or a base64data:URL. - The server enforces image caps (count/bytes). Excess images are dropped.
File uploads
from openai import BaseModel
from typing import Literal
class FileCreate(BaseModel):
id: str
response_id: str
object: Literal["file"]
filename: str
status: str
upload_url: str
upload_expires_at: str
created_at: str
file_record = client.post(
"/files",
body={"filename": "letter_to_nature_2026.pdf"},
cast_to=FileCreate,
)
upload_response = requests.put(
file_record.upload_url,
data=requests.get("https://…").content
)
upload_response.raise_for_status()
response = client.responses.create(
input=[{
"role": "user",
"content": [
{"type": "input_text", "text": "Summarize the uploaded report."},
{"type": "input_file", "file_id": file_record.id},
],
}],
previous_response_id = file_record.response_id,
)
upload_url expires after 3 minutes.