AWS API Gateway skill for Codex
Pick the right API type, then lock every route. Codex defaults to HTTP APIs and switches to REST only for usage plans, WAF, caching or private endpoints, then gives every route an explicit authorizer. It knows the Lambda authorizer cache trap, the REST deploy-to-stage step and the meaning of Missing Authentication Token, sets route throttles and JSON access logs, and closes the default execute-api endpoint behind a custom domain.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- aws-api-gateway Installs to ~/.agents/skills/aws-api-gateway/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install aws-api-gateway The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project aws-api-gateway Installs to .agents/skills/aws-api-gateway/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/aws-api-gateway.zipunzip -o aws-api-gateway.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) aws-api-gateway PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $aws-api-gateway, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Builds and debugs Amazon API Gateway APIs - choosing HTTP API vs REST API, Lambda proxy integrations and payload formats, JWT, Cognito, IAM and Lambda authorizers, CORS, throttling with stage, route and usage plan limits, custom domains, access logging, WAF and private APIs. Use when exposing Lambda or HTTP backends as an API, adding authentication to routes, fixing 403 Missing Authentication Token, 502 malformed proxy response, 504 timeouts or CORS errors, or rate limiting API consumers.
Use it when
- Creating an API in front of Lambda or an HTTP backend on AWS
- Adding JWT, IAM or Lambda authorizers
- Debugging 403, 502, 504 or CORS errors from API Gateway
- Throttling or metering API consumers
Use something else when
- You are designing rate-limit algorithms in your own service: use api-rate-limiting
- The question is the Lambda function behind the API: use aws-lambda
How to check the result
curl shows 401 without a token and 200 with one, CORS preflight returns the expected headers, the execute-api URL is disabled, and access logs record status and authorizer errors.
The mistake it prevents
Returning a Lambda authorizer policy scoped to the first methodArn, which gets cached and denies the caller's next route with a 403.
Done well means: the API type matches the features actually needed, every route has an
explicit authorizer (or is deliberately public), throttling protects the backend,
structured access logs identify every failed request, and the default execute-api
endpoint is not a back door around the custom domain and WAF.
Before changing anything
- Read
AGENTS.mdand the IaC (SAM, CDK, Terraform, OpenAPI file). Identify the API type:AWS::ApiGatewayV2::Api/AWS::Serverless::HttpApiis an HTTP API;AWS::ApiGateway::RestApi/AWS::Serverless::Apiis a REST API. - Inspect live config when credentials and network are available:
aws apigatewayv2 get-apis --query 'Items[].[Name,ApiId,ProtocolType,DisableExecuteApiEndpoint]'
aws apigatewayv2 get-routes --api-id $API --query 'Items[].[RouteKey,AuthorizationType,AuthorizerId]' --output table
aws apigateway get-rest-apis --query 'items[].[name,id,endpointConfiguration.types[0]]'
aws apigateway get-stage --rest-api-id $API --stage-name prod --query '{throttle:methodSettings,logs:accessLogSettings,waf:webAclArn}'
HTTP API or REST API
Default to HTTP API: lower price per request, lower latency, simpler config. Choose REST API only when you need one of its features:
| Need | HTTP API | REST API |
|---|---|---|
| JWT / OIDC authorizer (Cognito, Auth0, Entra ID) | Native | Cognito authorizer or Lambda authorizer |
| Lambda authorizer, IAM auth | Yes | Yes |
| API keys and usage plans (per-customer quotas) | No | Yes |
| AWS WAF directly on the API | No (put CloudFront plus WAF in front) | Yes |
| Response caching | No | Yes |
| Request validation, VTL mapping templates | No | Yes |
| Private endpoint (VPC-only API) | No | Yes |
| Resource policies (IP/VPC/account restrictions) | No | Yes |
| Canary stage deployments, X-Ray tracing | No | Yes |
| Max integration timeout | 30 s | 29 s default, raisable for regional and private APIs |
Both: 10 MB payload limit, Lambda and HTTP proxy integrations, custom domains, WebSocket support exists as a separate API type.
Lambda proxy integrations
HTTP APIs should use payload format 2.0; REST APIs use the 1.0 shape.
def handler(event, context):
method = event["requestContext"]["http"]["method"] # 2.0
path = event["rawPath"]
claims = event["requestContext"].get("authorizer", {}).get("jwt", {}).get("claims", {})
return {
"statusCode": 200,
"headers": {"content-type": "application/json"},
"body": json.dumps({"user": claims.get("sub")}),
}
bodymust be a string. Returning a dict body, a missingstatusCode, or an uncaught exception yields 502 (Malformed Lambda proxy responseon REST,Internal Server Erroron HTTP API).- Binary responses need
isBase64Encoded: trueand, on REST APIs, binary media types configured. - Keep the Lambda timeout below the API’s integration timeout, and the downstream timeouts below the Lambda timeout, so errors are explicit rather than 504s.
Authorization
| Caller | Use |
|---|---|
| Browser or mobile app with an identity provider | JWT authorizer (HTTP API) with issuer and audience; route-level scopes |
| Service-to-service inside AWS | IAM auth (SigV4) with least-privilege execute-api:Invoke on route ARNs |
| Custom tokens, API keys you validate yourself, multi-tenant rules | Lambda authorizer |
| Partners with quotas | REST API usage plans with API keys, plus a real authorizer (API keys alone are not authentication) |
JWT authorizer (SAM, HTTP API):
OrdersApi:
Type: AWS::Serverless::HttpApi
Properties:
Auth:
DefaultAuthorizer: Cognito
Authorizers:
Cognito:
IdentitySource: $request.header.Authorization
JwtConfiguration:
issuer: !Sub https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}
audience: [!Ref UserPoolClient]
The authorizer checks aud, or client_id for Cognito access tokens. Use
AuthorizationScopes per route for scope checks. Setting a default authorizer and
explicitly opting public routes out (Authorizer: NONE) is safer than the reverse.
Lambda authorizer caching trap (REST, TOKEN/REQUEST type): the policy returned is cached
per identity for the TTL (300 s default) and reused for every method. If the policy only
allows the methodArn of the first call, the next route returns 403. Return a policy
covering the routes the identity may call (e.g. arn:aws:execute-api:region:acct:api/stage/*)
or include the route in the identity source. For HTTP APIs, prefer simple responses
({"isAuthorized": true, "context": {...}}).
CORS
- HTTP API: configure
CorsConfigurationon the API (allowed origins, methods, headers,AllowCredentials). API Gateway answers preflight; do not also add OPTIONS routes that hit Lambda. Credentials cannot be combined with origin*. - REST API with Lambda proxy: API Gateway CORS settings only cover the OPTIONS mock; your
Lambda must return
Access-Control-Allow-Originon every response, including errors. Also add CORS headers to gateway responses (DEFAULT_4XX,DEFAULT_5XX) so auth failures are readable by the browser instead of looking like CORS errors.
Throttling and protection
- Account-level default is 10,000 requests per second with a 5,000 burst per region, shared by all APIs. One noisy API can starve the others; set stage or route limits.
- HTTP API: route-level throttling in stage
RouteSettings. REST API: method settings per stage (*/*for all) and usage plans per API key. - Throttled requests return 429; clients should back off with jitter.
- Public REST APIs: attach AWS WAF with rate-based rules and managed rule groups. HTTP APIs: front them with CloudFront plus WAF, and disable the default endpoint.
Custom domains and endpoints
- Regional custom domain with an ACM certificate in the same region; edge-optimized REST APIs need the certificate in us-east-1.
- Map base paths (
/v1,/v2) to stages or APIs. - Set
DisableExecuteApiEndpoint: trueonce the custom domain works, so clients cannot bypass the domain, CloudFront and WAF.
Logging
Enable access logs in JSON on every stage; execution logs on REST only when debugging (they can contain request data).
{"requestId":"$context.requestId","ip":"$context.identity.sourceIp","method":"$context.httpMethod",
"path":"$context.path","status":"$context.status","latency":"$context.responseLatency",
"integrationStatus":"$context.integrationStatus","integrationError":"$context.integrationErrorMessage",
"authError":"$context.authorizer.error"}
Debugging table
| Symptom | Cause |
|---|---|
REST 403 Missing Authentication Token | Route or stage does not exist (wrong path, missing stage prefix, not deployed), not an auth problem |
| REST change not visible | REST APIs need a new deployment to the stage: aws apigateway create-deployment --rest-api-id $API --stage-name prod |
| 401 on HTTP API with valid token | Wrong issuer (trailing slash), audience mismatch, ID token vs access token |
| 403 after first successful call | Lambda authorizer cached a narrow policy |
| 502 | Malformed Lambda response or Lambda error; check integrationError in access logs |
| 504 | Integration timeout; backend slower than 29-30 s or stuck on network |
| Browser shows CORS error on 401/500 | Missing CORS headers on error responses or gateway responses |
Deploying to a production stage or removing routes is outward-facing: confirm with the user first.
Verification
curl -i https://api.acme.com/v1/orders # 401 without token
curl -i -H "Authorization: Bearer $TOKEN" https://api.acme.com/v1/orders # 200
curl -i https://$API.execute-api.eu-west-1.amazonaws.com/orders # fails once disabled
curl -i -X OPTIONS -H "Origin: https://app.acme.com" \
-H "Access-Control-Request-Method: POST" https://api.acme.com/v1/orders
Then confirm access log lines show status, latency and authorizer errors for each request.
What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "AWS API Gateway"
short_description: "Pick the right API type, then lock every route"
default_prompt: "$aws-api-gateway Expose these Lambda functions as an HTTP API with Cognito JWT auth" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.