> ## Documentation Index
> Fetch the complete documentation index at: https://openrewardstandard.io/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP API Reference

> Complete ORS endpoint documentation

This page documents HTTP endpoints in the Open Reward Standard.

## Base URL

All endpoints are relative to the ORS server base URL:

```
http://localhost:8080         # Local development
https://your-server.com       # Production
```

## Common Headers

### X-Session-ID

Most endpoints require the `X-Session-ID` header to identify the episode:

```http theme={null}
X-Session-ID: abc-123-def-456
```

**Required for**: `/create`, `/delete`, `/delete_session`, `/ping`, `/{env_name}/call`, `/{env_name}/prompt`, `/{env_name}/task_tools`

**Not required for**: `/create_session`, discovery endpoints, `/health`

## Discovery Endpoints

These endpoints provide information about available environments, tools, and tasks. They generally do not require session IDs, with one exception: listing tools may require an active session when the environment defines task-specific tools (see below).

### GET /health

Health check endpoint.

**Request**:

```http theme={null}
GET /health
```

**Response** (200 OK):

```json theme={null}
{
  "status": "ok"
}
```

**Use case**: Verify server is running and responsive.

***

### GET /list\_environments

List all available environments on this server.

**Request**:

```http theme={null}
GET /list_environments
```

**Response** (200 OK):

```json theme={null}
[
  "math",
  "code_env",
  "web_nav"
]
```

Returns an array of environment names (strings). Each name can be used in `/{env_name}/*` endpoints.

**Use case**: Discover what environments are available before creating a session.

***

### GET /\{env\_name}/tools

List all tools available in the specified environment.

**Request**:

```http theme={null}
GET /math/tools
```

**Response** (200 OK):

```json theme={null}
{
  "tools": [
    {
      "name": "submit",
      "description": "Submit an answer to the math problem",
      "input_schema": {
        "type": "object",
        "properties": {
          "answer": {
            "type": "string",
            "description": "Your answer to the problem"
          }
        },
        "required": ["answer"]
      }
    }
  ]
}
```

**Response Schema**:

* `tools`: Array of ToolSpec objects
  * `name`: Tool identifier (string)
  * `description`: Human-readable description (string)
  * `input_schema`: JSON Schema defining tool parameters (object, optional)

**Error Responses**:

* **404 Not Found**: Environment name not recognized

**Use case**: Discover what actions the agent can take before starting an episode.

<Note>
  Some environments define **task-specific tools** (using `@tool(shared=False)`) that are only available within an active session. Use the `GET /{env_name}/task_tools` endpoint (which requires an active session) to get the combined set of shared and task-specific tools. This sessionless `/{env_name}/tools` endpoint returns only shared tools.
</Note>

***

### GET /\{env\_name}/splits

List all task splits available in the environment.

**Request**:

```http theme={null}
GET /math/splits
```

**Response** (200 OK):

```json theme={null}
[
  {
    "name": "train",
    "type": "train"
  },
  {
    "name": "test",
    "type": "test"
  }
]
```

**Response Schema**:

* Array of Split objects
  * `name`: Split identifier (string)
  * `type`: Split category - "train", "validation", or "test" (string)

**Error Responses**:

* **404 Not Found**: Environment name not recognized

**Use case**: Determine which task sets are available for training vs evaluation.

***

### POST /\{env\_name}/tasks

List tasks for a specific split.

**Request**:

```http theme={null}
POST /math/tasks
Content-Type: application/json

{
  "split": "train"
}
```

**Request Schema**:

* `split`: Split name to query (string, required)

**Response** (200 OK):

```json theme={null}
{
  "tasks": [
    {
      "question": "What is 2+2?",
      "answer": "4"
    },
    {
      "question": "If x + 5 = 12, what is x?",
      "answer": "7"
    }
  ],
  "env_name": "math"
}
```

**Response Schema**:

* `tasks`: Array of task objects (structure is environment-specific)
* `env_name`: Environment name (string)

**Error Responses**:

* **400 Bad Request**: Invalid split name
* **404 Not Found**: Environment name not recognized

**Use case**: Get the list of tasks to iterate through during training or evaluation.

**Note**: Task structure is environment-specific. Math environments have questions and answers. Coding environments have problem descriptions and test cases.

***

### POST /\{env\_name}/num\_tasks

Get the number of tasks for a specific split.

**Request**:

```http theme={null}
POST /math/num_tasks
Content-Type: application/json

{
  "split": "train"
}
```

**Request Schema**:

* `split`: Split name to query (string, required)

**Response** (200 OK):

```json theme={null}
{
  "num_tasks": 500
}
```

**Error Responses**:

* **400 Bad Request**: Invalid split name
* **404 Not Found**: Environment name not recognized

**Use case**: Check the size of a task set without downloading all tasks. Useful for pagination with `/{env_name}/task_range`.

***

### POST /\{env\_name}/task

Get a single task by split and index.

**Request**:

```http theme={null}
POST /math/task
Content-Type: application/json

{
  "split": "train",
  "index": 0
}
```

**Request Schema**:

* `split`: Split name (string, required)
* `index`: Zero-based task index (integer, required)

**Response** (200 OK):

```json theme={null}
{
  "task": {
    "question": "What is 2+2?",
    "answer": "4"
  }
}
```

**Error Responses**:

* **400 Bad Request**: Invalid split name or invalid index (out of range)
* **404 Not Found**: Environment name not recognized

**Use case**: Fetch individual tasks for lazy loading or selective evaluation, without downloading the full task list.

***

### POST /\{env\_name}/task\_range

Get tasks for a range of indices.

**Request**:

```http theme={null}
POST /math/task_range
Content-Type: application/json

{
  "split": "train",
  "start": 0,
  "stop": 10
}
```

**Request Schema**:

* `split`: Split name (string, required)
* `start`: Start index, inclusive (integer, optional, default: 0). Supports negative indices.
* `stop`: Stop index, exclusive (integer, optional, default: num\_tasks). Supports negative indices.

**Response** (200 OK):

```json theme={null}
{
  "tasks": [
    {"question": "What is 2+2?", "answer": "4"},
    {"question": "If x + 5 = 12, what is x?", "answer": "7"}
  ]
}
```

**Error Responses**:

* **400 Bad Request**: Invalid split name or invalid index range
* **404 Not Found**: Environment name not recognized

**Use case**: Paginate through large task sets. Follows Python `range()`/slice conventions: start is inclusive, stop is exclusive. Negative indices are resolved relative to the total number of tasks.

## Session Management

These endpoints manage episode lifecycle.

### POST /create\_session

Generate a new session ID.

**Request**:

```http theme={null}
POST /create_session
```

**Response** (200 OK):

```json theme={null}
{
  "sid": "abc-123-def-456"
}
```

**Response Schema**:

* `sid`: Session identifier (string, UUID format)

**Use case**: First step in episode lifecycle. Get a session ID to use in subsequent requests.

**Note**: This endpoint only generates an ID. The episode instance is created by `/create`.

***

### POST /create

Create an episode instance for a specific task.

**Request**:

```http theme={null}
POST /create
X-Session-ID: abc-123-def-456
Content-Type: application/json

{
  "env_name": "math",
  "task_spec": {
    "question": "What is 2+2?",
    "answer": "4"
  },
  "secrets": {
    "api_key": "sk-..."
  }
}
```

**Request Headers**:

* `X-Session-ID`: Session identifier from `/create_session` (required)

**Request Schema**:

* `env_name`: Environment to instantiate (string, optional). Defaults to the first registered environment if omitted. If the server hosts a single environment, requests to unrecognized paths are redirected to it automatically.
* `task_spec`: Task-specific data passed to the environment constructor (object). Provide **either** `task_spec` **or** both `split` and `index`, not both.
* `split`: Split name to resolve the task from (string). Must be provided together with `index`.
* `index`: Task index within the split (integer). Must be provided together with `split`.
* `secrets`: Secrets to pass to environment (object, optional, default: {})

<Note>
  You must provide exactly one of: `task_spec` (pass the full task object directly) or `split` + `index` (let the server resolve the task via its `get_task()` method). Providing both or neither will return a 400 error.
</Note>

**Response** (200 OK):

```json theme={null}
{
  "sid": "abc-123-def-456"
}
```

**Error Responses**:

* **400 Bad Request**: Session already exists, or missing X-Session-ID header
* **404 Not Found**: Environment name not recognized

**Use case**: Start an episode by initializing the environment with a specific task. This calls the environment's `setup()` method.

**Important**: The environment initialization happens asynchronously. Subsequent requests wait for setup to complete.

***

### POST /ping

Keep the session alive to prevent timeout.

**Request**:

```http theme={null}
POST /ping
X-Session-ID: abc-123-def-456
```

**Request Headers**:

* `X-Session-ID`: Session identifier (required)

**Response** (200 OK):

```json theme={null}
{
  "status": "ok"
}
```

**Error Responses**:

* **400 Bad Request**: Missing X-Session-ID header
* **404 Not Found**: Session not found

**Use case**: Sessions timeout after 15 minutes of inactivity. Call `/ping` periodically to keep the session alive. The reference SDK pings every 10 seconds; at minimum, ping well before the 15-minute timeout.

***

### POST /delete

Delete an episode and clean up resources.

**Request**:

```http theme={null}
POST /delete
X-Session-ID: abc-123-def-456
```

**Request Headers**:

* `X-Session-ID`: Session identifier (required)

**Response** (200 OK):

```json theme={null}
{
  "sid": "abc-123-def-456"
}
```

**Error Responses**:

* **400 Bad Request**: Missing X-Session-ID header
* **404 Not Found**: Session not found

**Use case**: Clean up after episode completes. This calls the environment's `teardown()` method.

**Important**: Always call `/delete` when done with an episode to free server resources.

***

### POST /delete\_session

Optional cleanup endpoint for session ID.

**Request**:

```http theme={null}
POST /delete_session
X-Session-ID: abc-123-def-456
```

**Request Headers**:

* `X-Session-ID`: Session identifier (required)

**Response** (200 OK):

```json theme={null}
{
  "sid": "abc-123-def-456"
}
```

**Use case**: Optional cleanup after `/delete`. In practice, `/delete` is sufficient.

## Episode Interaction

These endpoints interact with an active episode.

### GET /\{env\_name}/task\_tools

List all tools (shared + task-specific) available in the current session.

**Request**:

```http theme={null}
GET /math/task_tools
X-Session-ID: abc-123-def-456
```

**Request Headers**:

* `X-Session-ID`: Session identifier (required)

**Response** (200 OK):

```json theme={null}
{
  "tools": [
    {
      "name": "submit",
      "description": "Submit an answer",
      "input_schema": { "type": "object", "properties": { "answer": { "type": "string" } }, "required": ["answer"] }
    },
    {
      "name": "get_hint",
      "description": "Get a hint for this specific task",
      "input_schema": null
    }
  ]
}
```

**Response Schema**:

* `tools`: Array of ToolSpec objects (shared tools + task-specific tools combined)

**Error Responses**:

* **400 Bad Request**: Missing X-Session-ID header
* **404 Not Found**: Session not found
* **410 Gone**: Session was deleted

**Use case**: Get the full set of tools available for the current task, including task-specific tools that are not returned by the sessionless `/{env_name}/tools` endpoint. Tools marked with `@tool(shared=False)` or returned by `list_task_tools()` only appear here.

***

### GET /\{env\_name}/prompt

Get the initial prompt for the current task.

**Request**:

```http theme={null}
GET /math/prompt
X-Session-ID: abc-123-def-456
```

**Request Headers**:

* `X-Session-ID`: Session identifier (required)

**Response** (200 OK):

```json theme={null}
[
  {
    "text": "What is 2+2?",
    "detail": null,
    "type": "text"
  }
]
```

**Response Schema**:

* Array of Block objects
  * TextBlock: `{"text": "...", "detail": null, "type": "text"}`
  * ImageBlock: `{"data": "base64...", "mimeType": "image/png", "detail": null, "type": "image"}`

**Error Responses**:

* **400 Bad Request**: Missing X-Session-ID header
* **404 Not Found**: Session not found (no active environment for this session ID)
* **410 Gone**: Session was deleted

**Use case**: Get the initial state/instructions for the episode. Call this after `/create` and before calling tools.

**Note**: Prompts can be multi-modal (text + images). The environment is resolved by the session, not by the `{env_name}` path segment. The path parameter is not validated against the session's actual environment.

***

### POST /\{env\_name}/call

Call a tool in the environment.

**Request**:

```http theme={null}
POST /math/call
X-Session-ID: abc-123-def-456
Accept: text/event-stream
Content-Type: application/json

{
  "name": "submit",
  "input": {
    "answer": "4"
  },
  "task_id": "optional-trace-id"
}
```

**Request Headers**:

* `X-Session-ID`: Session identifier (required)
* `Accept`: Should be `text/event-stream` (recommended). The server returns SSE regardless, but setting this header is good practice for client clarity.

**Request Schema**:

* `name`: Tool name to call (string, required)
* `input`: Tool-specific parameters (object, required)
* `task_id`: Identifier for reconnection and tracing (string, optional). If provided on a reconnect, the server returns the result of the original task instead of starting a new one. Completed results linger for 60 seconds, enabling recovery from dropped connections.

**Response** (200 OK - Server-Sent Events):

Small response (fits in one event):

```
event: task_id
data: 877bb56c594e4a0f921ad55c439a3762

event: end
data: {"ok": true, "output": {"blocks": [{"text": "Correct!", "detail": null, "type": "text"}], "metadata": null, "reward": 1.0, "finished": true}}
```

Large response (>4 KB, chunked):

```
event: task_id
data: 877bb56c594e4a0f921ad55c439a3762

event: chunk
data: {"ok": true, "output": {"blocks": [{"text": "Very long out

event: end
data: put...", "detail": null, "type": "text"}], "metadata": null, "reward": 0.5, "finished": false}}
```

**SSE Event Types**:

| Event     | Data                         | Description                                                                                                                           |
| --------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `task_id` | Task identifier string       | Always sent first. Use this ID to reconnect if the connection drops.                                                                  |
| `chunk`   | Partial JSON string          | Intermediate chunk for responses exceeding 4 KB. Concatenate all `chunk` data with the final `end` data to reconstruct the full JSON. |
| `end`     | JSON string (or final chunk) | Final event. Contains the complete response JSON, or the last chunk if the response was split.                                        |
| `error`   | Error message string         | Sent if the `task_id` is unknown (on reconnect) or the task raised an exception. No `end` event follows.                              |

**Response Schema** (reconstructed from SSE data):

On success:

```json theme={null}
{
  "ok": true,
  "output": {
    "blocks": [
      {"text": "Tool output", "detail": null, "type": "text"}
    ],
    "metadata": null,
    "reward": 1.0,
    "finished": true
  }
}
```

On error:

```json theme={null}
{
  "ok": false,
  "error": "Error message"
}
```

**Error Responses**:

* **400 Bad Request**: Missing X-Session-ID, invalid tool name, or invalid input
* **404 Not Found**: Session not found, environment mismatch, or tool not found
* **410 Gone**: Session was deleted

**Use case**: Execute an action in the environment. This is the core interaction loop.

**Important**:

* Responses are delivered via Server-Sent Events (see Tool Execution with SSE section above)
* The `finished` field indicates if the episode is complete
* The `reward` field provides RL feedback signal

## Complete Episode Flow

Here's a complete example showing all endpoints in sequence:

```bash theme={null}
# 1. Health check
curl http://localhost:8080/health
# → {"status": "ok"}

# 2. List environments
curl http://localhost:8080/list_environments
# → ["math"]

# 3. List tools
curl http://localhost:8080/math/tools
# → {"tools": [{"name": "submit", ...}]}

# 4. List splits
curl http://localhost:8080/math/splits
# → [{"name": "train", "type": "train"}, ...]

# 5. List tasks
curl -X POST http://localhost:8080/math/tasks \
  -H "Content-Type: application/json" \
  -d '{"split": "train"}'
# → {"tasks": [...], "env_name": "math"}

# 6. Create session ID
curl -X POST http://localhost:8080/create_session
# → {"sid": "abc-123"}

# 7. Create episode (option A: pass task_spec directly)
curl -X POST http://localhost:8080/create \
  -H "X-Session-ID: abc-123" \
  -H "Content-Type: application/json" \
  -d '{"env_name": "math", "task_spec": {"question": "What is 2+2?"}, "secrets": {}}'
# → {"sid": "abc-123"}

# 7b. Or create episode (option B: resolve by split + index)
# curl -X POST http://localhost:8080/create \
#   -H "X-Session-ID: abc-123" \
#   -H "Content-Type: application/json" \
#   -d '{"env_name": "math", "split": "train", "index": 0, "secrets": {}}'
# → {"sid": "abc-123"}

# 8. Get prompt
curl http://localhost:8080/math/prompt \
  -H "X-Session-ID: abc-123"
# → [{"text": "What is 2+2?", "detail": null, "type": "text"}]

# 9. Call tool (SSE response)
curl -X POST http://localhost:8080/math/call \
  -H "X-Session-ID: abc-123" \
  -H "Accept: text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{"name": "submit", "input": {"answer": "4"}}'
# → SSE stream with result

# 10. Delete episode
curl -X POST http://localhost:8080/delete \
  -H "X-Session-ID: abc-123"
# → {"sid": "abc-123"}
```

## Error Handling

### Standard HTTP Status Codes

* **200 OK**: Request succeeded
* **400 Bad Request**: Invalid input (missing header, invalid JSON, etc.)
* **404 Not Found**: Resource not found (environment, session, tool)
* **410 Gone**: Session was deleted
* **500 Internal Server Error**: Server error during processing

### Error Response Format

For HTTP errors (non-SSE):

```json theme={null}
{
  "detail": "Error message explaining what went wrong"
}
```

For tool execution errors (SSE):

```json theme={null}
{
  "ok": false,
  "error": "Tool execution failed: reason"
}
```

## Rate Limiting and Timeouts

### Session Timeout

Sessions automatically expire after **15 minutes** of inactivity:

* "Activity" means any request with that session's X-Session-ID
* Use `/ping` to keep sessions alive
* Timeout is reset after each request

### Request Timeout

Individual requests may have server-specific timeouts. All tool calls use SSE, which keeps connections alive with periodic pings during long-running execution.

## Next Steps

<CardGroup cols={2}>
  <Card title="Data Types" icon="shapes" href="/specification/data-types">
    Detailed schemas for all request/response objects
  </Card>

  <Card title="Implementing a Client" icon="laptop-code" href="/guides/implementing-client">
    Build an ORS client using these endpoints
  </Card>

  <Card title="Implementing a Server" icon="server" href="/guides/implementing-server">
    Build an ORS server that implements these endpoints
  </Card>
</CardGroup>

***

**Reference Implementation**: The [ORS Python SDK](https://github.com/openrewardstandard/python-sdk) implements this complete API.
