# Getting started (/getting-started)
The end-to-end Esign flow — upload a PDF, initialize a session, track signers, and download the signed document.
A complete Esign flow has four steps:
1. **Upload** the PDF to be signed.
2. **Initialize** an Esign session with one or more signers and their
signature positions.
3. **Track** signer status until the session completes.
4. **Download** the final signed PDF.
All examples below use the sandbox base URL
`https://esign-api-v2.surepass.app` and require the
`Authorization: Bearer YOUR_ACCESS_TOKEN` header.
## 1. Upload the PDF [#1-upload-the-pdf]
Upload the document (max 50MB) with
[Upload PDF](/api-reference/files/uploadPdf):
```bash
curl --location 'https://esign-api-v2.surepass.app/api/v1/files/upload-pdf' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--form 'file=@"/path/to/your/file.pdf"'
```
**Success response:**
```json
{
"data": {
"user_id": "user_xxx",
"client_id": "file_xxx",
"name": "Form_26.pdf",
"mime": "application/pdf"
},
"status_code": 200,
"message": "Success",
"success": true
}
```
The `client_id` (e.g. `file_xxx`) is the `file_id` you pass when
initializing the Esign.
**Error responses:**
| Message Code | Description |
| ---------------- | ------------------------------------------ |
| `file_empty` | No file uploaded or file is empty. |
| `file_too_large` | Uploaded file exceeds maximum size (50MB). |
| `invalid_pdf` | File is not a valid PDF. |
## 2. Initialize the Esign session [#2-initialize-the-esign-session]
Start the session with
[Initialize Esign](/api-reference/esign/initializeEsign). Each signer gets
one or more `elements` — the fields placed on the document:
```json title="POST /api/v1/esign/initialize-esign"
{
"sign_type": "aadhaar",
"auth_mode": 1,
"file_id": "file_onwEUoMxnWhewUzodZeW",
"folder": "lehddsun",
"expiry_seconds": 7200,
"email_template_overrides": {
"company_name": "Acme Corp",
"legal_name": "Acme Corporation Pvt Ltd.",
"support_email": "support@acme.com"
},
"signers": [
{
"full_name": "Test1",
"email": "test1@example.com",
"reason": "Test1 Sign",
"verify_email": false,
"verify_mobile": false,
"name_match": true,
"face_ref_file_id": "file_faceReferenceImage",
"face_match_threshold": 80,
"face_max_attempts": 5,
"notification": {
"invite": true,
"completion": true,
"whatsapp": true
},
"elements": [
{
"x": 100,
"y": 10,
"page": 0,
"type": "signature",
"allow_wet_signature": false
}
]
},
{
"full_name": "Test2",
"email": "test2@example.com",
"mobile": "2222222222",
"reason": "Test2 Sign",
"verify_email": true,
"verify_mobile": false,
"notification": {
"invite": false,
"completion": true,
"whatsapp": false
},
"elements": [
{
"x": 100,
"y": 10,
"page": 0,
"type": "signature"
}
]
}
]
}
```
**Success response:**
```json
{
"data": {
"client_id": "esign_lpHGXloMQwkNPPXvBHCH",
"status": "pending",
"file_id": "file_onwEUoMxnWhewUzodZeW",
"signers": [ "..." ]
},
"status_code": 200,
"message": "Success",
"success": true
}
```
Each signer in the response includes an `invite_url` the signer opens to
complete signing. Signers are also notified by email/SMS unless disabled
via `notification`.
### Notes [#notes]
* `page` is zero-indexed: use `0` for the first page, `1` for the second,
and so on.
* `expiry_seconds`: min = 3600 (1 hour), max = 2592000 (30 days).
* `sign_type`: `"aadhaar"` (OTP-based) or `"suresign"` (auto-signing by
Surepass).
* Multiple elements can be added per signer on different pages.
* `signers[].name_match`: optional boolean for Aadhaar signers. If `true`,
the backend compares signer `full_name` with the Aadhaar certificate
name and returns a fuzzy `score` from `0` to `100`. Ignored for
non-Aadhaar signers.
* `signers[].face_ref_file_id`: optional reference image file ID. Providing
it enables face verification for that signer. Upload the JPEG or PNG
reference image through
[Upload asset](/api-reference/files/uploadAsset) before initialization
and pass the returned `client_id`.
* `signers[].face_match_threshold`: minimum face-match score from `50` to
`100`; defaults to `80`. `signers[].face_max_attempts`: maximum
face-verification attempts from `1` to `10`; defaults to `5`. Both are
only enforced when `face_ref_file_id` is provided.
* `allow_wet_signature`: per `signature` element flag, defaults to `false`.
When false, any signature `value` asset ID is stripped during request
parsing. Set it to `true` only for signature fields that intentionally
allow a wet-signature image.
### Email template overrides [#email-template-overrides]
| Field | Type | Required | Description |
| --------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------- |
| `company_name` | string | No | Custom company name shown in email. |
| `legal_name` | string | No | Legal entity name shown in footer (default: "Surepass Technologies Pvt Ltd.") |
| `support_email` | string | No | Support email shown in footer (default: "[techsupport@surepass.io](mailto:techsupport@surepass.io)") |
When `company_name` is provided, the default Surepass logo and branding
are hidden, allowing white-label emails.
### Notification [#notification]
| Field | Type | Required | Default | Description |
| ------------ | ------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `invite` | boolean | No | `true` | Send signer invite communication by email/SMS. |
| `completion` | boolean | No | `true` | Send completed-document communication after the Esign finishes successfully. |
| `whatsapp` | boolean | No | `false` | Also deliver the invite and the completed signed document over WhatsApp. Requires the signer to have a `mobile`; silently ignored otherwise. |
Set `invite`/`completion` to `false` to skip that communication for that
signer. `whatsapp` is opt-in and only sends when `invite`/`completion`
(respectively) are also enabled. Omit `notification` entirely to keep
`invite`/`completion` enabled and `whatsapp` disabled.
### Field types supported in `signers.elements` [#field-types-supported-in-signerselements]
| Type | Description |
| ----------- | ----------------------------------- |
| `signature` | Signature input box |
| `image` | Preloaded image (uploaded as asset) |
| `text` | Text field |
| `date` | Date field with formatting support |
| `initials` | Initials block |
**Error responses:**
| Message Code | Description |
| ------------------- | -------------------------------------------------- |
| `missing_fields` | Required fields are missing in the request. |
| `invalid_file_id` | The provided file ID is invalid or does not exist. |
| `invalid_sign_type` | Must be either `"aadhaar"` or `"suresign"`. |
| `unauthorized` | Invalid or expired access token. |
## 3. Track signer status [#3-track-signer-status]
Poll [Get signer details](/api-reference/esign/getEsignDetails) with the
Esign `client_id` from step 2:
```bash
curl --location --globoff 'https://esign-api-v2.surepass.app/api/v1/esign/details?client_id=esign_xxx' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
```
**Success response:**
```json
{
"data": {
"user_id": "user_kmJytYATMawoorDzWrUQ",
"client_id": "esign_PsLIyRDIHAqyFWhZqhDT",
"created_at": "2025-05-13T12:06:01.344000",
"sign_type": "aadhaar",
"file_id": "file_ugSBmcbzETuPjUqQLyts",
"allow_download": true,
"expiry_seconds": 7200,
"status": "completed",
"signers": [
{
"signer_id": "signer_vxgTyyOxYDhkhsIKQtda",
"full_name": "Test1",
"email": "test1@example.com",
"status": "completed",
"aadhaar_cert_details": {
"name": "Test Signer",
"country": "IN",
"state": "Delhi",
"pin_code": "110001",
"aadhaar": "0000",
"year_of_birth": "1990",
"gender": "M"
},
"name_match": {
"enforce": true,
"prefill_name": "Test1",
"signer_name": "Test Signer",
"score": 31.25,
"status": "completed"
},
"face_match": {
"enforce": true,
"ref_file_id": "file_faceReferenceImage",
"live_file_id": "file_faceLiveImage",
"match_threshold": 80,
"max_attempts": 5,
"liveness_score": 0.93,
"match_score": 87
},
"positions": {
"1": [{ "x": 100, "y": 200 }]
}
}
],
"signer_idx": 1
},
"status_code": 200,
"message": "Success",
"success": true
}
```
### Session lifecycle [#session-lifecycle]
A session starts as `pending`, moves through each signer in order
(`signer_idx` tracks whose turn it is), and becomes `completed` once every
signer has signed — or `expired` if `expiry_seconds` elapse first. The
signed PDF can be downloaded only after the status is `completed`.
### Response notes [#response-notes]
* `signers[].aadhaar_cert_details`: present for Aadhaar signers after
Aadhaar signing completes. It is `null` for non-Aadhaar signers and
pending Aadhaar signers.
* `signers[].name_match.status`: `pending` when requested and waiting for
Aadhaar cert details, `completed` when the score is calculated, and
`skipped` when not requested.
* `signers[].face_match.enforce`: `true` when initialization supplied
`face_ref_file_id`.
* `signers[].face_match.ref_file_id` / `live_file_id`: reference image and
successfully verified live image file IDs.
* `signers[].face_match.liveness_score` / `match_score`: scores from the
successful verification; each remains `null` until face verification
succeeds.
**Error response:**
```json
{
"data": null,
"status_code": 404,
"message": "Esign client with client_id 'esign_xxx' not found.",
"success": false
}
```
## 4. Download the signed PDF [#4-download-the-signed-pdf]
Once the session status is `completed`, fetch the document with
[Download signed PDF](/api-reference/esign/downloadSignedPdf):
```bash
curl --location --globoff 'https://esign-api-v2.surepass.app/api/v1/esign/download-pdf?client_id=esign_xxx' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
```
**Success response:**
```json
{
"data": {
"url": "https://test-esign-bucket.s3.amazonaws.com/..."
},
"status_code": 200,
"message": "Success",
"success": true
}
```
The `url` is a time-limited download link for the signed PDF.
**Error response** (session not finished yet):
```json
{
"data": null,
"status_code": 404,
"message": "Esign not completed.",
"success": false
}
```
---
# Surepass Esign API (/)
Digitally sign PDF documents with Aadhaar OTP or Suresign auto-signing.
The Surepass Esign API lets you upload PDF documents, collect legally valid
digital signatures from one or more signers, and download the completed
document. Signers can be verified with Aadhaar OTP, email/mobile OTP,
name matching, face matching, and geofencing.
## Base URLs [#base-urls]
| Environment | URL |
| ----------- | ---------------------------------------- |
| Sandbox | `https://esign-api-v2.surepass.app` |
| Production | `https://prod-esign-api-v2.surepass.app` |
## Authentication [#authentication]
All API requests require a bearer token in the `Authorization` header:
```http
Authorization: Bearer YOUR_ACCESS_TOKEN
```
Contact [techsupport@surepass.io](mailto:techsupport@surepass.io) to get
access credentials.
## Explore [#explore]
Upload a PDF, initialize an Esign session, and download the signed document.
Save reusable signer layouts and initialize Esign sessions from them.
Procure e-stamps and reserve them into Esign flows.
Full endpoint reference with schemas and an interactive playground.
---
# LLMs.txt (/llms)
AI-readable versions of this documentation for ChatGPT, Claude, Cursor, and other tools.
These docs ship in the [llms.txt](https://llmstxt.org) format — plain-text
versions of the entire documentation that AI assistants can read directly.
Point your AI tool at either file below (or paste the content into its
context) and it can answer questions about the Surepass Esign API using the
real, current documentation instead of guessing.
Both files are generated automatically from the same source as this site,
so they are always in sync with the API reference.
Compact index of every page — AI tools read this first to discover what
the docs contain.
The entire documentation as one plain-text file — guides plus pointers
to the API specification.
Machine-readable OpenAPI specification — the source of truth for every
endpoint, schema, example, and error code.
---
# Stamps (/stamps)
Procure e-stamps — order, track, and download stamp PDFs, and reserve them into Esign flows.
The Stamp APIs let you procure e-stamps and attach them to signing flows.
You look up the supported states and articles, place a procurement order,
poll until the stamps are issued, and then either download the stamp PDFs
or reserve them into an Esign session — where the stamp pages are prefixed
to your document before signing starts.
All stamp endpoints live under `/api/v1/stamps` and use the same base URLs
and `Authorization: Bearer YOUR_ACCESS_TOKEN` header as the rest of the API.
## How stamping works [#how-stamping-works]
1. [List state codes](/api-reference/stamps/listStateCodes) and
[List articles](/api-reference/stamps/listArticles) to find the
`state_code` and `article_id` you need.
2. [Create a stamp order](/api-reference/stamps/createStampOrder) with the
party names, duty amount, and quantity.
3. Poll [Get order status](/api-reference/stamps/getStampOrderStatus) (or
[List stamp orders](/api-reference/stamps/listStampOrders)) until the
stamps become `available`.
4. [Download the stamp PDF](/api-reference/stamps/downloadStampPdf), or
reserve the stamps into an Esign session (see below).
## 1. Find the state and article [#1-find-the-state-and-article]
Article IDs are per-state — fetch the article list for your state first:
```bash
curl --location 'https://esign-api-v2.surepass.app/api/v1/stamps/articles?state_code=AN' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
```
```json
{
"data": [
{
"article_id": "2337",
"article_code": "AFFIDAVIT",
"article_name": "Affidavit"
}
],
"status_code": 200,
"message": "Success",
"success": true
}
```
## 2. Create a procurement order [#2-create-a-procurement-order]
```json title="POST /api/v1/stamps/create-order"
{
"state_code": "AN",
"first_party_name": "Alice Test",
"second_party_name": "Bob Test",
"article_id": "2337",
"stamp_duty_amount": 10,
"consideration_amount": 1000.0,
"quantity": 1
}
```
The call is **asynchronous** — a successful response returns the order with
an empty `stamps` array while procurement runs in the background. Billing
charges `estamp_duty` (`stamp_duty_amount × quantity`) plus `estamp_fees`
(one per stamp); the request fails with `402` if the wallet balance is
insufficient.
Constraints: `stamp_duty_amount` must be a whole number (max 1,000,000),
`quantity` between 1 and 5000, party names up to 200 characters.
## 3. Track the order [#3-track-the-order]
Poll [Get order status](/api-reference/stamps/getStampOrderStatus) with the
order `client_id` (`stamp_...`). Once procurement completes, `stamps`
contains the issued instances and `status_counts` is populated:
```json
{
"data": {
"client_id": "stamp_pkHLyIzspuMWBMXizUSc",
"status_counts": {
"pending": 0,
"available": 1,
"reserved": 0,
"used": 0,
"rejected": 0
},
"stamps": [
{
"stamp_id": "stamp_inst_uNeOUwjMVGNFexnxFSCP",
"status": "available"
}
]
},
"status_code": 200,
"message": "Success",
"success": true
}
```
Each stamp instance moves through these statuses:
| Status | Meaning |
| ----------- | ----------------------------------------- |
| `pending` | Procurement is not completed yet |
| `available` | Issued and available for use or download |
| `reserved` | Temporarily reserved against another flow |
| `used` | Already consumed |
| `rejected` | Procurement was rejected by the vendor |
`stamps[].stamp_id` (`stamp_inst_...`) is the public **instance** ID — use
it for downloads and explicit reservation. The order ID (`stamp_...`) is
only for `order-status` and `list`.
## 4. Download the stamp PDF [#4-download-the-stamp-pdf]
[Download stamp PDF](/api-reference/stamps/downloadStampPdf) returns a
short-lived signed URL for a completed (`available`) stamp instance — PDF
bytes are not streamed through the API:
```bash
curl --location 'https://esign-api-v2.surepass.app/api/v1/stamps/download-pdf?client_id=stamp_inst_uNeOUwjMVGNFexnxFSCP' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
```
## Using stamps in an Esign [#using-stamps-in-an-esign]
Stamps can be reserved directly into
[Initialize Esign](/api-reference/esign/initializeEsign) (and
[Initialize Esign from template](/api-reference/templates/initializeFromTemplate))
through the `stamp` object. On success, the reserved stamp PDFs are prefixed
to the uploaded document before the signing flow starts.
### Mode 1 — reserve by search [#mode-1--reserve-by-search]
The backend picks matching stamps from your `available` inventory:
```json title="POST /api/v1/esign/initialize-esign (excerpt)"
{
"file_id": "file_gXvuxRnKgdRWEHAXpnXw",
"signers": ["..."],
"stamp": {
"amount": "30",
"state_code": "AN",
"article_id": "2337"
}
}
```
Optional `first_party_name` / `second_party_name` filters narrow the search
to inventory matching those names.
### Mode 2 — reserve by explicit stamp IDs [#mode-2--reserve-by-explicit-stamp-ids]
Pass the exact instance IDs from `order-status` or `list`; only IDs that are
still `available` are reserved:
```json title="POST /api/v1/esign/initialize-esign (excerpt)"
{
"file_id": "file_gXvuxRnKgdRWEHAXpnXw",
"signers": ["..."],
"stamp": {
"stamp_instance_ids": [
"stamp_inst_UZTqKQztUPyvnnjdCzxD",
"stamp_inst_uNeOUwjMVGNFexnxFSCP"
]
}
}
```
If the requested inventory is not available, initialization fails with
`422` and one of these error codes:
| Message Code | Description |
| ----------------------------- | -------------------------------------------------- |
| `stamp_combination_not_found` | No available stamps match the search parameters. |
| `inventory_conflict` | Requested stamp instances are no longer available. |
---
# Templates (/templates)
Save reusable signer layouts once and initialize Esign sessions from them at runtime.
If you send the same document layout repeatedly — offer letters, NDAs,
agreements — templates let you define the signer layout once and reuse it.
A template stores the source `file_id` and an ordered `signers` array,
where each signer contains its own PDF elements and optional defaults
(sign type, verification settings, geofencing, and more).
## How templates work [#how-templates-work]
1. [Save a template](/api-reference/templates/saveTemplate) with the file
and the per-signer element layout.
2. At runtime, call
[Initialize Esign from template](/api-reference/templates/initializeFromTemplate)
with the `template_id` and the actual signer details (name, email/mobile,
reason).
3. The template's signer defaults are merged with the runtime signer data,
then the normal Esign validation flow runs — the result is a real Esign
session, identical to one created with `initialize-esign`.
## Ordering rules [#ordering-rules]
The template's `signers` array is **ordered**: the first template signer
maps to the first runtime signer in `initialize-from-template`, the second
to the second, and so on. The runtime signer count must match the template
signer count exactly, or the request fails with `signer_count_mismatch`.
Every template signer must contain at least one `signature` element.
## Save a template [#save-a-template]
```json title="POST /api/v1/templates/save"
{
"name": "Employment Offer Template",
"file_id": "file_onwEUoMxnWhewUzodZeW",
"folder": "HR",
"signers": [
{
"sign_type": "suresign",
"auth_mode": 1,
"max_retry": 1,
"reason": "Employer sign",
"verify_email": false,
"verify_mobile": false,
"elements": [
{ "x": 100, "y": 120, "page": 0, "alignment": "center", "type": "signature" }
]
},
{
"sign_type": "suresign",
"auth_mode": 1,
"reason": "Employee sign",
"verify_email": true,
"verify_location": true,
"location_source": "ip",
"enforce_geofence": true,
"latitude": 19.076,
"longitude": 72.8777,
"radius": 500,
"elements": [
{ "x": 100, "y": 220, "page": 0, "alignment": "center", "type": "signature" }
]
}
]
}
```
Only `name`, `file_id`, and `signers` (with `x`, `y`, `page`, `alignment`,
`type` per element) are required — everything else is an optional signer
default. `file_id` must belong to the authenticated user.
**Success response** (truncated):
```json
{
"data": {
"client_id": "template_xxx",
"name": "Employment Offer Template",
"file_id": "file_onwEUoMxnWhewUzodZeW",
"folder": "HR",
"signers": [
{
"sign_type": "suresign",
"auth_mode": [1],
"reason": "Employer sign",
"elements": [
{
"field_id": "field_xxx",
"x": 100,
"y": 120,
"page": 0,
"type": "signature",
"alignment": "center",
"width": 50,
"height": 50
}
]
}
]
},
"status_code": 200,
"message": "Success",
"success": true
}
```
**Error responses:**
| HTTP Status | Message Code | Description |
| ----------- | -------------- | --------------------------------------------------------- |
| `400` | `bad_request` | Template signer validation failed. |
| `401` | `unauthorized` | Invalid or expired access token. |
| `404` | `not_found` | The provided file ID does not exist for the current user. |
## `field_id` and `element_values` [#field_id-and-element_values]
When a template is saved, each element gets an auto-generated `field_id`
(returned in the save/update response). At runtime, you can fill element
values per signer by referencing those IDs:
```json
{
"template_id": "template_xxx",
"signers": [
{
"full_name": "Employer A",
"email": "employer@example.com",
"reason": "Employer sign",
"element_values": {
"field_xxx": "INV-1001",
"field_yyy": "now"
}
}
]
}
```
`element_values` only overrides an element's `value` — position and type
always come from the template. Signature `value` asset IDs are stripped at
initialization unless the saved field sets `allow_wet_signature: true`.
## Initialize an Esign from a template [#initialize-an-esign-from-a-template]
```json title="POST /api/v1/esign/initialize-from-template"
{
"template_id": "template_xxx",
"sign_type": "suresign",
"auth_mode": 1,
"max_retry": 1,
"folder": "HR",
"allow_download": true,
"expiry_seconds": 7200,
"redirect_url": "https://example.com/esign/complete",
"backend": "emudhra",
"stamp": {
"amount": "100.00",
"state_code": "MH",
"article_id": "article_001",
"first_party_name": "Employer A",
"second_party_name": "Employee B"
},
"email_template_overrides": {
"company_name": "Acme Corp",
"legal_name": "Acme Corporation Pvt Ltd.",
"support_email": "support@acme.com"
},
"signers": [
{
"full_name": "Employer A",
"email": "employer@example.com",
"reason": "Employer sign",
"face_ref_file_id": "file_faceReferenceImage",
"face_match_threshold": 80,
"face_max_attempts": 5,
"notification": { "invite": true, "completion": true, "whatsapp": true },
"element_values": { "field_xxx": "INV-1001", "field_yyy": "now" }
},
{
"full_name": "Employee B",
"email": "employee@example.com",
"mobile": "9123456789",
"country_code": "+91",
"reason": "Employee sign",
"name_match": true,
"notification": { "invite": false, "completion": true, "whatsapp": false },
"element_values": { "field_zzz": "Employee B" }
}
]
}
```
### Notes [#notes]
* Only `template_id` and `signers` are required; signers must be supplied
in the same order as the template signer array, with a matching count.
* `full_name` should be supplied, `reason` must be supplied unless a
default reason is already saved in the template signer, and at least one
of `email` or `mobile` must be present (`country_code` is required when
`mobile` is provided).
* `expiry_seconds`: optional, min = 3600 (1 hour), max = 2592000 (30 days).
* `name_match` is optional and only applies to Aadhaar signers; it is
ignored for non-Aadhaar signers.
* `face_ref_file_id` enables face verification for the runtime signer —
upload the JPEG or PNG through
[Upload asset](/api-reference/files/uploadAsset) and use the returned
`client_id`. `face_match_threshold` (50–100, default 80) and
`face_max_attempts` (1–10, default 5) apply only when it is provided.
* `stamp`: optional e-stamping. Use either the amount-based reservation
flow (`amount` + `state_code` + `article_id`, required together) or pass
pre-reserved `stamp_instance_ids`.
* `notification` works exactly as in `initialize-esign` — see
[Getting started](/getting-started#notification).
The response is a full Esign session: each signer includes an
`invite_url`, `token`, merged element list, and verification state.
**Error responses:**
| HTTP Status | Message Code | Description |
| ----------- | ----------------------- | -------------------------------------------------------------- |
| `400` | `signer_count_mismatch` | Runtime signer count does not match the saved template. |
| `400` | `bad_request` | Validation failed after template and runtime signer merge. |
| `401` | `unauthorized` | Invalid or expired access token. |
| `404` | `not_found` | The provided template ID or referenced file ID does not exist. |
## Updating a template [#updating-a-template]
[Update template](/api-reference/templates/updateTemplate) takes the
template `client_id`; `name`, `folder`, `file_id`, and `signers` are all
optional:
```json title="PUT /api/v1/templates/update"
{
"client_id": "template_xxx",
"name": "Employment Offer Template v2",
"folder": "HR Updated",
"signers": [
{
"sign_type": "suresign",
"max_retry": 2,
"reason": "Employer sign updated",
"accept_virtual_sign": true,
"elements": [
{ "x": 140, "y": 120, "page": 0, "alignment": "center", "type": "signature" }
]
},
{
"sign_type": "pfx",
"auth_mode": [1, 2],
"max_retry": 3,
"pfx_id": "pfx_yyy",
"reason": "Employee sign updated",
"verify_email": true,
"verify_mobile": true,
"elements": [
{ "x": 140, "y": 220, "page": 0, "alignment": "center", "type": "signature" }
]
}
]
}
```
* If `signers` is provided, it **replaces** the saved blueprint completely
and regenerates every `field_id` — update any stored `element_values`
mappings accordingly.
* If `signers` is omitted, the saved signers are preserved.
* If `file_id` is provided, it must belong to the authenticated user.
**Error responses:**
| HTTP Status | Message Code | Description |
| ----------- | -------------- | ------------------------------------------------------------- |
| `400` | `bad_request` | Template signer validation failed when `signers` is provided. |
| `401` | `unauthorized` | Invalid or expired access token. |
| `404` | `not_found` | The template or provided file ID does not exist. |
## Signer defaults available in templates [#signer-defaults-available-in-templates]
Per template signer you can preset:
* `sign_type` (`aadhaar`, `suresign`, or `pfx`), `auth_mode`, `max_retry`,
`pfx_id`, `reason`
* `verify_email` / `verify_mobile`, `accept_virtual_sign`
* Location controls: `verify_location`, `location_source` (`device` or
`ip`), and geofencing via `enforce_geofence` + `latitude` + `longitude` +
`radius` (the last three are required together when geofencing is on)
Runtime-only signer options — `name_match`, face verification
(`face_ref_file_id`, `face_match_threshold`, `face_max_attempts`), and
`notification` — are passed in `initialize-from-template`.
---
# Upload PDF (/api-reference/files/uploadPdf)
Upload a PDF file (max size 50MB) for Esign.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Upload asset (/api-reference/files/uploadAsset)
Upload an image asset (JPEG or PNG). Use the returned `client_id` as:
- `face_ref_file_id` on a signer to enable face verification, or
- the `value` of an `image` element placed on the document.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Initialize Esign (/api-reference/esign/initializeEsign)
Start an Esign session for one or more signers on an uploaded document.
- `page` inside elements is zero-indexed: `0` is the first page.
- Multiple elements can be added per signer on different pages.
- `email_template_overrides` customizes invite email branding; when
`company_name` is provided, default Surepass branding is hidden for
white-label emails.
- `stamp` optionally reserves e-stamps into the flow — either by search
(`amount` + `state_code` + `article_id`, optionally narrowed by party
names) or by explicit `stamp_instance_ids`. Reserved stamp PDFs are
prefixed to the uploaded PDF before signing starts.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Get signer details (/api-reference/esign/getEsignDetails)
Retrieve signer status and metadata for a specific Esign session.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Download signed PDF (/api-reference/esign/downloadSignedPdf)
Download the final signed PDF after the Esign session completes.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Save template (/api-reference/templates/saveTemplate)
Save a reusable template blueprint for a file. A template stores the
source `file_id` and an ordered `signers` array, where each signer
contains its own PDF elements and optional signer-level defaults.
- `signers` is ordered: the first template signer maps to the first
runtime signer supplied later in `initialize-from-template`, and so on.
- Every signer must contain at least one `signature` element.
- A `field_id` is generated automatically for each saved element and
returned in template reads; those values are what
`initialize-from-template.signers[].element_values` uses later.
- `file_id` must belong to the authenticated user.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Update template (/api-reference/templates/updateTemplate)
Update an existing template. `name`, `folder`, `file_id`, and
`signers` are all optional in the request body.
- If `signers` is provided, it **replaces** the saved signer blueprint
completely and new `field_id` values are generated for all elements.
- If `signers` is omitted, existing signers are preserved.
- If `file_id` is provided, it must belong to the authenticated user.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Initialize Esign from template (/api-reference/templates/initializeFromTemplate)
Start an Esign session using a previously saved template. The template
contributes `file_id`, signer elements, and signer defaults; the
request contributes top-level Esign options plus runtime signer data
in the **same order** as the template's `signers` array.
- Runtime signer count must match the template signer count.
- `element_values` overrides template element `value`s at runtime by
template `field_id`; position and type still come from the template.
- Template signer defaults are merged with runtime signer input before
the normal `initialize-esign` validation flow runs.
- `full_name` should be supplied, `reason` must be supplied unless
saved in the template, and at least one of `email` or `mobile` must
be present.
- This endpoint creates a real Esign flow, just like `initialize-esign`.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# List state codes (/api-reference/stamps/listStateCodes)
List the state codes supported for stamp procurement.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# List articles (/api-reference/stamps/listArticles)
List the supported articles for a state. Use the returned `article_id`
when creating a procurement order.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Create stamp order (/api-reference/stamps/createStampOrder)
Create a stamp procurement order and start the background procurement
workflow.
- This call is **asynchronous** — a successful response does not mean
`stamps` is populated yet. Poll `GET /api/v1/stamps/order-status` or
`GET /api/v1/stamps/list` until instances become `available`.
- Billing charges `estamp_duty` (`stamp_duty_amount × quantity`) plus
`estamp_fees` (one per stamp). The request is rejected if the wallet
balance is insufficient.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Get order status (/api-reference/stamps/getStampOrderStatus)
Get the latest state of a procurement order. Once the background
workflow completes, `stamps` contains the vendor stamp instances and
`status_counts` is populated.
- `stamps[].stamp_id` is the public stamp **instance** ID — use it with
`GET /api/v1/stamps/download-pdf` and for explicit stamp reservation
in Esign initialization.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# List stamp orders (/api-reference/stamps/listStampOrders)
List procurement orders for the authenticated user, paginated.
When filtering by `status=pending`, matching orders are returned with an
empty `stamps` array (pending stamps are not yet materialized as
instances), while `status_counts.pending` reflects the pending count.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.
---
# Download stamp PDF (/api-reference/stamps/downloadStampPdf)
Get the vendor-provided download URL for a completed stamp PDF. PDF
bytes are not streamed through the API — the response contains a
short-lived signed URL.
Pass the stamp **instance** ID (`stamps[].stamp_id` from order-status
or list), not the order ID.
This page is generated from the OpenAPI specification — read /openapi.yaml for the full request/response schemas, examples, and error codes.