Coze integration (OpenAPI plugin)
You can call Re:port Flow from a Coze bot (ByteDance's AI agent platform) and generate PDFs straight from a conversation. Coze exposes external services as plugins defined by OpenAPI, so you register the Re:port Flow REST API as one.
An official listing in the Coze Plugin Store is in preparation. For now, follow the steps below to register Re:port Flow as your own private plugin in your Coze account. It uses your own workspace API key, so nothing is shared with other users.
Coze or MCP — which should I use?
Re:port Flow offers two entry points for AI agents. Pick based on whether your client speaks MCP.
| Client | Recommended |
|---|---|
| Claude Desktop / Claude Code / Cursor / VS Code | MCP server (reportflow-mcp) |
| Coze bots and workflows | The plugin approach on this page |
The MCP server authorizes each user through OAuth 2.0, while a Coze plugin connects with OpenAPI plus an API key. Both call the same Re:port Flow API, so the generated PDF is identical.
What you need first
- A Re:port Flow workspace — create one at re-port-flow.com
- An application key (a string starting with
ak_) — found under Workspace settings → API連携. See API keys - At least one template to generate from. If you have none yet, copy one from the template gallery
Registering the plugin in Coze
You do not need to enter the tools one by one. Import the OpenAPI file Re:port Flow publishes for Coze, and the base URL, tools, parameters and descriptions are registered in one go.
1. Import the OpenAPI file
In the Coze dashboard choose Create plugin → Create a plugin based on API, and use the JSON / YAML import to load this file:
https://doc.re-port-flow.com/openapi/coze-plugin.yaml
To import it as a local file instead, download it first:
curl -fsSL -o coze-plugin.yaml https://doc.re-port-flow.com/openapi/coze-plugin.yaml
The file is written in OpenAPI 3.0 so that Coze can read it (the full API specification, https://doc.re-port-flow.com/openapi/content-service.yaml, is OpenAPI 3.1). It already contains the base URL (https://api.re-port-flow.com/v1).
2. Configure authentication
After importing, set the plugin's authorization as follows.
| Field | Value |
|---|---|
| Plugin URL (base URL) | https://api.re-port-flow.com/v1 (keep the value from the imported file) |
| Authorization method | Service (sends a token in a header) |
| Location | Header |
| Parameter name (Key) | appkey |
| Service token | Your application key (ak_...) |
The header name appkey is lowercase. A wrong value returns 401; a missing header returns 412. See Authentication for how to tell them apart.
3. Check the imported tools
The import registers these four tools for chat use. This is also the order the agent should call them in. The file also contains the synchronous generatePdfSync, but it is not used from chat, for the reason below. The import registers generatePdfSync as a tool too, so delete generatePdfSync from the plugin's tool list (if it stays, the agent may pick it, using up pages while getting nothing back it can hand to the user).
| Tool | Method | Path | Purpose |
|---|---|---|---|
listTemplates | GET | /file/designs | List templates in the workspace (returns id and latestVersion) |
getDesignParameters | GET | /file/design/parameter/{designId} | Get the parameter schema the template expects |
generatePdfAsync | POST | /file/async/single | Start generation; returns requestId and files[] as JSON |
downloadGeneratedFile | GET | /file/download/{requestId}/{fileId} | Confirm the job finished (see below) |
Request and response structures are documented here:
The request body for generatePdfAsync looks like this:
{
"designId": "0eUDdgAjNXrrItA2",
"version": 1,
"content": {
"fileName": "invoice_2026-08.pdf",
"params": {
"customerName": "Sample Inc.",
"invoiceNumber": "INV-2026-0812",
"amount": 110000
}
}
}
The keys inside params must match the name of each field returned by getDesignParameters. They differ per template, so always fetch the schema instead of hard-coding them.
4. Check the tool descriptions
A Coze agent decides which tool to call by reading its description. The descriptions in the imported file already say the following; if you edit them, keep these points — they change behaviour in practice:
- Always call
getDesignParametersbeforegeneratePdfAsync, and never invent parameter values. Without this, the agent will happily fillparamswith fabricated data and produce a wrong document. - A
202fromgeneratePdfAsyncmeans "queued", not "done". Report completion only afterdownloadGeneratedFilereturns200. Without this, delayed or failed jobs get announced to the user as finished documents. - Do not set
shareType. Leave it at the default"01"(workspace only).
Why async instead of sync generation?
By default, POST /file/sync/single returns the raw PDF bytes as the response body, and the identifiers you need afterwards (File-URL, Request-Id, X-File-Mapping) are sent only as response headers. Adding ?response=json returns the identifiers as JSON, but this plugin definition does not include it.
Coze builds a tool's output from the response body, so registering the sync endpoint leaves the agent with "the PDF was created, but I cannot extract anything to show the user". From a chat agent, use POST /file/async/single, which returns JSON.
Sync generation itself works exactly as described in Sync single PDF and is fine for clients that can read response headers, such as your own backend.
How do I know generation finished?
The 202 Accepted from generatePdfAsync means the job was queued, not that the PDF exists. Stopping there makes the agent answer "done" even when generation is delayed or has failed.
The Re:port Flow API has a generation status endpoint (GET /v1/file/status/{requestId}), but this plugin's OpenAPI definition does not include it. With the plugin, you confirm completion through the download endpoint:
GET https://api.re-port-flow.com/v1/file/download/{requestId}/{fileId}
200— the file exists, generation is complete. Only now should you tell the user it is ready409(FILE_NOT_READY) — still being generated. Wait the number of seconds in the response body'sretryAfter(currently 2) and call again (Coze cannot read response headers, so use the body value rather than theRetry-Afterheader)422(FILE_GENERATION_FAILED) — generation failed. Do not retry; tell the user it failed404(FILE_NOT_FOUND) — the ids are wrong, belong to another workspace, the single generation failed more than 24 hours ago, or the output was deleted. A file that is still generating normally returns 409, but a request whose job record could not be saved returns 404 while still generating. If you get a 404 right after generating, wait a few seconds and check once or twice more before reporting a failure
Take requestId and fileId from the generatePdfAsync response (requestId and files[0].fileId). The response body is the PDF itself, but for the completion check only the status code matters.
When you call from your own backend, Get Generation Status (GET /v1/file/status/{requestId}) tells you processing / completed / failed and the failure reason directly.
If you want a guaranteed-complete result in a single call, sync generation (/file/sync/single) provides it — but as described above, Coze cannot extract the identifiers from the default response (adding ?response=json returns them as JSON). Sync is the simpler choice when you route through your own backend.
How do I hand the PDF to the user?
Once completion is confirmed, hand over files[].share.url from the generatePdfAsync response. Whether opening that link requires a Re:port Flow login depends on the shareType you sent.
shareType | Meaning | Needed to open the link |
|---|---|---|
"01" (default) | Workspace only | A Re:port Flow login |
"02" | Invited users | An invitation plus login |
"03" | Public URL | Nothing — anyone with the URL can read it |
Do not reach for "03" merely because the user wants to open the file from chat. "03" means everyone who has the URL can read the PDF, which for an invoice exposes your customer's details.
Use "03" only when the user has explicitly asked to publish the PDF, and consider pairing it with passcodeEnabled: true.
Where do the templates come from?
listTemplates responds with an object — { "designs": [...], "total": 12, "page": 1, "pageSize": 30, "totalPages": 1 } — not a top-level array. If designs is an empty array, the workspace has no templates yet. Copy a public template — invoice, quotation, receipt and more — from the template gallery into your workspace, and it will appear in listTemplates from then on.
The gallery search API (GET https://re-port-flow.com/api/v1/public/templates) needs no authentication, so you can register a separate no-auth plugin just for browsing templates. Note that a gallery slug cannot be used to generate a PDF — generation needs the designId you get after copying.
FAQ
Does this work on Coze's free plan?
There is no restriction on the Re:port Flow side. Whether you can create plugins depends on Coze's own plan rules. Re:port Flow's FREE plan allows up to 30 PDF pages per month.
Can I pass Chinese text as parameter values?
You can pass UTF-8 strings in params as they are, but whether they show up in the PDF depends on the template's font: the font embedded in your template must contain the characters you use. Japanese typography (kinsoku processing and font embedding) is supported, but whether Simplified or Traditional Chinese fonts are available as standard is unverified (Template Capabilities). Before relying on it, check that a font covering those characters can be selected in the template editor, and check the output with your real data.
Are there rate limits?
Per workspace: 30 req/min for the sync endpoints and 100 req/min for async and download endpoints. Exceeding a limit returns 429; the seconds to wait are in the response body's retryAfter (also sent as the Retry-After header). See Limitations.
What should I check when generation fails?
Start with the HTTP status. A 400 almost always means params does not match the template schema — compare your key names and types against the getDesignParameters output. 401 and 412 are authentication problems. See Error handling.