Event-Driven AWS Basics
Seven examples to get you started with EventBridge and Step Functions - five basic and two intermediate.
Search across all documentation pages
Seven examples to get you started with EventBridge and Step Functions - five basic and two intermediate.
Each example shows the same operation in both Python (boto3) and TypeScript (AWS SDK for JavaScript v3).
pip install boto3 (boto3 1.43.x, Python 3.10+). TypeScript: npm install @aws-sdk/client-eventbridge @aws-sdk/client-sfn (Node.js 18+).aws configure, environment variables, or an IAM role.Use a custom bus to isolate an application's own events from the account's default bus.
# --- Python (boto3) ---
import boto3
events = boto3.client("events")
bus = events.create_event_bus(Name="orders-bus")
print(bus["EventBusArn"])// --- TypeScript (AWS SDK v3) ---
import { EventBridgeClient, CreateEventBusCommand } from "@aws-sdk/client-eventbridge";
const events = new EventBridgeClient({});
const bus = await events.send(new CreateEventBusCommand({ Name: "orders-bus" }));
console.log(bus.EventBusArn);default bus already accepts events from AWS services and your own code.CreateEventBus is idempotent by name; calling it again with the same name returns the existing bus.EventBusArn for cross-account targets.Related: Event Buses, Rules & Pattern Matching - buses and patterns in depth.
A rule matches events by their source, detail-type, and detail fields.
# --- Python (boto3) ---
import boto3
import json
events = boto3.client("events")
events.put_rule(
Name="order-placed-rule",
EventBusName="orders-bus",
EventPattern=json.dumps({
"source": ["orders.api"],
"detail-type": ["OrderPlaced"],
}),
)// --- TypeScript (AWS SDK v3) ---
import { EventBridgeClient, PutRuleCommand } from "@aws-sdk/client-eventbridge";
const events = new EventBridgeClient({});
await events.send(new PutRuleCommand({
Name: "order-placed-rule",
EventBusName: "orders-bus",
EventPattern: JSON.stringify({
source: ["orders.api"],
"detail-type": ["OrderPlaced"],
}),
}));EventPattern is JSON matched structurally against incoming events; array values mean "any of these."EventBusName; omit it to use the default bus.PutTargets is called.detail, not just the envelope.Related: Event Buses, Rules & Pattern Matching - full pattern syntax.
Targets are what actually run when a rule matches - a Lambda function, a queue, or a state machine.
# --- Python (boto3) ---
import boto3
events = boto3.client("events")
events.put_targets(
Rule="order-placed-rule",
EventBusName="orders-bus",
Targets=[{
"Id": "notify-fulfillment",
"Arn": "arn:aws:lambda:us-east-1:111122223333:function:notify-fulfillment",
}],
)// --- TypeScript (AWS SDK v3) ---
import { EventBridgeClient, PutTargetsCommand } from "@aws-sdk/client-eventbridge";
const events = new EventBridgeClient({});
await events.send(new PutTargetsCommand({
Rule: "order-placed-rule",
EventBusName: "orders-bus",
Targets: [{
Id: "notify-fulfillment",
Arn: "arn:aws:lambda:us-east-1:111122223333:function:notify-fulfillment",
}],
}));Id within the rule and the target resource's ARN.RoleArn so EventBridge has permission to invoke them.PutEvents is how your own code tells EventBridge something happened.
# --- Python (boto3) ---
import boto3
import json
events = boto3.client("events")
result = events.put_events(Entries=[{
"Source": "orders.api",
"DetailType": "OrderPlaced",
"Detail": json.dumps({"orderId": "ord-1", "total": 42.50}),
"EventBusName": "orders-bus",
}])
print(result["FailedEntryCount"])// --- TypeScript (AWS SDK v3) ---
import { EventBridgeClient, PutEventsCommand } from "@aws-sdk/client-eventbridge";
const events = new EventBridgeClient({});
const result = await events.send(new PutEventsCommand({
Entries: [{
Source: "orders.api",
DetailType: "OrderPlaced",
Detail: JSON.stringify({ orderId: "ord-1", total: 42.50 }),
EventBusName: "orders-bus",
}],
}));
console.log(result.FailedEntryCount);Detail is a JSON string, not an object - serialize it yourself before sending.PutEvents accepts up to 10 entries per call; check FailedEntryCount since a partial failure does not raise an error.Source should be a stable string identifying the producer, conventionally dotted like orders.api.source, detail-type, or detail fields.StartExecution runs a registered state machine against a JSON input.
# --- Python (boto3) ---
import boto3
import json
sfn = boto3.client("stepfunctions")
run = sfn.start_execution(
stateMachineArn="arn:aws:states:us-east-1:111122223333:stateMachine:FulfillOrder",
input=json.dumps({"orderId": "ord-1"}),
)
print(run["executionArn"])// --- TypeScript (AWS SDK v3) ---
import { SFNClient, StartExecutionCommand } from "@aws-sdk/client-sfn";
const sfn = new SFNClient({});
const run = await sfn.send(new StartExecutionCommand({
stateMachineArn: "arn:aws:states:us-east-1:111122223333:stateMachine:FulfillOrder",
input: JSON.stringify({ orderId: "ord-1" }),
}));
console.log(run.executionArn);input is a JSON string, matching how the state machine's first state receives it.StartExecution returns immediately with an executionArn - it does not wait for the workflow to finish.name for the execution; omit it and Step Functions generates one.executionArn with DescribeExecution to check on progress or read the result.Related: Step Functions State Machines: Standard vs Express - Standard vs Express execution behavior.
CreateStateMachine registers an ASL definition as a runnable resource.
# --- Python (boto3) ---
import boto3
import json
sfn = boto3.client("stepfunctions")
definition = {
"Comment": "Fulfill an order",
"StartAt": "ReserveInventory",
"States": {
"ReserveInventory": {"Type": "Pass", "End": True},
},
}
sm = sfn.create_state_machine(
name="FulfillOrder",
definition=json.dumps(definition),
roleArn="arn:aws:iam::111122223333:role/sfn-fulfill-order",
type="STANDARD",
)
print(sm["stateMachineArn"])// --- TypeScript (AWS SDK v3) ---
import { SFNClient, CreateStateMachineCommand } from "@aws-sdk/client-sfn";
const sfn = new SFNClient({});
const definition = {
Comment: "Fulfill an order",
StartAt: "ReserveInventory",
States: {
ReserveInventory: { Type: "Pass", End: true },
},
};
const sm = await sfn.send(new CreateStateMachineCommand({
name: "FulfillOrder",
definition: JSON.stringify(definition),
roleArn: "arn:aws:iam::111122223333:role/sfn-fulfill-order",
type: "STANDARD",
}));
console.log(sm.stateMachineArn);definition is the ASL document as a JSON string - the same format regardless of which SDK creates it.type is STANDARD or EXPRESS; it cannot be changed after creation without recreating the state machine.roleArn needs permission to invoke whatever the states call - other AWS APIs, or Lambda functions.Pass state is a placeholder that does no work; replace it with Task states for real steps.Related: Step Functions SDK Integrations - calling real AWS APIs from a state.
DescribeExecution reads back status, input, and output for a specific run.
# --- Python (boto3) ---
import boto3
sfn = boto3.client("stepfunctions")
desc = sfn.describe_execution(
executionArn="arn:aws:states:us-east-1:111122223333:execution:FulfillOrder:abc123",
)
print(desc["status"], desc.get("output"))// --- TypeScript (AWS SDK v3) ---
import { SFNClient, DescribeExecutionCommand } from "@aws-sdk/client-sfn";
const sfn = new SFNClient({});
const desc = await sfn.send(new DescribeExecutionCommand({
executionArn: "arn:aws:states:us-east-1:111122223333:execution:FulfillOrder:abc123",
}));
console.log(desc.status, desc.output);status is one of RUNNING, SUCCEEDED, FAILED, TIMED_OUT, or ABORTED.output is only present once status is SUCCEEDED; poll or use a waiter rather than reading it immediately after StartExecution.DescribeExecution history lookups the same way Standard does - use StartSyncExecution's direct response instead.executionArn around anywhere you need to correlate a later result with the run that started it.Related: Error Handling & Retries in State Machines - handling a
FAILEDexecution.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 23, 2026