Adverse Event Reporting

Handles the detection and escalation of adverse events in a clinical trial. It validates the sample, enforces a brief Wait to allow upstream systems to settle, then runs concurrent safety and eligibility checks using a Parallel state before notifying investigators or triggering a critical alert.
{
  "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"
    }
  }
}
JSON
Expand
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.