API · Docrya

Docrya API

Generate PDFs from your templates with a single HTTP request. Internal operator docs live in the repository under docs/internal/.

  1. Create your account
  2. Create a template
  3. Customize the template (columns)
  4. Generate an API key
  5. Call POST /api/v1/render
  6. Copy a code example

Create your account

Sign up to get a workspace where templates and API keys live.

Create a template

In the dashboard, open Templates and create a document template. Publish a version when you are ready—render requests use the template templateId (UUID) and optionally templateVersion (positive integer). You can copy the template id from the URL or the template detail UI.

Customize your template

In the template editor, layouts use commercial_document_v1: you define line item columns with a stable key and a display label. Required keys are description, quantity, unit_price, and line_total. You may add up to three optional columns: custom_1, custom_2, custom_3 (for example “SKU” or “Unit”).

If a custom column appears in the layout, every row in data.items must include that field (string or number), or validation will fail.

Full JSON schema for layouts: docs/LAYOUT-SCHEMA-V1.md in the repository.

Generate an API key

From the dashboard, open the API card, generate a key, and copy it immediately—plaintext is shown only once. Store it in a secret manager or environment variable.

  • Dashboard → API card → Generate API key
  • Copy from the one-time modal
  • Send it as Authorization: Bearer <api_key> or X-API-Key

Request body (POST /api/v1/render)

Authenticate every request. The response body is a PDF (application/pdf).

  • Headers: Content-Type: application/json, plus Bearer or X-API-Key as above
  • templateId: UUID of the template
  • templateVersion: optional; omit to let the server resolve the active version
  • data: structured payload for the layout (company, client, line items, summary, optional order number and filename)

Example below includes custom_1 on the line item—use it only when your template layout defines that column.

{
  "templateId": "00000000-0000-4000-8000-000000000001",
  "templateVersion": 1,
  "data": {
    "company": {
      "name": "Acme Corp",
      "cnpj": "12.345.678/0001-99",
      "logo_url": null,
      "address": "123 Market St, Suite 100"
    },
    "client": {
      "name": "Jane Customer",
      "document": "ID-99281",
      "phone": "+1 555-0100"
    },
    "items": [
      {
        "description": "Consulting hours",
        "quantity": 2,
        "unit_price": 150,
        "total": 300,
        "custom_1": "SKU-42"
      }
    ],
    "summary": {
      "subtotal": 300,
      "discount": 0,
      "total": 300,
      "notes": "Thank you for your business."
    },
    "order_number": "SO-1001",
    "filename": "sales-order.pdf"
  }
}

Code examples

Replace https://your-domain.com, YOUR_API_KEY, and templateId in the JSON with your deployment URL, key, and template UUID. For local development, use http://localhost:3000.

cURL

curl -sS -X POST "https://your-domain.com/api/v1/render" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"templateId":"00000000-0000-4000-8000-000000000001","templateVersion":1,"data":{"company":{"name":"Acme Corp","cnpj":"12.345.678/0001-99","logo_url":null,"address":"123 Market St, Suite 100"},"client":{"name":"Jane Customer","document":"ID-99281","phone":"+1 555-0100"},"items":[{"description":"Consulting hours","quantity":2,"unit_price":150,"total":300,"custom_1":"SKU-42"}],"summary":{"subtotal":300,"discount":0,"total":300,"notes":"Thank you for your business."},"order_number":"SO-1001","filename":"sales-order.pdf"}}' \
  --output document.pdf

JavaScript (fetch)

const BASE_URL = "https://your-domain.com" // or http://localhost:3000 in development
const API_KEY = "YOUR_API_KEY"

const body = {
  "templateId": "00000000-0000-4000-8000-000000000001",
  "templateVersion": 1,
  "data": {
    "company": {
      "name": "Acme Corp",
      "cnpj": "12.345.678/0001-99",
      "logo_url": null,
      "address": "123 Market St, Suite 100"
    },
    "client": {
      "name": "Jane Customer",
      "document": "ID-99281",
      "phone": "+1 555-0100"
    },
    "items": [
      {
        "description": "Consulting hours",
        "quantity": 2,
        "unit_price": 150,
        "total": 300,
        "custom_1": "SKU-42"
      }
    ],
    "summary": {
      "subtotal": 300,
      "discount": 0,
      "total": 300,
      "notes": "Thank you for your business."
    },
    "order_number": "SO-1001",
    "filename": "sales-order.pdf"
  }
}

const res = await fetch(`${BASE_URL}/api/v1/render`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${API_KEY}`,
  },
  body: JSON.stringify(body),
})

if (!res.ok) {
  const err = await res.text()
  throw new Error(`Render failed: ${res.status} ${err}`)
}

const pdfBuffer = await res.arrayBuffer()
// Browser: save with Blob + object URL, or Node: fs.writeFileSync("out.pdf", Buffer.from(pdfBuffer))

Python (requests)

Install: pip install requests

import json
import requests

BASE_URL = "https://your-domain.com"  # e.g. http://localhost:3000
API_KEY = "YOUR_API_KEY"

payload = json.loads(r"""
{
  "templateId": "00000000-0000-4000-8000-000000000001",
  "templateVersion": 1,
  "data": {
    "company": {
      "name": "Acme Corp",
      "cnpj": "12.345.678/0001-99",
      "logo_url": null,
      "address": "123 Market St, Suite 100"
    },
    "client": {
      "name": "Jane Customer",
      "document": "ID-99281",
      "phone": "+1 555-0100"
    },
    "items": [
      {
        "description": "Consulting hours",
        "quantity": 2,
        "unit_price": 150,
        "total": 300,
        "custom_1": "SKU-42"
      }
    ],
    "summary": {
      "subtotal": 300,
      "discount": 0,
      "total": 300,
      "notes": "Thank you for your business."
    },
    "order_number": "SO-1001",
    "filename": "sales-order.pdf"
  }
}
""")

r = requests.post(
    f"{BASE_URL}/api/v1/render",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json=payload,
    timeout=120,
)
r.raise_for_status()

with open("document.pdf", "wb") as f:
    f.write(r.content)