Skip to main content
In this guide, you add a signing step to your signup flow. A new user fills in your form, signs a membership agreement without leaving your app, and lands back in your flow. A webhook then confirms the signature and activates the account. The flow has two tracks:
  • In the browser: your form, then the signing page, then a redirect back to your app. The user sees one continuous signup.
  • Server to server: the document.completed webhook event, then the PDF download, then account activation. This track is the source of truth, because it doesn’t depend on the user’s browser.

Before you begin

  • Store your API key in the SAJN_API_KEY environment variable. To create a key, go to workspace settings in the sajn app, then Utvecklare (Developer) > API-nycklar (API keys).
  • Create a template with the agreement’s content and FORM fields whose keys match your form, such as full-name and org-number. For more information, see Templates and forms.
  • Subscribe a webhook endpoint to document.completed. For more information, see Webhooks.
  • Have a page in your app that finishes the onboarding, such as https://app.example.com/onboarding/done.

Build the flow

1

Create the document from the template

When the user submits your form, create a document from the template with the user as the only party. "deliveryMethod": "NONE" stops sajn from emailing an invitation, because you show the signing page yourself:
Replace TEMPLATE_ID with your template’s ID. The response is the document. Store its id and parties[0].id.externalId links the document to the signup in your system, so the webhook handler can find the user. For the redirect placeholders, see Redirect after signing.
2

Optional: Match the national ID

To make BankID check that the person who signs is the person who signed up, set the party’s national identity number before you send:
Replace NATIONAL_ID with the personal identity number the user entered. The response is the party. You can set nationalId only while the document is a DRAFT.
3

Fill in the form values

Write every value in one request with Fill in values on a document. The keys must match the keys on the template’s FORM fields:
The response’s results array has one entry per key, with success and, when a value fails, an error with code, message, and userMessage. remaining lists the keys of required values that are still empty, and values lists every value on the document. To see every key on the document, send a GET request to the same path.
4

Send the document and get the signing URL

Send the document. Because the party has "deliveryMethod": "NONE", no email goes out. Then get the party’s signing URL:
Return signingUrl from the second response to your frontend, and redirect the browser to it. To show the signing page inside your app instead, see Embedded signing. The URL lets anyone sign as the party, so don’t log it.
5

Show the result after the redirect

After the user signs, sajn redirects the browser to your redirectUrl, such as https://app.example.com/onboarding/done?doc=cm4k2x9p10001abcd1234efgh. Show a confirmation screen, but don’t activate the account here: the user can close the tab before the redirect, and anyone can open the URL.
6

Activate the account from the webhook

When the document is sealed, sajn sends document.completed to your endpoint. Verify the signature, find the signup by the document’s externalId in data.object, archive the PDF, and activate the account:
SAJN_WEBHOOK_SECRET is the webhook’s whsec_ secret, returned when you create the webhook or rotate its secret. For timestamp checks and retries, see Verify webhook signatures and Delivery and retries.

Design choices

  • Why NONE instead of EMAIL? The user is already in your app. An email detour costs conversions. For a party who isn’t present, such as a co-signer, use EMAIL or SMS on that party; the delivery method is per party.
  • Why a template? The legal team edits the content in the sajn app without a deploy, and the FORM field keys stay the same when the text around them changes.
  • Why both a redirect and a webhook? The redirect decides what the user sees next. The webhook decides whether the signup is complete. With only the redirect, a closed tab leaves an orphaned signup. With only the webhook, the user sees no confirmation.
  • Which signing method? For identity you rely on later, such as KYC, use an eID such as SE_BANKID. For accepting terms of service, CLICK_TO_SIGN or DRAWING has no eID requirement. For more information, see Set the signing method.
To catch signups whose webhook delivery failed, run a reconciliation job that lists COMPLETED documents and checks each externalId. For an example, see Sync all completed documents nightly.

Handle errors

  • 400 VALIDATION_FAILED on create: a party or documentMeta field is invalid. Read issues.
  • results[].success: false on field-values: error.code is NOT_FOUND when the key doesn’t exist, and VALIDATION_FAILED when the value doesn’t match the field’s type or options. Fix the value; the other values are already written.
  • 404 NOT_FOUND: the template or the document doesn’t exist in the workspace that the API key belongs to. resource names which one.
  • 409 APPROVAL_REQUIRED on send: the API key’s user needs an approval to send. Give the user the permission to send without approval, or request an approval as described in Send a document for signing.
For the error shape and every code, see Errors.

Next steps

Embedded signing

Show the signing page inside your app.

Templates and forms

Build a template with field keys.

Download documents

Archive the sealed PDF and the journal.

Verify identity with sajn ID

Verify who the user is before they sign.