Back to neotek ↗Get Sandbox Access

Smart Signature

Smart Signature verifies the signatory through Nafath, the national identity system, instead of taking a full KYC block. The same POINumber runs through every call, which is what ties the signature to the person Nafath verified.

  1. AuthenticateSigner — Neotek asks Nafath to verify that person. You get back a NafathCode, a number you show on screen.
  2. The signatory opens their Nafath app and taps the matching number, proving their identity to the national system.
  3. CheckStatus — poll until it reports the signatory as authenticated.
  4. SignDocuments — sign the document.

Every call takes the signatory's POINumber in the path and carries the same headers:

ParameterRequiredDescription
authorizationRequiredBearer <ACCESS_TOKEN>, with scope digital_signature
x-request-idOptionalan RFC 4122 UUID you generate; quote it when raising a support ticket
sub-clientOptionalidentifies the downstream consumer when one client fronts several

content-type: application/json is required on SignDocuments only, since it is the only call with a body.


AuthenticateSigner

POSThttps://test.api.neotek.sa/neotek/neotek-sandbox/digital-signature-smart/v1/authenticate-signer/{POINumber}

Sandbox URL · in Production call https://api.neotek.sa

Starts authentication and returns the Nafath code the signatory has to confirm in the Nafath app. No body.

Path parameters:

ParameterRequiredDescription
POINumberRequiredthe signatory's national ID or iqama number

Query parameters:

ParameterRequiredDescription
AssuranceLevelOptionalMedium, the default, or High
cURL
curl -X POST 'https://test.api.neotek.sa/neotek/neotek-sandbox/digital-signature-smart/v1/authenticate-signer/{POINumber}' \  -H 'Authorization: Bearer <ACCESS_TOKEN>'

Response

JSON
{  "Data": {    "NafathCode": 28  }}

CheckStatus

POSThttps://test.api.neotek.sa/neotek/neotek-sandbox/digital-signature-smart/v1/check-status/{POINumber}

Sandbox URL · in Production call https://api.neotek.sa

Reports whether the signatory has confirmed the Nafath code yet. No query parameters and no body.

Path parameters:

ParameterRequiredDescription
POINumberRequiredthe signatory's national ID or iqama number
cURL
curl -X POST 'https://test.api.neotek.sa/neotek/neotek-sandbox/digital-signature-smart/v1/check-status/{POINumber}' \  -H 'Authorization: Bearer <ACCESS_TOKEN>'

Response (schema — both fields come back as strings)

JSON
{  "Data": {    "TokenValidTill": "string",    "Status": "string"  }}

SignDocuments

POSThttps://test.api.neotek.sa/neotek/neotek-sandbox/digital-signature-smart/v1/sign-documents/{POINumber}

Sandbox URL · in Production call https://api.neotek.sa

Signs the PDF once the signatory is authenticated.

Path parameters:

ParameterRequiredDescription
POINumberRequiredthe signatory's national ID or iqama number

Body — everything sits under a top-level Data object.

Data.KYCInformation carries the consent and how to reach the signatory:

ParameterRequiredDescription
UserConsentObtainedRequiredmust be true. You are legally undertaking that you informed the user and took explicit consent
MobileNumberOptionalthe signatory's mobile number
EmailOptionalthe signatory's email address

Data.DocumentDetails is an array, one entry per document. It is identical to the Lite variants:

ParameterRequiredDescription
DocumentRequiredthe PDF as a base64 string
SignerInformation.SignedByRequiredname of the signer
SignerInformation.AllowMultipleSigningsRequiredwhether the document can be signed more than once
SignerInformation.LocationOptional
SignerInformation.ReasonOptional
SigningFormatOptionalomit the whole block to accept the defaults listed on the Sadq page
cURL
curl -X POST 'https://test.api.neotek.sa/neotek/neotek-sandbox/digital-signature-smart/v1/sign-documents/{POINumber}' \  -H 'Authorization: Bearer <ACCESS_TOKEN>' \  -H 'Content-Type: application/json' \  -d '{  "Data": {    "KYCInformation": {      "UserConsentObtained": true,      "MobileNumber": "<mobile>",      "Email": "<email>"    },    "DocumentDetails": [      {        "Document": "<base64 PDF>",        "SignerInformation": {          "SignedBy": "Mohammed Ali",          "Location": "Riyadh",          "Reason": "Authentication.",          "AllowMultipleSignings": true        },        "SigningFormat": {          "PageSigningMode": "Specify",          "SigningPosition": "CenterRight",          "PagesToBeSigned": "1,2",          "SigningCoordinatesPerPage": "1,94,575,244,650;3,75,695,225,770",          "SignatureHasImage": "EmdhaLogo",          "SignatureImagePosition": "LeftOfText",          "SignatureContent": "This document is legally signed by a trusted CA.",          "SignatureFontSize": 8        }      }    ]  }}'

Response — the request is echoed back with Document replaced by the signed PDF, base64-encoded

JSON
{  "Data": {    "KYCInformation": {      "UserConsentObtained": true,      "MobileNumber": "5123456789",      "Email": "example@test.com"    },    "DocumentDetails": [      {        "Document": "JVBERi0xLjcNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFu…",        "SignerInformation": {          "SignedBy": "Ahmed Mohamed Ahmed",          "Location": "Riyadh",          "Reason": "Authentication.",          "AllowMultipleSignings": true        },        "SigningFormat": {          "PageSigningMode": "All",          "SigningPosition": "CenterRight",          "PagesToBeSigned": "1,2",          "SigningCoordinatesPerPage": "1,94,575,244,650;all,75,695,225,770",          "SignatureHasImage": "NoImage",          "SignatureImage": "JVBERi0xLjcNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFu…",          "SignatureImagePosition": "LeftOfText",          "SignatureContent": "This document is legally signed by a trusted CA.",          "SignatureFontSize": 8        }      }    ]  }}

Errors

CodeStatusCause and Action
400Bad RequestMissing required field, or a value that fails validation — check Code and Path in the body
401UnauthorizedBearer token missing, expired, or invalid
403ForbiddenToken doesn't carry the digital_signature scope
404Not FoundNo resource matches the identifier provided
405Method Not AllowedHTTP verb not supported on that path
406Not AcceptableAccept header doesn't match a supported representation
415Unsupported Media TypeContent-Type missing or wrong
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error — retry with backoff, quote your x-request-id