Skip to content

API Endpoints

Detailed documentation for all REST API endpoints.


Submissions

Create Submission

Submit a policy for evaluation.

POST /submissions

Content-Type: multipart/form-data

Parameters:

Name Type Required Description
policy file Yes Zip file containing policy.py
scenario_id string Yes Target scenario ID

Example:

curl -X POST https://botmanifold.com/api/submissions \
  -F "policy=@policy.zip" \
  -F "scenario_id=messy_room_v1"
import requests

with open("policy.zip", "rb") as f:
    response = requests.post(
        "https://botmanifold.com/api/submissions",
        files={"policy": f},
        data={"scenario_id": "messy_room_v1"}
    )

print(response.json())

Response (201 Created):

{
  "submission_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "PENDING",
  "scenario_id": "messy_room_v1",
  "created_at": "2026-01-20T10:30:00Z"
}

List Submissions

Get a list of submissions.

GET /submissions

Query Parameters:

Name Type Default Description
scenario string - Filter by scenario ID
limit integer 20 Max results (1-100)
offset integer 0 Pagination offset

Example:

curl "https://botmanifold.com/api/submissions?scenario=messy_room_v1&limit=10"

Response (200 OK):

{
  "submissions": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "scenario_id": "messy_room_v1",
      "status": "COMPLETED",
      "verdict": "PASS",
      "score": 95.5,
      "created_at": "2026-01-20T10:30:00Z"
    },
    ...
  ],
  "total": 42,
  "limit": 10,
  "offset": 0
}

Get Submission

Get details of a specific submission.

GET /submissions/{id}

Path Parameters:

Name Type Description
id string Submission UUID

Example:

curl "https://botmanifold.com/api/submissions/550e8400-e29b-41d4-a716-446655440000"

Response (200 OK):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "scenario_id": "messy_room_v1",
  "status": "COMPLETED",
  "verdict": "PASS",
  "score": 95.5,
  "video_url": "https://storage.botmanifold.com/videos/550e8400.mp4",
  "judge_feedback": "All objects successfully placed in target zone.",
  "created_at": "2026-01-20T10:30:00Z",
  "completed_at": "2026-01-20T10:32:15Z"
}

Submission Events (SSE)

Stream real-time updates for a submission.

GET /submissions/{id}/events

Content-Type: text/event-stream

Example:

const eventSource = new EventSource(
  'https://botmanifold.com/api/submissions/550e8400.../events'
);

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Status:', data.status);

  if (data.done) {
    eventSource.close();
  }
};

Event Data:

{"status": "QUEUED", "done": false}
{"status": "RUNNING", "done": false}
{"status": "COMPLETED", "verdict": "PASS", "score": 95.5, "done": true}

Scenarios

List Scenarios

Get all available scenarios.

GET /scenarios

Example:

curl "https://botmanifold.com/api/scenarios"

Response (200 OK):

{
  "scenarios": [
    {
      "id": "messy_room_v1",
      "name": "Messy Room Challenge",
      "description": "Clean up scattered items in a room",
      "difficulty": "medium",
      "time_limit_seconds": 60,
      "max_score": 120
    },
    ...
  ]
}

Get Scenario

Get details of a specific scenario.

GET /scenarios/{id}

Path Parameters:

Name Type Description
id string Scenario ID

Example:

curl "https://botmanifold.com/api/scenarios/messy_room_v1"

Response (200 OK):

{
  "id": "messy_room_v1",
  "name": "Messy Room Challenge",
  "description": "Clean up scattered items in a room",
  "difficulty": "medium",
  "time_limit_seconds": 60,
  "max_score": 120,
  "long_description": "The Messy Room scenario challenges...",
  "rules": [
    "Move all objects to the target zone",
    "Objects knocked off the table result in penalties",
    "Complete within the time limit"
  ]
}

Error Responses

Validation Error (400)

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid scenario ID: unknown_scenario",
    "details": {
      "field": "scenario_id",
      "value": "unknown_scenario"
    }
  }
}

Not Found (404)

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Submission not found"
  }
}

Rate Limited (429)

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests. Try again in 60 seconds.",
    "retry_after": 60
  }
}