The API
Everything the builder can do, the API can do. The builder is no more than a shell around it, so you can drive the whole tool from another system.
Authentication
Every call needs an API key. Create one on API keys and send it along, in either header; both work:
X-Api-Key: mrf_a1b2c3_... Authorization: Bearer mrf_a1b2c3_...
A key belongs to exactly one organization and grants access to nothing outside it. If it is
missing or revoked, you get 401.
Endpoints
| Method | Path | What |
|---|---|---|
| GET | /api/status | Is the service up, which PDF engine is running, and how is the queue doing. |
| GET | /api/templates | Every template in your organization. Filter on name with ?search=. |
| GET | /api/templates/{id} | One template, including its source, sample JSON and bindings. |
| POST | /api/templates | Save or update a template. Trial-rendered against your sample JSON first. |
| POST | /api/templates/{id}/copy | Make a copy, with an id of its own. |
| DELETE | /api/templates/{id} | Delete a template. Calls on that id fail afterwards. |
| POST | /api/render | The real work: a template plus data returns a PDF. |
| GET | /api/usage | Your plan, your limits and what you have used this month. |
| POST | /mcp | The MCP server. See the MCP page. |
Making a PDF
POST https://morfee.furo.solutions/api/render
X-Api-Key: mrf_...
Content-Type: application/json
{
"templateId": "quote-a3f9c1",
"data": { "customer": { "name": "Van Dommelen Buitenleven" }, "lines": [ ... ] },
"filename": "Quote 2026-0148.pdf"
}
You get the PDF bytes back, with Content-Type: application/pdf. Three variants
through the format query parameter:
?format=pdf | the default: the bytes themselves |
?format=url | JSON with a download URL, valid for an hour. Easier in Power Automate |
?format=html | the rendered document without the PDF step, useful for checking |
If you would rather send a flat payload, leave out data and send the fields
themselves. Everything but templateId, filename and format then becomes the
data.
Saving a template without the builder
The builder turns your HTML and your bindings into a Handlebars template. If you already have one, or you want to push a template from another system, send it straight in:
POST https://morfee.furo.solutions/api/templates
{
"name": "Quote",
"hbs": "<!doctype html>...{{text customer.name}}...",
"sample": { "customer": { "name": "Van Dommelen" } },
"bindings": [],
"source": "<!doctype html>..."
}
The template is trial-rendered against your sample JSON first. If it does not render it is not
stored, and you are told why. Send id along to update an existing one.
Error codes
400 | something is wrong with your request, and it says what |
401 | no key, or a revoked one |
402 | you are at the limit of your plan |
404 | that template does not exist, or is not yours |
502 | the rendering itself went wrong |
503 | the render queue is full, or your request waited too long |
Power Automate
Use the HTTP action with ?format=url, then you never have to drag binary content
through your flow:
Method : POST
URI : https://morfee.furo.solutions/api/render?format=url
Headers : { "X-Api-Key": "mrf_...", "Content-Type": "application/json" }
Body : { "templateId": "quote-a3f9c1", "data": @{outputs('Compose_the_data')} }
The response holds url, filename and bytes. Put a second
HTTP action behind it (GET on that URL) to fetch the bytes, or hand the URL to whoever has to download it.