How to Use the Open Notebook REST API for Programmatic Notebook and Source Management
Open Notebook exposes a FastAPI-based HTTP interface that enables external programs to create, read, update, and delete notebooks and sources asynchronously, with dedicated endpoints for file uploads, graph-based linking, and cascade deletion.
The Open Notebook project provides a privacy-first research assistant with a comprehensive REST API for programmatic notebook and source management. This FastAPI-driven interface, available in the lfnovo/open-notebook repository, enables external applications to automate research workflows, manage knowledge graphs, and handle long-running document processing jobs without blocking client requests. All endpoints are asynchronous and delegate persistence to SurrealDB through a centralized repository pattern.
API Architecture and Core Components
The REST API follows a layered architecture that separates HTTP handling from domain logic and data persistence.
HTTP Layer and Routing
The FastAPI application organizes endpoints into dedicated router modules. In api/routers/notebooks.py, the application implements CRUD operations for notebooks, optional filtering and sorting for list views, and graph-based source linking. The api/routers/sources.py module handles multipart file uploads, async processing coordination, status polling, and file downloads.
Both routers delegate business logic to domain models and use Pydantic schemas defined in api/models.py for request validation and response serialization.
Domain Models and Persistence
Domain logic resides in open_notebook/domain/notebook.py and open_notebook/domain/source.py. These modules implement business rules for persistence, reference handling, and cascade operations. The open_notebook/database/repository.py file provides a thin wrapper around SurrealDB through functions like repo_query and ensure_record_id, which build safe, parameterized queries to prevent injection attacks.
Asynchronous Processing Pipeline
Long-running operations such as text extraction, transformation, and vector embedding are handled through the api/command_service.py module. When async_processing is enabled, the API calls CommandService.submit_command_job to queue a command in the Surreal Commands queue rather than blocking the request. For synchronous processing, the API uses execute_command_sync within a thread pool.
Managing Notebooks via the REST API
Notebooks serve as the primary organizational units, maintaining references to sources through graph edges rather than direct ownership.
Creating and Listing Notebooks
To create a notebook, POST to the /notebooks endpoint with a JSON payload containing name and optional description. The endpoint returns a NotebookResponse object with the generated id.
Listing supports query parameters for filtering and sorting. The endpoint validates the order_by parameter against an allow-list in api/routers/notebooks.py before interpolating it into SurrealQL queries, preventing injection attacks. List responses include computed fields like source_count and note_count derived from graph edge counts.
Linking Sources to Notebooks
Sources can belong to zero or many notebooks. The dedicated endpoint POST /notebooks/{notebook_id}/sources/{source_id} creates a graph edge linking an existing source to a notebook without duplicating the source record. This maintains the "resource-first" design where sources exist independently and are referenced by multiple notebooks.
Cascade Deletion
The DELETE endpoint /notebooks/{notebook_id} accepts a delete_exclusive_sources boolean parameter. When true, the cascade logic in open_notebook/domain/notebook.py removes sources that are exclusively referenced by the deleted notebook while unlinking shared sources. The response includes counts of deleted notes, exclusive sources removed, and sources merely unlinked.
Managing Sources via the REST API
Sources represent research materials and support multiple content types including text, URLs, and uploaded files.
Text Sources and Synchronous Processing
For text-only sources, POST to /sources with "type": "text" and the content in the payload. Omitting async_processing or setting it to false triggers synchronous processing via execute_command_sync. The response immediately includes the extracted full_text, embedding status, and a null command_id.
File Uploads and Asynchronous Processing
For file uploads, use multipart/form-data with type=upload and include the file field. Setting async_processing=true queues the job via CommandService.submit_command_job and returns immediately with:
command_id: The background job identifierstatus:"new"(job queued)processing_info: Object indicating async queue status
The file is stored under UPLOADS_FOLDER with a unique filename generated by generate_unique_filename to prevent collisions.
Polling for Processing Status
Async sources can be polled via GET /sources/{source_id}/status or GET /sources/{source_id}. The status endpoint returns the current state (queued, running, completed, or failed) and a human-readable message. Upon completion, the full source record includes the extracted text and embedded: true.
Downloading Original Files
The endpoint GET /sources/{source_id}/download returns the original uploaded file as a FileResponse. The implementation in api/routers/sources.py uses _resolve_source_file to validate paths through generate_unique_filename, ensuring resolved paths remain within UPLOADS_FOLDER to prevent directory traversal attacks.
Security and Data Integrity
The API implements several safety measures at the repository and router layers. SurrealQL injection protection validates sort parameters against allow-lists before query construction. File path sanitization ensures uploads and downloads are restricted to the designated upload directory. The ensure_record_id helper in open_notebook/database/repository.py validates record identifiers before database operations.
Practical API Examples
The following curl commands demonstrate common operations against a local server running on port 5055.
Create a notebook:
curl -X POST http://localhost:5055/notebooks \
-H "Content-Type: application/json" \
-d '{
"name": "Research on AI Safety",
"description": "Collected papers and notes"
}'
List notebooks with filtering:
curl http://localhost:5055/notebooks?archived=false\&order_by=name\ asc
Link an existing source to a notebook:
curl -X POST http://localhost:5055/notebooks/nb456/sources/src123
Create a synchronous text source:
curl -X POST http://localhost:5055/sources \
-H "Content-Type: application/json" \
-d '{
"type": "text",
"content": "Open‑Notebook is a privacy‑first research assistant.",
"title": "About Open‑Notebook",
"notebooks": ["nb456"],
"embed": true
}'
Upload a file with async processing:
curl -X POST http://localhost:5055/sources \
-F type=upload \
-F title="Sample PDF" \
-F notebooks='["nb456"]' \
-F async_processing=true \
-F embed=true \
-F file=@/path/to/document.pdf
Check processing status:
curl http://localhost:5055/sources/<source_id>/status
Download original file:
curl -OJ http://localhost:5055/sources/<source_id>/download
Delete notebook with cascade:
curl -X DELETE "http://localhost:5055/notebooks/nb456?delete_exclusive_sources=true"
Summary
- Open Notebook provides a FastAPI-based REST API for full CRUD operations on notebooks and sources, accessible via
api/routers/notebooks.pyandapi/routers/sources.py. - The architecture separates HTTP handling from domain logic (
open_notebook/domain/) and SurrealDB persistence (open_notebook/database/repository.py). - Async processing via
CommandService.submit_command_jobprevents blocking during text extraction and embedding, with status polling available through dedicated endpoints. - Graph-based relationships allow sources to link to multiple notebooks without duplication, cascade deletion respects exclusive vs. shared references.
- Security measures include SurrealQL injection protection, path validation in
generate_unique_filename, and directory traversal prevention in_resolve_source_file.
Frequently Asked Questions
How does the API handle long-running document processing?
When async_processing is set to true in api/routers/sources.py, the endpoint writes a minimal source record and immediately queues a command via CommandService.submit_command_job. The client receives a command_id and initial status of "new". The background job handles text extraction, transformations, and vector embedding. Clients poll /sources/{source_id}/status to monitor progress through states (queued, running, completed, failed) without blocking the original request.
Can a single source belong to multiple notebooks simultaneously?
Yes. The data model in open_notebook/domain/source.py treats sources as independent resources. The POST /notebooks/{notebook_id}/sources/{source_id} endpoint creates a graph edge referencing the source without duplicating the record. This allows sources to be linked to zero or many notebooks. When deleting a notebook, the cascade logic in open_notebook/domain/notebook.py distinguishes between exclusive sources (deleted) and shared sources (unlinked).
What security measures protect against injection attacks and path traversal?
The API implements SurrealQL injection protection by validating order_by parameters against strict allow-lists before interpolation in api/routers/notebooks.py. For file operations, generate_unique_filename in the upload handler and _resolve_source_file in the download endpoint both resolve real paths and verify they remain within UPLOADS_FOLDER. The ensure_record_id helper in open_notebook/database/repository.py sanitizes record identifiers before database queries.
How do I retrieve the full text and embedding status of a processed source?
Query GET /sources/{source_id} to retrieve the complete source record. For async sources, once the command completes, the response includes full_text containing extracted content and embedded: true indicating successful vectorization. The /sources/{source_id}/status endpoint provides a lightweight alternative that returns only the processing state and message without the full document payload.
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 →