AWSAWS Certified Generative AI Developer - Professional2026-102.1 Implement agentic AI solutions and tool integrations
Bind human approval to the exact action
Completed by the knowledge check
Learning objective
Require human approval before high-impact actions with Step Functions callbacks and API Gateway feedback interfaces
Core idea
For an architect automating consequential actions, approval must be an execution gate. The agent proposes an action, a human reviews a fixed representation of it, and only an authorized decision can make the action executor reachable. A prompt asking the agent to seek permission does not establish that gate.
Bind approval to the exact operation: the target resource, amount or scope, tenant, evidence version, and expiration. If any consequential field changes, the action needs another approval. A generic approval of “the plan” must not authorize whatever arguments the model generates later.
Step Functions supplies a durable waiting mechanism through callback task tokens. API Gateway supplies the feedback interface. Your application establishes reviewer identity, business authorization, decision integrity, and replay handling. The token identifies a waiting task; it is not a statement that the reviewer has authority over the proposed action.
How it works
Create the review request before the action
Choose a Standard workflow for a callback gate. The .waitForTaskToken integration pattern pauses a task until the token returns through SendTaskSuccess or SendTaskFailure. Express workflows do not support that pattern. Standard workflows can run for up to one year. Set TimeoutSeconds on the .waitForTaskToken task to enforce a shorter approval deadline, and HeartbeatSeconds below it only if the review backend calls SendTaskHeartbeat while the proposal is pending; without heartbeats, HeartbeatSeconds becomes the effective approval deadline. Route States.Timeout through Catch to mark the application proposal record expired. Choose both intervals for the evidence and business context.
A common gate uses arn:aws:states:::lambda:invoke.waitForTaskToken. Pass the token from the Step Functions context object to a Lambda function that creates the review request. In a JSONata task, the context expression is $states.context.Task.Token; JSONPath tasks access it through $$.Task.Token. Keep those syntaxes separate.
The Lambda function records the proposal and makes it available to reviewers, then returns. It does not remain active while a human decides. The workflow stays waiting for the callback. With the optimized Lambda callback integration, the heartbeat timeout starts after the Lambda function finishes; the function’s runtime and the approval wait are different phases.
Store the token server-side with the immutable proposal identifier. Present reviewers with the operation and its supporting evidence, not the token itself. Treat a token as sensitive execution data: a browser URL, model prompt, or tool observation is the wrong place to use it as a business credential.
Distinguish notification from feedback
Step Functions can also call an API Gateway endpoint using arn:aws:states:::apigateway:invoke.waitForTaskToken. The outgoing request can carry the task token to a trusted service. AuthType supports NO_AUTH, IAM_ROLE, and RESOURCE_POLICY; its default is NO_AUTH. Choose authentication deliberately. IAM_ROLE uses the workflow’s execution role, while a resource policy must restrict the calling state machine.
That outgoing integration is separate from the endpoint a reviewer uses to submit a decision. Receiving an HTTP response from the review-request service only proves that the request completed. For the callback pattern, Step Functions still waits for the token to return through a callback API.
Expose a feedback route through API Gateway with a Lambda handler. For an IAM-authenticated REST API method, authorizationType: AWS_IAM requires signed credentials, and an IAM policy grants execute-api:Invoke for the allowed method and resource path. Authentication establishes the caller; the handler must still check that this caller can approve this proposal for this tenant and action scope.
Keep the feedback request small and explicit: proposal identifier, decision, and an expected proposal revision. Resolve the associated token from trusted storage. Never accept a caller-supplied action body as the approved action, and do not let a model call the approval endpoint as though it were the human reviewer.
Resume with an explicit decision
SendTaskSuccess accepts taskToken and an output string containing JSON. The name describes successful completion of the waiting task, not automatic approval of the business action. A handler can return a valid rejection decision through SendTaskSuccess; a following Choice then routes rejection to a terminal outcome. Make the approved branch depend on the validated decision value.
The task token returns to the waiting workflow from the feedback handler, not from the Lambda function that received it, and a Choice then routes the explicit decision.
Alternatively, SendTaskFailure reports that the callback task failed and enters workflow error handling. Choose one business-decision convention and one infrastructure-failure convention so reviewers and operators can distinguish rejection from a broken feedback service. Do not use absence of an error as the approval criterion.
The principal returning a task token must be in the same AWS account as the waiting workflow. Reviewers can use your interface, but the trusted backend that performs the callback needs states:SendTaskSuccess or states:SendTaskFailure, as appropriate, and the same-account context. A third party’s possession of the token is not enough to bypass that requirement.
Reject stale and replayed decisions
Before returning the token, verify the reviewer, pending state, proposal revision, and expiration. DynamoDB UpdateItem with ConditionExpression can claim a pending proposal revision and record the decision. A failed condition becomes a stale or already-decided response rather than a second approval. This is an application record, not a built-in approval object in Step Functions.
The decision record and the callback are separate operations. Persist enough state to retry delivery after a callback transport failure without accepting a different decision. If SendTaskSuccess returns TaskTimedOut, the token has expired or its task is already closed. Reconcile the recorded decision with the workflow outcome; do not interpret that response as permission to invoke the action directly.
After an approved callback, recheck that the executable action matches the approved revision. Revalidate any business precondition that can change while review is pending. The action executor should receive the fixed approved operation, use a stable operation identifier, and handle an uncertain write through reconciliation. See 2.1.7 for execution limits and IAM boundaries.
Key distinctions and trade-offs
The following events carry different meanings:
| Event | Meaning | Additional check |
|---|---|---|
| Review-request HTTP response | The notification endpoint responded | Human decision is still pending |
| Authenticated feedback request | Caller identity is established | Reviewer authority and proposal scope |
SendTaskSuccess | Waiting task completed with JSON output | Decision must explicitly authorize execution |
| Approval expiration | Task fails with States.Timeout; the expiry handler marks the application record expired | Re-propose and obtain fresh approval |
Long review windows improve availability to reviewers but increase the chance that evidence or business state changes. Immutable proposal revisions and revalidation preserve the meaning of approval without extending it to changed actions.
Architecture / decision scenario
A purchasing agent proposes a supplier payment. A Standard workflow creates a proposal containing the supplier, amount, currency, tenant, invoice revision, and expiration. Its callback Lambda stores the task token and returns after creating the review request.
A finance reviewer submits approval through an IAM-authenticated API Gateway method. The feedback Lambda checks the reviewer’s authority, conditionally claims the pending proposal revision, and calls SendTaskSuccess with an explicit approved decision. A workflow Choice permits the payment executor only for that decision.
While review is pending, the supplier bank details change. The executor’s precondition check detects that the target no longer matches the approved proposal. It ends without paying and requires a fresh proposal and review. If the reviewer instead rejects the original proposal, the callback completes normally with a rejected decision and the workflow terminates without reaching the payment task.
Common exam traps
- Selecting Express for
.waitForTaskTokenapproval. - Keeping a Lambda invocation running while a human reviews.
- Treating an HTTP notification response as human approval.
- Treating
SendTaskSuccessas an approved business decision regardless of output. - Using token possession instead of reviewer authorization.
- Applying approval to revised arguments or expired evidence.
What to remember
- The callback pauses execution; your application validates approval.
- Separate outgoing review requests from incoming feedback.
- Keep tokens server-side and return them through a trusted same-account principal.
- Route on an explicit decision bound to an immutable proposal revision.
- Reconcile callback delivery and revalidate the action before execution.
Knowledge check
A Standard workflow sends a payment proposal to a review service through arn:aws:states:::apigateway:invoke.waitForTaskToken, and the service responds with HTTP 200. No reviewer has looked at the proposal yet. What happens next?
SendTaskSuccess or SendTaskFailure .A team implements an approval gate with lambda:invoke.waitForTaskToken. Its Lambda function stores the token, notifies finance, and then polls DynamoDB until a reviewer decides, which can take two days. What should change?
Timeout so the poll can last for the whole reviewWait state followed by a decision lookup.waitForTaskToken . A fixed Wait guesses when the reviewer will be done instead of resuming on the decision.A finance reviewer rejects a payment proposal, and the feedback handler calls SendTaskSuccess with {"decision": "rejected"} as its output. The next state invokes the payment executor. What prevents the payment?
SendTaskFailure for every rejection, so success always means approvalChoice state that reaches the executor only when the decision is approvedSendTaskSuccess call itself, which records the rejection in Step FunctionsSendTaskSuccess means the waiting task completed with that JSON output, whatever the business decision inside it. The workflow has to route on the decision value. Using failure for rejection would mix business outcomes with a broken feedback service, and an expired token is a timeout, not a recorded decision.A reviewer approved a supplier payment of 12,000 EUR to a stated bank account. Before the executor runs, the supplier’s bank account changes in the vendor record. The amount and supplier name are unchanged. What should the executor do?
Two finance reviewers open the same pending proposal. One approves; seconds later the other submits a rejection for the same proposal revision. How does the feedback handler keep one decision per revision?
SendTaskSuccess for both, since Step Functions ignores the secondUpdateItem before the callback