{
"Comment": "Adverse event reporting workflow — demonstrates Pass, Task, Choice, Wait, Parallel, Succeed, and Fail states. Validates sample, holds briefly for upstream settlement, runs safety and eligibility checks in parallel, then escalates or resolves.",
"StartAt": "SetAdverseEventMeta",
"States": {
"SetAdverseEventMeta": {
"Type": "Pass",
"Comment": "Stamp a reportingId and initial status onto the payload without invoking a Lambda.",
"Parameters": {
"sampleId.$": "$.sampleId",
"participantId.$": "$.participantId",
"trialId.$": "$.trialId",
"sampleType.$": "$.sampleType",
"collectionDate.$": "$.collectionDate",
"reportedSymptoms.$": "$.reportedSymptoms",
"hospitalizationFlag.$": "$.hospitalizationFlag",
"investigatorId.$": "$.investigatorId",
"biomarkers.$": "$.biomarkers",
"participant.$": "$.participant",
"reportingId.$": "$$.Execution.Id",
"reportingStatus": "PENDING"
},
"Next": "ValidateAdverseEventSample"
},
"ValidateAdverseEventSample": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${ValidateSampleFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.validation",
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException"
],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "ReportingFailed"
}
],
"Next": "IsSampleValidForReporting"
},
"IsSampleValidForReporting": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.validation.Payload.isValid",
"BooleanEquals": true,
"Next": "UpstreamSettlementHold"
}
],
"Default": "RejectAdverseEventSample"
},
"RejectAdverseEventSample": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${RejectSampleFunctionArn}",
"Payload": {
"reason": "INVALID_SAMPLE_FOR_ADVERSE_REPORTING",
"sample.$": "$"
}
},
"ResultPath": "$.rejection",
"Next": "ReportingFailed"
},
"ReportingFailed": {
"Type": "Fail",
"Error": "AdverseEventReportingFailed",
"Cause": "Sample could not be processed for adverse event reporting"
},
"UpstreamSettlementHold": {
"Type": "Wait",
"Comment": "Brief hold to allow upstream laboratory systems to commit their data before screening proceeds.",
"Seconds": 3,
"Next": "SafetyAndEligibilityReview"
},
"SafetyAndEligibilityReview": {
"Type": "Parallel",
"Comment": "Run adverse event detection and eligibility check concurrently to minimise latency.",
"Branches": [
{
"StartAt": "SafetyScreen",
"States": {
"SafetyScreen": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${DetectAdverseEventFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.adverseEvent",
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException"
],
"IntervalSeconds": 2,
"MaxAttempts": 2,
"BackoffRate": 2
}
],
"End": true
}
}
},
{
"StartAt": "EligibilityRecheck",
"States": {
"EligibilityRecheck": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${CheckParticipantEligibilityFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.eligibility",
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException"
],
"IntervalSeconds": 2,
"MaxAttempts": 2,
"BackoffRate": 2
}
],
"End": true
}
}
}
],
"ResultPath": "$.reviewResults",
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "ReportingFailed"
}
],
"Next": "IsHighRiskEvent"
},
"IsHighRiskEvent": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.reviewResults[0].adverseEvent.Payload.isAdverseEvent",
"BooleanEquals": true,
"Next": "EscalateToInvestigator"
}
],
"Default": "AdverseEventCleared"
},
"EscalateToInvestigator": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${NotifyInvestigatorFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.notification",
"Retry": [
{
"ErrorEquals": [
"States.ALL"
],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "ReportingFailed"
}
],
"Next": "AdverseEventEscalated"
},
"AdverseEventEscalated": {
"Type": "Succeed"
},
"AdverseEventCleared": {
"Type": "Succeed"
}
}
}JSONExpand
100%
Biotechnology 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.