Public Document Reading
How to display documents on other sites for reading without formal acceptance
Guide: Public Document Reading (No Acceptance)
📖 Overview
This guide explains how to display documents (terms of use, policies, etc.) on other sites for reading, without the need for formal acceptance.
Important difference: - Reading: Just view the content (this guide) - Acceptance: Register formal acceptance with identity verification
---
🎯 When to Use
Use public reading when you want to: - Display terms of use on an "About" page of another site - Show privacy policy for reading - Embed legal documents on external sites - Allow consultation without mandatory acceptance
---
🚀 3 Implementation Methods
Option 1: Complete HTML Page (Best for direct links)
#### Available URLs:
`
https://your-platform.com/documents/read/<slug>/
`
Example:
`
https://legal-docs.com/documents/read/terms-of-use/
`
#### How to Use:
1. Configure public_acceptance_slug in the document
2. Enable public_acceptance_enabled = True
3. Share the link
#### Features: - ✅ Complete layout with header and footer - ✅ Button to accept (if needed) - ✅ Version and effective date information - ✅ Print button - ✅ Responsive
---
Option 2: Embedded Iframe (Best for embedding)
#### Available URLs:
`
https://your-platform.com/documents/read/embedded/<UUID>/
`
Example:
`html
<!-- On your external site -->
<div class="terms-container">
<iframe
id="terms-iframe"
src="https://legal-docs.com/documents/read/embedded/550e8400-e29b-41d4-a716-446655440000/"
width="100%"
height="600"
frameborder="0"
title="Terms of Use"
style="border: 1px solid #e5e7eb; border-radius: 8px;"
></iframe>
</div>
<script>
// Auto-adjust height
window.addEventListener('message', function(event) {
if (event.data.type === 'resize_iframe') {
document.getElementById('terms-iframe').style.height =
event.data.height + 'px';
}
});
</script>
`
#### Configuration (Optional):
If you want to control which domains can embed:
`python
from apps.agreements.models import Document, EmbeddedAcceptanceConfig
document = Document.objects.get(public_acceptance_slug="terms-of-use")
Create or update configuration
config, _ = EmbeddedAcceptanceConfig.objects.get_or_create( document=document, defaults={ 'enabled': True, 'allowed_domains': [ 'yoursite.com', 'www.yoursite.com', '*.yoursite.com', # Allows subdomains ], 'custom_css': ''' .title { color: #2563eb; } .content { font-size: 16px; } ''', 'hide_powered_by': True, } )`#### Features: - ✅ Minimalist layout (no header/footer) - ✅ Customizable via CSS - ✅ Allowed domain validation - ✅ Automatic height adjustment - ✅ No acceptance buttons
---
Option 3: REST API (JSON) (Best for total customization)
#### Available URLs:
`
GET https://your-platform.com/api/documents/<slug>/
`
Example Request:
`bash
curl https://legal-docs.com/api/documents/terms-of-use/
`
Response:
`json
{
"document_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Terms of Use",
"slug": "terms-of-use",
"description": "Terms and conditions of platform use",
"version": {
"version_number": "2.0",
"title": "Terms of Use - Version 2.0",
"content_html": "<h1>Terms of Use</h1><p>...</p>",
"content_plain": "Terms of Use\n\n...",
"effective_date": "2024-01-01",
"published_at": "2024-01-01T10:00:00Z"
},
"urls": {
"read": "https://...",
"accept": "https://...",
"embedded": "https://..."
}
}
`
#### Implementation on Your Site:
HTML:
`html
<div id="terms-content">
<div class="loading">Loading terms of use...</div>
</div>
`
JavaScript:
`javascript
async function loadTerms() {
try {
const response = await fetch(
'https://legal-docs.com/api/documents/terms-of-use/'
);
const data = await response.json();
// Render content
document.getElementById('terms-content').innerHTML = `
<div class="terms-header">
<h1>${data.version.title}</h1>
<p class="meta">
Version ${data.version.version_number} •
Effective: ${new Date(data.version.effective_date).toLocaleDateString('en-US')}
</p>
</div>
<div class="terms-body">
${data.version.content_html}
</div>
<div class="terms-footer">
<a href="${data.urls.accept}" class="btn-accept">
Accept Terms of Use
</a>
</div>
`;
} catch (error) {
console.error('Error loading terms:', error);
document.getElementById('terms-content').innerHTML =
'<p class="error">Error loading terms of use.</p>';
}
}
// Load when page loads
loadTerms();
`
React/Next.js:
`jsx
'use client';
import { useEffect, useState } from 'react';
export default function TermsOfService() { const [terms, setTerms] = useState(null); const [loading, setLoading] = useState(true);
useEffect(() => { fetch('https://legal-docs.com/api/documents/terms-of-use/') .then(res => res.json()) .then(data => { setTerms(data); setLoading(false); }) .catch(err => { console.error(err); setLoading(false); }); }, []);
if (loading) return <div>Loading...</div>; if (!terms) return <div>Error loading terms</div>;
return (
<div className="terms-container">
<h1>{terms.version.title}</h1>
<div className="meta">
Version {terms.version.version_number} •
Effective: {new Date(terms.version.effective_date).toLocaleDateString('en-US')}
</div>
<div
className="content"
dangerouslySetInnerHTML={{ __html: terms.version.content_html }}
/>
<a href={terms.urls.accept} className="btn-accept">
Accept Terms
</a>
</div>
);
}
`
#### Features:
- ✅ Total design customization
- ✅ CORS enabled (Access-Control-Allow-Origin: *)
- ✅ HTTP cache (1 hour)
- ✅ HTML and plain text available
- ✅ Links for acceptance and embedded included
---
📊 Option Comparison
| Aspect | HTML Page | Iframe | REST API | |--------|-----------|--------|----------| | Implementation | ⭐⭐⭐ Very easy | ⭐⭐ Easy | ⭐ Requires code | | Customization | ❌ Limited | ⚠️ CSS only | ✅ Total | | Maintenance | ✅ Zero | ✅ Zero | ⚠️ You control | | Updates | ✅ Automatic | ✅ Automatic | ⚠️ Depends on cache | | Performance | ⭐⭐ Good | ⭐⭐ Good | ⭐⭐⭐ Excellent | | SEO | ✅ Indexable | ❌ Not indexed | ✅ You control | | Mobile | ✅ Responsive | ✅ Responsive | ✅ You control |
---
🔧 Step-by-Step Configuration
1. In Django Admin:
`python
Via shell or admin
from apps.agreements.models import Document1. Configure document
document = Document.objects.get(name="Terms of Use") document.public_acceptance_slug = "terms-of-use" document.public_acceptance_enabled = True document.is_active = True document.save()2. Make sure you have an active version
version = document.get_active_version() print(f"Active version: {version.version}")3. Get URLs
print(f"Read URL: /documents/read/{document.public_acceptance_slug}/") print(f"Embedded URL: /documents/read/embedded/{document.document_key}/") print(f"API URL: /api/documents/{document.public_acceptance_slug}/")`2. (Optional) Configure Embedding with Restricted Domains:
`python
from apps.agreements.models import EmbeddedAcceptanceConfig
config = EmbeddedAcceptanceConfig.objects.create(
document=document,
enabled=True,
allowed_domains=[
'yoursite.com',
'www.yoursite.com',
'staging.yoursite.com',
],
custom_css='''
.title {
color: #1a56db;
font-family: 'Inter', sans-serif;
}
.content {
font-size: 15px;
line-height: 1.8;
}
''',
hide_powered_by=True,
)
`
3. Test URLs:
HTML Page:
`
http://localhost:8000/documents/read/terms-of-use/
`
Iframe:
`
http://localhost:8000/documents/read/embedded/550e8400-e29b-41d4-a716-446655440000/
`
API:
`bash
curl http://localhost:8000/api/documents/terms-of-use/
`
---
🎨 CSS Customization Examples
Modern Style (for Iframe):
`css
.title {
font-family: 'Inter', -apple-system, sans-serif;
font-size: 28px;
font-weight: 700;
color: #111827;
letter-spacing: -0.02em;
}
.content { font-size: 16px; line-height: 1.75; color: #374151; }
.content h2 {
color: #1f2937;
font-size: 20px;
font-weight: 600;
margin-top: 2em;
border-bottom: 2px solid #e5e7eb;
padding-bottom: 0.5em;
}
`
---
🔒 Security
CORS (API):
- API returnsAccess-Control-Allow-Origin: *
- Allows access from any domain
- No authentication required (public content)Iframe:
- CSP headers configured automatically - Allowed domain validation (if configured) - No cookies or sessions required---
💡 Use Cases
1. Institutional Site:
`html
<!-- On https://mysite.com/terms -->
<iframe src="https://legal-docs.com/documents/read/embedded/UUID/"
width="100%" height="800"></iframe>
`2. SPA/React App:
`jsx
import TermsAPI from './components/TermsAPI';function TermsPage() {
return <TermsAPI slug="terms-of-use" />;
}
`
3. Direct Link:
`html
<a href="https://legal-docs.com/documents/read/terms-of-use/"
target="_blank">
Read our Terms of Use
</a>
`---
🆚 Comparison: Reading vs Acceptance
| Feature | Reading | Acceptance | |---------|---------|------------| | Form | ❌ No | ✅ Yes | | Email Verification | ❌ No | ✅ Yes (Magic Link/OTP) | | Database Record | ❌ No | ✅ Yes | | Audit | ❌ No | ✅ Complete | | Certificate | ❌ No | ✅ Yes | | Use | Consultation | Legal Acceptance |
---
📱 Responsiveness
All 3 options are mobile-friendly: - HTML Page: Responsive with Tailwind CSS - Iframe: Auto-adjusts height - API: You control the design
---
⚡ Performance
Cache:
- API: HTTP cache of 1 hour (Cache-Control: public, max-age=3600)
- HTML/Iframe: Browser-controlled cache---
🐛 Troubleshooting
Iframe doesn't load:
1. Checkallowed_domains in configuration
2. Test with allowed_domains = [] (permissive mode)
3. Check browser console (CORS/CSP)API returns 404:
1. Checkpublic_acceptance_enabled = True
2. Check is_active = True
3. Check if active version existsContent doesn't update:
1. Clear browser cache 2. Check if version is published 3. Force refresh: Ctrl+Shift+R---
🎓 Next Steps
- [ ] Configure your documents - [ ] Choose method (HTML, Iframe or API) - [ ] Implement on your site - [ ] (Optional) Configure allowed domains - [ ] (Optional) Customize CSS - [ ] Test in production
---
💬 Questions?
If you need formal acceptance (with identity verification), use:
- /acceptance/<slug>/ - Public acceptance form
- /embedded/<uuid>/ - Acceptance via iframe (with form)
This guide is for reading only, without acceptance.