# Report Flow API Documentation > Developer documentation for Report Flow API Source: https://doc.re-port-flow.com Generated: 2026-05-30T09:43:07.007Z --- # docs/authentication/api-keys.md # APIキーの取得と管理 このページでは、Report Flow APIを使用するために必要なAPIキーの取得方法と管理方法を説明します。 ## APIキーの取得 ### 1. ワークスペースを作成 Report Flowの管理画面(`https://re-port-flow.com`)にログインし、新しいワークスペースを作成します。 ワークスペース作成時に、アプリケーションキーが自動的に生成されます。 ### 2. API連携情報を確認 ワークスペース詳細画面→「API連携」タブを開くと、以下の情報が表示されます: - **API URL**: `https://api.re-port-flow.com/`(コピーボタンあり) - **アプリケーションキー**: `ak_xxxxxxxxxxxxxxxx`(コピーボタンあり) 各項目の右側にあるコピーボタンをクリックすると、値がクリップボードにコピーされます。 :::info **1ワークスペースにつき1つのアプリケーションキー** 各ワークスペースには1つのアプリケーションキーが紐付いています。キーは定期的に再生成して更新できます。 ::: ## APIキーの管理 ### キーの確認 ワークスペース詳細画面の「API連携」タブで、現在のアプリケーションキー情報を確認できます: - **API URL**: `https://api.re-port-flow.com/`(コピー可能) - **アプリケーションキー**: `ak_xxxxxxxxxxxxxxxx`(コピー可能) - **最終ローテーション日**: `2024-02-14` ## セキュリティ推奨事項 ### 環境変数での管理 **推奨:** APIキーは環境変数で管理し、コードにハードコードしない ```bash # .env ファイル REPORT_FLOW_APP_KEY=ak_xxxxxxxxxxxxxxxx ``` ```javascript // 使用例 const appKey = process.env.REPORT_FLOW_APP_KEY; ``` ### .gitignoreへの追加 ```gitignore # APIキーを含むファイルをGit管理から除外 .env .env.local .env.production secrets.json ``` ### 定期的なローテーション セキュリティ向上のため、**3ヶ月ごと**にアプリケーションキーを再生成することを推奨します。 :::tip ワークスペース詳細画面の「API連携」セクションで、最終ローテーション日から90日以上経過している場合、警告が表示されます。 ::: **UIからの再生成手順:** 1. ワークスペース詳細画面→「API連携」タブを開く 2. 「アプリケーションキーを再生成」ボタンをクリック 3. 確認モーダルで「再生成」を実行 4. **表示された新しいキーをコピーし、アプリケーションに設定する** 5. 動作確認を実施 **APIからの再生成方法:** ```bash curl -X POST \ https://api.re-port-flow.com/v1/workspace/{workspaceId}/application-token/regenerate \ -H 'Authorization: Bearer {access_token}' \ -H 'Content-Type: application/json' ``` レスポンス例: ```json { "applicationKey": "ak_xxxxxxxxxxxxxxxx", "lastRotatedAt": "2024-02-14T10:30:00.000Z" } ``` :::warning アプリケーションキーを再生成すると、**古いキーは即座に無効**になります。アプリケーションのダウンタイムを避けるため、新しいキーを速やかに設定に反映してください。 ::: ## セキュリティ Report FlowのAPIキーは、業界標準のセキュリティ対策により安全に管理されています: - アプリケーションキーは暗号化されて保存されます - すべてのAPI操作は監査ログに記録されます - HTTPS通信のみをサポートし、平文での通信は受け付けません :::tip セキュリティを最大限に高めるため、定期的なキーローテーション(3ヶ月ごと)を推奨します。 ::: ### 本番環境のセキュリティ要件 HTTPS通信必須・TLSバージョン要件については[認証概要](./overview.md)を参照してください。 ### その他のセキュリティベストプラクティス #### APIキーの保管 - 環境変数で管理し、コードにハードコードしない - `.gitignore`に`.env`ファイルを追加 - シークレット管理サービス(AWS Secrets Manager等)の使用を推奨 #### アクセス制御 - 必要最小限のワークスペースにのみAPIキーを発行 - 不要になったAPIキーは即座に再生成 - 定期的なアクセスログの監査 詳細な制限事項については、[制限事項ページ](../limitations.md)を参照してください。 ## トラブルシューティング ### アプリケーションキーを再生成する場合 アプリケーションキーは管理画面の「API連携」タブから確認できます。確認できない場合は以下の手順で新しいキーを発行してください: 1. ワークスペース詳細画面→「API連携」タブを開く 2. 「アプリケーションキーを再生成」ボタンをクリック 3. 確認モーダルで再生成を実行 4. 表示された新しいキーをコピー 5. アプリケーションの設定を新しいキーで更新 ### キーが動作しない場合 以下を確認してください: - [ ] `appkey` ヘッダーが正しく設定されているか - [ ] アプリケーションキーが正しくコピーされているか - [ ] キーが有効化されているか(管理画面で確認) - [ ] ヘッダー名が `appkey`(小文字)になっているか ## 次のステップ - [認証方法の詳細](./overview) - [PDF生成ガイド](../guides/pdf-generation) --- # docs/authentication/oauth.md # OAuth 2.0 認証 Report Flow API は `appkey` ヘッダによる認証に加えて、OAuth 2.0 / OpenID Connect をサポートしています。サードパーティ統合(Make.com 等)や、独自アプリケーションからの認証フローに利用できます。 ## 利用シーンと選択指針 | 用途 | 推奨方式 | |------|---------| | 単一ワークスペースの自動化(cURL、社内バッチ) | `appkey` ヘッダ | | サーバー間連携(バックエンド → Report Flow) | OAuth 2.0 **Client Credentials** | | ユーザー個別の認可(Make.com、外部 SaaS、個人アプリ) | OAuth 2.0 **Authorization Code + PKCE** | ## エンドポイント > **重要**: OAuth フロー(Discovery / Authorization / Token / UserInfo)は **`re-port-flow.com/api/v1`** にホストされており、PDF 生成等の保護リソース API(`api.re-port-flow.com/v1/...`)とは **別ドメイン**です。発行された Access Token (`Bearer`) は保護リソース API 側で利用します。 OIDC Discovery ドキュメントから自動取得することを推奨します: ``` GET https://re-port-flow.com/api/v1/.well-known/openid-configuration ``` 主なエンドポイント: | 項目 | URL | |------|-----| | Issuer | `https://re-port-flow.com/api/v1` | | Authorization | `${issuer}/oauth/authorize` | | Token | `${issuer}/oauth/token` | | UserInfo | `${issuer}/oauth/userinfo` | | 保護リソース API(Bearer 利用先) | `https://api.re-port-flow.com/v1/...` | ## サポート仕様 | 項目 | 値 | |------|----| | `response_type` | `code` | | `grant_types` | `authorization_code`, `refresh_token`, `client_credentials` | | `code_challenge_method` | `S256`(PKCE 必須) | | `token_endpoint_auth_methods` | `none`, `client_secret_post`, `client_secret_basic` | | アクセストークン有効期限 | 3600 秒(1 時間) | ## スコープ | Scope | 説明 | |-------|------| | `openid` | ユーザーIDの確認(OIDC) | | `profile` | プロフィール情報の取得(本実装ではメールアドレスを含みます) | | `designs:read` | デザインの一覧・詳細を表示する(読み取りのみ) | | `designs:write` | デザインを作成・編集する | | `templates:read` | テンプレートの一覧・詳細を表示する(読み取りのみ) | | `templates:write` | テンプレートを作成・編集する | | `pdf:generate` | テンプレートから PDF を生成する | 複数スコープを指定する場合はスペース区切り(例: `pdf:generate designs:read`)。 ## クライアント種別 | 種別 | `is_public` | 認証方式 | 主な用途 | |------|------------|---------|---------| | Public Client | `true` | PKCE のみ(client_secret なし) | モバイル/SPA、Make.com 等の公開クライアント | | Confidential Client | `false` | client_secret 必須 | サーバー間連携、`client_credentials` グラント | `client_credentials` グラントは **Confidential Client 専用**です。Public Client では使用できません。 --- ## Authorization Code フロー(PKCE) ユーザーごとに認可を取得するフロー。Make.com 等の統合先や、ユーザーが自分のワークスペースに対して操作するアプリで利用します。 ### 1. Authorization リクエスト ``` GET https://re-port-flow.com/api/v1/oauth/authorize ?response_type=code &client_id=YOUR_CLIENT_ID &redirect_uri=https://your-app.example.com/callback &scope=openid%20profile%20pdf:generate &state=RANDOM_STATE &code_challenge=BASE64URL(SHA256(verifier)) &code_challenge_method=S256 ``` ユーザーがログインと同意を完了すると、`redirect_uri` に `code` と `state` が付与されてリダイレクトされます。 ### 2. Token リクエスト ```http POST https://re-port-flow.com/api/v1/oauth/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code=AUTHORIZATION_CODE &redirect_uri=https://your-app.example.com/callback &client_id=YOUR_CLIENT_ID &code_verifier=ORIGINAL_VERIFIER ``` レスポンス: ```json { "access_token": "eyJhbGc...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "rt_...", "scope": "openid profile pdf:generate" } ``` Confidential Client の場合は `client_secret` も同時に送信(`client_secret_post`)するか、Basic 認証ヘッダ(`client_secret_basic`)で送信します。 ### 3. Refresh Token ```http POST https://re-port-flow.com/api/v1/oauth/token Content-Type: application/x-www-form-urlencoded grant_type=refresh_token &refresh_token=rt_... &client_id=YOUR_CLIENT_ID ``` --- ## Client Credentials フロー サーバー間通信用。ユーザー認可なしで、登録済み Confidential Client が自分のワークスペースに対してアクセストークンを取得します。 ### Token リクエスト ```http POST https://re-port-flow.com/api/v1/oauth/token Content-Type: application/x-www-form-urlencoded Authorization: Basic BASE64(client_id:client_secret) grant_type=client_credentials &scope=pdf:generate%20templates:read ``` または `client_secret_post` 形式: ```http POST https://re-port-flow.com/api/v1/oauth/token Content-Type: application/x-www-form-urlencoded grant_type=client_credentials &client_id=YOUR_CLIENT_ID &client_secret=YOUR_CLIENT_SECRET &scope=pdf:generate%20templates:read ``` レスポンス(Refresh Token は発行されません): ```json { "access_token": "eyJhbGc...", "token_type": "Bearer", "expires_in": 3600, "scope": "pdf:generate templates:read" } ``` `scope` を省略した場合は、登録時に許可されたスコープ全体(`allowed_scopes`)が付与されます。 --- ## アクセストークンの利用 発行されたアクセストークンは `Authorization: Bearer` ヘッダで送信します。`appkey` ヘッダの代わりに利用できます。 ```bash curl -X POST https://api.re-port-flow.com/v1/file/sync/single \ -H "Authorization: Bearer eyJhbGc..." \ -H "Content-Type: application/json" \ -d '{...}' ``` ## エラーレスポンス トークンエンドポイントは [RFC 6749 Section 5.2](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2) に従います: ```json { "error": "invalid_client", "error_description": "Invalid client_secret" } ``` 主なエラーコード: | `error` | 状況 | |---------|------| | `invalid_client` | `client_id` / `client_secret` が不正 | | `invalid_grant` | 認可コード/リフレッシュトークンが不正・期限切れ | | `invalid_scope` | 要求スコープが `allowed_scopes` を超えている | | `unauthorized_client` | Public Client が `client_credentials` を要求した等 | | `unsupported_grant_type` | 未対応の `grant_type` | ## 次のステップ - [APIキーの取得方法](./api-keys) — `appkey` ヘッダの利用方法 - [認証方法(概要)](./overview) — 認証方式の選択と共通事項 - [PDF生成ガイド](../guides/pdf-generation) --- # docs/authentication/overview.md # 認証方法 Report Flow API は **`appkey` ヘッダ認証** と **OAuth 2.0 / OpenID Connect** の 2 方式をサポートします。用途に応じて選択してください。 ## 用途別の選択指針 | 用途 | 推奨方式 | |------|---------| | 単一ワークスペースの自動化(cURL、社内バッチ) | `appkey` ヘッダ | | サーバー間連携(バックエンド → Report Flow) | OAuth 2.0 **Client Credentials** | | ユーザー個別の認可(Make.com、外部 SaaS、個人アプリ) | OAuth 2.0 **Authorization Code + PKCE** | OAuth 2.0 の詳細は [OAuth 2.0 認証](./oauth) を参照してください。本ページでは `appkey` 方式を中心に説明します。 ## `appkey` 方式の仕組み `appkey` ヘッダで認証するリクエストは、以下のヘッダーを付与します: ```http appkey: your-application-key ``` ## APIエンドポイント **Base URL**: `https://api.re-port-flow.com/v1` 例: - 単一PDF生成: `https://api.re-port-flow.com/v1/file/sync/single` - デザインパラメータ取得: `https://api.re-port-flow.com/v1/file/design/parameter/{designId}` ## 認証エラー 認証に失敗した場合、以下のエラーが返されます: ### 401 Unauthorized ```json { "statusCode": 401, "message": "認証情報が不正です", "error": "Unauthorized" } ``` **原因:** - `appkey` ヘッダーが不正または無効 ### 412 Precondition Failed ```json { "statusCode": 412, "message": "認証方式ヘッダーが存在しません", "error": "Precondition Failed" } ``` **原因:** - `appkey` ヘッダーが欠落 ## セキュリティのベストプラクティス ### 1. APIキーの安全な保管 ```javascript // ❌ 悪い例:ソースコードにハードコード const APP_KEY = 'hardcoded-key'; // ✅ 良い例:環境変数を使用 const APP_KEY = process.env.REPORT_FLOW_APP_KEY; ``` ### 2. HTTPS通信の使用 すべてのAPIリクエストは必ずHTTPSを使用してください。HTTPでの通信は受け付けられません。 ### 3. キーのローテーション 定期的にAPIキーを再生成し、古いキーを無効化することを推奨します。 ### 4. スコープの制限 本番環境と開発環境で異なるAPIキーを使用し、環境ごとにアクセス権を分離してください。 ## サンプルコード ### cURL ```bash curl -X POST https://api.re-port-flow.com/v1/file/sync/single \ -H "appkey: your-application-key" \ -H "Content-Type: application/json" \ -d '{...}' ``` ### JavaScript ```javascript const headers = { 'appkey': process.env.REPORT_FLOW_APP_KEY, 'Content-Type': 'application/json' }; ``` ### Python ```python import os headers = { 'appkey': os.environ['REPORT_FLOW_APP_KEY'], 'Content-Type': 'application/json' } ``` ## OAuth 2.0 認証 サーバー間連携や、ユーザー個別の認可が必要な場合は、`appkey` の代わりに OAuth 2.0 を利用できます。 - **Authorization Code + PKCE**: Make.com、独自アプリ等のユーザー認可フロー - **Client Credentials**: バックエンドから Report Flow へのサーバー間連携 詳細は [OAuth 2.0 認証](./oauth) を参照してください。 ## 次のステップ - [APIキーの取得方法](./api-keys) - [OAuth 2.0 認証](./oauth) - [PDF生成ガイド](../guides/pdf-generation) --- # docs/guides/async-workflows.md # 非同期ワークフロー 非同期APIの活用方法とベストプラクティスを説明します。 ## 非同期APIとは Report Flow APIの一部のエンドポイントは非同期処理をサポートしています: | API | 同期 | 非同期 | |-----|------|--------| | 単一PDF生成 | `/file/sync/single` | `/file/async/single` | | 複数PDF生成 | `/file/sync/multiple` | `/file/async/multiple` | ## 非同期処理のメリット ### 1. タイムアウト回避 同期APIは120秒でタイムアウトしますが、非同期APIは長時間の処理に対応できます。 ```javascript // ❌ 同期API: 大量データで120秒を超える可能性 const pdf = await generateSyncPDF(largeData); // ✅ 非同期API: タイムアウトなし const { url } = await generateAsyncPDF(largeData); ``` ### 2. バックグラウンド処理 ユーザーを待たせずに処理を実行できます。 ```javascript // ユーザーリクエストを即座に受け付け app.post('/api/generate-report', async (req, res) => { // 非同期でPDF生成をキック const { url, fileId } = await startPDFGeneration(req.body); // 即座にレスポンス res.json({ message: 'レポート生成を開始しました', downloadUrl: url, fileId }); // 完了後にメール通知(バックグラウンド) notifyWhenComplete(fileId, req.user.email); }); ``` ### 3. 大量処理の並列化 複数のPDF生成を並列実行できます。 ```javascript // 100件のPDFを並列生成 const requests = invoices.map(invoice => generateAsyncPDF({ designId: 'invoice-template', content: invoice }) ); const results = await Promise.all(requests); console.log(`${results.length}件のPDFを生成しました`); ``` ## 大量処理のパターン ### パターン1: 並列実行(小〜中規模) ```javascript async function generateBulkPDFs(items, concurrency = 5) { const chunks = chunkArray(items, concurrency); const results = []; for (const chunk of chunks) { const promises = chunk.map(item => generateAsyncPDF({ designId: 'template-id', content: item }) ); const chunkResults = await Promise.all(promises); results.push(...chunkResults); console.log(`${results.length}/${items.length} 完了`); } return results; } function chunkArray(array, size) { const chunks = []; for (let i = 0; i < array.length; i += size) { chunks.push(array.slice(i, i + size)); } return chunks; } // 使用例: 100件を5件ずつ並列処理 const invoices = [...]; // 100件 const results = await generateBulkPDFs(invoices, 5); ``` ### パターン2: キューベース(大規模) ```javascript import Queue from 'bull'; // Redisベースのキューを作成 const pdfQueue = new Queue('pdf-generation', { redis: { host: 'localhost', port: 6379 } }); // ワーカー設定 pdfQueue.process(async (job) => { const { designId, content } = job.data; // PDF生成API呼び出し const result = await generateAsyncPDF({ designId, content }); // 進捗更新 await job.progress(100); return result; }); // ジョブ追加 async function enqueuePDFGeneration(items) { const jobs = items.map(item => pdfQueue.add({ designId: 'template-id', content: item }, { attempts: 3, backoff: { type: 'exponential', delay: 2000 } }) ); return Promise.all(jobs); } // イベント監視 pdfQueue.on('completed', (job, result) => { console.log(`ジョブ ${job.id} 完了:`, result); }); pdfQueue.on('failed', (job, err) => { console.error(`ジョブ ${job.id} 失敗:`, err); }); ``` ### パターン3: バッチ処理(超大規模) ```javascript // 1000件以上の場合は複数PDFの一括生成を使用 async function generateMassiveBulk(items) { const batchSize = 100; // APIの上限に合わせる const batches = chunkArray(items, batchSize); const results = []; for (const [index, batch] of batches.entries()) { console.log(`バッチ ${index + 1}/${batches.length} 処理中...`); // 複数PDF一括生成API const { url, files } = await generateMultiplePDFsAsync({ designId: 'template-id', contents: batch.map(item => ({ fileName: `${item.id}.pdf`, params: item })) }); results.push({ batchIndex: index, url, files }); // レート制限対策で少し待機 if (index < batches.length - 1) { await new Promise(resolve => setTimeout(resolve, 1000)); } } return results; } ``` ## エラーハンドリング ### リトライロジック ```javascript async function generateWithRetry(params, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await generateAsyncPDF(params); } catch (error) { const isLastAttempt = attempt === maxRetries - 1; if (isServerError(error) && !isLastAttempt) { const delay = 1000 * Math.pow(2, attempt); console.log(`リトライ ${attempt + 1}/${maxRetries} (${delay}ms後)`); await new Promise(resolve => setTimeout(resolve, delay)); continue; } throw error; } } } function isServerError(error) { return error.response?.status >= 500; } ``` ### 部分的な失敗の処理 ```javascript async function generateWithFallback(items) { const results = []; const errors = []; for (const item of items) { try { const result = await generateAsyncPDF({ designId: 'template-id', content: item }); results.push({ item, result, status: 'success' }); } catch (error) { console.error(`アイテム ${item.id} の生成失敗:`, error); errors.push({ item, error, status: 'failed' }); } } return { results, errors, summary: { total: items.length, success: results.length, failed: errors.length } }; } ``` ## Python実装例 ```python import asyncio import aiohttp from typing import List, Dict, Any async def generate_pdf_async(session: aiohttp.ClientSession, params: Dict[str, Any]) -> Dict: """非同期でPDFを生成""" url = "https://api.re-port-flow.com/v1/file/async/single" headers = { 'appkey': params['app_key'], 'Content-Type': 'application/json' } data = { 'designId': params['design_id'], 'version': params['version'], 'content': params['content'] } async with session.post(url, headers=headers, json=data) as response: response.raise_for_status() return await response.json() async def generate_bulk_pdfs( items: List[Dict], app_key: str, design_id: str, version: int = 1, concurrency: int = 5, ): """複数PDFを並列生成(Session を 1 つ共有して接続プールを効かせる)""" semaphore = asyncio.Semaphore(concurrency) async with aiohttp.ClientSession() as session: async def generate_with_limit(item): async with semaphore: return await generate_pdf_async(session, { 'app_key': app_key, 'design_id': design_id, 'version': version, 'content': item, }) tasks = [generate_with_limit(item) for item in items] return await asyncio.gather(*tasks) # 使用例 items = [...] # 生成対象データ results = asyncio.run(generate_bulk_pdfs( items, app_key='your-app-key', design_id='template-id', version=1, concurrency=10, )) ``` ## ベストプラクティス 1. **適切な並列度を設定**: サーバー負荷とレスポンス速度のバランスを取る 2. **指数バックオフを使用**: ポーリング間隔を徐々に増やす 3. **タイムアウトを設定**: 無限待機を防ぐ 4. **エラーをログ**: 失敗したアイテムを追跡 5. **進捗を表示**: ユーザーに状況を伝える ## 次のステップ - [PDF生成ガイド](./pdf-generation) - [エラーハンドリング](./error-handling) --- # docs/guides/endpoints/async-multiple.md # 複数PDF非同期生成 `POST /file/async/multiple` エンドポイントは、指定されたデザインと複数のパラメータから複数のPDFファイルを非同期的に生成します。 ## エンドポイント情報 - **URL**: `https://api.re-port-flow.com/v1/file/async/multiple` - **メソッド**: `POST` - **認証**: `appkey` ヘッダーが必要 - **タイムアウト**: なし(非同期処理) - **リクエストサイズ上限**: 50MB(Base64エンコード後、実質約37MB相当) ## 使用例 ### cURL ```bash curl -X POST https://api.re-port-flow.com/v1/file/async/multiple \ -H "appkey: your-application-key" \ -H "Content-Type: application/json" \ -d '{ "designId": "550e8400-e29b-41d4-a716-446655440000", "version": 1, "contents": [ { "fileName": "invoice_001.pdf", "shareType": "01", "passcodeEnabled": false, "params": { "customerName": "山田太郎", "invoiceNumber": "INV-001" } }, { "fileName": "invoice_002.pdf", "shareType": "01", "passcodeEnabled": false, "params": { "customerName": "佐藤花子", "invoiceNumber": "INV-002" } } ] }' ``` ## リクエストパラメータ | フィールド | 型 | 必須 | 説明 | |-----------|-----|------|------| | `designId` | string (UUID) | ✓ | デザインID | | `version` | integer | ✓ | バージョン番号 | | `contents` | array | ✓ | ContentDtoの配列(最小1件) | | `contents[].fileName` | string | ✓ | ファイル名(`/ \\ : * ? " < > \|` および制御文字以外は使用可能。**配列内で一意であること**(大文字・小文字は区別しない)) | | `contents[].shareType` | string | - | 共有タイプ(リクエストは数値コード)。`"01"`=ワークスペース内共有(デフォルト) / `"02"`=招待者共有 / `"03"`=公開URL共有。**レスポンス**の `share.shareType` は `workspace` / `invited` / `public` の人間可読な名前で返ります | | `contents[].passcodeEnabled` | boolean | - | パスコード保護(デフォルト: `false`) | | `contents[].passthrough` | object | - | レスポンスの `files[].passthrough` に透過する任意のメタデータ(例: `{ "pageId": "abc123" }`) | | `contents[].params` | object | ✓ | パラメータ([デザインパラメータ取得API](./design-parameters.md)で構造を確認可能) | ## レスポンス ### 成功時 (202 Accepted) ```json { "requestId": "550e8400-e29b-41d4-a716-446655440000", "url": "https://api.re-port-flow.com/v1/file/download/{requestId}", "files": [ { "fileName": "invoice_001.pdf", "fileId": "aaa111", "passthrough": { "pageId": "abc123" }, "share": { "shareType": "workspace", "url": "https://app.re-port-flow.com/file/{requestId}/aaa111", "passcodeEnabled": false } }, { "fileName": "invoice_002.pdf", "fileId": "bbb222", "share": { "shareType": "workspace", "url": "https://app.re-port-flow.com/file/{requestId}/bbb222", "passcodeEnabled": false } } ] } ``` | フィールド | 型 | 説明 | |-----------|-----|------| | `requestId` | string (UUID) | リクエストID(ZIPダウンロードエンドポイントで使用) | | `url` | string (URI) | ZIPダウンロードURL | | `files` | array | 各PDFファイルの情報 | | `files[].fileName` | string | PDFファイル名 | | `files[].fileId` | string | 個別ファイルID(個別ダウンロードエンドポイントで使用) | | `files[].passthrough` | object | リクエスト時に指定した `contents[].passthrough` の値(指定時のみ) | | `files[].share.shareType` | string | 共有タイプ(`workspace` / `invited` / `public`) | | `files[].share.url` | string | ファイル表示URL | | `files[].share.passcodeEnabled` | boolean | パスコード有効フラグ | | `files[].share.passcode` | string | サーバー生成パスコード(`passcodeEnabled=true` かつ生成直後のみ) | :::info passthrough の使い所 ReportFlow はセキュリティとペイロードサイズの観点から、レスポンスや Webhook 通知に `params`(生成データ)を返しません。 受信側で「どの業務レコードに対する PDF か」を識別したい場合は、 リクエスト時に `passthrough` に自社DBのIDなどを入れてください。 レスポンスや Webhook でそのまま返却されます。 ```json { "fileName": "invoice.pdf", "passthrough": { "invoiceId": "INV-001", "tenantId": "acme" }, "params": { "customerName": "山田太郎", "amount": 10000 } } ``` Webhook 受信時に `passthrough` の値がそのまま返るため、`invoiceId` から DB を引いて該当レコードを更新する、といった処理が組めます。 `params`(顧客名や金額)は外部に送信されません。 ::: ## エラー時 [単一PDF同期生成](./sync-single.md#エラー時)と同様のエラーレスポンスに加え、以下のエラーが返される場合があります。 ### 400 Bad Request(fileName 重複) ```json { "statusCode": 400, "message": [ "contents 内の fileName が重複しています(大文字・小文字は区別されません)。各ファイルには一意の名前を指定してください。" ], "error": "Bad Request" } ``` **原因**: `contents` 配列内に同じ `fileName`(大文字・小文字の違いのみを含む)が複数存在する ## ユースケース ### 大量請求書の一括生成 ```javascript async function generateBulkInvoices(invoices) { const batchSize = 100; const batches = chunkArray(invoices, batchSize); const results = []; for (const [index, batch] of batches.entries()) { console.log(`バッチ ${index + 1}/${batches.length} 処理中...`); const contents = batch.map(invoice => ({ fileName: `invoice_${invoice.number}.pdf`, shareType: '01', passcodeEnabled: false, params: invoice })); const response = await axios.post( 'https://api.re-port-flow.com/v1/file/async/multiple', { designId: 'template-id', version: 1, contents }, { headers: { 'appkey': process.env.APP_KEY } } ); results.push(response.data); if (index < batches.length - 1) { await new Promise(resolve => setTimeout(resolve, 1000)); } } return results; } ``` ## 次のステップ - [ZIP一括ダウンロード](./download.md#zip一括ダウンロード) - 生成されたZIPファイルのダウンロード - [非同期ワークフロー](../async-workflows.md) - 大量生成のベストプラクティス --- # docs/guides/endpoints/async-single.md # 単一PDF非同期生成 `POST /file/async/single` エンドポイントは、指定されたデザインとパラメータから単一のPDFファイルを非同期的に生成します。即座に `requestId` とファイル情報を返すため、タイムアウトを避けることができます。 ## エンドポイント情報 - **URL**: `https://api.re-port-flow.com/v1/file/async/single` - **メソッド**: `POST` - **認証**: `appkey` ヘッダーが必要 - **タイムアウト**: なし(非同期処理) - **リクエストサイズ上限**: 50MB(Base64エンコード後、実質約37MB相当) ## 使用例 ### cURL ```bash curl -X POST https://api.re-port-flow.com/v1/file/async/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 } } }' ``` ## リクエストパラメータ | フィールド | 型 | 必須 | 説明 | |-----------|-----|------|------| | `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 | - | レスポンスの `files[].passthrough` に透過する任意のメタデータ(例: `{ "pageId": "abc123" }`) | | `content.params` | object | ✓ | テンプレートに埋め込むパラメータ([デザインパラメータ取得API](./design-parameters.md)で構造を確認可能) | ## レスポンス ### 成功時 (202 Accepted) ```json { "requestId": "550e8400-e29b-41d4-a716-446655440000", "url": "https://api.re-port-flow.com/v1/file/download/{requestId}", "files": [ { "fileName": "invoice.pdf", "fileId": "7f3d1a2b-4c5e-6f7a-8b9c-0d1e2f3a4b5c", "passthrough": { "pageId": "abc123" }, "share": { "shareType": "workspace", "url": "https://app.re-port-flow.com/file/{requestId}/{fileId}", "passcodeEnabled": false } } ] } ``` | フィールド | 型 | 説明 | |-----------|-----|------| | `requestId` | string (UUID) | リクエストID(ダウンロードエンドポイントで使用) | | `url` | string (URI) | ZIPダウンロードURL | | `files` | array | 生成されたファイルの情報 | | `files[].fileName` | string | ファイル名 | | `files[].fileId` | string | ファイルID(個別ダウンロードエンドポイントで使用) | | `files[].passthrough` | object | リクエスト時に指定した `content.passthrough` の値(指定時のみ) | | `files[].share.shareType` | string | 共有タイプ(`workspace` / `invited` / `public`) | | `files[].share.url` | string | ファイル表示URL | | `files[].share.passcodeEnabled` | boolean | パスコード有効フラグ | | `files[].share.passcode` | string | サーバー生成パスコード(`passcodeEnabled=true` かつ生成直後のみ) | :::info passthrough の使い所 ReportFlow はセキュリティとペイロードサイズの観点から、レスポンスや Webhook 通知に `params`(生成データ)を返しません。 受信側で「どの業務レコードに対する PDF か」を識別したい場合は、 リクエスト時に `passthrough` に自社DBのIDなどを入れてください。 レスポンスや Webhook でそのまま返却されます。 ```json { "fileName": "invoice.pdf", "passthrough": { "invoiceId": "INV-001", "tenantId": "acme" }, "params": { "customerName": "山田太郎", "amount": 10000 } } ``` Webhook 受信時に `passthrough` の値がそのまま返るため、`invoiceId` から DB を引いて該当レコードを更新する、といった処理が組めます。 `params`(顧客名や金額)は外部に送信されません。 ::: ### エラー時 同期生成エンドポイントと同様のエラーレスポンスを返します。詳細は[単一PDF同期生成](./sync-single.md#エラー時)を参照してください。 ## 非同期処理のフロー ``` 1. クライアント → API: 生成リクエスト送信 ↓ 2. API → クライアント: 即座に requestId・url・files を返す (202 Accepted) ↓ 3. API: バックグラウンドでPDF生成開始 ↓ 4. API: PDF生成完了後、S3にアップロード ↓ 5. クライアント: 返された url または /download/{requestId}/{fileId} からPDFをダウンロード ``` ## ユースケース ### ケース1: バックグラウンドでPDF生成 ユーザーのリクエストを即座に受け付け、バックグラウンドで処理します。 ```javascript app.post('/api/generate-report', async (req, res) => { const response = await axios.post( 'https://api.re-port-flow.com/v1/file/async/single', { designId: '...', version: 1, content: { fileName: 'report.pdf', params: req.body } }, { headers: { 'appkey': process.env.APP_KEY } } ); const { requestId, url, files } = response.data; // 即座にレスポンス res.status(202).json({ message: 'レポート生成を開始しました', requestId, downloadUrl: url, fileId: files[0].fileId }); }); ``` ### ケース2: Webhook通知の活用(推奨) ReportFlowの標準Webhook機能を使用すると、ポーリング不要でリアルタイムに完了通知を受け取れます。 詳細は[Webhook通知ガイド](../webhooks.md)を参照してください。 ## 次のステップ - [複数PDF非同期生成](./async-multiple.md) - 複数のPDFを非同期で一括生成 - [非同期ワークフロー](../async-workflows.md) - 大量生成のベストプラクティス - [ファイルダウンロード](./download.md) - 生成されたファイルのダウンロード方法 --- # docs/guides/endpoints/design-parameters.md # デザインパラメータ取得 `GET /file/design/parameter/{designId}` エンドポイントは、指定されたデザインIDのパラメータ構造を取得します。PDFやサムネイル生成時に必要なパラメータのスキーマを確認できます。 ## エンドポイント情報 - **URL**: `https://api.re-port-flow.com/v1/file/design/parameter/{designId}` - **メソッド**: `GET` - **認証**: `appkey` ヘッダーが必要 ## 使用例 ### cURL ```bash # 最新バージョンのパラメータを取得 curl -X GET https://api.re-port-flow.com/v1/file/design/parameter/550e8400-e29b-41d4-a716-446655440000 \ -H "appkey: your-application-key" # 特定バージョンのパラメータを取得 curl -X GET "https://api.re-port-flow.com/v1/file/design/parameter/550e8400-e29b-41d4-a716-446655440000?version=1" \ -H "appkey: your-application-key" ``` ### JavaScript ```javascript async function getDesignParameters(designId, version = null) { const baseUrl = 'https://api.re-port-flow.com/v1'; const url = version ? `${baseUrl}/file/design/parameter/${designId}?version=${version}` : `${baseUrl}/file/design/parameter/${designId}`; const response = await axios.get(url, { headers: { 'appkey': process.env.APP_KEY } }); return response.data; } // 使用例 const schema = await getDesignParameters('550e8400-...'); console.log('パラメータスキーマ:', schema); ``` ### Python ```python def get_design_parameters(design_id, version=None): base_url = "https://api.re-port-flow.com/v1" url = f"{base_url}/file/design/parameter/{design_id}" if version: url += f"?version={version}" headers = { 'appkey': os.getenv('APP_KEY') } response = requests.get(url, headers=headers) response.raise_for_status() return response.json() # 使用例 schema = get_design_parameters('550e8400-...') print('パラメータスキーマ:', schema) ``` ## パラメータ | パラメータ | 型 | 必須 | 説明 | |-----------|-----|------|------| | `designId` | string (UUID) | ✓ | デザインID(パスパラメータ) | | `version` | integer | - | バージョン番号(クエリパラメータ、省略時は最新) | ## レスポンス ### 成功時 (200 OK) ```json { "customerName": "string", "invoiceNumber": "string", "amount": "number", "items": [ { "name": "string", "price": "number", "quantity": "number" } ], "issueDate": "date" } ``` **フィールドの型**: | 型 | 説明 | 例 | |----|------|-----| | `string` | 文字列 | `"山田太郎"` | | `number` | 数値 | `1000` | | `date` | 日付(ISO 8601) | `"2024-02-12"` | | `object` | ネストしたオブジェクト | `{ "name": "値" }` | | `array` | 配列 | `[{ "item": 1 }]` | ### エラー時 #### 404 Not Found ```json { "statusCode": 404, "message": "指定したデザインが存在しません", "error": "Not Found" } ``` **原因**: 指定されたデザインIDまたはバージョンが存在しない(特定バージョンが見つからない場合は `指定したバージョン(X)が存在しません` が返ります) ## ユースケース ### 動的フォーム生成 デザインパラメータから入力フォームを動的に生成する例。 ```javascript async function createDynamicForm(designId) { // パラメータスキーマを取得 const schema = await getDesignParameters(designId); // スキーマからフォームフィールドを生成 const formFields = Object.entries(schema).map(([key, type]) => { if (type === 'string') { return ``; } else if (type === 'number') { return ``; } else if (type === 'date') { return ``; } else if (Array.isArray(type)) { // 配列型の場合 return `
`; } }); return formFields.join('\n'); } ``` ### バリデーション ```javascript function validateParams(params, schema) { const errors = []; for (const [key, type] of Object.entries(schema)) { if (!(key in params)) { errors.push(`必須パラメータ ${key} が不足しています`); continue; } const value = params[key]; if (type === 'string' && typeof value !== 'string') { errors.push(`${key} は文字列である必要があります`); } else if (type === 'number' && typeof value !== 'number') { errors.push(`${key} は数値である必要があります`); } else if (type === 'date') { if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) { errors.push(`${key} はISO 8601形式の日付である必要があります`); } } } return errors; } // 使用例 const schema = await getDesignParameters('550e8400-...'); const params = { customerName: '山田太郎', invoiceNumber: 'INV-001', amount: 10000 }; const errors = validateParams(params, schema); if (errors.length > 0) { console.error('バリデーションエラー:', errors); } ``` ## ベストプラクティス ### 1. パラメータスキーマのキャッシュ デザインのバージョンが変わらない限り、パラメータスキーマは変わりません。キャッシュして再利用することを推奨します。 ```javascript const schemaCache = new Map(); async function getDesignParametersCached(designId, version) { const cacheKey = `${designId}:${version}`; if (schemaCache.has(cacheKey)) { return schemaCache.get(cacheKey); } const schema = await getDesignParameters(designId, version); schemaCache.set(cacheKey, schema); return schema; } ``` ### 2. PDF生成前の検証 パラメータスキーマを事前に取得してバリデーションを行うことで、エラーを早期に検出できます。 ```javascript async function generatePDFSafely(params) { // 1. パラメータスキーマを取得 const schema = await getDesignParameters(params.designId, params.version); // 2. バリデーション const errors = validateParams(params.data, schema); if (errors.length > 0) { throw new Error(`パラメータエラー: ${errors.join(', ')}`); } // 3. PDF生成 return await generatePDF(params); } ``` ## 次のステップ - [単一PDF同期生成](./sync-single.md) - パラメータを使用したPDF生成 - [エラーハンドリング](../error-handling.md) - バリデーションエラーの処理 --- # docs/guides/endpoints/download.md # ファイルダウンロード 生成されたPDFファイルをダウンロードするためのエンドポイントです。 ## ZIP一括ダウンロード ### エンドポイント情報 - **URL**: `https://api.re-port-flow.com/v1/file/download/{requestId}` - **メソッド**: `GET` - **認証**: アプリケーションキー(`appkey` ヘッダー)が必要 ### レスポンス (200 OK) **レスポンスボディ**: ZIPファイル(バイナリ) | ヘッダー | 説明 | 例 | |---------|------|-----| | `Content-Type` | コンテンツタイプ | `application/zip` | | `Content-Length` | ファイルサイズ(バイト) | `307200` | | `Content-Disposition` | ファイル名 | `attachment; filename="files.zip"` | ### 使用例 #### cURL ```bash curl -X GET https://api.re-port-flow.com/v1/file/download/550e8400-e29b-41d4-a716-446655440000 \\ -H "appkey: your-app-key" \\ --output files.zip ``` #### JavaScript ```javascript async function downloadZip(requestId) { const response = await axios.get( `https://api.re-port-flow.com/v1/file/download/${requestId}`, { headers: { 'appkey': process.env.APP_KEY }, responseType: 'arraybuffer' } ); fs.writeFileSync('files.zip', response.data); return response.data; } ``` ## 個別ファイルダウンロード ### エンドポイント情報 - **URL**: `https://api.re-port-flow.com/v1/file/download/{requestId}/{fileId}` - **メソッド**: `GET` - **認証**: アプリケーションキー(`appkey` ヘッダー)が必要 ### レスポンス (200 OK) **レスポンスボディ**: PDFファイル(バイナリ) | ヘッダー | 説明 | 例 | |---------|------|-----| | `Content-Type` | コンテンツタイプ | `application/pdf` | | `Content-Length` | ファイルサイズ(バイト) | `102400` | | `Content-Disposition` | ファイル名 | `attachment; filename="invoice.pdf"` | | `File-ID` | ファイルID | `7f3d1a2b-4c5e-6f7a-8b9c-0d1e2f3a4b5c` | ### 使用例 #### cURL ```bash curl -X GET https://api.re-port-flow.com/v1/file/download/550e8400-e29b-41d4-a716-446655440000/7f3d1a2b-4c5e-6f7a-8b9c-0d1e2f3a4b5c \\ -H "appkey: your-app-key" \\ --output invoice.pdf ``` #### JavaScript ```javascript async function downloadFile(requestId, fileId) { const response = await axios.get( `https://api.re-port-flow.com/v1/file/download/${requestId}/${fileId}`, { headers: { 'appkey': process.env.APP_KEY }, responseType: 'arraybuffer' } ); const returnedFileId = response.headers['file-id']; console.log('ダウンロードしたファイルID:', returnedFileId); return response.data; } ``` ## エラーレスポンス ### 404 Not Found ```json { "statusCode": 404, "message": "File not found", "error": "Not Found" } ``` **原因**: - 指定された `requestId` または `fileId` が存在しない - ファイルの有効期限が切れた **対処法**: - `requestId` / `fileId` が正しいことを確認 - ファイル生成から時間が経過している場合は再生成 ## ユースケース ### ZIP内の特定ファイルのみダウンロード ```javascript // async/multiple のレスポンスから requestId と files を取得済みとする async function downloadSpecificFile(requestId, files) { const targetFile = files.find(f => f.fileName === 'invoice_001.pdf'); if (targetFile) { const pdfData = await downloadFile(requestId, targetFile.fileId); fs.writeFileSync('invoice_001.pdf', pdfData); } } ``` ## 次のステップ - [単一PDF生成](./sync-single.md) - PDFファイルの生成方法 - [複数PDF生成](./sync-multiple.md) - 複数PDFの一括生成 --- # docs/guides/endpoints/sync-multiple.md # 複数PDF同期生成(ZIP) `POST /file/sync/multiple` エンドポイントは、指定されたデザインと複数のパラメータから複数のPDFファイルを同期的に生成し、ZIPファイルで返します。 ## エンドポイント情報 - **URL**: `https://api.re-port-flow.com/v1/file/sync/multiple` - **メソッド**: `POST` - **認証**: `appkey` ヘッダーが必要 - **タイムアウト**: 120秒 - **リクエストサイズ上限**: 50MB(Base64エンコード後、実質約37MB相当) ## 使用例 ### cURL ```bash 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": "550e8400-e29b-41d4-a716-446655440000", "version": 1, "contents": [ { "fileName": "invoice_001.pdf", "shareType": "01", "passcodeEnabled": false, "params": { "customerName": "山田太郎", "invoiceNumber": "INV-001" } }, { "fileName": "invoice_002.pdf", "shareType": "02", "passcodeEnabled": false, "params": { "customerName": "佐藤花子", "invoiceNumber": "INV-002" } } ] }' \ --output invoices.zip ``` ### JavaScript ```javascript import axios from 'axios'; import fs from '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' } ); 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('生成されたファイル:', fileMapping.map(f => ({ fileId: f.fileId, fileName: f.fileName, share: f.share }))); return { data: response.data, requestId, fileUrl, fileMapping }; } // 使用例 const contents = [ { fileName: 'invoice_001.pdf', shareType: '01', passcodeEnabled: false, params: { customerName: '山田太郎', invoiceNumber: 'INV-001' } }, { fileName: 'invoice_002.pdf', shareType: '02', passcodeEnabled: false, params: { customerName: '佐藤花子', invoiceNumber: 'INV-002' } } ]; const result = await generateMultiplePDFs('550e8400-...', contents); fs.writeFileSync('invoices.zip', result.data); ``` ## リクエストパラメータ | フィールド | 型 | 必須 | 説明 | |-----------|-----|------|------| | `designId` | string (UUID) | ✓ | デザインID | | `version` | integer | ✓ | バージョン番号 | | `contents` | array | ✓ | ContentDtoの配列(最小1件) | | `contents[].fileName` | string | ✓ | ファイル名(`/ \\ : * ? " < > \|` および制御文字以外は使用可能。**配列内で一意であること**(大文字・小文字は区別しない)) | | `contents[].shareType` | string | - | 共有タイプ(リクエストは数値コード)。`"01"`=ワークスペース内共有(デフォルト) / `"02"`=招待者共有 / `"03"`=公開URL共有。**レスポンス**の `share.shareType` は `workspace` / `invited` / `public` の人間可読な名前で返ります | | `contents[].passcodeEnabled` | boolean | - | パスコード保護(デフォルト: `false`) | | `contents[].passthrough` | object | - | レスポンスの `X-File-Mapping` に透過する任意のメタデータ(例: `{ "pageId": "abc123" }`) | | `contents[].params` | object | ✓ | パラメータ([デザインパラメータ取得API](./design-parameters.md)で構造を確認可能) | ## レスポンス ### 成功時 (200 OK) **レスポンスボディ**: ZIPファイル(バイナリ) **レスポンスヘッダー**: | ヘッダー | 説明 | 例 | |---------|------|-----| | `Content-Type` | コンテンツタイプ | `application/zip` | | `Content-Length` | ファイルサイズ(バイト) | `307200` | | `Content-Disposition` | ファイル名 | `attachment; filename="files.zip"` | | `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` の構造**: ```json [ { "fileId": "aaa111", "fileName": "invoice_001.pdf", "passthrough": { "pageId": "abc123" }, "share": { "shareType": "workspace", "url": "https://app.re-port-flow.com/file/{requestId}/aaa111", "passcodeEnabled": false } }, { "fileId": "bbb222", "fileName": "invoice_002.pdf", "share": { "shareType": "invited", "url": "https://app.re-port-flow.com/file/{requestId}/bbb222", "passcodeEnabled": false } } ] ``` `passthrough` フィールドは `contents[].passthrough` を指定したエントリのみに含まれます。 :::info passthrough の使い所 ReportFlow はセキュリティとペイロードサイズの観点から、レスポンスや Webhook 通知に `params`(生成データ)を返しません。 受信側で「どの業務レコードに対する PDF か」を識別したい場合は、 リクエスト時に `passthrough` に自社DBのIDなどを入れてください。 レスポンスや Webhook でそのまま返却されます。 ```json { "fileName": "invoice.pdf", "passthrough": { "invoiceId": "INV-001", "tenantId": "acme" }, "params": { "customerName": "山田太郎", "amount": 10000 } } ``` Webhook 受信時に `passthrough` の値がそのまま返るため、`invoiceId` から DB を引いて該当レコードを更新する、といった処理が組めます。 `params`(顧客名や金額)は外部に送信されません。 ::: ### エラー時 [単一PDF同期生成](./sync-single.md#エラー時)と同様のエラーレスポンスに加え、以下のエラーが返される場合があります。 #### 400 Bad Request(fileName 重複) ```json { "statusCode": 400, "message": [ "contents 内の fileName が重複しています(大文字・小文字は区別されません)。各ファイルには一意の名前を指定してください。" ], "error": "Bad Request" } ``` **原因**: `contents` 配列内に同じ `fileName`(大文字・小文字の違いのみを含む)が複数存在する ## ユースケース ### 月次請求書の一括生成 ```javascript async function generateMonthlyInvoices(month) { const invoices = await getInvoicesByMonth(month); const contents = invoices.map(invoice => ({ fileName: `invoice_${invoice.number}.pdf`, shareType: '01', passcodeEnabled: false, params: invoice })); const result = await generateMultiplePDFs('invoice-template-id', contents); fs.writeFileSync(`invoices_${month}.zip`, result.data); } ``` ## 次のステップ - [複数PDF非同期生成](./async-multiple.md) - タイムアウトを避けたい場合 - [ファイルダウンロード](./download.md) - 生成されたZIPファイルのダウンロード --- # docs/guides/endpoints/sync-single.md # 単一PDF同期生成 `POST /file/sync/single` エンドポイントは、指定されたデザインとパラメータから単一のPDFファイルを同期的に生成します。 ## エンドポイント情報 - **URL**: `https://api.re-port-flow.com/v1/file/sync/single` - **メソッド**: `POST` - **認証**: `appkey` ヘッダーが必要 - **タイムアウト**: 120秒 - **リクエストサイズ上限**: 50MB(Base64エンコード後、実質約37MB相当) ## 使用例 ### cURL ```bash 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) ```javascript 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 ```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](./design-parameters.md)で構造を確認可能) | ## レスポンス ### 成功時 (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` の構造**: ```json [ { "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` を指定した場合のみ含まれます。 :::info passthrough の使い所 ReportFlow はセキュリティとペイロードサイズの観点から、レスポンスや Webhook 通知に `params`(生成データ)を返しません。 受信側で「どの業務レコードに対する PDF か」を識別したい場合は、 リクエスト時に `passthrough` に自社DBのIDなどを入れてください。 レスポンスや Webhook でそのまま返却されます。 ```json { "fileName": "invoice.pdf", "passthrough": { "invoiceId": "INV-001", "tenantId": "acme" }, "params": { "customerName": "山田太郎", "amount": 10000 } } ``` Webhook 受信時に `passthrough` の値がそのまま返るため、`invoiceId` から DB を引いて該当レコードを更新する、といった処理が組めます。 `params`(顧客名や金額)は外部に送信されません。 ::: ### エラー時 #### 412 Precondition Failed ```json { "statusCode": 412, "message": "認証方式ヘッダーが存在しません" } ``` **原因**: `appkey` ヘッダーが欠落 #### 400 Bad Request ```json { "statusCode": 400, "message": [ "designId must be a UUID", "ファイル名に使用できない文字が含まれています(/ \\ : * ? \" < > | および制御文字は使用不可)" ], "error": "Bad Request" } ``` **原因**: リクエストパラメータが不正 #### 401 Unauthorized ```json { "statusCode": 401, "message": "認証情報が不正です", "error": "Unauthorized" } ``` **原因**: 認証エラー **対処法**: - `appkey` ヘッダーの値が正しいことを確認 #### 500 Internal Server Error ```json { "statusCode": 500, "message": "Internal server error", "error": "Internal Server Error" } ``` ## ベストプラクティス ### 1. タイムアウト処理 同期APIは120秒でタイムアウトします。大きなPDFや複雑なデザインの場合は、非同期APIの使用を検討してください。 ```javascript const response = await axios.post(url, data, { timeout: 120000 // 120秒 }); ``` ### 2. リトライロジック 一時的なサーバーエラー(500系)の場合は、指数バックオフでリトライを実装します。 ```javascript 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. ファイル名のサニタイゼーション ```javascript function sanitizeFileName(fileName) { // / \ : * ? " < > | および制御文字を除去 return fileName.replace(/[\/\\:*?"<>|\x00-\x1F]/g, '_'); } ``` ## ユースケース ### ケース1: 請求書の即時生成 ユーザーが「請求書ダウンロード」ボタンをクリックしたときに、即座にPDFを生成してダウンロードさせる。 ```javascript 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); }); ``` ### ケース2: プレビュー生成 デザインエディタでユーザーがプレビューボタンをクリックしたときに、リアルタイムでPDFを生成して表示する。 ```javascript async function showPreview(designId, params) { const pdf = await generatePDF({ designId, version: 1, fileName: 'preview.pdf', data: params }); // ブラウザでPDFを表示 const blob = new Blob([pdf.data], { type: 'application/pdf' }); const url = URL.createObjectURL(blob); window.open(url, '_blank'); } ``` ## 次のステップ - [単一PDF非同期生成](./async-single.md) - タイムアウトを避けたい場合 - [複数PDF同期生成](./sync-multiple.md) - 複数のPDFを一度に生成 - [非同期ワークフロー](../async-workflows.md) - 大量生成のベストプラクティス --- # docs/guides/error-handling.md # エラーハンドリング Report Flow APIのエラーハンドリング方法とベストプラクティスを説明します。 ## エラーレスポンスの形式 すべてのエラーは以下の形式で返されます: ```json { "statusCode": 400, "message": "エラーの詳細説明", "error": "エラータイプ" } ``` ## HTTPステータスコード一覧 ### 2xx 成功 | コード | 意味 | 説明 | |--------|------|------| | 200 OK | 成功 | リクエストが正常に処理されました | | 202 Accepted | 受付 | 非同期処理が受け付けられました | ### 4xx クライアントエラー #### 400 Bad Request **原因:** リクエストの形式や内容が不正 ```json { "statusCode": 400, "message": [ "designId must be a UUID", "version must be an integer number" ], "error": "Bad Request" } ``` **対処法:** - リクエストボディのバリデーション - 型の確認(UUID、integer等) - 必須フィールドの確認 **例:ファイル名バリデーションエラー** ```json { "statusCode": 400, "message": "ファイル名に使用できない文字が含まれています(/ \\ : * ? \" < > | および制御文字は使用不可)", "error": "Bad Request" } ``` #### 401 Unauthorized **原因:** 認証情報が不正または欠落 ```json { "statusCode": 401, "message": "認証情報が不正です", "error": "Unauthorized" } ``` **対処法:** - アプリケーションキー(`appkey` ヘッダー)を確認 - キーの有効期限・再生成状況を確認 #### 404 Not Found **原因:** リソースが見つからない ```json { "statusCode": 404, "message": "指定したデザインが存在しません", "error": "Not Found" } ``` **対処法:** - `designId` が正しいことを確認 - 指定したバージョンが存在するか確認(存在しない場合は `指定したバージョン(X)が存在しません` が返ります) #### 403 Forbidden **原因:** プラン制限超過またはアクセス権限不足 ```json { "statusCode": 403, "message": "プラン情報が見つからないため、この操作は許可されていません", "error": "Forbidden" } ``` **対処法:** - ワークスペースのプラン情報を確認(管理画面の **ワークスペース設定 → プラン**) - 月間ファイル作成回数の上限超過の場合は `今月のファイル作成回数上限(X)に達しました。プランをアップグレードするか、翌月までお待ちください。` メッセージが返るので、プランをアップグレードするか翌月まで待つ #### 412 Precondition Failed **原因:** 認証方式ヘッダー(`appkey`)が欠落 ```json { "statusCode": 412, "message": "認証方式ヘッダーが存在しません", "error": "Precondition Failed" } ``` **対処法:** - `appkey` ヘッダーが設定されているか確認 #### 413 Payload Too Large **原因:** リクエストサイズが50MBを超過 ```json { "statusCode": 413, "message": "Payload Too Large", "error": "Request Entity Too Large" } ``` **対処法:** - 画像を最適化する(解像度・圧縮率の調整) - リクエストを分割する - 詳細は[制限事項](../limitations.md#-リクエストサイズ制限)を参照 #### 429 Too Many Requests **原因:** Rate Limit超過(Workspace単位で適用) ```http HTTP/1.1 429 Too Many Requests Retry-After: 60 Content-Type: application/json { "statusCode": 429, "message": "Rate limit exceeded for this workspace. Please try again in 60 seconds.", "error": "Too Many Requests" } ``` **`Retry-After` ヘッダ:** RFC 9110 §10.2.3 に準拠し、再試行まで待つべき秒数を整数で返します。クライアントはエラーメッセージをパースする代わりにこのヘッダの値だけ待てば十分です。本文の数値もこのヘッダと同じ値です(人間可読版)。 **対処法:** - `Retry-After` ヘッダの秒数だけ待ってから再送する - 同期エンドポイント: 30 req/min、非同期エンドポイント: 100 req/min - バッチ処理の場合は非同期エンドポイントを使用 - 詳細は[制限事項](../limitations.md#-rate-limit)を参照 ### 5xx サーバーエラー #### 500 Internal Server Error **原因:** サーバー内部エラー ```json { "statusCode": 500, "message": "Internal server error", "error": "Internal Server Error" } ``` **対処法:** - リトライ処理を実装 - 継続する場合はサポートに連絡 ## エラーハンドリングのベストプラクティス ### 1. 適切なエラーキャッチ ```javascript async function generatePDF(params) { try { const response = await axios.post(url, data, config); return response.data; } catch (error) { if (error.response) { // サーバーがエラーレスポンスを返した handleApiError(error.response); } else if (error.request) { // リクエストは送信されたがレスポンスがない console.error('ネットワークエラー:', error.message); throw new Error('サーバーに接続できません'); } else { // リクエスト設定時のエラー console.error('リクエストエラー:', error.message); throw error; } } } function handleApiError(response) { const { status, data } = response; switch (status) { case 400: console.error('バリデーションエラー:', data.message); throw new Error(`リクエストが不正です: ${data.message}`); case 401: console.error('認証エラー'); throw new Error('APIキーを確認してください'); case 404: console.error('リソースが見つかりません'); throw new Error('指定されたリソースが存在しません'); case 500: console.error('サーバーエラー'); throw new Error('一時的なエラーです。しばらく待ってから再試行してください'); default: console.error('予期しないエラー:', data); throw new Error(`エラー (${status}): ${data.message}`); } } ``` ### 2. リトライロジック 一時的なサーバーエラー(5xx)に対してリトライを実装します。 ```javascript async function generatePDFWithRetry(params, options = {}) { const { maxRetries = 3, initialDelay = 1000, maxDelay = 10000, backoffFactor = 2 } = options; let lastError; for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await generatePDF(params); } catch (error) { lastError = error; // リトライ可能なエラーか判定 if (!isRetryableError(error)) { throw error; } // 最後の試行ではリトライしない if (attempt === maxRetries - 1) { break; } // 指数バックオフで待機 const delay = Math.min( initialDelay * Math.pow(backoffFactor, attempt), maxDelay ); console.log(`リトライ ${attempt + 1}/${maxRetries} (${delay}ms後)`); await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError; } function isRetryableError(error) { if (!error.response) { // ネットワークエラーはリトライ可能 return true; } const { status } = error.response; // 5xxエラーのみリトライ return status >= 500 && status < 600; } ``` ### 3. タイムアウト処理 ```javascript async function generatePDFWithTimeout(params, timeout = 120000) { return Promise.race([ generatePDF(params), new Promise((_, reject) => setTimeout(() => reject(new Error('タイムアウト')), timeout) ) ]); } ``` ### 4. エラーロギング ```javascript function logError(error, context) { const errorLog = { timestamp: new Date().toISOString(), context, error: { message: error.message, stack: error.stack, response: error.response ? { status: error.response.status, data: error.response.data } : null } }; console.error(JSON.stringify(errorLog)); // エラートラッキングサービスに送信(例: Sentry) // Sentry.captureException(error, { contexts: { custom: context } }); } // 使用例 try { await generatePDF(params); } catch (error) { logError(error, { operation: 'generatePDF', designId: params.designId, fileName: params.fileName }); throw error; } ``` ## Python実装例 ```python import requests import time from typing import Optional, Dict, Any class ReportFlowError(Exception): """Report Flow API エラーの基底クラス""" def __init__(self, status_code: int, message: str, error: str): self.status_code = status_code self.message = message self.error = error super().__init__(f"{error} ({status_code}): {message}") class ValidationError(ReportFlowError): """バリデーションエラー (400)""" pass class AuthenticationError(ReportFlowError): """認証エラー (401)""" pass class NotFoundError(ReportFlowError): """リソースが見つからない (404)""" pass class ServerError(ReportFlowError): """サーバーエラー (5xx)""" pass def generate_pdf_with_retry(params: Dict[str, Any], max_retries: int = 3) -> bytes: """リトライ付きPDF生成""" for attempt in range(max_retries): try: return generate_pdf(params) except ServerError as e: if attempt == max_retries - 1: raise delay = 1000 * (2 ** attempt) # 指数バックオフ time.sleep(delay / 1000) print(f"リトライ {attempt + 1}/{max_retries}") except ReportFlowError: # サーバーエラー以外はリトライしない raise def generate_pdf(params: Dict[str, Any]) -> bytes: """PDF生成""" 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'], 'params': params['data'] } } try: response = requests.post(url, headers=headers, json=data, timeout=120) response.raise_for_status() return response.content except requests.exceptions.HTTPError as e: handle_http_error(e.response) except requests.exceptions.Timeout: raise ServerError(504, 'リクエストがタイムアウトしました', 'Gateway Timeout') except requests.exceptions.RequestException as e: raise ServerError(500, str(e), 'Network Error') def handle_http_error(response): """HTTPエラーを適切な例外に変換""" try: error_data = response.json() status = error_data.get('statusCode', response.status_code) message = error_data.get('message', 'Unknown error') error = error_data.get('error', 'Error') except ValueError: status = response.status_code message = response.text error = 'Unknown Error' if status == 400: raise ValidationError(status, message, error) elif status == 401: raise AuthenticationError(status, message, error) elif status == 404: raise NotFoundError(status, message, error) elif 500 <= status < 600: raise ServerError(status, message, error) else: raise ReportFlowError(status, message, error) # 使用例 try: pdf_data = generate_pdf_with_retry({ 'app_key': 'your-app-key', 'design_id': '660e8400-e29b-41d4-a716-446655440000', 'version': 1, 'file_name': 'invoice.pdf', 'data': { 'customerName': '山田太郎', 'invoiceNumber': 'INV-2024-001' } }) with open('invoice.pdf', 'wb') as f: f.write(pdf_data) except ValidationError as e: print(f"バリデーションエラー: {e.message}") except AuthenticationError as e: print(f"認証エラー: APIキーを確認してください") except NotFoundError as e: print(f"リソースが見つかりません: {e.message}") except ServerError as e: print(f"サーバーエラー: しばらく待ってから再試行してください") except ReportFlowError as e: print(f"エラー: {e}") ``` ## トラブルシューティング ### よくあるエラーと対処法 | エラー | 原因 | 対処法 | |--------|------|--------| | `designId must be a UUID` | デザインIDの形式が不正 | UUID形式(例: `550e8400-e29b-41d4-a716-446655440000`)を確認 | | `ファイル名に使用できない文字が含まれています` | ファイル名に `/ \ : * ? " < > \|` または制御文字を含む | これらの文字を除去または置換 | | `認証情報が不正です` | 認証失敗 | アプリケーションキーを確認 | | `認証方式ヘッダーが存在しません` | `appkey` ヘッダー欠落 | リクエストに `appkey: <キー>` ヘッダーを追加 | ## 次のステップ - [PDF生成ガイド](./pdf-generation) - [非同期ワークフロー](./async-workflows) --- # docs/guides/pdf-generation.md # PDF生成ガイド Report Flow APIを使用したPDF生成の詳細ガイドです。 ## 概要 PDF生成には2つのモードがあります: | モード | エンドポイント | レスポンス | 用途 | |--------|--------------|-----------|------| | 同期生成 | `/file/sync/single` | PDFバイナリ | 即座に結果が必要な場合 | | 非同期生成 | `/file/async/single` | `requestId` / `url` / `files` 配列 (202 Accepted) | 大量生成やバックグラウンド処理 | ## 同期生成 ### 基本的な使い方 ```bash 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", "params": { "customerName": "山田太郎", "invoiceNumber": "INV-2024-001", "items": [ { "name": "商品A", "price": 1000, "quantity": 2 } ] } } }' \ --output invoice.pdf ``` ### レスポンスヘッダー ```http Content-Type: application/pdf Content-Disposition: attachment; filename="invoice.pdf" Content-Length: 123456 Request-Id: 550e8400-e29b-41d4-a716-446655440000 File-URL: https://api.re-port-flow.com/v1/file/download/{requestId} X-File-Mapping: %5B%7B%22fileId%22%3A...%7D%5D ``` `X-File-Mapping` は **URL エンコード済みの JSON 配列** で、`fileId` / `fileName` / `share` 情報を含みます。クライアント側で `decodeURIComponent` してから `JSON.parse` してください。詳細は [単一PDF同期生成エンドポイント](./endpoints/sync-single.md) を参照。 ### JavaScript実装例 ```javascript 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, 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']; const fileMapping = JSON.parse( decodeURIComponent(response.headers['x-file-mapping'] || '%5B%5D'), ); return { 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', data: { customerName: '山田太郎', invoiceNumber: 'INV-2024-001', items: [ { name: '商品A', price: 1000, quantity: 2 } ] } }); ``` ## 非同期生成 大量のPDFを生成する場合や、タイムアウトを避けたい場合は非同期生成を使用します。 ### 基本的な使い方 ```bash # 1. 生成リクエスト curl -X POST https://api.re-port-flow.com/v1/file/async/single \ -H "appkey: your-application-key" \ -H "Content-Type: application/json" \ -d '{ "designId": "550e8400-e29b-41d4-a716-446655440000", "version": 1, "content": { "fileName": "invoice.pdf", "params": {...} } }' # レスポンス例 (202 Accepted) { "requestId": "550e8400-e29b-41d4-a716-446655440000", "url": "https://api.re-port-flow.com/v1/file/download/{requestId}", "files": [ { "fileName": "invoice.pdf", "fileId": "7f3d1a2b-4c5e-6f7a-8b9c-0d1e2f3a4b5c", "share": { "shareType": "workspace", "url": "https://app.re-port-flow.com/file/{requestId}/{fileId}", "passcodeEnabled": false } } ] } ``` 詳細は [単一PDF非同期生成エンドポイント](./endpoints/async-single.md) を参照。 ### JavaScript実装例(ポーリング) ```javascript async function generatePDFAsync(params) { // 1. 非同期生成リクエスト const response = await axios.post( 'https://api.re-port-flow.com/v1/file/async/single', { designId: params.designId, version: params.version, content: { fileName: params.fileName, params: params.data } }, { headers: { 'appkey': process.env.APP_KEY, 'Content-Type': 'application/json' } } ); const { requestId, url, files } = response.data; // 2. ZIP ダウンロードURL (url) からまとめて、または個別ファイルを取得 // 個別取得: GET /v1/file/download/{requestId}/{fileId} const pdfResponse = await axios.get(url, { headers: { 'appkey': process.env.APP_KEY }, responseType: 'arraybuffer', }); return { data: pdfResponse.data, requestId, url, files, }; } ``` ## 複数PDF生成(ZIP) 複数のPDFを一度に生成してZIPで取得できます。 ### 同期生成 ```javascript async function generateMultiplePDFs(designId, contents) { const response = await axios.post( 'https://api.re-port-flow.com/v1/file/sync/multiple', { designId, version: 1, contents // ContentDto の配列 }, { headers: { 'appkey': process.env.APP_KEY }, responseType: 'arraybuffer' } ); // X-File-Mapping ヘッダーからファイルマッピングを取得 const fileMapping = JSON.parse(response.headers['x-file-mapping']); console.log('生成されたファイル:', fileMapping); return response.data; // ZIP binary } // 使用例 const contents = [ { fileName: 'invoice_001.pdf', params: { customerName: '山田太郎', invoiceNumber: 'INV-001' } }, { fileName: 'invoice_002.pdf', params: { customerName: '佐藤花子', invoiceNumber: 'INV-002' } } ]; const zipData = await generateMultiplePDFs('550e8400-...', contents); fs.writeFileSync('invoices.zip', zipData); ``` ## パラメータの構造 ### デザインパラメータの取得 生成前に、デザインで使用可能なパラメータ構造を確認できます: ```bash curl -X GET https://api.re-port-flow.com/v1/file/design/parameter/{designId}?version=1 \ -H "appkey: your-application-key" ``` **レスポンス例:** ```json { "customerName": "string", "invoiceNumber": "string", "amount": "number", "items": [ { "name": "string", "price": "number", "quantity": "number" } ], "issueDate": "date" } ``` ### パラメータ型の対応 | 型 | 説明 | 例 | |----|------|-----| | `string` | 文字列 | `"山田太郎"` | | `number` | 数値 | `1000` | | `date` | 日付(ISO 8601) | `"2024-02-12"` | | `object` | ネストしたオブジェクト | `{ "name": "値" }` | | `array` | 配列 | `[{ "item": 1 }]` | ## エラーハンドリング ### 一般的なエラー #### 400 Bad Request - バリデーションエラー ```json { "statusCode": 400, "message": [ "designId must be a UUID", "ファイル名に使用できない文字が含まれています(/ \\ : * ? \" < > | および制御文字は使用不可)" ], "error": "Bad Request" } ``` **対処法:** - リクエストボディを確認 - ファイル名の形式を確認(`/ \ : * ? " < > |` および制御文字は使用不可) #### 500 Internal Server Error ```json { "statusCode": 500, "message": "Internal server error", "error": "Internal Server Error" } ``` **対処法:** - 一時的なサーバーエラーの可能性 - リトライロジックを実装 - 継続する場合はサポートに連絡 ### リトライ戦略 ```javascript 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) { // 指数バックオフでリトライ await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i))); continue; } throw error; } } } ``` ## ベストプラクティス ### 1. タイムアウト設定 同期生成は120秒でタイムアウトします。大きなPDFの場合は非同期生成を使用してください。 ```javascript // axios でタイムアウト設定 const response = await axios.post(url, data, { timeout: 120000 // 120秒 }); ``` ### 2. ファイル名のサニタイゼーション ```javascript function sanitizeFileName(fileName) { // / \ : * ? " < > | および制御文字を除去 return fileName.replace(/[\/\\:*?"<>|\x00-\x1F]/g, '_'); } ``` ### 3. パラメータの検証 ```javascript function validateParams(params, schema) { // デザインパラメータスキーマと照合 for (const [key, type] of Object.entries(schema)) { if (!(key in params)) { throw new Error(`必須パラメータ ${key} が不足しています`); } // 型チェック等 } } ``` ## 次のステップ - [非同期ワークフローガイド](./async-workflows) - [エラーハンドリング](./error-handling) - [MCP Server](../integrations/mcp) — Claude / Cursor / VS Code から PDF 生成 - [n8n Integration](../integrations/n8n) — ノーコードワークフロー連携 --- # docs/guides/webhooks.md # Webhook通知 PDF生成完了時に自動的にWebhook通知を受け取ることができます。これにより、ポーリング不要でリアルタイムに完了を検知し、メール送信などのワークフローを自動化できます。 ## 概要 Webhook通知は、以下のすべてのPDF生成エンドポイントで自動的に送信されます: - `POST /file/sync/single` - 同期単一ファイル生成 - `POST /file/sync/multiple` - 同期複数ファイル生成 - `POST /file/async/single` - 非同期単一ファイル生成 - `POST /file/async/multiple` - 非同期複数ファイル生成 ## Webhook URLの設定 Webhook URLを設定するには、**ReportFlowのワークスペース設定画面**から行います: 1. ワークスペース設定を開く 2. 「開発者」タブに移動 3. 「Webhook URL」フィールドに、通知を受け取るHTTPS URLを入力 4. 「更新」をクリック ## Webhook通知ペイロード PDF生成が完了すると、設定されたWebhook URLに以下のペイロードがPOSTされます。 ### ペイロード形式 ```json { "event": "file.completed", "timestamp": "2026-02-15T10:30:45.123Z", "workspaceId": "ws_abc123", "requestId": "550e8400-e29b-41d4-a716-446655440000", "designId": "design_123", "version": 5, "files": [ { "fileId": "7f3d1a2b-4c5e-6f7a-8b9c-0d1e2f3a4b5c", "fileName": "請求書.pdf", "passthrough": { "pageId": "abc123" }, "share": { "shareType": "workspace", "url": "https://app.re-port-flow.com/file/{requestId}/{fileId}", "passcodeEnabled": false } } ] } ``` ### フィールド説明 | フィールド | 型 | 説明 | |-----------|---|------| | `event` | string | 固定値: `"file.completed"` | | `timestamp` | string | イベント発生時刻 (ISO 8601形式) | | `workspaceId` | string | ワークスペースID(参照用) | | `requestId` | string | リクエストID(ダウンロードエンドポイントで使用) | | `designId` | string | デザインID | | `version` | number | バージョン番号 | | `files` | array | 生成ファイル情報の配列 | | `files[].fileId` | string | ファイルID(個別ダウンロードエンドポイントで使用) | | `files[].fileName` | string | ファイル名(拡張子付き) | | `files[].passthrough` | object | リクエスト時に指定した `passthrough` の値(指定時のみ) | | `files[].share.shareType` | string | 共有タイプ(`workspace` / `invited` / `public`) | | `files[].share.url` | string | ファイル表示URL | | `files[].share.passcodeEnabled` | boolean | パスコード有効フラグ | | `files[].share.passcode` | string | サーバー生成パスコード(`passcodeEnabled=true` かつ生成直後のみ) | :::info passthrough の使い所 ReportFlow はセキュリティとペイロードサイズの観点から、レスポンスや Webhook 通知に `params`(生成データ)を返しません。 受信側で「どの業務レコードに対する PDF か」を識別したい場合は、 リクエスト時に `passthrough` に自社DBのIDなどを入れてください。 レスポンスや Webhook でそのまま返却されます。 ```json { "fileName": "invoice.pdf", "passthrough": { "invoiceId": "INV-001", "tenantId": "acme" }, "params": { "customerName": "山田太郎", "amount": 10000 } } ``` Webhook 受信時に `passthrough` の値がそのまま返るため、`invoiceId` から DB を引いて該当レコードを更新する、といった処理が組めます。 `params`(顧客名や金額)は外部に送信されません。 ::: ## PDFのダウンロード Webhook通知のペイロードから、以下の情報を使ってPDFをダウンロードできます: - `requestId`: ペイロードの `requestId`(ZIP一括ダウンロード用) - `fileId`: ペイロードの `files[].fileId`(個別ダウンロード用) ``` # ZIP一括ダウンロード GET /v1/file/download/{requestId} # 個別ファイルダウンロード GET /v1/file/download/{requestId}/{fileId} ``` 詳細は[ファイルダウンロード](./endpoints/download.md)を参照してください。 ## 実装例 ### Node.js (Express) ```javascript import express from 'express'; import axios from 'axios'; const app = express(); app.use(express.json()); app.post('/webhooks/pdf-completed', async (req, res) => { const payload = req.body; // イベント検証 if (payload.event !== 'file.completed') { return res.status(400).json({ error: 'Unknown event' }); } console.log(`PDF生成完了: ${payload.files.length}件`); // 各ファイルをダウンロード for (const file of payload.files) { const downloadUrl = `https://api.re-port-flow.com/v1/file/download/${payload.requestId}/${file.fileId}`; try { // PDFダウンロード const pdfResponse = await axios.get(downloadUrl, { headers: { 'appkey': process.env.APP_KEY }, responseType: 'arraybuffer' }); const pdfBuffer = Buffer.from(pdfResponse.data); // 送信先などの業務メタデータは、リクエスト時に content.passthrough に // 入れて Webhook で受け取る。Webhook ペイロードに params (顧客名や金額) // は返らないため、params から引くサンプルは動かない。 // 例: リクエスト content.passthrough = { recipientEmail, invoiceId } await sendEmailWithAttachment({ to: file.passthrough?.recipientEmail, subject: `PDFファイル: ${file.fileName}`, attachments: [{ filename: file.fileName, content: pdfBuffer }] }); console.log(`メール送信完了: ${file.fileName}`); } catch (error) { console.error(`ファイルダウンロードエラー: ${file.fileName}`, error.message); } } // 200 OKを返す res.status(200).json({ received: true }); }); app.listen(3000, () => { console.log('Webhook server listening on port 3000'); }); ``` ### Python (Flask) ```python 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 # イベント検証 if payload.get('event') != 'file.completed': return jsonify({'error': 'Unknown event'}), 400 print(f"PDF生成完了: {len(payload['files'])}件") # 各ファイルをダウンロード request_id = payload['requestId'] for file_info in payload['files']: download_url = f"https://api.re-port-flow.com/v1/file/download/{request_id}/{file_info['fileId']}" try: # PDFダウンロード pdf_response = requests.get( download_url, headers={ 'appkey': os.getenv('APP_KEY') } ) pdf_response.raise_for_status() # ファイルに保存 with open(file_info['fileName'], 'wb') as f: f.write(pdf_response.content) print(f"ダウンロード完了: {file_info['fileName']}") except Exception as e: print(f"エラー: {file_info['fileName']}, {str(e)}") # 200 OKを返す return jsonify({'received': True}), 200 if __name__ == '__main__': app.run(port=3000) ``` ## セキュリティベストプラクティス ### 1. HTTPS URLを使用 Webhook URLは必ずHTTPSを使用してください。HTTP URLは拒否されます。 ### 2. 署名検証の実装(HMAC-SHA256) ReportFlow は Webhook ペイロードに HMAC-SHA256 署名を付与します。受信側で署名を検証することで、第三者によるなりすましリクエストを拒否できます。 #### 署名鍵(Webhook Secret)の取得 ワークスペースごとに発行される `whsec_` プレフィックス付きの秘密鍵を使用します。 - **UI から取得**: ワークスペース設定 > 開発者タブ - **API から再生成**: `POST /workspace/:workspaceId/webhook/secret/regenerate` - **API から取得**: `GET /workspace/:workspaceId/webhook/secret` 再生成すると既存のシークレットは即座に無効化されます。 #### 署名ヘッダの形式 ``` X-Report-Flow-Signature: t=1739610645,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd ``` - `t`: Unix タイムスタンプ(秒)。リプレイ攻撃対策として、現在時刻との差が 5 分以内であることを推奨。 - `v1`: HMAC-SHA256 署名。署名対象は `.`(タイムスタンプとリクエストボディを `.` で連結)。 > **重要**: 署名対象は **生のリクエストボディ(パース前)** です。JSON パース後にシリアライズし直すとキー順序や空白が変わって署名が一致しなくなります。 #### 検証サンプル(Node.js) ```javascript import crypto from 'crypto'; import express from 'express'; const app = express(); app.use(express.raw({ type: 'application/json' })); // 生のボディを保持 app.post('/webhook', (req, res) => { const sigHeader = req.header('X-Report-Flow-Signature') || ''; const parts = Object.fromEntries( sigHeader .split(',') .map((kv) => kv.split('=')) .filter(([, v]) => v !== undefined), ); const timestamp = parts.t; const signature = parts.v1; if (!timestamp || !signature) { return res.status(400).send('Missing signature components'); } // 5分以内のタイムスタンプか確認 if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) { return res.status(400).send('Stale timestamp'); } const expected = crypto .createHmac('sha256', process.env.REPORT_FLOW_WEBHOOK_SECRET) .update(`${timestamp}.${req.body.toString()}`) .digest('hex'); // timingSafeEqual は長さが違うと TypeError を投げるので先に長さを揃える const expectedBuf = Buffer.from(expected); const signatureBuf = Buffer.from(signature); if ( expectedBuf.length !== signatureBuf.length || !crypto.timingSafeEqual(expectedBuf, signatureBuf) ) { return res.status(400).send('Invalid signature'); } const payload = JSON.parse(req.body.toString()); console.log('Verified webhook:', payload.event); res.status(200).send('OK'); }); ``` #### 検証サンプル(Python / Flask) ```python import hmac, hashlib, os, time from flask import Flask, request, abort app = Flask(__name__) SECRET = os.environ['REPORT_FLOW_WEBHOOK_SECRET'] @app.post('/webhook') def webhook(): raw = request.get_data() # 生バイト列 sig_header = request.headers.get('X-Report-Flow-Signature', '') parts = dict( kv.split('=', 1) for kv in sig_header.split(',') if '=' in kv ) timestamp, signature = parts.get('t'), parts.get('v1') if not timestamp or not signature: abort(400, 'Missing signature components') if abs(time.time() - int(timestamp)) > 300: abort(400, 'Stale timestamp') # raw はバイナリのまま結合する (UTF-8 でない可能性に備えて decode しない) signed_payload = f'{timestamp}.'.encode() + raw expected = hmac.new(SECRET.encode(), signed_payload, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, signature): abort(400, 'Invalid signature') return 'OK', 200 ``` #### 検証サンプル(Go) ```go package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "io" "net/http" "os" "strconv" "strings" "time" ) var secret = os.Getenv("REPORT_FLOW_WEBHOOK_SECRET") func handler(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) parts := map[string]string{} for _, kv := range strings.Split(r.Header.Get("X-Report-Flow-Signature"), ",") { if i := strings.Index(kv, "="); i > 0 { parts[kv[:i]] = kv[i+1:] } } if parts["t"] == "" || parts["v1"] == "" { http.Error(w, "missing signature components", 400) return } ts, err := strconv.ParseInt(parts["t"], 10, 64) if err != nil { http.Error(w, "invalid timestamp", 400) return } if diff := time.Now().Unix() - ts; diff > 300 || diff < -300 { http.Error(w, "stale timestamp", 400) return } mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(parts["t"] + ".")) mac.Write(body) expected := hex.EncodeToString(mac.Sum(nil)) // hmac.Equal は長さ不一致でも安全に false を返す if !hmac.Equal([]byte(expected), []byte(parts["v1"])) { http.Error(w, "invalid signature", 400) return } w.WriteHeader(200) } ``` #### 後方互換について シークレット未生成のワークスペースには `X-Report-Flow-Signature` ヘッダは付与されません。署名検証を導入する受信側は、まず開発者タブでシークレットを生成してください。 ### 3. タイムアウト設定 Webhookエンドポイントは5秒以内に応答することを推奨します。重い処理(メール送信、データベース書き込みなど)は非同期ジョブキューで実行してください。 ### 4. URLに認証情報を含めない Webhook URLのクエリパラメータに認証トークンやシークレットを含めないでください。認証が必要な場合は、Headerで送信するか、別途管理してください。 ## リトライ動作 Webhookエンドポイントが以下のステータスコードを返した場合、ReportFlowは自動的にリトライします: - **200-299**: 成功(リトライしない) - **400-499**: クライアントエラー(リトライしない) - **500-599**: サーバーエラー(**リトライする**) リトライは最大3回まで行われます。すべてのリトライが失敗しても、PDF生成処理自体は成功扱いとなります。 ## トラブルシューティング ### 通知が届かない 以下を確認してください: 1. **Webhook URLが設定されているか** - ワークスペース設定 > 開発者タブで確認 2. **HTTPSを使用しているか** - HTTP URLは拒否されます 3. **公開URLを使用しているか** - `localhost`やプライベートIPアドレスは拒否されます - テスト環境では[webhook.site](https://webhook.site)などのサービスを利用してください 4. **エンドポイントが200を返すか** - エラーステータスコード(4xx/5xx)を返している場合、リトライされます - エンドポイントのログを確認してください 通知が届かない場合は、サポートにお問い合わせください。 ## 次のステップ - [非同期ワークフロー](./async-workflows.md) - Webhook通知を活用した大量生成フロー - [エラーハンドリング](./error-handling.md) - エラー時の対処方法 - [ファイルダウンロード](./endpoints/download.md) - PDFファイルのダウンロード方法 --- # docs/integrations/mcp.mdx import Head from '@docusaurus/Head'; # MCP Server (Claude / Cursor / VS Code) [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) に対応した公式サーバー `reportflow-mcp` を提供しています。Claude Desktop / Claude Code / Cursor / VS Code などの MCP 対応 AI クライアントから、自然言語で Report Flow テンプレートを呼び出して PDF を生成できます。 - ホスト型エンドポイント(npm 不要): `https://mcp.re-port-flow.com/mcp` - npm: [`reportflow-mcp`](https://www.npmjs.com/package/reportflow-mcp) - ソース: [github.com/re-port-flow/reportflow-mcp](https://github.com/re-port-flow/reportflow-mcp) - MCP Registry: [`io.github.re-port-flow/reportflow-mcp`](https://registry.modelcontextprotocol.io/v0/servers?search=reportflow) ## なぜ Report Flow MCP か 帳票生成系の MCP サーバーは複数存在しますが、Report Flow MCP は次の 3 点で他のサーバーと差別化されます。 1. **リモート対応・セットアップ不要** — `https://mcp.re-port-flow.com/mcp` にホスト型エンドポイントを提供しており、npm / Node.js のインストール無しで Claude.ai (Web) からそのまま接続できます。ローカル MCP サーバー(stdio)のみを提供する他社の帳票 MCP サーバーと異なり、開発者でないビジネスユーザーでも使い始められます。 2. **ノーコード設計 + テンプレートマーケットプレイス** — テンプレートはブラウザベースの GUI エディタ(Konva ベース)でノーコードに設計できます。さらに [テンプレートギャラリー](https://templates.re-port-flow.com) に請求書・見積書・領収書・納品書・レポートなどのフリーテンプレートが揃っており、AI エージェントは初日からそれらを呼び出して PDF 生成できます。テンプレートを作るために JSON スキーマを書く必要はありません。 3. **OAuth 2.0 + 動的クライアント登録** — AI クライアントに API キーを渡さない、OAuth 2.0(Authorization Code + PKCE)+ Dynamic Client Registration ベースの認証を採用しています。トークンはローカル実行時は OS のキーチェーン、リモートエンドポイント利用時はサーバー側で管理されます。Claude.ai の 1st party Connectors と同等のセキュリティモデルです。 ## できること - 「Acme 社向けに合計 ¥33,000 の請求書を作って」のような自然言語から PDF を生成 - ワークスペースのデザインとパラメータスキーマを MCP **Resources** として AI に直接公開 - 複数 PDF を一括生成し ZIP で受け取り - 出力ファイルを、AI クライアントが現在開いているワークスペースフォルダに保存 ## 動作要件 - Node.js 18 以上(`npx` 実行時に自動取得) - 初回ログイン時にブラウザを開ける環境(ローカルマシン推奨) - [Report Flow](https://re-port-flow.com) アカウント 上記は **ローカル実行(npx)** の場合の要件です。**リモート(ホスト型)サーバー**を使う場合は Node.js のインストールやローカルのブラウザ/キーチェーンは不要で、必要なのは Report Flow アカウントと、リモート MCP に対応した AI クライアントだけです。 ## セットアップ セットアップには 2 つの方法があります。**ローカル実行(npx)が推奨**で、生成した PDF をローカルのワークスペースフォルダに保存できます。Node.js を入れたくない場合や、Claude.ai(Web)などブラウザベースのクライアントから使う場合は、**リモート(ホスト型)サーバー**を利用できます。 ### ローカル実行(npx・推奨) #### Claude Desktop / Claude Code / Cursor 設定ファイル(`.mcp.json` / `claude_desktop_config.json` / `~/.cursor/mcp.json` など)に以下を追加します。 ```json { "mcpServers": { "reportflow": { "command": "npx", "args": ["-y", "reportflow-mcp"] } } } ``` API キーや環境変数の設定は不要です。OAuth で認証するため、シークレット管理は AI クライアント側で行いません。 #### VS Code(MCP 対応ビルド) VS Code は top-level キーが `servers` です(Claude / Cursor の `mcpServers` とは異なります)。`.vscode/mcp.json` に以下を配置してください。 ```json { "servers": { "reportflow": { "command": "npx", "args": ["-y", "reportflow-mcp"] } } } ``` ### リモート(ホスト型)サーバー npm / Node.js のインストールは不要です。ホスト型エンドポイントに URL で接続します。Claude.ai(Web)やリモート MCP に対応したクライアントに適しています。認証はクライアント側のブラウザで OAuth(動的クライアント登録)が実行され、トークンはサーバー側で管理されるため、ローカルのキーチェーンは使いません。 エンドポイント: ``` https://mcp.re-port-flow.com/mcp ``` HTTP トランスポートに対応したクライアント(Claude Code / Cursor など)では、設定ファイルに以下を追加します。 ```json { "mcpServers": { "reportflow": { "type": "http", "url": "https://mcp.re-port-flow.com/mcp" } } } ``` VS Code(`.vscode/mcp.json`)では top-level キーが `servers` です。 ```json { "servers": { "reportflow": { "type": "http", "url": "https://mcp.re-port-flow.com/mcp" } } } ``` Claude Code の CLI では次のコマンドでも追加できます。 ```bash claude mcp add --transport http reportflow https://mcp.re-port-flow.com/mcp ``` Claude.ai(Web / Desktop)では、設定の **Connectors(カスタムコネクタ)** から上記 URL を登録してください。接続時にブラウザで OAuth の同意画面が表示されます。 :::note ローカル実行との違い リモートサーバーはユーザーのファイルシステムにアクセスできないため、生成した PDF はローカルのワークスペースフォルダには保存されず、ダウンロード用 URL として返されます。生成物をローカルへ自動保存したい場合は、ローカル実行(npx)を利用してください。 ::: ## 初回認証 クライアントを再起動した後、AI に次のように依頼します。 > ReportFlow にログインして ブラウザが開くので **サインイン → ワークスペースを選択 → 同意** で完了です。アクセストークンは OS のキーチェーン(macOS Keychain / Windows Credential Manager / Linux libsecret)に保存され、自動でリフレッシュされます。 :::tip キーチェーンが使えない環境 Linux で libsecret が利用できない場合は、`$XDG_STATE_HOME/reportflow-mcp/` 以下に chmod 0600 のファイルとして自動的にフォールバック保存されます。 ::: :::note リモート(ホスト型)サーバーの場合 上記の手順はローカル実行(npx)向けです。リモートサーバーでは、クライアントが接続したタイミングで OAuth の同意フローが自動的に開始され、トークンはサーバー側で管理されます。AI への「ログインして」という依頼や、ローカルのキーチェーンは不要です。 ::: ## 使い方 ### 1. 自然言語で PDF 生成 ``` 請求書テンプレートで、Acme 社向けに合計 ¥33,000 の PDF を作成して ``` AI は内部で以下を実行します。 1. `list_templates` で利用可能なデザインを検索 2. `get_design_parameters` でパラメータスキーマを取得 3. ユーザーの指示から `params` を組み立て 4. `generate_pdf_sync` で PDF を生成し、ローカルファイルパスを返す ### 2. スラッシュコマンド(プロンプトテンプレート) | コマンド | 用途 | |---|---| | `/generate_pdf` | 単一 PDF 生成のステップバイステップレシピ | | `/generate_pdfs` | 一括 PDF 生成のレシピ | | `/reportflow_help` | 機能ツアー | ### 3. 出力ファイルの保存先 以下の優先順位で解決されます。 1. ユーザーが明示した場所(例: 「デスクトップに保存して」) 2. AI クライアントが開いているワークスペースのルート(Claude Code / Cursor / VS Code) 3. OS の一時ディレクトリ(フォールバック) ## リファレンス ### Tools(AI が呼び出す) | Tool | 用途 | |---|---| | `authenticate` | 初回認証・再認証 | | `list_templates` | 利用可能なデザインの一覧 | | `get_design_parameters` | デザインのパラメータスキーマ取得 | | `generate_pdf_sync` / `generate_pdf_async` | 単一 PDF 生成(同期はパス、非同期は requestId) | | `generate_pdfs_sync` / `generate_pdfs_async` | 複数 PDF 生成(ZIP で返す) | | `download_file` / `download_zip` | 非同期生成結果のダウンロード | | `suggest_params` | 自然言語ブリーフから `params` を MCP Sampling で生成(Sampling 対応クライアント必須) | ### Resources(AI のコンテキストに添付) | URI | 内容 | |---|---| | `reportflow://designs` | 利用可能なデザインの一覧 | | `reportflow://designs/{designId}/parameters` | 1 デザインのパラメータスキーマ | | `reportflow://errors` | Content Service のエラーメッセージカタログ | | `reportflow://server-info` | サーバー機能の概要 | ### Prompts `/generate_pdf`, `/generate_pdfs`, `/reportflow_help` — 引数を渡すと AI が用意されたワークフローに沿って動作します。 ## 関連エンドポイント MCP サーバー内部では Report Flow API を呼び出しています。詳細は API リファレンスを参照してください。 - [Single PDF Sync Generation](../guides/endpoints/sync-single.md) - [Single PDF Async Generation](../guides/endpoints/async-single.md) - [Multiple PDF Sync Generation](../guides/endpoints/sync-multiple.md) - [Multiple PDF Async Generation](../guides/endpoints/async-multiple.md) - [File Download](../guides/endpoints/download.md) - [Design Parameters](../guides/endpoints/design-parameters.md) ## トラブルシューティング ### `re-authentication required` を含むエラー トークンが失効しています。AI に「ReportFlow に再ログインして」と依頼してください。 ### `npx` がパッケージを見つけられない ```bash npm cache clean --force ``` を実行してから再試行してください。 ### Linux でキーチェーンが使えない libsecret が無い環境では、自動的に `$XDG_STATE_HOME/reportflow-mcp/` 以下のファイル(chmod 0600)にフォールバックします。 ### SSH / リモートシェルでブラウザが開けない 初回認証だけは **ローカルマシン上で** 行ってください。一度ログインに成功すれば、保存されたトークンはそのままリモートホストへ持ち込めます(同じ OS のキーチェーンエントリ、または上記のフォールバックファイルを移送)。 ### `Rate limit exceeded` (429) ワークスペース単位の API レート制限に達しています。`Retry-After` ヘッダの秒数だけ待ってから再試行してください。一括生成では非同期エンドポイントの利用を推奨します。 ## サポート - バグ報告・要望: [GitHub Issues](https://github.com/re-port-flow/reportflow-mcp/issues) - API 全般: [Report Flow API ドキュメント](../intro.md) ## 次のステップ - [非同期ワークフロー設計](../guides/async-workflows.md) — 大量生成のベストプラクティス - [Webhook 通知](../guides/webhooks.md) — 完了通知と HMAC-SHA256 検証 - [n8n インテグレーション](./n8n.md) — ローコードワークフロー連携 --- # docs/integrations/n8n.md # n8n Integration [n8n](https://n8n.partnerlinks.io/6ts9dtojxmtx) のワークフローから Report Flow API を呼び出すための公式コミュニティノード `n8n-nodes-reportflow` を提供しています。コードを書かずに「フォーム送信 → PDF 生成 → メール添付」のような自動化を組めます。 - npm: [`n8n-nodes-reportflow`](https://www.npmjs.com/package/n8n-nodes-reportflow) - ソース: [github.com/re-port-flow/n8n-nodes-reportflow](https://github.com/re-port-flow/n8n-nodes-reportflow) ## インストール n8n の **Settings → Community Nodes → Install** から、パッケージ名を入力してインストールします。 ``` n8n-nodes-reportflow ``` 詳細は [n8n Community Nodes インストールガイド](https://docs.n8n.io/integrations/community-nodes/installation/) を参照してください。 :::info セルフホスト n8n の場合 セルフホスト環境では、`N8N_COMMUNITY_PACKAGES_ENABLED=true` がセットされている必要があります。Docker Compose で運用している場合は環境変数を追加してから再起動してください。 ::: インストール後、ノードパレットに **ReportFlow** が表示されます。 ## 認証情報の作成 ReportFlow ノードの認証は **AppKey 方式** です。 1. Report Flow にログインし **ワークスペース設定 → API連携** を開く 2. **アプリケーションキー** をコピー(`ak_xxxxxxxxxxxxxxxx` 形式) 3. n8n で **Credentials → New → ReportFlow AppKey API** を作成 4. **App Key** に貼り付けて保存 ## 操作(Operations) ### PDF リソース | Operation | 同期/非同期 | 説明 | |-----------|-------------|------| | **Generate (Sync)** | 同期 | 単一 PDF を即時生成。ノードの出力にバイナリプロパティとして PDF を返す | | **Generate (Async)** | 非同期 | 単一 PDF を非同期生成。`requestId` とダウンロード URL を返す(Webhook と組み合わせ推奨) | | **Generate Multiple (Sync)** | 同期 | 複数 PDF を一括生成し ZIP で受け取る | | **Generate Multiple (Async)** | 非同期 | 複数 PDF を非同期で一括生成 | | **Download** | - | `requestId` / `fileId` を指定して既存ファイルをダウンロード | ### Design リソース | Operation | 説明 | |-----------|------| | **Get Parameters** | デザインテンプレートが要求するパラメータ構造を取得 | エンドポイントとパラメータの詳細は API リファレンスを参照してください: - [Single PDF Sync Generation](../guides/endpoints/sync-single.md) - [Single PDF Async Generation](../guides/endpoints/async-single.md) - [Multiple PDF Sync Generation](../guides/endpoints/sync-multiple.md) - [Multiple PDF Async Generation](../guides/endpoints/async-multiple.md) - [File Download](../guides/endpoints/download.md) - [Design Parameters](../guides/endpoints/design-parameters.md) ## 使い方 ### 1. パラメータ構造を確認する PDF 生成の前に、デザインが要求するパラメータを把握しておくとスムーズです。 1. **ReportFlow** ノードを追加 2. **Resource** = `Design`、**Operation** = `Get Parameters` 3. **Design ID** に Report Flow ダッシュボードからコピーした UUID を入力 4. 実行すると、`{ "customerName": "string", "amount": "number", ... }` のような構造が返る ### 2. 単一 PDF を同期生成する 最も基本的なワークフローです。 1. **ReportFlow** ノードを追加 2. **Resource** = `PDF`、**Operation** = `Generate (Sync)` 3. 以下を設定: - **Design ID**: 対象デザインの UUID - **Version**: バージョン番号(整数) - **File Name**: 出力ファイル名(例: `invoice.pdf`) - **Parameters**: JSON でパラメータを指定。前ステップから式(`{{ $json.params }}`)で受け取る形が便利 4. 後続ノードでバイナリプロパティ(既定 `data`)を Email/Slack/Drive 等に渡す :::info ファイル名のバリデーション `/ \ : * ? " < > |` および制御文字は使用できません。エラー詳細は [エラーハンドリングガイド](../guides/error-handling.md) を参照。 ::: ### 3. 非同期生成 + Webhook で完了通知 大量生成や数十秒かかる処理では、非同期エンドポイントと Webhook の組み合わせが推奨です。 1. **ReportFlow** ノードで **Generate (Async)** を実行 → `requestId` が即時返る 2. Report Flow 側で **Workspace Settings → Developer → Webhook URL** に n8n の **Webhook** ノード URL を登録 3. PDF 生成完了時に Webhook が n8n に POST される 4. Webhook ノードに続けて **ReportFlow** の **Download** Operation を `requestId` / `fileId` 指定で実行 業務 ID とのひも付けには `passthrough` フィールドを使ってください。`params`(業務データ)は Webhook には含まれません。詳しくは [Webhook 通知ガイド](../guides/webhooks.md) を参照。 ### 4. 複数 PDF の一括生成 CSV や DB の各行から PDF を作る場合: 1. **Spreadsheet File** や **MySQL** などで行データを取得 2. **ReportFlow** ノードで **Generate Multiple (Sync)** または **(Async)** 3. **Files** に行ごとの `{ fileName, params }` 配列を渡す(n8n の式エディタでマッピング) レート制限(同期 30 req/min、非同期 100 req/min)を超えないよう、必要に応じて **Split In Batches** ノードでスロットリングしてください。 ## トラブルシューティング ### `認証方式ヘッダーが存在しません` (412) AppKey 方式で Credential が紐付いていない、もしくはノードのバージョン更新でリセットされたケース。Credentials を再選択して保存してください。 ### `認証情報が不正です` (401) - AppKey の値を再確認(前後の空白・改行に注意) - Report Flow 管理画面で AppKey を再生成した場合は、n8n 側の Credentials も更新する必要あり ### `プラン情報が見つからないため、この操作は許可されていません` (403) ワークスペースのプラン情報が取得できない、もしくは月次ファイル作成回数の上限に到達しています。後者の場合は `今月のファイル作成回数上限(X)に達しました。プランをアップグレードするか、翌月までお待ちください。` メッセージが返ります。管理画面からプランをアップグレードしてください。 ### `Rate limit exceeded` (429) ワークスペース単位の制限です。`Retry-After` ヘッダの秒数だけ待ってから再試行してください。バッチ処理では非同期エンドポイントの利用と **Split In Batches** ノードでの分割が有効です。 ### Webhook が届かない 1. Webhook URL は HTTPS であること(`localhost` や HTTP は拒否される) 2. n8n をセルフホストしている場合、外部公開できているか確認(ngrok や Cloudflare Tunnel など) 3. n8n の Webhook ノードを **Production URL** に切り替えて保存(Test URL は実行中のみ有効) 詳細は [Webhook 通知ガイド](../guides/webhooks.md) を参照。 ## サポート - バグ報告・要望: [GitHub Issues](https://github.com/re-port-flow/n8n-nodes-reportflow/issues) - API 全般: [Report Flow API ドキュメント](../intro.md) ## 次のステップ - [非同期ワークフロー設計](../guides/async-workflows.md) — 大量生成のベストプラクティス - [Webhook 通知](../guides/webhooks.md) — 完了通知と HMAC-SHA256 検証 --- # docs/intro.md # Report Flow API - Getting Started Report Flow APIへようこそ!このドキュメントでは、5分でPDF生成を始める方法を説明します。 ## ⚡ 5分クイックスタート ### ステップ1: APIキーを取得(1分) Report Flow管理画面にアクセスして、以下の情報を取得します: 1. **ワークスペース設定** → **API連携** へ移動 2. 以下をコピー: - **アプリケーションキー**: `ak_xxxxxxxxxxxxxxxx` ### ステップ2: デザインIDを確認(1分) 1. **デザイン管理** へ移動 2. 使用するデザインを選択 3. **デザインID** をコピー(例: `550e8400-e29b-41d4-a716-446655440000`) ### ステップ2.5: パラメータ構造を確認(1分、オプション) デザインが必要とするパラメータの構造を確認します: ```bash curl -X GET https://api.re-port-flow.com/v1/file/design/parameter/550e8400-e29b-41d4-a716-446655440000 \ -H "appkey: your-app-key" ``` レスポンス例: ```json { "customerName": "string", "invoiceNumber": "string", "amount": "number" } ``` :::tip パラメータ構造の詳細は [デザインパラメータ取得ガイド](./guides/endpoints/design-parameters.md) を参照してください。 ::: ### ステップ3: 最初のPDFを生成(3分) 以下のcURLコマンドをターミナルで実行します: ```bash curl -X POST https://api.re-port-flow.com/v1/file/sync/single \ -H "Content-Type: application/json" \ -H "appkey: your-app-key" \ -d '{ "designId": "550e8400-e29b-41d4-a716-446655440000", "version": 1, "content": { "fileName": "invoice.pdf", "params": { "customerName": "山田太郎", "invoiceNumber": "INV-2024-001", "amount": 10000 } } }' \ --output my-first-pdf.pdf ``` :::success 成功すると `my-first-pdf.pdf` がカレントディレクトリに保存されます! ::: ## 🎯 主なユースケース ### ユースケース1: 請求書の即時生成 ユーザーがボタンをクリックしたときに、即座にPDFをダウンロードさせる。 **使用エンドポイント**: [単一PDF同期生成](./guides/endpoints/sync-single.md) ```javascript // ボタンクリック時 async function downloadInvoice(invoiceId) { const pdf = await generatePDF({ designId: 'invoice-template', version: 1, fileName: `invoice_${invoiceId}.pdf`, data: invoiceData }); // ブラウザでダウンロード downloadFile(pdf, `invoice_${invoiceId}.pdf`); } ``` ### ユースケース2: 月次レポートの一括生成 毎月末に全顧客のレポートを一括生成してメール送信。 **使用エンドポイント**: [複数PDF非同期生成](./guides/endpoints/async-multiple.md) ```javascript // バッチ処理 async function generateMonthlyReports(customers) { const contents = customers.map(customer => ({ fileName: `report_${customer.id}.pdf`, params: customer.data })); const result = await generateMultiplePDFsAsync('report-template', contents); // 各顧客にメール送信 await sendEmailsWithPDFs(result); } ``` ### ユースケース3: プレビュー機能 デザインエディタでリアルタイムプレビュー。 **使用エンドポイント**: [単一PDF同期生成](./guides/endpoints/sync-single.md) ```javascript // プレビューボタンクリック時 async function showPreview(designId, params) { const pdf = await generatePDF({ designId, version: 1, fileName: 'preview.pdf', data: params }); // ブラウザで表示 openPDFInNewTab(pdf); } ``` ## 🧭 API概要 ### PDF生成エンドポイント | エンドポイント | 用途 | タイムアウト | レスポンス | |--------------|------|-------------|-----------| | [POST /file/sync/single](./guides/endpoints/sync-single.md) | 単一PDF同期生成 | 120秒 | PDFファイル | | [POST /file/async/single](./guides/endpoints/async-single.md) | 単一PDF非同期生成 | なし | URL + fileId | | [POST /file/sync/multiple](./guides/endpoints/sync-multiple.md) | 複数PDF同期生成 | 120秒 | ZIPファイル | | [POST /file/async/multiple](./guides/endpoints/async-multiple.md) | 複数PDF非同期生成 | なし | URL + fileId | ### その他のエンドポイント | エンドポイント | 説明 | |--------------|------| | [GET /file/download/\{uuid\}](./guides/endpoints/download.md) | ZIP一括ダウンロード | | [GET /file/download/\{uuid\}/\{fileId\}](./guides/endpoints/download.md) | 個別ファイルダウンロード | | [GET /file/design/parameter/\{designId\}](./guides/endpoints/design-parameters.md) | デザインパラメータ取得 | ## 🧪 Postmanで試す APIをすぐに試したい場合は、公式のPostmanコレクションを使用できます: ### Postman Public Workspace [![Run in Postman](https://run.pstmn.io/button.svg)](https://www.postman.com/mone-pla/reportflow) 公式のPostmanコレクションには以下が含まれています: - ✅ 全7エンドポイントの実行可能なリクエスト - ✅ 認証設定(アプリケーションキー)の自動適用 - ✅ 実行可能なサンプルパラメータ - ✅ レスポンス検証の自動テストスクリプト - ✅ エラーケース(412、401、400)のサンプル ## 📚 次のステップ - [デザインパラメータ取得](./guides/endpoints/design-parameters.md) - パラメータ構造の確認方法 - [認証方法の詳細](./authentication/overview) - [PDF生成ガイド](./guides/pdf-generation) ## 🔧 制限事項 詳細は [制限事項ページ](./limitations.md) を参照してください。 ### 主な制限 - **リクエストサイズ**: 最大50MB(Base64エンコード後、実質約37MB相当) - **タイムアウト**: 同期エンドポイント120秒、非同期は制限なし - **Rate Limit**: Workspace単位で適用(同期30 req/min、非同期100 req/min) - **ファイル名**: `/ \ : * ? " < > |` および制御文字以外は使用可能 - **認証**: アプリケーションキー(`appkey` ヘッダー)が必須 - **HTTPS通信**: 必須(HTTP通信は自動的にHTTPSへリダイレクトされます) ## 💡 サンプルコード ### JavaScript (Node.js) ```javascript import axios from 'axios'; async function generatePDF() { const response = await axios.post( 'https://api.re-port-flow.com/v1/file/sync/single', { designId: '550e8400-e29b-41d4-a716-446655440000', version: 1, content: { fileName: 'invoice.pdf', params: { customerName: '山田太郎', invoiceNumber: 'INV-2024-001', amount: 10000 } } }, { headers: { 'appkey': 'your-app-key' }, responseType: 'arraybuffer' } ); return response.data; } ``` ### Python ```python import requests def generate_pdf(): url = 'https://api.re-port-flow.com/v1/file/sync/single' headers = { 'appkey': 'your-app-key', 'Content-Type': 'application/json' } data = { 'designId': '550e8400-e29b-41d4-a716-446655440000', 'version': 1, 'content': { 'fileName': 'invoice.pdf', 'params': { 'customerName': '山田太郎', 'invoiceNumber': 'INV-2024-001', 'amount': 10000 } } } response = requests.post(url, headers=headers, json=data) return response.content ``` ## インテグレーション API を直接叩かなくても、以下のツールから Report Flow を呼び出せます。 - [MCP Server (Claude / Cursor / VS Code)](./integrations/mcp) — AI クライアントから自然言語で PDF 生成 - [n8n Integration](./integrations/n8n) — ノーコードワークフロー自動化 ## 🆘 サポート 質問やサポートが必要な場合は、以下までお問い合わせください: - Email: support@re-port-flow.com --- # docs/limitations.md # 制限事項 Report Flow Content Service APIの制限事項について説明します。 :::tip これらの制限は本番環境(`api.re-port-flow.com`)に適用されます。 ::: --- ## 📦 リクエストサイズ制限 ### 最大リクエストサイズ: 50MB リクエストボディの最大サイズは**50MB**(Base64エンコード後)です。 ```text 実質的なファイルサイズ目安: - Base64エンコード前: 約37MB - エンコード後の膨張率: 約1.33倍 ``` :::tip 帳票出力SaaSとして、大量の高解像度画像を含む帳票にも対応できるよう、十分な容量を確保しています。 ::: ### 超過時の挙動 リクエストサイズが50MBを超えた場合: ```json { "statusCode": 413, "message": "Payload Too Large", "error": "Request Entity Too Large" } ``` ### 大容量ファイルの扱い方 大容量の画像やデータを扱う場合は、以下の方法を検討してください: 1. **画像の最適化** - 解像度を下げる(Web表示用は72-150dpi、印刷用は300dpi) - 圧縮率を調整する(JPEG品質80-85%推奨) - 不要なメタデータを削除する 2. **非同期エンドポイントの使用** - 大量のPDFを生成する場合は[非同期エンドポイント](./guides/endpoints/async-multiple)を使用 - タイムアウトを気にせず大容量データを処理可能 3. **データの分割** - 1リクエストで複数のPDFを生成する場合、リクエストを分割する --- ## ⏱️ タイムアウト制限 ### 同期エンドポイント: 120秒 **対象エンドポイント**: - `POST /v1/file/sync/single` - `POST /v1/file/sync/multiple` 複雑な帳票処理にも対応できるよう、120秒のタイムアウトを設定しています。 **タイムアウト時の挙動**: ```json { "statusCode": 504, "message": "Gateway Timeout", "error": "Request timeout" } ``` **120秒を超える処理の場合**: - 非同期エンドポイント(`/async/single`, `/async/multiple`)を使用 - デザインの複雑さを軽減(画像数、テーブル行数を削減) - リクエストを分割 ### 非同期エンドポイント: 制限なし **対象エンドポイント**: - `POST /v1/file/async/single` - `POST /v1/file/async/multiple` 非同期エンドポイントにはタイムアウト制限がありません。大量のPDF生成や複雑なデザインの処理に適しています。 --- ## 🚦 Rate Limit **2024年2月より、Rate Limitが実装されています。** ### Workspace単位の制限 Rate LimitはWorkspace単位で適用されます。同一Workspaceからのリクエストがカウントされるため、他のWorkspaceには影響しません。 | エンドポイント種別 | 制限値 | 対象エンドポイント | |-------------------------|----------------|------------------------------------------| | **同期エンドポイント** | 30 requests/分 | `/sync/single`, `/sync/multiple` | | **非同期エンドポイント** | 100 requests/分 | `/async/single`, `/async/multiple` | | **ダウンロード・取得** | 100 requests/分 | `/download/*`, `/design/parameter/*` | ### Rate Limit超過時の挙動 Rate Limitを超過した場合、以下のレスポンスが返されます: ```http HTTP/1.1 429 Too Many Requests Retry-After: 60 Content-Type: application/json { "statusCode": 429, "message": "Rate limit exceeded for this workspace. Please try again in 60 seconds.", "error": "Too Many Requests" } ``` レスポンスには **`Retry-After` ヘッダ** が含まれており、再試行まで待つべき秒数を整数で示します(RFC 9110 §10.2.3 準拠)。クライアントはこのヘッダ値を読み取り、指定された秒数だけ待機してから再試行してください(指数バックオフではなく、サーバ指定の秒数に従うのが推奨)。エラーメッセージ本文の数値もこのヘッダと同じ値です(人間可読版)。 ### 設計根拠 - **同期エンドポイント(30 req/min)**: 120秒のタイムアウトを考慮し、最大60並列処理に対応。ECSタスク容量に合わせた制限。 - **非同期エンドポイント(100 req/min)**: バッチ処理や大量生成に対応。サーバーリソースを保護しつつ高スループットを実現。 - **ダウンロード(100 req/min)**: 軽量な操作のため高い制限値を設定。 :::tip UI操作での通常利用(2秒に1リクエスト程度)であれば、制限に達することはありません。バッチ処理の場合は非同期エンドポイントをご利用ください。 ::: --- ## 📝 ファイル名制限 ### 使用不可な文字 以下の文字はファイル名に使用できません: | 文字 | 名称 | |------|------| | `/` | スラッシュ | | `\` | バックスラッシュ | | `:` | コロン | | `*` | アスタリスク | | `?` | クエスチョンマーク | | `"` | ダブルクォート | | `<` `>` | 山括弧 | | `\|` | パイプ | | 制御文字 | `0x00`〜`0x1F` | 上記以外の文字(英数字、日本語、スペース、各種記号等)はすべて使用可能です。 ### 例 ```json ✅ 正しい例: { "fileName": "請求書_2024-01.pdf", "fileName": "invoice-2024-01.pdf", "fileName": "レポート (1月分).pdf", "fileName": "report #001.pdf" } ❌ 間違った例: { "fileName": "請求書/2024.pdf", // スラッシュ不可 "fileName": "data:2024.pdf", // コロン不可 "fileName": "report*.pdf" // アスタリスク不可 } ``` --- ## 🔐 セキュリティ要件 ### HTTPS通信必須 本番環境ではHTTPS通信のみを受け付けます。HTTP通信は自動的にHTTPSにリダイレクトされます。 ```bash # ✅ 正しい curl -X POST https://api.re-port-flow.com/v1/file/sync/single \ -H "appkey: your-application-key" # ❌ 間違い(HTTPは使用不可) curl -X POST http://api.re-port-flow.com/v1/file/sync/single ``` ### TLS バージョン - **最小バージョン**: TLS 1.2 - **推奨バージョン**: TLS 1.3 - TLS 1.0/1.1は使用できません ### APIキーのローテーション セキュリティ向上のため、**3ヶ月ごと**にアプリケーションキーを再生成することを推奨します。 詳細は[APIキー管理](./authentication/api-keys#定期的なローテーション)を参照してください。 --- ## 📊 プラン制限 ワークスペースのプランによって以下の制限があります: ### デザインファイル数制限 プランごとにワークスペース内で作成できるデザインファイルの上限が設定されています。 ### ファイル生成回数制限 プランごとに月間のPDF生成回数の上限が設定されています。 :::info 詳細な制限値はプランによって異なります。管理画面で現在の使用状況と上限を確認できます。 ::: ### プラン制限超過時の挙動 プラン制限に達した場合: ```json { "statusCode": 403, "message": "プラン情報が見つからないため、この操作は許可されていません", "error": "Forbidden" } ``` 月次ファイル作成回数の上限に達した場合は次のメッセージが返ります: ```json { "statusCode": 403, "message": "今月のファイル作成回数上限(X)に達しました。プランをアップグレードするか、翌月までお待ちください。", "error": "Forbidden" } ``` --- ## 🌐 ネットワーク制限 ### CloudFront経由のリクエスト 外部からのリクエストはCloudFront経由で処理されます: ```text Client → CloudFront → ALB → ECS (content-service) ``` --- ## 📋 その他の制限 ### ヘルスチェックエンドポイント `/v1/health/check`エンドポイントは認証不要でアクセス可能です。このエンドポイントはアクセスログに記録されません。 ### CORS設定 以下のヘッダーが許可されています: - `Origin` - `X-Requested-With` - `Content-Type` - `Accept` - `Authorization` - `appkey` すべてのオリジン(`*`)からのリクエストを受け付けます。 --- ## 🆘 制限超過時の対処方法 ### リクエストサイズ超過の場合 1. 画像を最適化する 2. データを分割する 3. 非同期エンドポイントを使用する ### タイムアウトの場合 1. 非同期エンドポイント(`/async/single`, `/async/multiple`)を使用 2. デザインを簡素化する 3. リクエストを分割する ### プラン制限超過の場合 1. 管理画面でプランをアップグレードする 2. 不要なデザインファイルを削除する 3. 使用頻度を調整する --- ## 📞 サポート 制限事項に関するご質問やサポートが必要な場合: - Email: `support@re-port-flow.com` - 管理画面: `https://re-port-flow.com/support` --- ## 更新履歴 | 日付 | 内容 | |------------|----------| | 2026-02-15 | 初版作成 | --- # docs/user-guide/01-getting-started.md # はじめに Report Flow へようこそ。このガイドでは、初めてご利用いただく方が **30 分以内に最初の PDF を生成できる** ように、画面を見ながら順を追ってご案内します。 :::info このガイドの対象 帳票・請求書などを Report Flow で作成する **すべての方** が対象です。プログラミングの知識は必要ありません。 ::: ## まずは 30 秒の動画で見る 下の動画は、Report Flow のデザイン画面で請求書を 30 秒ほどで作っていく様子です。「こういうものが作れる」という全体感を最初につかんでください。 ## このガイドで学ぶこと このガイドの最後まで進むと、ご自身でデザインを作って PDF を出力できる状態になります。 1. **このページ**: アカウントを登録して Report Flow にログインする 2. [ワークスペースとデザインの作成](./create-design): デザインを作成する場所(ワークスペース)と、最初のデザインを用意する 3. [エディタの基本操作](./editor-basics): デザインを編集する画面の使い方 4. [オブジェクトを追加する](./add-objects): デザインに要素を配置する 5. [変数の基本](./variables-basics): 出力時に値を差し込めるようにする 6. [保存と PDF 出力](./output-pdf): 仕上げて PDF として書き出す 7. [よくある質問](./faq) --- ## ステップ 1: アカウントを登録する すでにアカウントをお持ちの方は [ステップ 2](#ステップ-2-ログインする) へ進んでください。 1. ブラウザで Report Flow の登録ページを開きます。 2. **メールアドレス** と **パスワード** を入力します。 3. **「登録する」** ボタンを押します。 4. 入力したメールアドレス宛に **確認メール** が届くので、メール内のリンクを開いてください。 5. 確認が完了すると、ログイン画面に切り替わります。 :::tip パスワードの目安 英大文字・小文字・数字を混ぜて、8 文字以上にすると安全です。 ::: ### ✅ ここまでで成功しているか確認 - 確認メールが届いた - メール内のリンクを開くと「メール認証が完了しました」という画面が表示された 両方とも当てはまっていれば、登録は完了しています。 --- ## ステップ 2: ログインする 1. ログイン画面を開きます。 2. 登録したときの **メールアドレス** と **パスワード** を入力します。 3. **「ログイン」** ボタンを押します。 4. ワークスペースの一覧画面が表示されます。 ### ✅ ここまでで成功しているか確認 - ログイン後にワークスペースの一覧画面が表示された 成功していれば、次のページに進める準備ができています。 --- ## うまくいかないときは | 症状 | 試してほしいこと | |------|----------------| | 確認メールが届かない | 迷惑メールフォルダをご確認ください。それでも届かない場合は登録ページからもう一度送信できます。 | | パスワードを忘れた | ログイン画面の「パスワードをお忘れの方」から再設定できます。 | | ログイン後に画面が真っ白 | ブラウザを再読み込み(Windows: `Ctrl + R` / Mac: `Cmd + R`)してみてください。 | | 登録ボタンが押せない | メールアドレスの形式(`@` を含む)と、パスワードが入力されているかをご確認ください。 | --- ## 次のステップ 次は **[ワークスペースとデザインの作成](./create-design)** に進みましょう。デザインを作るための入れ物(ワークスペース)と、最初のデザインを用意します。 --- # docs/user-guide/02-create-design.md # ワークスペースとデザインの作成 このページでは、デザインを作るための入れ物 **「ワークスペース」** を作成し、その中に **最初のデザイン** を用意するまでをご案内します。 :::info ワークスペースとは 請求書や納品書などのデザインをまとめて管理する場所です。会社全体で 1 つ、または部門ごとに 1 つ用意するのが一般的です。 ::: --- ## ワークスペースを作成する ログインしたあとに最初に表示されるのが、ワークスペースの一覧画面です。まだ 1 つもない場合は、新しく作成します。 1. 一覧画面で **「新規作成」** カードを押します。 2. ワークスペース作成画面が開きます。 3. **ワークスペース名** を入力します(例: `経理部` / `自社用`)。 4. **「ワークスペース作成」** ボタンを押します。 5. 作成完了画面に切り替わり、続けてデザイン作成に進めます。 ### ✅ ここまでで成功しているか確認 - 「ワークスペースの作成が完了しました」と表示される - 「デザイン作成」ボタンが表示される --- ## デザインを作成する ワークスペースができたら、その中で **最初のデザイン** を作ります。 1. デザイン一覧画面で、左上の **「+」**(新規作成)カードを押します。 2. 新規デザイン作成のモーダルが開きます。 3. **デザイン名** を入力します(例: `請求書テンプレート`)。 4. 用紙のサイズを選びます。 - **A4** が最も一般的です。迷ったらこれを選んでください。 5. **「作成」** ボタンを押します。 6. デザインを編集する画面(エディタ)が開きます。 :::tip 名前は後から変更できます デザイン名はあとからいつでも変えられます。最初は仮の名前でも構いません。 ::: ### ✅ ここまでで成功しているか確認 - 真っ白な用紙のような領域(**キャンバス**)が中央に表示されている - 左側に「テキスト」「画像」「表」などのアイコンが並んでいる - 上部に保存ボタンや戻るボタンがある 3 つとも当てはまっていれば、エディタが正しく開いています。 --- ## うまくいかないときは | 症状 | 試してほしいこと | |------|----------------| | 「新規作成」カードがない | 既にワークスペースに参加している可能性があります。一覧から該当するものを選んでください。 | | 用紙サイズが選べない | ご利用のプランによって選べる用紙が異なります。とりあえず **A4** で進めてください。 | | エディタが真っ白でアイコンが出ない | ブラウザを再読み込み(Windows: `Ctrl + R` / Mac: `Cmd + R`)してください。それでも直らない場合、ログアウト → ログインをお試しください。 | --- ## 次のステップ デザインを編集する準備ができました。次は **[エディタの基本操作](./editor-basics)** で、画面の見方と基本の使い方を覚えましょう。 --- # docs/user-guide/03-editor-basics.md # エディタの基本操作 デザインを編集する画面(**エディタ**)の見方と、基本となる操作(**拡大縮小・移動・選択・やり直し・グリッド**)を覚えましょう。これさえ知っていれば、迷わずに作業を進められます。 --- ## 画面の各部分 エディタの画面は、大きく 3 つの場所に分かれています。 | 場所 | 役割 | |------|------| | **左のサイドバー** | テキスト・画像・表など、デザインに置く道具が並んでいます | | **中央のキャンバス** | 用紙そのもの。ここに道具を配置していきます | | **右のプロパティパネル** | 選んでいるオブジェクトの設定(文字サイズ・色など)を変えられます | --- ## 拡大縮小(ズーム) 細かい部分を見たいときや、全体を見渡したいときに使います。 | やりたいこと | 操作方法 | |-------------|---------| | 拡大する | **`Ctrl` + マウスホイールを奥に回す**(Mac: `Cmd` + ホイール)
または **画面下部のツールバーの `+` ボタン** を押す | | 縮小する | **`Ctrl` + マウスホイールを手前に回す**(Mac: `Cmd` + ホイール)
または **画面下部のツールバーの `-` ボタン** を押す | | 全体表示に戻す | 画面下部のズーム表示部分(例: `100%`)をクリックして **「全体表示」** を選ぶ | :::tip 使い分けのコツ - キーボード操作が苦手な場合は、**画面下部のツールバー** の `+` / `-` ボタンを使うとマウスだけで拡大縮小できます。現在のズーム率(例: `100%`)も同じ場所に表示されています。 - 細かい位置合わせをするときは **150〜200%** に拡大、全体のバランスを見るときは **50〜75%** に縮小すると作業しやすくなります。 ::: --- ## キャンバスの移動(パン) 拡大して用紙の一部だけが見えている状態で、見たい場所に移動するための操作です。 - **`Space` キーを押しながらドラッグ** すると、キャンバスを動かせます。 - マウスのドラッグだけだと、誤って配置済みのオブジェクトを動かしてしまうので注意してください。 --- ## オブジェクトの選択 配置したテキストや画像を編集するには、まず **選択** する必要があります。 | やりたいこと | 操作方法 | |-------------|---------| | 1 つだけ選ぶ | キャンバス上で **クリック**、または **右側の「オブジェクト一覧」** から選びたい項目(例: `text1`)をクリック | | 複数まとめて選ぶ | 何もないところからドラッグして囲む | | 追加で 1 つだけ選ぶ | `Shift` を押しながらクリック | | 選択を解除する | キャンバスの何もない場所をクリック、または `Esc` キー | 選択中のオブジェクトは、青い枠と角の四角い印で示されます。選んでいる間は右のプロパティパネルで設定を変更できます。 :::tip オブジェクト一覧から選ぶと確実 オブジェクトが小さい・他のオブジェクトと重なっているなどでクリックしにくい場合は、画面右側の **「オブジェクト一覧」** タブから直接項目を選ぶと確実です。一覧には配置済みのすべてのオブジェクトが `text1` `image1` のような名前で表示されています。 ::: --- ## やり直し(Undo / Redo) 操作を間違えてしまっても、簡単に元に戻せます。 | やりたいこと | 操作方法 | |-------------|----------| | ひとつ前に戻す | キーボード `Ctrl + Z`(Mac: `Cmd + Z`)
または **画面下部のツールバーの戻る矢印(↶)ボタン** | | 戻したのをやり直す | キーボード `Ctrl + Shift + Z`(Mac: `Cmd + Shift + Z`)
または **画面下部のツールバーのやり直し矢印(↷)ボタン** | :::tip 下部ツールバーのやり直しボタン 画面下部のツールバーには **戻る(↶) / やり直し(↷)** ボタンが用意されています。マウスでクリックするだけなので、キーボードショートカットを覚えなくても操作できます。戻れる操作がないときはボタンが薄く表示されます。 ::: --- ## グリッドの表示 オブジェクトをきれいに揃えて配置したいときは、**グリッド(方眼)** を表示すると便利です。グリッドの設定はすべて画面下部のツールバーから行います。 1. 画面下部のツールバーの **グリッドアイコン(格子状)** をクリックします。 2. 開くメニューで以下を切り替えできます: - **グリッド表示**: `ON` にするとキャンバスに方眼が表示されます。 - **グリッドに吸着**: `ON` にするとオブジェクトをドラッグしたときに最寄りのグリッド線に揃います。 - **サイズ**: グリッドの間隔(px)を変更できます(1〜100)。 :::tip 位置合わせを楽にする設定 オブジェクトの位置をきっちり揃えたいときは **「グリッド表示 ON + グリッドに吸着 ON」** にすると、ドラッグ操作が最寄りのグリッド線にカクッと吸着するので、整った見た目を作りやすくなります。 ::: --- ## 自動保存について 編集中の内容は **自動的に保存** されます。明示的に「保存」ボタンを押さなくても、しばらく経てばサーバー側に反映されます。 ただし、確実に保存しておきたい節目では、上部の **「保存」** ボタンを押してください。 --- ## ✅ ここまでで身につけておきたいこと - `Ctrl` + マウスホイールまたは下部ツールバーの `+` / `-` で拡大縮小できる - `Space` を押しながらドラッグでキャンバスを動かせる - クリックまたは右の「オブジェクト一覧」からオブジェクトを選べる - `Ctrl + Z` / `Cmd + Z` または下部ツールバーの戻る矢印で取り消せる - 下部ツールバーのグリッドアイコンからグリッド表示・吸着を切り替えられる すべて当てはまれば、これからの作業を快適に進められます。 --- ## 次のステップ 操作の基本が身についたら、次は **[オブジェクトを追加する](./add-objects)** に進みましょう。実際にデザインに要素を配置していきます。 --- # docs/user-guide/04-add-objects.md # オブジェクトを追加する このページでは、デザインに置ける **すべてのオブジェクト種類** とその追加方法を、章ごとの短尺動画つきでご案内します。 :::info オブジェクトとは デザインに配置する要素を **オブジェクト** と呼びます。テキスト・画像・テーブル・図形などすべてオブジェクトの一種です。 ::: --- ## オブジェクト一覧 | 種類 | 用途 | |------|------| | [**テキスト**](#テキストを置く) | 見出しや本文、説明文 | | [**四角形 / 楕円**](#図形四角形楕円を置く) | 枠線・区切り・装飾 | | [**画像**](#画像を置く) | ロゴ・写真・印鑑画像 | | [**QR コード / バーコード**](#qr-コードバーコードを置く) | URL や商品コードの読み取り | | [**テーブル**](#テーブルを置く) | 明細行・一覧表 | | [**ヘッダー / フッター**](#ヘッダーフッターを置く) | ページ上部・下部の見出し | | [**グループ**](#グループ化) | 複数のオブジェクトをまとめて扱う | 各セクションのリンクから直接ジャンプできます。 --- ## テキストを置く 請求書の項目名や金額など、文字を配置します。 1. ツールバーの **「テキスト」** アイコンを選びます。 2. キャンバス上で文字を置きたい場所を **ドラッグして枠を作ります**。 3. 表示された入力欄に文字を入力します。 4. **「確定」** ボタンを押します。 文字を編集するには、配置済みのテキストを **ダブルクリック** します。プロパティパネルで **文字サイズ・色・フォント** を変更できます。 --- ## 図形(四角形・楕円)を置く 枠線や区切り、装飾としての図形を配置します。 1. ツールバーの **図形ツール** を開きます。 2. **四角形** または **楕円** を選びます。図形ツールは **クリックすると現在の図形が選ばれ、ドロップダウンの矢印部分で別の図形に切り替え** られます。 3. キャンバス上でドラッグして大きさを決めます。 4. 配置後、プロパティパネルで **塗り色・線の太さ・角丸** などを調整できます。 --- ## 画像を置く 会社のロゴや判子の画像など、画像ファイルを配置します。 1. ツールバーの **画像ツール** を開きます。 2. **「画像を選択」** ダイアログが開きます。 3. **アップロード** タブから新しいファイルを追加するか、**ライブラリ** タブから既存の画像を選びます。 - 対応形式: PNG / JPEG / GIF / SVG / WebP 4. **「画像を設定」** ボタンを押します。 5. ダイアログが閉じ、**画像の配置モード** になります。 6. キャンバス上でドラッグして大きさを決めると、画像が配置されます。 :::tip ロゴの使い回し・サイズの目安 - 一度アップロードした画像は、同じワークスペース内のすべてのデザインで再利用できます(ライブラリタブから選べます)。 - 画像サイズに厳密な上限はありませんが、**数 MB 以内** の画像にしておくと表示・出力がスムーズです。撮ったままの高解像度写真などは、必要に応じて圧縮してからアップロードすることをおすすめします。 ::: --- ## QR コード/バーコードを置く データを読み取れるコードを配置します。 1. ツールバーの **画像ツール** のドロップダウンを開きます。 2. **QR コード** または **バーコード** を選びます。 3. キャンバス上でドラッグして配置します。 4. 配置後、右のプロパティパネルの **「値」** 欄に、QR/バーコードに埋め込むデータ(URL・商品コード・番号など)を入力します。 5. 入力するとキャンバス上のコードが対応する内容に変わります。 :::tip 出力ごとに値を変えたいとき QR コード/バーコードの「値」には、**変数(パラメータ)を埋め込む** こともできます。たとえばお客様ごとに違う URL を埋め込みたい場合、次のページの **[変数の基本](./variables-basics)** で作ったパラメータを「値」に指定します。 ::: --- ## テーブルを置く 明細行など、行数が変わるデータを配置します。 1. ツールバーの **テーブル** ツールを選びます。 2. キャンバス上でドラッグして大きさを決めます。 3. **「テーブル設定」** ダイアログが開きます。 4. **配列パラメータ** を選びます。事前に配列タイプのパラメータが必要です([変数の基本](./variables-basics) 参照)。 5. **列** を必要に応じて追加・編集します。 6. **「確定」** を押して配置します。 --- ## ヘッダー/フッターを置く ページの上部・下部に見出しや注釈を表示します。 1. ページ上部に **ヘッダー** にしたい内容を **テキスト** として配置します。 2. ページ下部に **フッター** にしたい内容を同様に **テキスト** として配置します。 3. ロゴを使う場合は画像を、ページ番号などは変数を組み合わせて利用できます。 :::info ヘッダー/フッターの専用領域について 現在のバージョンでは、**通常のページ上にテキスト・画像として配置する方法を推奨** しています。今後のアップデートで、全ページ共通の専用領域機能を提供する予定です。 ::: --- ## グループ化 関連するオブジェクトをまとめて扱えるようにします。 1. グループ化したい複数のオブジェクトを **ドラッグで囲んで選択** します。 2. ツールバーの **グループ化** ボタンを押します。 3. 以後、グループ全体を一つの単位として **移動・サイズ変更・コピー** できるようになります。 --- ## ✅ このページの完了チェック - テキスト・図形・画像・QR コード/バーコード・テーブルをそれぞれ 1 つずつ配置できた - 配置済みのオブジェクトをクリックで選択し、プロパティを変更できた - 複数のオブジェクトをドラッグで囲んでグループ化できた --- ## 次のステップ オブジェクトの配置ができたら、次は **[変数の基本](./variables-basics)** に進みましょう。出力するたびに違う値を差し込めるようにします。 --- # docs/user-guide/05-variables-basics.md # 変数の基本(固定値を可変にする) 請求書の **宛名** や **金額** のように、出力するたびに変わる値があります。Report Flow では、まず **パラメータ** を定義し、それを **テキスト** に埋め込むことで、出力時に値を差し込めるようになります。 :::info ここで学ぶこと - 「固定値」と「変数(パラメータ)」の違い - 上部の **「パラメータ」** ボタンからパラメータを定義する方法 - 配置済みのテキストに、定義したパラメータを埋め込む方法 ::: --- ## 「固定値」と「変数」の違い | 種類 | 例 | 出力するたびに値は変わる? | |------|-----|------------------------| | **固定値** | `請求書` という見出し | 変わらない | | **変数(パラメータ)** | お客様の氏名(例: `山田 太郎`) | 出力ごとに変わる | 固定値はそのままテキストとして入力します。変数は **「ここには毎回違う値が入る」** という箱(パラメータ)を先に定義してから、テキストに埋め込みます。 --- ## ステップ 1: パラメータを定義する まず、出力ごとに値が変わる項目を **パラメータ** として登録します。 1. 上部のツールバーで **「パラメータ」** ボタンを押します。 2. パラメータの定義画面(ダイアログ)が開きます。 3. 左側の **「パラメータ追加」** から **テキスト** を押すと、新しいテキスト型のパラメータが追加されます。 4. **パラメータ名** を自分で覚えやすい名前に変更します(例: `customerName`)。 5. **値** にサンプルを入力します(例: `山田 太郎`)。編集中の表示確認に使われます。 6. **「更新」** ボタンを押して保存します。 7. 入力が終わったら、ダイアログを閉じます(`Esc` または右上の ×)。 ### 変数名のつけ方のコツ | 用途 | 推奨する名前 | |------|------------| | お客様の氏名 | `customerName` | | 請求日 | `invoiceDate` | | 請求金額 | `amount` | | 注文番号 | `orderNumber` | 半角英数字を使うのがおすすめです。覚えにくい名前(`var1` / `aaa` 等)は避けてください。 ### ✅ 成功しているか確認 - パラメータダイアログを開いたとき、自分が定義したパラメータが一覧に表示される --- ## ステップ 2: テキストに変数を埋め込む 定義したパラメータを、デザイン上のテキストに埋め込みます。 1. **テキストツール** を選び、キャンバスでドラッグして枠を作ります。 2. **「テキスト編集」** モーダルが開きます。 3. 上部のタブで **「変数・数式」** を選びます。 4. パラメータ一覧から、ステップ 1 で定義した `customerName` のボタンを押すと、入力欄に `@customerName` が挿入されます。 5. **「確定」** ボタンを押します。 6. キャンバス上のテキストが、ステップ 1 で入力したサンプル値(例: `山田 太郎`)で表示されます。 ### ✅ 成功しているか確認 - キャンバス上のテキストが、ステップ 1 のサンプル値で表示されている - 別のテキストに同じ手順で別のパラメータを埋め込むこともできる --- ## 数式について(応用) 金額に 3 桁ごとのカンマを入れたい、日付を `2026年4月25日` の形式にしたい、といった整形は **数式** を使うと実現できます。「変数・数式」タブには `comma()` / `dateFormat()` などの関数が用意されており、変数と組み合わせて使えます。 詳しい数式の使い方は、初版のこのガイドではカバーしていません。今後のアップデートでご案内します。 --- ## 次のステップ 変数を埋め込めたら、次は **[保存と PDF 出力](./output-pdf)** に進みましょう。実際にデザインを保存し、変数に値を入れて PDF を出力します。 --- # docs/user-guide/06-output-pdf.md # 保存と PDF 出力 ここまでで作ってきたデザインを **保存** し、**PDF として出力** する方法をご案内します。最後に **出力履歴** で過去に出した PDF を確認する方法も覚えましょう。 --- ## ステップ 1: デザインを保存する Report Flow では、編集中の内容は **自動的に保存** されます。明示的に「保存」する必要はありません。ただし、確実に保存しておきたい節目では **「保存」** ボタンを押してください。 1. 上部のツールバーで **「保存」** ボタンを押します。 2. 保存中の表示が出て、すぐに「最新です」のような表示に切り替わります。 ### ✅ 成功しているか確認 - 保存ボタンの近くに「保存しました」「最新です」のような表示が出ている --- ## ステップ 2: PDF として出力する 保存ができたら、PDF を出力します。 1. 上部の **「出力」** ボタンを押します。 2. 出力ダイアログが開きます。 3. 変数を使っている場合は、ここで **値を入力** します(例: `customerName` → `山田 太郎`)。値が空でもパラメータの既定値で出力されます。 4. ダイアログ内の **「出力」** ボタンを押します。 5. PDF の生成が始まります。完了すると **ダウンロード** または **プレビュー** が始まります。 :::tip 値の入力を省略したいとき パラメータを作ったときに **既定値(サンプル値)** を設定していれば、空欄のままでもその値が使われます。 ::: ### ✅ 成功しているか確認 - 出力した PDF が開く、またはダウンロードフォルダに保存される - 変数に入れた値が PDF 上に表示されている(仮の値ではなく入力した値) --- ## ステップ 3: 出力履歴を確認する 過去に出力した PDF は **出力履歴** で確認できます。再ダウンロードもできるので、毎回出力し直す必要はありません。 1. 左のメニューから **「出力履歴」** を選びます(または、デザインの詳細画面から開きます)。 2. 履歴の一覧から、ダウンロードしたい PDF を選びます。 3. **ダウンロード** ボタンを押します。 :::info 履歴の保存期間 出力した PDF はご利用のプランで定められた期間、保存されます。長期保存が必要な場合は、お手元のフォルダにもダウンロードしておくことをおすすめします。 ::: ### ✅ 成功しているか確認 - 出力履歴に、自分が今出したばかりの PDF が表示されている - そこから再ダウンロードできる --- ## 🎉 おめでとうございます ここまでできれば、Report Flow の基本機能の **ゴールデンパス** をひととおりお試しいただけたことになります。 | 達成したこと | |-------------| | ✅ アカウントを作って Report Flow にログインできる | | ✅ ワークスペースとデザインを作れる | | ✅ エディタの基本操作ができる | | ✅ テキスト・画像・表など、すべてのオブジェクトを配置できる | | ✅ 変数を使って差し込み出力ができる | | ✅ PDF を出力して履歴から再ダウンロードできる | --- ## うまくいかないときは | 症状 | 試してほしいこと | |------|----------------| | 「出力」ボタンを押しても何も起きない | ブラウザのポップアップブロックを確認してください。Report Flow のドメインを許可する必要がある場合があります。 | | PDF に変数の値が反映されていない | 値を入力した変数名と、デザインに登録した変数名が **完全に一致** しているか確認してください(大文字小文字の違いも区別されます)。 | | 履歴に出力結果が出てこない | 数秒〜数分の遅れで反映される場合があります。少し待って画面を再読み込みしてください。 | --- ## 次のステップ これでガイド本編は完了です。気になる点や、ここで扱っていない機能については **[よくある質問](./faq)** をご覧ください。 --- # docs/user-guide/07-faq.md # よくある質問 このガイドだけでは触れられなかった点について、よくいただくご質問にお答えします。 --- ## 操作・編集に関する質問 ### Q1. 編集中の内容はどのタイミングで保存されますか? **自動的に保存されます。** 明示的に「保存」を押さなくても、編集を加えるたびに少し遅れてサーバーに反映されます。確実に保存しておきたい節目では、上部の **「保存」** ボタンを押してください。 --- ### Q2. 同じデザインを別のお客様用に使い回したい デザイン一覧から **「複製」** を選ぶと、現在のデザインのコピーを作れます。コピーしたほうのデザイン名を変更すれば、別バージョンとして使えます。 --- ### Q3. 操作を間違えて元に戻したい `Ctrl + Z`(Mac: `Cmd + Z`)でひとつ前の状態に戻せます。何度か押すとさらに戻れます。やり直したい場合は `Ctrl + Shift + Z`(Mac: `Cmd + Shift + Z`)です。 --- ## 変数・差し込みに関する質問 ### Q4. 金額に 3 桁ごとのカンマを入れたい 数値を整形する **数式機能** を使うと、`comma()` という関数で `1,234,567` のように表示できます。日付の整形(`2026年4月25日` 形式など)も同じ仕組みで対応できます。 このユーザーガイドの初版では数式機能の詳細はカバーしていませんが、今後のアップデートで詳しくご案内する予定です。 --- ### Q5. 明細行のように、行数が出力ごとに変わる表を作りたい 可能です。**表** に対して **明細データを差し込む** 設定を行うと、データの件数に応じて行が自動的に増えます。 明細表の詳しい設定方法は、初版のガイドではカバーしていません。今後のアップデートでご案内します。 --- ## 出力・PDF に関する質問 ### Q6. 出力した PDF はどこから再ダウンロードできますか? **出力履歴** から再ダウンロードできます。ご利用のプランによって保存期間が異なるため、長期保管が必要な場合はお手元のフォルダにも保存しておくことをおすすめします。 --- ### Q7. PDF にお客様の情報をまとめて差し込んで、複数枚を一括出力できますか? 可能です。複数件のデータを一括で渡して、件数分の PDF を一度に出力する **API 連携** をご用意しています。少し技術的な設定が必要なので、開発担当の方向けの **[API リファレンス](/docs/intro)** をご覧ください。 --- ## アカウント・プランに関する質問 ### Q8. 複数人でデザインを編集したい ワークスペースに **メンバーを招待** することで、同じワークスペース内のデザインを共同で編集できます。招待方法はワークスペース設定からご確認ください。 --- ### Q9. プランを変更したい ワークスペース設定の **「プラン」** から、現在のご契約プランの確認・変更ができます。 --- ## それでも解決しないときは このガイドや FAQ で解決しないご質問・お困りごとは、お問い合わせ窓口までご連絡ください。改善・追加すべき内容として、本ガイドにも反映していきます。 --- ## 次に何をする? - 開発者の方は、API での自動 PDF 生成について **[API リファレンス](/docs/intro)** をご覧ください。 - ユーザーガイドを最初から読み直したい方は **[はじめに](./getting-started)** へどうぞ。 --- # docs/user-guide/_glossary.md # 製品用語ミニ用語集(非公開・社内参照用) > **このページは公開しません。**読者が同じ概念に対して複数の呼び方で混乱しないよう、ガイド全体で表記を統一するための社内メモです。 | 概念 | 採用呼称 | 別表記(避ける) | 備考 | |------|---------|----------------|------| | 利用組織の単位 | **ワークスペース** | チーム / 組織 | 画面の文言に揃える | | 帳票テンプレート | **デザイン** | テンプレート | アプリでは「デザイン」と呼ぶ | | デザインに配置する要素 | **オブジェクト** | パーツ / 要素 | テキスト・画像・表など | | 描画領域 | **キャンバス** | ボード / ステージ | エディタ中央の白い面 | | 静的な値 | **固定値** | 直接入力 | 変数化する前の状態 | | 差し込み値 | **変数** | プロパティ / フィールド | 出力時に値を流し込む箱 | | PDF 生成 | **出力** | エクスポート / 書き出し | 「PDF を出力する」と統一 | | 出力結果の一覧 | **出力履歴** | 履歴 / アウトプット | | ## 確認手順 新しいページを書いたら、上記の「採用呼称」と画面文言が一致しているかを確認する。画面側が変わっていたら、本表 → 全ガイドの順で更新する。 --- # OpenAPI Reference **Report Flow Content Service API** (version 1.0.0) Report Flow Content Service provides PDF generation capabilities for your reports and documents. ## Authentication Two authentication methods are supported: - **API key (`appkey` header, lowercase)** — for single-workspace automation. - **OAuth 2.0 / OIDC (`Authorization: Bearer ...`)** — Authorization Code + PKCE for per-user delegation (Make.com etc.) and Client Credentials for backend-to-backend integrations. See the [OAuth 2.0 guide](https://doc.re-port-flow.com/docs/authentication/oauth) for details. ## Base URL `https://api.re-port-flow.com/v1` ## Limits - Maximum request body size: 50MB (after Base64 encoding, roughly 37MB of binary data). - Sync timeout: 120 seconds. Use the async endpoints for longer jobs. - Rate limit: per-workspace, 30 req/min for sync endpoints and 100 req/min for async / download endpoints. Exceeding the limit returns 429 with a `Retry-After` header (RFC 9110 §10.2.3). **Servers:** - https://api.re-port-flow.com/v1 — Production server ## POST /file/sync/single **Summary:** 単一PDFの同期生成 指定されたデザインとパラメータから単一のPDFファイルを同期的に生成します。 レスポンスボディとしてPDFファイルが直接返されます。 **制限事項:** - リクエストサイズ上限: 50MB(Base64エンコード後、実質約37MB相当) - タイムアウト: 120秒(これより長い処理は非同期エンドポイントを使用) **Webhook通知:** PDF生成完了時、ワークスペースに設定されたWebhook URLに通知が送信されます。詳細は[Webhook通知ガイド](https://doc.re-port-flow.com/docs/guides/webhooks)を参照してください。 **Tags:** PDF Generation **Operation ID:** `syncSingle` **Request Body:** - Content-Type: `application/json` - `designId` (string) (required) — デザインID - `version` (integer) (required) — バージョン番号 - `content` (object) (required) - `fileName` (string) (required) — ファイル名。`/ \\ : * ? " < > |` および制御文字以外は使用可能。 - `shareType` (string) — 共有タイプ(リクエスト側は数値コード)。 - `"01"`: ワークスペース内共有(デフォルト、レスポンスでは `workspace`) - `"02"`: 招待者共有(レスポンスでは `invited`) - `"03"`: 公開URL共有(レスポンスでは `public`) - enum: "01", "02", "03" - `passcodeEnabled` (boolean) — パスコード保護を有効化する。`true` の場合のみレスポンス `share.passcode` にサーバ生成パスコードが一度だけ返却される。 - `passthrough` (object) — 任意の文字列 KV。レスポンスの `X-File-Mapping[].passthrough` または `files[].passthrough` にエコーバックされる。Webhook 追跡やバッチ処理の紐付けに活用できる。 - additionalProperties: - (string) - `params` (object) (required) — テンプレートに埋め込むパラメータ - additionalProperties: any **Responses:** - `200` — PDF生成成功 - Content-Type: `application/pdf` - (string) - `400` — リクエストが不正 - Content-Type: `application/json` - `statusCode` (integer) — HTTPステータスコード - `message` (string) — エラーメッセージ - `error` (string) — エラータイプ - `401` — 認証エラー (`appkey` ヘッダーの値が不正/失効) - Content-Type: `application/json` - `statusCode` (integer) — HTTPステータスコード - `message` (string) — エラーメッセージ - `error` (string) — エラータイプ - `412` — 認証ヘッダー欠落 (`appkey` ヘッダーが含まれていない) - Content-Type: `application/json` - `statusCode` (integer) — HTTPステータスコード - `message` (string) — エラーメッセージ - `error` (string) — エラータイプ - `429` — レート制限超過 (同期エンドポイントは 30 req/min) - Content-Type: `application/json` - `statusCode` (integer) — HTTPステータスコード - `message` (string) — エラーメッセージ - `error` (string) — エラータイプ - `500` — 内部サーバーエラー - Content-Type: `application/json` - `statusCode` (integer) — HTTPステータスコード - `message` (string) — エラーメッセージ - `error` (string) — エラータイプ --- ## POST /file/async/single **Summary:** 単一PDFの非同期生成 指定されたデザインとパラメータから単一のPDFファイルを非同期的に生成します。 即座にファイルURLとIDを返します。 **Webhook通知:** PDF生成完了時、ワークスペースに設定されたWebhook URLに通知が送信されます。詳細は[Webhook通知ガイド](https://doc.re-port-flow.com/docs/guides/webhooks)を参照してください。 **Tags:** PDF Generation **Operation ID:** `asyncSingle` **Request Body:** - Content-Type: `application/json` - `designId` (string) (required) — デザインID - `version` (integer) (required) — バージョン番号 - `content` (object) (required) - `fileName` (string) (required) — ファイル名。`/ \\ : * ? " < > |` および制御文字以外は使用可能。 - `shareType` (string) — 共有タイプ(リクエスト側は数値コード)。 - `"01"`: ワークスペース内共有(デフォルト、レスポンスでは `workspace`) - `"02"`: 招待者共有(レスポンスでは `invited`) - `"03"`: 公開URL共有(レスポンスでは `public`) - enum: "01", "02", "03" - `passcodeEnabled` (boolean) — パスコード保護を有効化する。`true` の場合のみレスポンス `share.passcode` にサーバ生成パスコードが一度だけ返却される。 - `passthrough` (object) — 任意の文字列 KV。レスポンスの `X-File-Mapping[].passthrough` または `files[].passthrough` にエコーバックされる。Webhook 追跡やバッチ処理の紐付けに活用できる。 - additionalProperties: - (string) - `params` (object) (required) — テンプレートに埋め込むパラメータ - additionalProperties: any **Responses:** - `202` — PDF生成を受け付けました - Content-Type: `application/json` - `requestId` (string) — リクエストID(ダウンロードエンドポイントで使用) - `url` (string) — ZIP ダウンロードURL - `files` (array) - items: object - `fileName` (string) - `fileId` (string) - `passthrough` (object) — リクエスト時に指定した `passthrough` の値(指定時のみ) - additionalProperties: - (string) - `share` (object) - `shareType` (string) — 共有タイプ(レスポンス側は人間可読な名前)。リクエストの `01/02/03` と次のように対応する。 - `workspace` ← `"01"`: ワークスペース内共有 - `invited` ← `"02"`: 招待者共有 - `public` ← `"03"`: 公開URL共有 - enum: "workspace", "invited", "public" - `url` (string) — ファイル表示URL(全 shareType 共通: `/file/{requestId}/{fileId}`) - `passcodeEnabled` (boolean) — パスコード有効フラグ - `passcode` (string) — サーバー生成パスコード(`passcodeEnabled=true` かつ生成直後の一度のみ返却) - `400` — リクエストが不正 - Content-Type: `application/json` - `statusCode` (integer) — HTTPステータスコード - `message` (string) — エラーメッセージ - `error` (string) — エラータイプ - `401` — 認証エラー - Content-Type: `application/json` - `statusCode` (integer) — HTTPステータスコード - `message` (string) — エラーメッセージ - `error` (string) — エラータイプ --- ## POST /file/sync/multiple **Summary:** 複数PDFの同期生成(ZIP) 指定されたデザインと複数のパラメータから複数のPDFファイルを同期的に生成し、ZIPで返します。 **Webhook通知:** PDF生成完了時、ワークスペースに設定されたWebhook URLに通知が送信されます。詳細は[Webhook通知ガイド](https://doc.re-port-flow.com/docs/guides/webhooks)を参照してください。 **Tags:** PDF Generation **Operation ID:** `syncMultiple` **Request Body:** - Content-Type: `application/json` - `designId` (string) (required) — デザインID - `version` (integer) (required) — バージョン番号 - `contents` (array) (required) - items: object - `fileName` (string) (required) — ファイル名。`/ \\ : * ? " < > |` および制御文字以外は使用可能。 - `shareType` (string) — 共有タイプ(リクエスト側は数値コード)。 - `"01"`: ワークスペース内共有(デフォルト、レスポンスでは `workspace`) - `"02"`: 招待者共有(レスポンスでは `invited`) - `"03"`: 公開URL共有(レスポンスでは `public`) - enum: "01", "02", "03" - `passcodeEnabled` (boolean) — パスコード保護を有効化する。`true` の場合のみレスポンス `share.passcode` にサーバ生成パスコードが一度だけ返却される。 - `passthrough` (object) — 任意の文字列 KV。レスポンスの `X-File-Mapping[].passthrough` または `files[].passthrough` にエコーバックされる。Webhook 追跡やバッチ処理の紐付けに活用できる。 - additionalProperties: - (string) - `params` (object) (required) — テンプレートに埋め込むパラメータ - additionalProperties: any **Responses:** - `200` — ZIP生成成功 - Content-Type: `application/zip` - (string) - `400` — リクエストが不正 - Content-Type: `application/json` - `statusCode` (integer) — HTTPステータスコード - `message` (string) — エラーメッセージ - `error` (string) — エラータイプ --- ## POST /file/async/multiple **Summary:** 複数PDFの非同期生成(ZIP) 指定されたデザインと複数のパラメータから複数のPDFファイルを非同期的に生成し、ZIPで保存します。 **Webhook通知:** PDF生成完了時、ワークスペースに設定されたWebhook URLに通知が送信されます。詳細は[Webhook通知ガイド](https://doc.re-port-flow.com/docs/guides/webhooks)を参照してください。 **Tags:** PDF Generation **Operation ID:** `asyncMultiple` **Request Body:** - Content-Type: `application/json` - `designId` (string) (required) — デザインID - `version` (integer) (required) — バージョン番号 - `contents` (array) (required) - items: object - `fileName` (string) (required) — ファイル名。`/ \\ : * ? " < > |` および制御文字以外は使用可能。 - `shareType` (string) — 共有タイプ(リクエスト側は数値コード)。 - `"01"`: ワークスペース内共有(デフォルト、レスポンスでは `workspace`) - `"02"`: 招待者共有(レスポンスでは `invited`) - `"03"`: 公開URL共有(レスポンスでは `public`) - enum: "01", "02", "03" - `passcodeEnabled` (boolean) — パスコード保護を有効化する。`true` の場合のみレスポンス `share.passcode` にサーバ生成パスコードが一度だけ返却される。 - `passthrough` (object) — 任意の文字列 KV。レスポンスの `X-File-Mapping[].passthrough` または `files[].passthrough` にエコーバックされる。Webhook 追跡やバッチ処理の紐付けに活用できる。 - additionalProperties: - (string) - `params` (object) (required) — テンプレートに埋め込むパラメータ - additionalProperties: any **Responses:** - `202` — ZIP生成を受け付けました - Content-Type: `application/json` - `requestId` (string) — リクエストID(ダウンロードエンドポイントで使用) - `url` (string) — ZIP ダウンロードURL - `files` (array) - items: object - `fileName` (string) - `fileId` (string) - `passthrough` (object) — リクエスト時に指定した `passthrough` の値(指定時のみ) - additionalProperties: - (string) - `share` (object) - `shareType` (string) — 共有タイプ(レスポンス側は人間可読な名前)。リクエストの `01/02/03` と次のように対応する。 - `workspace` ← `"01"`: ワークスペース内共有 - `invited` ← `"02"`: 招待者共有 - `public` ← `"03"`: 公開URL共有 - enum: "workspace", "invited", "public" - `url` (string) — ファイル表示URL(全 shareType 共通: `/file/{requestId}/{fileId}`) - `passcodeEnabled` (boolean) — パスコード有効フラグ - `passcode` (string) — サーバー生成パスコード(`passcodeEnabled=true` かつ生成直後の一度のみ返却) --- ## GET /file/download/{uuid} **Summary:** ZIP一括ダウンロード 指定されたUUIDのZIPファイルをダウンロードします。 **Tags:** File Download **Operation ID:** `downloadZip` **Parameters:** - `uuid` in path (string) (required) — ファイルUUID **Responses:** - `200` — ZIPダウンロード成功 - Content-Type: `application/zip` - (string) - `404` — ファイルが見つかりません - Content-Type: `application/json` - `statusCode` (integer) — HTTPステータスコード - `message` (string) — エラーメッセージ - `error` (string) — エラータイプ --- ## GET /file/download/{uuid}/{fileId} **Summary:** 個別ファイルダウンロード 指定されたUUIDとファイルIDの個別ファイルをダウンロードします。 **Tags:** File Download **Operation ID:** `downloadSingleFile` **Parameters:** - `uuid` in path (string) (required) — ファイルUUID - `fileId` in path (string) (required) — ファイルID **Responses:** - `200` — ファイルダウンロード成功 - Content-Type: `application/pdf` - (string) - `404` — ファイルが見つかりません --- ## GET /file/design/parameter/{designId} **Summary:** デザインパラメータ構造を取得 指定されたデザインIDのパラメータ構造を取得します。 PDFやサムネイル生成時に必要なパラメータのスキーマを確認できます。 **Tags:** Design Parameters **Operation ID:** `getDesignParameters` **Parameters:** - `designId` in path (string) (required) — デザインID - `version` in query (integer) — デザインのバージョン番号(省略時は最新バージョン) **Responses:** - `200` — パラメータ構造の取得成功 - Content-Type: `application/json` - additionalProperties: - oneOf: - (string) - (object) - array of object - (object) - `404` — デザインが見つかりません - `500` — 内部サーバーエラー --- ## Full OpenAPI Specification (YAML) ```yaml openapi: 3.0.0 info: title: Report Flow Content Service API description: | Report Flow Content Service provides PDF generation capabilities for your reports and documents. ## Authentication Two authentication methods are supported: - **API key (`appkey` header, lowercase)** — for single-workspace automation. - **OAuth 2.0 / OIDC (`Authorization: Bearer ...`)** — Authorization Code + PKCE for per-user delegation (Make.com etc.) and Client Credentials for backend-to-backend integrations. See the [OAuth 2.0 guide](https://doc.re-port-flow.com/docs/authentication/oauth) for details. ## Base URL `https://api.re-port-flow.com/v1` ## Limits - Maximum request body size: 50MB (after Base64 encoding, roughly 37MB of binary data). - Sync timeout: 120 seconds. Use the async endpoints for longer jobs. - Rate limit: per-workspace, 30 req/min for sync endpoints and 100 req/min for async / download endpoints. Exceeding the limit returns 429 with a `Retry-After` header (RFC 9110 §10.2.3). version: 1.0.0 contact: name: Monepla Support url: https://re-port-flow.com servers: - url: https://api.re-port-flow.com/v1 description: Production server security: - ApiKeyAuth: [] - OAuth2: - pdf:generate - designs:read - templates:read tags: - name: PDF Generation description: PDF file generation operations (sync and async) - name: File Download description: Download generated PDF files - name: Design Parameters description: Get design parameter structure components: securitySchemes: ApiKeyAuth: type: apiKey in: header name: appkey description: | Authentication requires a single header: - `appkey`: Your application key (lowercase header name) OAuth2: type: oauth2 description: | OAuth 2.0 / OpenID Connect. The OAuth flow (Authorize / Token / UserInfo) is hosted on `https://re-port-flow.com/api/v1` (a different host from this protected resource API). See the [OAuth 2.0 guide](https://doc.re-port-flow.com/docs/authentication/oauth). flows: authorizationCode: authorizationUrl: https://re-port-flow.com/api/v1/oauth/authorize tokenUrl: https://re-port-flow.com/api/v1/oauth/token refreshUrl: https://re-port-flow.com/api/v1/oauth/token scopes: openid: Confirm the user's id (OIDC) profile: Profile info (this implementation includes the email address) designs:read: Read designs (list and detail) designs:write: Create and edit designs templates:read: Read templates (list and detail) templates:write: Create and edit templates pdf:generate: Generate PDFs from a template clientCredentials: tokenUrl: https://re-port-flow.com/api/v1/oauth/token scopes: designs:read: Read designs (list and detail) designs:write: Create and edit designs templates:read: Read templates (list and detail) templates:write: Create and edit templates pdf:generate: Generate PDFs from a template schemas: ContentDto: type: object required: - fileName - params properties: fileName: type: string description: | ファイル名。`/ \\ : * ? " < > |` および制御文字以外は使用可能。 example: invoice_2024.pdf shareType: type: string enum: ['01', '02', '03'] default: '01' description: | 共有タイプ(リクエスト側は数値コード)。 - `"01"`: ワークスペース内共有(デフォルト、レスポンスでは `workspace`) - `"02"`: 招待者共有(レスポンスでは `invited`) - `"03"`: 公開URL共有(レスポンスでは `public`) example: '01' passcodeEnabled: type: boolean default: false description: | パスコード保護を有効化する。`true` の場合のみレスポンス `share.passcode` にサーバ生成パスコードが一度だけ返却される。 passthrough: type: object additionalProperties: type: string description: | 任意の文字列 KV。レスポンスの `X-File-Mapping[].passthrough` または `files[].passthrough` にエコーバックされる。Webhook 追跡やバッチ処理の紐付けに活用できる。 example: orderId: ORD-2024-001 userId: user-abc123 params: type: object additionalProperties: true description: テンプレートに埋め込むパラメータ example: customerName: "山田太郎" invoiceNumber: "INV-2024-001" items: - name: "商品A" price: 1000 quantity: 2 SingleReq: type: object required: - designId - version - content properties: designId: type: string format: uuid description: デザインID example: "550e8400-e29b-41d4-a716-446655440000" version: type: integer description: バージョン番号 example: 1 content: $ref: '#/components/schemas/ContentDto' MultipleReq: type: object required: - designId - version - contents properties: designId: type: string format: uuid description: デザインID example: "550e8400-e29b-41d4-a716-446655440000" version: type: integer description: バージョン番号 example: 1 contents: type: array minItems: 1 items: $ref: '#/components/schemas/ContentDto' ExportRes: type: object properties: requestId: type: string format: uuid description: リクエストID(ダウンロードエンドポイントで使用) example: "550e8400-e29b-41d4-a716-446655440000" url: type: string format: uri description: ZIP ダウンロードURL example: "https://api.re-port-flow.com/v1/file/download/550e8400-e29b-41d4-a716-446655440000" files: type: array items: type: object properties: fileName: type: string fileId: type: string passthrough: type: object additionalProperties: { type: string } description: | リクエスト時に指定した `passthrough` の値(指定時のみ) share: $ref: '#/components/schemas/ShareResponseDto' ShareResponseDto: type: object properties: shareType: type: string enum: [workspace, invited, public] description: | 共有タイプ(レスポンス側は人間可読な名前)。リクエストの `01/02/03` と次のように対応する。 - `workspace` ← `"01"`: ワークスペース内共有 - `invited` ← `"02"`: 招待者共有 - `public` ← `"03"`: 公開URL共有 url: type: string format: uri description: | ファイル表示URL(全 shareType 共通: `/file/{requestId}/{fileId}`) example: "https://app.re-port-flow.com/file/550e8400-e29b-41d4-a716-446655440000/file_123456" passcodeEnabled: type: boolean description: パスコード有効フラグ passcode: type: string description: | サーバー生成パスコード(`passcodeEnabled=true` かつ生成直後の一度のみ返却) GetDesignParametersResDto: type: object additionalProperties: oneOf: - type: string - type: object - type: array items: type: object example: params1: "string" params2: "number" params3: "date" params4: params4-1: "string" params5: - params51: "string" params52: "number" Error: type: object properties: statusCode: type: integer description: HTTPステータスコード message: type: string description: エラーメッセージ error: type: string description: エラータイプ paths: /file/sync/single: post: tags: - PDF Generation summary: 単一PDFの同期生成 description: | 指定されたデザインとパラメータから単一のPDFファイルを同期的に生成します。 レスポンスボディとしてPDFファイルが直接返されます。 **制限事項:** - リクエストサイズ上限: 50MB(Base64エンコード後、実質約37MB相当) - タイムアウト: 120秒(これより長い処理は非同期エンドポイントを使用) **Webhook通知:** PDF生成完了時、ワークスペースに設定されたWebhook URLに通知が送信されます。詳細は[Webhook通知ガイド](https://doc.re-port-flow.com/docs/guides/webhooks)を参照してください。 operationId: syncSingle x-codeSamples: - lang: cURL source: | 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", "params": { "customerName": "山田太郎", "invoiceNumber": "INV-2024-001", "amount": 10000 } } }' \ --output invoice.pdf requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SingleReq' example: designId: "550e8400-e29b-41d4-a716-446655440000" version: 1 content: fileName: "invoice.pdf" params: customerName: "山田太郎" invoiceNumber: "INV-2024-001" amount: 10000 responses: '200': description: PDF生成成功 headers: Content-Type: schema: type: string example: application/pdf Content-Length: schema: type: integer example: 102400 description: PDF ファイルサイズ (bytes) Content-Disposition: schema: type: string example: 'attachment; filename="invoice_2024.pdf"' File-URL: schema: type: string format: uri example: "https://api.re-port-flow.com/v1/file/download/{requestId}" description: | ワークスペースのダウンロードエンドポイント URL (`ExportRes.url` と同形式)。 Request-Id: schema: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440000" description: リクエスト ID。後続のファイルダウンロード API 等で使用。 X-File-Mapping: schema: type: string description: | 生成ファイルのメタデータと共有設定 (JSON 配列文字列)。 **URL エンコード済み**で返るため、クライアントは `decodeURIComponent()` してから `JSON.parse()` すること。 デコード後の構造は `[{ fileId, fileName, share, passthrough? }, ...]` (`share` は `ShareResponseDto`)。 content: application/pdf: schema: type: string format: binary '400': description: リクエストが不正 content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: 認証エラー (`appkey` ヘッダーの値が不正/失効) content: application/json: schema: $ref: '#/components/schemas/Error' '412': description: 認証ヘッダー欠落 (`appkey` ヘッダーが含まれていない) content: application/json: schema: $ref: '#/components/schemas/Error' '429': description: レート制限超過 (同期エンドポイントは 30 req/min) content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: 内部サーバーエラー content: application/json: schema: $ref: '#/components/schemas/Error' /file/async/single: post: tags: - PDF Generation summary: 単一PDFの非同期生成 description: | 指定されたデザインとパラメータから単一のPDFファイルを非同期的に生成します。 即座にファイルURLとIDを返します。 **Webhook通知:** PDF生成完了時、ワークスペースに設定されたWebhook URLに通知が送信されます。詳細は[Webhook通知ガイド](https://doc.re-port-flow.com/docs/guides/webhooks)を参照してください。 operationId: asyncSingle x-codeSamples: - lang: cURL source: | curl -X POST https://api.re-port-flow.com/v1/file/async/single \ -H "appkey: your-application-key" \ -H "Content-Type: application/json" \ -d '{ "designId": "550e8400-e29b-41d4-a716-446655440000", "version": 1, "content": { "fileName": "invoice.pdf", "params": { "customerName": "山田太郎", "invoiceNumber": "INV-2024-001", "amount": 10000 } } }' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SingleReq' responses: '202': description: PDF生成を受け付けました content: application/json: schema: $ref: '#/components/schemas/ExportRes' '400': description: リクエストが不正 content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: 認証エラー content: application/json: schema: $ref: '#/components/schemas/Error' /file/sync/multiple: post: tags: - PDF Generation summary: 複数PDFの同期生成(ZIP) description: | 指定されたデザインと複数のパラメータから複数のPDFファイルを同期的に生成し、ZIPで返します。 **Webhook通知:** PDF生成完了時、ワークスペースに設定されたWebhook URLに通知が送信されます。詳細は[Webhook通知ガイド](https://doc.re-port-flow.com/docs/guides/webhooks)を参照してください。 operationId: syncMultiple x-codeSamples: - lang: cURL source: | 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": "550e8400-e29b-41d4-a716-446655440000", "version": 1, "contents": [ { "fileName": "invoice_001.pdf", "params": { "customerName": "山田太郎", "invoiceNumber": "INV-001" } }, { "fileName": "invoice_002.pdf", "params": { "customerName": "佐藤花子", "invoiceNumber": "INV-002" } } ] }' \ --output invoices.zip requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MultipleReq' example: designId: "550e8400-e29b-41d4-a716-446655440000" version: 1 contents: - fileName: "invoice_001.pdf" params: customerName: "山田太郎" invoiceNumber: "INV-001" - fileName: "invoice_002.pdf" params: customerName: "佐藤花子" invoiceNumber: "INV-002" responses: '200': description: ZIP生成成功 headers: Content-Type: schema: type: string example: application/zip Content-Length: schema: type: integer example: 307200 description: ZIP ファイルサイズ (bytes) Content-Disposition: schema: type: string example: 'attachment; filename="files.zip"' File-URL: schema: type: string format: uri example: "https://api.re-port-flow.com/v1/file/download/{requestId}" description: | ワークスペースのダウンロードエンドポイント URL (`ExportRes.url` と同形式)。 Request-Id: schema: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440000" description: リクエスト ID。後続のファイルダウンロード API 等で使用。 X-File-Mapping: schema: type: string description: | 各 PDF のメタデータと共有設定 (JSON 配列文字列)。 **URL エンコード済み**で返るため、クライアントは `decodeURIComponent()` してから `JSON.parse()` すること。 デコード後の構造は `[{ fileId, fileName, share, passthrough? }, ...]` (`share` は `ShareResponseDto`)。 content: application/zip: schema: type: string format: binary '400': description: リクエストが不正 content: application/json: schema: $ref: '#/components/schemas/Error' /file/async/multiple: post: tags: - PDF Generation summary: 複数PDFの非同期生成(ZIP) description: | 指定されたデザインと複数のパラメータから複数のPDFファイルを非同期的に生成し、ZIPで保存します。 **Webhook通知:** PDF生成完了時、ワークスペースに設定されたWebhook URLに通知が送信されます。詳細は[Webhook通知ガイド](https://doc.re-port-flow.com/docs/guides/webhooks)を参照してください。 operationId: asyncMultiple x-codeSamples: - lang: cURL source: | curl -X POST https://api.re-port-flow.com/v1/file/async/multiple \ -H "appkey: your-application-key" \ -H "Content-Type: application/json" \ -d '{ "designId": "550e8400-e29b-41d4-a716-446655440000", "version": 1, "contents": [ { "fileName": "invoice_001.pdf", "params": { "customerName": "山田太郎", "invoiceNumber": "INV-001" } }, { "fileName": "invoice_002.pdf", "params": { "customerName": "佐藤花子", "invoiceNumber": "INV-002" } } ] }' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MultipleReq' responses: '202': description: ZIP生成を受け付けました content: application/json: schema: $ref: '#/components/schemas/ExportRes' /file/download/{uuid}: get: tags: - File Download summary: ZIP一括ダウンロード description: | 指定されたUUIDのZIPファイルをダウンロードします。 operationId: downloadZip x-codeSamples: - lang: cURL source: | curl -X GET https://api.re-port-flow.com/v1/file/download/550e8400-e29b-41d4-a716-446655440000 \ -H "appkey: your-application-key" \ --output files.zip parameters: - name: uuid in: path required: true schema: type: string format: uuid description: ファイルUUID responses: '200': description: ZIPダウンロード成功 content: application/zip: schema: type: string format: binary '404': description: ファイルが見つかりません content: application/json: schema: $ref: '#/components/schemas/Error' /file/download/{uuid}/{fileId}: get: tags: - File Download summary: 個別ファイルダウンロード description: | 指定されたUUIDとファイルIDの個別ファイルをダウンロードします。 operationId: downloadSingleFile x-codeSamples: - lang: cURL source: | curl -X GET https://api.re-port-flow.com/v1/file/download/550e8400-e29b-41d4-a716-446655440000/file_123456 \ -H "appkey: your-application-key" \ --output invoice.pdf parameters: - name: uuid in: path required: true schema: type: string format: uuid description: ファイルUUID - name: fileId in: path required: true schema: type: string description: ファイルID responses: '200': description: ファイルダウンロード成功 headers: File-ID: schema: type: string content: application/pdf: schema: type: string format: binary '404': description: ファイルが見つかりません /file/design/parameter/{designId}: get: tags: - Design Parameters summary: デザインパラメータ構造を取得 description: | 指定されたデザインIDのパラメータ構造を取得します。 PDFやサムネイル生成時に必要なパラメータのスキーマを確認できます。 operationId: getDesignParameters x-codeSamples: - lang: cURL source: | curl -X GET https://api.re-port-flow.com/v1/file/design/parameter/550e8400-e29b-41d4-a716-446655440000 \ -H "appkey: your-application-key" - lang: cURL (with version) source: | curl -X GET "https://api.re-port-flow.com/v1/file/design/parameter/550e8400-e29b-41d4-a716-446655440000?version=1" \ -H "appkey: your-application-key" parameters: - name: designId in: path required: true schema: type: string format: uuid description: デザインID - name: version in: query required: false schema: type: integer description: デザインのバージョン番号(省略時は最新バージョン) responses: '200': description: パラメータ構造の取得成功 content: application/json: schema: $ref: '#/components/schemas/GetDesignParametersResDto' '404': description: デザインが見つかりません '500': description: 内部サーバーエラー ```