Upload Document
Upload a document (PDF, PowerPoint, or Word file) and publish it as a LinkedIn document post.
Endpoint
POST https://api.indreelforge.com/api/upload_document
Authentication
Authorization: Apikey YOUR_API_KEY
Platform Support
Document posts are currently supported on LinkedIn only. The platform is fixed to linkedin — you do not pass a platform[] parameter for this endpoint.
Your LinkedIn account must be connected in the profile you specify via the user field. LinkedIn document posts are available on all paid plans.
Parameters
Required
| Parameter | Type | Description |
|---|---|---|
document | file | Document file to upload. Accepted formats: PDF, PPT, PPTX, DOC, DOCX. Max 100 MB. |
user | string | Profile name (case-sensitive) to post from. |
Optional — General
| Parameter | Type | Description |
|---|---|---|
title | string | Title of the document post. Defaults to the original filename if omitted. |
description | string | Body text / caption for the LinkedIn post. |
schedule | datetime | ISO 8601 timestamp to schedule the post (e.g. 2026-06-15T09:00:00Z). Must be a future time. Requires a paid plan with scheduling enabled. |
schedule_at | datetime | Alias for schedule. Either field is accepted. |
timezone | string | IANA timezone string for display purposes (e.g. America/New_York). |
request_id | string | Your own idempotency / tracking ID. Returned in the response. |
LinkedIn-Specific
| Parameter | Type | Description |
|---|---|---|
visibility | string | Post audience. PUBLIC (default) or CONNECTIONS. |
target_linkedin_page_id | string | Post to a LinkedIn Company Page instead of your personal profile. Pass the page URN or numeric ID. |
Idempotency
Pass an Idempotency-Key request header with a unique string to safely retry the same request without creating duplicate posts. If the key has already been processed, the original upload record is returned immediately.
Supported File Types
| Format | Extension(s) | Notes |
|---|---|---|
.pdf | Verified via %PDF magic bytes | |
| PowerPoint (legacy) | .ppt | Verified via OLE2 Compound File magic bytes |
| PowerPoint (modern) | .pptx | Verified via ZIP/OOXML magic bytes |
| Word (legacy) | .doc | Verified via OLE2 Compound File magic bytes |
| Word (modern) | .docx | Verified via ZIP/OOXML magic bytes |
All uploads are verified against their file magic bytes after multer writes the file to disk. Files whose content does not match the declared extension are rejected with INVALID_FILE_CONTENT.
This endpoint accepts multipart file uploads only. There is no remote URL support for document uploads — the file must be sent as a form field named document.
File Size Limit
LinkedIn enforces a 100 MB maximum for document uploads. Requests exceeding this limit are rejected with HTTP 413 before the file reaches LinkedIn.
Plan Gating
- Document uploads count against your monthly upload quota. Free plan users receive 10 uploads/month.
- Scheduling (
schedule/schedule_at) requires a paid plan (Basic or above). Free-plan requests with a schedule value are rejected with HTTP 403 andSCHEDULING_NOT_AVAILABLE.
Examples
cURL
curl \
-H 'Authorization: Apikey YOUR_API_KEY' \
-F 'document=@/path/to/presentation.pdf' \
-F 'user=my-brand' \
-F 'title=Q2 2026 Results' \
-F 'description=Our highlights from Q2 — swipe to read the full report.' \
-F 'visibility=PUBLIC' \
-X POST https://api.indreelforge.com/api/upload_document
Schedule for later
curl \
-H 'Authorization: Apikey YOUR_API_KEY' \
-F 'document=@/path/to/deck.pptx' \
-F 'user=my-brand' \
-F 'title=Product Roadmap' \
-F 'schedule=2026-06-15T09:00:00Z' \
-X POST https://api.indreelforge.com/api/upload_document
Post to a LinkedIn Company Page
curl \
-H 'Authorization: Apikey YOUR_API_KEY' \
-F 'document=@/path/to/report.pdf' \
-F 'user=my-brand' \
-F 'title=Annual Report 2025' \
-F 'target_linkedin_page_id=123456789' \
-X POST https://api.indreelforge.com/api/upload_document
JavaScript
const form = new FormData();
form.append('document', fs.createReadStream('/path/to/presentation.pdf'));
form.append('user', 'my-brand');
form.append('title', 'Q2 2026 Results');
form.append('description', 'Our highlights from Q2.');
form.append('visibility', 'PUBLIC');
const response = await fetch('https://api.indreelforge.com/api/upload_document', {
method: 'POST',
headers: { 'Authorization': 'Apikey YOUR_API_KEY' },
body: form,
});
const data = await response.json();
console.log(data);
Python
import requests
files = {'document': open('/path/to/presentation.pdf', 'rb')}
data = {
'user': 'my-brand',
'title': 'Q2 2026 Results',
'description': 'Our highlights from Q2.',
'visibility': 'PUBLIC',
}
headers = {'Authorization': 'Apikey YOUR_API_KEY'}
response = requests.post(
'https://api.indreelforge.com/api/upload_document',
files=files,
data=data,
headers=headers,
)
print(response.json())
Response
HTTP 201 — document queued for immediate posting:
{
"success": true,
"message": "Document upload queued successfully",
"request_id": "req_abc123",
"data": {
"upload_id": "upl_abc123",
"platforms": {
"linkedin": { "status": "queued" }
},
"total_platforms": 1
}
}
HTTP 202 — document scheduled for future posting:
{
"success": true,
"message": "Document upload scheduled",
"request_id": "req_abc123",
"data": {
"upload_id": "upl_abc123",
"platforms": {
"linkedin": { "status": "queued" }
},
"total_platforms": 1
}
}
After the job runs, poll GET /api/upload/{id}/status to check the final publish status and retrieve the LinkedIn post URL.
Error Responses
| Status | Error Code | Description |
|---|---|---|
| 400 | INVALID_FILE_CONTENT | File magic bytes do not match the declared extension, or a non-document file was submitted. |
| 400 | PLATFORM_NOT_CONNECTED | LinkedIn is not connected for the specified profile. |
| 400 | INVALID_SCHEDULE | schedule value could not be parsed as an ISO 8601 timestamp. |
| 400 | SCHEDULE_IN_PAST | schedule resolves to a time in the past. |
| 401 | — | Missing or invalid API key. |
| 403 | UPLOAD_LIMIT_REACHED | Monthly upload quota exhausted. Upgrade your plan or wait for reset. |
| 403 | SCHEDULING_NOT_AVAILABLE | Scheduling requires a paid plan. |
| 404 | PROFILE_NOT_FOUND | No profile matching the user field was found for your account. |
| 413 | FILE_TOO_LARGE_FOR_PLATFORM | Document exceeds LinkedIn's 100 MB limit. |
| 429 | PLATFORM_RATE_LIMIT | LinkedIn's daily posting limit has been reached (150 posts/day). |
| 500 | — | Internal server error. |
| 502 | B2_UPLOAD_FAILED | File was received but could not be stored in object storage. Retry the request. |
| 503 | STORAGE_NOT_CONFIGURED | Object storage is not configured on the server. |