Content formats & media

Work with rich text content in JSON, HTML, or Markdown, and extract media URLs from your journal entries.

Multi-format content support

All endpoints with rich text editor content support three formats for maximum flexibility:

  • TipTap JSON (native format)
  • HTML (for web display)
  • Markdown (for external tools and MCP servers)

Supported endpoints:

  • Trade Plans (/api/v1/trade-plans)
  • Trade Write-ups (/api/v1/trade-writeups)
  • Strategies (/api/v1/strategies)
  • Report Cards (/api/v1/report-cards)
  • Playbooks (/api/v1/playbooks)

Reading content in different formats

Using the format parameter

Add ?format=json|html|markdown to any GET request:

# Get content as HTML
GET /api/v1/trade-plans/123?format=html

# Get content as Markdown
GET /api/v1/trade-writeups/456?format=markdown

# Get all three formats (default)
GET /api/v1/strategies/789

Response formats

Default (JSON format):

When format=json or omitted, you receive all three versions:

{
  "data": {
    "id": "123",
    "title": "My Trade Plan",
    "content": {
      "type": "doc",
      "content": [
        {
          "type": "paragraph",
          "content": [
            { "type": "text", "text": "This is a trade plan." }
          ]
        }
      ]
    },
    "content_html": "<p>This is a trade plan.</p>",
    "content_markdown": "This is a trade plan."
  }
}

Specific format:

When format=html or format=markdown, only that format is returned in the content field:

{
  "data": {
    "id": "123",
    "title": "My Trade Plan",
    "content": "<p>This is a trade plan.</p>"
  }
}

Writing content in different formats

When creating or updating entries, you can submit content in any format:

Option 1: TipTap JSON (native)

await fetch('https://app.mypropjournal.com/api/v1/trade-plans', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer mpj_your_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'My Plan',
    content: {
      type: 'doc',
      content: [
        {
          type: 'paragraph',
          content: [{ type: 'text', text: 'Plan details here' }]
        }
      ]
    }
  })
});

Option 2: HTML

await fetch('https://app.mypropjournal.com/api/v1/trade-plans', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer mpj_your_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'My Plan',
    content_html: '<p>Plan details here</p><ul><li>Item 1</li><li>Item 2</li></ul>'
  })
});

Option 3: Markdown

await fetch('https://app.mypropjournal.com/api/v1/trade-plans', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer mpj_your_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'My Plan',
    content_markdown: '# Plan Details\n\n- Item 1\n- Item 2\n\nThis is a **bold** statement.'
  })
});

Priority: If multiple formats are provided, the API uses this priority:

  1. content (TipTap JSON)
  2. content_html
  3. content_markdown

Media extraction

Automatically extract all image and video URLs from your content without parsing.

Using include_media

Add ?include_media=true to any GET request:

GET /api/v1/trade-plans/123?include_media=true
GET /api/v1/trade-writeups?format=markdown&include_media=true

Response format

{
  "data": {
    "id": "123",
    "title": "My Trade Plan",
    "content": { ... },
    "media": {
      "images": [
        "https://example.com/chart1.jpg",
        "https://example.com/setup.png"
      ],
      "videos": [
        {
          "src": "https://www.youtube.com/embed/abc123",
          "originalUrl": "https://www.youtube.com/watch?v=abc123"
        }
      ],
      "all": [
        "https://example.com/chart1.jpg",
        "https://example.com/setup.png",
        "https://www.youtube.com/watch?v=abc123"
      ]
    }
  }
}

Media object structure

FieldTypeDescription
imagesstring[]Array of all image URLs in the content
videosobject[]Array of video objects with src (embed URL) and originalUrl
allstring[]Combined array of all media URLs (images + video original URLs)

Use cases

const response = await fetch(
  'https://app.mypropjournal.com/api/v1/trade-writeups/123?include_media=true',
  {
    headers: { 'Authorization': 'Bearer mpj_your_api_key' }
  }
);

const data = await response.json();
const images = data.data.media.images;

// Display in a gallery component
images.forEach(url => {
  const img = document.createElement('img');
  img.src = url;
  document.getElementById('gallery').appendChild(img);
});

MCP server integration

import requests

# Fetch trade plan as Markdown for LLM consumption
response = requests.get(
    'https://app.mypropjournal.com/api/v1/trade-plans/123?format=markdown',
    headers={'Authorization': f'Bearer {api_key}'}
)

trade_plan = response.json()['data']
markdown_content = trade_plan['content']

# Use markdown in LLM prompts
print(f"# {trade_plan['title']}\n\n{markdown_content}")

Downloading media assets

const response = await fetch(
  'https://app.mypropjournal.com/api/v1/trade-plans?include_media=true&limit=100',
  {
    headers: { 'Authorization': 'Bearer mpj_your_api_key' }
  }
);

const data = await response.json();

// Collect all media URLs
const allMedia = data.data.flatMap(plan => plan.media.all);

// Download each asset
for (const url of allMedia) {
  await downloadFile(url, `./media/${url.split('/').pop()}`);
}
import requests

response = requests.get(
    'https://app.mypropjournal.com/api/v1/trade-writeups?include_media=true',
    headers={'Authorization': f'Bearer {api_key}'}
)

for writeup in response.json()['data']:
    for url in writeup.get('media', {}).get('all', []):
        try:
            check = requests.head(url, timeout=5)
            if check.status_code >= 400:
                print(f"Broken link in {writeup['title']}: {url}")
        except:
            print(f"Unreachable in {writeup['title']}: {url}")

Performance notes

  • Multi-format conversion happens on-demand and has minimal overhead
  • Media extraction only runs when include_media=true is present
  • URLs are extracted from the TipTap JSON directly (no external requests)
  • Both features work with any format parameter value

Next steps