{"slug": "integrating-lambda-durable-functions-into-a-step-functions-workflow", "title": "Integrating Lambda Durable Functions into a Step Functions Workflow", "summary": "AWS Lambda Durable Functions, announced at re:Invent 2025, introduce a checkpoint/replay mechanism enabling executions up to one year. A developer integrated them with Step Functions, finding the hybrid approach useful despite a steep learning curve. The combination uses durable functions for application logic and Step Functions for high-level workflow coordination.", "body_md": "At re:Invent 2025, AWS [announced Lambda Durable Functions](https://aws.amazon.com/about-aws/whats-new/2025/12/lambda-durable-multi-step-applications-ai-workflows/). The feature introduces a **checkpoint/replay mechanism** that allows Lambda executions to run for up to one year, automatically recovering from interruptions by replaying from the last checkpoint.\n\nLambda's 15-minute timeout is not a bug or a limitation to work around. It is a deliberate **design** choice that encourages keeping functions simple and focused, and in most cases it does its job well. When a function needs more time, the usual approach is **fanout**: split the work into smaller Lambdas, orchestrate them, move on. I have done it many times and it works perfectly fine.\n\nBut a few days ago I was developing a new Lambda function for a pipeline orchestrated by Step Functions, and the execution time exceeded 15 minutes. I could have done the usual split, but durable functions had just come out and I wanted to **try** them.\n\nAt first glance, durable functions can look like a replacement for Step Functions. Both services manage multi-step **workflows**, both offer **checkpointing** and automatic **recovery**, and both let you **coordinate** complex operations. For certain use cases, that might actually be the case: if your entire workflow lives inside a single Lambda, durable functions can handle everything on their own without an external orchestrator.\n\nBut the [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-step-functions.html) actually suggests using them together. The \"Hybrid architectures\" section says it explicitly: many applications benefit from **combining** the two services, using durable functions for application-level logic within Lambda and Step Functions to coordinate the high-level workflow across multiple AWS services. My case fit that description, and more than a perfect architectural match, I wanted to learn how the two services actually work **together** and form my own opinion on when the hybrid approach makes sense.\n\nI figured integrating the two would be a small change. It was my first time working with the durable execution SDK, and since the code I write is mostly infrastructure and automation rather than application development, the learning curve turned out to be **steeper** than I expected.\n\nThis article walks through the real journey, from the initial attempt, through the errors I hit, to the patterns that actually work in production.\n\nA durable function needs three things **on top** of a regular Lambda: a `durable_config`\n\nwith `execution_timeout`\n\nand `retention_period`\n\non the L2 `Function`\n\nconstructor, a Lambda Version and Alias ([durable functions require qualified ARNs](https://docs.aws.amazon.com/lambda/latest/dg/durable-invoking.html)), and the `AWSLambdaBasicDurableExecutionRolePolicy`\n\nmanaged policy on the execution role.\n\nSince CDK's L2 `Function`\n\nconstruct natively supports `durable_config`\n\n, you declare it directly in the constructor.\n\nOne thing to be aware of: you cannot add\n\n`durable_config`\n\nto an existing Lambda function. Adding it triggers a resource replacement, meaning CDK will delete the old Lambda and create a new one. In my case this was fine because I was reusing the same application code, so the new function behaved identically. But if you have event source mappings, reserved concurrency, or other configuration tied to the function ARN, plan for the replacement accordingly.\n\nI then created a helper to handle the remaining boilerplate (IAM policy, Step Functions callback permissions, version, alias):\n\n``` python\nfrom aws_cdk import aws_lambda as _lambda, aws_iam as iam, Duration\n\n# The function itself declares durable_config in the constructor\nfn = _lambda.Function(\n    scope, \"MyFunction\",\n    runtime=_lambda.Runtime.PYTHON_3_14,\n    handler=\"handler.handler\",\n    code=_lambda.Code.from_asset(lambda_path),\n    timeout=Duration.minutes(15),\n    durable_config=_lambda.DurableConfig(\n        execution_timeout=Duration.hours(1),\n        retention_period=Duration.days(3),\n    ),\n)\n\ndef _make_durable(\n    fn: _lambda.Function,\n    alias_name: str = \"live\",\n) -> _lambda.Alias:\n    fn.role.add_managed_policy(\n        iam.ManagedPolicy.from_aws_managed_policy_name(\n            \"service-role/AWSLambdaBasicDurableExecutionRolePolicy\"\n        )\n    )\n\n    fn.add_to_role_policy(iam.PolicyStatement(\n        actions=[\"states:SendTaskSuccess\", \"states:SendTaskFailure\"],\n        resources=[\"*\"],\n    ))\n\n    version = fn.current_version\n    alias = _lambda.Alias(fn, f\"DurableAlias-{alias_name}\",\n                          alias_name=alias_name, version=version)\n    return alias\n```\n\nThe helper returns an `Alias`\n\n(which implements `IFunction`\n\n), so it plugs directly into `tasks.LambdaInvoke`\n\nwithout any workflow changes. A nice property of this approach is that you can have durable and standard Lambdas **coexisting** in the same Step Functions workflow. I only needed to make the new function durable; the other tasks kept running as regular Lambdas.\n\nThe `@durable_execution`\n\n**decorator** replaces the standard Lambda handler signature. Each unit of work becomes a `context.step()`\n\ncall that gets independently checkpointed.\n\nThere are a few key rules from the [best practices documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-best-practices.html) to keep in mind: code outside steps must be **deterministic** (no API calls, no `datetime.now()`\n\n, no UUIDs), boto3 sessions are **not serializable** so you need to recreate them inside each step, and step return values must be serializable and **under 256 KB** (the checkpoint limit).\n\nHere is the pattern I used for the function:\n\n``` python\nfrom aws_durable_execution_sdk_python import DurableContext, durable_execution\n\n@durable_execution\ndef handler(event: dict, context: DurableContext) -> dict:\n    # Deterministic setup, no I/O here\n    job_id = event[\"job_id\"]\n    target_id = event.get(\"target_id\")\n\n    # Step 1: Get the list of endpoints to call\n    def _get_endpoints(_):\n        client = create_api_client(target_id)  # fresh client per step\n        return client.list_endpoints()\n\n    endpoints = context.step(_get_endpoints, name=\"get_endpoints\")\n\n    # Step 2: Process global data\n    def _process_global(_):\n        client = create_api_client(target_id)\n        return client.get_global_data()\n\n    global_data = context.step(_process_global, name=\"global_data\")\n\n    # Step N: Per-endpoint processing\n    per_endpoint_results = {}\n    for endpoint in endpoints:\n        def _process_endpoint(_, ep=endpoint):\n            client = create_api_client(target_id)\n            return client.get_data(ep)\n\n        per_endpoint_results[endpoint] = context.step(\n            _process_endpoint, name=f\"endpoint_{endpoint}\"\n        )\n\n    # Final step: store results\n    def _store(_):\n        store_results(job_id, target_id, per_endpoint_results)\n        return {\"processed\": len(per_endpoint_results), \"status\": \"complete\"}\n\n    result = context.step(_store, name=\"store_results\")\n\n    return {\"statusCode\": 200, \"result\": result}\n```\n\nEach step is **independently checkpointed**. If the Lambda times out after completing `global_data`\n\n, the next invocation replays `get_endpoints`\n\nand `global_data`\n\nfrom checkpoint (without re-executing them), then continues with the next step.\n\nWith the durable Lambda deployed and the CDK alias wired into the workflow, I ran the pipeline. It failed immediately:\n\n```\nLambda.InvalidParameterValueException: You cannot synchronously invoke\na durable function with an executionTimeout greater than 15 minutes.\n```\n\nIn hindsight this makes perfect sense. Step Functions' default `LambdaInvoke`\n\nuses synchronous invocation: it calls the Lambda and waits for the response. But [durable functions with execution timeouts beyond 15 minutes can only be invoked asynchronously](https://docs.aws.amazon.com/lambda/latest/dg/durable-invoking.html). Synchronous invocations are capped at 15 minutes regardless of the durable config.\n\nTo solve this, I switched to the [Wait for Callback with Task Token](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html) integration pattern with **asynchronous** invocation.\n\n```\ntasks.LambdaInvoke(\n    scope, \"MyDurableTask\",\n    lambda_function=lambdas[\"my_function\"],\n    integration_pattern=sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN,\n    invocation_type=tasks.LambdaInvocationType.EVENT,\n    payload=sfn.TaskInput.from_object({\n        \"TaskToken\": sfn.JsonPath.task_token,\n        \"job_id.$\": \"$.job_id\",\n        \"target_id.$\": \"$.target_id\",\n    }),\n    result_path=sfn.JsonPath.DISCARD,\n    heartbeat_timeout=sfn.Timeout.duration(Duration.hours(1)),\n)\n```\n\nWith `WAIT_FOR_TASK_TOKEN`\n\n, Step Functions fires the Lambda asynchronously (fire-and-forget) and **pauses** the workflow. The Lambda receives a `TaskToken`\n\nin its event payload. When the Lambda finishes, it must call `SendTaskSuccess`\n\n(or `SendTaskFailure`\n\n) with that token to signal completion.\n\nThe `heartbeat_timeout`\n\ndefines how long Step Functions will wait before considering the task failed. I set it to match the durable execution timeout.\n\nWith `WAIT_FOR_TASK_TOKEN`\n\n, the Lambda needs to call `SendTaskSuccess`\n\nwhen it finishes to resume the workflow. The instinct is to put it at the end of the handler as regular code, after all the durable steps:\n\n``` php\n@durable_execution\ndef handler(event: dict, context: DurableContext) -> dict:\n    task_token = event.pop(\"TaskToken\", None)\n\n    # ... all your durable steps ...\n\n    # Seems natural, but problematic\n    if task_token:\n        boto3.client(\"stepfunctions\").send_task_success(\n            taskToken=task_token, output=json.dumps(response, default=str)\n        )\n    return response\n```\n\nThe problem is that code outside `context.step()`\n\ncalls runs on every replay. So this `send_task_success`\n\nwould fire again every time the Lambda replays, sending duplicate callbacks. And if the Lambda gets interrupted right before that line, it never fires at all and Step Functions hangs forever.\n\nThe `SendTaskSuccess`\n\ncall **must be a durable step**:\n\n``` php\n@durable_execution\ndef handler(event: dict, context: DurableContext) -> dict:\n    # Extract token BEFORE durable steps (deterministic, no I/O)\n    task_token = event.pop(\"TaskToken\", None)\n\n    # ... all your durable steps ...\n\n    response = {\"statusCode\": 200, \"result\": result}\n\n    # Send callback as the FINAL durable step\n    if task_token:\n        def _send_callback(_):\n            sfn_client = boto3.client(\"stepfunctions\")\n            sfn_client.send_task_success(\n                taskToken=task_token,\n                output=json.dumps(response, default=str),\n            )\n        context.step(_send_callback, name=\"send_task_callback\")\n\n    return response\n```\n\nWrapped in a `context.step()`\n\n, the callback is checkpointed: it runs once, and on replay the SDK skips it. `event.pop(\"TaskToken\")`\n\nis deterministic (same result on replay), so it is safe outside steps.\n\nThe best practices I mentioned earlier list the 256 KB checkpoint limit, but I glossed over it at first. I did not expect any of my steps to return that much data. I was wrong: as soon as I ran the pipeline on a larger input, one of my steps hit the wall:\n\n```\nInvalidParameterValueException: STEP output payload size must be\nless than or equal to 262144 bytes.\n```\n\nThe step in question returned a JSON that, for certain inputs, exceeded the limit.\n\nI merged the large step into `store_results`\n\n. Since that step already stores everything to DynamoDB, the data never needs to cross a checkpoint boundary:\n\n```\n# Before: large step as a separate step (can exceed 256KB)\ndata = context.step(_process_data, name=\"process_data\")\n# ... later ...\nresult = context.step(_store, name=\"store_results\")\n\n# After: moved inside store_results\ndef _store(_):\n    # Process inline, no checkpoint needed for this data\n    data = do_work(job_id, target_id, endpoints)\n\n    # ... aggregate, store to DynamoDB ...\n    store_results(job_id, target_id, payload)\n\n    # Return only a small summary (well under 256KB)\n    return {\"processed\": len(data), \"status\": \"complete\"}\n```\n\nOnce I had this working, I realized it came with a tradeoff. When the large step was on its own, it had its own checkpoint: if the Lambda restarted after completing it, the replay would skip it and jump straight to `store_results`\n\n. By merging the two, that checkpoint is **gone**. If the Lambda restarts during `store_results`\n\n, it replays the entire merged step from scratch. Repeating work that was already done is not ideal, especially when that work is slow or expensive.\n\nThe merge works because the window of risk is small: the processing takes a few seconds, the DynamoDB store is fast, and an interruption between the two is unlikely. But this tradeoff kept bothering me, so I went back to the SDK documentation to see if there was a **better option**.\n\nThere is. The SDK provides `run_in_child_context`\n\n, which groups multiple steps into a single unit while preserving their individual checkpoints. The key behavior is described in the docs: if the child context's result exceeds 256 KB, the SDK re-executes the context code on replay, but the steps inside it are resolved from the checkpoint log without re-executing. So the expensive work stays checkpointed individually, and only the lightweight assembly logic (pure in-memory, no I/O) re-runs on replay.\n\nIn practice, this means that if the Lambda restarts after the heavy step but before the store completes, the replay skips it and jumps straight to the assembly. With the merge approach, it would have repeated everything. The difference can seem marginal, but for functions dealing with slow or expensive operations, it would matter.\n\n``` php\ndef _process_and_store(child_ctx: DurableContext) -> Dict:\n    # Inner step 1: the heavy work (checkpointed individually)\n    def _process_data(_):\n        return do_work(job_id, target_id, endpoints)\n\n    data = child_ctx.step(_process_data, name=\"process_data\")\n\n    # Deterministic assembly (no I/O, re-runs on replay)\n    aggregated = aggregate_results(per_endpoint_results, global_data, data)\n    payload = build_payload(job_id, target_id, aggregated)\n\n    # Inner step 2: store to DynamoDB (checkpointed individually)\n    def _store_results(_):\n        store_results(job_id, target_id, payload)\n        return {\"processed\": len(data), \"status\": \"complete\"}\n\n    return child_ctx.step(_store_results, name=\"store_results\")\n\nresult = context.run_in_child_context(\n    _process_and_store,\n    name=\"process_and_store\",\n)\n```\n\nAt this point I assumed the child context handled the size problem entirely, so I did not worry about what the inner steps returned. When I tested again with a large workload, I found out that was **wrong**:\n\n```\nRequestEntityTooLargeException: Request must be smaller than\n6291456 bytes for the InvokeFunction operation\n```\n\nThe error came from `CheckpointDurableExecution`\n\n, the SDK's internal call to persist checkpoint data. The stack trace pointed to the `process_data`\n\ninner step.\n\nHere is what I missed: `run_in_child_context`\n\nprevents the child context's *overall result* from being checkpointed when it exceeds 256 KB. But the *inner steps* inside the child context are still individually checkpointed. The `process_data`\n\nstep returned a large JSON, and the SDK tried to checkpoint that result. `CheckpointDurableExecution`\n\nis a Lambda API call under the hood, so it is subject to Lambda's standard [invocation payload limit of 6 MB](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html). When the serialized checkpoint exceeded that, the call failed.\n\nSo there are two separate limits at play: **256 KB** for a single step's checkpointed result, and **6 MB** for the entire `CheckpointDurableExecution`\n\nAPI call payload. The child context handled the first limit. But the inner step's return value was still being serialized into the checkpoint API call, and for large enough datasets it blew past the second.\n\nThe fix follows the [best practices](https://docs.aws.amazon.com/lambda/latest/dg/durable-best-practices.html) more literally: \"**Store IDs and references**, not full objects. Use Amazon S3 or DynamoDB for large data, pass references in state.\" Instead of returning the full dataset from the step, I store it directly in DynamoDB *inside* the step and return only a lightweight summary:\n\n``` php\ndef _process_and_store(child_ctx: DurableContext) -> Dict:\n    # Inner step 1: do the work, store to DynamoDB, return summary\n    def _process_data(_):\n        data = do_work(job_id, target_id, endpoints)\n\n        # Store data directly, don't return it\n        persist_data(job_id, target_id, data)\n\n        # Return only what downstream code needs\n        return {\n            \"total\": len(data),\n            \"by_category\": count_by_category(data),\n            \"flagged\": [\n                {\"id\": d[\"id\"], \"type\": d[\"type\"]}\n                for d in data\n                if d.get(\"flagged\")\n            ],\n        }\n\n    summary = child_ctx.step(_process_data, name=\"process_data\")\n\n    # Deterministic assembly uses the lightweight summary\n    aggregated = aggregate_results(per_endpoint_results, global_data)\n    flagged_alerts = build_alerts(summary[\"flagged\"])\n    payload = build_payload(job_id, target_id, aggregated, flagged_alerts, summary)\n\n    # Inner step 2: store aggregated results (data already persisted)\n    def _store_results(_):\n        store_results(job_id, target_id, payload)\n        return {\n            \"processed\": summary[\"total\"],\n            \"alerts\": len(flagged_alerts),\n            \"status\": \"complete\",\n        }\n\n    return child_ctx.step(_store_results, name=\"store_results\")\n```\n\nThe checkpoint for `process_data`\n\nnow contains a few KB instead of potentially megabytes. The full data lives in DynamoDB, where it was going to end up anyway. I just moved the write earlier in the pipeline.\n\nThe key insight is that `run_in_child_context`\n\nis about the child's *return value*, not about the inner steps' return values. Each inner step still gets checkpointed normally, and those checkpoints are still subject to payload limits. If an inner step produces large data, the data needs to go to external storage *inside* the step, with only a reference or summary returned for checkpointing.\n\nHere is the CDK configuration for a durable function in the workflow:\n\n```\n# 1. Declare durable_config in the Function constructor\nfn = _lambda.Function(\n    scope, \"MyFunction\",\n    runtime=_lambda.Runtime.PYTHON_3_14,\n    handler=\"handler.handler\",\n    code=_lambda.Code.from_asset(lambda_path),\n    timeout=Duration.minutes(15),\n    durable_config=_lambda.DurableConfig(\n        execution_timeout=Duration.hours(1),\n        retention_period=Duration.days(3),\n    ),\n)\n\n# 2. Add IAM policy, Step Functions callback permissions, version, and alias\nalias = _make_durable(fn)\n\n# 3. Use WAIT_FOR_TASK_TOKEN + EVENT invocation in Step Functions\ntask = tasks.LambdaInvoke(\n    scope, \"MyTask\",\n    lambda_function=alias,  # must use the alias (qualified ARN)\n    integration_pattern=sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN,\n    invocation_type=tasks.LambdaInvocationType.EVENT,\n    payload=sfn.TaskInput.from_object({\n        \"TaskToken\": sfn.JsonPath.task_token,\n        # ... your payload fields\n    }),\n    result_path=sfn.JsonPath.DISCARD,  # result comes via callback\n    heartbeat_timeout=sfn.Timeout.duration(Duration.hours(1)),\n)\n```\n\nAfter going through this process, a few things stood out.\n\nYou **cannot synchronously invoke** a durable function with a timeout greater than 15 minutes. You need `WAIT_FOR_TASK_TOKEN`\n\nwith `invocation_type=EVENT`\n\n. This is probably the first thing anyone will bump into, and the error message is at least clear about it.\n\nThe `@durable_execution`\n\ndecorator **must be on the Lambda entry point**. The durable runtime replays from the decorated function directly, so the decorator needs to be on the function that Lambda actually calls.\n\n`SendTaskSuccess`\n\n**must be a durable step**. If it sits outside the durable context, it will not execute on replay. If it is not checkpointed, it might execute twice.\n\n`event.pop(\"TaskToken\")`\n\nis safe before durable steps. It is deterministic, same input produces same result on replay. No need to wrap it in a step.\n\nKeep step return values under 256 KB. If a step produces large data, use `run_in_child_context`\n\nto group the production and consumption of that data into a child context. Inner steps keep their checkpoints, but the child context's overall result is not subject to the limit. The SDK re-runs the child code on replay, resolving inner steps from the log without re-executing them.\n\n**Child contexts don't protect inner step checkpoints from the 6 MB Lambda payload limit.** `run_in_child_context`\n\nonly skips checkpointing the child's *return value*. Inner steps are still individually checkpointed. If an inner step returns large data, store it in DynamoDB or S3 inside the step and return only a lightweight summary.\n\nRecreate boto3 sessions inside every step. Sessions are not serializable and STS credentials expire. A fresh session per step handles both issues cleanly.\n\nYou can mix durable and standard Lambdas in the same workflow. Since `_lambda.Alias`\n\nimplements `IFunction`\n\n, adding a durable function to an existing Step Functions workflow does not require touching the other tasks. You just wire it in alongside the standard Lambdas.\n\nAfter working with this integration for a while, I have a few personal observations.\n\nThe most notable thing about durable functions is that the **heavy lifting happens entirely in code**. The AWS-side configuration is minimal: you toggle a flag on the Lambda, add a policy, create an alias. Everything else, the checkpointing, the step definitions, the error handling, the callback logic, lives in your application code. Compare this with Step Functions, where most of the workflow logic is expressed through ASL (Amazon States Language) definitions and AWS console configuration.\n\nThis makes durable functions especially **attractive for developers**. If you are someone who thinks in code and prefers to have the full workflow logic visible in your IDE, you are going to like this model. On the other hand, if you come from an operations background and prefer visual workflows, drag-and-drop designers, and declarative configuration, Step Functions is probably still the more comfortable tool.\n\nThere is obviously no universal winner here. Every architecture needs to be designed for its specific use case. In my case, the function already contained complex business logic, so durable functions seemed appealing because **in theory** I would not have to restructure the code. As I described above, reality was a bit more nuanced than that. The workflow logic was already in the code; durable functions just made the code resilient. If I had been building something from scratch with lots of AWS service integrations (SQS, SNS, DynamoDB, parallel branches, human approvals), I would probably lean towards Step Functions and its native integrations.\n\nThe feature is new, and the main thing I noticed is the lack of practical **examples** for non-trivial integration patterns. The AWS documentation covers durable functions and Step Functions separately, but if you want to use them together you need to piece together information from the durable functions docs, the Step Functions integration patterns, and the best practices guide before you get a coherent picture. That is part of the reason I wrote this article.\n\nI should also be honest about my own bias: my background is infrastructure and automation, not application development. The durable SDK patterns (checkpoint/replay, deterministic code outside steps, child contexts) were **unfamiliar** territory for me. Someone who writes application code daily might find them straightforward. Even so, for the right use case, durable functions let me keep all the logic in a single function instead of splitting into a fanout architecture, and the result is arguably simpler to reason about.\n\n[Lambda durable functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html) - Overview and how it works\n\n[Durable functions or Step Functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-step-functions.html) - When to use which\n\n[Invoking durable Lambda functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-invoking.html) - Qualified ARNs, sync vs async\n\n[Best practices for Lambda durable functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-best-practices.html) - Determinism, idempotency, state management\n\n[Creating Lambda durable functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-getting-started.html) - Getting started tutorial\n\n[Deploy durable functions with IaC](https://docs.aws.amazon.com/lambda/latest/dg/durable-getting-started-iac.html) - CDK, SAM, and CloudFormation examples\n\n[Security and permissions for durable functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-security.html) - IAM policies and checkpoint permissions\n\n[Step Functions service integration patterns](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html) - Request Response, .sync, and Wait for Callback\n\n[Lambda quotas](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html) - Invocation payload size limits (6 MB) and other quotas", "url": "https://wpnews.pro/news/integrating-lambda-durable-functions-into-a-step-functions-workflow", "canonical_source": "https://dev.to/aws-heroes/integrating-lambda-durable-functions-into-a-step-functions-workflow-3c7o", "published_at": "2026-07-11 12:40:02+00:00", "updated_at": "2026-07-11 12:57:10.838206+00:00", "lang": "en", "topics": ["developer-tools", "ai-infrastructure"], "entities": ["AWS", "Lambda Durable Functions", "Step Functions", "CDK"], "alternates": {"html": "https://wpnews.pro/news/integrating-lambda-durable-functions-into-a-step-functions-workflow", "markdown": "https://wpnews.pro/news/integrating-lambda-durable-functions-into-a-step-functions-workflow.md", "text": "https://wpnews.pro/news/integrating-lambda-durable-functions-into-a-step-functions-workflow.txt", "jsonld": "https://wpnews.pro/news/integrating-lambda-durable-functions-into-a-step-functions-workflow.jsonld"}}