Skip to main content

Webhook Notifications

You can receive automatic webhook notifications when PDF generation is complete. This eliminates the need for polling and enables real-time detection of completion, allowing you to automate workflows such as email sending.

Overview

Webhook notifications are automatically sent for all PDF generation endpoints:

  • POST /file/sync/single - Synchronous single file generation
  • POST /file/sync/multiple - Synchronous multiple file generation
  • POST /file/async/single - Asynchronous single file generation
  • POST /file/async/multiple - Asynchronous multiple file generation

Setting Up Webhook URL

To configure the Webhook URL, use the ReportFlow workspace settings screen:

  1. Open workspace settings
  2. Navigate to the "Developer" tab
  3. Enter the HTTPS URL to receive notifications in the "Webhook URL" field
  4. Click "Update"

Webhook Notification Payload

When PDF generation is complete, the following payload is POSTed to the configured Webhook URL.

Payload Format

{
"event": "file.completed",
"timestamp": "2026-02-15T10:30:45.123Z",
"workspaceId": "ws_abc123",
"designId": "design_123",
"version": 5,
"files": [
{
"fileId": "file_xyz",
"uuid": "uuid_456",
"fileName": "invoice.pdf",
"params": {
"companyName": "Company A",
"amount": 50000
}
}
]
}

Field Descriptions

FieldTypeDescription
eventstringFixed value: "file.completed"
timestampstringEvent occurrence time (ISO 8601 format)
workspaceIdstringWorkspace ID
designIdstringDesign ID
versionnumberVersion number
filesarrayArray of generated file information
files[].fileIdstringFile ID (unique identifier)
files[].uuidstringUUID for downloading
files[].fileNamestringFile name (with extension)
files[].paramsobjectParameters used during generation

Downloading PDFs

From the webhook notification payload, you can download PDFs using the following information:

  • workspaceId: From payload's workspaceId
  • designId: From payload's designId
  • uuid: From payload's files[].uuid

See File Download for details.

Implementation Examples

Node.js (Express)

const express = require('express');
const axios = require('axios');
const app = express();

app.use(express.json());

app.post('/webhooks/pdf-completed', async (req, res) => {
const payload = req.body;

// Event verification
if (payload.event !== 'file.completed') {
return res.status(400).json({ error: 'Unknown event' });
}

console.log(`PDF generation completed: ${payload.files.length} files`);

// Download each file
for (const file of payload.files) {
const downloadUrl = `https://${payload.workspaceId}.re-port-flow.com/v1/file/download/${file.uuid}/${file.fileId}`;

try {
// Download PDF
const pdfResponse = await axios.get(downloadUrl, {
headers: {
'AppKey': process.env.APP_KEY,
'SecretKey': process.env.SECRET_KEY
},
responseType: 'arraybuffer'
});

const pdfBuffer = Buffer.from(pdfResponse.data);

// Send email, etc.
await sendEmailWithAttachment({
to: extractEmailFromParams(file.params),
subject: `PDF File: ${file.fileName}`,
attachments: [{
filename: file.fileName,
content: pdfBuffer
}]
});

console.log(`Email sent: ${file.fileName}`);
} catch (error) {
console.error(`File download error: ${file.fileName}`, error.message);
}
}

// Return 200 OK
res.status(200).json({ received: true });
});

app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});

Python (Flask)

from flask import Flask, request, jsonify
import requests
import os

app = Flask(__name__)

@app.route('/webhooks/pdf-completed', methods=['POST'])
def webhook_handler():
payload = request.json

# Event verification
if payload.get('event') != 'file.completed':
return jsonify({'error': 'Unknown event'}), 400

workspace_id = payload['workspaceId']

print(f"PDF generation completed: {len(payload['files'])} files")

# Download each file
for file_info in payload['files']:
download_url = f"https://{workspace_id}.re-port-flow.com/v1/file/download/{file_info['uuid']}/{file_info['fileId']}"

try:
# Download PDF
pdf_response = requests.get(
download_url,
headers={
'AppKey': os.getenv('APP_KEY'),
'SecretKey': os.getenv('SECRET_KEY')
}
)
pdf_response.raise_for_status()

# Save to file
with open(file_info['fileName'], 'wb') as f:
f.write(pdf_response.content)

print(f"Download completed: {file_info['fileName']}")

except Exception as e:
print(f"Error: {file_info['fileName']}, {str(e)}")

# Return 200 OK
return jsonify({'received': True}), 200

if __name__ == '__main__':
app.run(port=3000)

Security Best Practices

1. Use HTTPS URLs

Webhook URLs must use HTTPS. HTTP URLs are rejected.

2. Implement Signature Verification

Currently, webhook notifications do not include payload signatures. To prevent spoofing, we recommend verifying the source using the following methods:

  • IP whitelist
  • Authentication with API Key or secret token
  • Custom signature scheme

3. Timeout Configuration

Webhook endpoints should respond within 5 seconds. For heavy processing (email sending, database writes, etc.), use asynchronous job queues.

4. Do Not Include Credentials in URL

Do not include authentication tokens or secrets in Webhook URL query parameters. If authentication is required, send it via Header or manage it separately.

Retry Behavior

ReportFlow automatically retries when your webhook endpoint returns the following status codes:

  • 200-299: Success (no retry)
  • 400-499: Client error (no retry)
  • 500-599: Server error (retry)

Retries are performed up to 3 times. Even if all retries fail, the PDF generation process itself is considered successful.

Troubleshooting

Notifications Not Received

Check the following:

  1. Is the Webhook URL configured?

    • Check in Workspace Settings > Developer tab
  2. Using HTTPS?

    • HTTP URLs are rejected
  3. Using a public URL?

    • localhost and private IP addresses are rejected
    • For testing, use services like webhook.site
  4. Does the endpoint return 200?

    • If returning error status codes (4xx/5xx), retries will occur
    • Check your endpoint logs

If notifications still don't arrive, please contact support.

Next Steps