---
updatedAt: 2026-08-14T08:21:21.000Z
agentTools:
  projectIndex: https://developers.gateway.smallcase.com/llms.txt
---

# Webhooks

Webhook enables partners to receive push notifications to their server as and when the events like single stock (or basket) order update and holding import occur.

# Overview ⚓️

Webhook is an event-driven communication method where a webhook provider (smallcase Gateway backend) delivers data to a webhook consumer (Gateway partner backend) as and when an event occurs. A more traditional way to get almost real-time data was for consumers to frequently poll the provider. However, webhooks are considered more efficient and reliable.

smallcase Gateway Webhook enables partners to receive push notifications to their server as and when the events like securities order update, holding import, or subscription lifecycle changes occur.

smallcase Gateway sends a `POST` request with a JSON payload to your registered postback url.&#x20;

1. securities transaction
   * order is placed
   * order is completed (all stocks in batch are in their final state)
2. holdings import
   * user successfully completes holdings import transaction on client-side
3. Mutual Fund Holdings Import
   * user successfully completes Mutual funds holdings import transaction on client-side
4. smallcase Module
   1. Subscription lifecycle events
      * a subscribed user's subscription state changes
   2. User created

# Consuming a Webhook

1. Create a public API endpoint in your backend which will listen for new data that would be delivered by the smallcase Gateway backend.
2. Once the API is up & running, share the endpoint URL with the smallcase Gateway team so that we can register in our system.

# Authentication

To ensure the authenticity of webhook hits, checksum-based authentication is used.

The JSON payload has a `checksum` key which is SHA256 hash of `timestamp` + `smallcaseAuthId` (signed with <Glossary>API\_SECRET</Glossary>. For every Postback you receive, you should compute this checksum at your end and match it with the checksum in the payload.

Dino Chiesa's [HMAC generator](https://dinochiesa.github.io/hmachash/index.html) is open-sourced playground (Note: pasting secrets on a third-party website is dangerous. Take utmost caution!).

<details>
  <summary>How to verify checksum at our end? Sample Code?</summary>

<https://developers.gateway.smallcase.com/discuss/61f1c4341361af0039f395e5>

</details>

# smallcase Gateway Webhooks

smallcase Gateway offer webhooks for the following use cases -

1. Stocks order
2. Holdings Import
3. Mutual Fund Holdings Import
4. Subscription events

## Stocks order

### Sample payload

```json PLACED (regular order)
{
	"batchId":"63afcadb204dc65ef0c72862",
	"buyAmount":0,
	"sellAmount":0,
	"quantity":2,
	"filled":0,
	"status":"PLACED",
	"variety":"regular",
	"orders":[
		{
			"status":"PLACED",
			"quantity":1,
			"tradingsymbol":"RELIANCE",
			"transactionType":"SELL",
			"exchange":"NSE",
			"orderType":"MARKET",
			"product":"CNC",
			"errorCode":null,
			"statusMessage":null,
			"price":0
		},
		{
			"status":"PLACED",
			"quantity":1,
			"tradingsymbol":"ADANIENT",
			"transactionType":"BUY",
			"exchange":"NSE",
			"orderType":"MARKET",
			"product":"CNC",
			"errorCode":null,
			"statusMessage":null,
			"price":0
		}
	],
	"unplaced":[
	],
	"transactionId":"TRX_59671f981a79466fb1be4d918dd3bc6a",
	"broker":"kite-leprechaun",
	"smallcaseAuthId":"5ef33705f610f80b5453b319",
	"timestamp":"2022-12-31T05:38:37.595Z",
	"checksum":"f6609f15f0dd38eaf07c282911e7dfd69c11e03d14f814d23d2dc84627d80299"
}
```
```json PLACED (AMO)
{
	"batchId":"63afc98b0a485bcb6316aa93",
	"buyAmount":0,
	"sellAmount":0,
	"quantity":1,
	"filled":0,
	"status":"PLACED",
	"variety":"amo",
	"orders":[
		{
			"status":"PLACED",
			"quantity":1,
			"tradingsymbol":"INFY",
			"transactionType":"BUY",
			"exchange":"NSE",
			"orderType":"MARKET",
			"product":"CNC",
			"errorCode":null,
			"statusMessage":null,
			"price":0
		}
	],
	"unplaced":[
	],
	"transactionId":"TRX_e8c96d4a58c54cbcb9e466dcf6b5b7cf",
	"broker":"kite-leprechaun",
	"smallcaseAuthId":"5ef33705f610f80b5453b319",
	"timestamp":"2022-12-31T05:32:59.786Z",
	"checksum":"7a31b1aaeba385c0347383bd21c2c290456ef11905939318c3355083b207bdba"
}
```
```json PLACED (Limit order)
{
	"batchId":"63afc92e0a485bcb6316aa6c",
	"buyAmount":0,
	"sellAmount":0,
	"quantity":3,
	"filled":0,
	"status":"PLACED",
	"variety":"regular",
	"orders":[
		{
			"status":"PLACED",
			"quantity":1,
			"tradingsymbol":"INFY",
			"transactionType":"SELL",
			"exchange":"NSE",
			"orderType":"SLM",
			"product":"CNC",
			"errorCode":null,
			"statusMessage":null,
			"price":0,
			"triggerPrice":1500
		},
		{
			"status":"PLACED",
			"quantity":1,
			"tradingsymbol":"CREDITACC",
			"transactionType":"BUY",
			"exchange":"NSE",
			"orderType":"LIMIT",
			"product":"CNC",
			"errorCode":null,
			"statusMessage":null,
			"price":900
		},
		{
			"status":"PLACED",
			"quantity":1,
			"tradingsymbol":"RELIANCE",
			"transactionType":"BUY",
			"exchange":"NSE",
			"orderType":"SL",
			"product":"CNC",
			"errorCode":null,
			"statusMessage":null,
			"price":2610,
			"triggerPrice":2600
		}
	],
	"unplaced":[
	],
	"transactionId":"TRX_107f74ebb93f4a2fb304b17da095c9a1",
	"broker":"kite-leprechaun",
	"smallcaseAuthId":"5ef33705f610f80b5453b319",
	"timestamp":"2022-12-31T05:31:28.477Z",
	"checksum":"c08d83f90b5506bec4a8460d0de3c49bfe2e88cafa3ff77d6d786f4544e8e92f"
}
```
```json COMPLETED
{
	"batchId":"63afcadb204dc65ef0c72862",
	"buyAmount":3858.25,
	"sellAmount":2547.14,
	"quantity":2,
	"filled":2,
	"status":"COMPLETED",
	"variety":"regular",
	"completedDate":"2022-12-31T05:38:40.616Z",
	"orders":[
		{
			"status":"COMPLETE",
			"quantity":1,
			"tradingsymbol":"RELIANCE",
			"transactionType":"SELL",
			"averagePrice":2547.14,
			"exchange":"NSE",
			"orderType":"MARKET",
			"product":"CNC",
			"filledQuantity":1,
			"exchangeOrderId":"5ee87f210f059b28fee59afb63afcadbf563a6d60600140e",
			"errorCode":null,
			"statusMessage":"NA",
			"orderTimestamp":"2022-12-31T00:08:35.473Z",
			"price":0
		},
		{
			"status":"COMPLETE",
			"quantity":1,
			"tradingsymbol":"ADANIENT",
			"transactionType":"BUY",
			"averagePrice":3858.25,
			"exchange":"NSE",
			"orderType":"MARKET",
			"product":"CNC",
			"filledQuantity":1,
			"exchangeOrderId":"5ee87f210f059b28fee59afb63afcaddf563a6d606001424",
			"errorCode":null,
			"statusMessage":"NA",
			"orderTimestamp":"2022-12-31T00:08:37.530Z",
			"price":0
		}
	],
	"unplaced":[
		
	],
	"transactionId":"TRX_59671f981a79466fb1be4d918dd3bc6a",
	"broker":"kite-leprechaun",
	"smallcaseAuthId":"5ef33705f610f80b5453b319",
	"timestamp":"2022-12-31T05:38:40.669Z",
	"checksum":"08e084d16fec3e283d5988195f36044bedd25af56bf81c6b87464c8e92ad15c1"
}
```
```json MARKEDCOMPLETE
{
	"batchId":"63afc9fc0a485bcb6316aaa2",
	"buyAmount":0,
	"sellAmount":0,
	"quantity":1,
	"filled":0,
	"status":"MARKEDCOMPLETE",
	"variety":"regular",
	"completedDate":"2022-12-31T05:34:55.496Z",
	"orders":[
		{
			"status":"CANCELLED",
			"quantity":1,
			"tradingsymbol":"ZOMATO",
			"transactionType":"BUY",
			"averagePrice":59,
			"exchange":"NSE",
			"orderType":"MARKET",
			"product":"CNC",
			"filledQuantity":0,
			"exchangeOrderId":"5ee87f210f059b28fee59afb63afc9fcf563a6d6060013ba",
			"errorCode":"exchgNotEnabled",
			"statusMessage":"This segment is not activated for your Upstox account.",
			"orderTimestamp":"2022-12-31T00:04:52.417Z",
			"price":0
		}
	],
	"unplaced":[
	],
	"transactionId":"TRX_275aaab7082b44fba6e23cdf594ab555",
	"broker":"kite-leprechaun",
	"smallcaseAuthId":"5ef33705f610f80b5453b319",
	"timestamp":"2022-12-31T05:34:55.557Z",
	"checksum":"36c83f1c9740cb2bec25440f452eeb4087fe68525ba0545b39500a66686d0d70"
}
```
```json UNPLACED
{
	"batchId":"63afc98b0a485bcb6316aa93",
	"buyAmount":0,
	"sellAmount":0,
	"quantity":1,
	"filled":0,
	"status":"UNPLACED",
	"variety":"regular",
	"orders":[
	],
	"unplaced":[
		{
			"status":"ERROR",
			"quantity":1,
			"tradingsymbol":"SATHAISPAT",
			"transactionType":"BUY",
			"exchange":"NSE",
			"orderType":"MARKET",
			"product":"CNC",
			"errorCode":"circuitLimitExceeded",
			"statusMessage":"Exception found for order: GMK2212301301195HCLH3QCU51V This company is currently banned from the stock market.",
			"price":0
		}
	],
	"transactionId":"TRX_e8c96d4a58c54cbcb9e466dcf6b5b7cf",
	"broker":"kite-leprechaun",
	"smallcaseAuthId":"5ef33705f610f80b5453b319",
	"timestamp":"2022-12-31T05:32:59.786Z",
	"checksum":"7a31b1aaeba385c0347383bd21c2c290456ef11905939318c3355083b207bdba"
}
```

<Callout icon="🚧" theme="warn">
  ### Batch status (`status`) should not be relied for determining order status

  Use individual stock status (`orders[].status`) & filled quantity (`orders[].filledQuantity`) to determine the status of each security.
</Callout>

<Callout icon="📘" theme="info">
  ### [Payload explanation](https://developers.gateway.smallcase.com/docs/securities-transactions#response-structure)

  The structure is explained in the securities transaction guide [here](https://developers.gateway.smallcase.com/docs/securities-transactions#response-structure). It contains an explanation for each key in the response, along with enums. Note that the structure would vary a bit for SDK response vs webhook. The sample response above is the correct representation of the webhook structure.
</Callout>

**Can there be multiple hits for a transactionId?**<br />Yes. For every transaction, the webhook will be called on order placement, and thereafter on transaction's batch status update (if any).

The batch status can update for transaction with batch status in "PLACED" state (`orderBatches[].status` = PLACED).

For example, a user placed an AMO order, and on the next trading session the order got executed. The first webhook event would be as soon as the user confirms the order. And the next event would be fired when the order got executed / cancelled.

<hr />

## Holdings Import

### Sample payload

```json sample webhook data for holdings import v2
{
  "smallcases": {
    "public": [
      {
        "scid": "SCAW_0001",
        "name": "Timeless Asset Allocation",
        "investmentDetailsURL": "https://smallcase.zerodha.com/details/5f2d22636678954954f29a2d?",
        "shortDescription": "Diversify with equity, gold & fixed income ETFs for recession-proof investing",
        "imageUrl": "https://assets.smallcase.com/images/smallcases/200/SCAW_0001.png",
        "stats": {
          "currentValue": 4921.89,
          "totalReturns": -36.65253999999927
        },
        "constituents": [
          {
            "ticker": "NIFTYBEES",
            "shares": 10
          },
          {
            "ticker": "JUNIORBEES",
            "shares": 3
          },
          {
            "ticker": "LIQUIDBEES",
            "shares": 1
          },
          {
            "ticker": "GOLDBEES",
            "shares": 39
          }
        ]
      }
    ],
    "private": {
      "stats": {
        "currentValue": 57.31,
        "totalReturns": 1.31
      }
    }
  },
  "securities": [
    {
      "holdings": {
        "quantity": 4,
        "averagePrice": 36.68
      },
      "positions": {
        "nse": {
          "quantity": 1,
          "averagePrice": 34.1
        },
        "bse": {
          "quantity": 1,
          "averagePrice": 34.1
        }
      },
      "transactableQuantity": 5,
      "smallcaseQuantity": 2,
      "nseTicker": "J&KBANK",
      "bseTicker": "J&KBANK",
      "isin": "INE168A01041",
      "name": "Jammu and Kashmir Bank Ltd"
    },
    {
      "holdings": {
        "quantity": 1,
        "averagePrice": 28.4
      },
      "positions": {
        "nse": {
          "quantity": 0,
          "averagePrice": 0
        },
        "bse": {
          "quantity": 0,
          "averagePrice": 0
        }
      },
      "transactableQuantity": 1,
      "smallcaseQuantity": 0,
      "nseTicker": "MASPTOP50",
      "bseTicker": "MASPTOP50",
      "isin": "INF769K01HP3",
      "name": "Mirae Asset S&P 500 Top 50 ETF"
    },
    {
      "holdings": {
        "quantity": 1,
        "averagePrice": 10.53
      },
      "positions": {
        "nse": {
          "quantity": 0,
          "averagePrice": 0
        },
        "bse": {
          "quantity": 0,
          "averagePrice": 0
        }
      },
      "transactableQuantity": 1,
      "smallcaseQuantity": 0,
      "nseTicker": "AXISBPSETF",
      "bseTicker": null,
      "isin": "INF846K01Z04",
      "name": "Axis AAA Bond Plus SDL ETF-2026 Matur. Reg. Growth"
    },
    {
      "holdings": {
        "quantity": 1,
        "averagePrice": 9.8
      },
      "positions": {
        "nse": {
          "quantity": 0,
          "averagePrice": 0
        },
        "bse": {
          "quantity": 0,
          "averagePrice": 0
        }
      },
      "transactableQuantity": 1,
      "smallcaseQuantity": 0,
      "nseTicker": "SUZLON",
      "bseTicker": "SUZLON",
      "isin": "INE040H01021",
      "name": "Suzlon Energy Ltd"
    },
    {
      "holdings": {
        "quantity": 1,
        "averagePrice": 48.5
      },
      "positions": {
        "nse": {
          "quantity": 0,
          "averagePrice": 0
        },
        "bse": {
          "quantity": 0,
          "averagePrice": 0
        }
      },
      "transactableQuantity": 1,
      "smallcaseQuantity": 0,
      "nseTicker": "ICICIB22",
      "bseTicker": "ICICIB22",
      "isin": "INF109KB15Y7",
      "name": "Bharat 22 ETF"
    },
    {
      "holdings": {
        "quantity": 1,
        "averagePrice": 36.05
      },
      "positions": {
        "nse": {
          "quantity": 0,
          "averagePrice": 0
        },
        "bse": {
          "quantity": 0,
          "averagePrice": 0
        }
      },
      "transactableQuantity": 1,
      "smallcaseQuantity": 0,
      "nseTicker": "PNB",
      "bseTicker": "PNB",
      "isin": "INE160A01022",
      "name": "Punjab National Bank"
    }
  ],
  "updating": false,
  "lastUpdate": "2022-03-24T11:33:52.986Z",
  "snapshotDate": "2022-03-24T11:24:00.871Z",
  "notes": "your-notes-goes-here",
  "smallcaseAuthId": "6195e288360acf9ebc060d23",
  "broker": "groww",
  "transactionId": "TRX_06726c28769e44b8920bd6eb5bbeef17",
  "timestamp": "2022-03-24T11:33:53.091Z",
  "checksum": "fe7a17d4f82831918a1363af080f5a92f382efa872b1a2a7823964b6bf7e3285"
}
```
```json holdings import v1
{
  "smallcases": {
    "public": [
      {
        "scid": "SCAW_0001",
        "name": "All Weather Investing",
        "investmentDetailsURL": "https://smallcase.zerodha.com/details/5f7ec65caf9e9e3ce695bc4a?",
        "shortDescription": "Diversify with equity, gold & fixed income ETFs for recession-proof investing",
        "imageUrl": "https://assets.smallcase.com/images/smallcases/200/SCAW_0001.png",
        "stats": {
          "currentValue": 4308.15,
          "totalReturns": 727.0412199999994
        },
        "constituents": [
          {
            "ticker": "GOLDBEES",
            "shares": 23
          },
          {
            "ticker": "JUNIORBEES",
            "shares": 3
          },
          {
            "ticker": "NIFTYBEES",
            "shares": 6
          },
          {
            "ticker": "LIQUIDBEES",
            "shares": 1
          }
        ]
      },
      {
        "scid": "SCNM_0010",
        "name": "The Great Indian Middle Class - Custom",
        "investmentDetailsURL": "https://smallcase.zerodha.com/details/60d2bbb999a5de24eba97ab4?",
        "shortDescription": "Companies focused on the growing middle class. Running India's consumption engine",
        "imageUrl": "https://assets.smallcase.com/images/smallcases/200/SCNM_0010.png",
        "stats": {
          "currentValue": 0,
          "totalReturns": 0
        },
        "constituents": []
      }
    ],
    "private": []
  },
  "securities": {
    "holdings": [
      {
        "ticker": "INFY",
        "shares": 14,
        "name": "Infosys Ltd",
        "exchange": "NSE",
        "averagePrice": 1474.2927272727275
      },
      {
        "ticker": "MRF",
        "shares": 10,
        "name": "MRF Ltd",
        "exchange": "NSE",
        "averagePrice": 66105.91
      },
      {
        "ticker": "ADROITINFO",
        "shares": 2,
        "name": "Adroit Infotech Ltd",
        "exchange": "NSE",
        "averagePrice": 7
      },
      {
        "ticker": "DRREDDY",
        "shares": 14,
        "name": "Dr Reddy's Laboratories Ltd",
        "exchange": "NSE",
        "averagePrice": 4097.744285714286
      },
      {
        "ticker": "ITC",
        "shares": 29,
        "name": "ITC Ltd",
        "exchange": "NSE",
        "averagePrice": 200.40530153724868
      },
      {
        "ticker": "ASIANPAINT",
        "shares": 7,
        "name": "Asian Paints Ltd",
        "exchange": "NSE",
        "averagePrice": 2118.8002857142856
      }
    ]
  },
  "updating": false,
  "lastUpdate": "2021-12-20T09:27:03.014Z",
  "snapshotDate": "2021-12-20T09:27:03.014Z",
  "smallcaseAuthId": "5ef33705f610f80b5453b319",
  "broker": "iifl",
  "notes": "your-notes-goes-here",
  "transactionId": "TRX_7c30e4d9904f4f79b692f281a83cdc1f",
  "timestamp": "2021-12-20T09:27:03.059Z",
  "checksum": "d04a000b47c44bf6a721c829b8a8815b8470b2b5c767112b92f82943ea712fdd"
}
```

Continue reading: [Holdings Import v2 📃](https://developers.gateway.smallcase.com/page/holdings-import-v2) | [Guide to Holdings Import integration](https://developers.gateway.smallcase.com/docs/holdings-import).

<hr />

<br />

## Mutual Fund Holdings Import

### Sample payload

Refer [here](https://developers.gateway.smallcase.com/docs/mutual-fund-holdings-import#sample-response)

### Note on webhook

Webhook authentication slightly varies for MF Holdings Import. As described in the [Authentication](https://developers.gateway.smallcase.com/docs/webhooks#authentication), the JSON payload has a checksum key which is SHA256 hash of `timestamp` + `smallcaseAuthId`

For the Mutual funds holdings Import webhook response, the JSON payload has a checksum key which is SHA256 hash of `timestamp` + `transactionId` (instead of `smallcaseAuthId`)

<hr />

<br />

## Subscription events

smallcase Gateway sends subscription lifecycle events to your configured webhook URL whenever a subscribed user's subscription state changes. These events allow your platform to react in real-time - for example, granting or revoking access, updating UI state, or sending your own notifications.

<Callout icon="📘" theme="info">
  ### Eligibility

  Subscription webhooks are only sent for users who authenticated via your Gateway integration. AUM-based plans are not included.
</Callout>

***

### Event types

| `event.eventName`                | When it fires                                                                                                                                                 | `subscription.paymentStatus` |
| :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------- |
| `subscription.access_requested`  | User successfully completes the request-access flow (email verified)                                                                                          | `null`                       |
| `subscription.payment_initiated` | when a user lands on the payment step and selects the payment method                                                                                          | `CREATED` or `PENDING`       |
| `subscription.activated`         | Subscription is granted                                                                                                                                       | `PAID` or `WAIVED`           |
| `subscription.payment_pending`   | if a payment order is in authorized/pending-capture state; OR a renewal order is created from a bill                                                          | `PENDING`                    |
| `subscription.payment_failed`    | A debit attempt failed                                                                                                                                        | `FAILED`                     |
| `subscription.grace_started`     | The **first** payment failure on an active subscription — cancellation is scheduled and grace period begins; OR a one-time plan is auto-downgraded at renewal | `FAILED`                     |
| `subscription.cancelled`         | User cancelled the subscription                                                                                                                               | `null`                       |
| `subscription.resumed`           | User reverted a pending (scheduled) cancellation                                                                                                              | `null`                       |
| `subscription.expired`           | User gets unsubscribed                                                                                                                                        | `null`                       |

***

### Sample payloads

```json subscription.activated
{
  "event": {
    "eventId": "a3f1c2d4-9b8e-4f2a-bc01-d3e7f5a6b8c9",
    "eventName": "subscription.activated",
    "eventTimestamp": "2026-01-15T10:30:00.000Z"
  },
  "user": {
    "smallcaseAuthId": "5ef33705f610f80b5453b319"
  },
  "subscription": {
    "subscriptionId": "67a8c1d204dc65ef0c72891f",
    "status": "SUBSCRIBED",
    "state": "SUBSCRIBED",
    "paymentStatus": "PAID",
    "startDate": "2026-01-15T00:00:00.000Z",
    "endDate": "2027-01-15T00:00:00.000Z",
    "paymentAmount": 50000
  },
  "smallcase": {
    "scid": "GATSC_0042",
    "name": "Publisher Flagship Fund",
    "publisherName": "ExamplePublisher",
    "pricingType": "SMALLCASE",
    "amount": 50000,
    "duration": "1y",
    "planType": "AUTO"
  },
  "checksum": "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"
}
```
```json subscription.access_requested
{
  "event": {
    "eventId": "b1e2d3f4-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
    "eventName": "subscription.access_requested",
    "eventTimestamp": "2026-01-15T09:55:00.000Z"
  },
  "user": {
    "smallcaseAuthId": "5ef33705f610f80b5453b319"
  },
  "subscription": {
    "subscriptionId": "67a8c1d204dc65ef0c72891f",
    "status": "REQUESTED_ACCESS",
    "state": "SUBSCRIBED",
    "paymentStatus": null,
    "startDate": null,
    "endDate": null,
    "paymentAmount": null
  },
  "smallcase": {
    "scid": "GATSC_0042",
    "name": "Publisher Flagship Fund",
    "publisherName": "ExamplePublisher",
    "pricingType": "SMALLCASE",
    "amount": 50000,
    "duration": "1y",
    "planType": "AUTO"
  },
  "checksum": "e7f3a1b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1"
}
```
```json subscription.payment_failed / subscription.grace_started
{
  "event": {
    "eventId": "d4e5f6a7-2b3c-4d5e-9f0a-1b2c3d4e5f6a",
    "eventName": "subscription.payment_failed",
    "eventTimestamp": "2026-03-15T08:10:00.000Z"
  },
  "user": {
    "smallcaseAuthId": "5ef33705f610f80b5453b319"
  },
  "subscription": {
    "subscriptionId": "67a8c1d204dc65ef0c72891f",
    "status": "SUBSCRIBED",
    "state": "GRACE_PERIOD",
    "paymentStatus": "FAILED",
    "startDate": "2026-01-15T00:00:00.000Z",
    "endDate": "2027-01-15T00:00:00.000Z",
    "paymentAmount": 50000
  },
  "smallcase": {
    "scid": "GATSC_0042",
    "name": "Publisher Flagship Fund",
    "publisherName": "ExamplePublisher",
    "pricingType": "SMALLCASE",
    "amount": 50000,
    "duration": "1y",
    "planType": "AUTO"
  },
  "checksum": "f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9"
}
```
```json subscription.cancelled
{
  "event": {
    "eventId": "e5f6a7b8-3c4d-5e6f-0a1b-2c3d4e5f6a7b",
    "eventName": "subscription.cancelled",
    "eventTimestamp": "2026-02-10T14:20:00.000Z"
  },
  "user": {
    "smallcaseAuthId": "5ef33705f610f80b5453b319"
  },
  "subscription": {
    "subscriptionId": "67a8c1d204dc65ef0c72891f",
    "status": "SUBSCRIBED",
    "state": "MANUALLY_CANCELLED",
    "paymentStatus": null,
    "startDate": "2026-01-15T00:00:00.000Z",
    "endDate": "2027-01-15T00:00:00.000Z",
    "paymentAmount": null
  },
  "smallcase": {
    "scid": "GATSC_0042",
    "name": "Publisher Flagship Fund",
    "publisherName": "ExamplePublisher",
    "pricingType": "SMALLCASE",
    "amount": 50000,
    "duration": "1y",
    "planType": "AUTO"
  },
  "checksum": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
```
```json subscription.resumed
{
  "event": {
    "eventId": "f6a7b8c9-4d5e-6f7a-1b2c-3d4e5f6a7b8c",
    "eventName": "subscription.resumed",
    "eventTimestamp": "2026-02-12T11:05:00.000Z"
  },
  "user": {
    "smallcaseAuthId": "5ef33705f610f80b5453b319"
  },
  "subscription": {
    "subscriptionId": "67a8c1d204dc65ef0c72891f",
    "status": "SUBSCRIBED",
    "state": "SUBSCRIBED",
    "paymentStatus": null,
    "startDate": "2026-01-15T00:00:00.000Z",
    "endDate": "2027-01-15T00:00:00.000Z",
    "paymentAmount": null
  },
  "smallcase": {
    "scid": "GATSC_0042",
    "name": "Publisher Flagship Fund",
    "publisherName": "ExamplePublisher",
    "pricingType": "SMALLCASE",
    "amount": 50000,
    "duration": "1y",
    "planType": "AUTO"
  },
  "checksum": "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3"
}
```

***

### Field reference

#### `event` object

| Field            | Type                | Description                                                                                                                           |
| :--------------- | :------------------ | :------------------------------------------------------------------------------------------------------------------------------------ |
| `eventId`        | `string` (UUID v4)  | Unique identifier for this event. Use this as an idempotency key — the same `eventId` will not be sent twice for the same occurrence. |
| `eventName`      | `string`            | One of the 9 event types listed in the table above.                                                                                   |
| `eventTimestamp` | `string` (ISO 8601) | The time at which the event was generated, in UTC.                                                                                    |

#### `user` object

| Field             | Type     | Description                                                                             |
| :---------------- | :------- | :-------------------------------------------------------------------------------------- |
| `smallcaseAuthId` | `string` | The Gateway user's `smallcaseAuthId`, matching the value returned during login/connect. |

#### `subscription` object

| Field            | Type               | Description                                                                                                                                                                                                                                                      |
| :--------------- | :----------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `subscriptionId` | `string`           | Unique identifier for the user's subscription (plan). Stable across all lifecycle events for the same subscription.                                                                                                                                              |
| `status`         | `string`           | High-level subscription status. One of `REQUESTED_ACCESS`, `SUBSCRIBED`, `UNSUBSCRIBED`, `BLOCKED`. Reflects the upstream subscription system state — use `event.eventName` to drive your logic, not this field.                                                 |
| `state`          | `string`           | Granular billing-cycle state. One of `SUBSCRIBED`, `DEBIT_PENDING`, `GRACE_PERIOD`, `FIRST_CYCLE_DEBIT_FAIL`, `MANUALLY_CANCELLED`, `UNSUBSCRIBED`. Reflects the upstream subscription system state — use `event.eventName` to drive your logic, not this field. |
| `paymentStatus`  | `string` \| `null` | Status of the most recent bill. One of `PAID`, `WAIVED`, `PENDING`, `CREATED`, `FAILED`. `null` for non-payment events — see the event types table above for which events carry a value.                                                                         |
| `startDate`      | `string` \| `null` | Subscription start date in ISO 8601 (UTC). `null` before activation.                                                                                                                                                                                             |
| `endDate`        | `string` \| `null` | Subscription end date in ISO 8601 (UTC). `null` before activation.                                                                                                                                                                                               |
| `paymentAmount`  | `number` \| `null` | Amount of the current billing cycle in paise (e.g., `50000` = ₹500). `null` for non-payment events.                                                                                                                                                              |

#### `smallcase` object

| Field           | Type     | Description                                                  |
| :-------------- | :------- | :----------------------------------------------------------- |
| `scid`          | `string` | The smallcase ID (publisher's product identifier).           |
| `name`          | `string` | Display name of the smallcase.                               |
| `publisherName` | `string` | Name of the publisher who owns this smallcase.               |
| `pricingType`   | `string` | Pricing model type (e.g., `SMALLCASE`).                      |
| `amount`        | `number` | Plan price in paise.                                         |
| `duration`      | `string` | Billing period (e.g., `1y` for 1 year, `6m` for 6 months).   |
| `planType`      | `string` | `AUTO` (auto-debit mandate) or `ONETIME` (one-time payment). |

#### Top-level

| Field      | Type     | Description                                                               |
| :--------- | :------- | :------------------------------------------------------------------------ |
| `checksum` | `string` | HMAC-SHA256 signature for payload verification. See Authentication below. |

***

### Authentication

The payload includes a `checksum` field that you **must** verify on your server before processing the event.

**Checksum formula:**

```
HMAC-SHA256( eventTimestamp + smallcaseAuthId, API_SECRET )
```

<Callout icon="⚠️" theme="warn">
  ### Note on checksum input fields

  Subscription event checksums use `event.eventTimestamp` and `user.smallcaseAuthId` — not `timestamp` as used in order and holdings webhooks. Ensure your verification logic reads the correct fields.
</Callout>

**Verification example (Node.js):**

```javascript
const crypto = require('crypto');

function verifySubscriptionWebhook(payload, apiSecret) {
  const { event, user, checksum } = payload;
  const data = event.eventTimestamp + user.smallcaseAuthId;
  const expected = crypto
    .createHmac('sha256', apiSecret)
    .update(data)
    .digest('hex');
  return expected === checksum;
}
```

***

### Can a subscription trigger multiple webhook calls?

Yes. A single subscription can produce multiple events across its lifecycle. For example:

1. `subscription.access_requested` — when the user initiates the flow
2. `subscription.payment_initiated` — when the payment mandate is created
3. `subscription.activated` — when the first payment succeeds

Each event carries its own unique `eventId`. Use `subscriptionId` to correlate all events belonging to the same subscription, and `eventId` as an idempotency key to safely handle duplicate deliveries.

<br />

<Callout icon="📘" theme="info">
  ### Idempotency recommendation

  Because retries may deliver the same event more than once, we recommend storing processed `eventId` values and discarding duplicates before applying any state changes on your end.
</Callout>

<hr />

## User Created

<br />

Fired once, the first time a Gateway user is provisioned- when a person completes their first login on your platform and Gateway creates a new user record. Use it to link a smallcase identity to your own user record without polling. It is delivered to a separate `events` postback URL, and `user.created` is the only event on that endpoint today.

### **When it fires**

Exactly once per user, when their Gateway record is first created. Returning logins never re-fire it's build around "first sight", not a login stream.

### Enabling

Share an HTTPS endpoint with your integration contact to register as your events URL; until one is on file, this webhook stays silent (nothing is queued). The endpoint must accept a POST with a JSON body, reply within a 10-second timeout, and return any 2xx to acknowledge receipt.

### Field reference

| Field             | Type              | Description                                                                                    | Presence    |
| ----------------- | ----------------- | ---------------------------------------------------------------------------------------------- | ----------- |
| `event`           | string            | Always `user.created`                                                                          | Always      |
| `smallcaseAuthId` | string            | New user's smallcase identifier — store against your own user record.                          | Always      |
| `partnerUserId`   | string            | Your own user identifier, echoed back if supplied at login (see below).                        | If supplied |
| `phoneNumber`     | string            | Phone number associated with the login. *(will be present if the user does mob. number login)* | Optional    |
| `timestamp`       | string (ISO 8601) | When the event was generated; also signed into `checksum`.                                     | Always      |
| `checksum`        | string (hex)      | HMAC-SHA256 signature (see Verifying below).                                                   | Always      |

### Sample payload

```json user.created
{
	"event": "user.created",
	"smallcaseAuthId": "64f1a2b3c9d4e5f6a7b8c9d0",
	"partnerUserId": "pu_8841203",
	"phoneNumber": "9198xxxxxx10",
	"timestamp": "2026-07-20T09:12:41.038Z",
	"checksum": "a1f9c3...e02b"
}
```

***

### Supplying `partnerUserId` (optional)

**Initialising Gateway Session:-&#x20;**

`partnerUserId` lets the webhook echo back your own user identifier, so you don't have to reconcile on `smallcaseAuthId` and `phoneNumber` alone. It rides in on the token you already pass when initialising the Gateway session- no separate API call is needed.

[See Step 2](https://developers.gateway.smallcase.com/docs/smallcase-module-integration#step-2---initialise-gateway-session)- Initialise Gateway Session for the full flow. At this step you pass a `smallcaseAuthToken`, which can be a guest or connected token ([details here](https://developers.gateway.smallcase.com/page/using-jwt#guest-user)). While generating the guest auth token- before the user's first login- add a `partnerUserId` claim to its payload:

```json partnerUserId- guest mode
{
	"guest": true,
	"partnerUserId": "pu_8841203"
}
```

The claim flows through untouched to the `user.created` payload; there is no way to backfill it after the user has been created. If you skip it, the webhook still fires- just without the field.

### Verifying & delivery

`checksum` uses the same scheme as Authentication above — HMAC-SHA256 over `timestamp` + `smallcaseAuthId`, keyed with your <Glossary>API\_SECRET</Glossary>. Recompute it server-side and reject any payload that doesn't match; sample code is linked in that section.

<Callout icon="🔁" theme="info">
  No retries for this event yet

  Unlike the transaction webhooks (see FAQs), `user.created` is at-most-once and fire-and-forget- a dropped or non-`2xx` response is logged but not retried today. It is non-blocking (never affects the user's login), so reconcile independently if you need a delivery guarantee, and dedupe on `smallcaseAuthId`.
</Callout>

# FAQs

1. **Is there any retry mechanism, in case our webhook could not capture data on the first try?**<br />Yes, we have a retry mechanism in place which would retry hitting the webhook URL after a certain duration -<br />i. First retry after 15 minutes<br />ii. Second retry after 24 hours<br />iii. Final retry after 48 hours

   Note that there is no retry mechanism for the `user.created` event.

2. **There's no Webhook support for our use case. Is it possible to add support?**<br />We are always open to exploring use cases. Let us know your use case for which you need webhook support, we will definitely see what can be done.