Embedded Signing
Let signers complete eSign inside your own application — in an iframe or a popup — and learn when they have finished.
Overview
Every signer in an eSign request gets an eSignLink in the API response. You can send it to the
signer (SignCare does this by email/SMS), or open it from your own application so the signer never
leaves it. There are two ways to do the latter:
| Iframe | Popup | |
|---|---|---|
| Signer stays on your page | Yes, inline | Yes, in a separate window |
| Completion signal in the browser | postMessage event, or your returnUrl | your returnUrl page |
| Browser permissions to delegate | camera, geolocation (see below) | none |
| Methods | Virtual Sign, Aadhaar eSign, Aadhaar Face. DSC: see Limitations | all |
Whichever you choose, the webhook (responseUrl) and the status API are the source of truth for
the outcome. Browser events tell you when to refresh your UI, not whether a signature is valid.
Iframe
Load the signer's eSignLink in an iframe and delegate the browser features the signing page may use:
<iframe
src="{eSignLink}"
allow="camera; geolocation"
style="width: 100%; height: 720px; border: 0"
></iframe>cameraandgeolocationare needed when the request enables photo capture or location for the signer. Without them the signer sees a message that access is blocked and cannot continue.- Your page must be served over https.
- The signer verifies with the OTP sent to them, inside the frame, exactly as on a normal signing link.
Knowing when the signer has finished
Without returnUrl, SignCare's end page sends a message to your page:
{
type: 'signcare.esign',
event: 'signed' | 'approved' | 'rejected' | 'failed' | 'expired',
signerId?: string,
message?: string // short reason, for 'failed'
}window.addEventListener('message', (e) => {
// Accept messages only from SignCare.
if (e.origin !== 'https://app.signcare.io') return; // Stage: https://stage.signcare.io
if (e.data?.type !== 'signcare.esign') return;
if (e.data.event === 'expired') {
// The signing session ran out; offer to reload the eSignLink.
}
// For every event: refresh your view from your server, which has the webhook result.
});| Event | Meaning |
|---|---|
signed | The signer completed their signature |
approved / rejected | The document was approved or rejected on the signing page |
failed | Signing failed, e.g. an Aadhaar OTP or detail mismatch. message carries a short reason. |
expired | The signing session expired inside the frame; reload the eSignLink to continue |
The message never contains personal data or the signed document.
With returnUrl, the iframe navigates to your returnUrl after the signer's step, as it does outside
an iframe. That page is yours, so it can notify its parent itself:
// on your returnUrl page, loaded inside the iframe
window.parent.postMessage({ type: 'my-app:esign-done' }, window.location.origin);Popup
Open the eSignLink from a user action (a click handler), otherwise browsers block the popup:
const popup = window.open(eSignLink, 'signcare_esign', 'width=1000,height=800');After signing, the popup navigates to your returnUrl, which can call
window.opener.postMessage(...) and window.close(). Also watch for the signer closing the popup early:
const timer = setInterval(() => {
if (popup.closed) { clearInterval(timer); refreshStatusFromServer(); }
}, 1000);Limitations
- DSC (USB token) signing in an iframe needs the SignCare Utility on the signer's machine. Recent
Chrome versions also ask the signer to allow access to local devices for the embedding site, and the
iframe needs
local-network-accessin itsallowattribute. If that is not an option for your users, use the popup for DSC signers. returnUrlis per signer. It fires after each signer's own step, not when the whole document is complete. Use the webhook for document completion.- The signing pages are the only SignCare pages that can be embedded. Other SignCare pages (login, dashboard) refuse to load inside an iframe.
Next
- Webhooks — the authoritative eSign outcome
- Environments — Stage vs Live hosts for the
eSignLinkand thepostMessageorigin check