One-time codes
A case created with multiFactorType asks each participant for a one-time code before it shows the document
or accepts a signature. The code proves the participant has the mailbox or phone you named; the signing link
alone is not enough.
{ "title": "Loan agreement 2026/0917", "validUntil": "2026-10-15T23:59:59Z", "multiFactorType": "EMAIL", "participants": [{"firstName": "Jana", "lastName": "Nováková", "email": "jana.novakova@example.com"}]}TrustInk’s signing page runs this flow for you. You only need the details below if you build your own signing page on the participant routes.
-
The signing page loads
GET /participant/{participantId}/caseDefinition. Without a session it answersotpRequired: trueand leaves outoriginalDocumentandsignedDocument. -
POST /participant/{participantId}/otp(participant token) sends a six-digit code by the case’smultiFactorTypeand answers{sentTo, channel, expiresAt}:{"sentTo": "j***@example.com", "channel": "EMAIL", "expiresAt": "2026-09-24T10:10:00.000Z"}A code is valid ten minutes; a new one replaces the previous one.
-
POST /participant/{participantId}/otp/verifywith{"code": "004711"}answers{otpSession, expiresAt}. -
The page sends
Trustink-Otp-Session: <otpSession>with every participant request for the next 30 minutes: the case definition then has its document links, and the signature and the signed document are accepted. After that, or on another device, the participant verifies a new code.
Answers
Section titled “Answers”| Endpoint | Status | code |
When |
|---|---|---|---|
POST .../otp |
409 | OTP_NOT_REQUIRED |
the case has no multiFactorType |
| 409 | OTP_NO_DESTINATION |
the participant has no email address or phone number for the channel | |
| 429 | OTP_RATE_LIMITED |
five codes were sent in the last hour; retryAfterSeconds says when the next one can go |
|
| 501 | OTP_CHANNEL_UNAVAILABLE |
SMS, until the SMS channel is available |
|
POST .../otp/verify |
400 | OTP_INVALID |
wrong code; attemptsLeft says how many tries remain |
| 423 | OTP_LOCKED |
the fifth wrong code, and any try after it, until a new code is requested | |
| 409 | OTP_NOT_REQUESTED |
no code was sent, or the last one was used | |
| 409 | OTP_EXPIRED |
the code is older than ten minutes, or a newer one replaced it | |
POST .../signature, GET .../signedDocument |
401 | OTP_REQUIRED |
the case has multiFactorType and the request has no valid Trustink-Otp-Session |
A code that is not six digits answers 400 with validationErrors and does not count as a try. Both code
endpoints answer 410 CASE_NOT_ACTIVE or LINK_EXPIRED like the other participant endpoints.
What is stored
Section titled “What is stored”TrustInk never stores the code itself, only a salted hash, compared in constant time; a right code is removed once used. The audit trail records that a code was sent (to the masked address), each wrong try and the successful verification, never the code. The evidence summary shows when each participant verified.