HTTP Contracts for the @openmaic/storage Package: Complete API Reference
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:
-
POST /runtime/sessions– Creates a new runtime session. Implemented at lines 27‑71, this endpoint accepts a JSON payload containingid,stageId,learnerKey,kind,status,createdAt, andupdatedAtfields. -
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 withid,sessionId,seq, andpayloadproperties. -
GET /runtime/sessions/:sessionId/records[?sceneId=...]– Lists all records for a session, optionally filtered bysceneId. 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.
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.
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, the handler examines the request path and routes accordingly:
- Requests matching
/documentsare forwarded to the document contract handler. - Requests matching
/assetsare 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– Core runtime router implementing the/runtimeendpoints and handler composition logic.src/server/document.ts– Document contract implementation viacreateDocumentHttpHandler.src/server/asset.ts– Asset contract implementation viacreateAssetHttpHandler.src/runtime/types.ts– TypeScript definitions for session and record payloads.src/asset/types.ts– Type definitions for asset metadata and upload structures.src/document/types.ts– Type definitions for document schemas.
Code Examples
Create a new runtime session:
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:
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:
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:
const doc = await fetch('https://api.example.com/documents/stage-01/scene-42')
.then(r => r.json());
Upload an asset using multipart form data:
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) manage session lifecycles, learner records, and administrative data wiping. - Document and Asset contracts are delegated to separate handlers in
src/server/document.tsandsrc/server/asset.ts, providing CRUD operations for scene definitions and binary storage. - All contracts share a mandatory
authenticatehook while supporting optional fine-grained authorization hooks for documents, assets, learner merging, and admin operations. - Error handling uses
RuntimeHttpErrorto 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. 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →