Skip to main content
Embedding v2 is in beta. Embed sessions and SDK 1.0 can change in ways that break your integration before the first stable release, estimated for November 2026. Embedding v1, described in Embedded signing, is stable.
In this recipe, you build an Express app where a signed-in user signs an agreement without leaving your site. The server creates and sends the document once, and creates an embed session each time the page asks for one. The page shows the signing page with @sajn/embed-js and moves on when the user has signed.

Before you begin

  • Embedded signing turned on for your organization, with your domain in the embed domains. For more information, see Add your embed domains.
  • Node.js 18 or later, and Express: npm install express.
  • An API key in the SAJN_API_KEY environment variable. To create one, go to Inställningar > Utvecklare > API-nycklar (Settings > Developer > API keys) in the sajn app.
  • A template for the agreement. Store its ID in SAJN_TEMPLATE_ID.
  • The public HTTPS origin your app runs on, such as https://signing.example.com, in APP_ORIGIN.

Build the app

1

Prepare the document and the session on the server

Save the following file as server.js. The /api/embed-session route creates and sends the document the first time the user asks, then creates an embed session for the user’s party and returns its clientSecret:
The SDK calls /api/embed-session again each time a session ends, so the route creates a new session on every call but sends the document only once. The client secret lets the frame sign as the party, so return it only to that user’s session, and never log it. If the template has signature boxes placed on a PDF, leave out parties and update the template’s party instead. See Create documents from templates.
2

Show the signing page

Save the following file as public/index.html. It mounts the signing page and gets a client secret from the server:
3

Confirm the signature on the server

onSigned runs in the browser, so use it to move the user on, not as proof. To confirm the signature, subscribe a webhook to document.party.signed or document.completed, and match the document by data.object.externalId. For a complete handler, see Send a contract from your CRM.

Run it

Start the server with APP_ORIGIN=https://signing.example.com node server.js behind a public HTTPS domain that’s in the embed domains, such as a tunnel to port 3000. Open the domain in a browser. The signing page loads in the frame, and after signing, the browser opens /done.html.

Handle errors

  • A blank frame and a bootstrap_timeout error: APP_ORIGIN doesn’t match the page’s origin exactly, the domain isn’t in the embed domains, or the page isn’t served over HTTPS. For every cause, see Fix a blank frame.
  • A client_secret_failed error: /api/embed-session failed. The server logs the API code and requestId, and the page shows the error’s userMessage, which is safe to show users. For what each code means, see Errors.