Exeq Documentation
Exeq lets you embed PDF form building and document signing into any website. Everything runs client-side in the browser — no backend required.
Live Demo
Try the full experience — build a template or sign a sample NDA.
Installation
1npm install @unlev/exeqImport the components and styles in your React app:
1import { DesignerView, SignerView } from '@unlev/exeq';
2import '@unlev/exeq/styles';Template Editor
The editor lets you open a PDF and place form fields on it. Everything runs locally in the browser — your files are never uploaded to any server. You can pre-fill or pre-sign fields that belong to you (the "Sender" role) before exporting the template for signers.
1import { DesignerView } from '@unlev/exeq';
2import '@unlev/exeq/styles';
3
4function Editor() {
5 return (
6 <DesignerView
7 apiKey="your-api-key"
8 initialPdfUrl="/contracts/blank.pdf"
9 onSave={(template) => {
10 // Save the template JSON to your server
11 console.log(template);
12 }}
13 />
14 );
15}DesignerView Props
| Prop | Type | Required | Description |
|---|---|---|---|
apiKey | string | Yes | Your Exeq API key |
initialPdfUrl | string | No | URL to a PDF to pre-load |
initialTemplate | Template | No | Existing template to resume editing |
onSave | (template: Template) => void | No | Called when "Export Template" is clicked. If omitted, downloads JSON. |
What the editor does
- Renders each PDF page as an image
- Drag fields from the palette onto the page, or click to place
- Select a field to edit its properties (type, label, placeholder, required, assignee)
- Drag fields to reposition, resize via the corner handle
- Assign fields to signer roles — "Sender" is for you (the host), other roles are for signers
- Pre-fill or pre-sign Sender fields before exporting
- Blackout and whiteout fields for redaction
- Export the template JSON
Document Signing
Shows a PDF with pre-placed fields for the signer to fill out. Fields belonging to the Sender (pre-filled by the host) appear as read-only. The signer fills their fields, then the completed PDF is generated client-side.
1import { SignerView } from '@unlev/exeq';
2import '@unlev/exeq/styles';
3
4function Signer() {
5 return (
6 <SignerView
7 apiKey="your-api-key"
8 initialPdfUrl="/contracts/template.pdf"
9 initialTemplate={template}
10 initialSigner="Signer 1"
11 initialValues={{
12 "Full Name": "Jane Smith",
13 "Email": "jane@example.com",
14 }}
15 onComplete={(blob) => {
16 // Upload the signed PDF to your server
17 const formData = new FormData();
18 formData.append('file', blob, 'signed.pdf');
19 fetch('/api/upload', { method: 'POST', body: formData });
20 }}
21 />
22 );
23}SignerView Props
| Prop | Type | Required | Description |
|---|---|---|---|
apiKey | string | Yes | Your Exeq API key |
initialPdfUrl | string | No | URL to the PDF |
initialTemplate | Template | No | Template object with fields and signer roles |
initialSigner | string | No | Signer role name. Defaults to "Signer 1" |
initialValues | Record<string, string> | No | Pre-fill fields by label (case-insensitive) or ID |
callbackUrl | string | No | URL to POST the signed PDF to |
onComplete | (blob: Blob) => void | No | Callback with the signed PDF Blob |
submitLabel | string | No | Label for the final submit button. Defaults to "Complete" |
signerOrder | string[] | No | Signing order for multi-party documents. Signers complete one at a time. If omitted, non-Sender roles go first, then Sender. |
What the signer sees
- PDF rendered with all fields visible
- Fields assigned to this signer are editable (highlighted)
- Fields assigned to other roles show as read-only with pre-filled values
- Prev/Next buttons navigate through fields in document order
- "Complete" generates the final PDF with all values overlaid
Multi-party Signing
For documents that require multiple signers, use the signerOrder prop to define who signs in what order. Signers complete their fields one at a time — only the current signer's fields are active, while other signers' fields appear greyed out.
1<SignerView
2 apiKey="your-api-key"
3 initialPdfUrl="/contracts/template.pdf"
4 initialTemplate={template}
5 signerOrder={['Signer 1', 'Sender']} // recipient signs first, then sender
6 onComplete={(blob) => {
7 // Final PDF with all signatures from all parties
8 uploadToServer(blob);
9 }}
10/>If signerOrder is omitted, the default order is: non-Sender roles first (in their template order), then Sender last. The PDF is only generated after the final signer completes their fields.
Mail Merge (Pre-fill Fields)
Use the initialValues prop to pre-fill form fields programmatically. This enables a mail-merge workflow where your app fills in the data and the user only needs to review and sign — no manual data entry required.
Keys are matched against field labels (case-insensitive) first, then field IDs. Field labels are enforced to be unique in the designer, so there are no conflicts.
1<SignerView
2 apiKey="your-api-key"
3 initialPdfUrl="/contracts/lease.pdf"
4 initialTemplate={leaseTemplate}
5 initialSigner="Tenant"
6 initialValues={{
7 "Tenant Name": "Jane Smith",
8 "Email": "jane@example.com",
9 "Move-in Date": "2026-06-01",
10 "Monthly Rent": "$2,400",
11 "Unit Number": "4B",
12 }}
13 onComplete={(blob) => {
14 // The signed PDF has all values baked in
15 uploadToServer(blob);
16 }}
17/>Fields matched by initialValues appear pre-filled in the signing UI. The signer can still edit them unless they belong to a different role (e.g. Sender fields are read-only).
Utilities & Types
Utility Functions
1import { renderPdfPages, generateFilledPdf, downloadPdf } from '@unlev/exeq';
2
3// Render PDF pages to images
4const pages = await renderPdfPages(pdfUrlOrBytes);
5
6// Generate a filled PDF with form values overlaid
7const bytes = await generateFilledPdf(pdfSource, fields);
8
9// Trigger browser download
10downloadPdf(bytes, 'signed-document.pdf');Types
1import type { FormField, Template, FieldType, RenderedPage } from '@unlev/exeq';Additional Components
| Component | Description |
|---|---|
PdfViewer | Low-level PDF page renderer with draggable field overlays |
SignatureCanvas | Freehand signature/initials drawing canvas |
FieldPropertyPanel | Field property editor (type, label, assignee) |
FieldNavigator | Prev/Next navigation through signer fields |
SignerRoleSelector | Manage signer roles |
Template JSON Schema
The template JSON exported by the editor has this structure:
1{
2 "pdfUrl": "https://yoursite.com/contract.pdf",
3 "signerRoles": ["Sender", "Signer 1"],
4 "fields": [
5 {
6 "id": "550e8400-e29b-41d4-a716-446655440000",
7 "type": "text",
8 "textSubtype": "freeform",
9 "label": "Full Name",
10 "placeholder": "Enter your full name",
11 "required": true,
12 "assignee": "Signer 1",
13 "page": 0,
14 "x": 15.5,
15 "y": 42.3,
16 "width": 25,
17 "height": 3,
18 "fontSize": 12,
19 "value": ""
20 }
21 ]
22}Coordinate system
page: 0-indexed page numberx,y: position as percentage of page dimensions (0–100). Origin is top-left.width,height: size as percentage of page dimensions (0–100)fontSize: font size in points (used for text fields)
Field Types
| Type | Description | Value format | Notes |
|---|---|---|---|
text | Text input | String | Set textSubtype: freeform, number, date, email, phone |
signature | Freehand signature | PNG data URL | Supports ink color (black, blue) |
initials | Smaller freehand drawing | PNG data URL | Same as signature, smaller default size |
signed-date | Auto-filled date | Locale date string | Auto-fills when the signer signs |
checkbox | Toggle checkbox | "true" or "" | Renders a checkmark when checked |
blackout | Black redaction rectangle | N/A | Designer-only. Covers content with a solid black box. |
whiteout | White redaction rectangle | N/A | Designer-only. Covers content with a solid white box. |
Typical Workflow
- Design — Use
<DesignerView />to create a template. Open a PDF, place fields, assign roles. - Pre-fill — Fill Sender fields (company name, your signature, dates). These are baked into the template.
- Export — Use the
onSavecallback to capture the template JSON. Host the PDF on your server. - Sign — Render
<SignerView />with the template. UseinitialValuesfor mail-merge pre-fill. - Collect — Receive the signed PDF via the
onCompletecallback.