Skip to main content

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: appkey header 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

FieldTypeRequiredDescription
designIdstring (UUID)Design ID
versionintegerVersion number
content.fileNamestringFile name (anything except / \\ : * ? " < > | and control characters is allowed)
content.shareTypestring-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.passcodeEnabledboolean-Enable passcode protection (default: false). When true, the response share.passcode returns a server-generated passcode exactly once.
content.passthroughobject-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.paramsobjectParameters embedded into the template (check structure with the Design Parameters API)

Response

Success (200 OK)

Response Body: PDF file (binary)

Response Headers:

HeaderDescriptionExample
Content-TypeContent typeapplication/pdf
Content-LengthFile size in bytes102400
Content-DispositionFile nameattachment; filename="invoice.pdf"
File-URLURL of the app screen (output history) where the generated result can be reviewed. This is not a direct file download URLhttps://re-port-flow.com/{workspaceId}/design/{designId}/outputs?requestId={requestId}
Request-IdRequest ID550e8400-e29b-41d4-a716-446655440000
X-File-MappingFile metadata and share info (JSON array). URL-encoded, so clients must call decodeURIComponent before JSON.parse.See below
File-URL is not a download URL

File-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 appkey header 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