The handler is the single seam between AWS's request/response format and your agent's actual logic — get its shape wrong and nothing else matters.
1The event Dict Is a Contract, Not a Convenience
AWS Lambda's integration with API Gateway hands your function an event dict with a specific, fixed shape — the actual HTTP request body arrives as a raw string under event["body"], regardless of how nested the real data inside it is. Your handler is responsible for parsing that string into something usable, exactly as a model's tool call arguments needed explicit parsing earlier in this masterclass.
2The Response Shape Mirrors the Request Shape
API Gateway expects the Lambda response to include a statusCode and a body that is itself a string — even though that string typically holds serialized JSON representing structured data. Returning result directly as a nested dict in body, instead of json.dumps(result), breaks that contract and produces a malformed HTTP response.
3Step-by-Step Breakdown
Real Input, Real Output, One Function. AWS hands lambda_handler an event dict where the actual HTTP request body arrives as a raw JSON string, not a parsed object — the same lesson from parsing a model's tool-call arguments, applied at the deployment boundary this time. Your handler has to parse it, run the agent, and return a response shaped exactly the way API Gateway expects.
Write the Real Handler. run_agent and the request-body parsing are done. Finish lambda_handler so it returns a real API Gateway-shaped response: a statusCode and a body that's itself a JSON string of the agent's result.
Why must the response's "body" field be a JSON string (via json.dumps), rather than returning result as a plain nested dict directly?
- →API Gateway's integration contract expects the Lambda response's body field to be a string it can pass through as the actual HTTP response body — a nested object there wouldn't match what a real HTTP client expects to receive as raw response text.
- →It's purely a stylistic convention with no effect on whether the real HTTP response works correctly.
A Real Handler, One Guard Left. lambda_handler now genuinely works end to end — real parsing in, real agent logic, real response shape out. One more thing stands between this and safely running in production: making sure the function only has the AWS permissions it actually needs. Final lesson: IAM, guardrails, and shipping.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Set an Explicit Content-Type Header on Every Response
Returning JSON in the body without declaring `"headers": {"Content-Type": "application/json"}` can cause some clients to misinterpret the response — always declare the content type explicitly rather than relying on a default.
{"statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": "..."}SEO Implications
- 1
Target 'AWS Lambda handler Python example' and 'API Gateway Lambda proxy integration format' separately
Developers writing their first handler search for the general pattern and the specific response-shape contract independently.
Best Practices
Wrap the Body-Parsing Step in Error Handling
A malformed or missing request body should return a clean 400 response with a clear error message, not let an unhandled json.loads exception produce an opaque 500 error to the caller.
Frequent Bugs
Returning `{"statusCode": 200, "body": result}` with `result` as a raw dict instead of a JSON string.
This violates API Gateway's Lambda proxy integration contract and typically produces a malformed or rejected HTTP response — body must always be serialized to a string with json.dumps first.
Real-World Examples
Consistent Request/Response Shape
Every Lambda function behind API Gateway using proxy integration follows this exact same pattern — parse a string body in, return a string body out — regardless of how different the underlying business logic is between functions.
body = json.loads(event["body"]); ...; return {"statusCode": 200, "body": json.dumps(out)}