SHCIL eStamp

6 endpoints · Core API

SHCIL eStamp enables secure and legally valid electronic stamp certificate generation through the Stock Holding Corporation of India Limited (SHCIL). Your applications can initiate e-Stamp requests, generate stamp certificates using system templates or custom PDF documents, and retrieve the generated e-Stamp digitally. No physical stamp paper procurement is required. SignCare provides seamless integration with SHCIL's e-Stamping services, so you can generate and manage e-Stamps directly from your application without dealing with the underlying SHCIL platform.

New to SHCIL eStamp? Read this integration guide first.

SHCIL eStamp lets you generate legally valid electronic stamp certificates through the Stock Holding Corporation of India Limited (SHCIL) — fully digitally, with no physical stamp paper. You call the REST APIs below, SignCare handles the SHCIL platform integration end to end, and you get back the stamped document (or stamp certificate) as a base64 PDF.

The walkthrough covers which API to call, when, why, and where every field comes from — including the two flows (Pdf vs Template), the full field-origin map, validation rules, and error codes.

Table of Contents

  1. Before You Start — Authentication
  2. The Two Flows — Pdf Flow vs Template Flow
  3. End-to-End Flow at a Glance
  4. API 1 — Get Master Details
  5. API 2 — Get Template Fields
  6. API 3 — Generate E-Stamp
  7. Slow Generation and the 202 Response
  8. API 4 — Get Procured E-Stamps
  9. API 5 — Attach E-Stamp
  10. API 6 — Report Execution
  11. Webhook Delivery
  12. Field Origin Map — Where Every Field Comes From
  13. Validation Rules & Limits
  14. Error Codes
  15. FAQ

Before You Start — Authentication

Every API call must include these two headers (issued to you during onboarding):

HeaderDescription
X-API-KEYYour API key
X-API-APP-IDYour application id

Base path: {BASE_URL}/api/v1/StockHoldingEStamp

All requests and responses are JSON. All responses use a common envelope:

{
  "success": true,
  "statusCode": 200,
  "message": "Success",
  "data": { }
}

Fields with no value are omitted from responses — always code defensively for absent optional fields.

Configure a webhook before you start. The Generate API needs a webhook URL — either sent as webhookUrl on each request, or configured for your app during onboarding. A request with neither is rejected. See Webhook Delivery for why.


The Two Flows — Pdf Flow vs Template Flow

The Master Details API returns articles in two lists — one per flow — and every article carries an articleType confirming which flow it belongs to:

ListarticleTypeFlowWhat you sendWhat you get back
pdfArticlesCUSTOMPdf flow — you already have the documentYour own PDF/DOCX in documentInfoYour document with the e-Stamp certificate applied — ready in one call
templateArticlesSYSTEMTemplate flow — the document is generated from a SHCIL templateField values in fields (field ids from the Template Fields API)The e-Stamp certificate. Attach it to your own PDF later with the Attach API

articleType is only ever SYSTEM or CUSTOM, and it always matches the list the article came from — so you never have to decide what a value means. An article that works with either flow simply appears in both lists under the same articleCode: take it from templateArticles to send fields, or from pdfArticles to send documentInfo.

Rule: documentInfo and fields are mutually exclusive — send exactly one of them.

Which flow should I use?

  • You already have the final agreement PDF → Pdf flow. One call and you're done: Generate returns the stamped document.
  • You want to procure stamps first and attach them to documents later → Template flow. Generate gives you a stamp certificate (status COMPLETED — procured); when your document is ready, call Attach to merge them (status becomes CONSUMED).

End-to-End Flow at a Glance

Pdf flow (CUSTOM article) — single call

StepWhat you sendWhat you get
1. Master Details—stateCode + articleCode (articleType = CUSTOM)
2. Generate E-StampdocumentInfo + parties + stampDetailsstamped PDF + documentId
3. eSign API (optional)the documentIdsignatures

Template flow (SYSTEM article) — procure now, attach later

StepWhat you sendWhat you get
1. Master Details—stateCode + articleCode (articleType = SYSTEM)
2. Template Fieldsthe articleCodefield ids to fill
3. Generate E-Stampfields + parties + stamp detailse-Stamp certificate + eStampId
4. Attach E-Stampyour PDF + eStampIdstamped PDF + documentId
5. eSign API (optional)the documentIdsignatures
Get Procured E-Stamps (any time after step 3)—stamps generated but not yet attached

Execution reporting. SHCIL requires the execution of every stamped document to be reported back once it has been signed. If you sign through the SignCare eSign API this happens automatically — see API 6 — Report Execution for when you would call it yourself.


API 1 — Get Master Details

GET /api/v1/StockHoldingEStamp/master-details

What it is: The starting point of every integration. Returns the reference data you need before generating any stamp.

Why you need it: The Generate API only accepts state codes and article codes from this list, and checks your stamp duty, consideration price and parties against the limits returned here. Calling Generate with values not present here is rejected.

When to call it: Once at integration time to build your state/article dropdowns, then refresh periodically (the data rarely changes — daily refresh is more than enough).

states[] — the states supported for SHCIL stamping:

FieldTypeDescription
stateNamestringDisplay name (e.g. "Assam")
stateCodestringTwo-letter code (e.g. "AS") → pass in stampDetails.stateCode of the Generate API

templateArticles[] and pdfArticles[] — the articles available in those states, one list per flow. Both use the same shape:

FieldTypeDescription
stateCodestringThe state this article belongs to
articleNumberstringStamp article number under the state's Stamp Act (e.g. "4" for Affidavit). Omitted when unmapped
articleCodenumberUnique code of the article → pass in stampDetails.articleCode of the Generate API
articleDescriptionstringDescription of the article. In pdfArticles: the generic legal description (e.g. "Affidavit"). In templateArticles: it also carries the specific template name (e.g. "Affidavit - Bharat Gas"), so you can tell templates of one article apart. Omitted when unavailable
articleNamestringDisplay name of the article. Omitted when unavailable
minDutystringMinimum stamp duty (INR) — stampDutyAmount must not be below this
maxDutystringMaximum stamp duty (INR) — stampDutyAmount must not exceed this. Omitted when the article has no upper limit
articleTypestringSYSTEM in templateArticles, CUSTOM in pdfArticles — always matches the list it came from
stampDutyTypestringHow stamp duty is calculated: STAMP_DUTY_RANGE (a fixed range between minDuty and maxDuty) or CONSIDERATION_PRICE (a percentage of the transaction value)
considerationPercentagestringFor CONSIDERATION_PRICE articles: the percentage of the consideration price that becomes the stamp duty
minConsiderationPricestringFor CONSIDERATION_PRICE articles: the lowest considerationPrice accepted for this article. Omitted for other articles
maxConsiderationPricestringFor CONSIDERATION_PRICE articles: the highest considerationPrice accepted for this article. Omitted when the article has no upper limit, and for other articles
bothPartyRequiredbooleantrue → this article needs both firstParty and secondParty (name and mobile) in the Generate request

Important: articleCode values are unique per state, not globally. Always pick the article for the same stateCode you intend to stamp in.

Tip: several articles can share one articleNumber and articleName — e.g. a state's Affidavit article covers many templates. Each still has its own articleCode; send the articleCode of the exact entry you want. In templateArticles, articleDescription carries the template name to help you choose.

What the stamp-duty fields mean for your Generate request:

stampDutyTypeconsiderationPricestampDutyAmount
STAMP_DUTY_RANGEOptionalMust be between minDuty and maxDuty
CONSIDERATION_PRICERequired, between minConsiderationPrice and maxConsiderationPriceMust be between minDuty and maxDuty

Example response (trimmed):

{
  "success": true,
  "statusCode": 200,
  "message": "Success",
  "data": {
    "states": [
      { "stateName": "Assam", "stateCode": "AS" },
      { "stateName": "Karnataka", "stateCode": "KA" }
    ],
    "templateArticles": [
      {
        "stateCode": "AS",
        "articleNumber": "4",
        "articleCode": 19,
        "articleDescription": "Affidavit - Bharat Gas",
        "articleName": "Affidavit",
        "minDuty": "10",
        "maxDuty": "100",
        "articleType": "SYSTEM",
        "stampDutyType": "STAMP_DUTY_RANGE",
        "considerationPercentage": "0",
        "bothPartyRequired": false
      },
      {
        "stateCode": "AS",
        "articleNumber": "4",
        "articleCode": 20,
        "articleDescription": "Affidavit - Domicile",
        "articleName": "Affidavit",
        "minDuty": "10",
        "maxDuty": "100",
        "articleType": "SYSTEM",
        "stampDutyType": "STAMP_DUTY_RANGE",
        "considerationPercentage": "0",
        "bothPartyRequired": false
      }
    ],
    "pdfArticles": [
      {
        "stateCode": "KA",
        "articleNumber": "6(2)(i)",
        "articleCode": 286,
        "articleDescription": "Pawn or pledge - loan between Rs.1 lakh and Rs.10 lakh",
        "articleName": "Pawn or pledge - loan between Rs.1 lakh and Rs.10 lakh",
        "minDuty": "500",
        "maxDuty": "5000",
        "articleType": "CUSTOM",
        "stampDutyType": "CONSIDERATION_PRICE",
        "considerationPercentage": "0.5",
        "minConsiderationPrice": "100001",
        "maxConsiderationPrice": "1000000",
        "bothPartyRequired": true
      }
    ]
  }
}

Note how the two template-flow rows share articleNumber "4" and articleName "Affidavit" — only articleCode (and the template name in articleDescription) differs. That is the normal case, not an edge case.


API 2 — Get Template Fields

GET /api/v1/StockHoldingEStamp/template/fields/{articleCode}

What it is: Returns the dynamic fields of a document template.

Why you need it: In the template flow the document is generated from a SHCIL template — you must supply values for the template's fields. This API tells you which fields exist, their ids, types, and validation rules.

When to call it: Template flow only, after the user picks a SYSTEM article from the Master Details response. Skip it entirely in the pdf flow.

Path parameter: articleCode (required, positive integer) — from templateArticles[].articleCode of the Master Details API.

FieldTypeDescription
idnumberTemplate id
articleTemplateNamestringName of the template
htmlstringHTML content of the template — render it to preview the document
fields[]arrayThe fields to populate
fields[].idnumberField id → pass as fields[].id in the Generate API (as a string, e.g. "1024")
fields[].fieldNamestringHuman-readable label for your form
fields[].fieldTypestringTEXT | TEXTAREA | NUMBER | DROPDOWN | DATE_PICKER | STRING_DATE | MULTISELECT
fields[].sectionGroupNamestringLogical group for your form layout
fields[].propsobjectValidation rules — see below

props (each key omitted when not applicable):

KeyMeaning
mandatory"true" → the field must be supplied to the Generate API
maxLength / minLengthCharacter-length limits
optionsAllowed values for DROPDOWN/MULTISELECT
minDate / maxDateDate range for date fields (dd/MM/yyyy)
prefilledSuggested/pre-filled value
multiple"true" → the value appears at multiple places in the document
hidden"true" → system-managed field, don't show it in your UI
dependentThis field's value is auto-copied into another field

Send dates in dd/MM/yyyy format, and no earlier than the field's minDate.


API 3 — Generate E-Stamp

POST /api/v1/StockHoldingEStamp/generate

What it is: The core API — generates the e-Stamp certificate. In the pdf flow it also applies the certificate to your document in the same call.

When to call it: After you've chosen the state + article from Master Details (and collected field values via Template Fields, if template flow).

FieldTypeRequiredWhere it comes from / rules
referenceIdstringrequiredYour own unique id for this request. 1–64 chars: letters, digits, -, _. Never reused — a duplicate is rejected. Echoed back in the response and webhook so you can correlate
selfReportExecutionbooleanoptionalWho reports the document's execution to SHCIL when it is not signed through SignCare eSign. false (default) — SignCare reports it for you. true — you report it yourself with API 6 — Report Execution. Ignored when the document is signed through SignCare eSign — that reports automatically
documentInfoobjectconditionalPdf flow only (CUSTOM articles). Must be null in the template flow
documentInfo.namestringrequiredFile name with extension, e.g. "Loan Agreement.pdf"
documentInfo.contentstringrequiredBase64-encoded PDF or DOCX, max 5 MB
fieldsarrayconditionalTemplate flow only (SYSTEM articles). Must be null in the pdf flow. Each field id may appear only once; an id that is not part of the article's template is rejected
fields[].idstringrequiredField id ← Template Fields API fields[].id, sent as a string (e.g. "1024")
fields[].valuestringrequiredThe value to print into the document. Checked against that field's props: mandatory fields must be filled, NUMBER must be numeric, DATE_PICKER must be dd/MM/yyyy and not before minDate, DROPDOWN must be one of options, TEXT/TEXTAREA must fit maxLength
firstPartyobjectconditionalAt least one of firstParty / secondParty is required. Required when the first party pays the duty, or when the article has bothPartyRequired: true
firstParty.namestringconditionalRequired whenever firstParty is sent
firstParty.mobilestringconditionalExactly 10 digits. Required when the first party pays the duty, or bothPartyRequired: true
firstParty.panstringconditional10-char PAN (AAAAA9999A). Required when the first party pays and stampDutyAmount is greater than ₹2,00,000
secondPartyobjectconditionalRequired when the second party pays the duty, or when the article has bothPartyRequired: true
secondParty.namestringconditionalRequired whenever secondParty is sent
secondParty.mobilestringconditionalExactly 10 digits. Required when the second party pays the duty, or bothPartyRequired: true
secondParty.panstringconditional10-char PAN. Required when the second party pays and stampDutyAmount is greater than ₹2,00,000
stampDetailsobjectrequiredStamp specifics
stampDetails.stateCodestringrequired← Master Details API states[].stateCode
stampDetails.articleCodenumberrequired← Master Details API — templateArticles[].articleCode (template flow) or pdfArticles[].articleCode (pdf flow), same state!
stampDetails.stampDutyAmountintegerrequiredINR, whole number greater than zero (no decimals). Must be within the article's minDuty–maxDuty range
stampDetails.stampDutyPaidBystringrequiredThe duty payer's name — must exactly match firstParty.name or secondParty.name
stampDetails.considerationPricenumberconditionalValue of the transaction. Required for CONSIDERATION_PRICE articles and must be between the article's minConsiderationPrice and maxConsiderationPrice. Optional for other articles. Never negative
stampDetails.documentDescriptionstringoptionalProperty / document description printed on the certificate. Max 100 characters; letters, numbers and spaces only
webhookUrlstringconditionalUse an HTTPS URL — the result is POSTed here. Required unless a webhook is configured for your app. The request value takes priority over the configured one
isAsyncbooleanoptionalDefault false. false — the API waits up to ~40 seconds and answers with the stamp (200); only when SHCIL is slow does it answer 202. true — the API does not wait: once the request is validated and accepted it answers 202 immediately and the result is delivered to your webhook. See Asking for the 202 on purpose

Example request — Pdf flow (a CONSIDERATION_PRICE article):

{
  "referenceId": "LOAN-2026-000123",
  "selfReportExecution": false,
  "documentInfo": {
    "name": "Loan Agreement.pdf",
    "content": "<base64>"
  },
  "firstParty": { "name": "Rahul Sharma", "pan": "ABCDE1234F", "mobile": "9876543210" },
  "secondParty": { "name": "Acme Finance Ltd", "pan": "AABCA9876B", "mobile": "9123456780" },
  "stampDetails": {
    "stateCode": "KA",
    "articleCode": 286,
    "stampDutyAmount": 2500,
    "stampDutyPaidBy": "Rahul Sharma",
    "considerationPrice": 500000,
    "documentDescription": "Loan agreement between Rahul Sharma and Acme Finance Ltd"
  },
  "webhookUrl": "https://yourapp.com/webhooks/estamp"
}

Example request — Template flow (a STAMP_DUTY_RANGE article):

{
  "referenceId": "AFF-2026-000124",
  "fields": [
    { "id": "1024", "value": "Rahul Sharma" },
    { "id": "1025", "value": "Guwahati" }
  ],
  "firstParty": { "name": "Rahul Sharma", "mobile": "9876543210" },
  "stampDetails": {
    "stateCode": "AS",
    "articleCode": 19,
    "stampDutyAmount": 100,
    "stampDutyPaidBy": "Rahul Sharma"
  },
  "webhookUrl": "https://yourapp.com/webhooks/estamp"
}

Response (HTTP 200)

FieldTypeDescription / where you use it next
referenceIdstringThe reference id you sent — correlate with your records
eStampIdstring (uuid)Unique id of the e-Stamp. Template flow: pass this to the Attach API. Keep it for support queries
documentIdstring (uuid)Id of the stamped document — pdf flow only (omitted in template flow). Pass this to the eSign API to send the document for signature
userDocumentIdnumberSHCIL document id — pass to the Report Execution API if you report execution yourself; quote in support queries
reconMappingIdstringAuto-generated correlation key — quote in reconciliation/support queries
statusstringCOMPLETED = stamp generated. Pdf flow stamps are applied immediately; template flow stamps are procured and ready to attach
certIdstringCertificate id issued by SHCIL
urnstringUnique reference number of the e-Stamp
certificateNostringCertificate number printed on the stamp
certificateIssuedDatestringCertificate issue date/time
base64FileDatastringThe PDF. Pdf flow: your document with the e-Stamp applied. Template flow: the e-Stamp certificate itself

This API can also return HTTP 202 — when generation is taking longer than usual, or always when you send isAsync: true — see Slow Generation and the 202 Response below. When that happens, eStampId, documentId, userDocumentId, and every certificate field above are absent (not yet created) — only referenceId, reconMappingId and status: "ACCEPTED" are present.

Charges: stamp duty and applicable charges are deducted from your SHCIL eStamp wallet when the stamp is generated. A request is refused up front if your wallet balance cannot cover it.


Slow Generation and the 202 Response

Generate normally answers synchronously — SHCIL almost always responds within seconds. Occasionally SHCIL's own platform is slower than usual. Rather than holding your connection open indefinitely, if the certificate isn't ready within the sync wait window (server-configured, currently ~40 seconds), the API responds immediately with:

{
  "success": true,
  "statusCode": 202,
  "message": "Accepted. Generation is taking longer than usual — the result will be delivered to your webhookUrl. Do not retry this referenceId.",
  "data": {
    "referenceId": "LOAN-2026-000123",
    "reconMappingId": "SC0000000000000ABCDE",
    "status": "ACCEPTED"
  }
}

202 is a normal response, not an error. During a slow spell it can be the outcome for most or all of your requests, not a rare edge case — don't treat it as a failure path you can skip building.

What happens next: the certificate is still being generated in the background. Once it finishes (success or failure), the outcome is delivered to your webhook — the same payload shape as the 200 response on success (see Webhook Delivery below for the failure shape). There is currently no separate "check status" endpoint — the webhook is the only delivery channel for a 202, which is why a webhook is required.

The one rule that matters — do not retry the same operation:

SituationHTTP you getWhat it meansWhat to do
SHCIL answers within the wait window200DoneNothing — use the response as normal
Still processing when the wait window elapses202Generation is in progressWait for the webhook. Do not retry
You call again with the same referenceId while it's still processing202 (again)Safe — echoes the same request, no second certificate is generatedStop retrying; one outcome is coming, for this referenceId only
You call again with the same referenceId after it already finished400That referenceId is already usedUse a new referenceId if you need to generate again
The webhook eventually reports failure(delivered via webhook, not HTTP)Nothing was chargedFix whatever the webhook's message says, then retry with a new referenceId

Calling again with a new referenceId while the original is still processing does not cancel or replace it — it starts a second, independent generation and, if both succeed, you pay for both. The only safe move while a referenceId is in flight is to wait.

Asking for the 202 on purpose — isAsync

If you would rather not hold the connection at all — because you handle every result on your webhook, or to test your own 202 handling — send isAsync: true on the Generate request. The default is false, and then everything above stays exactly as it is.

isAsync: false (default)isAsync: true
How long the call takesUp to ~40 seconds, usually a fewImmediate (after validation)
Normal answer200 with the stamp202 with status: "ACCEPTED" — no stamp yet
When you get a 202Only if SHCIL is slowAlways, once the request is accepted
Where the stamp arrivesIn the response (or on the webhook after a 202)On the webhook

How it behaves with isAsync: true:

  1. The request is validated first. Missing fields, a wrong article, an insufficient wallet, an unsupported state and similar problems are still returned immediately as the usual 4xx — nothing is accepted.

  2. If it is valid, you get 202 at once:

    {
      "success": true,
      "statusCode": 202,
      "message": "Accepted. As requested (isAsync), the stamp is generated in the background and the result will be delivered to your webhookUrl. Do not retry this referenceId.",
      "data": {
        "referenceId": "LOAN-2026-000123",
        "reconMappingId": "SC0000000000000ABCDE",
        "status": "ACCEPTED"
      }
    }
  3. The stamp is generated in the background, and the outcome is POSTed to your webhook — the same success payload as the 200 response, or the failure payload described in Webhook Delivery.

  4. A webhook is needed — send webhookUrl or have one configured for your app, as for every Generate request.

  5. Do not send the same referenceId again. While it is still being processed it just returns the same 202; once it has finished it is rejected as already used.

The 202 itself is identical to the "slow SHCIL" one described above, so one piece of code handles both: on a 202, store the referenceId and wait for the webhook.

When SHCIL has not finished the stamp — status: "PENDING"

Occasionally SHCIL creates the document but does not finish the eStamp (SHCIL statuses ESTAMP_PENDING or ERROR_IN_PDF_GENERATION). SignCare then completes it for you automatically by regenerating it with SHCIL — SHCIL does not charge again for this. You get:

{
  "success": true,
  "statusCode": 202,
  "message": "eStamp is being completed by SHCIL. The result will be delivered to your webhookUrl. Do not retry this referenceId — you have not been charged yet.",
  "data": {
    "referenceId": "LOAN-2026-000123",
    "reconMappingId": "SCAB12CD34EF56GH78IJ",
    "eStampId": "3f2a9c1e-5b7d-4e2a-9f10-2c3d4e5f6a7b",
    "userDocumentId": 45237,
    "status": "PENDING"
  }
}
  • You are not charged until the stamp is ready.
  • When it is ready, the normal success webhook is sent (same payload as for a 202 above), and the stamp appears in Get Procured E-Stamps.
  • If SHCIL cannot complete it after several automatic attempts, the failure webhook is sent and you are never charged for it.
  • Do not retry — the same referenceId is rejected, and a new referenceId would make SHCIL issue (and charge) a second stamp.

API 4 — Get Procured E-Stamps

POST /api/v1/StockHoldingEStamp/procured

What it is: Your stamp inventory — every e-Stamp procured via the template flow with the Generate API that has not yet been attached to a document, newest first.

Why you need it: In the procure-now-attach-later model this is your source of truth. Use it to build a "pick a stamp" screen, reconcile what you've paid for, or recover an eStampId you didn't store.

When to call it: Any time between Generate and Attach.

Request body — optional filters. Send {} (or no body) to get everything:

FieldTypeDescription
estampIdsarray of string (uuid)Optional. Only stamps whose id is in this list are returned
stateCodestringOptional. Only stamps from this state are returned

Response fields

FieldTypeDescription
countnumberNumber of procured stamps returned
stamps[]arrayProcured stamps, newest first
stamps[].eStampIdstring (uuid)→ pass to the Attach API
stamps[].userDocumentIdnumberSHCIL document id → pass in userDocumentIds of the Report Execution API
stamps[].reconMappingIdstringCorrelation key from generation time
stamps[].statusstringAlways COMPLETED (procured, ready to attach)
stamps[].certIdstringCertificate id issued by SHCIL
stamps[].urnstringUnique reference number
stamps[].certificateIssuedDatestringCertificate issue date and time
stamps[].articleNamestringArticle the stamp was generated with
stamps[].stampDetailsobjectstateCode, articleCode, stampDutyAmount, stampDutyPaidBy, considerationPrice, documentDescription from generation
stamps[].firstParty / secondPartyobjectParty details (name, pan, mobile) from generation. secondParty omitted when not provided
stamps[].createdOnstring (UTC)When the stamp was generated
stamps[].base64FileDatastringThe stamp's certificate PDF. Omitted if the file could not be retrieved

Example request:

{ "stateCode": "DL" }

Example response:

{
  "success": true,
  "statusCode": 200,
  "message": "Success",
  "data": {
    "count": 1,
    "stamps": [
      {
        "eStampId": "42bf8e05-7823-47f0-92b8-c997cac919e2",
        "userDocumentId": 80832,
        "reconMappingId": "SC641A5I2MR42I9NJ57L",
        "status": "COMPLETED",
        "certId": "IN-DL68613570100523Y",
        "urn": "DL1007DIGID2026217795031DLJN",
        "certificateIssuedDate": "05-Aug-2026 04:27 PM",
        "articleName": "Loan Agreement Title Deed",
        "stampDetails": { "stateCode": "DL", "articleCode": 1007, "stampDutyAmount": 100, "stampDutyPaidBy": "Acme Pvt Ltd" },
        "firstParty": { "name": "Acme Pvt Ltd", "mobile": "9876543210" },
        "createdOn": "2026-08-05T10:57:04Z",
        "base64FileData": "<the stamp's certificate PDF>"
      }
    ]
  }
}

A stamp disappears once attached. After a successful Attach the stamp becomes CONSUMED and drops out of this list — so what you see here is always safe to attach.

Response size: every entry carries its certificate PDF. If you hold many un-attached stamps, use the stateCode or estampIds filter to fetch only what you need.


API 5 — Attach E-Stamp

POST /api/v1/StockHoldingEStamp/attach

What it is: Attaches a procured e-Stamp (template flow) onto your own PDF — the certificate pages are merged with your document, producing the final stamped document.

Why you need it: Completes the template flow. Duty was already paid at generation — no additional stamp duty is charged here.

When to call it: After template-flow Generate, whenever your final document is ready.

FieldTypeRequiredWhere it comes from / rules
referenceIdstringrequiredA new unique reference id (same format rules as Generate; must not reuse any earlier referenceId)
eStampIdstring (uuid)required← Generate API response (template flow), or a Get Procured E-Stamps entry. Must belong to your account and must not already be attached. A stamp from a bulk batch is rejected
documentInfoobjectrequiredThe PDF to stamp
documentInfo.namestringrequiredFile name ending in .pdf
documentInfo.contentstringrequiredBase64 PDF only, max 5 MB (DOCX is not supported for attach)

Response (HTTP 200)

FieldTypeDescription / where you use it next
referenceIdstringThe reference id you sent
documentIdstring (uuid)Id of the stamped document → pass to the eSign API for signatures
base64FileDatastringThe stamped PDF (e-Stamp certificate pages + your document)

Each e-Stamp can be attached exactly once. After a successful attach its status becomes CONSUMED and it disappears from the Procured list. A second attempt is rejected with "This eStamp has already been attached to a document."


API 6 — Report Execution

POST /api/v1/StockHoldingEStamp/consume/report

What it is: Tells SHCIL that a stamped document has been executed (signed by its parties) — the document reference and how many people signed it. SHCIL requires this for every stamp.

Do you need to call it? Usually not:

Your situationWho reports execution
The document is signed through the SignCare eSign APISignCare, automatically — when the last signatory signs. Don't call this API
Signed outside SignCare, and you sent selfReportExecution: false (the default) on GenerateSignCare, automatically — after a grace period (currently 48 hours from generation)
Signed outside SignCare, and you sent selfReportExecution: true on GenerateYou — call this API once the document has actually been executed

Call it yourself when the exact signing date or signatory count matters to you, or when only you know when signing finished.

Request

FieldTypeRequiredDescription
userDocumentIdsarray of numberrequiredOne or more SHCIL document ids ← the userDocumentId from the Generate API or Get Procured E-Stamps. Each must be a positive number belonging to your account
executionDateTimestringoptionalWhen the document was executed — dd/MM/yyyy HH:mm:ss (e.g. "27/07/2026 14:30:00") or dd/MM/yyyy (current IST time is added). Omit to use the current IST date and time
noOfSignatoriesnumberoptionalTotal number of signatories on the document. Defaults to 0
noOfDigitalSignaturesnumberoptionalHow many signed with a DSC (Class 2 / Class 3 digital signature certificate). Defaults to 0
noOfElectronicSignaturesnumberoptionalHow many signed with Aadhaar or OTP-based electronic signature. Defaults to 0

The signatory counts apply to every id in userDocumentIds.

Example request:

{
  "userDocumentIds": [80832],
  "executionDateTime": "27/07/2026 14:30:00",
  "noOfSignatories": 2,
  "noOfDigitalSignatures": 0,
  "noOfElectronicSignatures": 2
}

Response

  • One id sent → data.message: SHCIL's confirmation. An id that is not found (or not yours) returns HTTP 404.
  • Several distinct ids sent → data.results[]: one entry per id, so one bad id never hides the others. HTTP 200 when all succeed, 207 when any failed.
FieldTypeDescription
messagestringConfirmation from SHCIL (one id sent)
results[]arrayOne entry per id (several ids sent)
results[].userDocumentIdnumberThe id this result is for
results[].successbooleantrue if SHCIL accepted the report for this stamp
results[].messagestringSHCIL's confirmation or the reason it failed

Safe to repeat. A stamp whose execution was already reported — by you, by SignCare eSign, or by the automatic report — is returned as successful with "Execution was already reported for this stamp." It is never filed with SHCIL twice.


Webhook Delivery

SignCare POSTs the outcome of every Generate request to your webhook as JSON — the webhookUrl on the request, or the webhook configured for your app if the request has none. This is not just a courtesy notification — for any request that returned HTTP 202 (see Slow Generation and the 202 Response), the webhook is the only place the outcome is delivered, which is why a webhook is required.

Success payload — the same fields as the Generate response:

{
  "success": true,
  "statusCode": 200,
  "message": "Success",
  "data": {
    "referenceId": "LOAN-2026-000123",
    "eStampId": "0d1f7f5e-…",
    "documentId": "8c2a41d9-…",
    "userDocumentId": 123456,
    "reconMappingId": "SC0000000000000ABCDE",
    "status": "COMPLETED",
    "certId": "IN-AS12345678901234X",
    "urn": "…",
    "certificateNo": "…",
    "certificateIssuedDate": "10-Jul-2026 11:42:05",
    "base64FileData": "<base64 PDF>"
  }
}

Failure payload — sent when a 202 request ultimately fails (nothing was created and nothing was charged in this case):

{
  "success": false,
  "statusCode": 422,
  "message": "eStamp generation failed. Please try again.",
  "data": {
    "referenceId": "LOAN-2026-000123",
    "status": "FAILED",
    "errorMessage": "eStamp generation failed. Please try again."
  }
}

On failure, eStampId/documentId/userDocumentId/certificate fields are all absent — nothing was generated. Read errorMessage, fix whatever it describes, and retry with a new referenceId.

Notes:

  • The PDF arrives as base64, unless your account is set up to receive webhooks without file content.
  • Use an HTTPS URL for webhookUrl.
  • Respond with HTTP 2xx to acknowledge. Failed deliveries are retried, so treat deliveries as at-least-once and de-duplicate on referenceId/eStampId.

Field Origin Map — Where Every Field Comes From

You send…In APIIt comes from
stampDetails.stateCodeGenerateMaster Details → states[].stateCode
stampDetails.articleCodeGenerateMaster Details → templateArticles[] or pdfArticles[] → articleCode (same state)
stampDetails.stampDutyAmountGenerateYour business logic — must fit the chosen article's minDuty–maxDuty
stampDetails.considerationPriceGenerateYour transaction value — required for CONSIDERATION_PRICE articles, within minConsiderationPrice–maxConsiderationPrice
stampDetails.stampDutyPaidByGenerateMust equal firstParty.name or secondParty.name in the same request
firstParty / secondPartyGenerateYour parties — both required when the article has bothPartyRequired: true
fields[].idGenerate (template flow)Template Fields → fields[].id
fields[].valueGenerate (template flow)Your user's input, validated against fields[].props
documentInfoGenerate (pdf flow) / AttachYour own document (base64)
eStampIdAttachGenerate response (template flow) → eStampId, or Get Procured E-Stamps → stamps[].eStampId
userDocumentIdsReport ExecutionGenerate response → userDocumentId, or Get Procured E-Stamps → stamps[].userDocumentId
referenceIdGenerate / AttachYou generate it — unique per request, never reused
webhookUrlGenerateYour endpoint (or the one configured for your app)

And where response fields go next:

You receive…From APIYou use it for
eStampIdGenerate / ProcuredAttach API (template flow); support queries
documentIdGenerate (pdf flow) / AttacheSign API — send the stamped document for signature
userDocumentIdGenerate / ProcuredReport Execution API; support queries
base64FileDataGenerate / Attach / ProcuredThe actual PDF — store or present it
reconMappingId, certId, urnGenerate / ProcuredReconciliation and support queries
referenceIdAllCorrelating responses/webhooks with your records

Validation Rules & Limits

RuleDetail
referenceId1–64 chars, A-Z a-z 0-9 - _ only, unique across every Generate and Attach request
WebhookRequired — webhookUrl on the request, or a webhook configured for your app. Use an HTTPS URL
File sizedocumentInfo.content max 5 MB (base64 decoded)
File typesGenerate: PDF or DOCX. Attach: PDF only
Flow exclusivitySend documentInfo or fields — never both, never neither
stateCodeTwo letters, and must be in the Master Details states list
articleCodeMust exist for that same stateCode in the flow's article list
Stamp dutyWhole number greater than zero, within the article's minDuty–maxDuty
Consideration priceNever negative. For CONSIDERATION_PRICE articles: required, within minConsiderationPrice–maxConsiderationPrice
PartiesAt least one of firstParty / secondParty. Every party sent needs a name. Both are required (name + mobile) when the article has bothPartyRequired: true
Duty payerstampDutyPaidBy must exactly match a party name in the request
Payer's mobileRequired for the party who pays — exactly 10 digits
Payer's PANRequired for the party who pays when stampDutyAmount > ₹2,00,000. Format AAAAA9999A
documentDescriptionMax 100 characters; letters, numbers and spaces only
Template field datesdd/MM/yyyy, not before the field's minDate
AttachOne attach per e-Stamp; already-attached stamps are rejected
Report ExecutionuserDocumentIds required, positive numbers, your own stamps only. executionDateTime as dd/MM/yyyy HH:mm:ss or dd/MM/yyyy

Error Codes

Every error uses the standard envelope, with a plain-English message naming the problem:

{
  "data": null,
  "messages": [],
  "request_id": null,
  "success": false,
  "statusCode": 400,
  "message": "Consideration price must be between 100001 and 1000000 for article 'Pawn or pledge - loan between Rs.1 lakh and Rs.10 lakh'."
}
HTTPMeaningWhat to do
202Generate only. Taking longer than usual, or status: "PENDING" (SHCIL is still completing the stamp; not charged yet) — not an error. See Slow Generation and the 202 ResponseWait for the webhook. Do not retry this referenceId
400Validation failed — message says exactly what is wrongFix the request and retry with a new referenceId
401Missing/invalid X-API-KEY or X-API-APP-IDCheck the headers
402Insufficient wallet balance / API access not enabled for your appRecharge, or contact your SignCare account manager
207Report Execution only, when several ids were sent and at least one failed — check data.results[]Retry only the ids that failed
404Attach: eStampId not found or does not belong to you. Report Execution: the single userDocumentId sent is not found or not yoursVerify the id via Get Procured E-Stamps
429Rate limit exceededBack off and retry
500Unexpected errorSee the retry rule below — do not always assume it's safe to retry blind

Common validation messages:

MessageCause
Consideration price is required for article 'X'.CONSIDERATION_PRICE article sent without considerationPrice
Consideration price must be between A and B for article 'X'.considerationPrice outside the article's range
Stamp duty amount must be between A and B for article 'X'.stampDutyAmount outside minDuty–maxDuty
Stamp duty amount must be a whole number (no decimals), e.g. 100, 500, 1000.Decimal duty amount
Stamp duty paid by must match the first party or second party name.stampDutyPaidBy matches neither party
Both party details are required for article 'X'. Please provide secondParty.name.bothPartyRequired article missing a party
firstParty.pan is required when stampDutyAmount is greater than ₹2,00,000.Payer's PAN missing above ₹2,00,000
Article code 'N' belongs to state 'XX', not 'YY'.articleCode taken from a different state
Validation Error: webhookUrl is required. …No webhookUrl and none configured for your app
Validation Error: The 'ReferenceId' passed in the request already exists.referenceId reused

Safe retry rule — read this before wiring up automatic retries. Not every non-200 means nothing happened:

  • 400 / 401 / 402 / 429 — nothing was created, safe to fix-and-retry with a new referenceId.
  • 500, or no response at all (a client-side timeout) — the request may have already succeeded on our side before your connection dropped or timed out. Blindly retrying with a new referenceId can create — and charge for — a second stamp for a request that actually went through. Before retrying, check whether the original referenceId already produced a result (a webhook that arrived late, or Get Procured E-Stamps for the template flow) rather than assuming it failed.
  • 202 is not in either bucket — it is not a failure at all. See the row above.

This is exactly why referenceId exists: reconcile on it before you resend the same operation.


FAQ

Q. Does the Generate API also get the document signed? No. Generation and signing are separate. Generate only produces the stamped document; to collect signatures, pass the returned documentId to the eSign API.

Q. Is eSign mandatory to procure a stamp? No. A stamp is procured — valid and charged — as soon as Generate returns success. eSign is how the document gets signed; when it is signed through SignCare eSign, execution is also reported to SHCIL automatically.

Q. Do you send document links in responses or webhooks? No — the document is always delivered inline as base64FileData. Nothing is exposed via public links.

Q. Can I attach one e-Stamp to multiple documents? No. One e-Stamp = one document. After attach, the stamp is CONSUMED.

Q. Can I reuse a referenceId? Never. It's your idempotency/correlation key — one per request, across Generate and Attach alike.

Q. How do I know whether considerationPrice is required? Check the article's stampDutyType in Master Details. For CONSIDERATION_PRICE it is required and must sit between minConsiderationPrice and maxConsiderationPrice. For any other type it is optional.

Q. When do I need to send both parties? When the article has bothPartyRequired: true — both firstParty and secondParty, each with name and mobile. Otherwise one party is enough, as long as stampDutyPaidBy names a party you sent.

Q. Do I have to call the Report Execution API? Only if you sign outside SignCare and set selfReportExecution: true on Generate. Signing through SignCare eSign reports automatically, and with the default selfReportExecution: false SignCare reports for you after a grace period.

Q. Do I have to store eStampId myself? It's recommended, but not required — anything procured and not yet attached is always retrievable from Get Procured E-Stamps.

Q. The articleCode I used worked for one state but is rejected for another. Why? Article codes are unique per state. The same numeric code can mean a different article in another state — always select the article entry matching your stateCode.

Q. Several articles have the same name. Which one do I pick? Many entries share one legal article (articleNumber and articleName). Each has its own articleCode — that is what identifies the exact entry. Send the articleCode of the one you want.

Q. What is reconMappingId for? It's an auto-generated correlation key tied to the stamp. Quote it (along with referenceId) in any support or reconciliation conversation.

Q. Which states/articles are supported? Whatever the Master Details API returns — it is the live source of truth. Don't hardcode the lists.

Q. I got a 202. Did my request fail? No — 202 means it's still processing, not that it failed. It happens when generation is taking longer than usual, or always when you sent isAsync: true. Wait for the webhook; don't retry the same referenceId. See Slow Generation and the 202 Response.

Q. What does isAsync do, and should I use it? It is optional and defaults to false (the API waits up to ~40 seconds and answers with the stamp). Set it to true if you want an immediate 202 and prefer to receive every result on your webhook — or to test your 202 handling. It needs a webhook. Request errors are still returned immediately as 4xx. See Asking for the 202 on purpose.

Q. My request timed out on my end (or I never got a response) — is it safe to retry? Not automatically. Unlike a clean 400/401/402/429, a timeout or a 500 doesn't tell you whether the stamp was actually generated on our side before your connection dropped. Check the webhook or Get Procured E-Stamps for that referenceId first — if it already succeeded, retrying with a new referenceId creates and charges for a second stamp.

Q. Is a webhook actually required? Yes — either webhookUrl on the request or a webhook configured for your app. It is the only delivery channel when a request returns 202; there is no separate status-check API.

GET

/api/v1/StockHoldingEStamp/master-details

#

Returns SHCIL master reference data, split into template and PDF generation flows.

docs.signcare.io/core/api/estamp/shcil-estamp/#get-api-v1-stockholdingestamp-master-details

The response contains three lists:

→ templateArticles: use these for template generation. Every article here has articleType: "SYSTEM". Send the selected articleCode with populated fields to the Generate E-Stamp API.

→ pdfArticles: use these for PDF generation. Every article here has articleType: "CUSTOM". Send the selected articleCode with documentInfo (base64 PDF) to the Generate E-Stamp API.

→ An article that supports both ways of stamping simply appears in both lists under the same articleCode — pick whichever list matches how you want to stamp. You never have to interpret a combined value.

→ states: the states supported for SHCIL stamping. Only these state codes are accepted by the Generate E-Stamp API.

→ templateArticles and pdfArticles: articles available in the supported states. Choose an article from the appropriate list and pass its articleCode to the Generate E-Stamp API. Fields with no value are omitted from the response.

states — entity fields:

FieldTypeDescription
stateNamestringDisplay name of the state (e.g. "Assam").
stateCodestringTwo-letter state code (e.g. "AS"). Pass this in stampDetails.stateCode of the Generate E-Stamp API.

templateArticles and pdfArticles — article fields:

FieldTypeDescription
stateCodestringTwo-letter state code the article belongs to. Matches an entry in the states list.
articleNumberstringStamp article number under the state's Stamp Act (e.g. "4" for Affidavit). Omitted when no mapping is available for the state.
articleCodenumberUnique code of the article. Pass this in stampDetails.articleCode of the Generate E-Stamp API.
articleNamestringName/category of the article (e.g. "Affidavit", "Agreement").
articleDescriptionstringDescription of the article. In pdfArticles it is the generic legal description of the article (e.g. "Affidavit"). In templateArticles it also carries the specific template name (e.g. "Affidavit - Bharat Gas" vs "Affidavit - Domicile"), so display it to tell templates of one article apart. Omitted when unavailable.
minDutystringMinimum stamp duty (INR) allowed for this article. stampDetails.stampDutyAmount must not be below this.
maxDutystringMaximum stamp duty (INR) allowed for this article. stampDetails.stampDutyAmount must not exceed this.
articleTypestringOnly two values are returned: SYSTEM = template flow (send fields), CUSTOM = PDF flow (send documentInfo). Always matches the list the article appears in, so it confirms which payload to send.
stampDutyTypestringHow stamp duty is calculated for this article — STAMP_DUTY_RANGE (fixed range between minDuty/maxDuty) or CONSIDERATION_PRICE (percentage of the transaction value; see considerationPercentage).
considerationPercentagestringPercentage of the consideration price used to compute stamp duty when stampDutyType is CONSIDERATION_PRICE; not meaningful otherwise.
minConsiderationPricestringCalculated by us, not returned by SHCIL. Only present for CONSIDERATION_PRICE articles: the transaction value at which minDuty is the resulting stamp duty (minDuty × 100 / considerationPercentage). A guide, not a guarantee — SHCIL's own validation at generate time is authoritative.
maxConsiderationPricestringCalculated by us, not returned by SHCIL. Only present for CONSIDERATION_PRICE articles when maxDuty is present (maxDuty × 100 / considerationPercentage); omitted when maxDuty is blank, meaning SHCIL places no upper bound. A guide, not a guarantee — SHCIL's own validation at generate time is authoritative.
bothPartyRequiredbooleantrue means the Generate E-Stamp API needs both firstParty and secondParty (name and mobile) for this article. false means one party is enough, or the article has not been verified yet.

stampDutyType values, and when considerationPercentage applies:STAMP_DUTY_RANGE — a fixed stamp duty is charged for this article, independent of the transaction value (typical for affidavits, general agreements, etc.). Pick any stampDutyAmount between minDuty and maxDuty when calling the Generate E-Stamp API. considerationPercentage does not apply here — SHCIL always returns "0" for these articles; ignore it.CONSIDERATION_PRICE — stamp duty is a percentage of the transaction value (typical for loan agreements, sale deeds, and other value-based documents). considerationPercentage is that rate — e.g. "0.5" means 0.5% of the transaction value. Pass the transaction value in stampDetails.considerationPrice, and compute: stampDutyAmount = considerationPrice × (considerationPercentage / 100)

The result must still fall within `minDuty`/`maxDuty` when either is
present.</item></list><b>Worked example</b> — article returns `stampDutyType = "CONSIDERATION_PRICE"`,

minDuty = "5000", maxDuty = "" (no upper bound), considerationPercentage = "0.5". We calculate and return minConsiderationPrice = "1000000" (₹10,00,000 is the smallest transaction value that still charges the ₹5,000 minimum duty); maxConsiderationPrice is omitted since maxDuty is blank. For a ₹10,00,000 loan: send considerationPrice = 1000000 and stampDutyAmount = 5000 (0.5% of ₹10,00,000).

This data is used to:

  • Populate the State and Article dropdowns
  • Identify the articleCode to send in stampDetails to the Generate E-Stamp API
  • Determine article numbers and stamp duty limits

Headers

  • X-API-KEY
    stringrequired
    Your secret API key for the environment you are calling (Stage and Live use different keys).
  • X-API-APP-ID
    stringrequired
    The App ID issued together with the key.

Responses

200OK
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    objectoptional
    Trimmed master reference data: supported states + articles split by flow.
    3 fields
    • states
      array<object>optional
      States supported for SHCIL stamping — the only states accepted by the Generate API.
      nullable
      Each item: 2 fields
      • stateName
        stringoptional
        No description.
        nullable
      • stateCode
        stringoptional
        Two-letter state code (e.g. "AS", "DL") — use this in the Generate API.
        nullable
    • templateArticles
      array<object>optional
      Articles for the template flow — every row has articleType "SYSTEM". Pass the article's articleCode and filled template fields to the Generate API.
      nullable
      Each item: 13 fields
      • stateCode
        stringoptional
        Two-letter state code the article belongs to.
        nullable
      • articleNumber
        stringoptional
        Stamp article number (omitted when unmapped).
        nullable
      • articleCode
        integer (int32)optional
        Article code — pass this as `stampDetails.articleCode` to the Generate API. Unique per state, not globally.
      • articleDescription
        stringoptional
        Full original name of the article as per StockHolding; omitted when unavailable.
        nullable
      • articleName
        stringoptional
        Article name (category).
        nullable
      • minDuty
        stringoptional
        Minimum stamp duty for this template.
        nullable
      • maxDuty
        stringoptional
        Maximum stamp duty for this template.
        nullable
      • articleType
        stringoptional
        Only two values are returned: "SYSTEM" (template flow — send fields) and "CUSTOM" (pdf flow — send documentInfo). Always matches the list the article appears in, so it confirms which payload the Generate API expects.
        nullable
      • stampDutyType
        stringoptional
        How stamp duty is calculated for this article — e.g. "STAMP_DUTY_RANGE" (fixed range between MinDuty/MaxDuty) or "CONSIDERATION_PRICE" (percentage of the transaction value; see ConsiderationPercentage).
        nullable
      • considerationPercentage
        stringoptional
        Percentage of the consideration price used to compute stamp duty when StampDutyType is "CONSIDERATION_PRICE"; not meaningful otherwise.
        nullable
      • minConsiderationPrice
        stringoptional
        CALCULATED BY US — not returned by SHCIL. Only present when StampDutyType is "CONSIDERATION_PRICE" and ConsiderationPercentage is a valid non-zero number: the smallest transaction value (considerationPrice) for which MinDuty is the resulting stamp duty, i.e. MinDuty × 100 / ConsiderationPercentage. A guide, not a guarantee — SHCIL's own validation at generate time is authoritative.
        nullable
      • maxConsiderationPrice
        stringoptional
        CALCULATED BY US — not returned by SHCIL. Only present when StampDutyType is "CONSIDERATION_PRICE", ConsiderationPercentage is a valid non-zero number, and MaxDuty is present (SHCIL sometimes leaves MaxDuty blank, meaning "no upper bound" — in that case this is omitted too): MaxDuty × 100 / ConsiderationPercentage. A guide, not a guarantee — SHCIL's own validation at generate time is authoritative.
        nullable
      • bothPartyRequired
        booleanoptional
        True when this article requires BOTH firstParty and secondParty at generate time; false when only one party is required OR the article hasn't been verified yet (defaults to false when unclassified — NOT a confirmed "no"). NOT returned by SHCIL.
    • pdfArticles
      array<object>optional
      Articles for the PDF/upload flow — every row has articleType "CUSTOM". Pass the article's articleCode and documentInfo (base64-encoded PDF) to the Generate API.
      nullable
      Each item: 13 fields
      • stateCode
        stringoptional
        Two-letter state code the article belongs to.
        nullable
      • articleNumber
        stringoptional
        Stamp article number (omitted when unmapped).
        nullable
      • articleCode
        integer (int32)optional
        Article code — pass this as `stampDetails.articleCode` to the Generate API. Unique per state, not globally.
      • articleDescription
        stringoptional
        Full original name of the article as per StockHolding; omitted when unavailable.
        nullable
      • articleName
        stringoptional
        Article name (category).
        nullable
      • minDuty
        stringoptional
        Minimum stamp duty for this template.
        nullable
      • maxDuty
        stringoptional
        Maximum stamp duty for this template.
        nullable
      • articleType
        stringoptional
        Only two values are returned: "SYSTEM" (template flow — send fields) and "CUSTOM" (pdf flow — send documentInfo). Always matches the list the article appears in, so it confirms which payload the Generate API expects.
        nullable
      • stampDutyType
        stringoptional
        How stamp duty is calculated for this article — e.g. "STAMP_DUTY_RANGE" (fixed range between MinDuty/MaxDuty) or "CONSIDERATION_PRICE" (percentage of the transaction value; see ConsiderationPercentage).
        nullable
      • considerationPercentage
        stringoptional
        Percentage of the consideration price used to compute stamp duty when StampDutyType is "CONSIDERATION_PRICE"; not meaningful otherwise.
        nullable
      • minConsiderationPrice
        stringoptional
        CALCULATED BY US — not returned by SHCIL. Only present when StampDutyType is "CONSIDERATION_PRICE" and ConsiderationPercentage is a valid non-zero number: the smallest transaction value (considerationPrice) for which MinDuty is the resulting stamp duty, i.e. MinDuty × 100 / ConsiderationPercentage. A guide, not a guarantee — SHCIL's own validation at generate time is authoritative.
        nullable
      • maxConsiderationPrice
        stringoptional
        CALCULATED BY US — not returned by SHCIL. Only present when StampDutyType is "CONSIDERATION_PRICE", ConsiderationPercentage is a valid non-zero number, and MaxDuty is present (SHCIL sometimes leaves MaxDuty blank, meaning "no upper bound" — in that case this is omitted too): MaxDuty × 100 / ConsiderationPercentage. A guide, not a guarantee — SHCIL's own validation at generate time is authoritative.
        nullable
      • bothPartyRequired
        booleanoptional
        True when this article requires BOTH firstParty and secondParty at generate time; false when only one party is required OR the article hasn't been verified yet (defaults to false when unclassified — NOT a confirmed "no"). NOT returned by SHCIL.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": { "states": [ { "stateName": "string", "stateCode": "string" } ], "templateArticles": [ { "stateCode": "string", "articleNumber": "string", "articleCode": 0, "articleDescription": "string", "articleName": "string", "minDuty": "string", "maxDuty": "string", "articleType": "string", "stampDutyType": "string", "considerationPercentage": "string", "minConsiderationPrice": "string", "maxConsiderationPrice": "string", "bothPartyRequired": false } ], "pdfArticles": [ { "stateCode": "string", "articleNumber": "string", "articleCode": 0, "articleDescription": "string", "articleName": "string", "minDuty": "string", "maxDuty": "string", "articleType": "string", "stampDutyType": "string", "considerationPercentage": "string", "minConsiderationPrice": "string", "maxConsiderationPrice": "string", "bothPartyRequired": false } ] }, "messages": [ "string" ], "request_id": "string", "isBillable": false }
400Bad Request
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 400
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 400 }
401Unauthorized
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 401
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 401 }
429Too Many Requests
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    anyoptional
    No description.
    nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": null, "messages": [ "string" ], "request_id": "string", "isBillable": false }
500Internal Server Error
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 500
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 500 }
Code samplescurl · Node.js · Python
Stage: uat-ext.signcare.io
curl -X GET 'https://uat-ext.signcare.io/api/v1/StockHoldingEStamp/master-details' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'X-API-APP-ID: YOUR_APP_ID'
GET

/api/v1/StockHoldingEStamp/template/fields/{articleCode}

#

Retrieve the complete structure of a document template using the articleCode.

docs.signcare.io/core/api/estamp/shcil-estamp/#get-api-v1-stockholdingestamp-template-fields-articlecode

This API returns the complete structure of a document template, including:

→ Template metadata.

→ HTML content used for document preview/rendering.

→ All dynamic fields that must be populated before generating an E-Stamp document.

The articleCode is obtained from the Get Master Details API.

Each returned field corresponds to a placeholder inside the HTML (e.g. span{id}), and the populated values must be sent to the Generate E-Stamp Document API.

Path Parameter:

→ articleCode (required, integer): The articleCode of the template for which fields are needed, a positive integer taken from templateArticles[].articleCode of the Master Details API.

The articleCode must be provided as a path parameter in the request URL (e.g., /api/v1/StockHoldingEStamp/template/fields/22, where 22 is the articleCode).

Headers

  • X-API-KEY
    stringrequired
    Your secret API key for the environment you are calling (Stage and Live use different keys).
  • X-API-APP-ID
    stringrequired
    The App ID issued together with the key.

Path parameters

  • articleCode
    integer (int32)required
    Required. Article code (positive integer) from the Master Details API (templateArticles).

Responses

200OK
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    objectoptional
    Template fields response from GetTemplateFields API.
    4 fields
    • id
      integer (int32)optional
      No description.
    • articleTemplateName
      stringoptional
      No description.
      nullable
    • html
      stringoptional
      No description.
      nullable
    • fields
      array<object>optional
      No description.
      nullable
      Each item: 5 fields
      • sectionGroupName
        stringoptional
        No description.
        nullable
      • id
        integer (int32)optional
        No description.
      • fieldName
        stringoptional
        No description.
        nullable
      • fieldType
        stringoptional
        TEXT | TEXTAREA | NUMBER | DROPDOWN | STRING_DATE | MULTISELECT
        nullable
      • props
        objectoptional
        No description.
        10 fields
        • prefilled
          stringoptional
          No description.
          nullable
        • mandatory
          stringoptional
          No description.
          nullable
        • maxLength
          stringoptional
          Maximum character length. JSON key: maxlength (lowercase).
          nullable
        • minLength
          stringoptional
          Minimum character length. JSON key: minlength (lowercase).
          nullable
        • options
          stringoptional
          No description.
          nullable
        • minDate
          stringoptional
          Minimum selectable date (dd/MM/yyyy). JSON key: minDate.
          nullable
        • maxDate
          stringoptional
          Maximum selectable date (dd/MM/yyyy). JSON key: maxDate.
          nullable
        • multiple
          stringoptional
          When "true", the field appears at multiple locations in the document (span duplicates).
          nullable
        • hidden
          stringoptional
          When "true", the field is hidden in the UI (system-managed).
          nullable
        • dependent
          stringoptional
          JSON string specifying a dependent field. Format: {"id": fieldId, "value": "duplicate"}. When present, this field's value is copied into the dependent field automatically.
          nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": { "id": 0, "articleTemplateName": "string", "html": "string", "fields": [ { "sectionGroupName": "string", "id": 0, "fieldName": "string", "fieldType": "string", "props": { "prefilled": "string", "mandatory": "string", "maxLength": "string", "minLength": "string", "options": "string", "minDate": "string", "maxDate": "string", "multiple": "string", "hidden": "string", "dependent": "string" } } ] }, "messages": [ "string" ], "request_id": "string", "isBillable": false }
400Bad Request
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 400
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 400 }
401Unauthorized
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 401
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 401 }
429Too Many Requests
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    anyoptional
    No description.
    nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": null, "messages": [ "string" ], "request_id": "string", "isBillable": false }
500Internal Server Error
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 500
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 500 }
Code samplescurl · Node.js · Python
Stage: uat-ext.signcare.io
curl -X GET 'https://uat-ext.signcare.io/api/v1/StockHoldingEStamp/template/fields/{articleCode}' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'X-API-APP-ID: YOUR_APP_ID'
POST

/api/v1/StockHoldingEStamp/generate

#

Create and generate an E-Stamp certificate using a selected article template or your own uploaded document.

docs.signcare.io/core/api/estamp/shcil-estamp/#post-api-v1-stockholdingestamp-generate

This API generates an E-Stamp certificate.

→ A URN and a reconMappingId are generated automatically and returned in the response.

→ On success, the certificate details are returned in the response and also delivered to your webhook.

Two ways to generate:

The Master Details API returns articles in two lists, and each article's articleType tells you which flow to use:

→ Pdf flow (articleType = CUSTOM, from pdfArticles): send your own document in documentInfo and the e-Stamp is applied to it. fields must be null.

→ Template flow (articleType = SYSTEM, from templateArticles): send the article's field values in fields (field ids from the Fields-By-Template API) and the document is generated from the template. documentInfo must be null.

→ An article that works with either flow appears in both lists under the same articleCode — just take it from the list matching the flow you want.

How to consume:

  1. Call the Master Details API to get the supported states and articles — pick the article from templateArticles or pdfArticles depending on the flow you want.

  2. Call this API with the party details, the selected stateCode + articleCode in stampDetails, and either documentInfo (pdf flow) or fields (template flow).

  3. Read the certificate details from the response — the same stamp details are also delivered to your webhook.

  4. Pdf flow: to get the stamped document signed, call the eSign API with the documentId returned in the response.

  5. Template flow: attach the generated e-Stamp onto your own PDF using the Attach E-Stamp API with the eStampId returned in the response.

Request Body — entity fields:

FieldTypeRequiredDescription
referenceIdstringrequiredYour unique reference id for this request (1-64 chars: letters, numbers, hyphen, underscore). Must not have been used before by any Generate or Attach request.
selfReportExecutionbooleanoptionalWho reports the document's execution to SHCIL when it is not signed through SignCare eSign. false (default): SignCare reports it. true: you report it with the Report Execution API. See "Execution Reporting" below.
documentInfoobjectconditionalThe document to be stamped. Required for the pdf flow (CUSTOM articles); must be null in the template flow.
documentInfo.namestringrequiredFile name with extension (e.g. "Loan Agreement.pdf").
documentInfo.contentstringrequiredBase64-encoded PDF or DOCX, max 5 MB.
fieldsarrayconditionalField values for the article — required for the template flow (SYSTEM articles); must be null in the pdf flow. Each field id may appear only once; ids not in the article's template are rejected.
fields[].idstringrequiredField id from the Fields-By-Template API (send it as a string, e.g. "1024").
fields[].valuestringrequiredValue to populate in the document. Checked against the field's rules: mandatory fields must be filled, NUMBER must be numeric, DATE_PICKER must be dd/MM/yyyy and not before minDate, DROPDOWN must be one of the options, TEXT/TEXTAREA must fit maxLength.
firstPartyobjectconditionalFirst party details. Required when the first party pays the stamp duty, or when the article has bothPartyRequired: true. At least one of firstParty or secondParty must be provided.
firstParty.namestringconditionalFirst party full name. Required when firstParty is provided.
firstParty.mobilestringconditionalExactly 10 digits. Required when the first party pays the stamp duty, or when the article has bothPartyRequired: true.
firstParty.panstringconditionalPAN (10 chars, format AAAAA9999A, e.g. ABCDE1234F). Required when the first party pays and stampDutyAmount exceeds ₹2,00,000.
secondPartyobjectconditionalSecond party details. Required when the second party pays the stamp duty, or when the article has bothPartyRequired: true. At least one of firstParty or secondParty must be provided.
secondParty.namestringconditionalSecond party full name. Required when secondParty is provided.
secondParty.mobilestringconditionalExactly 10 digits. Required when the second party pays the stamp duty, or when the article has bothPartyRequired: true.
secondParty.panstringconditionalPAN (10 chars, format AAAAA9999A, e.g. ABCDE1234F). Required when the second party pays and stampDutyAmount exceeds ₹2,00,000.
stampDetailsobjectrequiredStamp-specific details.
stampDetails.stateCodestringrequiredTwo-letter state code (e.g. "AS"). Must be one of the supported states from the Master Details API.
stampDetails.articleCodeintegerrequiredThe articleCode value from the Master Details API, for the same state. Article codes are unique per state, not globally.
stampDetails.stampDutyAmountintegerrequiredStamp duty amount in INR, a whole number greater than zero. Decimals and fractional notation are rejected. Must be within the article's minDuty/maxDuty range.
stampDetails.stampDutyPaidBystringrequiredName of the duty payer. Must exactly match firstParty.name or secondParty.name (case-insensitive).
stampDetails.considerationPricenumberconditionalTransaction value in INR. Required for articles whose stampDutyType is CONSIDERATION_PRICE (and then it must be between minConsiderationPrice and maxConsiderationPrice); optional for other articles. Never negative.
stampDetails.documentDescriptionstringoptionalProperty or document description printed on the certificate. Max 100 characters; letters, numbers and spaces only.
webhookUrlstringconditionally requiredUse an HTTPS URL for the final result. The request value has priority. If omitted or blank, the webhook configured for your app is used. At least one must be available; otherwise validation fails before acceptance, charging, or the SHCIL call.
isAsyncbooleanoptionalDefault false. false: the API waits up to about 40 seconds and answers with the stamp (200); only when SHCIL is slow does it answer 202. true: the API does not wait — after validating and accepting the request it answers 202 immediately and the result is delivered to your webhook. See "Getting the result later (isAsync)" below.

Response — entity fields:

FieldTypeDescription
referenceIdstringThe unique reference id you passed in the request.
eStampIdstring (uuid)Unique id of the generated e-Stamp. Keep this for reference and support queries.
documentIdstring (uuid)Unique id of the stamped document (pdf flow; omitted in the template flow). Pass this id to the eSign API to send the document for signature.
userDocumentIdnumberSHCIL document id. Pass it in userDocumentIds of the Report Execution API if you report execution yourself; quote it in support queries.
reconMappingIdstringAuto-generated correlation key. Quote this in support and reconciliation queries.
statusstringStatus of the e-Stamp (e.g. COMPLETED, CONSUMED).
certIdstringCertificate id issued by SHCIL.
urnstringUnique reference number of the e-Stamp.
certificateNostringCertificate number printed on the stamp.
certificateIssuedDatestringCertificate issue date and time.
base64FileDatastringBase64-encoded generated PDF — the stamped document (pdf flow) or the e-Stamp certificate (template flow).

Fields with no value are omitted from the response.

Next step — signing the stamped document (pdf flow):

This API only generates the e-Stamp — it does not initiate any signature. To get the stamped document signed, call the eSign API with the documentId returned in this response. The eSign API sends the signing request to your signers, and each signer receives a link to complete the signature on the stamped document.

Execution Reporting — what it is and why it is required:

StockHolding (SHCIL) issues the e-Stamp certificate at the moment it is generated, but the certificate on its own does not record that the document was ever actually executed. SHCIL requires execution to be confirmed back against the certificate once the parties have signed it — the document reference and how many signatories signed. This is what keeps the issued certificate and the executed document consistent in SHCIL's records, and it is expected for every stamp that gets used.

The selfReportExecution field on this request decides who sends that confirmation:

Left as false (default) — SignCare submits the confirmation for you, so there is nothing further to integrate.

Set to true — you submit the confirmation yourself by calling Report Execution (POST /api/v1/StockHoldingEStamp/consume/report) after the document has actually been executed. Choose this when the exact signatory count or execution date matters to you, or when signing happens on your side and only you know when it completed.

If the document is signed through SignCare eSign, the confirmation is sent automatically when the last signer completes — whatever this field is set to. It only decides what happens when signing takes place outside SignCare.

Getting the result later (isAsync):

By default (isAsync: false) this API waits up to about 40 seconds for SHCIL and answers with the stamp (200). Set isAsync: true if you would rather not wait:

→ The request is validated first. Any problem (missing fields, wrong article, insufficient wallet…) is still returned immediately as a normal 4xx.

→ If it is valid, the API accepts it and answers 202 at once with referenceId, reconMappingId and status: "ACCEPTED". There is no stamp in that response.

→ The stamp is generated in the background. When it is ready (or if it fails) the result is POSTed to your webhook — the same payload as described below. A webhook URL is therefore needed: send webhookUrl or configure one for your app.

→ Do not send the request again for the same referenceId: sending it again while it is still being processed just returns the same 202, and after it finished it is rejected as a duplicate.

The same 202 can also reach you without the flag, on a slow SHCIL day. Handle it the same way: wait for the webhook.

Webhook Delivery:

Once processing is complete, the generated stamp details are POSTed to the selected URL as JSON — the same fields as this API's response (referenceId, eStampId, documentId, userDocumentId, reconMappingId, status, certId, urn, certificateNo, certificateIssuedDate, base64FileData). When you send isAsync: true, or when StockHolding takes longer than the sync wait window, the webhook becomes the delivery channel — see the 202 response below.

This is not just a courtesy notification — for any request that returned 202, the webhook is the only place the outcome is delivered. There is no separate status-check endpoint. Make sure webhookUrl is set (directly, or configured for your app) before relying on this path.

Webhook payload — success (same shape as this API's 200 response):

{
  "success": true,
  "statusCode": 200,
  "message": "Success",
  "data": {
    "referenceId": "LOAN-2026-000123",
    "eStampId": "0d1f7f5e-...",
    "documentId": "8c2a41d9-...",
    "userDocumentId": 123456,
    "reconMappingId": "SC0000000000000ABCDE",
    "status": "COMPLETED",
    "certId": "IN-AS12345678901234X",
    "urn": "...",
    "certificateNo": "...",
    "certificateIssuedDate": "10-Jul-2026 11:42:05",
    "base64FileData": ""
  }
}
Webhook payload — failure (sent when a 202 request ultimately fails; nothing was

generated and nothing was charged — eStampId/documentId/certificate fields are all absent):

{
  "success": false,
  "statusCode": 422,
  "message": "eStamp generation failed. Please try again.",
  "data": {
    "referenceId": "LOAN-2026-000123",
    "status": "FAILED",
    "errorMessage": "eStamp generation failed. Please try again."
  }
}

Respond with HTTP 2xx to acknowledge. Treat deliveries as at-least-once and de-duplicate on referenceId/eStampId. A webhook is sent after persistence, so a 202 response does not mean the stamp already exists; wait for the final webhook payload.

Headers

  • X-API-KEY
    stringrequired
    Your secret API key for the environment you are calling (Stage and Live use different keys).
  • X-API-APP-ID
    stringrequired
    The App ID issued together with the key.
  • Content-Type
    stringrequired
    application/json

Body parameters

application/json
  • selfReportExecution
    booleanoptional
    Optional. Who reports execution of this stamp back to SHCIL. Defaults to false. SHCIL requires the execution of a stamped document to be confirmed against the certificate once the parties have signed it — the document reference and the number of signatories. This field tells us who will send that confirmation. <b>false (default)</b> — SignCare sends the confirmation for you. Choose this if you do not want to make an extra API call. <b>true</b> — you will send the confirmation yourself by calling `POST /api/v1/StockHoldingEStamp/consume/report` after the document has actually been executed. Recommended when the exact signatory count or execution date matters to you, or when signing is completed outside SignCare and only you know when it happened. If the document is signed through SignCare eSign, the confirmation is sent automatically when the last signer completes, whatever this field is set to. It only decides what happens when signing takes place outside SignCare.
  • referenceId
    stringrequired
    Required. Your unique reference id for this request (1-64 chars: A-Z a-z 0-9 - _). Must be unique across every Generate and Attach request — a request with an already-used referenceId is rejected.
    minLength 1maxLength 64pattern ^[a-zA-Z0-9-_]{1,64}$
  • documentInfo
    objectoptional
    The customer's document to be stamped — file name + base64 content.
    2 fields
    • name
      stringrequired
      Required. File name including extension (e.g. "Loan Agreement.pdf"). Attach APIs require a name ending in .pdf.
      minLength 1default Loan Agreement.pdf
    • content
      stringrequired
      Required. Base64-encoded file content (PDF or DOCX, max 5 MB). Attach APIs accept PDF only.
      minLength 1
  • firstParty
    objectoptional
    A party to the stamped document (first or second party).
    3 fields
    • name
      stringconditional
      Conditional: required whenever the party object is sent. Full name of the party.
      nullabledefault Rahul Sharma
    • pan
      stringconditional
      Conditional: required when this party pays the stamp duty and the stamp duty amount is greater than 2,00,000. 10-character PAN in the format AAAAA9999A. Validated whenever it is sent.
      nullablelength 10pattern ^[A-Z]{5}[0-9]{4}[A-Z]{1}$
    • mobile
      stringconditional
      Conditional: required when this party pays the stamp duty (and for both parties when the article needs both). Exactly 10 digits.
      nullablelength 10pattern ^\d{10}$
  • secondParty
    objectoptional
    A party to the stamped document (first or second party).
    3 fields
    • name
      stringconditional
      Conditional: required whenever the party object is sent. Full name of the party.
      nullabledefault Rahul Sharma
    • pan
      stringconditional
      Conditional: required when this party pays the stamp duty and the stamp duty amount is greater than 2,00,000. 10-character PAN in the format AAAAA9999A. Validated whenever it is sent.
      nullablelength 10pattern ^[A-Z]{5}[0-9]{4}[A-Z]{1}$
    • mobile
      stringconditional
      Conditional: required when this party pays the stamp duty (and for both parties when the article needs both). Exactly 10 digits.
      nullablelength 10pattern ^\d{10}$
  • stampDetails
    objectrequired
    Stamp-specific details: state, article, duty and description.
    6 fields
    • stateCode
      stringrequired
      Required. Two-letter state code (e.g. "AS", "DL"). Must be a supported state from the Master Details API.
      length 2default AS
    • articleCode
      integer (int32)required
      Required. Article code from the Master Details API. Article codes are unique per state, so use the code listed for the same stateCode.
      default 7
    • stampDutyAmount
      number (double)required
      Required. Stamp duty amount in INR. Must be a whole number greater than zero — decimals are rejected. Must be within the article's minDuty and maxDuty. Validated in the handler (not via DataAnnotation) so every stampDutyAmount error uses the same clean response shape.
      default 1000
    • stampDutyPaidBy
      stringrequired
      Required. Name of the duty payer. Must match firstParty.name or secondParty.name (case-insensitive).
      minLength 1default Rahul Sharma
    • considerationPrice
      number (double)conditional
      Conditional: required for articles whose stampDutyType is CONSIDERATION_PRICE, and then it must be between the article's minConsiderationPrice and maxConsiderationPrice. Optional for other articles. Transaction value in INR. Never negative.
      nullable
    • documentDescription
      stringoptional
      Optional. Property or document description printed on the certificate. Max 100 characters; letters, numbers and spaces only.
      nullablemaxLength 100
  • fields
    array<object>conditional
    Conditional: required for the template flow (SYSTEM articles); must be null in the pdf flow (when documentInfo is provided). Article field values, one entry per template field (ids from the Template Fields API). Each field id may appear only once.
    nullable
    Each item: 2 fields
    • id
      stringrequired
      Required. Field id, sent as a string, from the Template Fields API (`fields[].id`). Must be a field of the article's template and used only once.
      minLength 1
    • value
      stringrequired
      Required. Value to populate in the document. Checked against the field's rules from the Template Fields API: mandatory, NUMBER must be numeric, DATE_PICKER must be dd/MM/yyyy and not before minDate, DROPDOWN must be one of the options, TEXT/TEXTAREA must fit maxLength.
      minLength 1
  • webhookUrl
    stringconditional
    Conditional: required unless a webhook is configured for your app. The generated stamp details are POSTed to this URL. Use an HTTPS URL. The request value takes priority over the configured one.
    nullable
  • isAsync
    booleanoptional
    Optional. Defaults to false. <b>false (default)</b> — the API waits up to about 40 seconds for the stamp and answers with it (HTTP 200). Only when SHCIL is slow does it answer 202 instead, and the result then arrives on your webhook. <b>true</b> — the API does not wait. Once your request has been validated and accepted it answers <b>202 straight away</b> (no stamp in that response), and the stamp or the failure is delivered to your webhook. Use it when you prefer to handle every result on the webhook, or to test your 202 handling. Needs a webhook URL (this field or the one configured for your app). Request errors (missing fields, wrong article, insufficient wallet…) are still returned immediately as 4xx, because they are checked before the request is accepted.
Sample request body
{ "referenceId": "LOAN-2026-000123", "selfReportExecution": false, "documentInfo": { "name": "Loan Agreement.pdf", "content": "JVBERi0xLjQK" }, "firstParty": { "name": "Rahul Sharma", "pan": "ABCDE1234F", "mobile": "9876543210" }, "secondParty": { "name": "Acme Finance Ltd", "pan": "AABCA9876B", "mobile": "9123456780" }, "stampDetails": { "stateCode": "KA", "articleCode": 286, "stampDutyAmount": 2500, "stampDutyPaidBy": "Rahul Sharma", "considerationPrice": 500000, "documentDescription": "Loan agreement between Rahul Sharma and Acme Finance Ltd" }, "webhookUrl": "https://example.com/webhooks/estamp", "isAsync": false }

Responses

200Stamp generated — full result, including base64FileData.
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    objectoptional
    Flat response of the Generate E-Stamp API (external SHCIL flow). Certificate essentials in one level — the same stamp details are additionally delivered to the request's webhookUrl.
    12 fields
    • referenceId
      stringoptional
      The unique reference id you passed in the request.
      nullable
    • eStampId
      string (uuid)optional
      SignCare eStamp record id — use this to reference the stamp itself (e.g. Phase 2). Omitted on a 202 (ACCEPTED) response — the stamp does not exist yet at that point.
      nullable
    • documentId
      string (uuid)optional
      SignCare Document id of the stored document (documentInfo flow); omitted for template flow.
      nullable
    • userDocumentId
      integer (int64)optional
      SHCIL document id — quote it in support queries and pass it to the Report Execution API. Omitted on a 202 (ACCEPTED) response — the stamp has not been created yet at that point.
      nullable
    • reconMappingId
      stringoptional
      Correlation key (client-supplied or auto-generated).
      nullable
    • status
      stringoptional
      Document status (e.g. COMPLETED, IN_PROCESS).
      nullable
    • certId
      stringoptional
      No description.
      nullable
    • urn
      stringoptional
      No description.
      nullable
    • certificateNo
      stringoptional
      No description.
      nullable
    • certificateIssuedDate
      stringoptional
      No description.
      nullable
    • requestId
      stringoptional
      Reserved. Not currently returned — the field is always omitted from the response.
      nullable
    • base64FileData
      stringoptional
      Base64-encoded generated PDF — the stamped document (pdf flow) or the eStamp certificate (template flow). Omitted if the file could not be retrieved.
      nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": { "referenceId": "string", "eStampId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "documentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "userDocumentId": 0, "reconMappingId": "string", "status": "string", "certId": "string", "urn": "string", "certificateNo": "string", "certificateIssuedDate": "string", "requestId": "string", "base64FileData": "string" }, "messages": [ "string" ], "request_id": "string", "isBillable": false }
202Returned in two cases: (1) you sent isAsync: true — the request was accepted and is processed in the background, so you get this at once; (2) you did not, bu…same body as 200

Returned in two cases: (1) you sent isAsync: true — the request was accepted and is processed in the background, so you get this at once; (2) you did not, but StockHolding takes longer than the sync wait window (server-configured, currently ~40 seconds) to respond. Everything else about this API (fields, validation, flows) stays exactly the same — this is just what you get back instead of the 200 shown above. Example body:

{
  "data": {
    "referenceId": "LOAN-2026-000123",
    "reconMappingId": "SC0000000000000ABCDE",
    "status": "ACCEPTED"
  },
  "messages": [],
  "request_id": null,
  "success": true,
  "statusCode": 202,
  "message": "Accepted. Generation is taking longer than usual — the result will be delivered to your webhookUrl. Do not retry this referenceId."
}

With isAsync: true the body is identical except for the message: "Accepted. As requested (isAsync), the stamp is generated in the background and the result will be delivered to your webhookUrl. Do not retry this referenceId."

Why you got this instead of a 200: either you asked for it (isAsync: true), or the certificate is still being generated — StockHolding hasn't answered yet. This is not an error and not something wrong with your request.

What is NOT in this response: notice data only has referenceId, reconMappingId and status. There is no eStampId, no certId, no base64FileData — because the stamp does not exist yet at the moment this reply is sent. (A 202 with status: "PENDING" means SHCIL is still completing a stamp that was already created: eStampId and userDocumentId are present, and you are not charged until it is ready.)

Where the real result shows up: it is POSTed later to your webhookUrl — see the "Webhook payload — success / failure" examples above for the exact shape you'll receive. There is no separate status-check API, so if webhookUrl is not set (on the request or configured for your app), you will have no way to learn the outcome.

What to do: store this referenceId, stop calling this endpoint for it, and wait for the webhook. Calling again with the same referenceId just returns another 202 (harmless). Calling again with a new referenceId starts a second, independent generation — if both eventually succeed, you are charged twice.

400Invalid flow, unsupported state/article, invalid amount, unavailable webhook URL, or a referenceId that has already been used.
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 400
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 400 }
401Unauthorized
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 401
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 401 }
402Insufficient wallet balance, or API access not enabled for your app.
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    anyoptional
    No description.
    nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": null, "messages": [ "string" ], "request_id": "string", "isBillable": false }
409Rare: two requests with the same referenceId arrived at the same moment. Treat it like a duplicate referenceId — do not retry with a new one until you have checked the first request's outcome.
429Rate limit exceeded.same body as 402
500Unexpected processing failure.
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 500
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 500 }
Code samplescurl · Node.js · Python
Stage: uat-ext.signcare.io
curl -X POST 'https://uat-ext.signcare.io/api/v1/StockHoldingEStamp/generate' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'X-API-APP-ID: YOUR_APP_ID' \ -H 'Content-Type: application/json' \ -d '{ "referenceId": "LOAN-2026-000123", "selfReportExecution": false, "documentInfo": { "name": "Loan Agreement.pdf", "content": "JVBERi0xLjQK" }, "firstParty": { "name": "Rahul Sharma", "pan": "ABCDE1234F", "mobile": "9876543210" }, "secondParty": { "name": "Acme Finance Ltd", "pan": "AABCA9876B", "mobile": "9123456780" }, "stampDetails": { "stateCode": "KA", "articleCode": 286, "stampDutyAmount": 2500, "stampDutyPaidBy": "Rahul Sharma", "considerationPrice": 500000, "documentDescription": "Loan agreement between Rahul Sharma and Acme Finance Ltd" }, "webhookUrl": "https://example.com/webhooks/estamp", "isAsync": false }'
POST

/api/v1/StockHoldingEStamp/attach

#

Attach a generated E-Stamp onto your own PDF.

docs.signcare.io/core/api/estamp/shcil-estamp/#post-api-v1-stockholdingestamp-attach

This API attaches an e-Stamp generated via the template flow onto your own PDF — the e-Stamp certificate pages are added to your document and the stamped document is created.

→ No additional stamp duty is charged — the duty was already paid when the e-Stamp was generated.

→ Each e-Stamp can be attached only once.

How to consume:

  1. Generate an e-Stamp via the template flow of the Generate E-Stamp API and keep the eStampId from its response.

  2. Call this API with that eStampId, your PDF in documentInfo, and a new unique referenceId.

  3. Read the documentId from the response — to get the stamped document signed, call the eSign API with this documentId.

Request Body — entity fields:

FieldTypeRequiredDescription
referenceIdstringrequiredYour unique reference id for this request (1-64 chars: letters, numbers, hyphen, underscore). Must not have been used before by any Generate or Attach request.
eStampIdstring (uuid)requiredThe eStampId returned by the Generate E-Stamp API (template flow), or listed by the Get Procured E-Stamps API. It must belong to your account (otherwise 404) and must not already be attached. A stamp that belongs to a bulk batch is rejected — attach that whole batch with the Bulk Attach API using its batchId.
documentInfoobjectrequiredThe PDF the e-Stamp will be attached to.
documentInfo.namestringrequiredFile name with extension; it must end in .pdf (e.g. "Loan Agreement.pdf").
documentInfo.contentstringrequiredBase64-encoded PDF, max 5 MB. DOCX and other Office formats are not supported for attach.

Response — entity fields:

FieldTypeDescription
referenceIdstringThe unique reference id you passed in the request.
documentIdstring (uuid)Unique id of the stamped document. Pass this id to the eSign API to send the document for signature.
base64FileDatastringBase64-encoded stamped PDF (e-Stamp certificate pages + your document).

Headers

  • X-API-KEY
    stringrequired
    Your secret API key for the environment you are calling (Stage and Live use different keys).
  • X-API-APP-ID
    stringrequired
    The App ID issued together with the key.
  • Content-Type
    stringrequired
    application/json

Body parameters

application/json
  • referenceId
    stringrequired
    Required. Your unique reference id for this request (1-64 chars: A-Z a-z 0-9 - _). Must be new — a referenceId already used by any earlier Generate or Attach request is rejected.
    minLength 1maxLength 64pattern ^[a-zA-Z0-9-_]{1,64}$
  • eStampId
    string (uuid)required
    Required. The eStampId (uuid) returned by the Generate E-Stamp API (template flow) or listed by the Get Procured E-Stamps API. The e-Stamp must belong to your account and must not already be attached.
  • documentInfo
    objectrequired
    The customer's document to be stamped — file name + base64 content.
    2 fields
    • name
      stringrequired
      Required. File name including extension (e.g. "Loan Agreement.pdf"). Attach APIs require a name ending in .pdf.
      minLength 1default Loan Agreement.pdf
    • content
      stringrequired
      Required. Base64-encoded file content (PDF or DOCX, max 5 MB). Attach APIs accept PDF only.
      minLength 1
Sample request body
{ "referenceId": "string", "eStampId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "documentInfo": { "name": "Loan Agreement.pdf", "content": "string" } }

Responses

200OK
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    objectoptional
    Response of the Attach eStamp API.
    3 fields
    • referenceId
      stringoptional
      The unique reference id you passed in the request.
      nullable
    • documentId
      string (uuid)optional
      SignCare Document id of the stamped document. Pass this id to the eSign API to send it for signature.
    • base64FileData
      stringoptional
      Base64-encoded stamped PDF (eStamp certificate pages + your document).
      nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": { "referenceId": "string", "documentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "base64FileData": "string" }, "messages": [ "string" ], "request_id": "string", "isBillable": false }
400Bad Request
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 400
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 400 }
401Unauthorized
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 401
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 401 }
404Not Found
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    anyoptional
    No description.
    nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": null, "messages": [ "string" ], "request_id": "string", "isBillable": false }
429Too Many Requestssame body as 404
500Internal Server Error
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 500
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 500 }
Code samplescurl · Node.js · Python
Stage: uat-ext.signcare.io
curl -X POST 'https://uat-ext.signcare.io/api/v1/StockHoldingEStamp/attach' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'X-API-APP-ID: YOUR_APP_ID' \ -H 'Content-Type: application/json' \ -d '{ "referenceId": "string", "eStampId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "documentInfo": { "name": "Loan Agreement.pdf", "content": "string" } }'
POST

/api/v1/StockHoldingEStamp/procured

#

Get all procured single E-Stamps ready to be attached — use `eStampId` with the Attach API. Bulk-generated stamps are listed separately by the Get Bulk Procured E-Stamps API.

docs.signcare.io/core/api/estamp/shcil-estamp/#post-api-v1-stockholdingestamp-procured

This API is your single-stamp inventory: every stamp procured with the Generate API and not yet attached to a document, newest first. Anything listed here is safe to attach; once attached it disappears from this list.

Next step:POST /api/v1/StockHoldingEStamp/attach — pass eStampId.

For stamps from the Bulk Generate API use POST /api/v1/StockHoldingEStamp/bulk-procured and bulk-attach instead.

Response — entity fields:

FieldTypeDescription
countnumberNumber of procured single stamps returned.
stampsarrayProcured single stamps, newest first.
stamps[].eStampIdstring (uuid)Unique id of the e-Stamp — pass to the Attach E-Stamp API.
stamps[].userDocumentIdnumberSHCIL document id — pass in userDocumentIds of the Report Execution API.
stamps[].reconMappingIdstringCorrelation key from generation time.
stamps[].statusstringAlways COMPLETED — procured, ready to attach.
stamps[].certIdstringCertificate id issued by SHCIL.
stamps[].urnstringUnique reference number.
stamps[].certificateIssuedDatestringCertificate issue date and time.
stamps[].articleNamestringName of the article the e-Stamp was generated with.
stamps[].stampDetailsobjectStamp details from generation time — stateCode, articleCode, stampDutyAmount, stampDutyPaidBy, considerationPrice, documentDescription.
stamps[].firstPartyobjectFirst party details — name, pan, mobile.
stamps[].secondPartyobjectSecond party details — name, pan, mobile. Omitted when not provided at generation.
stamps[].createdOnstring (date-time)When the stamp was generated (UTC).
stamps[].base64FileDatastringBase64 PDF of the stamp certificate. Omitted if the file could not be retrieved.

Fields with no value are omitted from the response.

Request body (optional filters):

FieldTypeDescription
estampIdsarray of string (uuid)Optional. Only stamps whose eStampId is in this list are returned.
stateCodestringOptional. Only stamps from this two-letter state code (e.g. "DL") are returned.

Omit both fields (or send an empty body) to get everything.

Headers

  • X-API-KEY
    stringrequired
    Your secret API key for the environment you are calling (Stage and Live use different keys).
  • X-API-APP-ID
    stringrequired
    The App ID issued together with the key.
  • Content-Type
    stringrequired
    application/json

Body parameters

application/json
  • estampIds
    array<string>optional
    Optional. When provided, only stamps whose eStampId (uuid) is in this list are returned.
    nullable
  • stateCode
    stringoptional
    Optional. When provided, only stamps from this two-letter state code (e.g. "DL") are returned.
    nullable
Sample request body
{ "estampIds": [ "a1b2c3d4-e5f6-7890-abcd-ef1234567890" ], "stateCode": "string" }

Responses

200OK
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    objectoptional
    Response of the Get Procured E-Stamps API.
    2 fields
    • count
      integer (int32)optional
      Number of procured eStamps.
    • stamps
      array<object>optional
      Procured eStamps, newest first.
      nullable
      Each item: 15 fields
      • eStampId
        string (uuid)optional
        Unique id of the eStamp — pass this to the Attach E-Stamp API. Present for single stamps only; omitted for batches (use API.Models.SignCare.StockHolding.ShcilProcuredStamp.BatchId).
        nullable
      • batchId
        string (uuid)optional
        Unique id of the bulk batch — pass this to the Bulk Attach API. Present for batches only; omitted for single stamps (use API.Models.SignCare.StockHolding.ShcilProcuredStamp.EStampId).
        nullable
      • userDocumentId
        integer (int64)optional
        SHCIL document id of this stamp — pass it in `userDocumentIds` of the Report Execution API when the document is signed outside SignCare. Present for single stamps only; a batch has one per member, so for batches see `stamps[].userDocumentId`.
        nullable
      • reconMappingId
        stringoptional
        Correlation key generated at generation time.
        nullable
      • status
        stringoptional
        Status of the eStamp (always COMPLETED — procured, ready to attach).
        nullable
      • certId
        stringoptional
        No description.
        nullable
      • urn
        stringoptional
        No description.
        nullable
      • certificateIssuedDate
        stringoptional
        No description.
        nullable
      • articleName
        stringoptional
        Name of the article the eStamp was generated with.
        nullable
      • stampDetails
        objectoptional
        Stamp-specific details: state, article, duty and description.
        6 fields
        • stateCode
          stringrequired
          Required. Two-letter state code (e.g. "AS", "DL"). Must be a supported state from the Master Details API.
          length 2default AS
        • articleCode
          integer (int32)required
          Required. Article code from the Master Details API. Article codes are unique per state, so use the code listed for the same stateCode.
          default 7
        • stampDutyAmount
          number (double)required
          Required. Stamp duty amount in INR. Must be a whole number greater than zero — decimals are rejected. Must be within the article's minDuty and maxDuty. Validated in the handler (not via DataAnnotation) so every stampDutyAmount error uses the same clean response shape.
          default 1000
        • stampDutyPaidBy
          stringrequired
          Required. Name of the duty payer. Must match firstParty.name or secondParty.name (case-insensitive).
          minLength 1default Rahul Sharma
        • considerationPrice
          number (double)conditional
          Conditional: required for articles whose stampDutyType is CONSIDERATION_PRICE, and then it must be between the article's minConsiderationPrice and maxConsiderationPrice. Optional for other articles. Transaction value in INR. Never negative.
          nullable
        • documentDescription
          stringoptional
          Optional. Property or document description printed on the certificate. Max 100 characters; letters, numbers and spaces only.
          nullablemaxLength 100
      • firstParty
        objectoptional
        A party to the stamped document (first or second party).
        3 fields
        • name
          stringconditional
          Conditional: required whenever the party object is sent. Full name of the party.
          nullabledefault Rahul Sharma
        • pan
          stringconditional
          Conditional: required when this party pays the stamp duty and the stamp duty amount is greater than 2,00,000. 10-character PAN in the format AAAAA9999A. Validated whenever it is sent.
          nullablelength 10pattern ^[A-Z]{5}[0-9]{4}[A-Z]{1}$
        • mobile
          stringconditional
          Conditional: required when this party pays the stamp duty (and for both parties when the article needs both). Exactly 10 digits.
          nullablelength 10pattern ^\d{10}$
      • secondParty
        objectoptional
        A party to the stamped document (first or second party).
        3 fields
        • name
          stringconditional
          Conditional: required whenever the party object is sent. Full name of the party.
          nullabledefault Rahul Sharma
        • pan
          stringconditional
          Conditional: required when this party pays the stamp duty and the stamp duty amount is greater than 2,00,000. 10-character PAN in the format AAAAA9999A. Validated whenever it is sent.
          nullablelength 10pattern ^[A-Z]{5}[0-9]{4}[A-Z]{1}$
        • mobile
          stringconditional
          Conditional: required when this party pays the stamp duty (and for both parties when the article needs both). Exactly 10 digits.
          nullablelength 10pattern ^\d{10}$
      • createdOn
        string (date-time)optional
        Date and time (UTC) the eStamp was generated.
        nullable
      • stamps
        array<object>optional
        The individual certificates inside a bulk batch. Present for batches only — omitted entirely for single stamps, whose certificate details sit at the top level.
        nullable
        Each item: 6 fields
        • userDocumentId
          integer (int64)optional
          SHCIL document id of this individual stamp — pass it in `userDocumentIds` of the Report Execution API when the document is signed outside SignCare.
        • reconMappingId
          stringoptional
          No description.
          nullable
        • certId
          stringoptional
          No description.
          nullable
        • urn
          stringoptional
          No description.
          nullable
        • certificateIssuedDate
          stringoptional
          No description.
          nullable
        • stampDutyAmount
          number (double)optional
          Stamp duty paid for this individual stamp.
      • base64FileData
        stringoptional
        Base64-encoded certificate PDF — the stamp's own certificate for a single stamp, or the batch's combined certificate PDF (all stamps merged) for a batch. Omitted if the file could not be retrieved. Always the last field in the object.
        nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": { "count": 0, "stamps": [ { "eStampId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "batchId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "userDocumentId": 0, "reconMappingId": "string", "status": "string", "certId": "string", "urn": "string", "certificateIssuedDate": "string", "articleName": "string", "stampDetails": { "stateCode": "AS", "articleCode": 7, "stampDutyAmount": 1000, "stampDutyPaidBy": "Rahul Sharma" }, "firstParty": { "name": "Rahul Sharma", "pan": "string", "mobile": "string" }, "secondParty": { "name": "Rahul Sharma", "pan": "string", "mobile": "string" }, "createdOn": "2026-01-01T00:00:00Z", "stamps": [ { "userDocumentId": 0, "reconMappingId": "string", "certId": "string", "urn": "string", "certificateIssuedDate": "string", "stampDutyAmount": 0 } ], "base64FileData": "string" } ] }, "messages": [ "string" ], "request_id": "string", "isBillable": false }
400Bad Request
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 400
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 400 }
401Unauthorized
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 401
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 401 }
429Too Many Requests
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    anyoptional
    No description.
    nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": null, "messages": [ "string" ], "request_id": "string", "isBillable": false }
500Internal Server Error
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 500
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 500 }
Code samplescurl · Node.js · Python
Stage: uat-ext.signcare.io
curl -X POST 'https://uat-ext.signcare.io/api/v1/StockHoldingEStamp/procured' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'X-API-APP-ID: YOUR_APP_ID' \ -H 'Content-Type: application/json' \ -d '{ "estampIds": [ "a1b2c3d4-e5f6-7890-abcd-ef1234567890" ], "stateCode": "string" }'
POST

/api/v1/StockHoldingEStamp/consume/report

#

Report document execution to SHCIL.

docs.signcare.io/core/api/estamp/shcil-estamp/#post-api-v1-stockholdingestamp-consume-report

Use this API when you manage your own eSign flow outside SignCare and need to notify SHCIL that all signatories have completed signing on a stamped document.

Pass userDocumentIds as an array — one element for a single stamp, or one element per stamp for a bulk batch. Signer counts apply equally to all entries.

SHCIL will update each certificate record to reflect the signed state.

When to call this: SHCIL requires the execution of a stamped document to be confirmed against the certificate once the parties have signed. Call this endpoint after the document has actually been executed.

You do not need to call it when the document was signed through SignCare eSign — the confirmation is sent automatically on the final signature. You also do not need to call it if you left selfReportExecution as false on the Generate request, in which case SignCare submits the confirmation for you. Set selfReportExecution to true at generate time and call this endpoint when you want the exact signatory count and execution date to come from you.

Request Body — entity fields:

FieldTypeRequiredDescription
userDocumentIdsarray of numberrequiredSHCIL userDocumentIds to report; at least one, each a positive number belonging to your account. Single stamp: one element. Several stamps: one element per stamp. Each value is the userDocumentId returned by the Generate API or the Get Procured E-Stamps API.
executionDateTimestringoptionalExecution date and time in dd/MM/yyyy HH:mm:ss or dd/MM/yyyy format (the current IST time is added to a date-only value). Defaults to the current IST date and time when omitted.
noOfSignatoriesnumberoptionalTotal number of signatories on the document. Defaults to 0. Send the actual count — it is recorded against the certificate at SHCIL.
noOfDigitalSignaturesnumberoptionalSignatories who signed with a DSC (Class 2 / Class 3 digital signature certificate). Defaults to 0.
noOfElectronicSignaturesnumberoptionalSignatories who signed with an Aadhaar-based eSign or OTP-based electronic signature. Defaults to 0.

Response — entity fields (single stamp, HTTP 200):

FieldTypeDescription
messagestringConfirmation message from SHCIL.

A single stamp that is not found (or is not yours) returns HTTP 404. A stamp whose execution was already reported is returned as successful and is never filed twice.

Response — entity fields (several stamps, HTTP 200 all ok / HTTP 207 partial failure):

FieldTypeDescription
resultsarrayOne entry per userDocumentId submitted.
results[].userDocumentIdnumberThe stamp this result corresponds to.
results[].successbooleanTrue if SHCIL acknowledged this stamp successfully.
results[].messagestringSHCIL confirmation message or error description.

Headers

  • X-API-KEY
    stringrequired
    Your secret API key for the environment you are calling (Stage and Live use different keys).
  • X-API-APP-ID
    stringrequired
    The App ID issued together with the key.
  • Content-Type
    stringrequired
    application/json

Body parameters

application/json
  • userDocumentIds
    array<integer>required
    Required. One or more SHCIL userDocumentIds (positive numbers) to report; at least one. Single stamp: pass one element. Several stamps (for example a bulk batch): pass one element per stamp. Each value is the userDocumentId returned by the Generate API or the Get Procured E-Stamps API, and must belong to your account.
  • executionDateTime
    stringoptional
    Optional. Date (and optionally time) when the document was executed. Accepted formats: `dd/MM/yyyy HH:mm:ss` (e.g. "27/07/2026 14:30:00") or date-only `dd/MM/yyyy` (e.g. "27/07/2026") — current IST time is appended automatically. Omit entirely to use the current IST date and time.
    nullable
  • noOfSignatories
    integer (int32)optional
    Optional. Total number of signatories on the document. Defaults to 0 when omitted. Applies to every id in userDocumentIds.
    nullable
  • noOfDigitalSignatures
    integer (int32)optional
    Optional. Number of signatories who signed using a DSC (Digital Signature Certificate) — hardware USB token, PKI-based (Class 2 / Class 3). Defaults to 0 when omitted.
    nullable
  • noOfElectronicSignatures
    integer (int32)optional
    Optional. Number of signatories who signed using Aadhaar-based eSign or OTP-based electronic signature — not DSC. Defaults to 0 when omitted.
    nullable
Sample request body
{ "userDocumentIds": [ 0 ] }

Responses

200OK
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    objectoptional
    Response of the Report Execution API.
    2 fields
    • message
      stringoptional
      Confirmation message from SHCIL (single-stamp path).
      nullable
    • results
      array<object>optional
      Per-stamp results (multi-stamp path). One entry per userDocumentId submitted. Absent when a single userDocumentId was used.
      nullable
      Each item: 3 fields
      • userDocumentId
        integer (int64)optional
        The SHCIL userDocumentId this result corresponds to.
      • success
        booleanoptional
        True if SHCIL acknowledged this stamp successfully.
      • message
        stringoptional
        SHCIL confirmation message or reason for failure.
        nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": { "message": "string", "results": [ { "userDocumentId": 0, "success": false, "message": "string" } ] }, "messages": [ "string" ], "request_id": "string", "isBillable": false }
207Successsame body as 200
400Bad Request
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 400
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 400 }
401Unauthorized
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 401
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 401 }
404Not Found
  • success
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 200
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • data
    anyoptional
    No description.
    nullable
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • isBillable
    booleanoptional
    Whether this request was charged: true if it was, false if not. Errors always show false. Flows that finish later (ITR Fetch, SHCIL eStamp, DigiLocker, eSign) show false here, because the charge is made when the result is ready. eSign charges each signer when they complete signing. DigiLocker deducts the credit when the link is created and refunds it automatically if the link expires before the user completes it.
Example
{ "success": false, "statusCode": 200, "message": "XXXXX", "data": null, "messages": [ "string" ], "request_id": "string", "isBillable": false }
429Too Many Requestssame body as 404
500Internal Server Error
  • data
    objectoptional
    No description.
  • messages
    array<string>optional
    No description.
    nullable
  • request_id
    stringoptional
    No description.
    nullable
  • success
    booleanoptional
    No description.
    default false
  • message
    stringoptional
    No description.
    nullabledefault XXXXX
  • isBillable
    booleanoptional
    No description.
  • statusCode
    integer (int32)optional
    No description.
    default 500
Example
{ "data": {}, "messages": [ "string" ], "request_id": "string", "success": false, "message": "XXXXX", "isBillable": false, "statusCode": 500 }
Code samplescurl · Node.js · Python
Stage: uat-ext.signcare.io
curl -X POST 'https://uat-ext.signcare.io/api/v1/StockHoldingEStamp/consume/report' \ -H 'X-API-KEY: YOUR_API_KEY' \ -H 'X-API-APP-ID: YOUR_APP_ID' \ -H 'Content-Type: application/json' \ -d '{ "userDocumentIds": [ 0 ] }'