I rewarded bounty to Muzaffar Shaikh, but here I will give a more thorough explanation, which seems to be lacking on StackOverflow. His answer got the IP Address, but dropped my "body" field, but it certainly pointed me in the right direction.
In the AWS API Gateway tool, click "Resources" then your method (mine was "Post"), then click on "Integration Request" as shown here.

Scroll down to the bottom, and if there are not any templates, type in "application/json" and click the checkbox (note, "application/json" is there in light gray letters, but just clicking the checkbox without typing it doesn't work).
Then I put in the following template:
{
"client_ip" : "$input.params('X-Forwarded-For')",
"user_agent" : "$input.params('User-Agent')",
"body" : $input.json('$.body')
}
NOTE: if I put $input.json('$') or $input.json('body'), I ended up with a "body" field inside my "body" field, which broke the current logic.
When the web page was completed, it looked like this:

The next step is to redeploy your template to the "deploy stage" (environment) you were using.


By the way, when entering the template, if you click "Method Request passthrough" in the "Generate template" drop down box, it will generate a template like this (I didn't use this option, but will read more about it soon):
## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"body-json" : $input.json('$'),
"params" : {
#foreach($type in $allParams.keySet())
#set($params = $allParams.get($type))
"$type" : {
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
#if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
#if($foreach.hasNext),#end
#end
},
"context" : {
"account-id" : "$context.identity.accountId",
"api-id" : "$context.apiId",
"api-key" : "$context.identity.apiKey",
"authorizer-principal-id" : "$context.authorizer.principalId",
"caller" : "$context.identity.caller",
"cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
"cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
"cognito-identity-id" : "$context.identity.cognitoIdentityId",
"cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
"http-method" : "$context.httpMethod",
"stage" : "$context.stage",
"source-ip" : "$context.identity.sourceIp",
"user" : "$context.identity.user",
"user-agent" : "$context.identity.userAgent",
"user-arn" : "$context.identity.userArn",
"request-id" : "$context.requestId",
"resource-id" : "$context.resourceId",
"resource-path" : "$context.resourcePath"
}
}
Two reference for mapping templates:
- https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
- https://docs.aws.amazon.com/apigateway/latest/developerguide/models-mappings.html
I still have some research to do as to when to user "$input.params" vs "$context.some_value".