Backend examples — Node, Next.js, Python
Working code for calling the Renduo API from the three most common backends.
Ready-to-copy snippets for calling Renduo from common backends. All of them
authenticate with a Bearer token (RENDUO_API_KEY), generate a PDF in sync
mode, and save it. For background volume, switch mode to async and receive
a webhook — see the async + webhooks guide.
Node.js
If your backend runs on Node.js, use the official
@renduo/sdk — it returns the PDF already decoded as
a Buffer (no base64 step) and handles errors and async polling for you:
import { Renduo } from '@renduo/sdk'
import { writeFile } from 'node:fs/promises'
const renduo = new Renduo({ apiKey: process.env.RENDUO_API_KEY! })
const { pdf } = await renduo.generate({
templateId: process.env.TEMPLATE_ID!,
payload: { title: 'Invoice 2026-001', customer: 'Acme Corp' },
})
await writeFile('output.pdf', pdf)Prefer the raw API over an extra dependency? Here's the same thing with plain
fetch:
// generate.mjs
import { writeFileSync } from 'node:fs'
const API_URL = 'https://api.renduo.dev'
const API_KEY = process.env.RENDUO_API_KEY
const TEMPLATE_ID = process.env.TEMPLATE_ID
const res = await fetch(`${API_URL}/v1/generate`, {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
templateId: TEMPLATE_ID,
mode: 'sync',
payload: { title: 'Invoice 2026-001', customer: 'Acme Corp' },
}),
})
if (!res.ok) {
const err = await res.json()
throw new Error(`${err.error}: ${err.message}`)
}
const data = await res.json()
writeFileSync('output.pdf', Buffer.from(data.pdf, 'base64'))
console.log(`Saved output.pdf (${data.durationMs}ms)`)Next.js (Route Handler / Server Action)
// app/api/pdf/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function POST(req: NextRequest) {
const body = await req.json()
const res = await fetch('https://api.renduo.dev/v1/generate', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.RENDUO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
templateId: body.templateId,
mode: 'sync',
payload: body.payload,
}),
// Keep server actions / route handlers from timing out the render window.
// Renduo's own timeout is 10s; give this request a little more.
signal: AbortSignal.timeout(12_000),
})
if (!res.ok) {
const err = await res.json()
return NextResponse.json({ error: err.error, message: err.message }, { status: res.status })
}
const data = await res.json()
// Return the PDF as a binary download; use base64 only if you need to embed
// it in a JSON response.
return new NextResponse(Buffer.from(data.pdf, 'base64'), {
headers: { 'Content-Type': 'application/pdf' },
})
}Important: always call Renduo from the server (Route Handler or Server Action) — never from client components. Your API key must never reach the browser.
Python
# generate.py
import base64
import json
import os
import urllib.request
API_URL = "https://api.renduo.dev"
API_KEY = os.environ["RENDUO_API_KEY"]
TEMPLATE_ID = os.environ["TEMPLATE_ID"]
body = json.dumps({
"templateId": TEMPLATE_ID,
"mode": "sync",
"payload": {"title": "Invoice 2026-001", "customer": "Acme Corp"},
}).encode()
req = urllib.request.Request(
f"{API_URL}/v1/generate",
data=body,
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(req) as res:
data = json.loads(res.read())
with open("output.pdf", "wb") as f:
f.write(base64.b64decode(data["pdf"]))
print(f"Saved output.pdf ({data['durationMs']}ms)")Or with requests:
import base64, os, requests
res = requests.post(
"https://api.renduo.dev/v1/generate",
headers={
"Authorization": f"Bearer {os.environ['RENDUO_API_KEY']}",
"Content-Type": "application/json",
},
json={
"templateId": os.environ["TEMPLATE_ID"],
"mode": "sync",
"payload": {"title": "Invoice 2026-001", "customer": "Acme Corp"},
},
timeout=15,
)
res.raise_for_status()
with open("output.pdf", "wb") as f:
f.write(base64.b64decode(res.json()["pdf"]))Handling errors
Every backend should handle the structured error shape:
{
"error": "VALIDATION_ERROR",
"message": "Invalid data.",
"details": { "…": "…" },
"requestId": "…"
}Common codes: VALIDATION_ERROR (400), TEMPLATE_NOT_FOUND (404),
QUOTA_EXCEEDED (402), GENERATION_TIMEOUT (504), RENDER_ERROR (500).
Check the status code first, then read error for the machine-readable code
and log requestId for support.
See also
- First PDF in 5 minutes — the full end-to-end flow.
- Node.js SDK — the typed client for TypeScript/Node backends.
- Async + webhooks end-to-end — for background generation.
- API reference — every endpoint.