Blog/Use cases

Generate contract PDF files from templates

Use cases··6 min read

To generate contract pdf output on RelayPDF you merge data into a published Handlebars layout (POST /v1/pdf with templateId and templateData) or you fill an existing AcroForm and flatten it (POST /v1/pdf/form/fill). That is the whole product surface for NDAs, offer letters, and other letter-sized agreements. RelayPDF does not e-sign. There is no envelope, no signer routing, no certificate of completion, and no click-to-sign widget. You get a PDF. Counsel and your signing product sit after that file exists.

Stock legal and HR layouts live on the gallery. NDA (Letter, category Legal): https://relaypdf.com/pdf-templates/legal/nda (galleryId nda). Offer letter (Letter, category Human resources): https://relaypdf.com/pdf-templates/human-resources/offer-letter (galleryId offer-letter). There is no public /pdf-templates/legal/contract page as of this writing. A custom MSA or SOW is your HTML or a cloned draft. Catalog hub: https://relaypdf.com/pdf-templates. Template API: https://relaypdf.com/docs/templates. Form flatten: https://relaypdf.com/pdf-tools/form and https://relaypdf.com/blog/fill-pdf-form-api.

Two documented paths

Path A is a stored template. Clone or author Handlebars HTML, publish, then render. POST /v1/pdf accepts exactly one source: html, url, markdown, or templateId. For a repeatable contract, use templateId. templateData is a JSON object. Optional templateVersion pins a published integer. Optional strict true fails the job when a Handlebars path is missing. Request options override saved PDF options for that job. HTML in drafts is a UTF-8 JSON string, not Base64. The print engine is Chromium.

Path B is an AcroForm the legal team already owns. POST /v1/pdf/form/fields lists field names. POST /v1/pdf/form/fill writes values and flattens by default. Flattened widgets become ordinary page content. Recipients cannot type into the same fields later. That is the point for a locked offer or NDA you will store, not a fillable draft. The form API is AcroForm only. It is not XFA and it is not OCR. It does not create missing fields and it does not place signature widgets.

JobEndpointWhen
Merge Handlebars + JSONPOST /v1/pdfYou control the layout; data is structured
One-off HTML letterPOST /v1/pdf htmlA single custom agreement, no stored template
List AcroForm namesPOST /v1/pdf/form/fieldsCounsel gave you a fillable PDF
Fill and flattenPOST /v1/pdf/form/fillLock values into that PDF
Not availableE-sign, envelopes, certificates of completion

We do not e-sign

Say this in the integration notes so product does not invent a signing step. RelayPDF renders and, on the form path, flattens. A typed name in templateData is text on the page. It is not an electronic signature under any statute we document, because we document none. Do not call the API an ESIGN, UETA, or eIDAS product. Do not claim audit trails, signer identity, or multi-party routing. If you need those, generate the PDF here, then hand the bytes or the 24-hour file URL to whatever signing system you already run.

flatten on /v1/pdf/form/fill is a PDF-form lock, not a signature. Setting flatten false leaves widgets editable. The product page states flatten is on by default. OpenAPI lists flatten as a boolean without a schema default; treat the product page as the default and send flatten true when you want the lock explicit.

Clone the NDA or offer letter

POST /v1/templates with galleryId clones a stock layout into an account-owned draft. Publish before you render. GET /v1/templates/:id returns the draft if one exists, otherwise the published copy. A draft is not a renderable templateId on /v1/pdf.

curl https://api.relaypdf.com/v1/templates
  -H "Authorization: Bearer pdf_live_..."
  -H "Content-Type: application/json"
  -d '{
    "name": "NDA Template",
    "galleryId": "nda"
  }'

Offer letter clone uses galleryId offer-letter and the same POST /v1/templates body shape. After publish, send your slug or UUID as templateId. Pin templateVersion when counsel freezes clause text. Keep editing the draft; the last published version stays live until you publish again.

Render: templateId plus templateData

Host https://api.relaypdf.com. Bearer pdf_live_…. filename, if sent, must end in .pdf. response is binary (default), url, or async. binary returns application/pdf with x-relaypdf-id, x-relaypdf-size, and content-disposition. url returns a 24-hour GET /v1/files/:id link (no key). async returns 202; poll GET /v1/jobs/:id. callbackUrl must be https. Query-string keys are rejected. PDF from a template debits the render rate ($0.015 at launch). Failed jobs, 429, and 402 are not billed.

Sample NDA templateData from the public template page (effectiveOn, partyA, partyB, clauses). That is gallery copy, not legal advice.

curl -X POST https://api.relaypdf.com/v1/pdf
  -H "Authorization: Bearer pdf_live_..."
  -H "Content-Type: application/json"
  -d '{
    "templateId": "nda",
    "templateData": {
      "effectiveOn": "2026-08-21",
      "partyA": "Westmere Gallery LLC",
      "partyB": "Northline Atelier Inc.",
      "clauses": [
        "Confidential information includes non-public business, technical, financial, and customer information disclosed by either party.",
        "Receiving parties may use confidential information solely for evaluating and performing the contemplated relationship."
      ]
    },
    "filename": "nda.pdf",
    "templateVersion": 1,
    "strict": true
  }'
  --output nda.pdf

Offer letter sample fields on the public page: company, issuedOn, candidate, role, startOn, salary, currency, location, closing. Python SDK field names in the JSON stay camelCase.

from relaypdf import RelayPDF
client = RelayPDF(api_key="pdf_live_...")
pdf = client.pdf.from_template(
    templateId="offer-letter",
    templateData={
        "company": "Northline Atelier",
        "issuedOn": "2026-08-21",
        "candidate": "Elise Maren",
        "role": "Senior Environmental Designer",
        "startOn": "2026-10-01",
        "salary": 128000,
        "currency": "USD",
        "location": "New York, NY",
        "closing": "Please sign and return by Sep 1, 2026.",
    },
    filename="offer.pdf",
    strict=True,
)
pdf.save("offer.pdf")

Flatten a counsel-owned PDF

When the source is a fillable PDF, do not re-typeset it as Handlebars unless you intend to own layout. Inspect, then fill. Product JSON uses a url (or file) plus a fields object keyed by AcroForm names.

curl -X POST https://api.relaypdf.com/v1/pdf/form/fields
  -H "Authorization: Bearer pdf_live_..."
  -H "Content-Type: application/json"
  -d '{"url": "https://example.com/offer-acroform.pdf"}'
curl -X POST https://api.relaypdf.com/v1/pdf/form/fill
  -H "Authorization: Bearer pdf_live_..."
  -H "Content-Type: application/json"
  -d '{
    "url": "https://example.com/offer-acroform.pdf",
    "fields": {"Candidate": "Elise Maren", "StartDate": "2026-10-01"},
    "flatten": true,
    "filename": "offer-filled.pdf"
  }'
  --output offer-filled.pdf

Python: form_fields(**input) then form_fill(fields, **input). Node: formFields then formFill. CLI: relaypdf form-fields file.pdf and relaypdf form-fill file.pdf --fields '{...}' --out filled.pdf. Tools rate is $0.005 at launch. Same wallet as renders. Private, loopback, and metadata hosts on url return url_not_allowed. JSON only; no multipart. Full field notes: https://relaypdf.com/blog/fill-pdf-form-api.

What else is in scope

Letter format is the default page size on /v1/pdf (options.format letter). The NDA and offer-letter gallery pages are Letter. options.margin is CSS lengths if you print your own html. printBackground defaults to true. filename must end .pdf. Mixing html with templateId is invalid_request.

Related gallery pages that are not contracts: intake form (write-in stack from templateData) at https://relaypdf.com/pdf-templates/form/intake-form, project proposal at https://relaypdf.com/pdf-templates/proposal/project-proposal. Invoice merge is a different use case: https://relaypdf.com/blog/generate-invoice-pdf-api. Handlebars lifecycle (draft, publish, versions, restore, validate, preview): https://relaypdf.com/blog/handlebars-pdf-templates.

POST /v1/pdf/protect can password a finished file. POST /v1/pdf/merge can bind an NDA and an offer into one packet. Those are PDF tools, not signing. Do not invent PDF/A archival, HIPAA, or OCR on this path. We do not document them.

Errors

Failures return { error: { code, message } }. invalid_request for mixed sources, a filename that is not .pdf, or a missing fields object on fill. url_not_allowed for a private host. render_failed when Chromium cannot print. payment_required on an empty wallet. rate_limited with Retry-After. Failed jobs are not billed. Branch on error.code. Trial wallet is $5; trial 20/min; funded 60/min; burst 5/10s. GET /v1/account returns plan, rateTier, and wallet.balanceMillicents.

Ship it

Clone nda or offer-letter, publish, POST /v1/pdf with templateData. Or list AcroForm names and fill with flatten true. Do not add an e-sign step in this API. CTA gallery: https://relaypdf.com/pdf-templates. Docs: https://relaypdf.com/docs/templates and https://relaypdf.com/docs/pdf. Form flatten: https://relaypdf.com/pdf-tools/form. Sibling how-tos: https://relaypdf.com/blog/fill-pdf-form-api and https://relaypdf.com/blog/handlebars-pdf-templates.

Ready to generate?

One API for HTML, Markdown, URLs, and Office. REST, SDK, CLI, or MCP.