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.
- AuthenticateSigner — Neotek asks Nafath to verify that person. You get back a
NafathCode, a number you show on screen. - The signatory opens their Nafath app and taps the matching number, proving their identity to the national system.
- CheckStatus — poll until it reports the signatory as authenticated.
- SignDocuments — sign the document.
Every call takes the signatory's POINumber in the path and carries the same headers:
| Parameter | Required | Description |
|---|---|---|
authorization | Required | Bearer <ACCESS_TOKEN>, with scope digital_signature |
x-request-id | Optional | an RFC 4122 UUID you generate; quote it when raising a support ticket |
sub-client | Optional | identifies 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
https://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:
| Parameter | Required | Description |
|---|---|---|
POINumber | Required | the signatory's national ID or iqama number |
Query parameters:
| Parameter | Required | Description |
|---|---|---|
AssuranceLevel | Optional | Medium, the default, or High |
curl -X POST 'https://test.api.neotek.sa/neotek/neotek-sandbox/digital-signature-smart/v1/authenticate-signer/{POINumber}' \ -H 'Authorization: Bearer <ACCESS_TOKEN>'Response
{ "Data": { "NafathCode": 28 }}CheckStatus
https://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:
| Parameter | Required | Description |
|---|---|---|
POINumber | Required | the signatory's national ID or iqama number |
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)
{ "Data": { "TokenValidTill": "string", "Status": "string" }}SignDocuments
https://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:
| Parameter | Required | Description |
|---|---|---|
POINumber | Required | the 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:
| Parameter | Required | Description |
|---|---|---|
UserConsentObtained | Required | must be true. You are legally undertaking that you informed the user and took explicit consent |
MobileNumber | Optional | the signatory's mobile number |
Email | Optional | the signatory's email address |
Data.DocumentDetails is an array, one entry per document. It is identical to the Lite variants:
| Parameter | Required | Description |
|---|---|---|
Document | Required | the PDF as a base64 string |
SignerInformation.SignedBy | Required | name of the signer |
SignerInformation.AllowMultipleSignings | Required | whether the document can be signed more than once |
SignerInformation.Location | Optional | — |
SignerInformation.Reason | Optional | — |
SigningFormat | Optional | omit the whole block to accept the defaults listed on the Sadq page |
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
{ "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
| Code | Status | Cause and Action |
|---|---|---|
400 | Bad Request | Missing required field, or a value that fails validation — check Code and Path in the body |
401 | Unauthorized | Bearer token missing, expired, or invalid |
403 | Forbidden | Token doesn't carry the digital_signature scope |
404 | Not Found | No resource matches the identifier provided |
405 | Method Not Allowed | HTTP verb not supported on that path |
406 | Not Acceptable | Accept header doesn't match a supported representation |
415 | Unsupported Media Type | Content-Type missing or wrong |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Server error — retry with backoff, quote your x-request-id |