単一PDF同期生成
POST /file/sync/single エンドポイントは、指定されたデザインとパラメータから単一のPDFファイルを同期的に生成します。
エンドポイント情報
- URL:
https://api.re-port-flow.com/v1/file/sync/single - メソッド:
POST - 認証:
appkeyヘッダーが必要 - タイムアウト: 120秒
- リクエストサイズ上限: 50MB(Base64エンコード後、実質約37MB相当)
使用例
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": "山田太郎",
"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);
// レスポンスヘッダーから情報取得
const requestId = response.headers['request-id'];
const fileUrl = response.headers['file-url'];
// X-File-Mapping は URL エンコード済みの JSON 配列。
// decodeURIComponent → JSON.parse の順で復号する。
const fileMapping = JSON.parse(decodeURIComponent(response.headers['x-file-mapping']));
console.log('PDF生成成功:', { requestId, fileUrl, fileMapping });
return { data: response.data, requestId, fileUrl, fileMapping };
} catch (error) {
console.error('PDF生成エラー:', 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: '山田太郎',
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)
# レスポンスヘッダーから情報取得
request_id = response.headers.get('request-id')
file_url = response.headers.get('file-url')
# X-File-Mapping は URL エンコード済みの JSON 配列。unquote → json.loads の順で復号する。
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': '山田太郎',
'invoiceNumber': 'INV-2024-001',
'amount': 10000
}
})
リクエストパラメータ
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
designId | string (UUID) | ✓ | デザインID |
version | integer | ✓ | バージョン番号 |
content.fileName | string | ✓ | ファイル名(/ \\ : * ? " < > | および制御文字以外は使用可能) |
content.shareType | string | - | 共有タイプ(リクエストは数値コード)。"01"=ワークスペース内共有(デフォルト) / "02"=招待者共有 / "03"=公開URL共有。レスポンスの share.shareType は workspace / invited / public の人間可読な名前で返ります |
content.passcodeEnabled | boolean | - | パスコード保護(デフォルト: false) |
content.passthrough | object | - | レスポンスの X-File-Mapping に透過する任意のメタデータ(例: { "pageId": "abc123" }) |
content.params | object | ✓ | テンプレートに埋め込むパラメータ(デザインパラメータ取得APIで構造を確認可能) |
レスポンス
成功時 (200 OK)
レスポンスボディ: PDFファイル(バイナリ)
レスポンスヘッダー:
| ヘッダー | 説明 | 例 |
|---|---|---|
Content-Type | コンテンツタイプ | application/pdf |
Content-Length | ファイルサイズ(バイト) | 102400 |
Content-Disposition | ファイル名 | attachment; filename="invoice.pdf" |
File-URL | ZIPダウンロードURL | https://api.re-port-flow.com/v1/file/download/{requestId} |
Request-Id | リクエストID | 550e8400-e29b-41d4-a716-446655440000 |
X-File-Mapping | ファイル情報と共有設定(JSON 配列)。URL エンコード済みで返るため、クライアントは decodeURIComponent してから JSON.parse すること | 下記参照 |
X-File-Mapping の構造:
[
{
"fileId": "7f3d1a2b-4c5e-6f7a-8b9c-0d1e2f3a4b5c",
"fileName": "invoice.pdf",
"passthrough": { "pageId": "abc123" },
"share": {
"shareType": "workspace",
"url": "https://app.re-port-flow.com/file/{requestId}/{fileId}",
"passcodeEnabled": false
}
}
]
passthrough フィールドはリクエスト時に content.passthrough を指定した場合のみ含まれます。
passthrough の使い所
ReportFlow はセキュリティとペイロードサイズの観点から、レスポンスや
Webhook 通知に params(生成データ)を返しません。
受信側で「どの業務レコードに対する PDF か」を識別したい場合は、
リクエスト時に passthrough に自社DBのIDなどを入れてください。
レスポンスや Webhook でそのまま返却されます。
{
"fileName": "invoice.pdf",
"passthrough": { "invoiceId": "INV-001", "tenantId": "acme" },
"params": { "customerName": "山田太郎", "amount": 10000 }
}
Webhook 受信時に passthrough の値がそのまま返るため、invoiceId
から DB を引いて該当レコードを更新する、といった処理が組めます。
params(顧客名や金額)は外部に送信されません。
エラー時
412 Precondition Failed
{
"statusCode": 412,
"message": "認証方式ヘッダーが存在しません"
}
原因: appkey ヘッダーが欠落
400 Bad Request
{
"statusCode": 400,
"message": [
"designId must be a UUID",
"ファイル名に使用できない文字が含まれています(/ \\ : * ? \" < > | および制御文字は使用不可)"
],
"error": "Bad Request"
}
原因: リクエストパラメ ータが不正
401 Unauthorized
{
"statusCode": 401,
"message": "認証情報が不正です",
"error": "Unauthorized"
}
原因: 認証エラー
対処法:
appkeyヘッダーの値が正しいことを確認
500 Internal Server Error
{
"statusCode": 500,
"message": "Internal server error",
"error": "Internal Server Error"
}
ベストプラクティス
1. タイムアウト処理
同期APIは120秒でタイムアウトします。大きなPDFや複雑なデザインの場合は、非同期APIの使用を検討してください。
const response = await axios.post(url, data, {
timeout: 120000 // 120秒
});
2. リトライロジック
一時的なサーバーエラー(500系)の場合は、指数バックオフでリトライを実装します。
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. ファイル名のサニタイゼーション
function sanitizeFileName(fileName) {
// / \ : * ? " < > | および制御文字を除去
return fileName.replace(/[\/\\:*?"<>|\x00-\x1F]/g, '_');
}