> ## Documentation Index
> Fetch the complete documentation index at: https://developers.luminpdf.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Embedded Signing

> Embed the Lumin signing experience in your app using the Embed Signing SDK and signing-session API.

Mint a per-signer signing session and mount the Lumin signing UI inline with [`@luminpdf/lumin-embed-signing-sdk`](https://www.npmjs.com/package/@luminpdf/lumin-embed-signing-sdk?activeTab=readme). For redirect flows, use [Get Signing Link](/tabs/api-reference/api/signature-requests/get-signing-link) instead.

***

## When to use embedded signing

* Signers are already authenticated in your app and you want an in-context experience.
* You control when and how the signing prompt appears (modal, inline panel, dedicated step).
* You are building for web or mobile webview, not a native-only flow without a hosted shell page.

<Note>
  Lumin **does not authenticate signers** in embedded flows. Your app is responsible
  for verifying that the person opening the signing surface is the intended
  recipient.
</Note>

***

## Prerequisites

Before you integrate embedded signing, make sure you have:

* An API key or OAuth 2.0 app with the `sign:requests` scope. Create these yourself in **Settings → Developer settings** in the Lumin app. See [API Keys](/tabs/guides/authentication/api-key) or [OAuth 2.0](/tabs/guides/authentication/oauth2).
* An **embedding domain** allow-listed for that API key or OAuth app. Embedded signing only works when your host page origin matches an allow-listed domain.
* Signature requests created through the **public API** (`POST /signature_request/send` or `POST /signature_request/send-from-template`). In-app agreements created from the Lumin UI are not embeddable.

<Warning>
  Embedding domains are **currently allow-listed by Lumin on request**. Contact [Lumin Support](https://www.luminpdf.com/contact-support) with your Workspace ID/Workspace name and desired embedding domain to request allow-listing for your domain. You cannot mint signing sessions until your domain is allow-listed.
</Warning>

***

## Workflow overview

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
sequenceDiagram
    participant Signer
    participant HostFE as Your frontend
    participant HostBE as Your backend
    participant API as Lumin API
    participant SDK as Embed Signing SDK
    participant Iframe as Lumin Sign page

    Note over HostBE,API: One-time setup
    HostBE->>API: API key / OAuth client + allow-listed domain

    Signer->>HostFE: Click Sign now
    HostFE->>HostBE: Request signing session
    HostBE->>API: POST /signature_request/send or send-from-template
    API-->>HostBE: signature_request_id
    HostBE->>API: POST /signature_request/{id}/signing-session
    API-->>HostBE: sign_url, expires_at
    HostBE-->>HostFE: sign_url

    HostFE->>SDK: client.open({ signUrl })
    SDK->>Iframe: load sign_url in iframe
    Iframe->>API: validate parent origin against allow-list
    alt Origin allow-listed
        API-->>Iframe: render signing surface
        Iframe-->>SDK: postMessage loaded
        SDK-->>HostFE: emit loaded
        Signer->>Iframe: review and sign
        Iframe-->>SDK: postMessage signed
        SDK-->>HostFE: emit signed
        API-->>HostBE: webhook signature_request_signed / approved
    else Origin not allow-listed
        API-->>Iframe: 403 origin_not_allowed
        SDK-->>HostFE: emit error
    end
```

<Info>
  Webhooks are the **source of truth** for server-side state. SDK events are for
  host-app UX only, do not persist business state from client-side events alone.
</Info>

***

## Integration steps

<Steps>
  <Step title="Set up credentials and domain">
    Create an API key or OAuth app in **Settings → Developer settings** ([API Keys](/tabs/guides/authentication/api-key), [OAuth 2.0](/tabs/guides/authentication/oauth2)). Then contact [Lumin Support](https://www.luminpdf.com/contact-support) to allow-list embedding domain registration for API key/OAuth client.
  </Step>

  <Step title="Create a signature request">
    From your backend, send the document with `POST /signature_request/send` or `POST /signature_request/send-from-template`. See [Send Signature Request](/tabs/guides/walkthroughs/send-signature-request).
  </Step>

  <Step title="Mint a signing session">
    Call `POST /signature_request/{id}/signing-session` with the signer's email and pass the returned `sign_url` to your frontend. See [Create Signing Session](/tabs/api-reference/api/signature-requests/create-signing-session).
  </Step>

  <Step title="Embed the signing UI">
    Install [`@luminpdf/lumin-embed-signing-sdk`](https://www.npmjs.com/package/@luminpdf/lumin-embed-signing-sdk?activeTab=readme), initialize the client once, then call `client.open({ signUrl })` when the signer is ready.
  </Step>

  <Step title="Handle completion">
    Use SDK events for immediate UX feedback. Use webhooks as the source of truth for server-side state.
  </Step>
</Steps>

***

## API examples

### Create a signature request

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.luminpdf.com/v1/signature_request/send \
  -H "Authorization: API-key <your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Rental Agreement",
    "file_url": "https://example.com/rental-agreement.pdf",
    "signing_type": "SAME_TIME",
    "expires_at": 1927510980694,
    "signers": [
      { "email_address": "tenant@example.com", "name": "Alex Tenant" }
    ]
  }'
```

Save the `signature_request_id` from the response. If the status is `WAITING_FOR_PROCESSING`, poll `GET /signature_request/{id}` until the request is ready to sign.

### Mint a signing session

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.luminpdf.com/v1/signature_request/696d007913f3b8.../signing-session \
  -H "Authorization: API-key <your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "signer_email": "tenant@example.com",
    "expiry": 900000
  }'
```

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "sign_url": "https://sign.luminpdf.com/embed?session=8647b08b...",
  "signer_email": "tenant@example.com",
  "expires_at": 1927510980694,
  "status": "NEED_TO_SIGN"
}
```

Do not use [Get Signing Link](/tabs/api-reference/api/signature-requests/get-signing-link) for iframe embeds. Do not expose your API key to the browser.

<Tip>
  Mint the session when the signer is ready to sign. Each `sign_url` is
  single-use and expires after `expiry` milliseconds. See [Create Signing
  Session](/tabs/api-reference/api/signature-requests/create-signing-session) for
  request and response fields.
</Tip>

***

## SDK example

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npm install @luminpdf/lumin-embed-signing-sdk@1.0.0
```

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { LuminSigning } from "@luminpdf/lumin-embed-signing-sdk";

const client = new LuminSigning({ clientId: "your-lumin-app-id" });

client.on("loaded", () => { /* signing surface ready */ });
client.on("signed", () => { /* update UX — wait for webhook */ });
client.on("error", (event) => { console.error(event.code, event.message); });

const session = client.open({
  container: "#sign-root",
  signUrl: signUrlFromBackend,
});
```

```html theme={"theme":{"light":"github-light","dark":"github-dark"}}
<div id="sign-root" style="width: 100%; min-height: 600px;"></div>
```

***

## Events and webhooks

| Event      | When it fires            | Recommended action                                               |
| ---------- | ------------------------ | ---------------------------------------------------------------- |
| `loaded`   | Signing UI is ready      | Hide your loading state                                          |
| `signed`   | Signer completed signing | Show a thank-you state; wait for webhook                         |
| `declined` | Signer declined          | Show a message; wait for webhook                                 |
| `expire`   | `sign_url` TTL elapsed   | Mint a new session from your backend                             |
| `error`    | Signing aborted          | See [Domain verification](#domain-verification) for common codes |
| `close`    | Iframe torn down         | Final cleanup                                                    |

Listen for `signature_request_signed` and `signature_request_approved` webhooks to advance your workflow. See the [Webhooks guide](/tabs/guides/webhooks/overview).

***

## Domain verification

Embedded signing validates the **parent page origin** when the iframe loads. The signing surface renders only when the origin matches a domain allow-listed for the API key or OAuth app that minted the session.

Common `error` codes related to domains and sessions:

| Code                                 | Meaning                                                    |
| ------------------------------------ | ---------------------------------------------------------- |
| `origin_not_allowed`                 | Parent origin is not on your app's allow-list              |
| `session_expired`                    | `sign_url` was used after its TTL — mint a new session     |
| `session_already_used`               | The same `sign_url` was mounted twice — mint a new session |
| `signature_request_no_longer_active` | Request cancelled or expired while iframe was open         |

### Mobile webview

Native apps must load the SDK through a **hosted shell page** on your verified HTTPS domain (for example, `https://app.example.com/sign-shell`). You cannot bundle the SDK as a static `file://` asset, the origin check will fail.

Pass `sign_url` from your native backend into the webview shell via a query parameter or JavaScript bridge. The shell mounts the SDK the same way a desktop browser would.

***

## Email delivery

Embedded signing does not automatically suppress Lumin email notifications. Signers may still receive email from Lumin.

***

## Related resources

* [Send Signature Request](/tabs/guides/walkthroughs/send-signature-request) — create signing requests from a PDF
* [Create Signing Session](/tabs/api-reference/api/signature-requests/create-signing-session) — API reference for this endpoint
* [Get Signing Link](/tabs/api-reference/api/signature-requests/get-signing-link) — redirect or hosted signing (not for iframe embed)
* [Webhooks overview](/tabs/guides/webhooks/overview) — server-side completion events
* [Authentication](/tabs/guides/authentication/overview) — API keys and OAuth 2.0
