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.

  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 API_SECRET. 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 is open-sourced playground (Note: pasting secrets on a third-party website is dangerous. Take utmost caution!).

How to verify checksum at our end? Sample Code?

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

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

{
	"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"
}
{
	"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"
}
{
	"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"
}
{
	"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"
}
{
	"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"
}
{
	"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"
}
🚧

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.

📘

The structure is explained in the securities transaction guide here. 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.

Can there be multiple hits for a transactionId?
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.


Holdings Import

Sample payload

{
  "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"
}
{
  "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 📃 | Guide to Holdings Import integration.



Mutual Fund Holdings Import

Sample payload

Refer here

Note on webhook

Webhook authentication slightly varies for MF Holdings Import. As described in the 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)



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.

📘

Eligibility

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


Event types

event.eventNameWhen it firessubscription.paymentStatus
subscription.access_requestedUser successfully completes the request-access flow (email verified)null
subscription.payment_initiatedwhen a user lands on the payment step and selects the payment methodCREATED or PENDING
subscription.activatedSubscription is grantedPAID or WAIVED
subscription.payment_pendingif a payment order is in authorized/pending-capture state; OR a renewal order is created from a billPENDING
subscription.payment_failedA debit attempt failedFAILED
subscription.grace_startedThe first payment failure on an active subscription — cancellation is scheduled and grace period begins; OR a one-time plan is auto-downgraded at renewalFAILED
subscription.cancelledUser cancelled the subscriptionnull
subscription.resumedUser reverted a pending (scheduled) cancellationnull
subscription.expiredUser gets unsubscribednull

Sample payloads

{
  "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"
}
{
  "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"
}
{
  "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"
}
{
  "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"
}
{
  "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

FieldTypeDescription
eventIdstring (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.
eventNamestringOne of the 9 event types listed in the table above.
eventTimestampstring (ISO 8601)The time at which the event was generated, in UTC.

user object

FieldTypeDescription
smallcaseAuthIdstringThe Gateway user's smallcaseAuthId, matching the value returned during login/connect.

subscription object

FieldTypeDescription
subscriptionIdstringUnique identifier for the user's subscription (plan). Stable across all lifecycle events for the same subscription.
statusstringHigh-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.
statestringGranular 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.
paymentStatusstring | nullStatus 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.
startDatestring | nullSubscription start date in ISO 8601 (UTC). null before activation.
endDatestring | nullSubscription end date in ISO 8601 (UTC). null before activation.
paymentAmountnumber | nullAmount of the current billing cycle in paise (e.g., 50000 = ₹500). null for non-payment events.

smallcase object

FieldTypeDescription
scidstringThe smallcase ID (publisher's product identifier).
namestringDisplay name of the smallcase.
publisherNamestringName of the publisher who owns this smallcase.
pricingTypestringPricing model type (e.g., SMALLCASE).
amountnumberPlan price in paise.
durationstringBilling period (e.g., 1y for 1 year, 6m for 6 months).
planTypestringAUTO (auto-debit mandate) or ONETIME (one-time payment).

Top-level

FieldTypeDescription
checksumstringHMAC-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 )
⚠️

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.

Verification example (Node.js):

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.


📘

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.


User Created


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

FieldTypeDescriptionPresence
eventstringAlways user.createdAlways
smallcaseAuthIdstringNew user's smallcase identifier — store against your own user record.Always
partnerUserIdstringYour own user identifier, echoed back if supplied at login (see below).If supplied
phoneNumberstringPhone number associated with the login. (will be present if the user does mob. number login)Optional
timestampstring (ISO 8601)When the event was generated; also signed into checksum.Always
checksumstring (hex)HMAC-SHA256 signature (see Verifying below).Always

Sample payload

{
	"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:-

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- Initialise Gateway Session for the full flow. At this step you pass a smallcaseAuthToken, which can be a guest or connected token (details here). While generating the guest auth token- before the user's first login- add a partnerUserId claim to its payload:

{
	"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 API_SECRET. Recompute it server-side and reject any payload that doesn't match; sample code is linked in that section.

🔁

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.

FAQs

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


Did this page help you?