Stamp & Sign APIHMAC secured

Build e-stamp and e-sign agreement flows from one API surface.

Partner API for creating e-stamp and e-sign agreement transactions — upload the agreement document, set parties, billing, and stamp/sign options from your system. Continue with send_for_estamp into pay → stamp → sign. All paths are under /api/external/stampNsign/*.

UAT: https://ws-uat.realtimate.in/api
View EndpointsView Webhooks
4
documented endpoints
4
POST operations
0
PUT operations

Authenticate

Every partner call uses the integration JWT in auth. After create, store agreementOwnershipToken and send it on further API calls (agreement-ownership-token on send_for_estamp and estamp-recovery).

Sign Payloads

Create / update: sign the exact payload form-field string. send_for_estamp / estamp-recovery: sign JSON.stringify(request body). Header is x-payload-signature = HMAC-SHA256(..., ownership HMAC secret). Required on UAT/Prod; skipped on localhost.

Verify Webhooks

Callbacks are POSTed to your registered callback URL with X-Payload-Signature. JSON events: payment_order_created, payment_captured, status_updated (sign the raw body). doc_updated is multipart/form-data with the current PDF — sign the payload form field only, same as create. Not sent for transactions created in the Realtimate UI.

POST/external/stampNsign/new-agreement

Create New Agreement

Creates a new Stamp & Sign transaction. There is no status-read API — keep document status on your side from responses and webhooks.

Signature: Required

Request Headers

auth
Integration JWT we issue; send it as-is on every partner call
x-payload-signature
HMAC-SHA256(payload form-field string, ownership HMAC secret)
Content-Type
multipart/form-data

Multipart Form Fields

KeyTypeDescription
payloadstringJSON string of the request body. Sign this exact string value for x-payload-signature
txn-filefileAgreement document — PDF / DOC / DOCX, max 5 MB

Mandatory Body (payload JSON)

KeyTypeDescription
externalRefstringUnique external reference for this agreement. Duplicate values are rejected
documentTypestringDocument category. Allowed values include: "rentalagm", "mouforsale", "serviceagm", "agmforsale", "saleagm", "misc", "templatedAgm", and other TxnDocType values
servicesobjectAt least one of services.estamp or services.esign must be true
firstPartiesArrayNon-empty. Each party requires countryCode, phone, firstName, lastName. email required only when esignMethod is "email"
secondPartiesArrayNon-empty. Same fields as firstParties
billingAddressObjectRequires addressLine1, city, state, pincode. addressLine2 / addressLine3 / country optional (country defaults to "IND")
considerationAmountnumberNon-negative. Used for Karnataka rental stamp suggestion (min(500, consideration × 0.005))
paymentBystringRequired. Phone of the payer (digits, or countryCode+phone). Must match a first or second party phone. Same role as the website “Who will pay?” picker — stored as that party’s _id

Conditional Body (payload JSON)

Required when the corresponding service is enabled in services.

KeyTypeDescription
stampStatestringRequired when services.estamp is true. Stamp state code (e.g. "KA")
eStampAmountnumberRequired when services.estamp is true. Must be between 20 and 500. Special rules apply for KA rental / service and MOU for sale (₹500)
esignMethod"email" | "aadhaar"Required when services.esign is true. Mapped internally to email-only or aadhaar. For "email", every party (including stakeholders) must have a valid email. For "aadhaar", email is optional

Optional Body (payload JSON)

KeyTypeDescription
titlestringOptional. Custom document title. Non-empty if provided
stakeholdersArraySame party shape as first/second parties. Must not reuse a first/second party phone (or email, if provided)
propertyAddressObjectSame shape as billingAddress. Requires addressLine1, city, state, pincode when included
startDatestringISO 8601 date / timestamp
periodInMonthsnumberPositive integer
escrowOwnershipTokenstringEscrow ownership JWT ({ accountNum }, same as escrow API ownership-token). On create, links this Stamp & Sign into that escrow's stampNSignTxnIds. Omit = no link, no fail

Form File (mandatory)

KeyTypeDescription
txn-filefileAgreement PDF/DOC/DOCX, max 5 MB

Behavior Notes

  • If "escrowOwnershipToken" is sent on create, this Stamp & Sign id is linked into that escrow's stampNSignTxnIds. If omitted, create still succeeds with no escrow link.
  • Provided party emails and phones must be unique across firstParties, secondParties, and stakeholders. Stakeholders cannot be the same person as a first or second party.
  • paymentBy is required and must match a first or second party phone.
  • Create fails if externalRef already exists.
  • Create response includes "agreementOwnershipToken" — store it and send on further API calls (keep auth as the integration JWT) send_for_estamp and estamp-recovery expect it as agreement-ownership-token.
  • There is no status-read endpoint — maintain document status on your side from HTTP responses and webhooks (payment_order_created, payment_captured, doc_updated, status_updated).
  • After create, call send_for_estamp to start the next step. If stamp is needed, we send a payment link first, then stamp (and sign if you asked for it).
  • Do not send "agreementOwnershipToken" on create — the request will be rejected. Use update instead.
  • On success, review WhatsApp + email notifications are sent to the API user.
  • Partner integrations: use the integration JWT in auth on every call.

Sample Request

// multipart/form-data

// Form field: payload (string — sign this exact string)
{
  "externalRef": "CRM-SNS-1001",
  "documentType": "rentalagm",
  "title": "Rental Agreement — Brigade Metropolis",
  "services": {
    "estamp": true,
    "esign": true
  },
  "stampState": "KA",
  "eStampAmount": 500,
  "considerationAmount": 100000,
  "esignMethod": "aadhaar",
  "paymentBy": "9999999999",
  "firstParties": [
    {
      "countryCode": "91",
      "phone": "9999999999",
      "email": "owner@example.com",
      "firstName": "Mayank",
      "lastName": "Jain"
    }
  ],
  "secondParties": [
    {
      "countryCode": "91",
      "phone": "8888888888",
      "email": "tenant@example.com",
      "firstName": "Priya",
      "lastName": "Sharma"
    }
  ],
  "stakeholders": [
    {
      "countryCode": "91",
      "phone": "7777777777",
      "email": "witness@example.com",
      "firstName": "Arjun",
      "lastName": "Mehta"
    }
  ],
  "billingAddress": {
    "addressLine1": "Tower A, Brigade Metropolis",
    "addressLine2": "Whitefield",
    "addressLine3": "Mahadevapura",
    "city": "Bengaluru",
    "state": "Karnataka",
    "pincode": "560097",
    "country": "IND"
  },
  "propertyAddress": {
    "addressLine1": "Flat 5107, UKN Esperanza",
    "addressLine2": "Devanahalli Road",
    "addressLine3": "Bagalur",
    "city": "Bengaluru",
    "state": "Karnataka",
    "pincode": "560066",
    "country": "IND"
  },
  "startDate": "2026-08-01",
  "periodInMonths": 11,
  "escrowOwnershipToken": "<escrow ownership JWT from escrow API>"
}

// Form field: txn-file
agreement.pdf

Headers: auth, x-payload-signature, Content-Type: multipart/form-data

Sample Responses

Success Response
{
  "result": "success",
  "status": 200,
  "message": "Transaction created successfully",
  "agreementOwnershipToken": "<token — store and send on further API calls>"
}
Duplicate externalRef
{
  "result": "badrequest",
  "status": 400,
  "message": "externalRef 'CRM-SNS-1001' is already being used with another transaction"
}

Webhook Notifications

Callbacks for Stamp & Sign events

After you create a transaction via the Stamp & Sign API (externalRef required), we POST status updates to your registered callback URL. Every payload includes event and externalRef. JSON events: payment_order_created, payment_captured, status_updated. doc_updated is multipart/form-data (payload JSON + current PDF as document). HMAC X-Payload-Signature: for JSON events sign the raw body; for doc_updated sign the payload form-field string only (same as create). Webhooks are not sent for transactions created in the Realtimate UI.

Payment Link Created

A single callback is delivered to your endpoint.

Sent when the payment link is ready for the payer after send_for_estamp.

Verify incoming webhook

const expected = crypto
  .createHmac("sha256", hmacSecret)
  .update(rawBodyString)
  .digest("hex");
// timing-safe compare with header X-Payload-Signature
// then JSON.parse(rawBodyString)
{
  "event": "payment_order_created",
  "externalRef": "CRM-SNS-1001",
  "paymentLink": "https://rzp.io/...",
  "message": "Payment order created"
}

Error Shapes

Status codes

Create / update / send_for_estamp / estamp-recovery responses include result, status (numeric HTTP code, e.g. 200 / 400), and message — never "OK" or a string status code.

HTTPresultTypical causeMessage
400
Bad Request
badrequestMissing firstName / lastName / countryCode / phoneMissing fields in firstParties[0]: firstName, lastName
badrequestEmail e-sign and email missingMissing fields in firstParties[0]: email
badrequestBad email formatInvalid email in firstParties[0]: not-an-email
badrequestPhone not 6–15 digitsInvalid phone in firstParties[0]: 123
badrequestesignMethod: email and a party has no valid emailWhen esignMethod is email, all parties must have a valid email (stakeholders[0])
badrequestpaymentBy phone not on a first/second partypaymentBy '7777777777' does not match any first or second party phone
badrequestDuplicate externalRefexternalRef 'CRM-SNS-1001' is already being used with another transaction
badrequestRead-only updateTransaction cannot be updated in its current status
badrequestTransaction does not include e-stampingThis transaction does not include e-stamping (services.estamp was false)
badrequestNo e-stamp or e-sign serviceTransaction has no e-stamp or e-sign service
badrequestNo agreement documentTransaction has no agreement document
badrequestNo invoice for estamp-recoveryNo invoice found for this transaction.
badrequestInvoice not paid (estamp-recovery)Invoice is not paid (status: {status}). estamp-recovery is only for after payment
badrequestWrong status for send_for_estampTransaction cannot be sent for e-stamp (status: {status}) (allowed: created, wait4payment, wait4estamp)
401
Unauthorized
autherror / unauthorizedMissing/invalid auth, x-payload-signature, or agreement-ownership-token—
403
Forbidden
notallowedToken ownership mismatch / not allowed—
500
Internal Server Error
exceptionUnexpected server error{error.message} or Internal Server Error