# Overview of Sign

### **Sign** is a digital signing solution built on the Singpass ecosystem that allows users to sign documents such as contracts and other agreements remotely, via their Singpass App.

{% hint style="success" %}
Signatures made using Sign with Singpass are regarded as **Secure Electronic Signatures (SES)** under the Electronic Transactions Act 2010 (ETA). [Find out more about why SES is important](/start-here/how-do-our-digital-signatures-work#digital-signatures-under-singapore-law-oes-vs-ses)
{% endhint %}

## <mark style="color:red;">**Benefits of using Sign**</mark>

### Enhanced Protection

Singpass digital signatures guarantee that your signed documents cannot be forged or secretly changed. Unlike handwritten signatures that can be copied, each digital signature is uniquely tied to you through your Singpass digital identity, making it impossible for anyone else to replicate.&#x20;

This means you can benefit from **extra protection** against common scams, such as:

* **Fake contract changes:** If someone tries to alter your contract after you sign it, the changes will be automatically detected and flagged
* **Forged signatures:** No one can pretend to be you on important transactions like property agreements or insurance forms

In addition, every document you sign carries its own proof of authenticity. This means that you can ascertain the authenticity of a signature years later even if the signing company no longer exists. [Find out more about digital signatures](/start-here/how-do-our-digital-signatures-work#what-is-a-digital-signature)

{% hint style="danger" %}
**To protect yourself...**

You shouldn't rely on the appearance of a Singpass digital signature alone to judge a document's authenticity.&#x20;

You can verify a document easily using any PDF reader to confirm who signed it and whether anything has been changed since signing. [Learn how to verify signatures](/for-users/verifying-sign-with-singpass-signatures)
{% endhint %}

### Legal Assurance of Secure Electronic Signatures (SES)

Digital signatures created through Sign are regarded as Secure Electronic Signatures (SES) under the Electronic Transactions Act (2010) (ETA). This means that they are given certain presumptions in the course of any legal proceedings.

> ***Extract from Section 19 of the Electronic Transactions Act (2010)***\
> \
> (2) In any proceedings involving a secure electronic signature, it is presumed, unless evidence to the contrary is adduced, that —&#x20;
>
> \
> &#x20;       (a) the secure electronic signature is the signature of the person to whom it correlates; and \
> &#x20;       (b) the secure electronic signature was affixed by that person with the intention of signing or approving the electronic record.\
> \
> Source: [Singapore Statutes Online](https://sso.agc.gov.sg/Act/ETA2010?ProvIds=P13-#pr19-)

{% hint style="warning" %}
Users and relying parties should note the exclusions under the [First Schedule to the ETA.](https://sso.agc.gov.sg/Act/ETA2010?ProvIds=Sc1-#Sc1-)
{% endhint %}

### Convenience of Remote Digital Signing&#x20;

Parties can sign documents instantly and securely using their Singpass App, ensuring faster, more seamless transactions that are not limited to the constraints of space and time.

### Cost-effective & Environmentally-friendly

Using Sign allows organisations to do away with wet-ink (physical) signatures, saving cost and time both for the organisation and the signatory. For example, organisations can significantly reduce the need for printing, scanning, and storing and shipping of physical documents.&#x20;

Sign is currently offered free of charge, both for organisations to integrate with, and for users.&#x20;

{% embed url="<https://www.youtube.com/watch?v=IostdtfKMhU>" %}

***

### Find out more

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>For Users</td><td><strong>How to Sign</strong></td><td></td><td></td><td><a href="/pages/TRXWAFsHevnMbP162STw">/pages/TRXWAFsHevnMbP162STw</a></td></tr><tr><td>Basics</td><td><strong>How do Digital Signatures work?</strong></td><td></td><td></td><td><a href="/pages/pc3X2bfnNRFet9GDzqV7">/pages/pc3X2bfnNRFet9GDzqV7</a></td></tr><tr><td>For Relying Parties</td><td><strong>How to Get Started</strong></td><td></td><td></td><td><a href="/pages/lHKJLGkTnGVWYnNoA6FJ">/pages/lHKJLGkTnGVWYnNoA6FJ</a></td></tr></tbody></table>


# How do our Digital Signatures work?

## <mark style="color:red;">What is a Digital Signature?</mark>&#x20;

A **digital signature** is a type of electronic signature created using asymmetric or public key cryptography. Unlike traditional signatures, digital signatures are not directly visible on electronic records (e.g. a PDF of the contract). Instead, a cryptographic hash is embedded within the record itself. However, a visual representation is often included when the record is printed.

Digital signatures **ensure the integrity** of an electronic record. If the record is **altered or tampered with** in any way, the digital signature becomes invalid because the cryptographic integrity is compromised. Conversely, if the record remains unchanged, the signature remains valid.

The integrity of digital signatures can easily be verified via standard PDF document readers, which will check that the document has not been modified since it was signed. *Find out more about verifying digital signatures made with Sign* [*here*](/for-users/verifying-sign-with-singpass-signatures)*.*

<table><thead><tr><th width="444.82421875">Digital signatures</th><th>Other types of electronic signatures</th></tr></thead><tbody><tr><td><div><figure><img src="/files/TEfaoNGauJAaFKjWB3JX" alt=""><figcaption><p>Example of a visual representation of a digital signature on a PDF document, and the checks performed by Adobe Acrobat that the document has not been modified</p></figcaption></figure></div></td><td><ul><li>Attaching a digital image of a handwritten signature to an electronic document.</li><li>Using a finger or stylus to draw a signature on a touchscreen.</li><li>Typing a name at the end of an electronic document.</li><li>Selecting an "I agree" checkbox.</li></ul></td></tr></tbody></table>

## <mark style="color:red;">Digital Signatures under Singapore law: OES vs SES</mark>

In Singapore, digital signatures may either be regarded as **ordinary electronic signatures (OES)** or **secure electronic signatures (SES).**&#x20;

{% hint style="success" %}
**Why is it important to that your digital signature is considered SES?**

If you receive a document with a digital signature that qualifies as a Secure Electronic Signature (SES), it can **benefit from legal presumptions** under Singapore law.&#x20;

This **shifts the burden of proof** to the counterparty to show that: (i) the signature is not theirs; (ii) they did not intend to sign the document; or (iii) the document was altered after signing.&#x20;

In short, the counterparty (who allegedly signed) will be presumed to have signed the document unless they can prove otherwise.
{% endhint %}

One of the ways for a digital signature to be considered an SES is if it fulfils the following criteria to be:

* unique to the person using it;
* capable of identifying such person;
* created in a manner or using a means under the sole control of the person using it; and
* linked to the electronic record to which it relates in a manner such that if the record was changed the electronic signature would be invalidated (i.e. tamper-proof).

Alternatively, a digital signature can be considered SES if the certificate used to create it was issued by a public agency approved by the Minister to act as a Certification Authority.&#x20;

✅ **Signatures created through Sign are regarded as Secure Electronic Signatures** under the [Electronics Transaction Act 2010 (ETA)](https://sso.agc.gov.sg/Act/ETA2010?ProvIds=P13) as they meet both of the methods listed above.

❌ **All other electronic or digital signatures that&#x20;*****do not*****&#x20;meet the criteria to be an SES prescribed under the ETA are considered OES.** This is because the process of signing is less secure, which may make establishing the authenticity of the signature difficult in the future. For example, an impersonator might mimic the declarant by placing an image of their signature that they obtained from some other source, or by typing their name on a statutory declaration. Detecting such impersonation can be difficult for the person requesting the signature, especially if it is their first time meeting the person, or if the signing is done remotely (e.g. via email, online platforms, etc).

{% hint style="info" %}

### **How are digital signatures different from physical (wet-ink) signatures?**

**Digital signatures are resistant to tampering or forgery**. While a wet ink signature can be scanned and tampered with or forged, digital signatures cannot be easily modified or forged once created, as they are cryptographically linked to the signed document through a hash. This provides additional safeguards beyond a visual representation, unlike traditional wet-ink signatures.&#x20;

The authenticity of Sign with Singpass signatures can be verified via standard PDF document readers, which will check that the document has not been modified since it was signed. *Find out more about verifying digital signatures* [*here*](/for-users/verifying-sign-with-singpass-signatures)*.*
{% endhint %}

## <mark style="color:red;">How does Sign create SES-eligible signatures?</mark>&#x20;

Sign with Singpass signatures use **Public-Key Infrastructure (PKI).**

Public Key Infrastructure (PKI) involves the use of a **pair of cryptographic keys**: a **private key** (kept secret by the signer, on the Singpass app) and a **public key** (embedded in the Singpass signing certificate). The private key is used to create a digital signature, which is a unique encrypted code tied to the document. The public key can be used by the recipient of the document to verify the authenticity of the digital signature. GovTech issues the Singpass signing certificate, which ties the identity of the signer to the public key.

**This process ensures that only the holder of the private key is able to sign the document.**


# Frequently Asked Questions

## General

<details>

<summary>How can I use Sign with Singpass?</summary>

Organisations (such as government agencies, financial institutions, and private businesses) who are integrated with our service can request for you to sign documents using your Singpass.&#x20;

If the organization/application you are transacting with offers Sign with Singpass, you should see a button like this:

<figure><img src="/files/kdv5AeOWoBCwSfKF9D90" alt="Sign with Singpass buttons"><figcaption><p>Sign with Singpass buttons</p></figcaption></figure>

By clicking on it, you will be guided through the process of signing with your Singpass. See our guide on [How to Sign](/for-users/how-to-sign).

</details>

<details>

<summary>Who can use Sign?</summary>

Anyone with the Singpass App can sign documents using Sign.

Both government agencies and private businesses can integrate with Sign. To join us as a digital signing partner, please visit the [onboarding](/for-relying-parties/how-our-api-works) section.&#x20;

</details>

<details>

<summary>Can I sign without a Singpass App?</summary>

No, you are required to have the Singpass App in order to sign documents.

</details>

<details>

<summary>What is the difference between Secure Electronic Signatures and Ordinary Electronic Signatures?</summary>

Ordinary Electronic Signatures (OES) is a broad category that refers to electronic signatures that are not SES.

Under Section 19 of the Electronic Transactions Act (2010), **secure electronic signatures are accorded the following presumptions:**

(a) the secure electronic signature is the signature of the person to whom it correlates; and&#x20;

(b) the secure electronic signature was affixed by that person with the intention of signing or approving the electronic record.

[Source](https://sso.agc.gov.sg/Act/ETA2010?ProvIds=P13-#pr18-)

</details>

## Authenticity

<details>

<summary>How to verify signatures?</summary>

Refer to this [section](/for-users/verifying-sign-with-singpass-signatures) on verifying signatures.&#x20;

</details>

<details>

<summary>When I opened the document on Adobe PDF reader, it says that the signature is corrupted.</summary>

Refer to this [section](/for-users/verifying-sign-with-singpass-signatures) on verifying signatures. When downloading the document onto your device, please ensure that you do not have a firewall that is modifying the file in transit (content disarm and reconstruction), as that will affect the integrity of the signature.

</details>

<details>

<summary>"Certificate validity is unknown"?</summary>

If you see "Certificate validity is unknown", this is because you do not have the Singpass root signing certificate on your device. Please refer to this [guide](/for-users/verifying-sign-with-singpass-signatures/loading-singpass-root-signing-certificate) to install the Singpass root signing certificate.

</details>

<details>

<summary>If I delete my Singpass account, will my signature still be valid?</summary>

Yes, the signature will continue to be valid.

</details>

<details>

<summary>If the signer migrates, will the signature still be valid?</summary>

Yes. The document can be traced back to the signer.

</details>

<details>

<summary>How can I know if my signature is secure and protected?</summary>

Digital signatures rely on **public-key cryptography**, a method that uses a pair of keys:

* **Private Key**: A secret key known only to the signer. It is used to create the signature.
* **Public Key**: A key that is made publicly available through the Singpass signing certificate. It is used to verify the signature.

**Private keys are stored securely on the Singpass mobile app and can only be accessed by the user.**

Furthermore, it is **computationally infeasible to replicate the cryptographic process** (hashing and encryption) used to generate the signature.

Lastly, the signer's public key is validated by Singpass. Singpass verifies the identity of the signer and that the public key is associated with the correct person.

</details>

<details>

<summary>Can a document's details be altered after it is signed?</summary>

If there are unauthorised modifications to the document after it is signed, the digital signature will be invalid.&#x20;

As digital signatures are **cryptographically linked to the signed document** through a **hash,** the hash value will change if there are unauthorised modifications to the document after signing, and the signature will be deemed to be invalid.

</details>

## Others

<details>

<summary>Is there any cost to using Sign with Singpass?</summary>

Sign with Singpass is currently free of charge, both for organisations integrating with us, as well as for signers.

</details>

<details>

<summary><strong>Can I sign from overseas?</strong></summary>

As long as you have access to your Singpass App, you will be able to Sign with Singpass.

</details>

{% hint style="warning" %}
If you have a question and are unable to find an answer in this FAQ, please submit your question on our [Singpass Q\&A site](https://ask.gov.sg/singpass) to seek further assistance.
{% endhint %}


# How to sign

You may find related FAQs at the [bottom of this page](#you-may-have-some-questions).

{% hint style="warning" %}
The following guide is for applications which have implemented the newest version of Sign with Singpass. If you are **not** redirected to a new page when you click the Sign with Singpass button, please [refer to this guide on how to sign](/for-relying-parties/api-documentation/document-signing-v1/sign-docs) instead.
{% endhint %}

## Learn through our interactive demo!

{% embed url="<https://app.arcade.software/share/M5dRwckzpXJlf8yH9DoE>" %}

## Or, here's a step-by-step guide

#### 1. If you've clicked the "Sign with Singpass" button, you will be directed to our Singpass portal to complete the signing of your document.

![](/files/db5YjIT1XAyTm7RxvsBg)

#### 2. Authenticate your identity with Singpass before you can access the document.

![](/files/I1WI0TvSI0WQaONlC5VS)

#### 3. Scan/Tap the QR code to enter the Sign portal using your Singpass App .

![](/files/D8p1JfFfn4bfLCBG0GdI)

#### 4. Approve the log in request on your Singpass App.

![](https://image.mux.com/ScS6tro518ZEOnRgBZRCNzzc11J018EwANJafdJzvePM/animated.gif?start=0\&end=5.553333\&width=640\&fps=30)

#### 5. Upon successful authentication, you can begin reviewing the document.

![](/files/BS4GYzem4DdxwTYloytj)

#### 6. Use the navigation panel to locate the fields in the document that require your signature.

This panel is on the right of your desktop screen. For mobile users, it will be at the bottom.

![](/files/chMDDlCbMX2YFhJ8fZLX)

#### 7. Click on each field in the document once to 'review' them.

For mobile, you can also tap the 'Mark as reviewed' button to review each field.

![](/files/txbvyuL5EYHYlOYi9NAH)

#### 8. Select and review the remaining signature field(s) to complete the reviewing step.

![](/files/s0CMOmkJQkie8ST8G8yL)

#### 9. You will be able to perform the signing once all fields are reviewed (Sign button will be activated).

![](/files/wwz2asP3NjJwgLE1S42E)

#### 10. Selecting the Sign button will bring up a Sign QR code. Scan/Tap the QR code to enter the Sign portal using your Singpass App .

![](https://image.mux.com/sxQ4Weh5QO52fv15FsE9v1OEX9sqd8kIa4Q5p8nKDcQ/animated.gif?start=0\&end=3.533333\&width=640\&fps=30)

#### 11. Approve the sign request on your Singpass App.

![](https://image.mux.com/UJdgGDYCYEahtauHU4JTdtpwiDRbuYAaOveW7eKtTzA/animated.gif?start=0\&end=4.8\&width=640\&fps=30)

#### 12.  Document is successfully signed! At this point, you may exit the Sign portal.

At this point, you can also click 'View Signed Document' if you want to view the signed document before leaving the Sign portal.&#x20;

![](/files/PJvHBhoi6YLbES6vwQ3T)

![](https://image.mux.com/uwfDWl013eitl2uAY84VrhNwQ8RggeGVRLwROzrNZKsA/animated.gif?start=47.177\&end=49.799\&width=640\&fps=30)

#### 13. Return to the document sender's portal as the final step.

![](/files/fVP1uqZWWkfzHrsYyCjv)

## You may have some questions...

<details>

<summary>Why do I have to scan QR code twice? What’s the difference?</summary>

The first QR code you scan (in Step 3) is to verify your identity with Singpass and log in to the Sign portal. This protects your data privacy by ensuring that the document and any sensitive contents can only be seen by the intended parties.

The second QR code you scan (in Step 10; which comes with a 4-digit reference code) is for you to perform the signing of document.

</details>

<details>

<summary>What is the reference code for during the signing?</summary>

The purpose of the reference code is to allow users to verify that they are signing the correct document, when the code on their Singpass App matches that which is shown in the Sign Portal.

</details>

<details>

<summary>What happens if I reject the signing on my Singpass App?</summary>

If you **reject** the signing (Click 'Reject' in Step 8), the Sign Portal will continue to stay in 'pending sign' mode. You have to (i) refresh the Portal to get a new QR code or (ii) return back to the organization's webpage to restart the process.&#x20;

</details>

<details>

<summary>How do I retrieve the signed document?</summary>

You may retrieve the signed document from the organisation's (document sender) platform.

</details>

<details>

<summary>Why can’t I log in?</summary>

You might not be able to log in if i) you are not the intended signer for this document, or ii) someone else has logged into the portal to access the document before you.

If you believe this is an error, please contact the organization/document sender to request for a new signing session.

</details>

<details>

<summary>Why can’t I sign?</summary>

You might not be able to sign if the document has already been signed, or if you are not the intended signer for this document.&#x20;

If you believe this is an error, please contact the organization/document sender for assistance.&#x20;

</details>


# Verifying Sign with Singpass Signatures

Sign with Singpass signatures can be verified using PDF reader software, such as **Adobe Acrobat Reader** application ([download here](https://get.adobe.com/uk/reader/)).

{% hint style="warning" %}
Please ensure that the document has not been modified since your signature has been created, as this will invalidate the signature.
{% endhint %}

## Verifying signatures with Adobe Acrobat Reader <a href="#verifying-signatures" id="verifying-signatures"></a>

Please follow the steps below to verify a Sign with Singpass signature.

{% stepper %}
{% step %}

#### **Open up signed document on Adobe Acrobat Reader**

{% endstep %}

{% step %}

#### **Check if green tick with "Signed and all signatures are valid"**

{% hint style="warning" %}
If you see "Certificate validity is unknown", this is because you do not have the Singpass root signing certificate on your device. Please refer to this [guide](/for-users/verifying-sign-with-singpass-signatures/loading-singpass-root-signing-certificate) to install the Singpass root signing certificate.
{% endhint %}

<figure><img src="/files/YbJqqsuOy3HhYB5BY5v9" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Go to signature panel

<figure><img src="/files/IVxWZ5ZW3Pe9tGpgWfkF" alt=""><figcaption></figcaption></figure>

In signature panel, each signature should show:

* Document has not been modified
* Signer's identity is valid
* The signature includes an embedded timestamp
* Signature is long term validation (LTV) enabled

<figure><img src="/files/nbS1NNWOKBWGL7q8ZmfN" alt=""><figcaption></figcaption></figure>

Document owner should "click to view this version" to visually inspect that the contents of the document version is signed correctly.

<figure><img src="/files/aXyMOgg1dekGSJV1OJzi" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Right-click on each signature and select "Show Signature Properties".

<figure><img src="/files/RpwmjOZ3vIPCdCpF6t0C" alt=""><figcaption></figcaption></figure>

The following modal should appear, and it will tell you:

* The name of the signer (e.g. "MICHAEL ONG")
* The time the document was signed

<figure><img src="/files/ZRBtlJq3B9XlJZL3srVR" alt="" width="563"><figcaption></figcaption></figure>

If you select "Show Signer's Certificate" at the bottom, you will see another modal with details about the certificate. Ensure that the the following details are correct:&#x20;

* Singapore National Root CA - B1
* Singapore NDI Intermediate CA 1 - B2
* Signer's full name (as per NRIC)

<figure><img src="/files/ScfEka3lCrQtsCiCaEHA" alt=""><figcaption></figcaption></figure>

Click the full name and then "details".&#x20;

Check under "Subject" that signer's name and last 4 characters of NRIC is correct.

<figure><img src="/files/zh1GEjnavNEKDYVTfj6x" alt=""><figcaption></figcaption></figure>

<br>

<br>
{% endstep %}
{% endstepper %}


# Loading Singpass Root Signing Certificate

In order to verify if a Sign with Singpass signature is valid, see [here](/for-users/verifying-sign-with-singpass-signatures).

Before that, the Singpass root signing certificate must be installed on your device and configured for your Adobe Acrobat Reader:

* You can download the **Adobe Acrobat Reader** application [here](https://get.adobe.com/uk/reader/)
* If you do not have the Singpass root signing certificate on your device, please follow the steps below.

{% hint style="success" %}
For **government agencies**, this should already be automatically available on your GSIB. If it is not, please raise a ticket to AFM to push “GSSP\_Acrobat Reader Digital Signature Fix\_2.0” to the agency's GSIBs.

For the rest of the agencies, you may follow the steps below or approach your IT department to push the certificates so that it applies across your agency.
{% endhint %}

### Automatically load the certificate

{% stepper %}
{% step %}
**Go to** [**GovTech CA Repository**](https://repository.nca.gov.sg/) **and download "Singapore National Root CA - B1 FDF"**&#x20;

<figure><img src="/files/7wCMDAjHbnipviKaZRfn" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Also download the "\[Legacy] Singapore National Root CA - G1 FDF" file**

{% hint style="info" %}
The Legacy cert is needed in case you are validating signatures that were performed prior to March 2025.&#x20;
{% endhint %}

<figure><img src="/files/GHLtj6tn6J0m9Yeg3yGI" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Open both of the files downloaded.**&#x20;

This should automatically load the certificates into your Adobe Acrobat reader.
{% endstep %}

{% step %}
**All done!**&#x20;

Open up a document generated by Sign and you should be able to see the the green tick and the words "Signed and all signatures are valid".&#x20;

{% hint style="warning" %}
If you are still facing issues, please attempt to load the certificate with the instructions in the next section: [Manually load the certificate](#manually-load-the-certificate)
{% endhint %}

<figure><img src="/files/YbJqqsuOy3HhYB5BY5v9" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}

### Manually load the certificate

{% hint style="warning" %}
***Still can't seem to verify after completing the steps?***

**From 13 Feb 2025, all new Singpass signing certificates are issued by the GovTech CA.** Prior to this, Singpass App users would have been issued signing certificates by Assurity Trusted Solution Pte Ltd (ATS), GovTech's wholly-owned subsidiary and an Accredited CA under the Electronic Transactions Act (2010).&#x20;

If you are unable to verify the document signature, the document may have been signed with the legacy ATS certificates. \
\
**Please follow the same steps above and load the ATS root cert by downloading it** [**here**](https://product.assurity.sg/nca/) **under the section "NCA Root Cert"** (you may download either "SNRCA-G1.cer" or "SNRCA-G1.crl").
{% endhint %}

{% stepper %}
{% step %}
**Go to** [**GovTech CA Repository**](https://repository.nca.gov.sg/) **and download Singapore National Root CA - B1 Certificate.**

<figure><img src="/files/2aFrePHxeq1SjkCMrI7K" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Certificates Staging Environment

For staging environment, download the certificate below.

CER Format

{% file src="/files/yvTlGaQ4ir9k9awEXjbJ" %}
Singapore National Root CA - B1 CER format
{% endfile %}

CRT Format

{% file src="/files/poR3KJqute7Q1PYpP0j1" %}
Singapore National Root CA - B1 CRT format
{% endfile %}
{% endstep %}

{% step %}
**In your folder that you have chosen, you should find the file RCA-B1.crt**

<figure><img src="/files/7uJnhezY2enfQvB9qSRd" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Open Adobe PDF reader.**

If you do not have this, you may download and install [here](https://get.adobe.com/uk/reader/).
{% endstep %}

{% step %}
**Click "Acrobat Reader" on the top left.**

<figure><img src="/files/rGU9fZ7S7Eq8F6iUxqWH" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Navigate to "Preferences"**

<figure><img src="/files/RlmF32ntpasOnrIAQHOF" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Under Categories, find and click "Signatures".**

<figure><img src="/files/M76OuKSv79hbZh2fgdHh" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Click "more" under Identities and Trusted Certificates**.

<figure><img src="/files/xeM1AcJx7E0jE2OKf58U" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Click on "Trusted Certificates"**

<figure><img src="/files/lwFV5UTg4xuahzO0E8un" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Click Import**

<figure><img src="/files/J3pCaiCrjcpAdKJhncbf" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Under Contacts, click "Browse" and choose the certificate you have downloaded earlier. Click on "Singapore National Root CA - B1" that is loaded.**

<figure><img src="/files/zhvQvi3En8qG13lrVKHy" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Under Certificates, click on Singapore National Root CA - B1 and then click Trust**

<figure><img src="/files/ewIqXY60vJVNc3KgLr63" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Check "Use this certificate as a trusted root" with a tick and press OK**

<figure><img src="/files/wvTbFSvPXH7Le2KheVTc" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Click on Import**

<figure><img src="/files/x0np7csw6hhjfea8RHim" alt=""><figcaption></figcaption></figure>

You should now be able to see Singapore National Root CA - B1 under your Trusted Certificates.

<figure><img src="/files/RP7U669hp6OW1K716WnT" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Enable certification revocation checking**

This can be found under Preferences > Signatures > Verification

<figure><img src="/files/7raHGNXhNdQb8AFyIqTZ" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/TpvDta9p9lssj0vYQSKF" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### All done!

{% endstep %}
{% endstepper %}


# Use Cases

## <mark style="color:red;">Why Use Sign?</mark>

### :white\_check\_mark: **Greater trust & assurance**

Compared to other electronic signing options, signatures produced by Sign are considered as [Secure Electronic Signatures (SES)](/start-here/how-do-our-digital-signatures-work#digital-signatures-under-singapore-law-oes-vs-ses).&#x20;

### :white\_check\_mark: **Convenience for your users**

Enable remote signing with a seamless experience for your users, who can sign documents through their familiar Singpass mobile app - no new downloads required.

### :white\_check\_mark: **Faster time-to-market**

Easy and quick integration

* Our API is designed to minimise integration effort. Get started with our demo code, which is under 100 lines! [Check out our useful developer tools here](https://urldefense.com/v3/__https:/docs.sign.singpass.gov.sg/for-relying-parties/api-documentation/document-signing-v3*developer-tools__;Iw!!G1i-!Jo4cXGOTLr1_dO5xEZ9kte2W-Ng13qETMUFu7oTyiz4xybwSaJE994HAXGoTux5flz3ONpyZekWQ4zDqB1XF$)
* Ship quickly - beta users have tested complete end-to-end signing flows within 2 weeks.&#x20;

{% hint style="success" %}
For **government agencies**, Sign can also ensure your agency's compliance with IM8, with respect to Circular Minute No. 5/2021 on the Use of Electronic Signatures ([link to circular, accessible via intranet only](https://intranet.mof.gov.sg/portal/IM/Circulars/ICT/Circular-Minutes/2021/Use-of-Electronic-Signatures.aspx))
{% endhint %}

## <mark style="color:red;">Examples of current use cases for Sign include:</mark>

* Appointing your Lasting Power of Attorney (LPA), under the [Office of the Public Guardian Online (OPGO)](https://opg-eservice.msf.gov.sg/)
* Bank and insurance documents
* Contracts
* Declarations
* Legal documents, via [e-Litigation](https://www.elitigation.sg/) or MinLaw's [Legal Technology Platform (LTP)](https://ltpi.mlaw.gov.sg/)

{% hint style="warning" %}
**Under the First Schedule to the Electronics Transaction Act 2010 (ETA), the following use cases are excluded from the operation of Part II of the ETA. Sign with Singpass should not be used to create Secure Electronic Signatures in relation to these matters:**

1. The creation or execution of a will&#x20;
2. The creation, performance or enforcement of an indenture, declaration of trust or power of attorney, with the exception of implied, constructive and resulting trusts and a lasting power of attorney defined under section 2(1) of the Mental Capacity Act 2008
3. Any contract for the sale or other disposition of immovable property, or any interest in such property
4. The conveyance of immovable property or the transfer of any interest in immovable property.

(Source: [Singapore Statutes Online](https://sso.agc.gov.sg/Act/ETA2010?ProvIds=Sc1-#Sc1-))

*The above is not intended to be legal advice. Please seek your own legal advice if necessary.*
{% endhint %}


# How our API works

## Integrate Sign into your applications using our APIs

{% hint style="info" %}
If your organization is not capable of integrating directly with Sign APIs, you can consider approaching our [Digital Signing Partners](/for-relying-parties/digital-signing-partners).&#x20;
{% endhint %}

#### To get started on integration:

* [Read the documentation](/for-relying-parties/api-documentation/sign-v3)
* [Check the onboarding requirements](/for-relying-parties/onboard)&#x20;

This diagram illustrates at a high-level how your application and your users will interact with Sign:

<figure><img src="/files/Yf57N42yw5yToyohytdx" alt=""><figcaption></figcaption></figure>

## Conduct Code

Before integrating Sign into your application, please ensure that you adhere to the following principles:&#x20;

### <mark style="color:red;">What you see is what you sign</mark>

* You should always ensure that the user knows what they are signing before being directed to Sign.
* You should never use this system to misrepresent intended documents which your user plans to sign.

### <mark style="color:red;">PDPA & Applicable Regulations</mark>

* Protect, retain and transfer data retrieved according to the Personal Data Protection Act (PDPA), relevant industry regulations and applicable legislation.

### <mark style="color:red;">Lawful purposes</mark>

* Use Sign for lawful purposes only. Singpass reserves the right to terminate the integration if Sign is used for any unlawful purpose.

### <mark style="color:red;">Accessibility</mark>

* You should ensure that your user always has a fallback option if they are unable to use Singpass to sign (e.g. other forms of electronic signatures, or a physical option)&#x20;


# Singpass Services Agreement

Relying Parties (RPs) who wish to use Sign with Singpass in their production environments will first have to accept the Singpass Services Agreement, which can be found on the [Singpass Developer Portal (SDP).](https://developer.singpass.gov.sg/)

After logging into SDP, follow these instructions to submit your [Consent to the Singpass Services Agreement](https://docs.developer.singpass.gov.sg/docs/singpass-developer-portal-sdp/user-guide/consent-to-singpass-service-agreement)

{% hint style="info" %}
**If you do not have an SDP account,** please follow these instructions to [obtain an SDP account](https://docs.developer.singpass.gov.sg/docs/singpass-developer-portal-sdp/user-guide/obtain-access-to-sdp)
{% endhint %}

## Obtaining the acknowledgement

If you have previously consented to the agreement, you can log into SDP to [view the Singpass Services Agreement](https://docs.developer.singpass.gov.sg/docs/singpass-developer-portal-sdp/user-guide/view-singpass-service-agreement).&#x20;

**In order to submit a** [**request for production access**](/for-relying-parties/onboard#request-for-production-access)**,** you will need to take a screenshot of this page (sample below).

{% hint style="warning" %}
Please ensure that the **entity name** reflected in the screenshot is the same as the **company name** entered in the onboarding form.&#x20;
{% endhint %}

<figure><img src="/files/PcIjDflTsPa2VZgfkfAM" alt=""><figcaption><p>Sample screenshot</p></figcaption></figure>


# How to Onboard our API

This information is for onboarding onto Sign V3. Do note that we are no longer supporting new requests to onboard onto Sign V1.

{% hint style="success" %}
**Onboarding for Sign V3 is open.** Please follow the steps below to onboard via the Singpass Developer Portal.
{% endhint %}

## Onboarding Steps

{% stepper %}
{% step %}

### Before you start (Prerequisites)

#### Get Access to Singpass Developer Portal (SDP)

* Sign with Singpass integrations are managed via SDP
* 👉 [How to get access to developer.singpass.gov.sg](https://docs.developer.singpass.gov.sg/docs/singpass-developer-portal-sdp/user-guide/obtaining-access-to-the-singpass-developer-portal-sdp)

#### Download Singpass Staging App ([guide here](https://docs.developer.singpass.gov.sg/docs/testing/testing-with-singpass-app))

* You need a Singpass Staging App to test that your application is successfully redirecting users to the Sign portal, and that they can sign with their Singpass app.

#### Gather information about your use case

* Description of use case and estimated transaction volume
* Sample of document(s) to be signed
  {% endstep %}

{% step %}

### Design & UX Setup

#### Download the Sign with Singpass button

* The Sign button is available to download in 2 sizes here.

{% file src="/files/jm7Nr2jO7TVwfYTFA7bc" %}

* If you want to customise the Sign button e.g. change the button font to your brand font, please consult the section [UX Guidelines](/for-relying-parties/ux-guidelines#implementing-the-sign-with-singpass-button) in our [UX Guidelines](/for-relying-parties/ux-guidelines)on how to implement the button in your application

#### Prepare your application details

You can define the **App Name** and **Logo** that will be displayed to users on our Sign portal. Logos must meet the following requirements:

* PNG format
* Square (1:1 ratio) dimension
* Min size: 256px by 256px
* Max size: 512px by 512px in size.

*Note: **Entity Name** cannot be changed, it is based on the UEN associated with your Singpass Developer Portal account.*

<figure><img src="/files/NbJzlcDjwRSMmCiw7SRa" alt=""><figcaption></figcaption></figure>

#### Prepare your user journey

As part of your app creation in the **production** environment, you are required to submit a User Journey that illustrates how your digital service will use the Sign with Singpass API.

{% file src="/files/cjOpaX2VtYtYiZHjo1Gj" %}
{% endstep %}

{% step %}

### Technical Integration

Set up the following resources in your own staging environment:

* [**Webhook URL**](/for-relying-parties/api-documentation/sign-v3/accept-success-signing-webhook)
* [**JWKS URL**](/for-relying-parties/api-documentation/sign-v3/jwks-specification)
* [**Redirect URL**](/for-relying-parties/api-documentation/sign-v3/redirect-from-sign-with-singpass)&#x20;
  {% endstep %}

{% step %}

### Request for Staging access

1. Log onto [Singpass Developer Portal](https://developer.singpass.gov.sg)
2. Toggle to the **Staging Environment**
3. Click "**New App"** and select "Sign with Singpass"
4. Enter in the details you have prepared in Steps 1-3

If you encounter any issues, please submit a request at [partnersupport.singpass.gov.sg](http://partnersupport.singpass.gov.sg/).&#x20;
{% endstep %}

{% step %}

### Test your staging integration

Once you have received access to the endpoints, we recommend performing these tasks:

* [ ] Check that you can [initiate a signing request](/for-relying-parties/api-documentation/sign-v3/initiate-sign-request)
* [ ] Complete a signing request (with your Staging Singpass app) and check that your application's webhook is successfully called
* [ ] Retrieve the signed document from the [webhook call](/for-relying-parties/api-documentation/sign-v3/accept-success-signing-webhook)
* [ ] Check that you can call the [get signing result endpoint](/for-relying-parties/api-documentation/sign-v3/get-signing-result)
* [ ] (Upon smooth integration) Take screenshots of your end-to-end flow to complete the user journey document
  {% endstep %}

{% step %}

### Request for Production Access

Once your staging implementation is complete and you are ready to go live:

1. Log onto [Singpass Developer Portal](https://developer.singpass.gov.sg)
2. Ensure you have done the following:
   1. Agreed to the Singpass Services Agreement ([instructions here](https://docs.developer.singpass.gov.sg/docs/singpass-developer-portal-sdp/user-guide/consent-to-singpass-service-agreement))
   2. Updated your billing contact details ([instructions here](https://docs.developer.singpass.gov.sg/docs/singpass-developer-portal-sdp/user-guide/updating-billing-contact-information))
3. Toggle to the **Production Environment**
4. Click "**New App"** and select "Sign with Singpass"
5. Enter in the details you have prepared for your **production** application
6. Once your **production app has been approved**, you can start testing and using your integration.

{% hint style="warning" %}
⏳ Note: Production App approval may take up to 2 weeks. Please plan your rollout timeline accordingly.
{% endhint %}
{% endstep %}
{% endstepper %}


# API Documentation


# Sign V3

V3 continues our mission to enhance the convenience and security of digital signing in Singapore. It significantly reduces technical complexity, ensuring faster and safer implementation, and provides greater access to digital signing to businesses and citizens across various aspects of their lives.

## Change log

<table><thead><tr><th width="118.1431884765625">Date</th><th>Changes</th></tr></thead><tbody><tr><td>2 June 2025</td><td><p><strong>V3.0</strong></p><p>Initial version</p></td></tr><tr><td>14 August 2025</td><td>Added acceptable PDF dimensions</td></tr><tr><td>7 Oct 2025 </td><td><p><strong>V3.2</strong> </p><p>Released PDF viewer within Sign Portal; support for multiple signing locations by the same signer within the document, and option for RP to specify an intended signer; and returning of signer information to the RP's webhook.</p></td></tr><tr><td>21 Oct 2025</td><td>Update PDF requirements that it must not be hybrid-referenced</td></tr><tr><td>15 Jan 2026</td><td><strong>Cancel Sign Request:</strong> New feature to allow Relying Parties (RPs) to cancel an initiated sign request <br><strong>Increased limit for multiple signing locations:</strong> Support for up to 20 locations per signing request</td></tr></tbody></table>

## High-level integration flow

Relying Parties (RPs) will send us the document, and then redirect the user to our Sign portal to perform the signing. Sign will then notify the RP of a successful signing via their webhook, from which RPs can retrieve the signed document.&#x20;

<figure><img src="/files/Lxo0SZeA57bLW6iLUVIw" alt=""><figcaption><p>Sign V3 High-level integration flow</p></figcaption></figure>

RPs are expected to implement the following steps referenced from the diagram above:

<table><thead><tr><th width="62.7664794921875">Step</th><th width="106.00433349609375">Component</th><th width="264.38543701171875">Summary</th><th>Specifications</th></tr></thead><tbody><tr><td>1</td><td>Backend</td><td>Send raw document to Sign with Singpass and get <code>signing_url</code></td><td><a data-mention href="/pages/1605lqWkR8Ae9HUe4BXk">/pages/1605lqWkR8Ae9HUe4BXk</a></td></tr><tr><td>2</td><td>Frontend</td><td>Redirect user to Sign with Singpass</td><td>Redirect user to the <a href="/pages/KZDMjpgbRtbJhtcJdEQb">Sign Portal</a> via the <code>signing_url</code> retuned in step 1. RP should follow <a data-mention href="/pages/H1GnVxGqsRT53JH9tNvG">/pages/H1GnVxGqsRT53JH9tNvG</a> to render the "Sign with Singpass" button.</td></tr><tr><td>3.1</td><td>Frontend</td><td>Accept redirect from Sign with Singpass</td><td><a data-mention href="/pages/FOMdKAobnBf8rogNctkT">/pages/FOMdKAobnBf8rogNctkT</a></td></tr><tr><td>3.2</td><td>Backend</td><td>Accept success signing webhook</td><td><a data-mention href="/pages/sEYUe2wIjL8ZbpGyxgNP">/pages/sEYUe2wIjL8ZbpGyxgNP</a></td></tr><tr><td>4</td><td>Backend</td><td>Get signing result</td><td><a data-mention href="/pages/cIdoWFb8021AWzwQZKGG">/pages/cIdoWFb8021AWzwQZKGG</a></td></tr><tr><td>5</td><td>Backend</td><td>Download signed document</td><td>N/A. Just fetch the signed document from <code>signed_doc_url</code></td></tr><tr><td>0</td><td>Backend</td><td>JWKS specifications</td><td><a data-mention href="/pages/2p3No8jzT4Rnf4GuP7dl">/pages/2p3No8jzT4Rnf4GuP7dl</a></td></tr></tbody></table>

## Production and staging URLs

**Staging Base URL:** <https://staging.sign.singpass.gov.sg/api/v3>

**Production Base URL:** <https://app.sign.singpass.gov.sg/api/v3>

## Developer tools

{% hint style="warning" %}
These tools are open-source community projects and are not officially supported or maintained by the Sign team. We welcome contributions to improve them. Their inclusion here does not imply endorsement or preference by the Sign team.
{% endhint %}

### Demo App

[Sign V3 Demo App](https://github.com/singpass/sign-v3-demo-app) is a reference implementation that demonstrates how RPs can integrate with V3. It helps partners explore the API and conduct early-stage integration testing.

### Mockpass

[**Mockpass**](https://github.com/opengovsg/mockpass) is an open-source mock server that stubs various Singpass APIs, including the **Sign v3** signing flow. It is designed for local development, CI and low environment testing, enabling fast, repeatable, and secure integration without relying on staging infrastructure.

### Sign Location helper

[Sign Location Helper](https://github.com/singpass/sign-location-helper) is a fully client-side browser-based tool for visually determining signature coordinates within a PDF document. It allows users to upload a PDF, place and drag signature boxes, and copy normalised `(x, y, page)` values for use in Sign v3. Ideal for RPs experimenting signature placement.

<br>


# Initiate Sign Request

This endpoint allows RP to initiate a signing session by sending the raw PDF document. The service processes the document to set up a signing transaction and returns a unique sign request identifier along with a signing URL to redirect users to in order for them to perform the signing.

## Implementation Notes

1. Each signing session allows for **one user** to affix **up to 20 signatures** within **one PDF document.**
2. RPs may optionally **specify the intended signer** by providing their NRIC
   1. If specified, only this individual will be able to view and sign the document
   2. If left blank, the document will be "locked" to the first user who accesses it, and no other users will be able to access or sign it. If another user needs to access the document instead, RPs must initiate a new signing session.&#x20;
3. All signing sessions must be initiated via the [Singpass button](/for-relying-parties/ux-guidelines#implementing-the-sign-with-singpass-button) and not through providing any redirect links directly to the user.&#x20;

<figure><img src="/files/DgmmIuThkstt3RGCgyMA" alt=""><figcaption><p>Sign with Singpass button</p></figcaption></figure>

3. Each signing session has a validity of **30 mins**. RPs should only initiate a request when the application is ready to redirect the user to the Sign portal to perform the signing.

## **Path**

&#x20;<mark style="color:green;">`POST`</mark> `/sign-requests`

## **Headers**

| Name          | Value                      |
| ------------- | -------------------------- |
| Content-Type  | `application/octet-stream` |
| Authorization | `<token>`                  |

## **Authorization token**

RPs should sign the signature parameter into a JWT token as authorization token.&#x20;

**Token Type:** Standard JWT ([JSON Web Token](https://datatracker.ietf.org/doc/html/rfc7519))

{% hint style="info" %}
You can use the free tool <https://jwt.io/> to verify if your token is signed properly.&#x20;
{% endhint %}

RPs should specify the payload based on whether they require one or multiple signatures from the user. RPs can also increase the security of the signing session by specifying the identity of the individual who can view and sign the document using the `signer_uin_hash` field.&#x20;

**Note:** Multiple signatures will still only be performed by *one* user. If you require multiple *different* users to sign the same document, you must iteratively start a new signing session with the signed output of the previous user's signing session.

{% tabs %}
{% tab title="Single Signature" %}
**Use Case:** If you only require the user to sign on one location within the document.

**Payload:**

* `x` & `y`: Coordinates for placing the signature. The values should be ≥ 0 and <1, with a maximum precision of four decimal places. See detailed explanation in [#how-to-set-signing-coordinates](#how-to-set-signing-coordinates "mention")
* `page`: The page number for placing the signature. Starting from 1.&#x20;
* `doc_name`: The name of the document. This will be shown to the user.
* `client_id`: Your application's registered client ID.
* `signer_uin_hash` : (Optional) A SHA256 hash of the user's NRIC in uppercase. If specified, only this user will be able to view and sign the document. Refer to [Sign Portal User Journey](/for-relying-parties/api-documentation/sign-v3/sign-portal#negative-flows) for error flows your user might see after logging in.
* `redirect_uri` : The URL your user will be redirected to upon success/error. It must match against your redirect\_uri(s) registered on Singpass Developer Portal.
* `jti`: Standard JWT ID, a unique identifier for the JWT, must be a UUID.
* `iat` & `exp`: Standard Issued At / Expiration timestamp of JWT. Must issued within 2 minutes.

**Example:**

{% code overflow="wrap" %}

```
eyJhbGciOiJFUzI1NiIsImtpZCI6InBUaHpVbl9JNVJXZzcyQWEyUzI1LXhoRG0wRzBNWjNSN3FpWUdyYmVmT0EifQ.eyJkb2NfbmFtZSI6ImR1bW15LnBkZiIsImNsaWVudF9pZCI6ImxvY2FsLXRlc3QiLCJqdGkiOiJhOWIxYjU4MS1lYjY3LTRkZWEtODBhYy02NTViMWFhZmQ0NzEiLCJ4IjowLCJ5IjowLCJwYWdlIjoxLCJyZWRpcmVjdF91cmkiOiJodHRwczovL3JlZGlyZWN0LmNvbS9yZWRpcmVjdCIsImlhdCI6MTc3NDkzOTEyOCwiZXhwIjoxNzc0OTM5MjQ4fQ.gyXThRwkXNxypcrFv8PPvS7sfPoTMW-rMK-ntvgc17YNmLpTXQqRdyFIT8jUK0J2fpmEwZfsnROBIdzkOZyF0g
```

{% endcode %}
{% endtab %}

{% tab title="Multiple Signatures" %}
**Use Case:** If you require the same user to sign on multiple locations within the same document.&#x20;

**Payload:**

* `sign_locations` : an array of up to 20 signature locations. Each location is an object containing `x` ,`y` , and `page` .  See detailed explanation in [#how-to-set-signing-coordinates](#how-to-set-signing-coordinates "mention")
* `doc_name`: The name of the document. This will be shown to the user.
* `client_id`: Your application's registered client ID.
* `signer_uin_hash` : (Optional) A SHA256 hash of the user's NRIC in uppercase. If specified, only this user will be able to view and sign the document. Refer to [Sign Portal User Journey](/for-relying-parties/api-documentation/sign-v3/sign-portal#negative-login-flows) for error flows your user might see after logging in.
* `redirect_uri` : The URL your user will be redirected to upon success/error. It must match against your redirect\_uri(s) registered on Singpass Developer Portal.
* `jti`: Standard JWT ID, a unique identifier for the JWT, must be a UUID.
* `iat` & `exp`: Standard Issued At / Expiration timestamp of JWT. Must issued within 2 minutes.

{% hint style="warning" %}
Please note that we will reject the request if there are overlapping signatures detected.
{% endhint %}

**Example:**&#x20;

{% code overflow="wrap" %}

```
eyJhbGciOiJFUzI1NiIsImtpZCI6InBUaHpVbl9JNVJXZzcyQWEyUzI1LXhoRG0wRzBNWjNSN3FpWUdyYmVmT0EifQ.eyJkb2NfbmFtZSI6ImR1bW15LnBkZiIsImNsaWVudF9pZCI6ImxvY2FsLXRlc3QiLCJqdGkiOiI0NDE2ZDY2MS0xZmMxLTQ4MjItYWM4ZS00YzZmODhhYjI2MTIiLCJzaWduX2xvY2F0aW9ucyI6W3sieCI6MCwieSI6MCwicGFnZSI6MX0seyJ4IjowLjUsInkiOjAuNSwicGFnZSI6MX1dLCJyZWRpcmVjdF91cmkiOiJodHRwczovL3JlZGlyZWN0LmNvbS9yZWRpcmVjdCIsImlhdCI6MTc3NDkzOTE1OCwiZXhwIjoxNzc0OTM5Mjc4fQ.zQnogAwVAEabwqlw5gOaKJDR_dRn8qwjdmvWmkxQU2Sy8cHmzbhTo-BJLv2r8vp17jPl7dk-hDmCOMw8gwj5xQ
```

{% endcode %}
{% endtab %}
{% endtabs %}

## **Body**

Raw binary PDF content

### PDF Requirements

* PDF must be less than 10 MB in size.
* PDF must have less than 100 pages.
* PDF must be unencrypted.
* PDF must not be a hybrid-reference PDF
  * for example, this can happen if you export a PDF from Word using the option **“Best for electronic distribution and accessibility.”**
* If the PDF contains existing signature(s):
  * These signatures should fulfil the requirements of a PAdES signature (minimally B-T). \
    *Note: Signatures created with Sign will automatically fulfil these requirements*&#x20;
  * These signatures should not overlap with the x-y coordinates given.
* The dimension allowed for each page are as follows:
  * Minimum width: 220pt or 300px
  * Minimum height: 60pt or 80px
  * Maximum width and height: 3370pt or 4490px
  * Ratio between width and height must be less than 2. e.g. 300px x 1000px document will not be accepted, even though it is within the acceptable range of width and height.

### Privacy

The PDF document is held temporarily during the signing process and purged when no longer needed.

## **Sample request**

```sh
curl 'https://staging.sign.singpass.gov.sg/api/v3/sign-requests' \
  -H 'authorization: XXX' \
  -H 'content-type: application/octet-stream' \
  --data 'file=@path_to_your_test.pdf'
```

## **Response**

{% tabs %}
{% tab title="Created (201)" %}
The signing URL will bring the signer to the [Sign Portal](/for-relying-parties/api-documentation/sign-v3/sign-portal) where they will perform the signing.&#x20;

```json
{
  "request_id": "signv3-01961944-5491-785a-baf2-edea694ae61a",
  "signing_url": "https://staging.sign.singpass.gov.sg/?token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNpZ24tc3RnLTAxIn0.eyJyZXF1ZXN0X2lkIjoic2lnbnYzLTAxOTYxOTQ0LTU0OTEtNzg1YS1iYWYyLWVkZWE2OTRhZTYxYSIsImlhdCI6MTc0NDE4MDYzMCwiZXhwIjoxNzQ0MTgyNDMwfQ.UGBmPO2IHUKxyMO9ZjpGt_NuhHqbBTz2IuFmYcHFzi0slw3K1V-XpIfVqW8ua3PGoWf52jNPBH2lkPhcz69r6A",
  "exchange_code": "37994734-1d76-4d27-b5f0-96dd1504cf67"
}
```

{% hint style="info" %}
`signing_url` will only be valid for **30 minutes** upon issuance. RPs are expected to create sign requests on demand upon signing.&#x20;

We may change the format of`signing_url` at any point of time.
{% endhint %}

{% hint style="warning" %}
`exchange_code` should be treated as a secret as it will be used to [get the signing result](/for-relying-parties/api-documentation/sign-v3/get-signing-result). Please **do not** share the code or put in your application logs.
{% endhint %}
{% endtab %}

{% tab title="Invalid File Error (400)" %}
There may be a few scenarios under which you will receive a 400 error:

* `CONTENT_TOO_LARGE`: File size has exceeded 10 MB
* `BAD_SIGN_LOCATION`: The signature coordinates could be out of bounds, overlapping other forms or in a page that is non existent.
* `INVALID_PDF`: The PDF file in the request may be corrupted or set with MDP permissions that restrict further signing.
* `CLIENT_SIDE_ERROR`: A catch-all error for bad input values (for e.g. a number when a string is expected, or if redirect\_uri does not exists in the list of registered redirect\_uris)

```json
{
  "error": "BAD_SIGN_LOCATION",
  "error_description": "Signature field is out of bounds",
  "id": "ca21bb57-8255-46f0-b2f4-80613768ea6e",
  "trace_id": "7755321139078968799"
}
```

{% endtab %}

{% tab title="Auth Token Error (401)" %}

```json
{
  "error": "UNAUTHORIZED",
  "error_description": "Unauthorized.",
  "error_details": "JWT validation failed",
  "id": "ca21bb57-8255-46f0-b2f4-80613768ea6e",
  "trace_id": "7755321139078968799"
}
```

{% endtab %}

{% tab title="Server error (500)" %}

```json
{
  "error": "SERVER_SIDE_ERROR",
  "error_description": "Something went wrong.",
  "id": "ca21bb57-8255-46f0-b2f4-80613768ea6e",
  "trace_id": "7755321139078968799"
}
```

{% endtab %}
{% endtabs %}

## How to set signing coordinates

When starting a sign request, define the signature position using the `x` and `y` parameters in the JWT payload. These coordinates are percentage-based and relative to the PDF's dimensions, starting from the bottom left corner.

* **y**: Vertical position (0 to 1), where 0 is the bottom and 1 is the top.
* **x**: Horizontal position (0 to 1), where 0 is the left and 1 is the right.

We will not accept coordinates where a signature cannot be placed due to being outside the page boundaries. The **signature size is fixed at 60x225 points**, regardless of the PDF size.

| Example                                                                                                                                                                                                                                                     |                                                                     |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| <p>✅ <strong>x = 0, y =  0</strong></p><p></p><p>The bottom-left corner of the signature is positioned at the bottom-left corner of the document.</p>                                                                                                       | <img src="/files/MKFDKWLn7iMlDwR6f4mb" alt="" data-size="original"> |
| <p>✅ <strong>x = 0.5, y = 0.5</strong></p><p>The bottom-left corner of the signature is positioned in the center of the document.</p>                                                                                                                       | <img src="/files/JiwGgWn805Yvn4SDgPj6" alt="" data-size="original"> |
| <p>🚫 <strong>x = 1, y = 1</strong></p><p>Error: Signature will be out of page area</p>                                                                                                                                                                     |                                                                     |
| <p>🚫 <strong>x = 0.00001, y = 0.00001</strong></p><p>Error: Coordinates can only be specified up to 4 decimal places. </p>                                                                                                                                 |                                                                     |
| <p>⚠️ <strong>x = 0, y = 0</strong><br>Signature will appear cropped if the PDF has been set to a coordinate other than (0,0). This is usually not the default behaviour for PDFs, and you can check this at Page Box / Media box settings of your PDF.</p> | <img src="/files/EK2HZg5lhgeRiUUpCdm5" alt="" data-size="original"> |


# Redirect From Sign with Singpass

After completing the digital signing process on the Sign portal, the user will be redirected back to the RP’s application. The redirection uses the pre-registered URL set during [**onboarding**](/for-relying-parties/how-our-api-works) and includes key query parameters, allowing the RP to track and finalise the transaction.

## Additional Query Parameters

* **request\_id:**  Match the sign request id returned during the initiate sign request API.
* **status:** Either `success` or `cancelled`, RP must check the actual signing status from [webhook](/for-relying-parties/api-documentation/sign-v3/accept-success-signing-webhook) or [get signing result API](/for-relying-parties/api-documentation/sign-v3/get-signing-result)
  * `success`: user is being redirected back to RP after successfully completing the signing
  * `cancelled`: user is being redirected back to RP after being identified as a [non-intended signer](https://docs.sign.singpass.gov.sg/for-relying-parties/api-documentation/sign-v3/sign-portal#non-intended-signers)

{% hint style="warning" %}
The query parameters will be changed soon.
{% endhint %}

**Example**

```sh
# Given pre-registed recirect_uri is https://rp.com/sign-redirect
https://rp.com/sign-redirect?request_id=signv3-01961988-8b77-7a86-9641-5f2507965c04&status=success
```

<details>

<summary>Can I provide more than 1 redirect URL for my application?</summary>

We only accept one redirect URL per application onboarded. When we send the user back to your application, we will include the `request_id` in the query parameters, which you can then use to redirect the user back to whichever page on your application you deem necessary.&#x20;

</details>


# Accept Success Signing Webhook

Upon successful signing, Sign will notify the relying party (RP) via a webhook. The RP must implement and expose a webhook endpoint on their server to receive this notification. The webhook will inform the RP that a signing transaction has been successfully completed and provide the URL to download the signed document.

## Path

A <mark style="color:green;">`POST`</mark> request to the webhook URL that was provided to us during your [onboarding](/for-relying-parties/how-our-api-works).&#x20;

## **Body**

A JSON object with only 1 field `token` .

```json
{
  "token": "xxxx"
}
```

The token is a standard [JSON Web Token](https://datatracker.ietf.org/doc/html/rfc7519) with payload:

* `request_type`: Always `signed_doc_url`
* `signed_doc_url`: Where you can download the signed document. This link will expire after 2 minutes.&#x20;
* `request_id`: Same as the request id returned during initiate sign request
* `signer_info` : an object containing the following information:
  * `signer_name` : Name of the signer as per the user cert CN
  * `signer_partial_uinfin` : The last 4 alphanumerical value of the signer's NRIC
  * `signed_at` : Unix timestamp in milliseconds of when the user completed the sign request.
* `iat` & `exp`: Standard Issued At / Expiration timestamp of JWT. The expiry of the token is set to 2 minutes. The `signed_doc_url`  will expire within the `exp` as well.&#x20;

{% hint style="danger" %}
RP are required to implement token validation by fetching the [JWKS Specification](/for-relying-parties/api-documentation/sign-v3/jwks-specification#sign-with-singpass-jwks), and verify the signature, expiration, and claims contained in the JWT.
{% endhint %}

{% hint style="danger" %}
RP are required to download the document **immediately** as the `signed_doc_url` will expire in 2 minutes. We do not recommend RP to send the `signed_doc_url` to user for the same reason.
{% endhint %}

**Example:**&#x20;

{% code overflow="wrap" %}

```
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNpZ24tZGV2LTAxIn0.eyJzaWduZWRfZG9jX3VybCI6Imh0dHBzOi8vZGV2LnNpZ24uc2luZ3Bhc3MuZ292LnNnL3NpZ25lZC90ZXN0LXNpZ25lZC1wZGYtaWQ_RXhwaXJlcz0xNzU4NjE2MDM0JktleS1QYWlyLUlkPUsyQUUwQzdLRkhNODJOJlNpZ25hdHVyZT1EZ1FBY0V-ZjhWT2xJTmUybkYtWFlKYVJTMXQ2RzQ5ckcyZnJKUzJVT29wbDR6SnpmcH5-flBhRHFoaH52RzBxanVJQjFjOFg1QWtrdG15NkNRcDNPN2QwNHBmN1RnZm5ZaW5pakJLT3Z3Ui1KdEpjOUFzcGx2VzJ0NUQzNXBRbjFyLVFNVURrOUFQYUJRVWh4eX4wNTE0RDN4bXBoS2hidmFXeUN5QXBEaWRlLUlYelo3QzVRMWxpUDNVQ1JiLUxsdWpKLU4xUUZTaWN5dnNmNWhjanRQVUw5Tk5GRG5OdFlYR2U1Y29pOX5kZ0lramNQbEZzS29KdTBkS3UtbzA2UDhEMFhzeExkTllrRjlnY051bVBiOHNxOXE1ZjhkVHNBUUIwNnhFMmp3dVhmaTR5eG8yalRQTmczRGhxeWxiTi1aWkkwWjJrQk8tM0U3bHROQXliVUFfXyIsInNpZ25lcl9pbmZvIjp7InNpZ25lcl9uYW1lIjoiVXNlciBTMzE3Njc0OUQiLCJzaWduZXJfcGFydGlhbF91aW5maW4iOiI3NDlEIiwic2lnbmVkX2F0IjoxNzU4NjE1OTEzNTY3fSwiZXhwIjoxNzU4NjE2MDMzfQ.xxx
```

{% endcode %}

## Response

It is sufficient to respond with only 2XX HTTP status code without any response body.

## Retry Settings <a href="#retry-settings" id="retry-settings"></a>

Singpass will perform automated retries of webhook calls in the case of timeouts or certain errors.&#x20;

```
Per try timeout: 2s
Delay between each retry: 2s
Max attempts: 3
```

{% hint style="danger" %}
RP are required to respond to the webhook request within **2 seconds.**
{% endhint %}

## **IP Address**

We strive to maintain consistent egress IPs, but please note that they are subject to change with short notice.

* **Staging:**
  * 54.169.20.186
* **Production:**
  * 18.143.229.39
  * 54.179.76.90
  * 3.0.39.45


# Get Signing Result

This endpoint allows the relying party (RP) to actively query the signing results and obtain the signed document after completion.&#x20;

{% hint style="info" %}
It is **not recommended to use this endpoint for status polling** as the [webhook](/for-relying-parties/api-documentation/sign-v3/accept-success-signing-webhook) is more efficient. RPs do not need to invoke this API if the success signing webhook was captured.&#x20;
{% endhint %}

## **Path**

&#x20;<mark style="color:green;">`GET`</mark> `/sign-requests/:request_id/signed-doc`

## **Headers**

| Name          | Value              |
| ------------- | ------------------ |
| Content-Type  | `application/json` |
| Authorization | `<token>`          |

## **Authorization token**

RP should sign the signature parameter into a JWT token as authorization token.

* **Token Type:** Standard JWT ([JSON Web Token](https://datatracker.ietf.org/doc/html/rfc7519))
* **Payload:**&#x20;
  * `exchange_code`: The exchange\_code returned when initiate sign request.
  * `jti`: Standard JWT ID, a unique identifier for the JWT, must be a UUID.
  * `iat` & `exp`: Standard Issued At / Expiration timestamp of JWT. Must issued within 2 minutes.

Example:&#x20;

{% code overflow="wrap" %}

```
eyJhbGciOiJFUzI1NiIsImtpZCI6IjEyMDUwMzM5LWUzNTktNGUyYy04YTc2LWY0Nzk0MDZmZDliMyJ9.eyJleGNoYW5nZV9jb2RlIjoiY2JlNzM3MWMtZjExMC00OTEzLWE3YmQtNjQwOTk0YjA4MDczIiwianRpIjoiY2IyYTk5NmQtZjk3ZS00YzJiLWE5ZDAtZDM0NzgxNzhjY2RmIiwiaWF0IjoxNzQ3Mzc3NzIyLCJleHAiOjE3NDczNzc4NDJ9.ZLQTMnSiqRfFE1w2jDjQgOVDKWY2Fv-HTSn976dZXmt2FVNMEzOfTdV8qCWnw8mOE5aJzIi2DQyIDZqwKAFJ4Q
```

{% endcode %}

## **Body**

Empty

## **Response**

{% tabs %}
{% tab title="Signed (200)" %}

```json
{
    "signed_doc_url": "XXXX",
    "signer_info": 
        {
            "signer_name": <Name as per the CN in the user certifiate>,
            "signer_partial_uinfin": "123A",
            "signed_at": <Timestamp of the user completing the sign request>
        }
    "exp": 1744190335
}
```

* `signed_doc_url`: Where you can download the signed document.&#x20;
* `signer_info` : an object containing the following information:
  * `signer_name` : Name of the signer as per the user cert CN
  * `signer_partial_uinfin` : The last 4 alphanumerical value of the signer's NRIC
  * `signed_at` : Unix timestamp in milliseconds of when the user completed the sign request.
* `exp`:  Expiration time (in UNIX second) of `signed_doc_url` .

{% hint style="danger" %}
Similar to the [successful signing webhook](/for-relying-parties/api-documentation/sign-v3/accept-success-signing-webhook), the `signed_doc_url`will only be valid for 2 minutes. RPs should download the document immediately for their own application. We do not recommend forwarding this URL to the user.&#x20;
{% endhint %}
{% endtab %}

{% tab title="Error (400)" %}
There may be a few scenarios under which you will receive a 400 error:

* `DOCUMENT_NOT_SIGNED`: The user has not signed the document yet and the request has not expired yet
* `REQUEST_EXPIRED`: The request has expired without the user signing the document.
* `SIGNED_ACCESS_EXPIRED`: The document has been signed, but can no longer be retrieved via this endpoint as the 1h access period has passed.

```json
{
    "id": "91934263-3ad0-4f2d-afb6-441b2e256b64",
    "trace_id": "1747520091621739744",
    "error": "DOCUMENT_NOT_SIGNED",
    "error_description": "Document is not signed yet."
}
```

{% endtab %}

{% tab title="Auth Token Error (401)" %}

```json
{
  "error": "UNAUTHORIZED",
  "error_description": "Unauthorized.",
  "error_details": "JWT validation failed",
  "id": "ca21bb57-8255-46f0-b2f4-80613768ea6e",
  "trace_id": "7755321139078968799"
}
```

{% endtab %}

{% tab title="Server Error (500)" %}

```json
{
  "error_description": "Something went wrong.",
  "id": "ca21bb57-8255-46f0-b2f4-80613768ea6e",
  "trace_id": "7755321139078968799"
}
```

{% endtab %}
{% endtabs %}

## **Sample request**

```sh
curl 'https://staging.sign.singpass.gov.sg/api/v3/sign-requests/<request_id>/signed_doc' \
  -H 'Authorization: <auth_token>'
```


# JWKS Specification

## JWKS Endpoint requirements for RP&#x20;

The client must provide the public key(s) during onboarding by host the JWKS on a publicly accessible URL. This endpoint must be compatible with Singpass's [#jwks-url-service-level-expectations](#jwks-url-service-level-expectations "mention"). These public keys will be used to verify the signature of the token in [Initiate Sign Request](/for-relying-parties/api-documentation/sign-v3/initiate-sign-request) and [Get Signing Result](/for-relying-parties/api-documentation/sign-v3/get-signing-result).

RP are allowed to provide multiple keys on the JWKS url.

{% hint style="info" %}
&#x20;[mkjwk.org](https://mkjwk.org/) is a useful open-source tool to generate different types of JWK for signing and encryption; compliant with Singpass's broad requirements on structure. While we **DO NOT** suggest this as a secure way to generate your *real* keypair (including private key); this can be a useful tool to understand how JWK works; and how it is represented for signing and encryption purposes; while you are reviewing against our supported algorithms below.
{% endhint %}

### **Key requirements:**

* Must have key `use` of value `sig` per [rfc7517#section-4.2](https://tools.ietf.org/html/rfc7517#section-4.2)
* Must contain a key ID in the standard `kid` field per [rfc7517#section-4.5](https://tools.ietf.org/html/rfc7517#section-4.5)
  * Will be used by Singpass to select the relevant key to verify the client assertion
* Must be an EC key, with curves: `P-256`, `P-384` or `P-521` *(NIST curves, aka `secp256r1`, `secp384r1`, `secp521r1` respectively)*

### **Example**

```json
{
    "keys": [
        {
            "kty": "EC",
            "use": "sig",
            "crv": "P-256",
            "kid": "6X_-_oLSH0DQLtz16o-NTKcm0lG0J-VDGHOz6tPx0Jc",
            "x": "1tR88zrGoPUV-Fr4bh_9NR-mDhC9rLswDp85hkbKBT0",
            "y": "1vYh1M53NK_b7l9Y-1FgCENOp6Fl9StVVLr3KqK_Ka8",
            "alg": "ES256"
        }
    ]
}
```

### **Key Rotation Best Practices**

Our server caches the key for <mark style="color:red;">**1 hour**</mark>. When performing key rotation, do ensure that:

* New key should be added to the JWKS at least 1 hour before it is used to sign new JWTs.
* Old key must remain available for at least 1 hour after stop signing with it, ensuring that previously issued JWTs can still be validated.

### Service Level Expectations <a href="#jwks-url-service-level-expectations" id="jwks-url-service-level-expectations"></a>

Sign requires that any JWKS is published on an endpoint that

* is served behind HTTPS on port `443` using a TLS server certificate issued by a standard *publicly verifiable CA issuer* (no private CAs), with *complete cert chain* presented by the server;
* is publicly accessible (no IP whitelisting, mTLS or other custom HTTP header requirements outside standard HTTP headers such as `Content-Type`, `Accept`);
* is able to respond in a timely fashion with respect to the below configuration.
  * Per try timeout: 3s
  * Max attempts: 3
  * Cache duration for retrieved JWKS: 1h

> Note: While the above is a technical requirement; the user experience of your users may be affected if we are unable to retrieve your JWKS in a timely fashion upon our cache expiry due to slower token exchanges with your backend. We recommend aiming for this response to be as fast as possible based on an in-memory cache; or simple static asset retrieval.

## Sign with Singpass JWKS

RP can verify the signature of a JWT from Sign with Singpass by acquiring the signing public key from this endpoint. More information about a JSON Web Key (JWK) endpoint can be found [here](https://tools.ietf.org/html/rfc7517).

Public keys returned from this endpoint could be in random sequence or rotated for security enhancement.&#x20;

### URLs

<table><thead><tr><th width="178">Environment</th><th width="576">URL</th></tr></thead><tbody><tr><td>Staging</td><td><a href="https://static.staging.sign.singpass.gov.sg/.well-known/keys.json">https://static.staging.sign.singpass.gov.sg/.well-known/keys.json</a></td></tr><tr><td>Production</td><td><a href="https://static.app.sign.singpass.gov.sg/.well-known/keys.json">https://static.app.sign.singpass.gov.sg/.well-known/keys.json</a></td></tr></tbody></table>

### Cache and key rotation

For varying reasons, keys used for signing can and will be rotated/changed with **no defined schedule**, and at the full discretion of Singpass. When a key rotation happens, the new key will be available from the JWKS endpoint and will have a different `kid` value. The new `kid` value will be reflected in all the new JWTs signed by Singpass. In such cases, cached copies of Singpass public keys must be refreshed by re-invoking the JWKS endpoint.

If the validation of the Singpass signature fails, re-fetch from the JWKS endpoint once for that validation.

{% hint style="danger" %}
Please read through the list of **DON’Ts** below:

* Do not assume the position of a signing key among the list of the returned keys.
* Do not validate Singpass signatures using a hardcoded public key. Always determine the correct key (for signature verification) by inspecting the `kid` from the JWS header, and use it to retrieve the public key from our JWKS endpoint.
* Do not cache only 1 key. Caching should be done for the entire JWKS.
  {% endhint %}


# Sign Portal User Journey

This page outlines what your users will see on the Sign portal when you direct them to the URL provided in the [Initiate Sign Request](/for-relying-parties/api-documentation/sign-v3/initiate-sign-request) response.&#x20;

## Happy Flow

This is the main user journey that your users will experience.&#x20;

*If the interactive demo below does not work, please see the* [*step-by-step illustration here*](/for-relying-parties/api-documentation/sign-v3/sign-portal/sign-portal)*.*

{% embed url="<https://app.arcade.software/share/tZAyhjFHyu5FvIEiUcEC>" %}

## Negative Flows

There may be instances where your users may encounter error screens when using the Sign Portal

### Non-intended signers

<div data-full-width="true"><figure><img src="/files/GQDf6UAPypuOJKJTx8F9" alt="Error screen if the user is not the intended signer" width="563"><figcaption><p>Error screen if the user is not the intended signer</p></figcaption></figure></div>

RPs can specify which user is the intended signer by providing a hash of the user's NRIC when creating a sign request. If an RP includes this specification, after the user performs the login, they will see the above error screen.&#x20;

* The user will not be able to view and sign the document
* The error message will tell the user to contact the RP directly for help.
* The only action the user will be allowed to do is to "Leave Sign Portal". This button will be linked to the RP's redirect URL.&#x20;

{% hint style="info" %}
Note: The user will see that they have performed the login step **successfully** on their mobile's Singpass App. However, the Sign Portal page will thereafter show this error screen.
{% endhint %}

{% hint style="warning" %}
It is RP's responsibility to provide the **correct** NRIC in the **appropriate** format when creating sign requests. [See the documentation here](/for-relying-parties/api-documentation/sign-v3/initiate-sign-request).
{% endhint %}

### Expired or Incorrect Links

<figure><img src="/files/VeKrbW2vcdbsZT5XiF4S" alt="" width="563"><figcaption><p>Error screen if the user visits a signing request that has already expired or the link is invalid</p></figcaption></figure>

The link provided is **valid for 30 minutes**. If the link has expired, users will not be able to log in to view and sign the document. Instead, user will see a generic error screen when they attempt to access the portal.

The same error screen will be shown if users access the portal with an incorrect link (e.g. due to typos).&#x20;

## Commonly Asked Questions

<details>

<summary>Why does the user have to scan QR code twice? What’s the difference?</summary>

The first QR code is to verify the user's identity with Singpass and log in to the Sign portal. **If you have specified in the** [**Initiate Sign Request**](/for-relying-parties/api-documentation/sign-v3/initiate-sign-request) **parameters the NRIC of the user who can access the document, only that user would be able to perform a successful log in.** This will ensure that any sensitive contents within the document are not exposed to unintended users.&#x20;

The second QR code (which comes with a 4-digit reference code) is for the user to sign the document.

</details>

<details>

<summary>What is the reference code for during the signing?</summary>

The purpose of the reference code is to allow users to verify that they are signing the correct document, when the code on their Singpass App matches that which is shown in the Sign Portal.

</details>

<details>

<summary>What happens if a user rejects the signing on their Singpass App?</summary>

If the user **rejects** the signing (Clicks 'Reject' in Step 8), the Sign Portal will continue to stay in 'pending sign' mode. Users will have to (i) refresh the Portal to get a new QR code or (ii) return back to the organization's webpage to restart the process.&#x20;

</details>

<details>

<summary>Why can’t the user log in?</summary>

The user might not be able to log in if i) you [specified an intended signer](/for-relying-parties/api-documentation/sign-v3/initiate-sign-request#authorization-token) for the document that is not them, or ii) you did not specify a signer, but someone else has logged into the portal to access the document before them.

</details>

<details>

<summary>Why can’t the user sign?</summary>

The user will not be able to sign if i) you [specified an intended signer](/for-relying-parties/api-documentation/sign-v3/initiate-sign-request#authorization-token) for the document that is not them, ii) the document has already been signed, or iii) an unexpected error occurred.&#x20;

</details>


# Step-by-step User Journey

#### 1. By clicking the "Sign with Singpass" button on your portal, the user will be redirected to Singpass signing portal.

The following elements in the sample image below will be replaced with information provided to us during your [onboarding](/for-relying-parties/how-our-api-works):

* "*Login PDF Viewer Client*" will be replaced with your display name provided
* Your organisation's logo will be displayed beside the display name, instead of the placeholder image

![](/files/db5YjIT1XAyTm7RxvsBg)

#### 2. The user is required to verify their identity with Singpass before they can view the document. They will do this by performing a Singpass Log In with their Singpass App.

![](https://image.mux.com/0224WrueMbF3tqP456inX99UnYphFtFXLMSsOcfMiN8k/animated.gif?start=0\&end=3.933333\&width=640\&fps=30)

![](https://image.mux.com/ScS6tro518ZEOnRgBZRCNzzc11J018EwANJafdJzvePM/animated.gif?start=0\&end=5.553333\&width=640\&fps=30)

#### 3. Upon successful authentication, the user can begin reviewing the document.

The document name ("dummy.pdf") will be taken from the `doc_name` parameter provided in the payload when [initiating the sign request](/for-relying-parties/api-documentation/sign-v3/initiate-sign-request).

![](/files/BS4GYzem4DdxwTYloytj)

#### 4. In the field navigation panel, the user can easily locate the fields in the document that require their signature.

This panel is on the right of the desktop screen. For mobile users, it will be at the bottom.

![](https://image.mux.com/uwfDWl013eitl2uAY84VrhNwQ8RggeGVRLwROzrNZKsA/animated.gif?start=16.271\&end=18.351\&width=640\&fps=30)

#### 5. The user must review each field by clicking/tapping on them.

For mobile, the user can also tap the 'Mark as reviewed' button to review each field.

![](/files/txbvyuL5EYHYlOYi9NAH)

![](/files/s0CMOmkJQkie8ST8G8yL)

#### 6. The Sign button becomes active once all fields are reviewed.

![](/files/wwz2asP3NjJwgLE1S42E)

#### 7. When the Sign button is clicked, the user will be shown a QR code, which they should scan with their Singpass App on their mobile device.

It will also display a **match reference code** ("9736" in the example below)

![](https://image.mux.com/sxQ4Weh5QO52fv15FsE9v1OEX9sqd8kIa4Q5p8nKDcQ/animated.gif?start=0\&end=3.533333\&width=640\&fps=30)

#### 8. On their Singpass App, the user will select Approve or Reject.

They can also check the **Match reference code** to ensure they are signing the right document.&#x20;

![](https://image.mux.com/UJdgGDYCYEahtauHU4JTdtpwiDRbuYAaOveW7eKtTzA/animated.gif?start=0\&end=4.8\&width=640\&fps=30)

#### 9. Document is successfully signed! At this point, the user can return to your portal, or view their signed document first.

The user can view their signed document by clicking 'View Signed Document'.

![](/files/PJvHBhoi6YLbES6vwQ3T)

#### 10. At this point, the user can check signature details for added transparency and peace of mind.

The user will return to your portal by clicking 'Leave Sign Portal' as the final step, to complete the rest of the process (if any).

![](/files/fVP1uqZWWkfzHrsYyCjv)


# Cancel Sign Request

This endpoint allows RP to initiate a cancellation to a sign request. RP can cancel any active or expired sign requests created by them.

A cancellation request will be rejected if **any** of the following conditions are met:

* The user has already initiated a sign session (clicked *Proceed to Sign* and signing QR code is shown)&#x20;
* The document has already been signed
* The sign request has already been cancelled

### Path <a href="#path" id="path"></a>

<mark style="color:green;">`POST`</mark> `/sign-requests/cancel`

### Headers <a href="#headers" id="headers"></a>

| Name          | Value            |
| ------------- | ---------------- |
| Content-Type  | application/json |
| Authorization | \<token>         |

## Authorization token

RP should sign the signature parameter into a JWT token as authorization token.

* **Token Type:** Standard JWT ([JSON Web Token](https://datatracker.ietf.org/doc/html/rfc7519))
* **Payload:**
  * `request_id`: Same as the request id returned during initiate sign request
  * `client_request`: Your application's registered client ID
  * `iat` & `exp`: Standard Issued At / Expiration timestamp of JWT. Must issued within 2 minutes.

Example:

{% code overflow="wrap" %}

```
eyJhbGciOiJFUzI1NiIsImtpZCI6InBUaHpVbl9JNVJXZzcyQWEyUzI1LXhoRG0wRzBNWjNSN3FpWUdyYmVmT0EifQ.eyJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInJlcXVlc3RfaWQiOiJzaWdudjMtMDE5OWNjOWMtOGI3Yy03MGRmLWIyOWItMzk4NDNhMTBjMzMyIiwiaWF0IjoxNzYwMDc0NDkxLCJleHAiOjE3NjAwNzQ2MTF9.-mf63KNiCCKNuVgAwn7OL4Lv7vnZ9fvPT5Pm2lCWQn36k6SK5jkBm8Iwonr0Yg5cTb1Jf2T1DK5aG_1VzYkqig
```

{% endcode %}

## Body

Empty

## Response

{% tabs %}
{% tab title="Cancelled (200)" %}

* `200 OK` Sign request successfully cancelled
  {% endtab %}

{% tab title="Active Session (400)" %}

```
{
  'error': 'ACTIVE_SESSION_ONGOING'
  'error_description': 'Request cannot be cancelled due to an active sign session'
}
```

* The user has already initiated a sign session (clicked *Proceed to Sign*)
* RP will not be able to cancel and will receive `400 Bad Request`
  {% endtab %}

{% tab title="Request Signed (400)" %}

```
{
  'error': 'DOCUMENT_ALREADY_SIGNED'
  'error_description': 'Document has already been signed'
}
```

* The document has already been signed
* RP will not be able to cancel and will receive `400 Bad Request`
  {% endtab %}

{% tab title="Request Cancelled (400)" %}

```
{
  'error': 'REQUEST_ALREADY_CANCELLED'
  'error_description': 'Document has already been cancelled'
}
```

* The sign request has already been cancelled
* RP will not be able to cancel and will receive `400 Bad Request`
  {% endtab %}

{% tab title="Invalid Request ID" %}

```
{
  'error': 'CLIENT_SIDE_ERROR'
  'error_description': 'No record found'
}
```

* If the request\_id is invalid or not found, RP will receive `400 Bad Request` .
  {% endtab %}
  {% endtabs %}

### Sample Request <a href="#sample-request" id="sample-request"></a>

```sh
curl -X POST http://staging.sign.singpass.gov.sg/api/v3/sign-requests/cancel \
  -H "Authorization: eyJhbGciOiJFUzI1NiIsImtpZCI6InBUaHpVbl9JNVJXZzcyQWEyUzI1LXhoRG0wRzBNWjNSN3FpWUdyYmVmT0EifQ.eyJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInJlcXVlc3RfaWQiOiJzaWdudjMtMDE5OWNjOWMtOGI3Yy03MGRmLWIyOWItMzk4NDNhMTBjMzMyIiwiaWF0IjoxNzYwMDc0NDkxLCJleHAiOjE3NjAwNzQ2MTF9.-mf63KNiCCKNuVgAwn7OL4Lv7vnZ9fvPT5Pm2lCWQn36k6SK5jkBm8Iwonr0Yg5cTb1Jf2T1DK5aG_1VzYkqig" \
  -H "Content-Type: application/json"

```


# Sign V1

Last Updated: 2 June 2025

{% hint style="warning" %}
We are no longer accepting requests to onboard Sign V1. Please refer to the [latest documentation for V3](/for-relying-parties/api-documentation/sign-v3) instead.&#x20;
{% endhint %}

## Changelog

<table><thead><tr><th width="174">Date</th><th>Changes</th></tr></thead><tbody><tr><td>30 Sep 2024</td><td><ul><li>Introduction of <a href="#authenticating-via-json-web-token"><mark style="color:blue;">JWT as the preferred authentication mech</mark></a><mark style="color:blue;">anism</mark></li><li>Deprecation of Basic Authentication</li></ul></td></tr><tr><td>11 Nov 2024</td><td>Added IP addresses for whitelisting</td></tr><tr><td>5 Mar 2025</td><td>Update to <a href="#qr-code-deeplinking-specifications">QR Code deeplinking specifications</a> to support new staging app</td></tr><tr><td>27 May 2025</td><td>Update to <a data-mention href="#other-dsap-requirements">#other-dsap-requirements</a></td></tr><tr><td>2 June 2025</td><td>Updated to indicate that <a href="/pages/LKnjALwqjUpQVNhpg4ih">V3 is released</a>; onboarding for v1 no longer supported</td></tr></tbody></table>

## Introduction <a href="#introduction" id="introduction"></a>

The guide provide a clear illustration of the web-based application programming interfaces (API) for the use of Document Signing Application Providers (DSAP). Described here are the necessary APIs that DSAPs must invoke to facilitate a document signing process for a Singpass user.

## Document Signing Flow Diagram <a href="#document-signing-flow-diagram" id="document-signing-flow-diagram"></a>

An overview of the document signing flow and the interactions between DSAP, Singpass and other dependencies.

<figure><img src="/files/PmgQk2syKxBeTQsFnAiZ" alt=""><figcaption><p>Document signing flow</p></figcaption></figure>

## Staging and Production URLs <a href="#staging-and-production-urls" id="staging-and-production-urls"></a>

**Disclaimer:** The domains used in the sample requests in the API specs below may not be accurate for your environment. Please choose the correct one from the table below that suits your testing needs.

| Environment    | Domain                                          | Access Mechanism                                                                                                                                                     |
| -------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Staging**    | <https://staging.sign.singpass.gov.sg/api/v1/*> | <ul><li>SSL</li><li>Basic Authentication (Deprecated)</li><li>JWT <a href="https://docs.sign.singpass.gov.sg/#authenticating-via-json-web-token">(New)</a></li></ul> |
| **Production** | <https://app.sign.singpass.gov.sg/api/v1/*>     | <ul><li>SSL</li><li>Basic Authentication (Deprecated)</li><li>JWT <a href="https://docs.sign.singpass.gov.sg/#authenticating-via-json-web-token">(New)</a></li></ul> |

{% hint style="warning" %}
You must not do "cert pinning" or create any form of dependency to the **leaf** TLS certificates of the above domains. Singpass reserves the right to rotate its TLS leaf certificate without prior notice to partners.

mTLS is no longer required. We will ignore all incoming mTLS client authentication.
{% endhint %}

#### JWKS URLs

<table><thead><tr><th width="192">Environment</th><th>Domain</th><th>Access Mechanism</th></tr></thead><tbody><tr><td><strong>Staging</strong></td><td><a href="https://static.staging.sign.singpass.gov.sg/.well-known/keys.json">https://static.staging.sign.singpass.gov.sg/.well-known/keys.json</a></td><td>SSL</td></tr><tr><td><strong>Production</strong></td><td><a href="https://static.app.sign.singpass.gov.sg/.well-known/keys.json">https://static.app.sign.singpass.gov.sg/.well-known/keys.json</a></td><td>SSL</td></tr></tbody></table>

{% hint style="warning" %}
Please note that there may be more than one key appearing in the JWKS, and DSAPs must use the `kid` value to identify the right key.
{% endhint %}

## DSAP Server Certificate <a href="#dsap-server-certificate" id="dsap-server-certificate"></a>

The webhook server must be configured to present the full certificate chain to Singpass when Singpass invokes the notification webhook endpoints. This can be verified using a tool like [SSL Labs](https://www.ssllabs.com/). Please note that your cert should not be a self-signed certificate.

Additionally, server certificates presented must minimally conform to the following requirements:<br>

<table><thead><tr><th width="192">Requirement</th><th>RSA Certs</th><th>ECC Certs</th></tr></thead><tbody><tr><td><strong>Required Public Key dimensions</strong></td><td><p></p><p>>= 2048 bits </p><p>>= 3072 bits (recommended)</p></td><td>>= 256-bit based on curves <code>P-256</code>, <code>P-384</code> or <code>P-521</code> <em>(NIST curves, aka <code>secp256r1</code>, <code>secp384r1</code>, <code>secp521r1</code> respectively)</em></td></tr><tr><td><strong>X.509 v3 extension KeyUsage</strong></td><td>digitalSignature, keyEncipherment</td><td>digitalSignature</td></tr><tr><td><strong>X509 v3 extension ExtendedKeyUsage</strong></td><td>serverAuth</td><td>serverAuth</td></tr></tbody></table>

## General Error Response <a href="#general-error-response" id="general-error-response"></a>

DSS APIs are RESTful in design and communicate classes of errors based on the **Http Status** code. The status code should be used to determine if the error is caused by consumer or provider. Consumers should log the HTTP status code along with the `id` and/or `trace_id` of the error.

<table><thead><tr><th width="338">HTTP Status Code</th><th>Description</th><th data-hidden>RSA Certs</th></tr></thead><tbody><tr><td><strong>4XX</strong></td><td><p>Errors caused by API consumer. You can expect codes such as 400, 401, 403, 404 etc if incorrect requests are made to APIs.</p><p></p><p>Example: <strong>400: Invalid/missing request arguments</strong></p></td><td><p></p><p>>= 2048 bits </p><p>>= 3072 bits (recommended)</p></td></tr><tr><td><strong>5XX</strong></td><td><p>Errors caused by DSS or its dependencies. You can expect codes such as 500, 502, 503 etc if there is an issue on DSS or its dependencies.</p><p></p><p>Example: <strong>500: Internal Server Error due to some kind of programming error.</strong></p></td><td>digitalSignature, keyEncipherment</td></tr></tbody></table>

#### **Example: Invalid Request Parameters**

```
HTTP/1.1 400 Bad Request
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Cache-Control: no-cache, no-store, must-revalidate
Transfer-Encoding: chunked
Content-Type: application/json
Date: Tue, 24 Sep 2024 02:30:42 GMT
Content-Length: 190

{
  "id" : "bcba4bc3-534e-4891-bfa2-e872b4502d80",
  "error" : "CLIENT_SIDE_ERROR",
  "error_description" : "This is an invalid request.",
  "trace_id" : "66f22452fb051066bf30f8002a3c5e4a"
}
```

#### **Example: Server Error**

```
HTTP/1.1 500 Internal Server Error
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Cache-Control: no-cache, no-store, must-revalidate
Transfer-Encoding: chunked
Content-Type: application/json
Date: Tue, 24 Sep 2024 02:30:42 GMT
Content-Length: 192

{
  "id" : "bcba4bc3-534e-4891-bfa2-e872b4502d80",
  "error" : "SERVER_SIDE_ERROR",
  "error_description" : "An unexpected error occurred.",
  "trace_id" : "66f22452d4ed47e5b4fbb870813ab8eb"
}
```

### Description of fields <a href="#description-of-fields" id="description-of-fields"></a>

<table><thead><tr><th width="192">Path</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>id</code></td><td>String</td><td>The unique identifier for this error/request. Please log this identifier for support and debugging purposes.</td></tr><tr><td><code>trace_id</code></td><td>String</td><td>(Optional) An auxiliary id for request correlation across services. Please also log this identifier for operational support and debugging purposes.</td></tr><tr><td><code>error</code></td><td>String</td><td>Error code representing broad class of error. See Error Codes for the list of possible error codes that can be returned and what they represent.</td></tr><tr><td><code>error_description</code></td><td>String</td><td>Returns human readable general information about the reason for the error. Note that due to security reasons; detailed information is unlikely to be available in this message.</td></tr></tbody></table>

### Error Codes<br>

<table><thead><tr><th width="338">Error Code</th><th>Description</th><th data-hidden>RSA Certs</th></tr></thead><tbody><tr><td><code>CLIENT_SIDE_ERROR</code></td><td>Generic error code for an invalid request.</td><td><p></p><p>>= 2048 bits </p><p>>= 3072 bits (recommended)</p></td></tr><tr><td><code>SERVER_SIDE_ERROR</code></td><td>Generic error code for an error that occurred in Singpass.</td><td>digitalSignature, keyEncipherment</td></tr><tr><td><code>UNAUTHORIZED</code></td><td>Authorization header value is invalid.</td><td></td></tr><tr><td><code>MISSING_AUTHORIZATION</code></td><td>Authorization header was required but not found.</td><td></td></tr><tr><td><code>ARGUMENTS_NOT_VALID</code></td><td>Some request parameters are invalid.</td><td></td></tr><tr><td><code>SIGN_REF_NOT_FOUND</code></td><td>Requested sign ref is invalid or has already expired.</td><td></td></tr><tr><td><code>MULTIPLE_ASSERTION_HEADER</code></td><td>Multiple DSS assertion headers.</td><td></td></tr><tr><td><code>ASSERTION_NOT_VALID</code></td><td>DSS assertion header is invalid.</td><td></td></tr><tr><td><code>JWKS_URL_NOT_FOUND</code></td><td>DSAP JWKS URL is not found.</td><td></td></tr><tr><td><code>CLIENT_JWKS_ERROR</code></td><td>Unable to fetch DSAP JWKS or DSAP JWKS is invalid.</td><td></td></tr></tbody></table>

## Document Signing endpoints invoked by DSAP <a href="#document-signing-endpoints-invoked-by-dsap" id="document-signing-endpoints-invoked-by-dsap"></a>

The API in this sections are endpoints invoked by DSAPs at various part of the DSS flow.

* /doc-signing-sessions
* /doc-signing-sessions/\<sign\_ref>/hash
* /.well-known/keys.json

### Authenticating via Basic Authentication (Deprecated)

{% hint style="warning" %}
This is deprecated in favour of [authenticating via JSON Web Tokens.](https://docs.sign.singpass.gov.sg/#authenticating-via-json-web-token-recommended)
{% endhint %}

For all DSS APIs except the JWKS endpoint, DSAPs will need to authenticate themselves using their base64-encoded `client_id` and `client_secret` via the [basic authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#Basic_authentication_scheme) scheme in the following format:

```
Authorization: Basic <client_id>:<client_secret>
```

### Authenticating via JSON Web Token <a href="#authenticating-via-json-web-token" id="authenticating-via-json-web-token"></a>

DSAPs are to use JSON Web Token (JWT) assertion for authentication when calling the DSS APIs.

```
X-Dss-Assertion: <jwt_assertion>
```

#### Structure of JWT

#### Sample JWT

{% code overflow="wrap" %}

```
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InJwLXRlc3QtMDEifQ.eyJpc3MiOiJtb2NrQ2xpZW50SWQiLCJzdWIiOiJtb2NrQ2xpZW50SWQiLCJhdWQiOiJodHRwczovL3N0YWdpbmcuc2lnbi5zaW5ncGFzcy5nb3Yuc2ciLCJpYXQiOjE3Mjc2NjUwMzcsImV4cCI6MTcyNzY2NTYzNywianRpIjoiN2Q2NDdmNTMtZDEzOC00YjUxLWIxZDgtZGM2OWQ1NmNhNDAxIn0.iF9WH09CW1zhOhaSH37Faaw6kw7dbUMx2yuIAW9tD7p50ixUWIBTswvwxkutIPFY4zGFxcjrV4USwBfxpe28RA
```

{% endcode %}

**Decoded JWT Header**

```
{
  "alg": "ES256",
  "typ": "JWT",
  "kid": "rp-test-01"
}
```

| Parameter | Type   |                                                                                             |
| --------- | ------ | ------------------------------------------------------------------------------------------- |
| `alg`     | String | Algorithm used in the JWT. Acceptable values are: `ES256`, `ES384`, `ES512`                 |
| `typ`     | String | Acceptable value: `JWT`                                                                     |
| `kid`     | String | The Key ID used for the JWT. This value must correspond to a key in the DSAP JWKS endpoint. |

#### Decoded JWT Body

```
{
  "iss": "mockClientId",
  "sub": "mockClientId",
  "aud": "https://staging.sign.singpass.gov.sg",
  "iat": 1727665037,
  "exp": 1727665637,
  "jti": "7d647f53-d138-4b51-b1d8-dc69d56ca401"
}
```

| Parameter | Type   | Description                                                                                                                                                                                                                                                |
| --------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `iss`     | String | Use the ClientID provided by Singpass                                                                                                                                                                                                                      |
| `sub`     | String | Use the ClientID provided by Singpass                                                                                                                                                                                                                      |
| `aud`     | String | <p>The DSS domain that will be accepting the JWT.<br><br>Value:<br>Staging: <https://staging.sign.singpass.gov.sg><br>Production:<br><https://app.sign.singpass.gov.sg></p>                                                                                |
| `iat`     | Number | A Unix timestamp in seconds indicating the date and time when the JWT was issued.                                                                                                                                                                          |
| `exp`     | Number | A Unix timestamp in seconds indicating the date and time when the JWT will expire. Max expiry allowed is`600` seconds from `iat`                                                                                                                           |
| `jti`     | String | A random string that must be regenerated for **each JWT** to prevent replay attacks. Singpass will cache and reject all duplicate `jti` that is sent within a preset period. As such, DSAPs are advised to use a UUID-v4 value when populating this value. |

**DSAP JWKS Endpoint**

Singpass will attempt to retrieve the public key from the DSAP JWKS endpoint in order to verify the JWT. This endpoint is provided during onboarding and is tied to the DSAP Client ID.

Singpass will only accept JWT signed by EC key. DSAP JWKS should have at least 1 EC key.

#### Sample JWKS Endpoint

```
{
  "keys": [
    {
      "kty": "EC",
      "x": "FuIv2xuY0coivK-MRg01T3_JHUMEtFHdpjj8AuxCPLA",
      "y": "N6pMWRTjPzsBdUTyLrhX7tftfx4IWxvL3z1s9PoxQlE",
      "crv": "P-256",
      "use": "sig",
      "kid": "sign-prod-01"
    }
  ]
}
```

| Parameter | Type   | Description                                                                                                                         |
| --------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `kty`     | String | Key Type Acceptable value: `EC`                                                                                                     |
| `x`       | String | X-Coordinate This is the X-coordinate of the elliptic curve point, which is a component of the public key. It is base64url-encoded. |
| `y`       | String | Y-Coordinate This is the Y-coordinate of the elliptic curve point, which is a component of the public key. It is base64url-encoded. |
| `crv`     | Number | Curve Name                                                                                                                          |
| `use`     | Number | Public Key Use. Acceptable value: `sig`                                                                                             |
| `kid`     | String | Key ID. Singpass will search for the `kid` that was specified in the JWT for verification.                                          |
| `alg`     | String | \[Optional] Algorithm                                                                                                               |

DSAPs are to put the JWT in the request header with the key `X-Dss-Assertion` when calling all DSS APIs.

{% hint style="info" %}
For existing DSAP that are currently using Basic Authentication, DSAPs can continue to use the `Authorization` header when calling DSS APIs. DSS APIs will first detect if `X-Dss-Assertion` exists in the header before falling back to `Authorization`. Existing DSAPs are advised to start preparing for the change to JWT early.
{% endhint %}

### `POST /doc-signing-sessions` <a href="#post-doc-signing-sessions" id="post-doc-signing-sessions"></a>

DSAPs can call this endpoint to start a document signing session. On success, a unique signing reference value - `sign_ref` - is included in the response object. The `sign_ref` is the primary identifier of a signing session used in all exchanges between Singpass and DSAP.

DSAPs have to specify a **client notification token** when initialising a session. Singpass will specify this token in an `Authorization` header for subsequent calls to the DSAP notification webhook endpoints.

{% hint style="info" %}
A document signing session is only valid for a short amount of time. The expiry of a signing session can be found via the `expires_at` response field. When a sign session expires, Singpass will delete it from storage and calls to [<mark style="color:blue;">/doc-signing-sessions/\<sign-ref>/hash</mark>](#post-doc-signing-sessions-less-than-sign-ref-greater-than-hash) will result in a `SIGN_REF_NOT_FOUND` error. Also, any attempts to scan a doc signing QR code or sign a doc hash will fail.&#x20;

Please note that **Singpass will not notify DSAPs** when the signing session expires. It is recommended to make use of the aforementioned expires\_at response field to coordinate the session expiry between Singpass and DSAPs.
{% endhint %}

#### Request and Response Structure

**Sample Curl Request&#x20;**<mark style="color:red;">**(Deprecated)**</mark>

```
$ curl 'https://staging.sign.singpass.gov.sg/api/v1/doc-signing-sessions' -i -u 'client_id:client_secret' -X POST \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -d '{"tenant_id":"some_tenant_id","client_notification_token":"client_notification_token"}' \
    --cert client.crt --key client.key
```

**Sample Curl Request&#x20;**<mark style="color:red;">**(New)**</mark>

```
$ curl 'https://staging.sign.singpass.gov.sg/api/v1/doc-signing-sessions' -i -X POST \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'X-Dss-Assertion: <jwt-assertion>' \
    -d '{"tenant_id":"some_tenant_id","client_notification_token":"client_notification_token"}' \
    --cert client.crt --key client.key
```

#### Sample HTTP Request <mark style="color:red;">(Deprecated)</mark>

```
POST /doc-signing-sessions HTTP/1.1
Accept: application/json
Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=
Content-Type: application/json
Host: staging.sign.singpass.gov.sg
Content-Length: 86

{"tenant_id":"some_tenant_id","client_notification_token":"client_notification_token"}
```

#### **Sample HTTP Request&#x20;**<mark style="color:red;">**(New)**</mark>

```
POST /doc-signing-sessions HTTP/1.1
Accept: application/json
X-Dss-Assertion: <jwt-assertion>
Content-Type: application/json
Host: staging.sign.singpass.gov.sg
Content-Length: 86

{"tenant_id":"some_tenant_id","client_notification_token":"client_notification_token"}
```

**Sample Response**

{% code overflow="wrap" %}

```
HTTP/1.1 200 OK
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Cache-Control: no-cache, no-store, must-revalidate
Transfer-Encoding: chunked
Content-Type: application/json
Date: Tue, 24 Sep 2024 02:30:17 GMT
Content-Length: 181

{"sign_ref":"4b1b5241-9594-4e98-b9e3-7f53b73e3fcc","expires_at":1727145019,"qr_code":{"payload":"https://app.singpass.gov.sg/docsign?sign_ref=4b1b5241-9594-4e98-b9e3-7f53b73e3fcc"}}// Some code
```

{% endcode %}

**Request Body**

| Name                        | Type   | Description                                                                                                                                                                                                                                 |
| --------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client_notification_token` | String | <p>A token provided by the DSAP to be used by Singpass when invoking the DSAP’s webhook endpoint.</p><p></p><p>Allowed Characters: Alphanumeric, underscore ( \_ ), dash ( - ), and whitespace </p><p></p><p>Max length: 255 characters</p> |
| `tenant_id`                 | String | <p>The identifier of the tenant of the DSAP client using the doc signing service.</p><p></p><p>Allowed Characters: Alphanumeric, underscore ( \_ ), dash ( - ), and whitespace</p><p></p><p>Max length: 255 characters</p>                  |

{% hint style="info" %}
The value of the `tenant_id` is not the signer's identity. Rather, it is an unique identifier for the DSAP's corporate customers that we can refer to in case of any anomaly detected. If you are not reselling your service to a third party, you can reuse the DSAP name provided during onboarding as the `tenant_id`.

For example: If ABC Bank goes through DSAP to reach DSS, then `tenant_id` must be a unique identifier of ABC Bank. However, if ABC Bank is directly integrated with DSS, then `tenant_id` can be just `ABC Bank`.
{% endhint %}

#### **Request Headers**

| Name              | Description                                                                                                                                                                                           |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Authorization`   | <p><mark style="color:red;">(Deprecated)</mark> </p><p>The DSAP’s client id and client secret in http basic authentication scheme format.</p><p>Not required if DSAP is using JWT to authenticate</p> |
| `X-Dss-Assertion` | <p><mark style="color:red;">(New)</mark> </p><p>JWT issued by the DSAP for authentication.</p>                                                                                                        |

#### **Request Headers**

| Name              | Description                                                                                                                                                                                                    |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Authorization`   | <p><mark style="color:red;">(Deprecated)</mark> </p><p>The DSAP’s client id and client secret in http basic authentication scheme format.</p><p> </p><p>Not required if DSAP is using JWT to authenticate.</p> |
| `X-Dss-Assertion` | <p><mark style="color:red;">(New)</mark> </p><p>JWT issued by the DSAP for authentication.</p>                                                                                                                 |

#### **Response Body**

| Parameter Description | Type   |                                                                                                                                                                                                                                                                           |
| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sign_ref`            | String | The primary identifier of a signing session used in all exchanges between Singpass and DSAP.                                                                                                                                                                              |
| `qr_code.payload`     | String | The QR payload containing the sign ref in a format understood by SingPass Mobile (SPM). DSAPs are expected to encode and display this as a QR code for SPM users to scan. Refer to the UX Guide provided in the developer package for the QR code display specifications. |
| `expires_at`          | Number | A Unix timestamp in seconds indicating the date and time when the `sign_ref` will expire.                                                                                                                                                                                 |

{% hint style="info" %}
Please note that `sign_ref` should not be treated as a UUID-v4, and the length of the `sign_ref` may vary from time to time.
{% endhint %}

{% hint style="warning" %}
The host of the URL in `qr_code.payload` in staging environment will change to stg-app.singpass.gov.sg after 18 Mar 2025.
{% endhint %}

#### Error Responses

Please refer to [General Error Response.](#general-error-response)

### `POST /doc-signing-sessions/<sign-ref>/hash` <a href="#post-doc-signing-sessions-less-than-sign-ref-greater-than-hash" id="post-doc-signing-sessions-less-than-sign-ref-greater-than-hash"></a>

DSAPs must use this endpoint to send the document hash, document name, and challenge code to Singpass. Singpass will then forward the document information and challenge code to the user for verification and signing.

The DSAP must invoke this API only after it has received the user’s digital signing certificate through its notification webhook endpoint. See **Steps 6, 7 and 8** of the [flow diagram](#document-signing-flow-diagram) for more details about what DSAPs need to do before invoking this endpoint.

#### **Request and Response Structure**

#### **Sample Curl Request&#x20;**<mark style="color:red;">**(Deprecated)**</mark>

```
$ curl 'https://staging.sign.singpass.gov.sg/api/v1/doc-signing-sessions/c6258905-c196-4465-9182-4d95e0163e96/hash' -i -u 'client_id:client_secret' -X POST \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -d '{
  "challenge_fqdn" : "signing-partner.gov.sg",
  "doc_name" : "signable-document.pdf",
  "nonce" : "bxUoQ4XiThs0oR19arXcaHpj+lm+i0BPta4YeTXkLXX=",
  "app_launch_url" : "com.example.dsap://home",
  "doc_hash" : "6AC7637DA92C76385F95A92C7617E591A8F6DF8F74F37EF8DB7E25E648E1DB7E",
  "challenge_code" : "1567"
}' \
    --cert client.crt --key client.key
```

#### **Sample Curl Request&#x20;**<mark style="color:red;">**(New)**</mark>

```
$ curl 'https://staging.sign.singpass.gov.sg/api/v1/doc-signing-sessions/c6258905-c196-4465-9182-4d95e0163e96/hash' -i -X POST \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'X-Dss-Assertion: <jwt-assertion>' \
    -d '{
  "challenge_fqdn" : "signing-partner.gov.sg",
  "doc_name" : "signable-document.pdf",
  "nonce" : "bxUoQ4XiThs0oR19arXcaHpj+lm+i0BPta4YeTXkLXX=",
  "app_launch_url" : "com.example.dsap://home",
  "doc_hash" : "6AC7637DA92C76385F95A92C7617E591A8F6DF8F74F37EF8DB7E25E648E1DB7E",
  "challenge_code" : "1567"
}' \
    --cert client.crt --key client.key
```

**Sample HTTP Request&#x20;**<mark style="color:red;">**(Deprecated)**</mark>

```
POST /doc-signing-sessions/c6258905-c196-4465-9182-4d95e0163e96/hash HTTP/1.1
Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=
Accept: application/json
Content-Type: application/json
Host: staging.sign.singpass.gov.sg
Content-Length: 309

{
  "challenge_fqdn" : "signing-partner.gov.sg",
  "doc_name" : "signable-document.pdf",
  "nonce" : "bxUoQ4XiThs0oR19arXcaHpj+lm+i0BPta4YeTXkLXX=",
  "app_launch_url" : "com.example.dsap://home",
  "doc_hash" : "6AC7637DA92C76385F95A92C7617E591A8F6DF8F74F37EF8DB7E25E648E1DB7E",
  "challenge_code" : "1567"
}
```

#### **Sample HTTP Request&#x20;**<mark style="color:red;">**(New)**</mark>

```
POST /doc-signing-sessions/c6258905-c196-4465-9182-4d95e0163e96/hash HTTP/1.1
Accept: application/json
X-Dss-Assertion: <jwt-assertion>
Content-Type: application/json
Host: staging.sign.singpass.gov.sg
Content-Length: 309

{
  "challenge_fqdn" : "signing-partner.gov.sg",
  "doc_name" : "signable-document.pdf",
  "nonce" : "bxUoQ4XiThs0oR19arXcaHpj+lm+i0BPta4YeTXkLXX=",
  "app_launch_url" : "com.example.dsap://home",
  "doc_hash" : "6AC7637DA92C76385F95A92C7617E591A8F6DF8F74F37EF8DB7E25E648E1DB7E",
  "challenge_code" : "1567"
}
```

#### **Sample HTTP Response**

```
HTTP/1.1 200 OK
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Date: Tue, 24 Sep 2024 02:30:20 GMT
```

#### Path Parameters

| Parameter  | Description                                                                                 |
| ---------- | ------------------------------------------------------------------------------------------- |
| `sign_ref` | The primary identifier of a signing session used in all exchanges between Singpass and DSAP |

#### **Request Body**

```
{
  "challenge_fqdn" : "signing-partner.gov.sg",
  "doc_name" : "signable-document.pdf",
  "nonce" : "bxUoQ4XiThs0oR19arXcaHpj+lm+i0BPta4YeTXkLXX=",
  "app_launch_url" : "com.example.dsap://home",
  "doc_hash" : "6AC7637DA92C76385F95A92C7617E591A8F6DF8F74F37EF8DB7E25E648E1DB7E",
  "challenge_code" : "1567"
}

```

#### **Request Fields**

<table><thead><tr><th width="207">Parameter</th><th width="154">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>doc_name</code></td><td>String</td><td><p>The name of the document to be signed by the user. Do note that this value will be displayed to the user, so it is recommended to use a human-readable name. </p><p></p><p>Length must be between 1 and 255</p></td></tr><tr><td><code>doc_hash</code></td><td>String</td><td><p>The hash of the document to be signed by the user. Refer to <a href="https://docs.sign.singpass.gov.sg/#document-hash-specifications">Document Hash Specifications</a> for more details. </p><p></p><p>Length must be 64</p></td></tr><tr><td><code>challenge_code</code></td><td>String</td><td><p>A set of characters used by the user to visually verify the signing session between the Singpass App and the DSAP website. </p><p></p><p>Length must be 4. Numerical values are recommended.</p></td></tr><tr><td><code>nonce</code></td><td>String</td><td>Singpass-generated random string sent to the DSAP's webhook endpoint during user certificate notification.</td></tr><tr><td><code>challenge_fqdn</code></td><td>String</td><td><p>The domain name of the webpage displaying the QR code. This will be displayed to the user on the Singpass app. </p><p></p><p>Length must be between 1-255</p></td></tr><tr><td><p><code>app_launch_url</code></p><p></p><p></p></td><td>String</td><td><p>(Optional) This adds the possibility for the user to be redirected back to the provided App Link after they successfully authorize themselves on the Singpass App. The value passed here should be the App Link registered with Apple’s App Store and/or Google’s Play Store. </p><p></p><p>Max length allowed is 255 characters.</p></td></tr></tbody></table>

#### **Request Headers**

<table><thead><tr><th width="375">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>Authorization</code></td><td><p><mark style="color:red;">(Deprecated)</mark> </p><p>The DSAP’s client id and client secret in http basic authentication scheme format. Not required if DSAP is using JWT to authenticate</p></td></tr><tr><td><code>X-Dss-Assertion</code></td><td><p><mark style="color:red;">(New)</mark> </p><p>JWT issued by the DSAP for authentication.</p></td></tr></tbody></table>

**Response Body**

There is no body in the response for successful API call.

#### **Error Response**

If Singpass fails to find the requested `sign_ref` (e.g. due to expiry), this API will return a `HTTP 400` error, and an accompanying `SIGN_REF_NOT_FOUND`error code in the response body.

For other errors, please refer to [General Error Response.](#general-error-response)

### `GET /.well-known/keys.json`

Integrating parties can verify the signature of a JWT from Singpass by acquiring the signing public key from this endpoint. More information about a JSON Web Key (JWK) endpoint can be found [here](https://tools.ietf.org/html/rfc7517).

Public keys returned from this endpoint could be in random sequence or rotated for security enhancement. For more information, please refer to [Caching and key rotation.](#caching-and-key-rotation)

#### Request and Response Structure

#### **Sample Curl Request**

```
$ curl 'https://static.staging.sign.singpass.gov.sg/.well-known/keys.json' -i -X GET
```

#### **Sample HTTP Request**

```
GET /.well-known/keys.json HTTP/1.1
Host: static.staging.sign.singpass.gov.sg
```

#### **Sample HTTP Response**

```
HTTP/1.1 200 OK
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Cache-Control: max-age=21600, must-revalidate, no-transform, public
Content-Type: application/json
Content-Length: 436
Date: Tue, 24 Sep 2024 02:30:00 GMT

{
  "keys" : [ {
    "kty" : "EC",
    "use" : "sig",
    "crv" : "P-256",
    "kid" : "ndi_dss_signer_02",
    "x" : "ObZ0BvgODnkQqMK5OjBWn5V6DCWlEhICwC7UuXxs-Vw",
    "y" : "c9CjVC779_rvOWMwbh4cE4Jx3bcfKe_20WVDtZYc_Ns"
  }, {
    "kty" : "EC",
    "use" : "sig",
    "crv" : "P-256",
    "kid" : "ndi_dss_signer",
    "x" : "dL8zGIrEMDfwlMxsd_uYqNbjq6PUHaswudG5lMJlcyI",
    "y" : "f2mU8Q2eE1pUsN37ktEHTf3n0u1u_2N7VRRus-3u4Z0"
  } ]
}
```

#### Request Body

There is no request body for this API call.

#### **Response Body**

```
{
  "keys" : [ {
    "kty" : "EC",
    "use" : "sig",
    "crv" : "P-256",
    "kid" : "ndi_dss_signer_02",
    "x" : "ObZ0BvgODnkQqMK5OjBWn5V6DCWlEhICwC7UuXxs-Vw",
    "y" : "c9CjVC779_rvOWMwbh4cE4Jx3bcfKe_20WVDtZYc_Ns"
  }, {
    "kty" : "EC",
    "use" : "sig",
    "crv" : "P-256",
    "kid" : "ndi_dss_signer",
    "x" : "dL8zGIrEMDfwlMxsd_uYqNbjq6PUHaswudG5lMJlcyI",
    "y" : "f2mU8Q2eE1pUsN37ktEHTf3n0u1u_2N7VRRus-3u4Z0"
  } ]
}
```

#### Caching and Key Rotation

{% hint style="warning" %}
Responses from this endpoint, or individual keys from inside the JWKS can and should be cached for at least 1 hour, and NOT retrieved for each JWT validation. Cache-Control headers on the response indicate a possible policy.
{% endhint %}

For varying reasons, keys used for signing can and will be rotated/changed with no defined schedule, and at the full discretion of Singpass. When a key rotation happens, the new key will be available from the JWKS endpoint and will have a different `kid` value. The new `kid` value will be reflected in all the new JWTs signed by Singpass. In such cases, cached copies of Singpass public keys must be refreshed by re-invoking the JWKS endpoint.

If the validation of the Singpass signature fails, re-fetch from the JWKS endpoint once for that validation.

Please read through the list of **DON’Ts** below:

* Do not assume the position of a signing key among the list of the returned keys.
* Do not validate Singpass signatures using a hardcoded public key OR `kid`. Always determine the correct key (for signature verification) by inspecting the `kid` from the JWS header, and use it to retrieve the public key from our JWKS endpoint.
* Do not cache only 1 key. Caching should be done for the entire JWKS.

## DSAP Notification Webhook Endpoint <a href="#dsap-notification-webhook-endpoint" id="dsap-notification-webhook-endpoint"></a>

### Usage <a href="#usage" id="usage"></a>

Singpass will inform DSAP of various events that happen during the document signing process. To facilitate this, DSAPs must implement and expose a webhook endpoint to accept information from Singpass.

{% hint style="info" %}
The DSAP’s webhook is the main and only mechanism for Singpass to notify the DSAP of the events that happen in the signing flow. DSAPs cannot poll or query Singpass to get the state of the user’s doc signing session. See the Session expiry section for details on how session expiry should be handled.
{% endhint %}

Singpass will call the webhook endpoint to inform of the following events:

1. User certificate notification - To send the user’s certificate (See Step 6 of flow diagram).
2. Document hash signature notification - To send the user’s document hash signature (See Step 14 of flow diagram).
3. Webhook error notification - To inform DSAP of any errors encountered during the webhook calls above.

{% hint style="warning" %}
Please note that for all calls to the webhook endpoint, DSAPs are expected to respond `HTTP 200` to the webhook calls immediately, before any significant processing is performed on the request. This is to keep low latencies on the user-facing APIs that are dependent on the webhook calls and give a better overall experience to Singpass users.
{% endhint %}

### Specifications

* The webhook must accept the `POST` HTTP method.
* It should expect a [JSON Web Tokens (JWT)](https://tools.ietf.org/html/rfc7519) in the request body.
* It should authenticate incoming calls using the [Bearer authentication scheme](https://tools.ietf.org/html/rfc6750), accepting only the `client_notification_token` given to Singpass during session initialisation.
* For non-repudiation purposes, Singpass will sign all JWTs sent to DSAPs webhook with its signing key. DSAPs can retrieve the corresponding public keys for verification from [/.well-known/keys.json.](#get-.well-known-keys.json)
* Singpass will perform automated retries of webhook requests if it encounters timeouts and on certain errors. See the webhook Retry Settings for more details.
* It is sufficient for DSAPs to respond with an HTTP status code without any response body for this endpoint.

### IP Address Whitelisting

Singpass will call your webhook endpoints from these IP addresses, which you may choose to whitelist.

| Staging       | Production    |
| ------------- | ------------- |
| 54.169.20.186 | 18.143.229.39 |
|               | 54.179.76.90  |
|               | 3.0.39.45     |

### Request and Response Structure <a href="#request-and-response-structure-3" id="request-and-response-structure-3"></a>

#### **Sample Curl Request**

{% code overflow="wrap" %}

```
$ curl 'https://<dsap_domain_url>/dsap/webhook' -i -X POST \
    -H 'Authorization: Bearer <client_notification_token>' \
    -H 'Content-Type: application/json' \
    -d '{"token":"eyJraWQiOiJuZGlfZHNzX3NpZ25lciIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJzaWduX3JlZiI6IjU2MTYzZTg5LWEyMzItNDUwMi1hNDRmLWUyMjI1MTJiYTFhZCIsInJlcXVlc3RfdHlwZSI6InVzZXJfY2VydCIsImV4cCI6MTcyNzE0NTE1MiwidXNlcl9jZXJ0IjoiTUlJQnJUQ0NBVEtnQXdJQkFnSUNBK2N3Q2dZSUtvWkl6ajBFQXdNd05URUxNQWtHQTFVRUJoTUNVMGN4RERBS0JnTlZCQW9NQTA1RVNURVlNQllHQTFVRUF3d1BkR1Z6ZEVCdVpHa3VaMjkyTG5Obk1CNFhEVEl3TURZd016QTNNalF5TjFvWERUSXdNRFl3TXpBM01qWXdOMW93VERFdE1Dc0dBMVVFQlJNa09HRmtPREExWW1VdE56Z3pOQzAwWWpZNExXRmxZek10Tm1NNE5UUTNObVpsWWpFeE1Sc3dHUVlEVlFRRERCSlRNREF3TURBd01Ea2dTbTlvYmlCRWIyVXdkakFRQmdjcWhrak9QUUlCQmdVcmdRUUFJZ05pQUFSb2FRWUVTQWpaUzBISnJwY1g1bWpRZlFzT0RaQ0s1WW1ybFdJejFyaXp3dzRBWEQ5bzRkdFJVZHBNOStGQWtlM2NreFlpWmM5SzJoYXZZdVRLLy9kM09KRzlFVHlyZ0VsVXRoV1c2R2FCZEZzV1pnRHMvenMzRkhyMFJvTThYLzB3Q2dZSUtvWkl6ajBFQXdNRGFRQXdaZ0l4QUw5WUl1M3hZMnY5YndiL2NoUWdPN0p6YnJxOGd0aTJOVmFoc0Q3Sk5kOUErOFJKcmR5QlRGZlpSMDA0elYzNk9RSXhBTzVZVHFYUWdydys1UFpXWjZSYWV3VkRKbjdEeXdYUUpiZXk3WnJ5MjdlZDdoeHNZYVQ5QlBBNElvNll5MmhHS1E9PSIsImlhdCI6MTcyNzE0NTAzMiwibm9uY2UiOiI5NjBiYzY5Yy1lY2M5LTRlOWEtODY4Yy04OTkyNTgwYjUyNTcifQ.ERUp7x8yEYZkdJEDU3z_0IZNYb0MdKknaoYYZXjQ8X4Dh29qtAP39W4N3dGJu_vvhYbtSjXX-JgXEINsMj8rtkkULZWsRXN8GTPalFTFc6l00_Kx09SLiha6Lc-UevED"}'
```

{% endcode %}

#### Sample HTTP Request

{% code overflow="wrap" %}

```
POST /dsap/webhook HTTP/1.1
Authorization: Bearer <client_notification_token>
Content-Type: application/json
Host: <dsap_domain_url>:443
Content-Length: 1215

{"token":"eyJraWQiOiJuZGlfZHNzX3NpZ25lciIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJzaWduX3JlZiI6IjU2MTYzZTg5LWEyMzItNDUwMi1hNDRmLWUyMjI1MTJiYTFhZCIsInJlcXVlc3RfdHlwZSI6InVzZXJfY2VydCIsImV4cCI6MTcyNzE0NTE1MiwidXNlcl9jZXJ0IjoiTUlJQnJUQ0NBVEtnQXdJQkFnSUNBK2N3Q2dZSUtvWkl6ajBFQXdNd05URUxNQWtHQTFVRUJoTUNVMGN4RERBS0JnTlZCQW9NQTA1RVNURVlNQllHQTFVRUF3d1BkR1Z6ZEVCdVpHa3VaMjkyTG5Obk1CNFhEVEl3TURZd016QTNNalF5TjFvWERUSXdNRFl3TXpBM01qWXdOMW93VERFdE1Dc0dBMVVFQlJNa09HRmtPREExWW1VdE56Z3pOQzAwWWpZNExXRmxZek10Tm1NNE5UUTNObVpsWWpFeE1Sc3dHUVlEVlFRRERCSlRNREF3TURBd01Ea2dTbTlvYmlCRWIyVXdkakFRQmdjcWhrak9QUUlCQmdVcmdRUUFJZ05pQUFSb2FRWUVTQWpaUzBISnJwY1g1bWpRZlFzT0RaQ0s1WW1ybFdJejFyaXp3dzRBWEQ5bzRkdFJVZHBNOStGQWtlM2NreFlpWmM5SzJoYXZZdVRLLy9kM09KRzlFVHlyZ0VsVXRoV1c2R2FCZEZzV1pnRHMvenMzRkhyMFJvTThYLzB3Q2dZSUtvWkl6ajBFQXdNRGFRQXdaZ0l4QUw5WUl1M3hZMnY5YndiL2NoUWdPN0p6YnJxOGd0aTJOVmFoc0Q3Sk5kOUErOFJKcmR5QlRGZlpSMDA0elYzNk9RSXhBTzVZVHFYUWdydys1UFpXWjZSYWV3VkRKbjdEeXdYUUpiZXk3WnJ5MjdlZDdoeHNZYVQ5QlBBNElvNll5MmhHS1E9PSIsImlhdCI6MTcyNzE0NTAzMiwibm9uY2UiOiI5NjBiYzY5Yy1lY2M5LTRlOWEtODY4Yy04OTkyNTgwYjUyNTcifQ.ERUp7x8yEYZkdJEDU3z_0IZNYb0MdKknaoYYZXjQ8X4Dh29qtAP39W4N3dGJu_vvhYbtSjXX-JgXEINsMj8rtkkULZWsRXN8GTPalFTFc6l00_Kx09SLiha6Lc-UevED"}
```

{% endcode %}

#### Sample HTTP Response

```
HTTP/1.1 200 OK
Date: Tue, 24 Sep 2024 02:30:32 GMT
Connection: close
```

#### **Request Body**

{% code overflow="wrap" %}

```
{"token":"eyJraWQiOiJuZGlfZHNzX3NpZ25lciIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJzaWduX3JlZiI6IjU2MTYzZTg5LWEyMzItNDUwMi1hNDRmLWUyMjI1MTJiYTFhZCIsInJlcXVlc3RfdHlwZSI6InVzZXJfY2VydCIsImV4cCI6MTcyNzE0NTE1MiwidXNlcl9jZXJ0IjoiTUlJQnJUQ0NBVEtnQXdJQkFnSUNBK2N3Q2dZSUtvWkl6ajBFQXdNd05URUxNQWtHQTFVRUJoTUNVMGN4RERBS0JnTlZCQW9NQTA1RVNURVlNQllHQTFVRUF3d1BkR1Z6ZEVCdVpHa3VaMjkyTG5Obk1CNFhEVEl3TURZd016QTNNalF5TjFvWERUSXdNRFl3TXpBM01qWXdOMW93VERFdE1Dc0dBMVVFQlJNa09HRmtPREExWW1VdE56Z3pOQzAwWWpZNExXRmxZek10Tm1NNE5UUTNObVpsWWpFeE1Sc3dHUVlEVlFRRERCSlRNREF3TURBd01Ea2dTbTlvYmlCRWIyVXdkakFRQmdjcWhrak9QUUlCQmdVcmdRUUFJZ05pQUFSb2FRWUVTQWpaUzBISnJwY1g1bWpRZlFzT0RaQ0s1WW1ybFdJejFyaXp3dzRBWEQ5bzRkdFJVZHBNOStGQWtlM2NreFlpWmM5SzJoYXZZdVRLLy9kM09KRzlFVHlyZ0VsVXRoV1c2R2FCZEZzV1pnRHMvenMzRkhyMFJvTThYLzB3Q2dZSUtvWkl6ajBFQXdNRGFRQXdaZ0l4QUw5WUl1M3hZMnY5YndiL2NoUWdPN0p6YnJxOGd0aTJOVmFoc0Q3Sk5kOUErOFJKcmR5QlRGZlpSMDA0elYzNk9RSXhBTzVZVHFYUWdydys1UFpXWjZSYWV3VkRKbjdEeXdYUUpiZXk3WnJ5MjdlZDdoeHNZYVQ5QlBBNElvNll5MmhHS1E9PSIsImlhdCI6MTcyNzE0NTAzMiwibm9uY2UiOiI5NjBiYzY5Yy1lY2M5LTRlOWEtODY4Yy04OTkyNTgwYjUyNTcifQ.ERUp7x8yEYZkdJEDU3z_0IZNYb0MdKknaoYYZXjQ8X4Dh29qtAP39W4N3dGJu_vvhYbtSjXX-JgXEINsMj8rtkkULZWsRXN8GTPalFTFc6l00_Kx09SLiha6Lc-UevED"}
```

{% endcode %}

#### **Request Fields**

| Name    | Type   | Description                                                                                                            |
| ------- | ------ | ---------------------------------------------------------------------------------------------------------------------- |
| `token` | String | The signed JWT containing standard and custom claims that describe a user or error event of a digital signing session. |

#### **Request Headers**

| Name            | Description                                                                                                                                              |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Authorization` | Bearer token authentication credentials. The token value is equal to the `client_notification_token` provided by the DSAP during session initialization. |

#### **Response Body**

There is no body in the response for this API call.

#### Request `token` structure

As mentioned previously, the request body `token` field is a JWT signed by Singpass, which can be verified by DSAPs.

#### **JWT Header (User Certificate)**

```
{
  "kid" : "ndi_dss_signer",
  "typ" : "JWT",
  "alg" : "ES256"
}
```

#### **JWT Claims (User Certificate)**

{% code overflow="wrap" %}

```
{
  "sign_ref" : "56163e89-a232-4502-a44f-e222512ba1ad",
  "request_type" : "user_cert",
  "exp" : 1727145152,
  "user_cert" : "MIIBrTCCATKgAwIBAgICA+cwCgYIKoZIzj0EAwMwNTELMAkGA1UEBhMCU0cxDDAKBgNVBAoMA05ESTEYMBYGA1UEAwwPdGVzdEBuZGkuZ292LnNnMB4XDTIwMDYwMzA3MjQyN1oXDTIwMDYwMzA3MjYwN1owTDEtMCsGA1UEBRMkOGFkODA1YmUtNzgzNC00YjY4LWFlYzMtNmM4NTQ3NmZlYjExMRswGQYDVQQDDBJTMDAwMDAwMDkgSm9obiBEb2UwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARoaQYESAjZS0HJrpcX5mjQfQsODZCK5YmrlWIz1rizww4AXD9o4dtRUdpM9+FAke3ckxYiZc9K2havYuTK//d3OJG9ETyrgElUthWW6GaBdFsWZgDs/zs3FHr0RoM8X/0wCgYIKoZIzj0EAwMDaQAwZgIxAL9YIu3xY2v9bwb/chQgO7Jzbrq8gti2NVahsD7JNd9A+8RJrdyBTFfZR004zV36OQIxAO5YTqXQgrw+5PZWZ6RaewVDJn7DywXQJbey7Zry27ed7hxsYaT9BPA4Io6Yy2hGKQ==",
  "iat" : 1727145032,
  "nonce" : "960bc69c-ecc9-4e9a-868c-8992580b5257"
}
```

{% endcode %}

#### **Description of Claims (User Certificate)**

<table><thead><tr><th width="251">Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><p><code>sign_ref</code></p><p></p></td><td>String</td><td>The primary identifier of a signing session used in all exchanges between Singpass and DSAP.</td></tr><tr><td><code>user_cert</code></td><td>String</td><td>The user's digital signing certificate in base64-encoded DER format.</td></tr><tr><td><code>iat</code></td><td>Number</td><td>A Unix timestamp in seconds indicating the date and time when the JWT was issued.</td></tr><tr><td><code>exp</code></td><td>Number</td><td>A Unix timestamp in seconds indicating the date and time when the JWT will expire.</td></tr><tr><td><code>nonce</code></td><td>String</td><td>A randomly generated string used to associate this user certificate notification to a document hash callback . This nonce must be included in the subsequent document hash callback request from DSAP.</td></tr><tr><td><code>request_type</code></td><td>String</td><td>Value to indicate the type of the request, value is <code>user_cert</code>.</td></tr></tbody></table>

#### **Description of Claims (Document Hash Signature)**

| Parameter            | Type   | Description                                                                                                                                                          |
| -------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sign_ref`           | String | The primary identifier of a signing session used in all exchanges between Singpass and DSAP.                                                                         |
| `doc_hash`           | String | The hash of the document to be signed by the user. Refer to [Document Hash Specifications](#document-hash-specifications) for more details.                          |
| `iat`                | Number | A Unix timestamp in seconds indicating the date and time when the JWT was issued.                                                                                    |
| `exp`                | Number | A Unix timestamp in seconds indicating the date and time when the JWT will expire.                                                                                   |
| `doc_hash_signature` | String | The signature of the document hash, signed by the user. Refer to [Document Hash Signature Specifications](#document-hash-signature-specifications) for more details. |
| `request_type`       | String | Value to indicate the type of the request, value is `signed_doc_hash`.                                                                                               |

#### **Description of Claims (Error)**

| Parameter           | Type   | Description                                                                                                                                 |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `sign_ref`          | String | The primary identifier of a signing session used in all exchanges between Singpass and DSAP.                                                |
| `error`             | String | Error code representing an error, e.g. `CLIENT_NOTIFICATION_FAILED`                                                                         |
| `iat`               | Number | A Unix timestamp in seconds indicating the date and time when the JWT was issued.                                                           |
| `exp`               | Number | A Unix timestamp in seconds indicating the date and time when the JWT will expire.                                                          |
| `error_description` | String | Returns human readable general information about the reason for the error.                                                                  |
| `request_type`      | String | Value to indicate the type of the request, value is `error`.                                                                                |
| `ndi_request_id`    | String | <p></p><p>A randomly generated ID that can be used to correlate requests between Singpass and DSAP. This ID is unique per user request.</p> |

### Retry Settings <a href="#retry-settings" id="retry-settings"></a>

Singpass will retry on any connection errors, client timeout and the following http response codes returned from the DSAP webhook endpoint:

* 502 - BAD\_GATEWAY
* 503 - SERVICE\_UNAVAILABLE
* 504 - GATEWAY\_TIMEOUT

Singpass is using the following configuration for retry strategy:

```
Total timeout across all attempts: 5s
Per try timeout: 2s
Min back off: 500ms
Max attempts: 3
```

### Error Response

The DSAP webhook API must follow RESTful conventions and communicate errors by returning a proper error response and with a corresponding HTTP status code, following the structure indicated below. All fields are mandatory.

#### Response Fields

| Parameter           | Type   | Description                                                                                                                                 |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                | String | A randomly generated ID that can be used to correlate requests between Singpass and DSAP. This ID is unique per user request.               |
| `error`             | String | Error code representing an error. Must be one of the values from the list of [v](#valid-error-codes)[alid error codes.](#valid-error-codes) |
| `error_description` | String | Returns human readable general information about the reason for the error.                                                                  |

#### **Sample Error Response**

```
{
  "id" : "1234",
  "error" : "SIGN_REF_NOT_FOUND",
  "error_description" : "The requested sign ref does not exist."
}
```

### Valid Error Codes <a href="#valid-error-codes" id="valid-error-codes"></a>

| Error                | Expected Http Status |                                                                                                                                                                                        |
| -------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SIGN_REF_NOT_FOUND` | 400                  | To be returned if the sign ref in the request is unknown or expired.                                                                                                                   |
| `INVALID_REQUEST`    | 400                  | A catch all indicating an invalid request parameter from DSS. Eg. invalid client notification token, invalid user cert, invalid doc hash signature, etc.                               |
| `SERVER_ERROR`       | 5XX                  | <p></p><p>A catch all for any other errors that occurred in the DSAP.</p><p></p><p>Singpass will retry on 502, 503 and 504. Refer to <a href="#retry-settings">Retry Settings.</a></p> |

## Other DSAP Requirements <a href="#other-dsap-requirements" id="other-dsap-requirements"></a>

DSAPs are expected to verify the validity of the user’s certificate status against National Certificate Authority (NCA).

{% hint style="info" %}
Singpass only validates the expiry, signature and signer of the certificate against the NCA Root and Intemediary CAs.
{% endhint %}

* DSAPs must use [Online Certificate Status Protocol (OCSP)](https://tools.ietf.org/html/rfc6960) to do the status verification against NCA. The OCSP response must be embedded in the signed document.
* DSAPs shall not send unknown OCSP requests (e.g. unknown OCSP certificate serial number or unknown CA Hash name).
* Cryptographic algorithms: DSAP shall ensure that the default cryptographic algorithms used in its systems and during the Digital Signing Process are as follows:
  * Digital Signature Algorithms: ECDSA (key size with 256 bits or more);
  * ECC Curves: NIST P-256, P-384, P-521;
  * Hash Algorithms: SHA-2 (256/384/512).
* The [Certificate Revocation List (CRL) file](https://www.nca.gov.sg/SNICA-G1.crl) size is **large** so we do not recommend using [CRL](https://tools.ietf.org/html/rfc5280#section-6.3) to verify the cert status to avoid large signed document file sizes.

## Document Hash Specifications

DSAPs are expected to use the received user cert and generate a document hash that can later be digitally signed (locally) to form a [PAdES-LTV/LTA](https://www.etsi.org/deliver/etsi_ts/102700_102799/10277804/01.01.02_60/ts_10277804v010102p.pdf) compliant signature.

The document hash should be **SHA256 hashed** and **hex-encoded** before sending to Singpass.

## Document Hash Signature Specifications <a href="#document-hash-signature-specifications" id="document-hash-signature-specifications"></a>

The document hash is locally signed by the user’s digital identity, verified by Singpass and then sent to the DSAP.

The signature follows the [**ASN.1/DER format**](https://www.cryptosys.net/pki/manpki/pki_ecchexformat.html) and will be **hex-encoded**.

{% hint style="info" %}
The document hash is not re-hashed during signing.
{% endhint %}

DSAPs are expected to then generate a [PAdES-LTV/LTA](https://www.etsi.org/deliver/etsi_ts/102700_102799/10277804/01.01.02_60/ts_10277804v010102p.pdf) compliant signature using this.

## UI/UX Specifications

DSAPs should follow the UX Guide provided in the developer package given during onboarding when displaying the QR code and/or challenge code to the users on their webpage.

### QR Code deeplinking specifications <a href="#qr-code-deeplinking-specifications" id="qr-code-deeplinking-specifications"></a>

To support mobile users, DSAPs are also required to render the QR code with the mobile app links (deep links) following the format below.

**For production:**

<table data-full-width="false"><thead><tr><th width="170">Platform</th><th width="580"></th></tr></thead><tbody><tr><td>Android</td><td><code>intent://app.singpass.gov.sg/docsign?sign_ref=&#x3C;sign-ref>#Intent;scheme=https;package=sg.ndi.sp;S.browser_fallback_url=https://app.singpass.gov.sg/docsign;end</code></td></tr><tr><td>iOS</td><td><code>https://app.singpass.gov.sg/docsign?sign_ref=&#x3C;sign-ref></code></td></tr></tbody></table>

**For staging:**

<table data-full-width="false"><thead><tr><th width="170">Platform</th><th width="580"></th></tr></thead><tbody><tr><td>Android</td><td><code>intent://stg-app.singpass.gov.sg/docsign?sign_ref=&#x3C;sign-ref>#Intent;scheme=https;package=sg.ndi.sp.dev;S.browser_fallback_url=https://stg-app.singpass.gov.sg/docsign;end</code></td></tr><tr><td>iOS</td><td><code>https://stg-app.singpass.gov.sg/docsign?sign_ref=&#x3C;sign-ref></code></td></tr></tbody></table>

{% hint style="danger" %}
The new staging domain `stg-app.singpass.gov.sg` will only take effect from 18 Mar 2025. In the meantime, RP must use `app.singpass.gov.sg` for both staging and production.
{% endhint %}

## Frequently Asked Questions (FAQs) <a href="#frequently-asked-questions-faqs" id="frequently-asked-questions-faqs"></a>

**Question: Since the** [<mark style="color:blue;">**user cert notification**</mark>](#dsap-notification-webhook-endpoint) **webhook is defined as asynchronous and non-blocking, are there any constraints on when a DSAP should invoke the** [<mark style="color:blue;">**/doc-signing-sessions/\<sign-ref>/hash endpoint**</mark>](#post-doc-signing-sessions-less-than-sign-ref-greater-than-hash)**?**

Answer: Yes. The agreed average response time between the start of the [<mark style="color:blue;">user cert notification</mark>](#dsap-notification-webhook-endpoint) webhook and the invocation of the hash endpoint must be 8 seconds. This 8-second interval correlates to the period that an end-user has to wait after scanning the QR (till he/she receives a "Signing Challenge"). While waiting for the "Signing Challenge" (which is provided by a DSAP), the end-user will encounter a loading/ spinner screen.<br>

**Question: Are DSAPs allowed to cache an end-user’s public X.509 Certificate beyond the TTL of a signing-session (`sign_ref`) to cater for better user experience?**

Answer: Although it is technically possible for DSAPs to cache an end-user’s public certificate beyond the TTL of a signing-session (`sign_ref`), Singpass does not condone such a practice as it infringes Data Protection Policies. i.e. Lack of end-user’s consent in providing his/her personal information to a third-party site/application (<https://www.singpass.gov.sg/singpass/common/privacystatement>).

The [<mark style="color:blue;">/doc-signing-sessions/\<sign-ref>/hash endpoint</mark>](#post-doc-signing-sessions-less-than-sign-ref-greater-than-hash) will proceed to reject any requests of such nature if an invalid signing-reference ID (`sign_ref`) is provided. Repeated attempts to guess a signing-reference ID (sign\_ref) will be logged and recognised by Singpass as a malicious security event.


# User Journey (V1)

{% stepper %}
{% step %}

### On the organisation's webpage, click / tap on the 'Sign' button

<figure><img src="/files/6oz5vyg3Rl0NCpseXf16" alt=""><figcaption><p>Sample organisation's webpage</p></figcaption></figure>
{% endstep %}

{% step %}

### Scan the QR code with your Singpass App

<figure><img src="/files/HoFK9yL3oSOycURCzULo" alt=""><figcaption><p>Screen showing QR (Desktop &#x26; Mobile views)</p></figcaption></figure>
{% endstep %}

{% step %}

### Approve or Reject on your Singpass App:&#x20;

Check that the reference codes on your desktop and mobile screens are the same.

Tap *Approve* to sign the document, or *Reject* to cancel the signing.

<figure><img src="/files/4tRiNPXR4G39jk9No6CB" alt=""><figcaption><p>Screens shown after QR is scanned (Desktop &#x26; Mobile views)</p></figcaption></figure>
{% endstep %}

{% step %}

### Signing complete!&#x20;

Return to the organisation's webpage to complete the rest of the process (if any).

<figure><img src="/files/YFq3xncwRVfltMhc90xg" alt=""><figcaption><p>Successful signing (Desktop &#x26; Mobile views)</p></figcaption></figure>
{% endstep %}
{% endstepper %}

## You may have some questions...

<details>

<summary>What is the reference code for during the signing?</summary>

The purpose of the reference code is to allow users to verify that they are signing the correct document, when the code on their Singpass App matches that which is shown on the organisation's webpage.

</details>

<details>

<summary>How can I download my document after signing?</summary>

You may return to the organisation who has sent you this document for the signed copy.

</details>


# Transaction Signing

## Transaction Signing API Specifications

This document outlines the technical specifications for the **Singpass Transaction Signing API**, designed to enable secure and seamless transaction signing for applications integrating with Singpass. It details the API endpoints, authentication mechanisms, request/response structures, and security protocols to ensure integrity, confidentiality, and user trust.

This specification serves as a guide for developers and system integrators to facilitate smooth implementation and interoperability.

## Flow Diagram

Refer to this diagram for an overview of the transaction signing flow and the interactions between RP, Singpass and other dependencies.

<figure><img src="/files/vEIdH6PDyK39jv0JpvV1" alt=""><figcaption></figcaption></figure>

RPs are expected to implement the following steps referenced from the flow diagram above:

<table><thead><tr><th width="105">Step(s)</th><th width="140">Component</th><th>Summary</th><th width="247">Specifications</th></tr></thead><tbody><tr><td>1</td><td>Frontend</td><td>Retrieve Singpass transaction signing javascript (Singpass JS)</td><td><a href="/pages/O7Z7oyKMnxlOvWGkBNGi">Embedding Singpass JS</a></td></tr><tr><td>2</td><td>Frontend</td><td>Initialise a transaction signing session via Singpass JS</td><td><a href="/pages/TbzsUsZlGu1vdK5FB2rm">Init Transaction Signing</a></td></tr><tr><td>6</td><td>Backend</td><td>After receiving the sign code, invoke Singpass API to exchange it for the user-signed transaction hash</td><td><a href="/pages/d5LVIk1uuB0sFDnsCYTh">Exchange Transaction Signature</a></td></tr></tbody></table>


# Embedding Singpass JS

We provide a **core JavaScript library** (Singpass JS) to enable seamless integration with the Singpass Digital Signing Service (DSS). It facilitates transaction signing workflows for Relying Party (RP) applications, covering steps **2.1 to 5.3** in the flow diagram.

**Key Functionalities**

* **Initialisation**: Singpass JS is initialised in the RP’s frontend to set up the transaction signing process.
* **Session Management**: Starts the signing session with transaction details (Step 2.1).
* **QR Code Rendering**: Generates and displays the QR code for the user to scan via SingPass Mobile App (Step 2.2).
* **Status Handling**: Tracks user actions, returning the CHALLENGED status when the QR code is scanned (Step 3.2).
* **Success Confirmation**: Confirms the signed challenge response and returns the success status with the sign code (Step 5.3).

RPs shall embed this Javascript in your front end application.&#x20;

## **URLs**

<table><thead><tr><th width="178">Environment</th><th width="576">URL</th></tr></thead><tbody><tr><td>Staging</td><td><p><a href="https://static.staging.sign.singpass.gov.sg/static/ndi_txn_sign.js">https://static.staging.sign.singpass.gov.sg/static/ndi_txn_sign.js</a><br>OR</p><p><a href="https://stg-id.singpass.gov.sg/static/ndi_txn_sign.js">https://stg-id.singpass.gov.sg/static/ndi_txn_sign.js</a> <mark style="color:red;">(Deprecated)</mark></p></td></tr><tr><td>Production</td><td><a href="https://static.app.sign.singpass.gov.sg/static/ndi_txn_sign.js">https://static.app.sign.singpass.gov.sg/static/ndi_txn_sign.js</a><br>OR<br><a href="https://id.singpass.gov.sg/static/ndi_txn_sign.js">https://id.singpass.gov.sg/static/ndi_txn_sign.js</a> <mark style="color:red;">(Deprecated)</mark></td></tr></tbody></table>

{% hint style="warning" %}
You **must provide your frontend origin** during onboarding to allow communication with us.
{% endhint %}

## Content Security Policy (CSP) Requirements <a href="#content_security_policy_csp_requirements" id="content_security_policy_csp_requirements"></a>

This section is important if the RP’s website (where Singpass JS is embedded) uses a Content Security Policy (CSP) header. For Singpass JS to fully function, please ensure that the following CSP attributes are set/added:

* `script-src` [`https://*.singpass.gov.sg`](https://%2A.singpass.gov.sg/)`;`
* `connect-src` [`https://*.singpass.gov.sg`](https://%2A.singpass.gov.sg/)`;`
* `style-src` [`https://*.singpass.gov.sg`](https://%2A.singpass.gov.sg/)`;`
* `img-src` [`https://*.singpass.gov.sg`](https://%2A.singpass.gov.sg/) `data:;`
* `font-src` [`https://fonts.gstatic.com`](https://fonts.gstatic.com/)`;`

Sample CSP header:&#x20;

{% code overflow="wrap" %}

```
content-security-policy: default-src 'self'; script-src 'self' https://*.singpass.gov.sg; connect-src'self' https://*.singpass.gov.sg; style-src 'self' https://*.singpass.gov.sg; img-src 'self' https://*.singpass.gov.sg data:; font-src https://fonts.gstatic.com;
```

{% endcode %}

{% hint style="info" %}
It is not necessary to set/add any CSP attributes if the **RP’s website does not use a CSP header** or has **less strict policies** for all the attributes mentioned above.
{% endhint %}

## UI dimensions <a href="#ui_dimensions" id="ui_dimensions"></a>

The HTML elements will be rendered in a rectangular area with a fixed size with these dimensions, depending on the mode specified via the `additionalOptions` parameter:

| Mode                                 | Dimensions (width by height) |
| ------------------------------------ | ---------------------------- |
| Basic QR with no additional elements | 298px by 367px               |
| QR with download link                | 298px by 416px               |

{% hint style="danger" %}
The RP must allot a minimum space according to these dimensions for the transaction signing UI element to be rendered properly.
{% endhint %}

## UI Reference <a href="#ui_reference" id="ui_reference"></a>

For RP’s reference, this section displays the screens that Singpass JS renders during the transaction signing process. RPs can refer to these screens to get a better idea of how integrating with Singpass JS will look like on their website.

The green border indicates the [`minimum expected size of the DOMElementID`](#ui_dimensions) provided by the RP.

<table><thead><tr><th width="355">Description</th><th>Screen</th></tr></thead><tbody><tr><td>Session initialisation success</td><td><img src="/files/yoGgLDoL9WXBLXZXt7A9" alt=""></td></tr><tr><td>User scanned the QR code using SPM</td><td><img src="/files/7zDxM2oH2Uzh9v6k96lO" alt="" data-size="original"></td></tr><tr><td>Session expired (Before QR code was scanned)</td><td><img src="/files/Ex1EBKZqQgNLjSIoHCyH" alt="" data-size="original"></td></tr><tr><td>Session expired (After QR code was scanned)</td><td><img src="/files/tMlJ8QuXPpwyuN5q3amd" alt="" data-size="original"></td></tr><tr><td>Retryable error occurred <br><br>Users are only allowed to retry up to 1 time. This is to prevent excessive retries on the Singpass server because automated retries are already in place for such API calls.</td><td><img src="/files/8qj8EbaOMKv0H4Bpw1SH" alt="" data-size="original"></td></tr><tr><td><p>Unretryable error occurred<br></p><p>Examples of possible scenarios:</p><ul><li>Error occurred when invoking transactionParamsSupplier </li><li>Pre-flight checks have failed (ie. RP invoked .initTxnSigning() with invalid parameters) </li><li>Retries exhausted Malformed response from Singpass server, which cannot be recovered by retrying </li><li>Client side errors returned from Singpass server </li><li>In the situation where a corresponding erroneous HTTP status code is received, it will be displayed in brackets.</li></ul></td><td><img src="/files/DguYYb2xmBkTUeYtNwkl" alt="" data-size="original"></td></tr><tr><td>Service unavailable. This screen will be displayed if transaction signing service is toggled off on Singpass.</td><td><img src="/files/7OCz6iIEf6QH7IxpzSjM" alt="" data-size="original"></td></tr><tr><td><p>Browser unsupported.<br><br>This screen will be displayed if the end-user’s browser/user-agent is not supported by Singpass JS.</p><p><br>Singpass JS supports recent versions of major browsers, including but not limited to Chrome, Firefox, Edge, Safari, SamsungBrowser and IE 11.</p></td><td><img src="/files/wSWpibuD520IhMbcdluT" alt="" data-size="original"></td></tr><tr><td>Transaction Cancelled. This screen will be displayed if RP invoked .cancelTxnSigningSession()</td><td><img src="/files/YGmxvHpurGiEFbSOWNdj" alt="" data-size="original"></td></tr></tbody></table>


# Init Transaction Signing

RPs should invoke this function to start a transaction signing session with Singpass. This is the only function that needs to be invoked by the RP for every transaction signing session.

{% code overflow="wrap" %}

```js
initTxnSigning(DOMElementID, clientParams, transactionParamsSupplier, onError, additionalOptions)
```

{% endcode %}

Behind the scenes, Singpass JS will coordinate the required backend API calls to create a new session and renders the UI screens accordingly as users progress through the signing process.

Firstly, Singpass JS will perform pre-flight checks to validate the function method parameters and ensure that the end-user’s browser/user-agent is supported by Singpass JS.

Next, Singpass JS will call the `/txn-signing-sessions` endpoint that is exposed on the Singpass backend to initiate the transaction signing flow.

Upon successful invocation of this function and the session initiation by Singpass, a QR code will be generated and displayed for the user to scan with the Singpass app (SPM) and proceed with the transaction.

At the same time, Singpass JS will call the `/status` endpoint, which is a long-polling connection to the Singpass backend. This endpoint reacts to user actions in the SPM app, i.e. "user scanned" and "user authorised".

{% hint style="info" %}
Wrap the invocation of `initTxnSigning` in a **try-catch block**, in case of any unforeseen errors.\
RPs can choose to default to an alternate authorisation mechanism in such error cases.
{% endhint %}

## **Following up after redirection**

Once the user has successfully scanned and authorised the transaction using SPM, Singpass JS will redirect the end-user’s browser/user-agent to the RP’s registered `redirect_uri` along with the `code` and `state` parameters.

Once redirected, the RP’s backend **should invoke the /txn-signatures** endpoint to retrieve the user’s transaction hash signature and complete the transaction signing flow.

Sample redirect location upon successful transaction signing

{% code overflow="wrap" %}

```
https://partner.txnsign.gov.sg/redirect?code=v2-BYE9fSQpDXCM0tlcI0yHwkwiff3bNy09n%2F703h4rmZI%3D&state=ewoidXNlciI6ImEiLAoic2Vzc2lvbiI6IjEyMyIsCiJyYW5kb20iOiJ4eXoiCn0=
```

{% endcode %}

## **Method Parameters**

**`DOMElementID`** `string`

Represents [the unique ID of the Document Object Model (DOM) Element](https://developer.mozilla.org/en-US/docs/Web/API/Element/id) where Singpass JS will render the transaction signing UI elements (QR code, text, images) on.

{% hint style="info" %}
The DOM element should be **empty**; Singpass JS will clear it before rendering any UI assets.
{% endhint %}

{% hint style="info" %}
See [UI dimensions](/for-relying-parties/api-documentation/transaction-signing/embedding-singpass-js#ui_dimensions) for the minimum size required of this element.
{% endhint %}

**`clientParams`** `object: { clientId, redirectUri }`

* **`clientId`** `string`

  The client identifier that was assigned by Singpass during RP registration.
* **`redirectUri`** `string`

  The URL that Singpass JS will eventually redirect the user to after the user completes the signing process using SPM. This value will be validated against the RP’s `redirect_uri` that was specified during RP registration.

***

**`transactionParamsSupplier`** `function: () ⇒ object: {state, nonce, txnInfo}`

This function will be invoked by Singpass to retrieve the necessary session parameters from the RP’s backend everytime a new sign session is to be initialised.

{% hint style="danger" %}
Singpass JS may invoke this function **multiple times** (eg. when the user clicks on the Singpass-rendered retry button to re-initialise another session due to an unforeseen error or session expiry). RPs should ensure that a **new** set of values (`txnInfo`, `state`, `nonce`) is returned on each invocation as these values are expected to be session-based.
{% endhint %}

{% hint style="danger" %}
RPs should ensure that they perform **automated retries on any network calls** made during this function invocation because Singpass **JS will not retry** on any failures arising from this function invocation.
{% endhint %}

This function can either be an `async` function **OR** a standard function that returns a `Promise` object that resolves to an object containing the following structure:

* **`state`** `string`

  A session-based, unique, and non-guessable value that RP’s backend should generate per transaction signing session.<br>

  As part of threat modelling, Singpass JS requests for the state parameter to mitigate replay attacks against the RP’s redirect uri. This parameter serves the same purpose as the [OAuth 2.0’s `state` parameter](https://tools.ietf.org/html/rfc6749#section-4.1.1).<br>

  It should have a **maximum of 255 characters** and **must match `regexp` pattern of `[A-Za-z0-9/_\-=.]`**.
* **`nonce`** `string`

  A session-based, unique, and non-guessable value that the RP’s backend should generate per transaction signing session.

  As part of threat modelling, Singpass JS requests for the nonce parameter to mitigate MITM attacks against the `/txn-signatures` endpoint. This parameter serves the same purpose as the [OIDC 1.0’s `nonce` parameter](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).

  It should have a **maximum of 255 characters**. We recommend that you use a hex-encoded random number such as `java.security.SecureRandom` or `UUIDv4`.
* **`txnInfo`** `string`

  A signed JWT that contains the transaction information that the user would sign on using SPM. The structure of the JWT and how it will be validated by Singpass is detailed in the Structure of [`txnInfo` JWT section.](#structure-of-txninfo-jwt)

**Sample `transactionParamsSupplier` Implementation**

{% code overflow="wrap" %}

```javascript
const initiateSessionOnBackend = async () => {
  // Replace the below with an `await`ed call to initiate a session on your backend
  // which will generate state+nonce+txnInfo values.
  // Keep in mind to put in place automated retries, because Singpass will not retry on
  // any errors coming from this function invocation.

  //e.g
  return { state: "dummySessionState", nonce: "dummySessionNonce", txnInfo: "dummyTxnInfo" };
}

const transactionParamsSupplier = async () => {
  const backendSession = await initiateSessionOnBackend();
  return {
    state: backendSession.state,
    nonce: backendSession.nonce,
    txnInfo: backendSession.txnInfo
  };
};
```

{% endcode %}

***

**`onError`** `function: (errorId, message) ⇒ {}`

A callback function that will be invoked by Singpass JS upon certain error events.

On error events, you can expect the appropriate error screens to be rendered on the provided `DOMElementID` parameter.

The function must accept the following parameters:

* **`errorId`** `string`

  An Singpass-generated unique identifier for the error/request. If present, it can be used for debugging the error in Singpass.
* **`message`** `string`

  A short description of the error that occurred. Possible values:

  | Error Code                 | Description                                                                  |
  | -------------------------- | ---------------------------------------------------------------------------- |
  | `invalid_request`          | General client side errors, eg. an API call returned a 400 bad request error |
  | `status_subscription_fail` | The status subscription API call that Singpass JS makes has failed           |
  | `sign_session_expired`     | The current signing session has expired                                      |
  | `service_unavailable`      | Transaction signing service is toggled off at Singpass                       |
  | `txn_signing_fail`         | Catch all for unhandled/unknown errors                                       |
  | `txn_signing_cancelled`    | The current signing session is cancelled                                     |

**Sample `onError` implementation**

{% code overflow="wrap" %}

```javascript
const onError = (errorId, message) => {
  // Can be any custom implementation to handle the error

  // eg.
  console.log(`onError. errorId:${errorId} message:${message}`);
};
```

{% endcode %}

***

**`additionalOptions`** `object`

Optional configuration to customise additional behaviour:

* **`renderDownloadLink`** `string` (Optional)

  Whether to render a link to download the Singpass app. Defaults to false if not specified. The link appears below the QR area. Clicking the link opens the Singpass app webpage in a new tab/window.

|   | These additional UI elements will change the height of the rendered UI. See UI dimensions for the expected heights of the UI for the different modes. |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------- |

* **`appLaunchUrl`** `boolean` (Optional)

  Intended for mobile apps which embed this javascript and render a QR code. This adds the possibility for the user to be redirected back to the provided App Link after they successfully authorize themselves on the Singpass Mobile App. The value passed here should be the App Link registered with Apple’s App Store and/or Google’s Play Store.

  * Please note that opting into this would mean the relying party is required to be able to initialize auth, i.e generate the QR code, both with and without the appLaunchUrl. This is to ensure the Singpass App only triggers App Links when desired.

| Authenticating From                       | appLaunchUrl Param |
| ----------------------------------------- | ------------------ |
| Relying Party website                     | Do not include     |
| Relying Party website on a mobile browser | Do not include     |
| Relying Party mobile app                  | Can Include        |

* Sample `additionalOptions` implementation:

```
{
  renderDownloadLink: boolean,
  appLaunchUrl: string
}
```

## **Returns**

`initTxnSigning` returns a `string` that may be one of the following values:

* `SUCCESSFUL`

  Pre-flight checks were successful, and you can expect a QR code to be rendered. In case of unforeseen errors (eg. API errors during initialisation), a generic error screen will be rendered with the corresponding erroneous HTTP code, and the `onError` callback will be invoked.
* `FAILED`

  Pre-flight checks failed due to missing or malformed input parameters. If a valid `DOMElementID` is provided, a generic error screen will be rendered suggesting the user to opt for alternative authorization mechanisms. The `onError` callback will **NOT** be invoked.
* `NO_OP`

  Input method parameters are valid but pre-flight checks have determined that Singpass JS will not run on the end-user’s browser/user-agent. If a valid `DOMElementID` is provided, an error screen will be rendered indicating the unsupported browser/user-agent. The onError callback will **NOT** be invoked.

**Sample HTML invoking `.initTxnSigning()`**

{% code overflow="wrap" %}

```html
<html>
<script src="https://stg-id.singpass.gov.sg/static/ndi_txn_sign.js"></script>
<script>
  async function init() {
    const transactionParamsSupplier = async () => {
      const backendSession = await initiateSessionOnBackend();
      return {
        state: backendSession.state,
        nonce: backendSession.nonce,
        txnInfo: backendSession.txnInfo
      };
    };

    const onError = (errorId, message) => {
      console.log(`onError. errorId:${errorId} message:${message}`);
    };

    const initTxnSigningResponse = window.NDI.initTxnSigning(
      'singpass-qr',
      {
        clientId: 'T5sM5a53Yaw3URyDEv2y9129CbElCN2F',
        redirectUri: 'https://partner.gov.sg/redirect'
      },
      transactionParamsSupplier,
      onError,
      {
        renderDownloadLink: true,
        appLaunchUrl:'https://partner.gov.sg' // Replace with your iOS/Android App Link
      }
    );

    console.log('initTxnSigning: ', initTxnSigningResponse);
}
</script>
<body onload="init()">
<div id="singpass-qr"></div>
</body>
</html>
```

{% endcode %}

## **Structure of `txnInfo` JWT**

The `txnInfo` returned by the RP-provided `transactionParamsSupplier` function described above should wrap the required transaction information.

This is a standard signed JWT in [compact serialization format](https://tools.ietf.org/html/rfc7515#section-3.1).

Sample Signed JWT

{% code overflow="wrap" %}

```
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im1vY2stY2xpZW50LWVzMjU2LTEifQ.eyJleHAiOjE2MTQ2NTk1MDAsImlzcyI6IlQ1c001YTUzWWF3M1VSeURFdjJ5OTEyOUNiRWxDTjJGIiwiYXVkIjoiaHR0cHM6Ly9zdGctaWQuc2luZ3Bhc3MuZ292LnNnL3R4bi1zaWduaW5nLXNlc3Npb25zIiwidHhuX2lkIjoidHJhbnNhY3Rpb25faWRlbnRpZmllciIsInR4bl9pbnN0cnVjdGlvbnMiOiJ1cGRhdGUgYmFuayBhY2NvdW50IGZyb20geHh4IHRvIHl5eSIsInR4bl9oYXNoIjoiYTRlNTlhNDBmODM2MjA0NDVlODk1ODcxZTU5Njg5NTcwZWQ2YjY0OTdkMzE1OTYxYTlkNDNmNDRkNDE4NzU4YiIsImlhdCI6MTYxNDY1OTM5Nywic3ViIjoiMWMwY2VlMzgtM2E4Zi00ZjhhLTgzYmMtN2EwZTRjNTlkNmE5In0.JTR09Ou6xBqo1nH19rTzrXyz4aW01_1JeMd6dcEFBJOAFAaVol3erfHvH6hDzA6m5RgfBfO-El6zQODY7o9uig
```

{% endcode %}

| Parameters                                                                                                                                            | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Header** - standard JWS Headers. Refer to <https://tools.ietf.org/html/rfc7515#section-4>.                                                          | <p><code>type</code> - Must be "JWT"<br><br><code>alg</code> - Must be an ECDSA-based algorithm. See complete list <a href="https://tools.ietf.org/html/rfc7518#section-3.4">here</a>.<br><br><code>kid</code> - The key id indicating the key used to sign the JWT</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| **Payload** - standard registered claims alongside custom claims highlighted in **bold**. Refer to <https://tools.ietf.org/html/rfc7519#section-4.1>. | <p><code>iat</code> - A Unix timestamp in seconds indicating the date and time when the JWT was issued. Must be at most 2 minutes from the current time.<br><br><code>exp</code> - A Unix timestamp in seconds indicating the date and time when the JWT will expire. Must not be greater than two minutes from <code>iat</code>.<br><br><code>sub</code> - (optional) The user’s Singpass ID (in UUIDv4 format) if available.<br><br><strong><code>txn\_id</code></strong> - The unique ID of the transaction generated by the RP.<br><br><strong><code>txn\_instructions</code></strong> - Human-readable instructions that the user can read and consent on. Any sensitive information must be masked.<br><br><strong><code>txn\_hash</code></strong> - Hashed value of the combined <code>txn\_id</code> and <code>txn\_instructions</code> claims in the format: <code>\<txn\_id>:\<txn\_instructions></code>. Must be <strong>SHA256 hashed and hex-encoded</strong>.</p> |
| **Signature**                                                                                                                                         | Standard [JWT signature](https://tools.ietf.org/html/rfc7515#section-3.3)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |

**JWT Validation**

Singpass backend will validate the signature of the signed JWT using the RP’s JWKS endpoint that was provided during registration.

{% hint style="info" %}
Singpass caches the keys found in the RP’s JWKS endpoint for **one hour**.\
\
When the RP’s signing keypair needs to be rotated, new keypairs must use a **different key id (`kid`)** value to ensure that consumers (e.g. Singpass) of the keys do not use a stale version of the public key in their caches.
{% endhint %}

## `.cancelTxnSigningSession()` <a href="#cancel_txn_signing_session" id="cancel_txn_signing_session"></a>

This function does **not** require any method parameters, and it also does **not** return anything.

Upon invocation, all ongoing API calls (if any) made by `.initTxnSession()` will be aborted. A [screen](https://stg-id.singpass.gov.sg/docs/txn-signing/js#ui_cancelled) indicating that the transaction has been cancelled is rendered, and there will be no UI changes thereafter.

If a valid [`onError`](https://stg-id.singpass.gov.sg/docs/txn-signing/js#on_error_callback) callback is provided, then it will be invoked with the following parameters:\
`onError(errorId: '', message: 'txn_signing_cancelled')`

{% hint style="info" %}
We recommend invoking this function when a **transaction has been cancelled by the user** (eg. when the transaction modal is closed) to ensure that the transaction is aborted properly.\
It is safe to invoke this method regardless of the session’s state (ie. whether the session is active or in an erroneous state).
{% endhint %}


# Exchange Transaction Signature

This section describes the process where the Relying Party (RP) backend exchanges the **sign code** received from the Singpass for the **user's digital signature**. This step finalises the transaction signing workflow, ensuring the transaction is securely signed and validated.

## Exchange signature API

### URLs

<table><thead><tr><th width="165">Environment</th><th width="176">Access Mechanism</th><th>URL</th></tr></thead><tbody><tr><td>Staging</td><td>mTLS</td><td>https://stg-id.singpass.gov.sg:8443/txn-signatures<br><mark style="color:red;">(Deprecated)</mark></td></tr><tr><td>Staging</td><td>TLS</td><td><a href="https://staging.sign.singpass.gov.sg/api/v1/txn-signatures
">https://staging.sign.singpass.gov.sg/api/v1/txn-signatures</a></td></tr><tr><td>Production</td><td>mTLS</td><td>https://id.singpass.gov.sg:8443/txn-signatures<br><mark style="color:red;">(Deprecated)</mark></td></tr><tr><td>Production</td><td>TLS</td><td><a href="https://app.sign.singpass.gov.sg/api/v1/txn-signatures
">https://app.sign.singpass.gov.sg/api/v1/txn-signatures</a></td></tr></tbody></table>

### Client Authentication

RPs are required to authenticate themselves by specifying a signed JWT via the `Authorization` header.

&#x20;**JWT header**

The following standard JWS headers need to be included. Refer to [Jose Header RFC](https://tools.ietf.org/html/rfc7515#section-4) for more details about what each header presents.

**JWT claims**

The following claims need to be present.

<table><thead><tr><th width="146">Path</th><th width="145">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>sub</code></td><td><code>String</code></td><td>The Client ID generated during transaction signing replying party registration.</td></tr><tr><td><code>sign_code</code></td><td><code>String</code></td><td>The Singpass generated code that was returned to the RP via the RP’s redirect URI (specified in the <code>sign_code</code> query parameter).</td></tr><tr><td><code>iat</code></td><td><code>Number</code></td><td>The time at which the JWT was issued.</td></tr></tbody></table>

Claims example

```json
{
  "sign_code": "aRpFkeEbdOSBaeY7aGIk0rkvb90yr9nezLM4cMLOuVc=",
  "iat": 1598238992,
  "sub": "4iHWBHNNrYcXKKMOvk3bIE3CYAQnQ84V"
}
```

### Request

**Request headers**

Should include the Authorization token descibed in [Client Authentication](#client-authentication).

**Request body**

Request body shall be a json object with the sign\_code parameter.

```json
{"sign_code":"wOU1mqb7XICjhdTUgl73"}
```

**Example request**

{% code overflow="wrap" %}

```http
POST https://app.sign.singpass.gov.sg/api/v1/txn-signatures HTTP/1.1
Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJhdWQiOiJodHRwczovL2lkLnNpbmdwYXNzLmdvdi5zZy90eG4tc2lnbmF0dXJlcyIsInN1YiI6Im5kaV9yZWdpc3RlcmVkX2NsaWVudF9pZCIsInNpZ25fY29kZSI6IndPVTFtcWI3WElDamhkVFVnbDczIiwiaXNzIjoibmRpX3JlZ2lzdGVyZWRfY2xpZW50X2lkIiwiZXhwIjoxNzM0MzM4Nzc4LCJpYXQiOjE3MzQzMzg3MTh9.1s2vk5I7o1cPYu2Djbf5qVLO4NmruwgI_L2rVqU7Q3q_b_TclSA4ue637OsiCoLTxMC9g5zDDIEjlz3hykBAq4o9jPOhMu4HsBFsiaz9IYmP2OkNnx3kMxBUxeqKZUxz
Content-Type: application/json

{"sign_code":"wOU1mqb7XICjhdTUgl73"}
```

{% endcode %}

### Response

The response is a JWT signed by Singpass wrapped in a json response.

Example:

{% code overflow="wrap" %}

```json
{"token":"eyJraWQiOiJuZGlfZHNzX3NpZ25lciIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJhdWQiOiJodHRwczovL2F1ZGllbmNlLm9yaWdpbiIsInN1YiI6IjZlNjc2NjU0LTViYjQtNDIyNy1iYTFhLWYzMDU5ZTIyNWYyMCIsInR4bl9oYXNoX3NpZ25hdHVyZSI6IjZhYzc2MzdkYTkyYzc2Mzg1Zjk1YTkyYzc2MTdlNTkxYThmNmRmOGY3NGYzN2VmOGRiN2UyNWU2NDhlMWRiN2UiLCJ0eG5faGFzaCI6IjZhYzc2MzdkYTkyYzc2Mzg1Zjk1YTkyYzc2MTdlNTkxYThmNmRmOGY3NGYzN2VmOGRiN2UyNWU2NDhlMWRiN2UiLCJpc3MiOiJodHRwczovL2NsaWVudC5yZWRpcmVjdC5vcmlnaW4iLCJleHAiOjE3MzQzMzg4MzUsImlhdCI6MTczNDMzODcxNSwibm9uY2UiOiJjZWJhUTRYaVRoczBvUDA4YXJYY0JRcGordnkraTBCUHRhNFllVFhrTEVFPSJ9.vVZxJrZoUoloLzjvCwB8-Cg0yVxlBRQmGDcGrxNy_Owr8n_NV_sy7oQI0FMzLd4XcGBNfhIdLKBuycQIOZCVWPUU6qrhZFVORfjOCyJc10BvtB3_inOP8drRkHb4Q1o5"}
```

{% endcode %}

#### Token JWT structure

**Headers**

The following standard JWS headers will be included. Refer to [Jose Header RFC](https://tools.ietf.org/html/rfc7515#section-4) for more details about what each header presents.

Header Example

```json
{
  "kid" : "abcd",
  "typ" : "JWT",
  "alg" : "ES256"
}
```

**Claims**

The following claims will be returned.

<table><thead><tr><th width="240">Path</th><th width="123">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>iss</code><br><mark style="color:red;">(Deprecated)</mark></td><td><code>String</code></td><td>The principal that issued the JWT. <a href="https://tools.ietf.org/html/rfc7519#section-4.1.1">https://tools.ietf.org/html/rfc7519#section-4.1.1</a><br></td></tr><tr><td><code>sub</code></td><td><code>String</code></td><td>The user’s Singpass user id signed the transaction. <a href="https://tools.ietf.org/html/rfc7519#section-4.1.2">https://tools.ietf.org/html/rfc7519#section-4.1.2</a></td></tr><tr><td><code>iat</code></td><td><code>Number</code></td><td>The time at which the JWT was issued. <a href="https://tools.ietf.org/html/rfc7519#section-4.1.6">https://tools.ietf.org/html/rfc7519#section-4.1.6</a></td></tr><tr><td><code>exp</code></td><td><code>Number</code></td><td>The expiration time on or after which the JWT MUST NOT be accepted by Singpass for processing. Additionally, Singpass will not accept tokens with an <code>exp</code> longer than 2 minutes since <code>iat</code>. <a href="https://tools.ietf.org/html/rfc7519#section-4.1.4">https://tools.ietf.org/html/rfc7519#section-4.1.4</a></td></tr><tr><td><code>nonce</code></td><td><code>String</code></td><td>The value passed by the signing partner during the init session API call</td></tr><tr><td><code>txn_hash_signature</code></td><td><code>String</code></td><td>The hex-encoded user signature over the <code>txn_hash</code></td></tr><tr><td><code>txn_hash</code></td><td><code>String</code></td><td>The SHA-256 hash of the <code>txn_id</code> and <code>txn_instructions</code> field calculated in this manner: <code>sha256(&#x3C;txn_id>:&#x3C;txn_instructions></code>). Encoded in hexadecimal.</td></tr></tbody></table>

Claims Example

{% code overflow="wrap" %}

```json
{
  "sub" : "6e676654-5bb4-4227-ba1a-f3059e225f20",
  "txn_hash_signature" : "6ac7637da92c76385f95a92c7617e591a8f6df8f74f37ef8db7e25e648e1db7e",
  "txn_hash" : "6ac7637da92c76385f95a92c7617e591a8f6df8f74f37ef8db7e25e648e1db7e",
  "exp" : 1734338835,
  "iat" : 1734338715,
  "nonce" : "cebaQ4XiThs0oP08arXcBQpj+vy+i0BPta4YeTXkLEE="
}
```

{% endcode %}

**Signature**

Standard JWT signature, RP **MUST** validate the signature with [Singpass public keys. ](#fetch-our-public-keys)

### Error handling

Singpass APIs are RESTful in design and communicate classes of errors based on the **Http Status** code. The status code should be used to determine if the error is caused by consumer or provider. Consumers should log the HTTP status code along with the `id` and/or `trace_id` of the error.

<table><thead><tr><th width="165">Http Status</th><th>Description</th></tr></thead><tbody><tr><td>4xx</td><td><p>Errors caused by API consumer. You can expect codes such as 400, 401, 403, 404 etc if incorrect requests are made to APIs.</p><p>Example: <strong>400: Invalid/missing request arguments</strong></p></td></tr><tr><td>5xx</td><td><p>Errors caused by API provider or its dependencies. You can expect codes such as 500, 502, 503 etc if there is an issue on Singpass or its dependencies.</p><p>Example: <strong>500: Internal Server Error due to some kind of programming error.</strong></p></td></tr></tbody></table>

**Error response json**

<table><thead><tr><th width="230">Path</th><th width="101">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>id</code></td><td><code>String</code></td><td>The unique identifier for this error/request. Please log this identifier for support and debugging purposes.</td></tr><tr><td><code>trace_id</code></td><td><code>String</code></td><td>(Optional) An auxiliary id for request correlation across services. Please also log this identifier for operational support and debugging purposes.</td></tr><tr><td><code>error</code></td><td><code>String</code></td><td>Error code representing broad class of error. See <a href="https://stg-id.singpass.gov.sg/docs/txn-signing/api#api_error_codes">Error Codes</a> for the list of possible error codes that can be returned and what they represent.</td></tr><tr><td><code>error_description</code></td><td><code>String</code></td><td>Returns human readable general information about the reason for the error. Note that due to security reasons; detailed information is unlikely to be available in this message.</td></tr></tbody></table>

**Error codes**

| Error Code            | Description                                                |
| --------------------- | ---------------------------------------------------------- |
| `CLIENT_SIDE_ERROR`   | Generic error code for an invalid request.                 |
| `SERVER_SIDE_ERROR`   | Generic error code for an error that occurred in Singpass. |
| `UNAUTHORIZED`        | Authorisation header value is invalid.                     |
| `ARGUMENTS_NOT_VALID` | Some request parameters are invalid.                       |

Example: Invalid Request Parameters

```http
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "id" : "bcba4bc3-534e-4891-bfa2-e872b4502d80",
  "error" : "CLIENT_SIDE_ERROR",
  "error_description" : "This is an invalid request.",
  "trace_id" : "675fe8742c6442a7c0a1f3d1643ff9af"
}
```

## Fetch our public keys

Integrating parties can verify the signature of a JWT from Singpass by acquiring the signing public key from this endpoint. More information about a JSON Web Key (JWK) endpoint can be found [here](https://tools.ietf.org/html/rfc7517).

Public keys returned from this endpoint could be in random sequence or rotated for security enhancement.&#x20;

### URLs

<table><thead><tr><th width="178">Environment</th><th width="576">URL</th></tr></thead><tbody><tr><td>Staging</td><td><p><a href="https://static.staging.sign.singpass.gov.sg/.well-known/keys.json">https://static.staging.sign.singpass.gov.sg/.well-known/keys.json</a><a href="https://static.staging.sign.singpass.gov.sg/.well-known/keys.json"><br></a>OR</p><p><a href="https://stg-id.singpass.gov.sg/.well-known/digital-signing-keys">https://stg-id.singpass.gov.sg/.well-known/digital-signing-keys</a> <mark style="color:red;">(Deprecated)</mark></p></td></tr><tr><td>Production</td><td><a href="https://static.app.sign.singpass.gov.sg/.well-known/keys.json">https://static.app.sign.singpass.gov.sg/.well-known/keys.json</a><a href="https://static.app.sign.singpass.gov.sg/.well-known/keys.json"><br></a>OR<br><a href="https://id.singpass.gov.sg/.well-known/digital-signing-keys">https://id.singpass.gov.sg/.well-known/digital-signing-keys</a> <mark style="color:red;">(Deprecated)</mark></td></tr></tbody></table>

### Cache and key rotation

For varying reasons, keys used for signing can and will be rotated/changed with **no defined schedule**, and at the full discretion of Singpass. When a key rotation happens, the new key will be available from the JWKS endpoint and will have a different `kid` value. The new `kid` value will be reflected in all the new JWTs signed by Singpass. In such cases, cached copies of Singpass public keys must be refreshed by re-invoking the JWKS endpoint.

If the validation of the Singpass signature fails, re-fetch from the JWKS endpoint once for that validation.

{% hint style="danger" %}
Please read through the list of **DON’Ts** below:

* Do not assume the position of a signing key among the list of the returned keys.
* Do not validate Singpass signatures using a hardcoded public key OR `kid`. Always determine the correct key (for signature verification) by inspecting the `kid` from the JWS header, and use it to retrieve the public key from our JWKS endpoint.
* Do not cache only 1 key. Caching should be done for the entire JWKS.
  {% endhint %}


# UX Guidelines

This section contains important UX guidelines to ensure signers are well-guided through their signing journey.

* [Adding sufficient signature placeholder space in your document](#add-sufficient-signature-placeholder-space-in-your-document)
* [Upload your app logo](#upload-your-app-logo)
* [Implementing the Sign with Singpass button](#implementing-the-sign-with-singpass-button)
* [Presenting Sign with other signing options](#presenting-sign-with-other-signing-options)&#x20;

## **Add sufficient signature placeholder space in your document**

On a regular A4 document, a Sign with Singpass signature will take up an area of 7.78cm x 2.12cm (294 x 80 pixels).&#x20;

When preparing a PDF of the unsigned document, clearly demarcate the placements of the signatures and **mark a sufficient area (at least 3.12 x 7.98cm) for the signature placeholders to appear.** This ensures that the document's content will not be obscured by the digital signature.&#x20;

<figure><img src="/files/Bvm1wbDwwdbXUuFuYsGV" alt=""><figcaption><p>Example of a clearly demarcated signature area in the document (size annotations are not required)</p></figcaption></figure>

For ease, you may copy the placeholder sample from this Word document to use in your own document:

{% file src="/files/qDNo46ZvdHvuLCzClBvz" %}

<details>

<summary>How do I define the location on the document where my user's signature should appear?</summary>

When initiating a sign request to our API, you are required to send us the coordinates of the desired location for your user's signature to appear.&#x20;

You can use our [Sign Location Helper](https://docs.sign.singpass.gov.sg/for-relying-parties/api-documentation/document-signing-v3#sign-location-helper) to experiment with signature placement and copy the `(x, y, page)` values for use in Sign v3.

</details>

## Upload your app logo

Your organisation's logo will be displayed in various places like the Login screen and Singpass app. This helps users identify your organisation when they are transacting with you.&#x20;

### App logo requirement &#x20;

When uploading your logo, ensure that they meet the following requirements:

* **File format**: PNG (transparent background)&#x20;
* **Dimension**: 256px by 256px to 512px by 512px
* **File size**: Up to 200KB
* **Aspect Ratio**: Square (1:1 ratio)
* **No padding**: The logo should fill the entire canvas with no surrounding whitespace. Padding is applied automatically and does not need to be included in your upload.

### Example of logo&#x20;

<figure><img src="/files/MHla9CWR9EMs3O0DXk1f" alt=""><figcaption></figcaption></figure>

Note:

* For Singpass Login screen: Changes will take effect from 1 Jul 2026.
* For Singpass app: Logo changes will only be reflected once users update to the latest app version, from late Jul 2026 onwards.

## Implementing the Sign with Singpass button

{% hint style="danger" %}
**All signing sessions must be initiated via the Singpass button** and not through providing any redirect links directly to the user.&#x20;
{% endhint %}

#### You can download the Sign button in 2 sizes here:

{% file src="/files/jm7Nr2jO7TVwfYTFA7bc" %}

#### Otherwise, please adhere to the following guidelines to create the Sign button.

* [Singpass button types](#singpass-button-types)
* [Font usage for button label](#font-usage-for-button-label)
* [Aria labels for screen readers](#accessibility-aria-labels-for-screen-readers)
* [Singpass logo size](#singpass-logo-size)
* [Button colour](#button-colour)
* [Border stroke](#border-stroke)
* [Border radius](#border-radius)
* [Hover state](#hover-state)
* [Width and height](#width-and-height)
* [Loading state](#loading-state)

\
**Singpass logo download:**

You can also use the following Singpass logo for your online and print materials

{% file src="/files/iN6e3HKB5Xir1ynewOqc" %}

#### **Singpass button types**

By default, use the white fill button to pair it with your service’s interface. Alternatively, you may use the red fill button if it’s suitable.

* White-filled button: **#FFFFFF**
* Red-filled button: **#F4333D**

<figure><img src="/files/6KZLKhWsdbtl7rRZoMrQ" alt=""><figcaption></figcaption></figure>

#### **Font usage for button label**

By default, please use **Poppins at 16px** size. Alternatively, you may use your brand’s typeface. San serif is highly recommended for this button to better match the Singpass logo.

Download Poppins Bold on [Google fonts](https://fonts.google.com/specimen/Poppins).

<figure><img src="/files/h4F5AhvXOPyrzbmkigko" alt=""><figcaption></figcaption></figure>

#### **Accessibility: Aria labels for screen readers**

Please ensure that the **Singpass logo has a aria-label** to ensure that screen readers will read it as “Sing pass” to avoid it reading out the entire word as ‘S-i-n-g-p-a-s-s’.

<figure><img src="/files/VtmSoa3UcJWGQatdEu7c" alt=""><figcaption></figcaption></figure>

#### **Singpass logo size**

**Match the x-height of the label** as close as possible to ensure visual balance and proportion.

<figure><img src="/files/C3W8Qpkxlg0ovw1ZyNeP" alt=""><figcaption></figcaption></figure>

#### **Button colour**

To ensure brand recognition and consistency, use the **white-filled** or **red-filled** buttons as stated below. Avoid using colours other than white-filled or red-filled buttons.

<figure><img src="/files/DPjqkG9gbUo2UT2SxtYm" alt=""><figcaption></figcaption></figure>

#### **Border stroke**

When using the white-filled button, please ensure to adhere to the specs stated below.

* Border colour: **#C8C9CC**
* Border width: **1px**

<figure><img src="/files/tvibHTvFSTVpkjcG3nRI" alt=""><figcaption></figcaption></figure>

#### **Border radius**

Ensure that the border radius of Singpass button matches your button.

<figure><img src="/files/9Y9sezUcepSgMPkXVmKR" alt=""><figcaption></figcaption></figure>

#### **Hover state**

Please adhere to the following colours, and avoid using another hover state styling.

* White-filled button hover colour: **#F5F5F7**
* Red-filled button hover colour: **#B0262D**

<figure><img src="/files/E8KzecXpvMB88RL8cA8g" alt=""><figcaption></figcaption></figure>

#### **Width and height**

Please ensure that the Singpass button visually matches your primary button with the following:

* Width: **Fill to match your primary button width**
* Height: **Match your button height**

<figure><img src="/files/bwZNagIbGOCvknOtHuRD" alt=""><figcaption></figcaption></figure>

#### **Loading state**

Please ensure that the button has a loading state as follows. The loading state should appear for the duration of time after the user clicks the button and is waiting to be redirected to the Sign portal.

<figure><img src="/files/12YmHrnHqy7KRULoF3bC" alt=""><figcaption></figcaption></figure>

## Presenting Sign with other signing options

**If users are given multiple options to sign**, with Sign as one of the options, we recommend positioning Sign as the preferred signing choice, and including the following liner to inform users of the security benefit of using Sign with Singpass:

> *By signing with Singpass, you create secure digital signatures that are highly resistant to forgery and tampering.*

<figure><img src="/files/8tmvlrHXigH2mDUJKjUY" alt=""><figcaption><p>Example demonstrating how this liner can be included</p></figcaption></figure>


# User Journey Illustration

Relying Parties (RPs) who wish to use Sign with Singpass in their production environments will also have to submit a **user journey illustration.** Please fill in the following template before uploading it into the onboarding form.

{% file src="/files/v3DF3shuSM2qvxyyHOGT" %}

{% hint style="success" %}
Please also ensure that your user journey design adheres to our [UX Guidelines](/for-relying-parties/ux-guidelines)
{% endhint %}


# Digital Signing Partners

You may also choose to contact the following Digital Signing Partners who have experience integrating Sig&#x6E;**:**

{% hint style="info" %}
Alternatively, you may **directly** integrate with Sign with Singpass APIs by submitting on [onboarding request here](/for-relying-parties/onboard).
{% endhint %}

<table data-view="cards" data-full-width="true"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>DocuSign</strong><br>Docusign brings agreements to life. Over 1.5 million customers and more than a billion people in over 180 countries use Docusign solutions to accelerate the process of doing business and simplify people’s lives.</td><td><a href="/files/WsWgWR3Ys3kTotwxf7c2">/files/WsWgWR3Ys3kTotwxf7c2</a></td><td><a href="/pages/suG2nRCoMf6Ivs2IlvWZ">/pages/suG2nRCoMf6Ivs2IlvWZ</a></td></tr><tr><td><strong>Kofax</strong><br>Kofax enables organisations to Work Like Tomorrow™—today. Kofax’s Intelligent Automation software platform and solutions digitally transform document intensive workflows.</td><td><a href="/files/4zHBSQoWLvEw5Ef5HsZP">/files/4zHBSQoWLvEw5Ef5HsZP</a></td><td><a href="/pages/R0meiNybifvfDxfZmqUz">/pages/R0meiNybifvfDxfZmqUz</a></td></tr><tr><td><strong>OneSpan</strong><br>From digital onboarding to fraud mitigation to workflow management, OneSpan’s unified Trusted Identity platform reduces costs, accelerates customer acquisition, and increases customer satisfaction.</td><td><a href="/files/NOoUBHDszyxzcM0iDWgo">/files/NOoUBHDszyxzcM0iDWgo</a></td><td><a href="/pages/5gWjXnk674K3akhHfTjy">/pages/5gWjXnk674K3akhHfTjy</a></td></tr><tr><td><strong>Tessaract Technologies Pte Ltd</strong><br>Tessaract.io is an all-in-one enterprise management system for professionals built on a cloud-native architecture, readily accessible on all smart devices.</td><td><a href="/files/yLgdpVzbN6IT8AqZ6r5u">/files/yLgdpVzbN6IT8AqZ6r5u</a></td><td><a href="/pages/F7K7MX81vh7ACMUjpNDi">/pages/F7K7MX81vh7ACMUjpNDi</a></td></tr><tr><td><strong>Netrust Pte Ltd</strong><br>Netrust is Asia’s first public Certification Authority (CA) and Singapore's first IMDA-accredited CA since 2001 which provides individuals, business and government organizations with a complete online identification and security infrastructure to enable secure electronic transactions.</td><td><a href="/files/sBPxy8OWEX6HX4TjJJsI">/files/sBPxy8OWEX6HX4TjJJsI</a></td><td><a href="/pages/1SHPybKs2v90XLoxAj8i">/pages/1SHPybKs2v90XLoxAj8i</a></td></tr><tr><td><strong>Modus Consulting</strong><br>Modus Consulting enables organisations to develop digital document frameworks and orchestrations, starting with intelligent data capture via "Dynamic Interactive PDFs" to back-office submission and orchestrations.</td><td><a href="/files/KkCb5CIlKwrnYGKBj6c3">/files/KkCb5CIlKwrnYGKBj6c3</a></td><td><a href="/pages/VZEh4lmzXs1wRawrmmZ8">/pages/VZEh4lmzXs1wRawrmmZ8</a></td></tr><tr><td><p><strong>Redoc.co by Real Estate Doc Pte Ltd.</strong></p><p>Redoc.co, by Real Estate Doc, is the cloud-based Revenue and Contract Management software for Sales-Driven and Real Estate Businesses.</p></td><td><a href="/files/vn2LSL5WYIjF0qXbN0mU">/files/vn2LSL5WYIjF0qXbN0mU</a></td><td><a href="/pages/1Hs3IhWMLjqYY6vTE7Ij">/pages/1Hs3IhWMLjqYY6vTE7Ij</a></td></tr><tr><td><p><strong>CrimsonLogic</strong> </p><p>For more than 30 years, we have partnered with governments across the world. Together as their trusted partner, we deliver value to citizens and businesses through sustainable Digital Transformation.</p></td><td><a href="/files/g5S1XOvsL91j94E0vWC3">/files/g5S1XOvsL91j94E0vWC3</a></td><td><a href="/pages/Y2CBVWCfjR2UV6k389PS">/pages/Y2CBVWCfjR2UV6k389PS</a></td></tr><tr><td><p><strong>Zoho Sign</strong> </p><p>Zoho Sign is a complete digital signature app for business signatories. It helps organisations to sign, send, and manage documents from anywhere and at anytime.</p></td><td><a href="/files/ECt4AkQzVEXFGOvt7K9q">/files/ECt4AkQzVEXFGOvt7K9q</a></td><td><a href="/pages/gLNrXkTpxoBFlXZT32Fv">/pages/gLNrXkTpxoBFlXZT32Fv</a></td></tr><tr><td><p><strong>Securemetric Technology Pte. Ltd.</strong></p><p>SigningCloud is a SaaS based digital signature solution for you to sign, assign &#x26; manage documents signature in one place. </p></td><td><a href="/files/yDkMfPelPoE8xRPnaqm1">/files/yDkMfPelPoE8xRPnaqm1</a></td><td><a href="/pages/xcC71U9BgmiyTV6Qntyj">/pages/xcC71U9BgmiyTV6Qntyj</a></td></tr><tr><td><p><strong>Rently Pte. Ltd.</strong> </p><p>Rently is the new revolutionary mobile application that digitalises the entire property renting process and it is now integrated with Sign with Singpass.</p></td><td><a href="/files/f03rHcT0G1AiaQ4gvJP7">/files/f03rHcT0G1AiaQ4gvJP7</a></td><td><a href="/pages/rFpZY35n9erDRvRIn9YR">/pages/rFpZY35n9erDRvRIn9YR</a></td></tr></tbody></table>


# Docusign

## **About Docusign**

Docusign brings agreements to life. Over 1.5 million customers and more than a billion people in over 180 countries use Docusign solutions to accelerate the process of doing business and simplify people’s lives.

With intelligent agreement management, Docusign unleashes business-critical data that is trapped inside of documents. Until now, these were disconnected from business systems of record, costing businesses time, money, and opportunity.

Using Docusign IAM, companies can create, commit, and manage agreements with solutions created by the #1 company in e-signature and contract lifecycle management (CLM).

To find out more, click [here](https://www.docusign.com/).

## **Contact**

**Sales Email Address**

* <apac@docusign.com>

**Singapore Hotline**

* +800 1206 719

**Sydney Hotline**

* +61 2 9392 1998

**US Hotline**

* +1 877 720 2040


# Tungsten Automation

## **About Tungsten Automation**

Kofax enables organisations to Work Like Tomorrow™—today. Kofax’s Intelligent Automation software platform and solutions digitally transform document intensive workflows. Customers realise greater agility and resiliency by combining Kofax’s process orchestration, cognitive capture, RPA, output management, analytics and mobile capabilities to speed time-to-value and increase competitiveness, growth and profitability while mitigating compliance risk.

Kofax SignDoc® included in Kofax Intelligent Automation software platform enables trustworthy, secure and convenient paperless signing for a wide variety of functions including customer and employee onboarding, procurement, account management, service documentation, payroll and finance.

To find out more, click [here](https://www.kofax.com/).

***

## Contact

**Enquiry Email Address**

* <SSR.APAC@kofax.com>

**Singapore Hotline**

* +65 6278 7662


# OneSpan

## **About OneSpan**

OneSpan enables financial institutions, government departments and other organisations to succeed by making bold advances in their digital transformation. OneSpan establishes trust in people’s identities, the devices they use, and the transactions that shape their lives. More than 10,000 customers, including over half of the top 100 global banks, rely on OneSpan solutions to protect their most important relationships and business processes. From digital onboarding to fraud mitigation to workflow management, OneSpan’s unified Trusted Identity platform reduces costs, accelerates customer acquisition, and increases customer satisfaction.

To find out more, click [here](https://www.onespan.com/).

***

## Contact

**Contact**

**Enquiry Email Address**

* <info@onespan.com>

**Singapore Hotline**

* +65 6323 0906


# Tessaract Technologies Pte Ltd

## **About Tessaract Technologies Pte Ltd**

Tessaract.io is an all-in-one enterprise management system for professionals built on a cloud-native architecture, readily accessible on all smart devices. Tessaract.io keeps track of workflows, schedules and billings from initial execution to eventual completion. Tessaract.io's automation features include one-click workflow generation, automated document creation and digital signing, client engagement and notifications, and an integrated full-fledge accounting software that allows for e-invoicing.

To find out more, click [here](https://tessaract.io/).

***

## **Contact**

**Cherilyn**

* <cherilyn@tessaract.io> +65 8692 0222


# Netrust Pte Ltd

## **About Netrust Pte Ltd**

Established in 1997, Netrust is Asia’s first public Certification Authority (CA) and Singapore's first IMDA-accredited CA since 2001 which provides individuals, business and government organizations with a complete online identification and security infrastructure to enable secure electronic transactions. Netrust also delivers professional services including security consulting, PKI deployment and custom application development.

Netrust issues digital certificates for online applications including secure access to government applications, Internet banking, supply chain management, virtual private networks and secure access to intranet portals. Parties can rely on Netrust certificates for evidentiary presumption and users are assured of the legality and security of their transactions.

To find out more, click [here](https://www.netrust.net/).

***

## **Contact**

**Enquiry Email Address**

* <infoline@netrust.net>

**Singapore Hotline**

* +65 6212 1388


# Modus Consulting

## About Modus Consulting

Modus Consulting enables organisations to develop digital document frameworks and orchestrations, starting with intelligent data capture via "Dynamic Interactive PDFs" to back-office submission and orchestrations. NDISign enables a fully digital document workflow. With no printing and wet signing required, organisations can realise the true value of the fully digital document workflow.

This on-premise solution allows for customised branding of the experience interface with a platform-agnostic approach and the most straightforward signing ceremony with only two clicks on the interface - one for selecting the document and one for getting it back.

To find out more, click [here](https://www.modus.sg/).

***

## **Contact**

**Enquiry Email Address**

* <info@modus.sg>

**Singapore Hotline**

* +65 6673-9853


# Redoc.co by Real Estate Doc Pte Ltd.

## **About Redoc.co by Real Estate Doc Pte Ltd.**

Redoc.co, by Real Estate Doc, is the cloud-based Revenue and Contract Management software for Sales-Driven and Real Estate Businesses.

Designed and developed for Salespersons and Sales-driven organisations, Redoc.co digitalises and automates workflow, converting deals faster from quotation to closure.

To find out more, click [here](https://redoc.co/).

***

**Contact**

**Enquiry Email Address**

* <enquiries@realestatedoc.co>
* <ivan.lim@realestatedoc.co>
* <eugene.lee@realestatedoc.co>

**Hotline**

* +65 9180 1197


# CrimsonLogic

## **About CrimsonLogic**

For more than 30 years, we have partnered with governments across the world. Together as their trusted partner, we deliver value to citizens and businesses through sustainable Digital Transformation.

To find out more, click [here](https://www.crimsonlogic.com/).

***

## **Contact**

**Enquiry Email Address**

* <sales@crimsonlogic.com>

**Singapore Hotline**

* (65) 6887 7888


# Zoho Sign

## About Zoho Sign

Zoho Sign is a complete digital signature app for business signatories. It helps organisations to sign, send, and manage documents from anywhere and at anytime.

Say goodbye to the traditional method of signing documents with pen and paper. With Zoho Sign, you can reduce the document turnaround time, save costs on printing and mailing, and automate mundane tasks.

Zoho Sign readily integrates with more than 50+ popular apps, including Zoho CRM, Dropbox, HubSpot, Microsoft Teams, Google Workplace, and much more. In addition, our powerful APIs and mobile SDKs help businesses to connect Zoho Sign with their existing in-house applications.

Sign paperless and go green with Zoho Sign.

To find out more, click [here](https://www.zoho.com/sign).

***

## **Contact**

**Enquiry Email Address**

* <support@zohosign.com>

**Singapore Hotline**

* +65 6723 1040

**US Hotline**

* +1 877 834 4428


# Securemetric Technology Pte. Ltd.

## **About Securemetric Technology Pte. Ltd.**

SigningCloud is a SaaS based digital signature solution for you to sign, assign & manage documents signature in one place. We have simplified the document signing process by automating your document delivery. SigningCloud tracks progress by checking each signer’s status. Notifications and reminders will be sent to related parties to keep everyone on the same page. You’ll be able to spend less time tracking down signatures and devote more time to other mission-critical tasks. SigningCloud uses state-of-the-art cryptography algorithms to ensure confidentiality and integrity of all signed documents.

To find out more, click [here](https://www.signingcloud.com/).

***

## Contact

**Enquiry Email Address**

* <helpdesk@signingcloud.com>


# Rently Pte. Ltd.

## **About Rently Pte. Ltd.**

Rently is the new revolutionary mobile application that digitalises the entire property renting process and it is now integrated with Sign with Singpass. Rently is the safest and fastest way to draft and sign rental contracts. It helps tenants, landlords and real estate agents save time and streamline communication beyond contract signature. Rently believes that technology can break the hassle of renting properties and bring a new paperless and scam-free experience for everyone.

To find out more, click [here](https://www.rently.sg/).

***

## **Contact**

Enquiry Email Address

* <support@rently.sg>


# Support

## Enquiries

If you have any questions or encounter any issues, please don’t hesitate to raise a support ticket at <https://partnersupport.singpass.gov.sg/hc/en-sg/requests/new> &#x20;

## Feedback

Have feedback or a feature request? Let us know at <https://go.gov.sg/sign-feedback>


# Guiding Principles

## <mark style="color:red;">What you see is what you sign</mark>

* As a trusted partner of Sign with Singpass, you should always ensure that the user gets to see what they sign before they proceed to sign the document.
* You should never use this system to misrepresent intended documents which your user plans to sign.

## <mark style="color:red;">PDPA & Applicable Regulations</mark>

* Protect, retain and transfer data retrieved according to the Personal Data Protection Act (PDPA), relevant industry regulations and applicable legislation.

## <mark style="color:red;">Lawful purposes</mark>

Use Sign with Singpass for lawful purposes only. Singpass reserves the right to terminate the integration if Sign with Singpass is used for any unlawful purpose.


