# HTTP Contracts for the @openmaic/storage Package: Complete API Reference

> Explore the HTTP contracts for the @openmaic/storage package. Discover Runtime, Document, and Asset APIs with the unified createStorageHttpHandler composable.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: api-reference
- Published: 2026-09-10

---

**The @openmaic/storage package exposes three distinct HTTP contracts—Runtime, Document, and Asset—through a unified `createStorageHttpHandler` composable that routes requests to `/runtime`, `/documents`, and `/assets` base paths with shared authentication and optional authorization hooks.**

The THU-MAIC/OpenMAIC repository provides a modular storage system for managing runtime session data, structured documents, and binary assets. Understanding the HTTP contracts for the @openmaic/storage package is essential for integrating with the platform's storage layer, whether you're appending learner records, managing scene definitions, or uploading file blobs. All endpoints are served through a single handler factory that bundles runtime session management, document CRUD operations, and asset storage capabilities.

## Runtime Contract Endpoints

The **Runtime** contract manages session lifecycles, learner records, and data migration under the `/runtime` base path. This contract handles the core operational data generated during learning interactions.

### Session Lifecycle Management

Create and manage runtime sessions using the following endpoints defined in [`src/server/index.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/index.ts):

- `POST /runtime/sessions` – Creates a new runtime session. Implemented at lines 27‑71, this endpoint accepts a JSON payload containing `id`, `stageId`, `learnerKey`, `kind`, `status`, `createdAt`, and `updatedAt` fields.

- `GET /runtime/sessions/:sessionId` – Retrieves an existing session by ID. Located at lines 72‑99.

- `PATCH /runtime/sessions/:sessionId/status` – Updates the status of a specific session. Defined alongside the fetch and delete handlers at lines 72‑99.

- `DELETE /runtime/sessions/:sessionId` – Permanently removes a session and its associated data. Also at lines 72‑99.

### Record Operations

Append and retrieve interaction records within a session:

- `POST /runtime/sessions/:sessionId/records` – Appends a new record to the specified session. Implemented at lines 115‑126, this accepts payloads with `id`, `sessionId`, `seq`, and `payload` properties.

- `GET /runtime/sessions/:sessionId/records[?sceneId=...]` – Lists all records for a session, optionally filtered by `sceneId`. Located at lines 115‑126.

### Learner Management

Manage learner identities and session aggregation:

- `GET /runtime/stages/:stageId/learners/:learnerKey/sessions` – Retrieves all sessions associated with a specific learner within a stage.

- `POST /runtime/learners/merge` – Merges two learner keys, migrating all data from one identity to another. Implemented at lines 138‑142.

- `DELETE /runtime/:stageId/learners/:learnerKey` – Deletes all runtime data associated with a specific learner key.

### Administrative Operations

System-wide cleanup endpoints restricted to admin roles:

- `DELETE /runtime` – Wipes **all** runtime data across the system. Defined at lines 59‑64.

- `DELETE /runtime/stages/:stageId` – Removes all runtime data for a specific stage. Also at lines 59‑64.

## Document Contract Endpoints

The **Document** contract provides CRUD operations for scene and stage definitions under the `/documents` base path. Unlike the runtime contract, document routes are delegated to `createDocumentHttpHandler` in [`src/server/document.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/document.ts).

Typical endpoints include:

- `GET /documents/:stageId/:sceneId` – Retrieves a specific scene document.
- `PUT /documents/:stageId/:sceneId` – Upserts a scene document with new data.
- `DELETE /documents/:stageId/:sceneId` – Removes a scene document.
- `GET /documents/:stageId` – Lists all scenes belonging to a specific stage.

## Asset Contract Endpoints

The **Asset** contract handles binary storage, file uploads, and metadata retrieval under the `/assets` base path. These routes are managed by `createAssetHttpHandler` in [`src/server/asset.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/asset.ts).

Key endpoints include:

- `GET /assets/:assetId` – Fetches asset metadata or binary content.
- `POST /assets/:assetId` – Uploads a new asset using multipart form data.
- `DELETE /assets/:assetId` – Removes an asset from storage.
- `GET /assets/:assetId/bytes` – Streams raw binary data for the asset.
- `GET /assets/:assetId/meta` – Retrieves asset metadata without fetching the binary content.

## Request Routing and Handler Composition

The `createStorageHttpHandler` function composes all three contracts into a single HTTP handler. According to the implementation in [`src/server/index.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/index.ts), the handler examines the request path and routes accordingly:

- Requests matching `/documents` are forwarded to the document contract handler.
- Requests matching `/assets` are forwarded to the asset contract handler.
- All other requests fall back to the **runtime** contract handler.

This design allows you to mount the entire storage API at a single route prefix while maintaining logical separation between runtime data, documents, and assets.

## Authentication and Authorization

All three contracts share a common **authentication hook** specified via `options.authenticate`. This hook validates the incoming principal before any contract-specific logic executes.

Authorization is enforced through optional per-contract hooks:

- `authorizeDocuments` – Validates permissions for document operations.
- `authorizeAssets` – Controls access to asset upload and retrieval.
- `authorizeMerge` – Specifically protects the learner merge endpoint.
- `authorizeAdmin` – Restricts access to administrative deletion endpoints.

If an authorization hook is not provided, the corresponding operations proceed without additional permission checks beyond authentication.

## Error Handling and Status Codes

All routes utilize `RuntimeHttpError` (and `RuntimeAppendConflictError` for record conflicts) to map internal errors to standardized HTTP responses. The error mapping follows these conventions:

| Error Type | HTTP Status | Description |
|------------|-------------|-------------|
| `SESSION_ALREADY_EXISTS` | 409 | Returned when attempting to create a session with an existing ID. JSON shape: `{ error: { code: 'SESSION_ALREADY_EXISTS', message: '...' } }` |
| `SESSION_NOT_FOUND` | 404 | Returned when requesting a non-existent session. |
| `VALIDATION_FAILED` | 400 | Payload validation failures. |
| `PAYLOAD_TOO_LARGE` | 413 | Request body exceeds size limits. |
| Unauthorized (missing principal) | 401 | Authentication hook failed or no credentials provided. |
| Forbidden learner / admin | 403 | Authorization hook rejected the request. |
| Internal server error | 500 | Unexpected exceptions or infrastructure failures. |

## Source File Reference

The HTTP contracts are implemented across these key files in the THU-MAIC/OpenMAIC repository:

- [`src/server/index.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/index.ts) – Core runtime router implementing the `/runtime` endpoints and handler composition logic.
- [`src/server/document.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/document.ts) – Document contract implementation via `createDocumentHttpHandler`.
- [`src/server/asset.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/asset.ts) – Asset contract implementation via `createAssetHttpHandler`.
- [`src/runtime/types.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/runtime/types.ts) – TypeScript definitions for session and record payloads.
- [`src/asset/types.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/asset/types.ts) – Type definitions for asset metadata and upload structures.
- [`src/document/types.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/document/types.ts) – Type definitions for document schemas.

## Code Examples

Create a new runtime session:

```typescript
await fetch('https://api.example.com/runtime/sessions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    id: 'session-123',
    stageId: 'stage-01',
    learnerKey: 'learner-abc',
    kind: 'chat',
    status: 'active',
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  }),
});

```

Append a record to an existing session:

```typescript
await fetch('https://api.example.com/runtime/sessions/session-123/records', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    id: 'rec-001',
    sessionId: 'session-123',
    seq: 0,
    payload: { role: 'user', content: 'Hello' },
  }),
});

```

Merge two learner keys:

```typescript
await fetch('https://api.example.com/runtime/learners/merge', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    fromLearnerKey: 'temp-123',
    toLearnerKey: 'learner-abc',
  }),
});

```

Fetch a document definition:

```typescript
const doc = await fetch('https://api.example.com/documents/stage-01/scene-42')
  .then(r => r.json());

```

Upload an asset using multipart form data:

```typescript
const form = new FormData();
form.append('file', fileBlob, 'image.png');

await fetch('https://api.example.com/assets/asset-99', {
  method: 'POST',
  body: form,
});

```

## Summary

- The **@openmaic/storage** package consolidates three distinct storage domains—Runtime, Document, and Asset—into a single composable HTTP handler.
- **Runtime endpoints** (lines 27‑142 in [`src/server/index.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/index.ts)) manage session lifecycles, learner records, and administrative data wiping.
- **Document and Asset contracts** are delegated to separate handlers in [`src/server/document.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/document.ts) and [`src/server/asset.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/asset.ts), providing CRUD operations for scene definitions and binary storage.
- All contracts share a mandatory `authenticate` hook while supporting optional fine-grained authorization hooks for documents, assets, learner merging, and admin operations.
- Error handling uses `RuntimeHttpError` to map domain-specific errors to appropriate HTTP status codes (400, 401, 403, 404, 409, 413, 500).

## Frequently Asked Questions

### What is the difference between the Runtime and Document contracts in @openmaic/storage?

The **Runtime** contract handles dynamic, operational data such as learner session states and interaction records that change frequently during application use. The **Document** contract manages static configuration data like scene definitions and stage metadata that define the structure of learning content. Runtime data is typically ephemeral and high-volume, while documents serve as the structural schema for the application.

### How does the handler decide which contract to route a request to?

The `createStorageHttpHandler` implementation checks the request path in [`src/server/index.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/server/index.ts). If the path starts with `/documents`, it delegates to `createDocumentHttpHandler`. If it starts with `/assets`, it delegates to `createAssetHttpHandler`. Any path that does not match these prefixes automatically falls through to the **runtime** contract handler, making runtime the default catch-all for storage operations.

### Can I use different authentication strategies for each contract?

While all contracts share the base `authenticate` hook, you can implement differentiated access control through the optional authorization hooks. Pass `authorizeDocuments`, `authorizeAssets`, `authorizeMerge`, or `authorizeAdmin` to `createStorageHttpHandler` to enforce specific permission checks for each contract. If you need completely different authentication mechanisms, you would need to instantiate separate handlers rather than using the unified composable.

### What happens when I try to create a session that already exists?

Attempting to create a session with an ID that already exists returns HTTP status **409 Conflict** with a JSON error body containing `code: 'SESSION_ALREADY_EXISTS'`. This is handled by the `RuntimeAppendConflictError` class in the runtime contract implementation. To update an existing session's status, use the `PATCH /runtime/sessions/:sessionId/status` endpoint instead of the creation endpoint.