---
title: "Create a stamp order (one state per order)"
method: POST
path: "/stamp-orders"
tags: ["Stamp orders"]
---

# Create a stamp order (one state per order)

`POST /stamp-orders`

## Request

- Base URL: `https://esahayak.io/api/v1`
- URL: `https://esahayak.io/api/v1/stamp-orders`
- Auth: HTTP bearer

Commits an order of stamps for ONE state. This RESERVES the total face value from your stamp-duty wallet — and, when your workspace is prepaid on fees, the gross service fee from your service-fee wallet — failing with 402 (see `details.wallet`) if either `available` is short. **All stamps in the order must be for the same state** — mixed-state payloads are rejected with per-row error code `MIXED_STATE` (the create fails fast on that check alone; run `/validate` to see the full per-row picture in one call). Split multi-state work into one order per state, each with its own `Idempotency-Key`. Call `/validate` first to catch row errors and see the quote without spending. Rows in states whose live availability switch is off (see `GET /states/availability`) are rejected with per-row error code `STATE_INACTIVE`. The `Idempotency-Key` is mandatory: a retry with the same key returns the original order instead of creating a duplicate. Returns immediately — fulfilment is async (poll the order or subscribe to webhooks).

**Webhook payload examples**

Every webhook delivery is a signed POST with this outer shape:

```json
{
  "type": "stamp.processing_started",
  "timestamp": "2026-06-16T05:10:01.000Z",
  "data": {}
}
```

`stamp.processing_started` data:

```json
{
  "stampId": "019ecebf-36ef-70b3-bb1e-cb4bf6caf566",
  "bulkOrderId": "019ecebf-36ac-7d02-a18e-367ad311e704",
  "clientReference": "po-7-1",
  "partnerBatchId": "019ecebf-4865-79cb-bd12-d3a22a1356bd",
  "acceptedAt": "2026-06-16T05:10:00.000Z"
}
```

`order.processing_started` data:

```json
{
  "bulkOrderId": "019ecebf-36ac-7d02-a18e-367ad311e704",
  "partnerBatchId": "019ecebf-4865-79cb-bd12-d3a22a1356bd",
  "acceptedAt": "2026-06-16T05:10:00.000Z",
  "stamps": [
    {
      "stampId": "019ecebf-36ef-70b3-bb1e-cb4bf6caf566",
      "clientReference": "po-7-1"
    }
  ],
  "clientReferences": ["po-7-1"]
}
```

`stamp.verified` data:

```json
{
  "stampId": "019ecebf-36ef-70b3-bb1e-cb4bf6caf566",
  "bulkOrderId": "019ecebf-36ac-7d02-a18e-367ad311e704",
  "clientReference": "po-7-1",
  "certificateNumber": "IN-MH12345678901234X",
  "documentUrl": "https://storage.example.com/signed/stamp-paper.png",
  "documentUrlExpiresInSeconds": 900,
  "certificateUrl": "https://esahayak.io/api/v1/stamps/019ecebf-36ef-70b3-bb1e-cb4bf6caf566/certificate"
}
```

`order.fulfilled` data:

```json
{
  "bulkOrderId": "019ecebf-36ac-7d02-a18e-367ad311e704",
  "stamps": [
    {
      "stampId": "019ecebf-36ef-70b3-bb1e-cb4bf6caf566",
      "clientReference": "po-7-1"
    }
  ],
  "clientReferences": ["po-7-1"]
}
```

`stamp.cancelled` data:

```json
{
  "stampId": "019ecebf-36ef-70b3-bb1e-cb4bf6caf566",
  "bulkOrderId": "019ecebf-36ac-7d02-a18e-367ad311e704",
  "clientReference": "po-7-1"
}
```

`order.cancelled` data:

```json
{
  "bulkOrderId": "019ecebf-36ac-7d02-a18e-367ad311e704",
  "cancelledStamps": 1
}
```

`wallet.low_balance` data:

```json
{
  "walletId": "019ecebf-72e1-7fac-92be-17d5e02ff5d8",
  "available": 750,
  "threshold": 1000
}
```

## Headers

- `Idempotency-Key` string, required

## Request body

- object
  - `reference` string: Optional. Your parent order / PO reference.
  - `acknowledgeWarnings` boolean: Optional. Set true only after /stamp-orders/validate returns non-blocking warnings you accept.
  - `stamps` StampInput[], required: Required. One or more stamp lines to procure — all for the SAME state (one order = one state). Each line can set quantity to create multiple identical stamps.
    - `clientReference` string: Optional. Your per-stamp id, echoed in stamp/order responses and webhooks.
    - `state` string, required: Required. State name or code. Must be the SAME state on every stamp in the order (one order = one state) — differing rows are rejected with per-row code `MIXED_STATE`.
    - `category` 'e_stamp' | 'traditional_stamp': Optional. Defaults to `e_stamp`.
    - `articleCode` string, required: Required. Validated against the state's article catalogue where one exists; free-text otherwise.
    - `denomination` number, required: Required. Face value (₹). Validated vs the state min/max, the article's minimum / fixed allowedAmounts, and the state's amountMultipleOf (Uttar Pradesh: multiples of ₹10 — an off-step value fails with AMOUNT_NOT_MULTIPLE).
    - `stampDutyPayer` string: Optional. The party paying stamp duty: the first or the second party's name (case, spacing and punctuation such as . , - & ( ) / _ ignored; other characters must match), or `FIRST_PARTY` / `SECOND_PARTY`. Defaults to the first party; stored as that party's name. In states where the sheet is endorsed to the first party (Telangana), the payer is always the first party. A name that is neither party, or `SECOND_PARTY` on a stamp with no second party, is rejected with per-row code `PAYER_NOT_A_PARTY`.
    - `payerPhone` string: Optional payer phone number.
    - `payerGender` 'MALE' | 'FEMALE' | 'OTHER': Required over ₹500 in catalogue states.
    - `firstParty` StampParty, required: A party to the instrument. `name` is required; address / father-or-husband name / gender are required only in states whose article requires them (see /states/{code}).
      - `name` string, required
      - `address` string: Party address. Required where isFirstPartyAddressRequired; optional (recommended) elsewhere — e.g. Haryana. Stored on the stamp and shown to the fulfilling partner.
      - `phone` string
      - `fatherHusbandName` string: Required where the state requires it.
      - `gender` 'MALE' | 'FEMALE' | 'OTHER': Payer gender — required over ₹500 in catalogue states.
    - `secondParty` StampParty: A party to the instrument. `name` is required; address / father-or-husband name / gender are required only in states whose article requires them (see /states/{code}).
      - `name` string, required
      - `address` string: Party address. Required where isFirstPartyAddressRequired; optional (recommended) elsewhere — e.g. Haryana. Stored on the stamp and shown to the fulfilling partner.
      - `phone` string
      - `fatherHusbandName` string: Required where the state requires it.
      - `gender` 'MALE' | 'FEMALE' | 'OTHER': Payer gender — required over ₹500 in catalogue states.
    - `purpose` string: Optional. Purpose / instrument description.
    - `district` string: Optional district code. Strongly recommended in sroRequired states (e.g. Haryana, Himachal Pradesh) — the partner procures from this jurisdiction. Validated against /states/{code}/districts where the state has a district catalogue; free-text otherwise.
    - `sro` string: Optional sub-registrar office / tehsil code. Send together with `district`; validated against /states/{code}/districts/{districtCode}/sros where the district has an SRO catalogue.
    - `cessAmount` number: Required in isCessRequired states.
    - `propertyArea` object: Optional property area details. If you send a unit other than `Not Applicable`, also send a positive value.
      - `unit` 'Square Feet' | 'Square Yard' | 'Square Meters' | 'Not Applicable'
      - `value` number
    - `costCentre` string: Optional. Your internal cost centre for reconciliation.
    - `businessUnit` string: Optional. Your internal business unit for reconciliation.
    - `linkedDocRef` string: Optional. Your reference to the underlying document.
    - `quantity` integer: Optional. Mint N identical stamps with distinct client references. Defaults to 1.

## Response `201`

Order created. Includes the created stamp IDs.

- OrderCreated
  - `id` string, uuid
  - `reference` string, nullable: Your PO / reference
  - `state` string, nullable: The order's single state (display name) — one order = one state. null on orders created before this contract.
  - `status` 'PLACED' | 'PARTIAL' | 'FULFILLED' | 'CANCELLED': Derived display status (never the raw DB status).
  - `isTest` boolean
  - `counts` object: Per-stage tallies across the order's stamps.
    - `requested` integer
    - `allocated` integer
    - `generated` integer
    - `verified` integer
    - `delivered` integer
    - `cancelled` integer
  - `createdAt` string, date-time
  - `stampCount` integer: Number of stamps in the order.
  - `stamps` object[]: Every stamp created for this order, in creation order. Use these `id`s directly with /stamps/{id}/*, /stamps/{id}/certificate, and /sandbox/stamps/{id}/simulate — no follow-up list call needed. NB: a stamp id is NOT the order id.
    - `id` string, uuid
    - `clientReference` string, nullable

## Other responses

- `400`: Idempotency-Key required
- `401`: Missing / invalid / expired / revoked key
- `402`: Wallet available balance is below the order face value
- `422`: Validation failed / warnings not acknowledged
- `429`: Rate limit exceeded. Limits are per API key: a burst of **120 requests** with a sustained refill of **20 requests/second**. The `Retry-After` header tells you how many seconds to wait before retrying. Limits are enforced per compute instance (pilot-grade), so real-world throughput may be higher.

---

[API](https://skmtc.dev/esahayak/apis/esahayak-stamp-api.md) - [All operations](https://skmtc.dev/esahayak/apis/esahayak-stamp-api/llms.txt) - [OpenAPI document](https://skmtc.dev/esahayak/apis/esahayak-stamp-api/revisions/cca124862a3e?raw)
