Multiple PDF Sync Generation (ZIP)
The POST /file/sync/multiple endpoint generates multiple PDF files synchronously from the specified template and multiple parameters, and returns them as a ZIP file.
Endpoint Information
- URL:
https://api.re-port-flow.com/v1/file/sync/multiple - Method:
POST - Authentication:
appkeyheader required - Timeout: 60 seconds (generation time depends on file count)
- Request Size Limit: 50MB (Base64-encoded; ~37MB raw equivalent)
Usage Examples
cURL
curl -X POST https://api.re-port-flow.com/v1/file/sync/multiple \
-H "appkey: your-application-key" \
-H "Content-Type: application/json" \
-d '{
"designId": "0eUDdgAjNXrrItA2",
"version": 1,
"contents": [
{
"fileName": "invoice_001.pdf",
"shareType": "01",
"passcodeEnabled": false,
"params": {
"customerName": "John Doe",
"invoiceNumber": "INV-001"
}
},
{
"fileName": "invoice_002.pdf",
"shareType": "02",
"passcodeEnabled": false,
"params": {
"customerName": "Jane Smith",
"invoiceNumber": "INV-002"
}
}
]
}' \
--output invoices.zip
JavaScript
const axios = require('axios');
const fs = require('fs');
async function generateMultiplePDFs(designId, contents) {
const response = await axios.post(
`https://api.re-port-flow.com/v1/file/sync/multiple`,
{
designId,
version: 1,
contents
},
{
headers: {
'appkey': process.env.APP_KEY
},
responseType: 'arraybuffer'
}
);
// Get file mapping from X-File-Mapping header
// X-File-Mapping is URL-encoded JSON; decode with decodeURIComponent → JSON.parse.
const fileMapping = JSON.parse(decodeURIComponent(response.headers['x-file-mapping']));
console.log('Generated files:', fileMapping);
return response.data; // ZIP binary
}
// Usage example
const contents = [
{
fileName: 'invoice_001.pdf',
params: { customerName: 'John Doe', invoiceNumber: 'INV-001' }
},
{
fileName: 'invoice_002.pdf',
params: { customerName: 'Jane Smith', invoiceNumber: 'INV-002' }
}
];
const zipData = await generateMultiplePDFs('0eUDdgAjNXrrItA2', contents);
fs.writeFileSync('invoices.zip', zipData);
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
designId | string | ✓ | Template ID (16-character alphanumeric, e.g. 0eUDdgAjNXrrItA2) |
version | integer | ✓ | Version number |
webhookUrl | string | - | Webhook destination for this request (https, up to 2048 characters). Must match the URL of an enabled webhook endpoint registered in the workspace, otherwise 400 WEBHOOK_URL_NOT_ALLOWED. When set, only that endpoint receives this request's events (details) |
contents | array | ✓ | Array of ContentDto (1 to 100 items) |
contents[].fileName | string | ✓ | File name (anything except / \ : * ? " < > | and control characters is allowed; must be unique within the array, case-insensitive) |
contents[].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. |
contents[].passcodeEnabled | boolean | - | Enable passcode protection (default: false). |
contents[].passthrough | object | - | Per-file string KV echoed back in the response X-File-Mapping[].passthrough. Top-level strings/numbers are also stored as report-search metadata. |
contents[].params | object | ✓ | Parameters (check structure via Template Parameters API) |
Response
Success (200 OK)
Response Body: ZIP file (binary)
Response Headers:
| Header | Description | Example |
|---|---|---|
Content-Type | Content type | application/zip |
Content-Length | File size in bytes | 307200 |
Content-Disposition | File name: <template name>_<requestId>.zip, URL-encoded | attachment; filename="Invoice_kZ3mP9qL2xV7nR4t.zip" (for a template named "Invoice") |
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}/templates/{designId}/outputs?requestId={requestId} |
Request-Id | Request ID | kZ3mP9qL2xV7nR4t |
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 PDF 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.
Structure of X-File-Mapping:
[
{
"fileId": "a8Fh2LkQ9zT4wE6y",
"fileName": "invoice_001.pdf",
"passthrough": { "pageId": "abc123" },
"share": {
"shareType": "workspace",
"url": "https://re-port-flow.com/file/{requestId}/a8Fh2LkQ9zT4wE6y",
"passcodeEnabled": false
}
},
{
"fileId": "b3Gk7MnP1qR5sT9v",
"fileName": "invoice_002.pdf",
"share": {
"shareType": "invited",
"url": "https://re-port-flow.com/file/{requestId}/b3Gk7MnP1qR5sT9v",
"passcodeEnabled": false
}
}
]
The passthrough field is included only on entries where contents[].passthrough was set on the request.
passthroughReportFlow does not echo back params (the data used to render the
PDF) on responses or webhooks, both for payload size and to avoid
leaking business data.
If you need to know which business record each PDF corresponds to, put
your own DB id (or any opaque token) into passthrough on the request.
The exact value comes back unchanged on responses and webhooks.
{
"fileName": "invoice.pdf",
"passthrough": { "invoiceId": "INV-001", "tenantId": "acme" },
"params": { "customerName": "John Doe", "amount": 10000 }
}
When the webhook arrives, look up your DB by invoiceId to find the
record to update. params (the customer name, amount, etc.) is never
sent off-server.
Top-level string/number values in passthrough are also stored as
report-search metadata (both in the generated PDF's XMP metadata and in the
Re:port Flow app's report-search index). See
passthrough and report-search metadata
for which values are indexed, the limits, and why personal data doesn't belong there.
Getting identifiers as JSON (?response=json)
Add ?response=json to the URL and the identifiers are returned as JSON instead of the ZIP body. This is for tools that cannot read response headers (Request-Id / File-URL / X-File-Mapping), such as Coze.
curl -X POST "https://api.re-port-flow.com/v1/file/sync/multiple?response=json" \
-H "appkey: YOUR_APP_KEY" \
-H "Content-Type: application/json" \
-d @request.json
{
"requestId": "Xk3pQ9vB2mN7aR1t",
"fileUrl": "https://re-port-flow.com/{workspaceId}/templates/{designId}/outputs?requestId=Xk3pQ9vB2mN7aR1t",
"files": [
{
"fileId": "a8Fh2LkQ9zT4wE6y",
"fileName": "invoice-001",
"pageCount": 1,
"downloadPath": "/v1/file/download/Xk3pQ9vB2mN7aR1t/a8Fh2LkQ9zT4wE6y",
"share": { "shareType": "workspace" }
}
]
}
- Only the
?response=jsonquery switches the format. TheAccept: application/jsonheader does not (so existing integrations do not start receiving JSON unexpectedly). fileUrlis the output-history page, not the file itself. Fetch the PDF by GETting the host (https://api.re-port-flow.com) +downloadPathwithappkey(or a Bearer token) — see PDF download.- Generation and page consumption are the same as when the PDF itself is returned.
Errors
In addition to the same error responses as Single PDF Sync Generation — Errors, the following error may be returned:
400 Bad Request (duplicate fileName)
{
"statusCode": 400,
"code": "FILE_NAME_DUPLICATED",
"message": [
"fileName values in contents must be unique (case-insensitive). Give each file a unique name."
],
"error": "Bad Request",
"details": [
{
"field": "contents",
"code": "FILE_NAME_DUPLICATED",
"message": "fileName values in contents must be unique (case-insensitive). Give each file a unique name."
}
],
"docsUrl": "https://doc.re-port-flow.com/en/docs/guides/error-codes#file_name_duplicated"
}
Cause: two or more entries in contents share the same fileName (case-insensitive).
Use Cases
Bulk Monthly Invoice Generation
async function generateMonthlyInvoices(month) {
// Get invoice data for the month
const invoices = await getInvoicesByMonth(month);
// Create ContentDto array
const contents = invoices.map(invoice => ({
fileName: `invoice_${invoice.number}.pdf`,
params: invoice
}));
// Bulk generation
const zipData = await generateMultiplePDFs('invoice-template-id', contents);
// Save ZIP file
fs.writeFileSync(`invoices_${month}.zip`, zipData);
}
FAQ
How many PDFs can one request generate?
Up to 100 items per request (more returns 400 TOO_MANY_CONTENTS). The request must also stay
within the request size cap (50MB after Base64 encoding) and the rate limit (30
requests/min for sync endpoints). For large runs, consider
Multiple PDF Async Generation instead.
Should I use sync or async generation?
Use the sync endpoint when you need the ZIP back immediately. When the batch is large and may take more than 60 seconds (see Limitations), use Multiple PDF Async Generation.
Next Steps
- Async Multiple PDF Generation - To avoid timeout
- PDF Download - How to download generated ZIP files