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

# Journals API

> Access and manage your Opennote journals programmatically.

# Journals API

The Journals API lets you programmatically access and manage journals from your [Opennote account](https://opennote.com). Create, edit, and organize your study materials through code.

**Note:** Your API key only has access to journals you've created. OAuth support for accessing user journals in third-party apps is coming soon.

## Available Operations

The Journals API provides the following endpoints:

### Core Operations

* **Create Journal** - Start a new blank journal
* **List Journals** - Get all your journals with pagination support
* **Get Content** - Retrieve full journal content in markdown format
* **Rename Journal** - Change a journal's title
* **Delete Journal** - Remove a journal (returns deleted data for recovery)

### Advanced Operations

* **Import from Markdown** - Create a journal from existing markdown content
* **Edit and Build Journal** - Modify journal structure with create/update/delete operations
* **Get Model Info** - Access the ProseMirror JSON representation

The full API reference can be found [here](/api-reference/create-journal).

## Quick Start

<CodeGroup>
  ```python Python theme={null}
  from opennote import OpennoteClient

  client = OpennoteClient(api_key="your_api_key")

  # Create a journal
  journal = client.journals.create(title="Study Notes")
  print(f"Created: {journal.journal_id}")

  # List all journals
  journals_list = client.journals.list()
  for j in journals_list.journals:
      print(f"{j.title} - {j.id}")

  # Get journal content
  content = client.journals.content(journal_id=journal.journal_id)
  print(content.content)

  # Rename journal
  renamed = client.journals.rename(
      journal_id=journal.journal_id,
      title="Updated Study Notes"
  )

  # Delete journal
  deleted = client.journals.editor.delete(journal_id=journal.journal_id)
  ```

  ```typescript TypeScript theme={null}
  import { OpennoteClient } from '@opennote-ed/sdk';

  const client = new OpennoteClient({ api_key: 'your_api_key' });

  // Create a journal
  const journal = await client.journals.create({ title: "Study Notes" });
  console.log(`Created: ${journal.journal_id}`);

  // List all journals
  const journalsList = await client.journals.list();
  journalsList.journals?.forEach(j => {
      console.log(`${j.title} - ${j.id}`);
  });

  // Get journal content
  const content = await client.journals.content({ 
      journal_id: journal.journal_id 
  });
  console.log(content.content);

  // Rename journal
  const renamed = await client.journals.rename({
      journal_id: journal.journal_id,
      title: "Updated Study Notes"
  });

  // Delete journal
  const deleted = await client.journals.editor.delete({ 
      journal_id: journal.journal_id 
  });
  ```

  ```bash cURL theme={null}
  # Create journal
  curl -X PUT "https://api.opennote.com/v1/journals/editor/create" \
    -H "Authorization: Bearer your_api_key" \
    -H "Content-Type: application/json" \
    -d '{"title": "Study Notes"}'

  # List journals
  curl -X GET "https://api.opennote.com/v1/journals/list" \
    -H "Authorization: Bearer your_api_key"

  # Get journal content
  curl -X GET "https://api.opennote.com/v1/journals/content/{journal_id}" \
    -H "Authorization: Bearer your_api_key"

  # Rename journal
  curl -X PATCH "https://api.opennote.com/v1/journals/editor/rename" \
    -H "Authorization: Bearer your_api_key" \
    -H "Content-Type: application/json" \
    -d '{"journal_id": "...", "title": "Updated Study Notes"}'

  # Delete journal
  curl -X DELETE "https://api.opennote.com/v1/journals/editor/delete/{journal_id}" \
    -H "Authorization: Bearer your_api_key"
  ```
</CodeGroup>

## Editing Journals

The edit endpoint allows you to modify journal content by applying operations in sequence. Each operation can create, update, or delete blocks within the journal.

### Edit Operations

Use the SDK's utility functions to build edit operations:

<CodeGroup>
  ```python Python theme={null}
  from opennote import OpennoteClient
  from opennote.types.block_types import HeadingBlock, ParagraphBlock, Position
  from opennote.util.edit_operations import create_block, delete_block, update_block

  client = OpennoteClient(api_key="your_api_key")

  # Get journal structure
  journal_info = client.journals.editor.model_info(journal_id="your_journal_id")
  last_block = journal_info.model.content[-1]

  # Apply edit operations
  result = client.journals.editor.edit(
      journal_id="your_journal_id",
      operations=[
          # Add a heading after the last block
          create_block(
              # You can use Position.BEFORE or Position.AFTER to specify insertion order
              position=Position.AFTER,
              reference_id=last_block.attrs.id,
              block=HeadingBlock(level=2, content="New Section")
          ),
          
          # Update an existing block by replacing it with the new `block`
          update_block(
              node_id=last_block.attrs.id,
              block=ParagraphBlock(content="Updated paragraph text")
          ),
          
          # Delete a block
          delete_block(node_id="block_id_to_delete")
      ],
      sync_realtime_state=True  # Sync changes to all connected users
  )
  ```

  ```typescript TypeScript theme={null}
  import { OpennoteClient } from '@opennote-ed/sdk';
  import { HeadingBlock, ParagraphBlock, Position } from '@opennote-ed/sdk/block_types';
  import { createBlock, deleteBlock, updateBlock } from '@opennote-ed/sdk/util/edit_operations';

  const client = new OpennoteClient({ api_key: 'your_api_key' });

  // Get journal structure
  const journalInfo = await client.journals.editor.modelInfo({ 
      journal_id: 'your_journal_id' 
  });
  const lastBlock = journalInfo.model.content[journalInfo.model.content.length - 1];

  // Apply edit operations
  const result = await client.journals.editor.edit({
      journal_id: 'your_journal_id',
      operations: [
          // Add a heading after the last block
          createBlock(
              new HeadingBlock(2, "New Section"),
              lastBlock.attrs.id,
              // You can use Position.BEFORE or Position.AFTER to specify insertion order
              Position.AFTER
          ),
          
          // Update an existing block
          updateBlock(
              lastBlock.attrs.id,
              new ParagraphBlock("Updated paragraph text")
          ),
          
          // Delete a block
          deleteBlock("block_id_to_delete")
      ],
      sync_realtime_state: true  // Sync changes to all connected users
  });
  ```
</CodeGroup>

### Important Notes on Editing

* **Markdown Support**: Both `ParagraphBlock` and `HeadingBlock` accept markdown strings that are automatically converted to the proper document structure
* **Sync to Collaborators**: Setting `sync_realtime_state: true` applies changes immediately to all connected users, but **(warning) disables undo functionality for those changes**. This is default to `true` to make sure document changes aren't overwritten by conflict resolution.
* **Operation Order**: Operations are applied sequentially - earlier operations can affect later ones
* **Editing through SDK**: Editing operations are quite complex in the API. The SDKs provide utility functions to help you build edit operations in a very simple way, and we recommend you prioritize using them over direct API calls.
  * The full reference of available block types can be found in the [Python SDK Repo](https://github.com/opennote-dev/api-sdk-python/blob/main/opennote/types/block_types.py) or [TypeScript SDK Repo](https://github.com/opennote-dev/api-sdk-typescript/blob/main/src/block_types.ts).

## Markdown Content Imports

Convert existing markdown content into a new journal using the `import_from_markdown` endpoint. This can be used to translate LLM generated content into a journal that can be edited and shared:

<CodeGroup>
  ```python Python theme={null}
  markdown_content = """
  # Chapter 1: Introduction

  This is my study guide for the exam.

  ## Key Concepts
  - Concept 1
  - Concept 2
  - Concept 3
  """

  imported = client.journals.editor.import_from_markdown(
      markdown=markdown_content,
      title="Imported Study Guide"  # Optional, defaults to "Imported Journal"
  )
  print(f"Imported journal: {imported.journal_id}")
  ```

  ```typescript TypeScript theme={null}
  const markdownContent = `
  # Chapter 1: Introduction

  This is my study guide for the exam.

  ## Key Concepts
  - Concept 1
  - Concept 2
  - Concept 3
  `;

  const imported = await client.journals.editor.importFromMarkdown({
      markdown: markdownContent,
      title: "Imported Study Guide"  // Optional
  });
  console.log(`Imported journal: ${imported.journal_id}`);
  ```
</CodeGroup>

## Pagination

When listing journals, use pagination for large collections to improve performance:

<CodeGroup>
  ```python Python theme={null}
  # Get first page
  first_page = client.journals.list()

  # Get next page using token
  if first_page.next_page_token:
      second_page = client.journals.list(page_token=first_page.next_page_token)
  ```

  ```typescript TypeScript theme={null}
  // Get first page
  const firstPage = await client.journals.list();

  // Get next page using token
  if (firstPage.next_page_token) {
      const secondPage = await client.journals.list({ 
          page_token: firstPage.next_page_token 
      });
  }
  ```
</CodeGroup>

## Responses

Response formats can be viewed in the [API Reference](/api-reference/create-journal).

## Error Handling

The API returns standard HTTP status codes:

* **200**: Success
* **401**: Invalid or missing API key
* **422**: Invalid request parameters
* **429**: Rate limit exceeded (check Retry-After header)
* **500**: Server error

Example error response:

```json theme={null}
{
  "error": "Rate limit exceeded. Please wait 60 seconds before retrying."
}
```

When rate limited, the API returns a 429 status with a `Retry-After` header indicating when you can retry.

You can always view your rate limits in the [API Dashboard](https://opennote.com/api/teams).

## Support

Need help with the Journals API?

* **Technical Support**: [devtools@opennote.me](mailto:devtools@opennote.me)
* **API Reference**: [Full API Documentation](/api-reference/journals)
* **Dashboard**: [Manage API Keys and the Playground](https://opennote.com/api/teams)
* **SDK Documentation**: [Python & TypeScript SDKs](../sdks)
