Sync Single PDF Generation
The POST /file/sync/single endpoint generates a single PDF file synchronously from the specified design and parameters.
Endpoint Information
- URL:
https://api.re-port-flow.com/v1/file/sync/single - Method:
POST - Authentication:
appkeyheader required - Timeout: 120 seconds
- Request Size Limit: 50MB (Base64-encoded; ~37MB raw equivalent)
Usage Examples
cURL
curl -X POST https://api.re-port-flow.com/v1/file/sync/single \
-H "appkey: your-application-key" \
-H "Content-Type: application/json" \
-d '{
"designId": "550e8400-e29b-41d4-a716-446655440000",
"version": 1,
"content": {
"fileName": "invoice.pdf",
"shareType": "01",
"passcodeEnabled": false,
"params": {
"customerName": "John Doe",
"invoiceNumber": "INV-2024-001",
"amount": 10000
}
}
}' \
--output invoice.pdf
JavaScript (Node.js)
import axios from 'axios';
import fs from 'fs';
async function generatePDF(params) {
try {
const response = await axios.post(
'https://api.re-port-flow.com/v1/file/sync/single',
{
designId: params.designId,
version: params.version,
content: {
fileName: params.fileName,
shareType: params.shareType ?? '01',
passcodeEnabled: params.passcodeEnabled ?? false,
params: params.data
}
},
{
headers: {
'appkey': process.env.APP_KEY,
'Content-Type': 'application/json'
},
responseType: 'arraybuffer'
}
);
fs.writeFileSync(params.fileName, response.data);
// X-File-Mapping is URL-encoded JSON. Decode with decodeURIComponent → JSON.parse.
const requestId = response.headers['request-id'];
const fileUrl = response.headers['file-url'];
const fileMapping = JSON.parse(decodeURIComponent(response.headers['x-file-mapping']));
console.log('PDF generated:', { requestId, fileUrl, fileMapping });
return { data: response.data, requestId, fileUrl, fileMapping };
} catch (error) {
console.error('PDF generation error:', error.response?.data || error.message);
throw error;
}
}
generatePDF({
designId: '550e8400-e29b-41d4-a716-446655440000',
version: 1,
fileName: 'invoice.pdf',
shareType: '01',
passcodeEnabled: false,
data: {
customerName: 'John Doe',
invoiceNumber: 'INV-2024-001',
amount: 10000
}
});
Python
import requests
import json
from urllib.parse import unquote
def generate_pdf(params):
url = "https://api.re-port-flow.com/v1/file/sync/single"
headers = {
'appkey': params['app_key'],
'Content-Type': 'application/json'
}
data = {
'designId': params['design_id'],
'version': params['version'],
'content': {
'fileName': params['file_name'],
'shareType': params.get('share_type', '01'),
'passcodeEnabled': params.get('passcode_enabled', False),
'params': params['data']
}
}
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
with open(params['file_name'], 'wb') as f:
f.write(response.content)
# X-File-Mapping is URL-encoded JSON; decode with unquote → json.loads.
request_id = response.headers.get('request-id')
file_url = response.headers.get('file-url')
file_mapping = json.loads(unquote(response.headers.get('x-file-mapping', '[]')))
return {'request_id': request_id, 'file_url': file_url, 'file_mapping': file_mapping}
result = generate_pdf({
'app_key': 'your-application-key',
'design_id': '550e8400-e29b-41d4-a716-446655440000',
'version': 1,
'file_name': 'invoice.pdf',
'share_type': '01',
'passcode_enabled': False,
'data': {
'customerName': 'John Doe',
'invoiceNumber': 'INV-2024-001',
'amount': 10000
}
})
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
designId | string (UUID) | ✓ | Design ID |
version | integer | ✓ | Version number |
content.fileName | string | ✓ | File name (anything except / \\ : * ? " < > | and control characters is allowed) |
content.shareType | string | - | Share type (request side uses numeric codes). "01" = workspace share (default) / "02" = invited-only / "03" = public URL share. The response share.shareType returns the human-readable name (workspace / invited / public). |
content.passcodeEnabled | boolean | - | Enable passcode protection (default: false). When true, the response share.passcode returns a server-generated passcode exactly once. |
content.passthrough | object | - | Arbitrary string KV that is echoed back in the response X-File-Mapping[].passthrough. Useful for tracking with webhooks (e.g. { "orderId": "...", "userId": "..." }). Top-level strings/numbers are also stored as report-search metadata. |
content.params | object | ✓ | Parameters embedded into the template (check structure with the Design Parameters API) |
Response
Success (200 OK)
Response Body: PDF file (binary)
Response Headers:
| Header | Description | Example |
|---|---|---|
Content-Type | Content type | application/pdf |
Content-Length | File size in bytes | 102400 |
Content-Disposition | File name | attachment; filename="invoice.pdf" |
File-URL | URL of the app screen (output history) where the generated result can be reviewed. This is not a direct file download URL | https://re-port-flow.com/{workspaceId}/design/{designId}/outputs?requestId={requestId} |
Request-Id | Request ID | 550e8400-e29b-41d4-a716-446655440000 |
X-File-Mapping | File metadata and share info (JSON array). URL-encoded, so clients must call decodeURIComponent before JSON.parse. | See below |
File-URL is not a download URLFile-URL points to the app screen (output history) where the generated result can be reviewed, and opening it requires signing in to Re:port Flow. To fetch files programmatically, use the file download endpoints instead (requestId for a ZIP of the whole request, requestId + fileId for a single PDF).
This header was previously documented as a "ZIP download URL", which did not match what the implementation returns. The documentation has been corrected to match the implementation; the response behaviour itself is unchanged.
X-File-Mapping structure (after decoding):
[
{
"fileId": "7f3d1a2b-4c5e-6f7a-8b9c-0d1e2f3a4b5c",
"fileName": "invoice.pdf",
"passthrough": { "orderId": "ORD-2024-001" },
"share": {
"shareType": "workspace",
"url": "https://re-port-flow.com/file/{requestId}/{fileId}",
"passcodeEnabled": false
}
}
]
passthrough is included only when content.passthrough was specified in the request.
Errors
412 Precondition Failed
{
"statusCode": 412,
"message": "認証方式ヘッダーが存在しません"
}
Cause: appkey header is missing.
400 Bad Request
{
"statusCode": 400,
"message": [
"designId must be a UUID",
"ファイル名に使用できない文字が含まれています(/ \\ : * ? \" < > | および制御文字は使用不可)"
],
"error": "Bad Request"
}
Cause: Invalid request parameters.
401 Unauthorized
{
"statusCode": 401,
"message": "認証情報が不正です",
"error": "Unauthorized"
}
(The server returns the message in Japanese; it translates to "Invalid credentials".)
Cause: Authentication failure.
Solution:
- Verify the value of the
appkeyheader is correct.
500 Internal Server Error
{
"statusCode": 500,
"message": "Internal server error",
"error": "Internal Server Error"
}
Best Practices
1. Timeout Handling
The sync API times out after 120 seconds. For large PDFs or complex designs, consider using the async API.
const response = await axios.post(url, data, {
timeout: 120000 // 120 seconds
});
2. Retry Logic
For transient 5xx errors, retry with exponential backoff.
async function generatePDFWithRetry(params, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await generatePDF(params);
} catch (error) {
if (error.response?.status === 500 && i < maxRetries - 1) {
const delay = 1000 * Math.pow(2, i);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
3. File Name Sanitization
function sanitizeFileName(fileName) {
// Remove / \ : * ? " < > | and control characters
return fileName.replace(/[\/\\:*?"<>|\x00-\x1F]/g, '_');
}
Use Cases
Case 1: Instant Invoice Generation
Generate and download a PDF immediately when the user clicks the "Download Invoice" button.
app.get('/api/invoices/:id/download', async (req, res) => {
const invoice = await getInvoice(req.params.id);
const pdf = await generatePDF({
designId: 'invoice-template-id',
version: 1,
fileName: `invoice_${invoice.number}.pdf`,
data: invoice
});
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename="invoice_${invoice.number}.pdf"`);
res.send(pdf.data);
});
Case 2: Preview Generation
Generate a PDF in real time when the user clicks the preview button in the design editor.
async function showPreview(designId, params) {
const pdf = await generatePDF({
designId,
version: 1,
fileName: 'preview.pdf',
data: params
});
const blob = new Blob([pdf.data], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
window.open(url, '_blank');
}
FAQ
Should I use sync or async generation?
Use the sync endpoint when you need to return a result to a user action on the spot. If generation may take longer than 120 seconds, or you want to avoid the timeout entirely, use Single PDF Async Generation.
What happens if the request times out?
Sync endpoints time out after 120 seconds and return 504 Gateway Timeout. For large PDFs
or complex designs, switch to an async endpoint or simplify the design — see
Limitations.
Next Steps
- Async Single PDF — to avoid timeout
- Sync Multiple PDFs — generate multiple PDFs in one call
- Async Workflows — best practices for bulk generation