OTP Based e-Sign API
Start an OTP e-Sign session for a PDF, send the signer to Digitap's signing page, then fetch the signed document.
What it verifies
Collects an OTP-authenticated electronic signature on a PDF (Digitap Digi-ClickWrap). You start a session with the PDF and signer details, send the person to the returned signing URL to verify OTP and sign, then fetch the signed PDF URL once they finish. PDFs are uploaded to Digitap for the session and are not kept by Techinnoverz.
Who uses it
- Lenders collecting signed loan agreements remotely
- Insurers and brokers signing proposals and mandates
- Marketplaces and HR platforms collecting signed onboarding documents
Start an OTP e-Sign session, upload the PDF, and get the signing URL.
https://verification.techinnoverz.com/api/v1/agreements/otp-esign/initiateStart an OTP e-Sign session, upload the PDF, and get the signing URL. This call is not billed.
Request body
documentstringrequiredUnsigned PDF as base64 (up to about 5 MB binary). Uploaded to Digitap for this signing session.
docClassIdstringrequiredDigitap ClickWrap document class id for your account (provided by Digitap).
firstNamestringrequiredSigner first name. Appears as signed-by on the document.
lastNamestringrequiredSigner last name.
mobilestringrequired10-digit Indian mobile that receives the OTP.
emailstringoptionalOptional. Signer email.
reasonstringoptionalOptional. Reason shown for the signature. Defaults to agreement.
redirectUrlstringrequiredhttps address the signer returns to after a successful signature.
errorUrlstringoptionalOptional. https address on failure. Defaults to redirectUrl.
sendNotificationbooleanoptionalOptional. When true, Digitap also notifies the signer about the signing link.
consentstringrequiredMust be "Y". Records that the end customer consented to this check; logged against the transaction.
referenceIdstringoptionalYour own id, echoed back in the response for reconciliation.
Example requests
curl -X POST "https://verification.techinnoverz.com/api/v1/agreements/otp-esign/initiate" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"document": "JVBERi0xLjQKMSwwIG9iajw8Pj5lbmRvYmoKdHJhaWxlcjw8Pj4KJSVFT0Y=",
"docClassId": "EI1OTPxxxxx",
"firstName": "John",
"lastName": "Doe",
"mobile": "9993059933",
"email": "john@gmail.com",
"reason": "security",
"redirectUrl": "https://example.com/esign/done",
"errorUrl": "https://example.com/esign/error",
"sendNotification": false,
"consent": "Y",
"referenceId": "app_20915"
}'const res = await fetch("https://verification.techinnoverz.com/api/v1/agreements/otp-esign/initiate", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"document": "JVBERi0xLjQKMSwwIG9iajw8Pj5lbmRvYmoKdHJhaWxlcjw8Pj4KJSVFT0Y=",
"docClassId": "EI1OTPxxxxx",
"firstName": "John",
"lastName": "Doe",
"mobile": "9993059933",
"email": "john@gmail.com",
"reason": "security",
"redirectUrl": "https://example.com/esign/done",
"errorUrl": "https://example.com/esign/error",
"sendNotification": false,
"consent": "Y",
"referenceId": "app_20915"
}),
});
const result = await res.json();import requests
res = requests.post(
"https://verification.techinnoverz.com/api/v1/agreements/otp-esign/initiate",
headers={"x-api-key": "YOUR_API_KEY"},
json={
"document": "JVBERi0xLjQKMSwwIG9iajw8Pj5lbmRvYmoKdHJhaWxlcjw8Pj4KJSVFT0Y=",
"docClassId": "EI1OTPxxxxx",
"firstName": "John",
"lastName": "Doe",
"mobile": "9993059933",
"email": "john@gmail.com",
"reason": "security",
"redirectUrl": "https://example.com/esign/done",
"errorUrl": "https://example.com/esign/error",
"sendNotification": False,
"consent": "Y",
"referenceId": "app_20915",
},
timeout=15,
)
result = res.json()200 response — status "initiated"
{
"status": "initiated",
"data": {
"esignRequestId": "134417342590497602",
"docTransactionId": "943e0eb1-da0e-4b82-bc9c-6c87934565",
"signingUrl": "https://sdksb.digitap.work/clickwrap/clickwrap.html?txnid=134417342590497602&redirect_url=https%3A%2F%2Fexample.com%2Fesign%2Fdone&error_url=https%3A%2F%2Fexample.com%2Fesign%2Ferror",
"previewUrl": "https://xxxx.s3.ap-south-1.amazonaws.com/preview.pdf"
},
"source": "provider",
"dataAsOf": "2026-11-03T09:30:00.000Z",
"referenceId": "app_20915",
"billing": {
"charged": false,
"walletBalance": "12450.00"
},
"txnId": "txn_8c31ba90",
"livemode": true
}Fields returned in data
esignRequestIdstringoptionalDigitap enterprise transaction id. Quote this when fetching the signed document.
docTransactionIdstringoptionalDigitap document transaction id for this upload.
signingUrlstringoptionalSend the signer here to complete OTP verification and sign.
previewUrlstringoptionalShort-lived preview URL of the uploaded PDF when Digitap provides one. null otherwise.
Fetch the signed PDF URL once the person has completed OTP e-Sign.
https://verification.techinnoverz.com/api/v1/agreements/otp-esign/fetchFetch the signed PDF URL once the person has completed OTP e-Sign. This call is billed when it returns a successful result.
Request body
esignRequestIdstringrequiredThe esignRequestId returned when the session was started.
consentstringrequiredMust be "Y". Records that the end customer consented to this check; logged against the transaction.
referenceIdstringoptionalYour own id, echoed back in the response for reconciliation.
Example requests
curl -X POST "https://verification.techinnoverz.com/api/v1/agreements/otp-esign/fetch" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"esignRequestId": "134417342590497602",
"consent": "Y",
"referenceId": "app_20915"
}'const res = await fetch("https://verification.techinnoverz.com/api/v1/agreements/otp-esign/fetch", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"esignRequestId": "134417342590497602",
"consent": "Y",
"referenceId": "app_20915"
}),
});
const result = await res.json();import requests
res = requests.post(
"https://verification.techinnoverz.com/api/v1/agreements/otp-esign/fetch",
headers={"x-api-key": "YOUR_API_KEY"},
json={
"esignRequestId": "134417342590497602",
"consent": "Y",
"referenceId": "app_20915",
},
timeout=15,
)
result = res.json()200 response — status "found"
{
"status": "found",
"data": {
"esignRequestId": "134417342590497602",
"documentUrl": "https://xxxx.s3.ap-south-1.amazonaws.com/signed.pdf",
"signed": true
},
"source": "provider",
"dataAsOf": "2026-11-03T09:30:00.000Z",
"referenceId": "app_20915",
"billing": {
"charged": true,
"walletBalance": "12450.00"
},
"txnId": "txn_8c31ba90",
"livemode": true
}Fields returned in data
esignRequestIdstringoptionalThe e-Sign request that was fetched.
documentUrlstringoptionalShort-lived download URL for the signed PDF (about 30 minutes).
signedbooleanoptionalAlways true on a successful fetch. An unfinished session is answered as source unavailable, not charged.
Error codes
Errors return a non-200 HTTP status and the same envelope, so your retry logic and your reconciliation agree.
400 INVALID_INPUTA field is missing or malformed. The details list each problem. Not charged.
401 INVALID_KEYThe key is missing, revoked, or not a key we issued.
402 WALLET_EMPTYYour available balance is below the price of this call. Top up and retry. Not charged.
403 PRODUCTION_NOT_ENABLEDA live key was used before live access was enabled for your account. Use a test key, or ask us to enable live access.
403 TEST_NOT_ENABLEDA test key was used before test (UAT) access was enabled for your account. Ask us to enable it.
403 API_NOT_ENABLEDThis check is switched off for your account, or is not available for live use yet.
403 IP_NOT_ALLOWEDThe key is restricted to certain IP addresses and this call came from another.
403 ACCOUNT_SUSPENDEDThe account is suspended. Contact support.
404 NO_DATAA valid request, but no record was found. Not charged.
404 ROUTE_NOT_FOUNDThere is no endpoint at that path.
405 METHOD_NOT_ALLOWEDUse the method shown in the Allow header.
409 IDEMPOTENCY_IN_PROGRESSA call with the same Idempotency-Key is still being processed. Retry after the delay in Retry-After.
413 PAYLOAD_TOO_LARGEThe request body is too large. Not charged.
415 UNSUPPORTED_MEDIA_TYPESend the request with Content-Type: application/json.
422 IDEMPOTENCY_KEY_REUSEDThis Idempotency-Key was already used with a different request body. Use a new key for a new request.
429 RATE_LIMITEDToo many requests or too many at once. Wait for the Retry-After delay.
500 INTERNAL_ERRORSomething went wrong on our side. Not charged. Quote the txnId if you contact support.
502 SOURCE_ERRORThe data source returned an error. Not charged.
503 SOURCE_DOWNThe data source is unavailable right now. Not charged. Retry after the delay in Retry-After.
504 SOURCE_TIMEOUTThe data source did not answer in time. Not charged.
{
"status": "error",
"error": {
"code": "NO_DATA",
"message": "No record found for the given input. Not charged."
},
"billing": {
"charged": false
},
"txnId": "txn_8c31ba91",
"livemode": true
}Billing
You are charged only on successful hits. Failed, no-data and error calls are free.
Every response carries billing.charged and your remaining billing.walletBalance. Per-hit pricing is set on your rate card; book a demo to get yours.
All API documentation · OpenAPI specification · Docs as plain text
