# Task: Sign without leaving your page

I want my application to do what Lacuna's "Sign without leaving your page" demo does: Sign on Dropsigner's signing page embedded in this page by the Signer widget, with no e-mail.

- Docs: https://docs.lacunasoftware.com/en-us/articles/signer/embedded-signature
- Live demo: https://try.lacunasoftware.com/en/signer/embedded-signature
- This demo for AI agents: https://try.lacunasoftware.com/en/signer/embedded-signature.md

## Samples

- Lacuna's samples for this product: https://github.com/LacunaSoftware/SignerSamples

## Call sequence

Recorded on 2026-10-12 against the real products with Lacuna's public test certificates and data. Keys, tokens, sessions, ids, file contents and personal data are masked.

### 1. Dropsigner takes the PDF and creates the document for the signer with notifications off, so nobody is e-mailed, then the document is read back and its signer compared with the one sent.

On the server: `SignerClient.UploadFileAsync, then SignerClient.CreateDocumentAsync, then SignerClient.GetDocumentDetailsAsync`

`POST https://www.dropsigner.com/api/uploads` → 200

Request:

```json
"<multipart/form-data, 2138 bytes>"
```

Response:

```json
{
  "location": "/api/uploads/00000000-0000-0000-0000-000000000000?access_ticket={value}&culture=en&contentType={value}",
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "Lacuna-Test-Contract.pdf",
  "contentType": "application/pdf"
}
```

`POST https://www.dropsigner.com/api/documents` → 200

Request:

```json
{
  "AreFilesScanned": false,
  "Files": [
    {
      "DisplayName": "[Lacuna Demo Site · binds no one] Lacuna-Test-Contract",
      "Id": "00000000-0000-0000-0000-000000000000",
      "Name": "Lacuna-Test-Contract.pdf",
      "ContentType": "application/pdf"
    }
  ],
  "IsEnvelope": false,
  "FlowActions": [
    {
      "User": {
        "Name": "Alan Mathison Turing",
        "Identifier": "***.***.***-**",
        "Email": "someone@example.com"
      },
      "Type": "Signer",
      "Step": 1,
      "AllowElectronicSignature": true
    }
  ],
  "DisablePendingActionNotifications": false,
  "DisableNotifications": true,
  "ForceCadesSignature": false,
  "SignatureType": 0,
  "ExpirationDate": "2026-10-12T00:00:00"
}
```

Response:

```json
[
  {
    "uploadId": "00000000-0000-0000-0000-000000000000",
    "documentId": "00000000-0000-0000-0000-000000000000"
  }
]
```

`GET https://www.dropsigner.com/api/documents/00000000-0000-0000-0000-000000000000` → 200

Response:

```json
{
  "checksumMd5": "<hex, 16 bytes>",
  "isDeleted": false,
  "flowActions": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "shouldNotarize": false,
      "creationDate": "2026-10-12T02:08:02.7520479+00:00",
      "pendingDate": "2026-10-12T02:08:02.7522816+00:00",
      "updateDate": "2026-10-12T02:08:02.7522813+00:00",
      "user": {
        "id": "00000000-0000-0000-0000-000000000000",
        "name": "Alan Mathison Turing",
        "identifierType": "Cpf",
        "identifier": "***.***.***-**",
        "identifiers": [
          {
            "id": "00000000-0000-0000-0000-000000000000",
            "isGrant": true,
            "type": "Cpf",
            "value": "***.***.***-**"
          }
        ],
        "email": "someone@example.com",
        "createdBySubscriptionId": "00000000-0000-0000-0000-000000000000"
      },
      "numberRequiredSignatures": 0,
      "numberRequiredActions": 0,
      "allowElectronicSignature": true,
      "requireEmailAuthenticationToSignElectronically": true,
      "requireSmsAuthenticationToSignElectronically": false,
      "requireWhatsappAuthenticationToSignElectronically": false,
      "requireAuthenticatorAppToSignElectronically": false,
      "requireSelfieAuthenticationToSignElectronically": false,
      "requireDatavalidAuthenticationToSignElectronically": false,
      "requirePixAuthenticationToSignElectronically": false,
      "requireLivenessAuthenticationToSignElectronically": false,
      "requireIdScanAuthenticationToSignElectronically": false,
      "requireCompanyCertificate": false,
      "signatureInitialsMode": "None",
      "isElectronic": false,
      "allowRuleFlowToContinueIfRefused": false,
      "allowIdrcSignature": false,
      "notificationLanguage": "Pt",
      "type": "Signer",
      "status": "Pending",
      "step": 1
    }
  ],
  "observers": [],
  "permissions": {
    "move": true,
    "editFlow": true,
    "viewSignedDocumentWithExternalFlowBeforeCompleted": true,
    "submitExternalFlowFiles": true,
    "updateDocumentVersion": true,
    "createAttachments": true,
    "deleteAttachments": true,
    "viewPrivateAttachments": true,
    "editAttachments": true,
    "createMeeting": true,
    "deleteMeeting": true,
    "refuse": true,
    "editInformation": true,
    "cancel": true,
    "editParticipantInformation": false
  },
  "pending": {},
  "hideDownloadOptionForPendingDocuments": false,
  "id": "00000000-0000-0000-0000-000000000000",
  "qrCode": "***",
  "name": "[Lacuna Demo Site · binds no one] Lacuna-Test-Contract",
  "key": "***",
  "filename": "Lacuna-Test-Contract.pdf",
  "fileSize": 5424,
  "mimeType": "application/pdf",
  "hasSignature": false,
  "status": "Pending",
  "isConcluded": false,
  "organization": {
    "id": "00000000-0000-0000-0000-000000000000",
    "name": "Lacuna Demo Site"
  },
  "isOwner": false,
  "creationDate": "2026-10-12T02:08:02.7520204+00:00",
  "updateDate": "2026-10-12T02:08:02.7520218+00:00",
  "expirationDate": "2026-10-13T02:59:59.9999999+00:00",
  "expirationDateWithoutTime": "2026-10-12",
  "createdBy": {
    "id": "00000000-0000-0000-0000-000000000000",
    "name": "Lacuna Demo Site"
  },
  "hasExternalFlow": false,
  "forceCadesSignature": false,
  "isScanned": false,
  "isEnvelope": false,
  "signatureType": "None"
}
```

### 2. Dropsigner gives an action URL for the signer, asked for with the e-mail read back alone, whose embed URL signs as them and is never logged or shown.

On the server: `SignerClient.GetActionUrlAsync`

`POST https://www.dropsigner.com/api/documents/00000000-0000-0000-0000-000000000000/action-url` → 200

Request:

```json
{
  "EmailAddress": "someone@example.com",
  "RequireEmailAuthentication": false
}
```

Response:

```json
{
  "url": "***",
  "embedUrl": "***"
}
```

### 3. The Signer widget renders the embed URL in a frame, inside a closed shadow root on the Demo's page, in the page's language.

In the browser: `widget.renderElement(embedUrl, root)`

### 4. The signer signs in the frame and the widget posts documentSigned, which the Demo confirms by reading the document back; nobody signs in this recording, so it is not performed.

In the browser: `documentSigned, the frame's message`

### 5. Start over cancels the document with its reason in the page's language; notifications are off, so nobody is e-mailed.

On the server: `SignerClient.CancelDocumentAsync`

`POST https://www.dropsigner.com/api/documents/00000000-0000-0000-0000-000000000000/cancellation` → 200

Request:

```json
{
  "Reason": "The visitor started over in the Lacuna Demo Site."
}
```

## Patterns to copy

- The Signer widget's events are only hints: accept messages only from the frame you rendered last and from its embed URL's origin, then confirm by reading the document back. Show success only once Dropsigner reports the document Concluded and the flow action Completed, and keep a safety poll in case an event is missed.
- Treat the action URL's `EmbedUrl` as a credential: whoever opens it signs as the document's signer. Use it only if it is absolute HTTPS, never log it or put it in markup, and render the frame into a closed shadow root.
- Before asking for an action URL, compare the read-back signer with what you sent: Dropsigner keeps a known e-mail's stored name and CPF. Ask for the action URL with the e-mail as read back, and treat `NoPendingActionFoundForEmailAndIdentifier` as "the signer already acted".
- For an in-page signature the Demo creates the document with `DisableNotifications = true`, so nobody is e-mailed, and a short expiry, because an abandoned document can't be reached again.

## What to do

Implement this in my project with its existing stack, language, framework and conventions, following the call sequence and the patterns above, and adapting the samples rather than copying them whole. Ask me before adding any dependency or package. Keep API keys and private keys out of the browser and out of the repository.
