Audience: Integration engineers and solution architects connecting a third-party Identity Provider (IdP) to Monosign
Applies to: Monosign v2026.09 and later (code-entry mode for the API Service Authenticator: v2026.10 and later)
Purpose
This guide explains how an external Identity Provider can use Monosign as its multi-factor authentication (MFA) service. The Identity Provider keeps ownership of the sign-in experience and the final access decision; Monosign verifies the user's second factor and reports the outcome through its REST API.
Two verification modes are supported:
|
Mode |
How the user confirms |
Typical use |
|---|---|---|
|
Approval request (default) |
The user approves or rejects a request on the registered authenticator (Monofor Identity push notification, or the external service behind the API Service Authenticator). |
Interactive sign-in where the user has the device at hand. |
|
Code entry ( |
No request is sent to the device. The user reads a one-time code from the authenticator application and enters it in the Identity Provider's sign-in page. |
Environments where push delivery is not possible or not desired, such as restricted networks or devices without notification access. |
The integration can be built in one of two variants, depending on where the user's password is validated:
|
Variant |
Password validated by |
Monosign session |
Described in |
|---|---|---|---|
|
A — With a Monosign session |
Monosign (step 1) |
Opened by the login call and sent in every MFA call |
Steps 1–4 |
|
B — Without a Monosign session |
The Identity Provider itself, or not required |
Not needed; the MFA calls carry |
Variant B — Without a Monosign Session |
Both verification modes are available in both variants.
Prerequisites
Before starting the integration, confirm the following:
-
Application registration. The Identity Provider is registered as an application in Monosign, and its App ID and App Key are available. Every API call carries them in the
Monosign-AppIdandMonosign-AppKeyheaders. -
API permissions. The application's API key is granted the permissions listed in the table below.
-
User readiness. The user exists in Monosign and has at least one enrolled, enabled authenticator (for example Monofor Identity or API Service Authenticator).
-
Base URL. The Monosign API base URL of the target environment, referred to below as
{ApiUrl}.
|
Permission |
Required for |
|---|---|
|
|
Step 1 — primary sign-in |
|
|
Step 2 — listing the user's authenticators |
|
|
Steps 3 and 4 — creating requests, checking their status, submitting Monofor Identity codes |
|
|
Submitting codes through |
Security note: The App Key is a credential. Store it in the Identity Provider's secret store, never in client-side code, and rotate it according to your organisation's policy.
Conventions
-
Request bodies are JSON (
Content-Type: application/json). Field names are case-insensitive; this guide uses camelCase. -
All fields not marked Required are optional. Omitted fields take the default shown.
-
Unknown fields are ignored, so sending an optional field to an older release does not cause an error.
Integration Flow at a Glance (Variant A)
|
Step |
Identity Provider calls |
Monosign returns |
Keep for later |
|---|---|---|---|
|
1 |
|
User and session identifiers |
|
|
2 |
|
The user's active authenticators |
|
|
3 |
|
A final result, or a pending transaction |
|
|
4 |
|
The current result (repeated until final) |
— |
From step 2 onwards, every call includes the Monosign-SessionId header with the SessionId returned in step 1.
Step 1 — Establish the User Session
The Identity Provider validates the user's primary credentials against Monosign. Monosign opens a session for the user and returns the identifiers required for the MFA steps.
The client details sent in this call are stored on the session. The Monofor Identity approval request shows them to the user (IP address, browser, device type and location derived from the IP), so the Identity Provider should forward the end user's details rather than its own.
Request
POST {ApiUrl}/v1.4/users/login
Monosign-AppId: {AppId}
Monosign-AppKey: {AppKey}
Content-Type: application/json
{
"userName": "jane.doe",
"password": "{Password}",
"isPasswordEncrypted": false,
"permanent": false,
"userIp": "203.0.113.24",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
"browserName": "Chrome",
"browserVersion": "129.0",
"operatingSystem": "Windows",
"operatingSystemVersion": "11",
"deviceType": 1,
"culture": "en"
}
Body fields
|
Field |
Type |
Default |
Description |
|---|---|---|---|
|
|
string |
— |
Required. The user's Monosign user name. |
|
|
string |
— |
Required. The user's password. |
|
|
boolean |
|
|
|
|
boolean |
|
Session lifetime. |
|
|
string |
— |
IP address of the end user. Stored on the session and shown in the approval request. Recommended. |
|
|
string |
— |
User agent of the end user's browser or client. Stored on the session. |
|
|
string |
— |
Browser of the end user. Shown in the approval request. |
|
|
string |
— |
Operating system of the end user. Stored on the session. |
|
|
integer |
|
Device category of the end user: |
|
|
string |
— |
Device details. Stored on the session. |
|
|
string |
— |
Preferred language, for example |
|
|
boolean |
|
|
Successful response — code 10188
{
"Message": "Login successful.",
"Code": 10188,
"Success": true,
"Data": {
"UserId": "9ca71310-eddf-4976-b684-f9204fd62b74",
"Session": { "SessionId": "a7bc6ea0-360b-49fe-84b8-f3a31cb44cc3" }
}
}
Retain Data.UserId and Data.Session.SessionId.
Step 2 — Select the User's Authenticator
The Identity Provider retrieves the user's active authenticators and selects the one to use for this sign-in. This call has no request body.
Request
GET {ApiUrl}/v1.3/mfa/options/{UserId}
Monosign-AppId: {AppId}
Monosign-AppKey: {AppKey}
Monosign-SessionId: {SessionId}
|
Path parameter |
Description |
|---|---|
|
|
Required. |
The same list is also available by user name: GET {ApiUrl}/v1.3/mfa/options/by-username/{UserName}.
Successful response — code 10131
{
"Message": "There are active authenticators for this user.",
"Code": 10131,
"Success": true,
"Data": [
{ "Id": "b47d6b96-2adc-412b-b6c8-f70eb2017eca", "Name": "Service", "Title": "API Service Authenticator" },
{ "Id": "5d0f3c1e-7a42-4b8e-9f61-2c4d8e0a9b17", "Name": "Mobile", "Title": "Monofor Identity" }
]
}
Select the entry by its Name, which is stable across languages and tenants. Title is a display label and may be customised.
|
|
Authenticator |
|---|---|
|
|
Monofor Identity |
|
|
API Service Authenticator |
The selected entry's Id is the UserAuthenticatorId used in steps 3 and 4. If the response code is 10130, the user has no active authenticator; the Identity Provider should direct the user to enrolment rather than continue.
Step 3 — Create the Verification Request
The Identity Provider creates a verification transaction for the selected authenticator. The body is optional; an empty body ({}) starts a standard approval request.
Request — approval mode (default)
POST {ApiUrl}/v1.3/mfa/request/{UserAuthenticatorId}
Monosign-AppId: {AppId}
Monosign-AppKey: {AppKey}
Monosign-SessionId: {SessionId}
Content-Type: application/json
{
"deviceName": "Corporate VPN",
"expiresInMinutes": 3
}
Request — code-entry mode
{
"disablePushRequest": true,
"expiresInMinutes": 3
}
|
Path parameter |
Description |
|---|---|
|
|
Required. |
Body fields
|
Field |
Type |
Default |
Applies to |
Description |
|---|---|---|---|---|
|
|
boolean |
|
Monofor Identity, API Service Authenticator |
|
|
|
boolean |
|
Monofor Identity, API Service Authenticator |
|
|
|
integer |
Authenticator's configured lifetime; 3 if none |
All |
Validity of this transaction in minutes. After expiry, status checks return |
|
|
string |
— |
Monofor Identity |
Shown in the approval request after the application name, for example "Access Portal - Corporate VPN". Use it to tell the user which system is asking. |
Note: The end user's IP address, browser and device details are taken from the session created in step 1, not from this call. Send them in the login request.
Responses in approval mode
After the request is created, Monosign holds the call open for approximately five seconds while the user responds (unless disableResponseWait is true).
|
Outcome |
HTTP status |
|
Identity Provider action |
|---|---|---|---|
|
User approved |
200 |
|
Grant access. |
|
User rejected |
200 |
|
Deny access. |
|
No answer yet |
499 |
|
Retain |
|
Request sent, no wait ( |
200 |
|
Retain |
Pending response example
{
"Message": "Warning",
"Code": 499,
"Success": false,
"Data": {
"UsageId": "a76dc5ea-0981-4068-b67f-53be6666905c",
"IsInteractive": true,
"IsCompleted": false
}
}
Implementation note: HTTP status 499 is not an error. HTTP clients that raise exceptions for non-2xx responses must be configured to read the response body for this status.
Step 4 — Retrieve the Result
While the transaction is pending, the Identity Provider queries its status using the UsageId from step 3. The body may be empty ({}).
Request
POST {ApiUrl}/v1.3/mfa/request/{UserAuthenticatorId}/{UsageId}
Monosign-AppId: {AppId}
Monosign-AppKey: {AppKey}
Monosign-SessionId: {SessionId}
Content-Type: application/json
{}
|
Path parameter |
Description |
|---|---|
|
|
Required. The same value used in step 3. |
|
|
Required. |
Body fields
|
Field |
Type |
Default |
Description |
|---|---|---|---|
|
|
string |
— |
One-time code entered by the user. When present, Monosign verifies the code instead of returning the status. Used in code-entry mode with Monofor Identity. |
Each status call waits up to about five seconds and returns one of the outcomes below. The Identity Provider repeats the call while it receives 499, until a final outcome is returned or the transaction expires.
|
Outcome |
|
Identity Provider action |
|---|---|---|
|
Approved |
|
Grant access. |
|
Rejected |
|
Deny access. |
|
Still pending |
|
Call again. |
|
Expired |
|
Deny access, or start a new request from step 3. |
|
Cancelled |
|
Deny access. |
|
Failed |
|
Deny access; see the Troubleshooting section. |
Because each call already waits up to five seconds on the server, no additional client-side delay is required between calls. Requests are rate-limited per transaction; a single sequential polling loop stays well within the limit.
Variant B — Without a Monosign Session
Use this variant when the Identity Provider validates the user's password itself (for example against its own directory) and needs Monosign only for the second factor. The login call is skipped, and no Monosign-SessionId header is sent. Instead, every MFA call carries "isPasswordless": true, and Monosign identifies the user from the UserAuthenticatorId.
Responsibility: In this variant Monosign does not verify the user's password. The Identity Provider must complete its own primary authentication before starting the MFA request.
Flow
|
Step |
Identity Provider calls |
Body |
Keep for later |
|---|---|---|---|
|
B1 |
|
— |
|
|
B2 |
|
|
|
|
B3 |
|
|
— |
All calls carry only the Monosign-AppId and Monosign-AppKey headers.
B1 — Select the user's authenticator
GET {ApiUrl}/v1.3/mfa/options/by-username/{UserName}
Monosign-AppId: {AppId}
Monosign-AppKey: {AppKey}
The response and the selection rule are identical to step 2. When the user is known by its Monosign ID, GET {ApiUrl}/v1.3/mfa/options/{UserId} can be used instead. If user names are not unique across user sources, add ?source={UserSourceName}. Code 10129 means the user was not found.
B2 — Create the verification request
Because there is no login call, the end user's client details are sent here. Monosign records them for the request and shows them in the approval request.
POST {ApiUrl}/v1.3/mfa/request/{UserAuthenticatorId}
Monosign-AppId: {AppId}
Monosign-AppKey: {AppKey}
Content-Type: application/json
{
"isPasswordless": true,
"userIp": "203.0.113.24",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
"deviceType": 1,
"operatingSystem": "Windows",
"operatingSystemVersion": "11",
"deviceName": "Corporate VPN"
}
Body fields — in addition to those of step 3
|
Field |
Type |
Default |
Description |
|---|---|---|---|
|
|
boolean |
|
Required in this variant. |
|
|
GUID |
— |
Optional safeguard. When sent, Monosign rejects the call if the authenticator belongs to a different user. |
|
|
string |
— |
IP address of the end user. Recorded and shown in the approval request. Recommended. |
|
|
string |
— |
User agent of the end user's browser or client. |
|
|
integer |
|
|
|
|
string |
— |
Operating system of the end user. |
disablePushRequest, disableResponseWait, expiresInMinutes and deviceName work exactly as in step 3, and the responses are the same: 10117, 10118, 499 with UsageId, or 10120 in code-entry mode.
B3 — Retrieve the result or submit the code
POST {ApiUrl}/v1.3/mfa/request/{UserAuthenticatorId}/{UsageId}
Monosign-AppId: {AppId}
Monosign-AppKey: {AppKey}
Content-Type: application/json
{
"isPasswordless": true
}
Repeat while the response is 499, as in step 4. In code-entry mode with Monofor Identity, add "code": "{Code entered by the user}". For the API Service Authenticator in code-entry mode, submit the code to v1.3/mfa/verify as described in Code-Entry Mode; that endpoint does not require a session.
Important:
isPasswordless: truemust be sent on every call of this variant. Without it, and without aMonosign-SessionIdheader, Monosign rejects the call because no session is present.
Session created by Monosign
To track the request, Monosign opens a session for the user in the Identity Provider's application. On approval (10117), its identifier is returned in Data.SessionId. The Identity Provider may ignore it; it expires according to the application's Session Timeout.
Code-Entry Mode (disablePushRequest)
In code-entry mode, Monosign creates the verification transaction but sends nothing to the user's device. The user obtains a one-time code from the authenticator application and enters it in the Identity Provider's sign-in page, and the Identity Provider submits that code to Monosign.
When to use it
-
The user's device cannot receive push notifications (no connectivity, notification access disabled, restricted network).
-
The organisation's policy requires the user to type a code rather than approve a prompt.
-
The Identity Provider offers the user an "Enter a code instead" option on its MFA page.
Behaviour by authenticator
|
Authenticator |
Effect of |
Where the user finds the code |
Available from |
|---|---|---|---|
|
Monofor Identity ( |
No push notification is sent. |
One-time code shown in the Monofor Identity app. |
v2026.09 |
|
API Service Authenticator ( |
Monosign does not call the external service's Request endpoint. |
The external service's own application. The code is checked through the service's Validation endpoint, or locally when Validate OTP First is enabled on the authenticator. |
v2026.10 |
|
Other authenticators |
The field is ignored and the authenticator behaves as usual. |
— |
— |
1. Create the transaction
Call step 3 with "disablePushRequest": true. Monosign responds immediately with code 10120:
{
"Message": "No need to create an action. Just type the code from your Identity app.",
"Code": 10120,
"Success": true,
"Data": {
"UsageId": "e3b9a7d2-4f1c-4c6b-8a0e-71d2f5c9b384",
"ExpirationDate": "2026-10-20T13:05:00"
}
}
Retain Data.UsageId. ExpirationDate is in UTC; the user must enter the code before this time.
2a. Submit the code — Monofor Identity
Send the code to the status endpoint from step 4:
POST {ApiUrl}/v1.3/mfa/request/{UserAuthenticatorId}/{UsageId}
Monosign-AppId: {AppId}
Monosign-AppKey: {AppKey}
Monosign-SessionId: {SessionId}
Content-Type: application/json
{
"code": "482913"
}
2b. Submit the code — API Service Authenticator
The code is passed to the external service through the input fields designed on the authenticator (Service Settings → Input Requirement). Submit it to the verification endpoint, keyed by the input field name:
POST {ApiUrl}/v1.3/mfa/verify
Monosign-AppId: {AppId}
Monosign-AppKey: {AppKey}
Monosign-SessionId: {SessionId}
Content-Type: application/json
{
"userId": "9ca71310-eddf-4976-b684-f9204fd62b74",
"userAuthenticatorId": "b47d6b96-2adc-412b-b6c8-f70eb2017eca",
"userAuthenticatorUsageId": "e3b9a7d2-4f1c-4c6b-8a0e-71d2f5c9b384",
"payload": {
"Form": { "otp": "482913" }
}
}
When Validate OTP First is enabled on the authenticator, also send the code in code; Monosign checks it locally first and calls the external service only if the local check fails.
Body fields — v1.3/mfa/verify
|
Field |
Type |
Description |
|---|---|---|
|
|
GUID |
Required. |
|
|
GUID |
Required. |
|
|
GUID |
Required. |
|
|
string |
One-time code entered by the user. Required for Monofor Identity; for the API Service Authenticator, used only when Validate OTP First is enabled. |
|
|
object |
API Service Authenticator only. Values of the authenticator's input fields, keyed by field name (in the example, |
Results
|
Outcome |
|
Identity Provider action |
|---|---|---|
|
Code accepted |
|
Grant access. |
|
Code incorrect |
|
Ask the user to re-enter the code. |
|
Transaction expired |
|
Start a new request. |
|
Transaction already used |
|
Start a new request. |
|
External service rejected the code |
|
Deny access, or start a new request. |
Limitation for the API Service Authenticator
Code-entry mode is not available when the external service's validation depends on an identifier that only its Request call returns (an Identifier Path is configured under Request Settings). In that case Monosign rejects the create call with HTTP 400 and code 10300, and the Identity Provider should fall back to approval mode for that authenticator.
Response Code Reference
|
Code |
Meaning |
Final? |
|---|---|---|
|
|
Primary sign-in succeeded |
— |
|
|
Active authenticators listed |
— |
|
|
User not found (authenticator list) |
Yes |
|
|
User has no active authenticator |
Yes |
|
|
Verification succeeded |
Yes |
|
|
User rejected the request |
Yes |
|
|
Waiting for the user (HTTP 499) |
No |
|
|
Request sent without waiting ( |
No |
|
|
Code-entry transaction created; awaiting code |
No |
|
|
Code incorrect |
No — user may retry |
|
|
Transaction expired |
Yes |
|
|
Transaction already processed |
Yes |
|
|
Transaction cancelled |
Yes |
|
|
Transaction failed or errored |
Yes |
|
|
Transaction not found |
Yes |
|
|
Transaction does not belong to this user, authenticator or session |
Yes |
|
|
External service rejected or could not process the request (API Service Authenticator) |
Yes |
|
|
Code-entry mode not supported by this service configuration |
Yes |
Integrations built against releases earlier than v2026.10 may have handled
10116for external service failures. From v2026.10 this condition returns10299; accept both during the transition.
Recommended Practices
-
Treat every non-final result as "not authenticated". Grant access only on
10117. -
Bind the flow to one session (Variant A). Use the same
SessionIdfor steps 2–4. Monosign rejects status checks made with a different session (10113). -
Forward the end user's details. Send
userIp,userAgentanddeviceTypein step 1 (Variant A) or step B2 (Variant B) so that the approval request and the audit trail show the user's real origin, not the Identity Provider's server. -
Name the requesting system. Use
deviceNamein step 3 so that users can recognise legitimate requests. -
Offer a fallback. When approval mode stays pending, consider offering the user code-entry mode in the same sign-in page instead of restarting the flow.
-
Do not log secrets. Exclude the App Key, passwords and one-time codes from Identity Provider logs.
Troubleshooting
|
Symptom |
Likely cause |
Resolution |
|---|---|---|
|
|
The user has no enrolled or enabled authenticator. |
Enrol the user, or verify that the authenticator is enabled under Security → Authenticators. |
|
HTTP 403 on any call |
The application's API key lacks a required permission. |
Grant the permissions listed under Prerequisites. |
|
Status checks return |
A different session or authenticator ID is used than in step 3. |
Reuse the |
|
Status checks keep returning |
The user did not respond in time, or the push notification did not reach the device. |
Ask the user to check device connectivity, or offer code-entry mode. |
|
The approval request shows the Identity Provider's IP address |
The login request did not carry the end user's details. |
Send |
|
|
The external service returned an error or a response that did not match the configured validation. |
Review the user's audit entries and use Test on the authenticator to reproduce the call. |
|
Variant B calls are rejected with HTTP 400 |
|
Send |
|
|
The external service requires its Request call for validation. |
Use approval mode for this authenticator. |
|
API Service Authenticator code is never accepted in code-entry mode |
The code was sent in |
Send it in |