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.

  • Cloud Platforms
  • $aws-api-gateway
  • 174 lines
  • 2 files
  • ~2,000 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- aws-api-gateway

Installs to ~/.agents/skills/aws-api-gateway/ for every project on this machine.

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.

~/.agents/skills/aws-api-gateway/SKILL.md

--- name: aws-api-gateway description: Builds and debugs Amazon API Gateway APIs - choosing HTTP API vs REST API, Lambda proxy integrations and payload formats, JWT, Cognito, IAM and… license: MIT ---

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

  1. Read AGENTS.md and the IaC (SAM, CDK, Terraform, OpenAPI file). Identify the API type: AWS::ApiGatewayV2::Api / AWS::Serverless::HttpApi is an HTTP API; AWS::ApiGateway::RestApi / AWS::Serverless::Api is a REST API.
  2. 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:

NeedHTTP APIREST API
JWT / OIDC authorizer (Cognito, Auth0, Entra ID)NativeCognito authorizer or Lambda authorizer
Lambda authorizer, IAM authYesYes
API keys and usage plans (per-customer quotas)NoYes
AWS WAF directly on the APINo (put CloudFront plus WAF in front)Yes
Response cachingNoYes
Request validation, VTL mapping templatesNoYes
Private endpoint (VPC-only API)NoYes
Resource policies (IP/VPC/account restrictions)NoYes
Canary stage deployments, X-Ray tracingNoYes
Max integration timeout30 s29 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")}),
    }
  • body must be a string. Returning a dict body, a missing statusCode, or an uncaught exception yields 502 (Malformed Lambda proxy response on REST, Internal Server Error on HTTP API).
  • Binary responses need isBase64Encoded: true and, 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

CallerUse
Browser or mobile app with an identity providerJWT authorizer (HTTP API) with issuer and audience; route-level scopes
Service-to-service inside AWSIAM auth (SigV4) with least-privilege execute-api:Invoke on route ARNs
Custom tokens, API keys you validate yourself, multi-tenant rulesLambda authorizer
Partners with quotasREST 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 CorsConfiguration on 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-Origin on 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: true once 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

SymptomCause
REST 403 Missing Authentication TokenRoute or stage does not exist (wrong path, missing stage prefix, not deployed), not an auth problem
REST change not visibleREST 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 tokenWrong issuer (trailing slash), audience mismatch, ID token vs access token
403 after first successful callLambda authorizer cached a narrow policy
502Malformed Lambda response or Lambda error; check integrationError in access logs
504Integration timeout; backend slower than 29-30 s or stuck on network
Browser shows CORS error on 401/500Missing 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.yamlgenerated from this page

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.

esc