Service Integrations in JSONata Mode
Integration Types
Step Functions can integrate with AWS services in three patterns:
- Optimized integrations — Purpose-built, recommended where available
- AWS SDK integrations — Call any AWS SDK API action directly
- HTTP Task — Call HTTPS APIs (e.g., Stripe, Salesforce)
Integration Patterns
| Pattern | Resource ARN | Behavior | When to Use |
|---|---|---|---|
| Optimized | arn:aws:states:::servicename:apiAction |
Call API and continue immediately | Fire-and-forget operations (start a process, send a message) |
| Optimized (sync) | arn:aws:states:::servicename:apiAction.sync |
Wait for the job to complete | When you need the result before continuing (run ECS task, execute child workflow, run Glue job) |
| Optimized (callback) | arn:aws:states:::servicename:apiAction.waitForTaskToken |
Pause until a task token is returned | Human approval, external system processing, long-running async operations |
| AWS SDK | arn:aws:states:::aws-sdk:serviceName:apiAction |
Call any AWS SDK API action directly | When no optimized integration exists for the service |
| HTTP Task | arn:aws:states:::http:invoke |
Call an HTTPS API endpoint | External APIs (e.g., Stripe, Salesforce) |
HTTP Task security: Store API keys, tokens, and other credentials in AWS Secrets Manager and reference them through an EventBridge connection (
ConnectionArn). Never embed credentials in the state machine definition or inArguments.
Examples
Lambda Function
Optimized Integration (Recommended)
Always review the AWS Documentation to check availability and proper usage of an optimized integration before using it: https://docs.aws.amazon.com/step-functions/latest/dg/integrate-optimized.html
"InvokeFunction": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Arguments": {
"FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:MyFunction:$LATEST",
"Payload": {
"orderId": "{% $states.input.orderId %}",
"customer": "{% $states.input.customer %}"
}
},
"Output": "{% $states.result.Payload %}",
"Next": "NextState"
}Always include a version qualifier (:$LATEST, :1, or an alias like :prod) on the function ARN.
The result is wrapped in a Payload field, so use $states.result.Payload to access the Lambda return value.
SDK Integration
"InvokeViaSDK": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:lambda:invoke",
"Arguments": {
"FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
"Payload": "{% $string($states.input) %}"
},
"Next": "NextState"
}DynamoDB
GetItem
"GetUser": {
"Type": "Task",
"Resource": "arn:aws:states:::dynamodb:getItem",
"Arguments": {
"TableName": "UsersTable",
"Key": {
"userId": {
"S": "{% $states.input.userId %}"
}
}
},
"Assign": {
"user": "{% $states.result.Item %}"
},
"Output": "{% $states.result.Item %}",
"Next": "ProcessUser"
}Encryption at rest: DynamoDB encrypts all tables by default with AWS-owned keys. For sensitive workloads that need key-rotation control, cross-account access, or audit visibility, use an AWS-managed (
aws/dynamodb) or customer-managed KMS key.
SQS (Simple Queue Service)
Send Message
"QueueMessage": {
"Type": "Task",
"Resource": "arn:aws:states:::sqs:sendMessage",
"Arguments": {
"QueueUrl": "https://sqs.us-east-1.amazonaws.com/123456789012/ProcessingQueue",
"MessageBody": "{% $string($states.input) %}"
},
"Next": "Done"
}Encryption: Enable server-side encryption (SSE-SQS or SSE-KMS) on the queue, even for fire-and-forget messages.
Send Message with Wait for Task Token
"WaitForApproval": {
"Type": "Task",
"Resource": "arn:aws:states:::sqs:sendMessage.waitForTaskToken",
"Arguments": {
"QueueUrl": "https://sqs.us-east-1.amazonaws.com/123456789012/ApprovalQueue",
"MessageBody": "{% $string({'taskToken': $states.context.Task.Token, 'orderId': $orderId}) %}"
},
"TimeoutSeconds": 86400,
"Next": "ProcessApproval"
}The execution pauses until an external system calls SendTaskSuccess or SendTaskFailure with the task token.
Encryption and sensitive data: Enable server-side encryption (
KmsMasterKeyId) on SQS queues and SNS topics used by a workflow. The task token is a sensitive credential, and message bodies may carry order or approval details — avoid placing PII, financial data, or secrets in the body; pass a reference ID and look up details through an authorized channel.
Step Functions (Nested Execution)
Start Execution (Synchronous)
"RunSubWorkflow": {
"Type": "Task",
"Resource": "arn:aws:states:::states:startExecution.sync:2",
"Arguments": {
"StateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:ChildWorkflow",
"Input": "{% $states.input %}"
},
"Output": "{% $parse($states.result.Output) %}",
"Next": "ProcessSubResult"
}Note: The .sync:2 suffix waits for completion. The child output is a JSON string in $states.result.Output, so use $parse() to deserialize it.
Start Execution (Async — Fire and Forget)
"StartAsync": {
"Type": "Task",
"Resource": "arn:aws:states:::states:startExecution",
"Arguments": {
"StateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:AsyncWorkflow",
"Input": "{% $string($states.input) %}"
},
"Next": "Continue"
}Cross-Account Access
Use the Credentials field to assume a role in another account:
"CrossAccountCall": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Credentials": {
"RoleArn": "arn:aws:iam::111122223333:role/CrossAccountRole"
},
"Arguments": {
"FunctionName": "arn:aws:lambda:us-east-1:111122223333:function:RemoteFunction:$LATEST",
"Payload": "{% $states.input %}"
},
"Output": "{% $states.result.Payload %}",
"Next": "Done"
}Restrict role assumption: In the trust policy of
CrossAccountRole, include a condition that scopes which state machines may assume it, e.g."aws:SourceArn": "arn:aws:states:us-east-1:<account>:stateMachine:<name>"(oraws:SourceAccount), to prevent unintended cross-account assumption.
Callback Pattern
"WaitForHumanApproval": {
"Type": "Task",
"Resource": "arn:aws:states:::sqs:sendMessage.waitForTaskToken",
"Arguments": {
"QueueUrl": "https://sqs.us-east-1.amazonaws.com/123456789012/ApprovalQueue",
"MessageBody": "{% $string({'taskToken': $states.context.Task.Token, 'requestId': $states.input.requestId}) %}"
},
"TimeoutSeconds": 604800,
"Catch": [
{
"ErrorEquals": ["States.Timeout"],
"Output": {
"status": "approval_timeout"
},
"Next": "HandleTimeout"
}
],
"Next": "ApprovalReceived"
}The external system must call SendTaskSuccess or SendTaskFailure with the task token to resume execution.