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 Contract Calls By Contract ID (Rule Type 4)
type SentinelContentContractCall = SentinelContentTransactionModel & {
contractCall: SentinelContentTransactionModelContractCall;
}
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;
};