Configure AWS API Gateway V2 Integrations

The aws:apigatewayv2/integration:Integration resource, part of the Pulumi AWS provider, defines how API Gateway V2 routes requests to backend services: Lambda functions, AWS services, or private endpoints. This guide focuses on three capabilities: Lambda proxy integrations, direct AWS service invocation, and VPC Link for private resources.

Integrations connect API Gateway V2 APIs to Lambda functions, AWS services, or load balancers via VPC Links. The examples are intentionally small. Combine them with your own API routes, authorizers, and deployment stages.

Route requests to Lambda functions

Most HTTP APIs route incoming requests to Lambda functions for serverless request handling.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.lambda.Function("example", {
    code: new pulumi.asset.FileArchive("example.zip"),
    name: "Example",
    role: exampleAwsIamRole.arn,
    handler: "index.handler",
    runtime: aws.lambda.Runtime.NodeJS20dX,
});
const exampleIntegration = new aws.apigatewayv2.Integration("example", {
    apiId: exampleAwsApigatewayv2Api.id,
    integrationType: "AWS_PROXY",
    connectionType: "INTERNET",
    contentHandlingStrategy: "CONVERT_TO_TEXT",
    description: "Lambda example",
    integrationMethod: "POST",
    integrationUri: example.invokeArn,
    passthroughBehavior: "WHEN_NO_MATCH",
});
import pulumi
import pulumi_aws as aws

example = aws.lambda_.Function("example",
    code=pulumi.FileArchive("example.zip"),
    name="Example",
    role=example_aws_iam_role["arn"],
    handler="index.handler",
    runtime=aws.lambda_.Runtime.NODE_JS20D_X)
example_integration = aws.apigatewayv2.Integration("example",
    api_id=example_aws_apigatewayv2_api["id"],
    integration_type="AWS_PROXY",
    connection_type="INTERNET",
    content_handling_strategy="CONVERT_TO_TEXT",
    description="Lambda example",
    integration_method="POST",
    integration_uri=example.invoke_arn,
    passthrough_behavior="WHEN_NO_MATCH")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/apigatewayv2"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := lambda.NewFunction(ctx, "example", &lambda.FunctionArgs{
			Code:    pulumi.NewFileArchive("example.zip"),
			Name:    pulumi.String("Example"),
			Role:    pulumi.Any(exampleAwsIamRole.Arn),
			Handler: pulumi.String("index.handler"),
			Runtime: pulumi.String(lambda.RuntimeNodeJS20dX),
		})
		if err != nil {
			return err
		}
		_, err = apigatewayv2.NewIntegration(ctx, "example", &apigatewayv2.IntegrationArgs{
			ApiId:                   pulumi.Any(exampleAwsApigatewayv2Api.Id),
			IntegrationType:         pulumi.String("AWS_PROXY"),
			ConnectionType:          pulumi.String("INTERNET"),
			ContentHandlingStrategy: pulumi.String("CONVERT_TO_TEXT"),
			Description:             pulumi.String("Lambda example"),
			IntegrationMethod:       pulumi.String("POST"),
			IntegrationUri:          example.InvokeArn,
			PassthroughBehavior:     pulumi.String("WHEN_NO_MATCH"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.Lambda.Function("example", new()
    {
        Code = new FileArchive("example.zip"),
        Name = "Example",
        Role = exampleAwsIamRole.Arn,
        Handler = "index.handler",
        Runtime = Aws.Lambda.Runtime.NodeJS20dX,
    });

    var exampleIntegration = new Aws.ApiGatewayV2.Integration("example", new()
    {
        ApiId = exampleAwsApigatewayv2Api.Id,
        IntegrationType = "AWS_PROXY",
        ConnectionType = "INTERNET",
        ContentHandlingStrategy = "CONVERT_TO_TEXT",
        Description = "Lambda example",
        IntegrationMethod = "POST",
        IntegrationUri = example.InvokeArn,
        PassthroughBehavior = "WHEN_NO_MATCH",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.apigatewayv2.Integration;
import com.pulumi.aws.apigatewayv2.IntegrationArgs;
import com.pulumi.asset.FileArchive;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new Function("example", FunctionArgs.builder()
            .code(new FileArchive("example.zip"))
            .name("Example")
            .role(exampleAwsIamRole.arn())
            .handler("index.handler")
            .runtime("nodejs20.x")
            .build());

        var exampleIntegration = new Integration("exampleIntegration", IntegrationArgs.builder()
            .apiId(exampleAwsApigatewayv2Api.id())
            .integrationType("AWS_PROXY")
            .connectionType("INTERNET")
            .contentHandlingStrategy("CONVERT_TO_TEXT")
            .description("Lambda example")
            .integrationMethod("POST")
            .integrationUri(example.invokeArn())
            .passthroughBehavior("WHEN_NO_MATCH")
            .build());

    }
}
resources:
  example:
    type: aws:lambda:Function
    properties:
      code:
        fn::FileArchive: example.zip
      name: Example
      role: ${exampleAwsIamRole.arn}
      handler: index.handler
      runtime: nodejs20.x
  exampleIntegration:
    type: aws:apigatewayv2:Integration
    name: example
    properties:
      apiId: ${exampleAwsApigatewayv2Api.id}
      integrationType: AWS_PROXY
      connectionType: INTERNET
      contentHandlingStrategy: CONVERT_TO_TEXT
      description: Lambda example
      integrationMethod: POST
      integrationUri: ${example.invokeArn}
      passthroughBehavior: WHEN_NO_MATCH

When a request arrives, API Gateway forwards it to the Lambda function specified in integrationUri. The AWS_PROXY integration type passes the full request context (headers, body, query parameters) to your function and expects a structured response. The integrationMethod specifies the HTTP method API Gateway uses when invoking the integration endpoint.

Integrate with AWS services using subtypes

HTTP APIs can invoke AWS services directly without Lambda middleware, translating HTTP requests into service API calls.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.apigatewayv2.Integration("example", {
    apiId: exampleAwsApigatewayv2Api.id,
    credentialsArn: exampleAwsIamRole.arn,
    description: "SQS example",
    integrationType: "AWS_PROXY",
    integrationSubtype: "SQS-SendMessage",
    requestParameters: {
        QueueUrl: "$request.header.queueUrl",
        MessageBody: "$request.body.message",
    },
});
import pulumi
import pulumi_aws as aws

example = aws.apigatewayv2.Integration("example",
    api_id=example_aws_apigatewayv2_api["id"],
    credentials_arn=example_aws_iam_role["arn"],
    description="SQS example",
    integration_type="AWS_PROXY",
    integration_subtype="SQS-SendMessage",
    request_parameters={
        "QueueUrl": "$request.header.queueUrl",
        "MessageBody": "$request.body.message",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/apigatewayv2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := apigatewayv2.NewIntegration(ctx, "example", &apigatewayv2.IntegrationArgs{
			ApiId:              pulumi.Any(exampleAwsApigatewayv2Api.Id),
			CredentialsArn:     pulumi.Any(exampleAwsIamRole.Arn),
			Description:        pulumi.String("SQS example"),
			IntegrationType:    pulumi.String("AWS_PROXY"),
			IntegrationSubtype: pulumi.String("SQS-SendMessage"),
			RequestParameters: pulumi.StringMap{
				"QueueUrl":    pulumi.String("$request.header.queueUrl"),
				"MessageBody": pulumi.String("$request.body.message"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.ApiGatewayV2.Integration("example", new()
    {
        ApiId = exampleAwsApigatewayv2Api.Id,
        CredentialsArn = exampleAwsIamRole.Arn,
        Description = "SQS example",
        IntegrationType = "AWS_PROXY",
        IntegrationSubtype = "SQS-SendMessage",
        RequestParameters = 
        {
            { "QueueUrl", "$request.header.queueUrl" },
            { "MessageBody", "$request.body.message" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.apigatewayv2.Integration;
import com.pulumi.aws.apigatewayv2.IntegrationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new Integration("example", IntegrationArgs.builder()
            .apiId(exampleAwsApigatewayv2Api.id())
            .credentialsArn(exampleAwsIamRole.arn())
            .description("SQS example")
            .integrationType("AWS_PROXY")
            .integrationSubtype("SQS-SendMessage")
            .requestParameters(Map.ofEntries(
                Map.entry("QueueUrl", "$request.header.queueUrl"),
                Map.entry("MessageBody", "$request.body.message")
            ))
            .build());

    }
}
resources:
  example:
    type: aws:apigatewayv2:Integration
    properties:
      apiId: ${exampleAwsApigatewayv2Api.id}
      credentialsArn: ${exampleAwsIamRole.arn}
      description: SQS example
      integrationType: AWS_PROXY
      integrationSubtype: SQS-SendMessage
      requestParameters:
        QueueUrl: $request.header.queueUrl
        MessageBody: $request.body.message

The integrationSubtype property specifies which AWS service action to invoke (here, SQS-SendMessage). The requestParameters map extracts values from the incoming request using variable references like $request.header.queueUrl and $request.body.message, then passes them as parameters to the SQS API. The credentialsArn grants API Gateway permission to call SQS on your behalf.

Applications that need to reach internal load balancers or services in private subnets use VPC Link integrations.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.apigatewayv2.Integration("example", {
    apiId: exampleAwsApigatewayv2Api.id,
    credentialsArn: exampleAwsIamRole.arn,
    description: "Example with a load balancer",
    integrationType: "HTTP_PROXY",
    integrationUri: exampleAwsLbListener.arn,
    integrationMethod: "ANY",
    connectionType: "VPC_LINK",
    connectionId: exampleAwsApigatewayv2VpcLink.id,
    tlsConfig: {
        serverNameToVerify: "example.com",
    },
    requestParameters: {
        "append:header.authforintegration": "$context.authorizer.authorizerResponse",
        "overwrite:path": "staticValueForIntegration",
    },
    responseParameters: [
        {
            statusCode: "403",
            mappings: {
                "append:header.auth": "$context.authorizer.authorizerResponse",
            },
        },
        {
            statusCode: "200",
            mappings: {
                "overwrite:statuscode": "204",
            },
        },
    ],
});
import pulumi
import pulumi_aws as aws

example = aws.apigatewayv2.Integration("example",
    api_id=example_aws_apigatewayv2_api["id"],
    credentials_arn=example_aws_iam_role["arn"],
    description="Example with a load balancer",
    integration_type="HTTP_PROXY",
    integration_uri=example_aws_lb_listener["arn"],
    integration_method="ANY",
    connection_type="VPC_LINK",
    connection_id=example_aws_apigatewayv2_vpc_link["id"],
    tls_config={
        "server_name_to_verify": "example.com",
    },
    request_parameters={
        "append:header.authforintegration": "$context.authorizer.authorizerResponse",
        "overwrite:path": "staticValueForIntegration",
    },
    response_parameters=[
        {
            "status_code": "403",
            "mappings": {
                "append:header.auth": "$context.authorizer.authorizerResponse",
            },
        },
        {
            "status_code": "200",
            "mappings": {
                "overwrite:statuscode": "204",
            },
        },
    ])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/apigatewayv2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := apigatewayv2.NewIntegration(ctx, "example", &apigatewayv2.IntegrationArgs{
			ApiId:             pulumi.Any(exampleAwsApigatewayv2Api.Id),
			CredentialsArn:    pulumi.Any(exampleAwsIamRole.Arn),
			Description:       pulumi.String("Example with a load balancer"),
			IntegrationType:   pulumi.String("HTTP_PROXY"),
			IntegrationUri:    pulumi.Any(exampleAwsLbListener.Arn),
			IntegrationMethod: pulumi.String("ANY"),
			ConnectionType:    pulumi.String("VPC_LINK"),
			ConnectionId:      pulumi.Any(exampleAwsApigatewayv2VpcLink.Id),
			TlsConfig: &apigatewayv2.IntegrationTlsConfigArgs{
				ServerNameToVerify: pulumi.String("example.com"),
			},
			RequestParameters: pulumi.StringMap{
				"append:header.authforintegration": pulumi.String("$context.authorizer.authorizerResponse"),
				"overwrite:path":                   pulumi.String("staticValueForIntegration"),
			},
			ResponseParameters: apigatewayv2.IntegrationResponseParameterArray{
				&apigatewayv2.IntegrationResponseParameterArgs{
					StatusCode: pulumi.String("403"),
					Mappings: pulumi.StringMap{
						"append:header.auth": pulumi.String("$context.authorizer.authorizerResponse"),
					},
				},
				&apigatewayv2.IntegrationResponseParameterArgs{
					StatusCode: pulumi.String("200"),
					Mappings: pulumi.StringMap{
						"overwrite:statuscode": pulumi.String("204"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.ApiGatewayV2.Integration("example", new()
    {
        ApiId = exampleAwsApigatewayv2Api.Id,
        CredentialsArn = exampleAwsIamRole.Arn,
        Description = "Example with a load balancer",
        IntegrationType = "HTTP_PROXY",
        IntegrationUri = exampleAwsLbListener.Arn,
        IntegrationMethod = "ANY",
        ConnectionType = "VPC_LINK",
        ConnectionId = exampleAwsApigatewayv2VpcLink.Id,
        TlsConfig = new Aws.ApiGatewayV2.Inputs.IntegrationTlsConfigArgs
        {
            ServerNameToVerify = "example.com",
        },
        RequestParameters = 
        {
            { "append:header.authforintegration", "$context.authorizer.authorizerResponse" },
            { "overwrite:path", "staticValueForIntegration" },
        },
        ResponseParameters = new[]
        {
            new Aws.ApiGatewayV2.Inputs.IntegrationResponseParameterArgs
            {
                StatusCode = "403",
                Mappings = 
                {
                    { "append:header.auth", "$context.authorizer.authorizerResponse" },
                },
            },
            new Aws.ApiGatewayV2.Inputs.IntegrationResponseParameterArgs
            {
                StatusCode = "200",
                Mappings = 
                {
                    { "overwrite:statuscode", "204" },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.apigatewayv2.Integration;
import com.pulumi.aws.apigatewayv2.IntegrationArgs;
import com.pulumi.aws.apigatewayv2.inputs.IntegrationTlsConfigArgs;
import com.pulumi.aws.apigatewayv2.inputs.IntegrationResponseParameterArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new Integration("example", IntegrationArgs.builder()
            .apiId(exampleAwsApigatewayv2Api.id())
            .credentialsArn(exampleAwsIamRole.arn())
            .description("Example with a load balancer")
            .integrationType("HTTP_PROXY")
            .integrationUri(exampleAwsLbListener.arn())
            .integrationMethod("ANY")
            .connectionType("VPC_LINK")
            .connectionId(exampleAwsApigatewayv2VpcLink.id())
            .tlsConfig(IntegrationTlsConfigArgs.builder()
                .serverNameToVerify("example.com")
                .build())
            .requestParameters(Map.ofEntries(
                Map.entry("append:header.authforintegration", "$context.authorizer.authorizerResponse"),
                Map.entry("overwrite:path", "staticValueForIntegration")
            ))
            .responseParameters(            
                IntegrationResponseParameterArgs.builder()
                    .statusCode("403")
                    .mappings(Map.of("append:header.auth", "$context.authorizer.authorizerResponse"))
                    .build(),
                IntegrationResponseParameterArgs.builder()
                    .statusCode("200")
                    .mappings(Map.of("overwrite:statuscode", "204"))
                    .build())
            .build());

    }
}
resources:
  example:
    type: aws:apigatewayv2:Integration
    properties:
      apiId: ${exampleAwsApigatewayv2Api.id}
      credentialsArn: ${exampleAwsIamRole.arn}
      description: Example with a load balancer
      integrationType: HTTP_PROXY
      integrationUri: ${exampleAwsLbListener.arn}
      integrationMethod: ANY
      connectionType: VPC_LINK
      connectionId: ${exampleAwsApigatewayv2VpcLink.id}
      tlsConfig:
        serverNameToVerify: example.com
      requestParameters:
        append:header.authforintegration: $context.authorizer.authorizerResponse
        overwrite:path: staticValueForIntegration
      responseParameters:
        - statusCode: 403
          mappings:
            append:header.auth: $context.authorizer.authorizerResponse
        - statusCode: 200
          mappings:
            overwrite:statuscode: '204'

Setting connectionType to VPC_LINK and providing a connectionId routes traffic through a VPC Link to private resources. The integrationUri points to a load balancer listener ARN. The tlsConfig block configures TLS verification for the backend connection. The requestParameters and responseParameters properties transform requests and responses as they pass through the integration, using prefixes like append: and overwrite: to control how values are merged.

Beyond these examples

These snippets focus on specific integration features: Lambda proxy integrations, AWS service integrations with subtypes, and VPC Link for private resources. They’re intentionally minimal rather than full API deployments.

The examples reference pre-existing infrastructure such as API Gateway V2 APIs, Lambda functions, IAM roles, and VPC Links or load balancers for private integrations. They focus on configuring the integration rather than provisioning everything around it.

To keep things focused, common integration patterns are omitted, including:

  • Mock integrations for testing (integrationType: MOCK)
  • Request and response transformation templates (requestTemplates)
  • Timeout configuration (timeoutMilliseconds)
  • Payload format versioning (payloadFormatVersion)

These omissions are intentional: the goal is to illustrate how each integration feature is wired, not provide drop-in API modules. See the API Gateway V2 Integration resource reference for all available configuration options.

Let's configure AWS API Gateway V2 Integrations

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Integration Types & Configuration
What integration types are available for API Gateway V2?
Available types depend on your API type. For WebSocket APIs: AWS, AWS_PROXY, HTTP, HTTP_PROXY, MOCK. For HTTP APIs: AWS_PROXY, HTTP_PROXY. Use HTTP_PROXY for HTTP API private integrations.
Do I need to specify an HTTP method for my integration?
Yes, unless your integrationType is MOCK. Set integrationMethod to specify the HTTP method (e.g., POST, GET, ANY).
What's the difference between WebSocket and HTTP API integrations?
Several properties are API-type specific. WebSocket-only: contentHandlingStrategy, passthroughBehavior, requestTemplates. HTTP-only: responseParameters, integrationSubtype. Some integrationType values are also restricted by API type.
Lambda & AWS Service Integrations
How do I integrate with a Lambda function?
Set integrationType to AWS_PROXY, integrationMethod to POST, and integrationUri to the Lambda function’s invokeArn. Optionally configure payloadFormatVersion (1.0 or 2.0).
What's the difference between payload format versions 1.0 and 2.0?
These control the format of the payload sent to Lambda integrations. Version 2.0 provides a simpler format. Default is 1.0.
How do I integrate with AWS services like SQS or DynamoDB?
Use integrationType of AWS_PROXY with integrationSubtype (e.g., SQS-SendMessage). Provide credentialsArn for IAM permissions and configure requestParameters to map request data to service parameters.
Private Integrations & VPC
How do I connect to a private VPC resource like a load balancer?
Set connectionType to VPC_LINK, provide the VPC link ID in connectionId, use integrationType of HTTP_PROXY, and set integrationUri to the ARN of your Application Load Balancer listener, Network Load Balancer listener, or AWS Cloud Map service.
Can I configure TLS for private integrations?
Yes, use the tlsConfig property to configure TLS settings like serverNameToVerify for HTTP API private integrations.
Request & Response Transformation
How do I transform requests before sending them to the backend?
Use requestParameters with transformation prefixes like append:header, overwrite:path, or remove:querystring. For WebSocket APIs, requestParameters passes method request parameters to the backend. For HTTP APIs with integrationSubtype, it passes parameters to AWS service integrations.
How do I transform responses from the backend?
Use responseParameters (HTTP APIs only) to map status codes to response transformations. You can append headers, overwrite status codes, or modify other response elements.
Timeouts & Limits
What are the timeout limits for integrations?
WebSocket APIs: 50 to 29,000 milliseconds (default 29 seconds). HTTP APIs: 50 to 30,000 milliseconds (default 30 seconds).
Why isn't Pulumi detecting changes to my timeout configuration?
Pulumi only performs drift detection on timeoutMilliseconds when it’s explicitly present in your configuration. Always specify the timeout value if you need drift detection.
Immutability & Import Limitations
What properties can't I change after creating an integration?
Three properties are immutable: apiId, integrationType, and integrationSubtype. Changing these requires recreating the integration.
Can I import an API Gateway quick-create integration?
No, integrations created as part of API Gateway’s quick-create feature cannot be imported into Pulumi. Create integrations explicitly if you need to manage them with Pulumi.

Using a different cloud?

Explore integration guides for other cloud providers: