Document Signing API Specifications
Last Updated: 11 Nov 2024
Last updated
Last Updated: 11 Nov 2024
Last updated
Date | Changes |
---|---|
11 Nov 2024 |
|
30 Sep 2024 |
|
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.
An overview of the document signing flow and the interactions between DSAP, Singpass and other dependencies.
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/* |
|
Production | https://app.sign.singpass.gov.sg/api/v1/* |
|
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 in Staging as of 23 Sep 2024. We will ignore all incoming mTLS client authentication.
Environment | Domain | Access Mechanism |
---|---|---|
Staging | https://static.staging.sign.singpass.gov.sg/.well-known/keys.json | SSL |
Production | https://static.app.sign.singpass.gov.sg/.well-known/keys.json | SSL |
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.
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. Please note that your cert should not be a self-signed certificate.
Additionally, server certificates presented must minimally conform to the following requirements:
Requirement | RSA Certs | ECC Certs |
---|---|---|
Required Public Key dimensions | >= 2048 bits >= 3072 bits (recommended) | >= 256-bit
based on curves |
X.509 v3 extension KeyUsage | digitalSignature, keyEncipherment | digitalSignature |
X509 v3 extension ExtendedKeyUsage | serverAuth | serverAuth |
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.
HTTP Status Code | Description |
---|---|
4XX | Errors caused by API consumer. You can expect codes such as 400, 401, 403, 404 etc if incorrect requests are made to APIs. Example: 400: Invalid/missing request arguments |
5XX | 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. Example: 500: Internal Server Error due to some kind of programming error. |
Path | Type | Description |
---|---|---|
| String | The unique identifier for this error/request. Please log this identifier for support and debugging purposes. |
| String | (Optional) An auxiliary id for request correlation across services. Please also log this identifier for operational support and debugging purposes. |
| String | 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. |
| String | 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. |
Error Code | Description |
---|---|
| Generic error code for an invalid request |
| Generic error code for an error that occurred in Singpass. |
| Authorization header value is invalid. |
| Authorization header was required but not found. |
| Some request parameters are invalid. |
| Requested sign ref is invalid or has already expired. |
| Multiple DSS assertion headers |
| DSS assertion header is invalid |
| DSAP JWKS URL is not found |
| Unable to fetch DSAP JWKS or DSAP JWKS is invalid |
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
This is deprecated in favour of authenticating via JSON Web Tokens.
For all DSS APIs, DSAPs will need to authenticate themselves using their base64-encoded client_id
and client_secret
via the basic authentication scheme in the following format:
DSAPs are to use JSON Web Token (JWT) assertion for authentication when calling the DSS APIs.
Parameter | Type | Description |
---|---|---|
| String | Algorithm used in the JWT.
Acceptable values are:
|
| String | Acceptable value: |
| String | The Key ID used for the JWT. This value must correspond to a key in the DSAP JWKS endpoint. |
Parameter | Type | Description |
---|---|---|
| String | Use the ClientID provided by Singpass |
| String | Use the ClientID provided by Singpass |
| String | The DSS domain that will be accepting the JWT. |
| Number | A Unix timestamp in seconds indicating the date and time when the JWT was issued. |
| Number | A Unix timestamp in seconds indicating the date and time when the JWT will expire.
Max expiry allowed is |
| String | A random string that must be regenerated for each JWT to prevent replay attacks.
Singpass will cache and reject all duplicate |
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.
Parameter | Type | Description |
---|---|---|
| String | Key Type
Acceptable value: |
| 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. |
| 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. |
| Number | Curve Name |
| Number | Public Key Use.
Acceptable value: |
| String | Key ID.
Singpass will search for the |
| String | [Optional] Algorithm |
DSAPs are to put the JWT in the request header with the key X-Dss-Assertion
when calling all DSS APIs.
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.
POST /doc-signing-sessions
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.
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 /doc-signing-sessions/<sign-ref>/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.
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.
Name | Type | Description |
---|---|---|
| String | A token provided by the DSAP to be used by Singpass when invoking the DSAP’s webhook endpoint. Allowed Characters: Alphanumeric, underscore ( _ ), dash ( - ), and whitespace Max length: 255 characters |
| String | The identifier of the tenant of the DSAP client using the doc signing service. Allowed Characters: Alphanumeric, underscore ( _ ), dash ( - ), and whitespace Max length: 255 characters |
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
.
Name | Description |
---|---|
| (Deprecated) The DSAP’s client id and client secret in http basic authentication scheme format. Not required if DSAP is using JWT to authenticate. |
| (New) JWT issued by the DSAP for authentication. |
Parameter | Type | Description |
---|---|---|
| String | The primary identifier of a signing session used in all exchanges between Singpass and DSAP. |
| 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. |
| Number | A Unix timestamp in seconds indicating the date and time when the |
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.
Please refer to General Error Response.
POST /doc-signing-sessions/<sign-ref>/hash
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 for more details about what DSAPs need to do before invoking this endpoint.
Path Parameters
Parameter | Description |
---|---|
| The primary identifier of a signing session used in all exchanges between Singpass and DSAP |
Parameter | Type | Description |
---|---|---|
| String | 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. Length must be between 1 and 255 |
| String | The hash of the document to be signed by the user. Refer to Document Hash Specifications for more details. Length must be 64 |
| String | A set of characters used by the user to visually verify the signing session between the Singpass App and the DSAP website. Length must be 4. Numerical values are recommended. |
| String | Singpass-generated random string sent to the DSAP's webhook endpoint during user certificate notification. |
| String | The domain name of the webpage displaying the QR code. This will be displayed to the user on the Singpass app. Length must be between 1-255 |
| String | (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. Max length allowed is 255 characters. |
Name | Description |
---|---|
| (Deprecated) The DSAP’s client id and client secret in http basic authentication scheme format. Not required if DSAP is using JWT to authenticate |
| (New) JWT issued by the DSAP for authentication. |
There is no body in the response for successful API call.
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.
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.
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.
There is no request body for this API call.
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.
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.
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.
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.
Singpass will call the webhook endpoint to inform of the following events:
User certificate notification - To send the user’s certificate (See Step 6 of flow diagram).
Document hash signature notification - To send the user’s document hash signature (See Step 14 of flow diagram).
Webhook error notification - To inform DSAP of any errors encountered during the webhook calls above.
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.
The webhook must accept the POST
HTTP method.
It should expect a JSON Web Tokens (JWT) in the request body.
It should authenticate incoming calls using the Bearer authentication scheme, 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.
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.
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 |
Name | Type | Description |
---|---|---|
| String | The signed JWT containing standard and custom claims that describe a user or error event of a digital signing session. |
Name | Description |
---|---|
| Bearer token authentication credentials. The token value is equal to the |
There is no body in the response for this API call.
token
structureAs mentioned previously, the request body token
field is a JWT signed by Singpass, which can be verified by DSAPs.
Parameter | Type | Description |
---|---|---|
| String | The primary identifier of a signing session used in all exchanges between Singpass and DSAP. |
| String | The user's digital signing certificate in base64-encoded DER format. |
| Number | A Unix timestamp in seconds indicating the date and time when the JWT was issued. |
| Number | A Unix timestamp in seconds indicating the date and time when the JWT will expire. |
| String | 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. |
| String | Value to indicate the type of the request, value is |
Parameter | Type | Description |
---|---|---|
| String | The primary identifier of a signing session used in all exchanges between Singpass and DSAP. |
| String | The hash of the document to be signed by the user. Refer to Document Hash Specifications for more details. |
| Number | A Unix timestamp in seconds indicating the date and time when the JWT was issued. |
| Number | A Unix timestamp in seconds indicating the date and time when the JWT will expire. |
| String | The signature of the document hash, signed by the user. Refer to Document Hash Signature Specifications for more details. |
| String | Value to indicate the type of the request, value is |
Parameter | Type | Description |
---|---|---|
| String | The primary identifier of a signing session used in all exchanges between Singpass and DSAP. |
| String | Error code representing an error, e.g. |
| Number | A Unix timestamp in seconds indicating the date and time when the JWT was issued. |
| Number | A Unix timestamp in seconds indicating the date and time when the JWT will expire. |
| String | Returns human readable general information about the reason for the error. |
| String | Value to indicate the type of the request, value is |
| String | A randomly generated ID that can be used to correlate requests between Singpass and DSAP. This ID is unique per user request. |
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:
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.
Parameter | Type | Description |
---|---|---|
| String | A randomly generated ID that can be used to correlate requests between Singpass and DSAP. This ID is unique per user request. |
| String | Error code representing an error. Must be one of the values from the list of valid error codes. |
| String | Returns human readable general information about the reason for the error. |
Error | Expected Http Status | Reason |
---|---|---|
| 400 | To be returned if the sign ref in the request is unknown or expired. |
| 400 | A catch all error indicating an invalid request parameter from DSS. Eg. invalid client notification token, invalid user cert, invalid doc hash signature, etc. |
| 5XX | A catch all for any other errors that occurred in the DSAP. Singpass will retry on 502, 503 and 504. Refer to Retry Settings. |
DSAPs are expected to verify the validity of the user’s certificate status against National Certificate Authority (NCA).
Singpass only validates the expiry, signature and signer of the certificate against the NCA Root and Intemediary CAs.
DSAPs must use Online Certificate Status Protocol (OCSP) to do the status verification against NCA. The OCSP response must be embedded in the signed document.
The Certificate Revocation List (CRL) file size is large so we do not recommend using CRL to verify the cert status to avoid large signed document file sizes.
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 compliant signature.
The document hash should be SHA256 hashed and hex-encoded before sending to Singpass.
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 and will be hex-encoded.
The document hash is not re-hashed during signing.
DSAPs are expected to then generate a PAdES-LTV/LTA compliant signature using this.
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.
To support mobile users, DSAPs are also required to render the QR code with the mobile app links (deep links) following the format below.
Platform | |
---|---|
Android |
|
iOS |
|
Question: Since the user cert notification webhook is defined as asynchronous and non-blocking, are there any constraints on when a DSAP should invoke the /doc-signing-sessions/<sign-ref>/hash endpoint?
Answer: Yes. The agreed average response time between the start of the user cert notification 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.
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 /doc-signing-sessions/<sign-ref>/hash endpoint 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.