Skip to main content

Receiving Webhooks

For Stream Rules with webhook destinations, you will need to run a server capable of handling http(s) POST requests with content-type application/json.

Request Headers

The following headers are included in every request.

  • content-type: application/json
  • user-agent: LWSentinel/1.0
    • Possible future usage for payload verisoning
  • lwsentinel-rule-id: <stream rule id>
    • The Stream rule ID
  • lwsentinel-event-id: <stream rule event id>
    • The stream rule audit ID for this event
  • lwsentinel-request-id: <trace request id>
    • Unique id for this delivery attempt
  • lwsentinel-retry-balance: <number of retries remaining>
    • Gives an indication on how many times this stream rule will automatically retry. When this value is zero, no retries will be performed on an error condition.

Request Body

The request body will be application/json with the following schemas. Note: the following schemas are written in typescript, but any language supporting json will work. C is a generic that changes based on the type of rule created. See Content Types for examples .

Single Item Requests

Rules created without batching will have the following request body. This type is also used in BatchedItemRequests.

type SentinelPayload<C> = {
content: C;
metadata: SentinelMetadata;
};

Batched Item Requests

For rules created with batching, a list of Single Item Payloads is included.

type BatchedSentinelPayload<C> = {
startingEventId?: string;
items: SentinelPayload<C>[];
};

Supporting Types

type Network = "mainnet" | "testnet";

type SentinelMetadata = {
rule: SentinelMetadataRule;
network: Network;
sentinelTimestamp: string; // `seconds.nano` format
timeSinceConsensus: string; // `seconds.nano` format
};

type SentinelMetadataRule = {
id: string;
name: string;
type: number; //enum rule type
predicateValue: string;
chain?: string;
};

Content Types

For the Generic C above, the following types are applicable per rule type.

Hedera HCS Message by Topic ID (Rule Type 0)

type SentinelContentHCSSubmitMessage = {
topicId: string;
consensusTimestamp: string;
message: string;
payerAccountId: string;
runningHash: string;
runningHashVersion: number;
sequenceNumber: number;
chunkInfo: {
number: number;
total: number;
initialTransactionId: {
accountId: string;
nonce: number;
scheduled: boolean;
transactionValidStart: string;
};
};
};

Hedera Token Mints By Token ID (Rule Type 1)

type SentinelContentTokenMintNftToken = SentinelContentBaseNftTransfer & {
metadata: string;
};
type SentinelContentTokenMint = {
tokens?: SentinelContentBaseFungibleTokenTransfer[];
nfts?: SentinelContentTokenMintNftToken[];
transaction: SentinelContentTransaction;
};

Hedera Token Burns By Token ID (Rule Type 2)

type SentinelContentTokenBurn = {
tokens?: SentinelContentBaseFungibleTokenTransfer[];
nfts?: SentinelContentBaseNftTransfer[];
transaction: SentinelContentTransaction;
};

Hedera Token Transfer By Token ID (Rule Type 3)

type SentinelContentTokenTransfer = {
tokens?: SentinelContentTransactionFungibleTransfer[];
nfts?: SentinelContentTransactionNftTransfer[];
transaction: SentinelContentTransaction;
};

Hedera Contract Calls By Contract ID (Rule Type 4)

type SentinelContentContractCall = SentinelContentTransactionModel & {
contractCall: SentinelContentTransactionModelContractCall;
}

Hedera Account activity by account (Rule Type 6)

type SentinelContentAccountActivity = SentinelContentTransactionModel;

Hedera Content Supporting Types

type SentinelContentBaseFungibleTokenTransfer = {
account: string;
tokenId: string;
amount: number;
};
type SentinelContentBaseNftTransfer = {
receiverAccountId: string;
senderAccountId: string;
serialNumber: number;
tokenId: string;
};
type SentinelContentTransactionTransfer = {
account: string;
amount: number;
isApproval: boolean;
};

type SentinelContentTransactionFungibleTransfer =
SentinelContentBaseFungibleTokenTransfer & {
isApproval: boolean;
};
type SentinelContentTransactionNftTransfer = SentinelContentBaseNftTransfer & {
isApproval: boolean;
};

type SentinelContentTransaction = {
consensusTimestamp: string;
chargedTxFee: number;
maxFee: number;
memo: string;
transfers: SentinelContentTransactionTransfer[];
tokenTransfers: SentinelContentTransactionFungibleTransfer[];
nftTransfers: SentinelContentTransactionNftTransfer[];
node?: string;
nonce: number;
parentConsensusTimestamp?: string;
status: string;
scheduled: boolean;
transactionHash: string;
transactionId: string;
transactionType: string;
payerAccountId: string;
validDurationSeconds?: number;
validStartTimestamp: string;
};


type SentinelContentTransactionModelAllowances = {
crypto: { owner: string; spender: string; amount: number }[];
tokens: { owner: string; spender: string; tokenId: string; amount: number }[];
nfts: {
owner: string;
spender: string;
tokenId: string;
serialNumbers: number[];
approvedForAll?: boolean;
delegatingSpender?: string;
}[];
nftDeletes: { owner: string; tokenId: string; serialNumbers: number[] }[];
};

type SentinelContentTransactionModelContractCall = {
contractId: string;
gas: number;
amount: number;
functionParameters: string;
gasUsed: number;
bloom: string;
contractCallResult: string;
errorMessage: string;
evmAddress?: string;
senderId?: string;
logInfo: {
contractId: string;
bloom: string;
data: string;
topic: string[];
}[];
};

type SentinelContentTransactionModelMetadata = {
consensusTimestamp: string;
chargedTxFee: number;
maxFee: number;
memo: string;
node: string;
nonce: number;
parentConsensusTimestamp?: string;
scheduled: boolean;
transactionHash: string;
transactionId: string;
transactionType: string;
payerAccountId: string;
validDurationSeconds: number;
validStartTimestamp: string;
};

type SentinelContentTransactionModelReceipt = {
status: string;
exchangeRate?: {
nextRate: {
centEquiv: number;
hbarEquiv: number;
expirationTime: number;
};
currentRate: {
centEquiv: number;
hbarEquiv: number;
expirationTime: number;
};
};
accountId?: string;
fileId?: string;
contractId?: string;
scheduledTransactionId?: string;
scheduleID?: string;
tokenId?: string;
serialNumbers?: number[];
newTotalSupply?: number;
topicId?: string;
topicSequenceNumber?: number;
topicRunningHash?: string;
topicRunningHashVersion?: number;
};

type SentinelContentTransactionModel = {
metadata: SentinelContentTransactionModelMetadata;
receipt: SentinelContentTransactionModelReceipt;
transfers: SentinelContentTransactionModelTransfers;
allowances?: SentinelContentTransactionModelAllowances;
contractCall?: SentinelContentTransactionModelContractCall;
};