{
"Comment": "Full smart meter billing pipeline: validates the reading, checks meter hardware status, calculates usage, runs anomaly detection and usage analysis in parallel, generates the bill, processes payment, and notifies the customer.",
"StartAt": "ValidateMeterReading",
"States": {
"ValidateMeterReading": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${ValidateMeterReadingFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.validation",
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException"
],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "ValidationError"
}
],
"Next": "IsReadingValid"
},
"IsReadingValid": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.validation.Payload.isValid",
"BooleanEquals": true,
"Next": "CheckMeterStatus"
}
],
"Default": "ValidationError"
},
"ValidationError": {
"Type": "Fail",
"Error": "InvalidMeterReading",
"Cause": "Incoming meter reading failed validation checks"
},
"CheckMeterStatus": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${CheckMeterStatusFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.meterStatus",
"Retry": [
{
"ErrorEquals": [
"States.ALL"
],
"IntervalSeconds": 2,
"MaxAttempts": 2,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "MeterStatusError"
}
],
"Next": "IsMeterOnline"
},
"IsMeterOnline": {
"Type": "Choice",
"Choices": [
{
"And": [
{
"Variable": "$.meterStatus.Payload.online",
"BooleanEquals": true
},
{
"Variable": "$.meterStatus.Payload.tamperDetected",
"BooleanEquals": false
}
],
"Next": "CalculateUsage"
}
],
"Default": "MeterStatusError"
},
"MeterStatusError": {
"Type": "Fail",
"Error": "MeterStatusError",
"Cause": "Meter is offline or tamper has been detected — manual inspection required"
},
"CalculateUsage": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${CalculateUsageFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.usage",
"Retry": [
{
"ErrorEquals": [
"States.ALL"
],
"IntervalSeconds": 2,
"MaxAttempts": 2,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "UsageCalculationError"
}
],
"Next": "AnomalyAndBillingAnalysis"
},
"UsageCalculationError": {
"Type": "Fail",
"Error": "UsageCalculationError",
"Cause": "Usage calculation lambda encountered an unexpected error"
},
"AnomalyAndBillingAnalysis": {
"Type": "Parallel",
"Comment": "Run anomaly detection and bill generation concurrently after usage is calculated.",
"Branches": [
{
"StartAt": "DetectAnomaly",
"States": {
"DetectAnomaly": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${DetectAnomalyFunctionArn}",
"Payload.$": "$"
},
"End": true
}
}
},
{
"StartAt": "GenerateBill",
"States": {
"GenerateBill": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${GenerateBillFunctionArn}",
"Payload.$": "$"
},
"End": true
}
}
}
],
"ResultPath": "$.billingAnalysis",
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "BillingAnalysisError"
}
],
"Next": "IsAnomalyFlagged"
},
"BillingAnalysisError": {
"Type": "Fail",
"Error": "BillingAnalysisError",
"Cause": "Anomaly detection or bill generation lambda failed"
},
"IsAnomalyFlagged": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.billingAnalysis[0].Payload.isAnomaly",
"BooleanEquals": false,
"Next": "ProcessPayment"
}
],
"Default": "FlagForReview"
},
"FlagForReview": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${NotifyCustomerFunctionArn}",
"Payload": {
"notificationType": "ANOMALY_ALERT",
"reading.$": "$"
}
},
"ResultPath": "$.anomalyNotification",
"Next": "AnomalyFlagged"
},
"AnomalyFlagged": {
"Type": "Fail",
"Error": "AnomalyDetected",
"Cause": "Unusual usage detected — bill held for manual review before payment processing"
},
"ProcessPayment": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${ProcessPaymentFunctionArn}",
"Payload": {
"reading.$": "$",
"bill.$": "$.billingAnalysis[1].Payload.bill"
}
},
"ResultPath": "$.payment",
"Retry": [
{
"ErrorEquals": [
"States.ALL"
],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "PaymentError"
}
],
"Next": "NotifyCustomer"
},
"PaymentError": {
"Type": "Fail",
"Error": "PaymentError",
"Cause": "Payment processing lambda encountered an unexpected error"
},
"NotifyCustomer": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${NotifyCustomerFunctionArn}",
"Payload": {
"notificationType": "BILL_READY",
"reading.$": "$",
"bill.$": "$.billingAnalysis[1].Payload.bill"
}
},
"ResultPath": "$.notification",
"Retry": [
{
"ErrorEquals": [
"States.ALL"
],
"IntervalSeconds": 2,
"MaxAttempts": 2,
"BackoffRate": 2
}
],
"Next": "BillingComplete"
},
"BillingComplete": {
"Type": "Succeed"
}
}
}JSONExpand
100%
Energy teams can use patterns like this to build reliable, compliant, and scalable automation for payment systems and can test and refine these flows locally with Thrubit to reduce cloud cost and speed up iteration.