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
.
- Receiving Webhooks
- Request Headers
- Request Body
- Single Item Requests
- Batched Item Requests
- Supporting Types
- Content Types
- Hedera HCS Message by Topic ID (Rule Type 0)
- Hedera Token Mints By Token ID (Rule Type 1)
- Hedera Token Burns By Token ID (Rule Type 2)
- Hedera Token Transfer By Token ID (Rule Type 3)
- Hedera Contract Calls By Contract ID (Rule Type 4)
- Hedera Account activity by account (Rule Type 6)
- Hedera Content Supporting Types
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;
};