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:
content(TipTap JSON)content_htmlcontent_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
Use cases
Building a gallery
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()}`);
}
Checking for broken links
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=trueis present - URLs are extracted from the TipTap JSON directly (no external requests)
- Both features work with any
formatparameter value
Next steps
- Trade Plans — Trading business plans and operational frameworks
- Trade Write-ups — Post-trade analysis and reviews
- Strategies — Trading rules and approaches
- Report Cards — Daily/weekly/monthly performance reviews