20minds Border Collie – Open Responses SDK

This document shows how to call the OpenAI Python SDK Responses API for the Border Collie server.

Prerequisites

  1. Sign up for a 20minds account
  2. Create an API key in settings
  3. 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_image with a public URL or a base64 data: 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.