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.
- securities transaction
- order is placed
- order is completed (all stocks in batch are in their final state)
- holdings import
- user successfully completes holdings import transaction on client-side
- Mutual Fund Holdings Import
- user successfully completes Mutual funds holdings import transaction on client-side
- smallcase Module
- Subscription lifecycle events
- a subscribed user's subscription state changes
- User created
- Subscription lifecycle events
Consuming a Webhook
- Create a public API endpoint in your backend which will listen for new data that would be delivered by the smallcase Gateway backend.
- 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 -
- Stocks order
- Holdings Import
- Mutual Fund Holdings Import
- 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 statusUse 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.
EligibilitySubscription webhooks are only sent for users who authenticated via your Gateway integration. AUM-based plans are not included.
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
{
"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
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
user object| Field | Type | Description |
|---|---|---|
smallcaseAuthId | string | The Gateway user's smallcaseAuthId, matching the value returned during login/connect. |
subscription object
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
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 )
Note on checksum input fieldsSubscription event checksums use
event.eventTimestampanduser.smallcaseAuthId— nottimestampas 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:
subscription.access_requested— when the user initiates the flowsubscription.payment_initiated— when the payment mandate is createdsubscription.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 recommendationBecause retries may deliver the same event more than once, we recommend storing processed
eventIdvalues 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
| 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
{
"event": "user.created",
"smallcaseAuthId": "64f1a2b3c9d4e5f6a7b8c9d0",
"partnerUserId": "pu_8841203",
"phoneNumber": "9198xxxxxx10",
"timestamp": "2026-07-20T09:12:41.038Z",
"checksum": "a1f9c3...e02b"
}Supplying partnerUserId (optional)
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.createdis at-most-once and fire-and-forget- a dropped or non-2xxresponse 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 onsmallcaseAuthId.
FAQs
-
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 hoursNote that there is no retry mechanism for the
user.createdevent. -
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.
Updated 2 days ago