---
title: Uploaded-PDF envelopes
summary: Upload a prepared PDF, drag signature and date fields onto it, assign each to a signer, optionally self-sign, and send.
order: 3
---

# Uploaded-PDF envelopes

Use this flow when the document you want signed already exists as
a PDF — a contract, an NDA, a vendor agreement — and isn't being
generated by a form.

The envelope's `source_kind` is `uploaded` (versus `form_submission`
for inline / multi-signer plans on forms). Field placements live in
the `esign_field_placements` table; coordinates are normalized
0..1 within each page, so the document can re-render at any DPI and
the signature still lands in the right spot.

## The flow

1. **Upload the PDF.** Open **Envelopes → New uploaded-PDF**. Drop
   a PDF in. The platform creates a draft envelope with
   `source_kind = 'uploaded'` and stores the source document as an
   attachment.
2. **Place fields.** Open the placement editor. Drag signature and
   date fields onto the pages where signers will sign. Each field
   is assigned to a specific signer slot.
3. **Add signers.** For each signer, supply a role label, capture
   mode (`inline` self-sign or `send` emailed link), and the
   email-source field.
4. **Optional self-sign.** If the admin is one of the signers,
   the inline self-sign step happens before send. The admin's
   signature stamps every field assigned to that admin slot.
5. **Send.** The platform fires invitation emails to every `send`
   signer. The envelope moves to `in_progress`. Each signer in
   order signs the document via their portal link.

When the last required signer signs, the platform stamps every
signer's signature PNG into the corresponding field positions
(signature fields become PDF XObjects; date fields become text
strings rendered at the assigned coordinates), then seals the
envelope and generates the Certificate of Completion.

## Field placement details

- **Signature fields** — accept a signature PNG image. Each
  signer has one signature image; the same image stamps every
  signature field assigned to that signer.
- **Date fields** — accept a text string. The signer's signed-at
  timestamp is rendered as the date on every date field assigned
  to that signer.
- **Coordinates** — `x`, `y`, `width`, `height` are all
  normalized to 0..1 within the page. Origin is top-left in the
  authoring UI; the platform flips to PDF's bottom-left origin
  during stamping.
- **Multi-page support** — fields can be placed on any page;
  the page index is part of the placement record.

## Self-sign step

When an admin is one of the signers — for example, the company's
counter-signature on a vendor agreement — the placement editor
offers an inline self-sign before send. After the admin signs,
their signature is stored as an attachment keyed against the
self-sign slot, and the rest of the signers receive their portal
invitations.

## Voiding an in-progress envelope

The same admin dashboard as forms-with-signing handles voids.
From the envelope detail view, click **Void** and supply a reason.
The envelope moves to `voided`; the audit log records the void and
the reason; no further signatures can be captured. Completed
(sealed) envelopes cannot be voided — the sealed PDF and
Certificate of Completion are immutable.

## API reference

Upload a PDF and receive an envelope id:

```http
POST /esign/documents
Content-Type: multipart/form-data
Authorization: Bearer <admin JWT>

<binary PDF body>
```

Response: `{ "envelope_id": "env_…" }`.

Save field placements and signer plan on the draft:

```http
PUT /esign/envelopes/:id/draft
Content-Type: application/json
Authorization: Bearer <admin JWT>

{
  "signers": [ { "role_label": "vendor", "capture": "send", "email_source": { … } } ],
  "placements": [
    { "page": 0, "field_type": "signature", "signer_role": "vendor",
      "x": 0.12, "y": 0.65, "width": 0.25, "height": 0.04 }
  ]
}
```

Admin self-sign:

```http
POST /esign/envelopes/:id/self-sign
Content-Type: application/json
Authorization: Bearer <admin JWT>

{ "signer_role": "company_counter", "signature_png_base64": "…" }
```

Send the envelope (admin → in_progress; invitation emails dispatch):

```http
POST /esign/envelopes/:id/send
Authorization: Bearer <admin JWT>
```

Fetch the current PDF (draft pre-send, sealed after finalize):

```http
GET /esign/envelopes/:id/document.pdf
Authorization: Bearer <admin JWT>
```

The `source_kind` discriminator on the envelope determines how the
last-signer hook builds the final document: `form_submission`
re-renders from the form payload; `uploaded` stamps signatures
onto the source PDF.

## Known limits

- **Field types**: only `signature` and `date` are supported today.
  `initials` and free-text fields are deferred — they will use the
  same placement model when they ship.
- **PDF format**: the upload must be a real PDF, not an image-only
  scan. The platform doesn't OCR.
- **Document edits after placement**: changing the source PDF
  after placements have been saved invalidates the placements;
  upload a fresh document instead.
