Complete reference for the Bytedoc REST API — PDF/DOCX document generation, filling, merging, splitting, and encryption.
https://api.bytedoc.dev/v1
All endpoints are prefixed with /v1/.
Every request must include an API key in the Authorization header:
Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
| Key Prefix | Environment | Description |
|---|---|---|
pk_live_ |
Production | Full access, documents count toward billing |
pk_test_ |
Sandbox | Watermarked output, stricter limits (60 docs/day, 60 req/min, 1h retention, no webhooks) |
Templates are the reusable source files (PDF forms, HTML templates, DOCX templates) that you fill or render with dynamic data.
POST /v1/templates
Upload a PDF, HTML, or DOCX file as a new template.
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file |
File | Yes | The template file (.pdf, .html, .docx) |
name |
String | No | Display name. Defaults to the filename. |
Response 201 Created
{
"id": "tpl_abc123def456ghij",
"name": "Invoice Template",
"type": "pdf",
"fields_count": 12,
"created_at": "2026-03-10T14:30:00.000Z"
}POST /v1/templates/from-pdf
Converts a static PDF into an editable HTML template using a 2-pass AI pipeline:
- Image extraction — each page is rendered as an image
- Structure analysis — AI identifies layout, fields, and data patterns
- HTML generation — produces a Handlebars HTML template with CSS
- Post-processing — cleanup and field normalization
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file |
File | Yes | Source PDF file |
name |
String | No | Display name for the generated template |
Response 201 Created
The template is returned immediately with status: "processing". Poll GET /v1/templates/:id until the status changes to completed or failed.
{
"id": "tpl_abc123def456ghij",
"name": "Converted Invoice",
"type": "html",
"status": "processing",
"fields_count": 0,
"created_at": "2026-03-10T14:30:00.000Z"
}GET /v1/templates
Returns all templates for the authenticated account.
Response 200 OK
{
"data": [
{
"id": "tpl_abc123def456ghij",
"name": "Invoice Template",
"type": "pdf",
"fields_count": 12,
"created_at": "2026-03-10T14:30:00.000Z"
}
]
}GET /v1/templates/:id
Returns a single template by ID.
Response 200 OK
{
"id": "tpl_abc123def456ghij",
"name": "Invoice Template",
"type": "pdf",
"fields_count": 12,
"created_at": "2026-03-10T14:30:00.000Z"
}GET /v1/templates/:id/fields
Returns the list of fillable fields detected in the template.
Response 200 OK
{
"data": [
{
"name": "client_name",
"type": "text",
"label": "Client Name",
"default": ""
},
{
"name": "invoice_date",
"type": "text",
"label": "Invoice Date",
"default": ""
}
]
}PATCH /v1/templates/:id/fields
Update labels and default values for template fields.
Request Body
{
"fields": [
{
"name": "client_name",
"label": "Customer Full Name",
"default": "John Doe"
}
]
}Response 200 OK — Updated fields array.
GET /v1/templates/:id/content
Downloads the original template file.
Response 200 OK — Binary file content with appropriate Content-Type header.
PATCH /v1/templates/:id
Replace the template file. The new file must be the same type as the original (e.g., PDF replaces PDF).
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file |
File | Yes | The replacement template file |
Response 200 OK — Updated template object.
DELETE /v1/templates/:id
Permanently deletes a template and its stored file.
Response 200 OK
{
"success": true
}Documents are the output files produced by filling templates or generating PDFs.
POST /v1/documents/fill
Fills an AcroForm PDF template with the provided data.
Request Body
{
"template_id": "tpl_abc123def456ghij",
"data": {
"client_name": "Acme Corp",
"invoice_date": "2026-03-10",
"total": "1,250.00"
},
"options": {
"flatten": true,
"filename": "invoice-acme-march.pdf"
}
}| Field | Type | Required | Description |
|---|---|---|---|
template_id |
String | Yes | ID of a PDF template |
data |
Object | Yes | Key-value pairs matching template field names |
options.flatten |
Boolean | No | Flatten form fields (non-editable). Default: false |
options.filename |
String | No | Custom filename for the output |
options.append |
Array | No | Array of source objects ({ "type": "document"|"template", "id": "..." }) to append after the primary document. Max 20. PDF-only. |
Response 201 Created
{
"id": "doc_xyz789uvw012klmn",
"status": "completed",
"download_url": "https://api.bytedoc.dev/v1/documents/doc_xyz789uvw012klmn/download",
"expires_at": "2026-04-09T14:30:00.000Z",
"filename": "invoice-acme-march.pdf",
"pages": 2,
"size_bytes": 145320
}POST /v1/documents/generate
Renders an HTML template with data and converts it to PDF using a headless browser.
Request Body
{
"template_id": "tpl_abc123def456ghij",
"data": {
"company_name": "Acme Corp",
"items": [
{ "description": "Consulting", "quantity": 10, "unit_price": 150 }
],
"total": "1,500.00"
},
"options": {
"format": "A4",
"margin": {
"top": "20mm",
"right": "15mm",
"bottom": "20mm",
"left": "15mm"
},
"landscape": false,
"filename": "invoice-march.pdf"
}
}| Field | Type | Required | Description |
|---|---|---|---|
template_id |
String | Yes | ID of an HTML template |
data |
Object | Yes | Data passed to the Handlebars template |
options.format |
String | No | Page size: "A4", "A3", "Letter", "Legal". Default: "A4" |
options.margin |
Object | No | Page margins (top, right, bottom, left) |
options.landscape |
Boolean | No | Landscape orientation. Default: false |
options.filename |
String | No | Custom filename for the output |
options.append |
Array | No | Array of source objects ({ "type": "document"|"template", "id": "..." }) to append after the primary document. Max 20. PDF-only. |
Response 201 Created — Same structure as Fill PDF.
POST /v1/documents/fill-docx
Fills a DOCX template with the provided data using docxtemplater syntax.
Request Body
{
"template_id": "tpl_abc123def456ghij",
"data": {
"client_name": "Acme Corp",
"address": "123 Main Street"
},
"options": {
"filename": "contract-acme.docx"
}
}| Field | Type | Required | Description |
|---|---|---|---|
template_id |
String | Yes | ID of a DOCX template |
data |
Object | Yes | Key-value pairs matching template placeholders |
options.filename |
String | No | Custom filename for the output |
Response 201 Created — Same structure as Fill PDF.
POST /v1/documents/merge
Merge multiple PDFs into a single document. Accepts a combination of existing documents, templates, and uploaded files.
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
sources |
JSON String | Yes | Array of source objects (min 2, max 20) |
options |
JSON String | No | { "filename": "merged.pdf" } |
file_0 ... file_N |
File | Conditional | Files referenced by index in sources |
Source object types:
[
{ "type": "document", "id": "doc_xxx" },
{ "type": "template", "id": "tpl_xxx" },
{ "type": "file", "index": 0 }
]Response 201 Created — Same structure as Fill PDF.
POST /v1/documents/split
Extract specific pages from a PDF into a new document.
Request Body
{
"source": {
"type": "document",
"id": "doc_xyz789uvw012klmn"
},
"pages": "1-3,5",
"options": {
"filename": "pages-1-3-5.pdf"
}
}| Field | Type | Required | Description |
|---|---|---|---|
source |
Object | Yes | `{ "type": "document" |
pages |
String | Yes | Page range expression (e.g., "1-3,5", "2", "1,3-5,8") |
options.filename |
String | No | Custom filename for the output |
Response 201 Created — Same structure as Fill PDF.
POST /v1/documents/encrypt
Apply password protection and permission restrictions to a PDF.
Request Body
{
"source": {
"type": "document",
"id": "doc_xyz789uvw012klmn"
},
"encryption": {
"user_password": "read-only-pass",
"owner_password": "admin-pass",
"permissions": {
"printing": true,
"modifying": false,
"copying": false,
"annotating": true
},
"algorithm": "aes256"
},
"options": {
"filename": "secured-document.pdf"
}
}| Field | Type | Required | Description |
|---|---|---|---|
source |
Object | Yes | `{ "type": "document" |
encryption.user_password |
String | Yes | Password required to open the document |
encryption.owner_password |
String | No | Password for full access. Defaults to user_password. |
encryption.permissions |
Object | No | Permission flags (all default to true) |
encryption.permissions.printing |
Boolean | No | Allow printing |
encryption.permissions.modifying |
Boolean | No | Allow content modification |
encryption.permissions.copying |
Boolean | No | Allow text/image copying |
encryption.permissions.annotating |
Boolean | No | Allow annotations |
encryption.algorithm |
String | No | "aes256" (default), "aes128", or "rc4" |
options.filename |
String | No | Custom filename for the output |
Response 201 Created — Same structure as Fill PDF.
GET /v1/documents
Returns a paginated list of documents.
| Parameter | Type | Required | Description |
|---|---|---|---|
limit |
Integer | No | Results per page (1-100). Default: 20 |
offset |
Integer | No | Pagination offset. Default: 0 |
status |
String | No | Filter by status (completed, processing, failed) |
template_id |
String | No | Filter by source template |
Response 200 OK
{
"data": [ ... ],
"total": 142,
"limit": 20,
"offset": 0
}GET /v1/documents/:id
Returns details for a single document.
Response 200 OK
{
"id": "doc_xyz789uvw012klmn",
"status": "completed",
"download_url": "https://api.bytedoc.dev/v1/documents/doc_xyz789uvw012klmn/download",
"expires_at": "2026-04-09T14:30:00.000Z",
"filename": "invoice-acme-march.pdf",
"pages": 2,
"size_bytes": 145320,
"template_id": "tpl_abc123def456ghij",
"created_at": "2026-03-10T14:30:00.000Z"
}GET /v1/documents/:id/download
Returns a signed, time-limited download URL for the document.
Response 200 OK
{
"download_url": "https://storage.bytedoc.dev/...",
"expires_in": 3600
}Error 410 Gone — Returned if the document has passed its retention period.
DELETE /v1/documents/:id
Permanently deletes a document and its stored file.
Response 200 OK
{
"success": true
}GET /v1/usage
Returns document generation usage for the current billing period.
Response 200 OK
{
"fill": 234,
"generate": 156,
"total": 390,
"limit": 1000,
"period": {
"start": "2026-03-01T00:00:00.000Z",
"end": "2026-03-31T23:59:59.999Z"
}
}GET /v1/account
Returns information about the authenticated account.
Response 200 OK
{
"id": "acc_abc123def456ghij",
"email": "user@example.com",
"name": "Acme Corp",
"plan": "pro",
"isAdmin": false,
"created_at": "2026-01-15T10:00:00.000Z"
}PATCH /v1/account
Update account details.
Request Body
{
"name": "Acme Corporation"
}Response 200 OK — Updated account object.
GET /v1/api-keys
Returns all API keys for the account. Key values are partially masked.
Response 200 OK
{
"data": [
{
"id": "ak_abc123def456ghij",
"name": "Production Key",
"mode": "live",
"prefix": "pk_live_abc1****",
"created_at": "2026-03-01T10:00:00.000Z"
}
]
}POST /v1/api-keys
Creates a new API key. The full key is returned only once in the response.
Request Body
{
"name": "My New Key",
"mode": "live"
}| Field | Type | Required | Description |
|---|---|---|---|
name |
String | No | Display name for the key |
mode |
String | No | "live" (default) or "test" for sandbox mode |
Response 201 Created
{
"id": "ak_abc123def456ghij",
"name": "My New Key",
"mode": "live",
"key": "pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}Important: Store the
keyvalue securely. It cannot be retrieved again after creation.
DELETE /v1/api-keys/:id
Revokes and deletes an API key. This action is immediate and irreversible.
Response 200 OK
{
"success": true
}POST /v1/billing/checkout
Creates a Stripe checkout session to subscribe or change plans.
Request Body
{
"plan": "pro"
}Response 200 OK
{
"url": "https://checkout.stripe.com/..."
}POST /v1/billing/portal
Returns a URL to the Stripe customer billing portal for managing subscriptions, payment methods, and invoices.
Response 200 OK
{
"url": "https://billing.stripe.com/..."
}GET /v1/billing/status
Returns the current billing and subscription status.
Response 200 OK
{
"plan": "pro",
"status": "active",
"current_period_end": "2026-04-10T00:00:00.000Z",
"cancel_at_period_end": false
}POST /v1/billing/change-plan
Upgrade, downgrade, cancel, or reactivate your subscription.
Request Body
{
"plan": "business"
}| Field | Type | Required | Description |
|---|---|---|---|
plan |
String | Yes | Target plan: free, starter, pro, business, enterprise |
Response 200 OK
{
"plan": "business",
"status": "active",
"message": "Plan changed successfully"
}Webhooks allow you to receive HTTP notifications when documents are processed. Available on Pro plans and above.
POST /v1/webhooks
Request Body
{
"url": "https://your-app.com/webhooks/bytedoc",
"events": ["document.completed", "document.failed"],
"secret": "whsec_your_signing_secret"
}| Field | Type | Required | Description |
|---|---|---|---|
url |
String | Yes | HTTPS endpoint URL |
events |
Array | Yes | Event types to subscribe to |
secret |
String | Yes | HMAC signing secret for verifying payloads |
Response 201 Created
{
"id": "wh_abc123def456ghij",
"url": "https://your-app.com/webhooks/bytedoc",
"events": ["document.completed", "document.failed"],
"created_at": "2026-03-10T14:30:00.000Z"
}GET /v1/webhooks
Response 200 OK
{
"data": [ ... ]
}GET /v1/webhooks/:id
Response 200 OK — Webhook endpoint object.
PATCH /v1/webhooks/:id
Request Body
{
"url": "https://your-app.com/webhooks/v2",
"events": ["document.completed"]
}Response 200 OK — Updated webhook endpoint object.
DELETE /v1/webhooks/:id
Response 200 OK
{
"success": true
}GET /v1/health
Public endpoint (no authentication required).
Response 200 OK
{
"status": "ok",
"version": "1.4.0-beta.1"
}All errors follow a consistent format:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description of what went wrong."
}
}| Code | HTTP Status | Description |
|---|---|---|
VALIDATION_ERROR |
400 | Request body or parameters failed validation |
INVALID_TEMPLATE_TYPE |
400 | Template type does not match the operation |
UNAUTHORIZED |
401 | Missing or invalid API key |
TEMPLATE_NOT_FOUND |
404 | Template does not exist or belongs to another account |
DOCUMENT_NOT_FOUND |
404 | Document does not exist or belongs to another account |
DOCUMENT_EXPIRED |
410 | Document retention period has passed |
USAGE_LIMIT_EXCEEDED |
429 | Monthly document limit reached |
RATE_LIMIT_EXCEEDED |
429 | Too many requests per minute |
INTERNAL_ERROR |
500 | Unexpected server error |
See Error Codes Reference for detailed resolution guidance.
Documents are automatically deleted after the retention period for your plan:
| Plan | Retention |
|---|---|
| Free | 7 days |
| Starter | 30 days |
| Pro | 90 days |
| Business | 365 days |
| Enterprise | Unlimited |
Download documents before expiry, or upgrade your plan for longer retention.
| Key Type | Limit |
|---|---|
pk_live_ |
Based on plan |
pk_test_ |
60 requests/minute, 60 documents/day |
Rate-limited responses return 429 Too Many Requests with a Retry-After header.