@cfxlabsinc/b2b-services
    Preparing search index...

    Interface LambdaRestApiArgs

    interface LambdaRestApiArgs {
        alarmTopicArn: Input<string>;
        allowedIps?: Input<string>[];
        customAuthorizer?: Input<
            {
                authorizerArn: Input<string>;
                cacheAuthTtlInSeconds?: Input<number> | undefined;
                identitySource: Input<string>;
                type: Input<string>;
            },
        >;
        customDomainApiMappingBasePath?: Input<string>;
        domainNames: ApiDomainName[];
        integrationLatencyAlarmMinRequests?: number;
        integrationLatencyAlarmThresholdMs?: number;
        integrationLatencyCeilingAlarmThresholdMs?: number;
        lambda: {
            invokeArn: Input<string>;
            name: Input<string>;
            qualifier?: Input<string>;
        };
        paths?: {
            apiKeyRequired?: Input<boolean>;
            authorizerRequired?: Input<boolean>;
            corsEnabled?: Input<boolean>;
            corsHeaders?: Input<string>[];
            corsMethods?: Input<string>[];
            corsOrigins?: Input<string>[];
            httpMethods: string[];
            integrationPassThroughBehavior?: Input<string>;
            integrationRequestParameters?: Input<{ [key: string]: Input<string> }>;
            integrationRequestTemplate?: Input<{ [key: string]: Input<string> }>;
            integrationType?: Input<string>;
            name: string;
            parentName?: string;
            path: string;
            requestParameters?: Input<{ [key: string]: Input<boolean> }>;
            responseMappings?: ResponseMapping[];
        }[];
        private?: boolean;
        proxyAll?: { apiKeyRequired: Input<boolean> };
        restApiName?: Input<string>;
        returnsDeliberate5xx?: boolean;
        rootPath?: {
            apiKeyRequired?: Input<boolean>;
            authorizerRequired?: Input<boolean>;
            corsEnabled?: Input<boolean>;
            corsHeaders?: Input<string>[];
            corsMethods?: Input<string>[];
            corsOrigins?: Input<string>[];
            integrationPassThroughBehavior?: Input<string>;
            integrationRequestParameters?: Input<{ [key: string]: Input<string> }>;
            integrationRequestTemplate?: Input<{ [key: string]: Input<string> }>;
            integrationType?: Input<string>;
            requestParameters?: Input<{ [key: string]: Input<boolean> }>;
            responseMappings?: ResponseMapping[];
        };
        vpcId: Input<string>;
        waf?: Input<boolean>;
    }
    Index
    alarmTopicArn: Input<string>

    SNS topic for the gateway-side alarms. Required, not optional, for the same reason as LambdaApp.alarmTopicArn: an API that silently ships without these is the failure mode they exist to prevent.

    allowedIps?: Input<string>[]
    customAuthorizer?: Input<
        {
            authorizerArn: Input<string>;
            cacheAuthTtlInSeconds?: Input<number> | undefined;
            identitySource: Input<string>;
            type: Input<string>;
        },
    >
    customDomainApiMappingBasePath?: Input<string>
    domainNames: ApiDomainName[]

    Custom domains this API mounts on, one base-path mapping each.

    Order carries no meaning — ApiDomainName.key is what identifies a mapping, so a domain can be dropped from this list without disturbing the others. See that field for why the alternative was an outage.

    integrationLatencyAlarmMinRequests?: number

    Minimum Count in a 5-minute period before the p99 latency alarm is allowed to fire. Defaults to 20.

    A percentile over a handful of samples is not a percentile — it is the slowest request in the bucket wearing a percentile's name. cfx-backoffice-api serves ~0.3 req/min, so on 2026-08-25 its alarm period held TWO requests and one 17s webhook that returned 200 tripped a p99 alarm. The same template is attached to 18 APIs whose traffic differs by three orders of magnitude, so the gate lives here rather than in per-app config.

    Latency coverage for the quiet APIs comes from integrationLatencyCeilingAlarmThresholdMs instead, which is valid at any sample count.

    integrationLatencyAlarmThresholdMs?: number

    p99 IntegrationLatency in ms that trips the early-warning alarm.

    Defaults to half of API Gateway's integration timeout. That ceiling is 29s and is NOT configurable for REST APIs, so any handler that can exceed it returns a 504 to the caller while the lambda keeps running to completion — the caller retries against work that already succeeded. Alarming at half the ceiling gives a margin to act in before requests start being cut.

    Raise it for an API with legitimately slow endpoints; a fleet-wide alarm that cries wolf gets muted, and a muted alarm is worse than none.

    integrationLatencyCeilingAlarmThresholdMs?: number

    Maximum IntegrationLatency in ms that trips the ceiling alarm. Defaults to 25s, i.e. 4s of headroom under API Gateway's hard 29s integration cap.

    Deliberately a Maximum and not a percentile: one request approaching the cap is the thing worth knowing about, and it is exactly the signal a volume-gated p99 throws away on a low-traffic API. This is what catches the 504-while-the-lambda-keeps-running failure before it happens.

    lambda: {
        invokeArn: Input<string>;
        name: Input<string>;
        qualifier?: Input<string>;
    }
    paths?: {
        apiKeyRequired?: Input<boolean>;
        authorizerRequired?: Input<boolean>;
        corsEnabled?: Input<boolean>;
        corsHeaders?: Input<string>[];
        corsMethods?: Input<string>[];
        corsOrigins?: Input<string>[];
        httpMethods: string[];
        integrationPassThroughBehavior?: Input<string>;
        integrationRequestParameters?: Input<{ [key: string]: Input<string> }>;
        integrationRequestTemplate?: Input<{ [key: string]: Input<string> }>;
        integrationType?: Input<string>;
        name: string;
        parentName?: string;
        path: string;
        requestParameters?: Input<{ [key: string]: Input<boolean> }>;
        responseMappings?: ResponseMapping[];
    }[]

    Explicit path configuration. Optional only when proxyAll is set — one of the two must be present or the API has no routes at all.

    private?: boolean

    When true, creates a PRIVATE API Gateway only accessible from within the VPC. A VPC endpoint for execute-api is created automatically. Cannot be used with domainNames or allowedIps.

    false
    
    proxyAll?: { apiKeyRequired: Input<boolean> }

    Shorthand for the catch-all shape every proxied service API uses: one {proxy+} resource taking ANY method, CORS open to *, and a matching root-path config.

    That eighteen-line literal was byte-identical in eighteen programs — the single most-copied block in the repo — and every copy was a chance to mismatch apiKeyRequired between the root and the proxy, which fails as a 403 on the root path only. Here the two cannot disagree: one flag sets both.

    Mutually exclusive with rootPath / paths; pass those instead when an API needs real per-path configuration.

    new LambdaRestApi(`cfx-${APP_NAME}`, { proxyAll: { apiKeyRequired: true }, … });
    

    Type Declaration

    • apiKeyRequired: Input<boolean>

      Applied to BOTH the root path and the {proxy+} resource.

    restApiName?: Input<string>

    Physical name of the REST API, when it must differ from the component name.

    The component name is the Pulumi resource name, so it cannot be changed without replacing a live API. This overrides only the name API Gateway shows, which UpdateRestApi patches in place.

    Reach for it when two stacks would otherwise create identically named APIs: CloudWatch keys AWS/ApiGateway metrics on the ApiName dimension, so duplicates silently SUM into one series. During the 2026-08-11 Green Dot outage that made Count and 4XXError unusable for telling which of the two cfx-greendot-processor-api APIs traffic was reaching.

    returnsDeliberate5xx?: boolean

    Declare that this API returns 5xx on purpose for some responses.

    AWS/ApiGateway's 5XXError has no dimension for cause, so a handler that returns 500 on purpose is indistinguishable from one that broke. That is not hypothetical: backoffice-api answers a Victor webhook with 500 when the withdrawal workflow has not reached PENDING yet, because a 5xx is what makes Victor re-deliver — and it accounted for 7 of the 14 days to 2026-08-26 on which -apigw-5xx-prod paged somebody for nothing.

    Set this and the alarm stops reading 5XXError altogether, switching to the access log's gateway-generated failures: 5xx carrying an errorResponseType, which is INTEGRATION_TIMEOUT / INTEGRATION_FAILURE and friends. A 5xx the handler chose to return passes through the gateway with errorResponseType: "-", so it is excluded structurally rather than subtracted — one metric, one source, and full single-datapoint sensitivity.

    The trade is that a 5xx the handler did not choose — an unhandled throw surfaced as 500 — no longer trips this alarm on such an API. That is already Sentry's half of the division of labour described on gatewayAlarms, and it is unavoidable here: on an API that returns 5xx deliberately, a lambda-passthrough 5xx is inherently ambiguous. Over the 7 days to 2026-08-27 every one of backoffice-api's six lambda-passthrough 500s was a deliberate Victor retry.

    rootPath?: {
        apiKeyRequired?: Input<boolean>;
        authorizerRequired?: Input<boolean>;
        corsEnabled?: Input<boolean>;
        corsHeaders?: Input<string>[];
        corsMethods?: Input<string>[];
        corsOrigins?: Input<string>[];
        integrationPassThroughBehavior?: Input<string>;
        integrationRequestParameters?: Input<{ [key: string]: Input<string> }>;
        integrationRequestTemplate?: Input<{ [key: string]: Input<string> }>;
        integrationType?: Input<string>;
        requestParameters?: Input<{ [key: string]: Input<boolean> }>;
        responseMappings?: ResponseMapping[];
    }
    vpcId: Input<string>

    VPC the execute-api interface endpoint is looked up in, as a SCALAR.

    This component used to take a commonInfraStackRef and call requireOutput("vpcId") on it internally — the same hidden coupling that LambdaApp and FargateApp had, where a stack that stopped exporting an output broke every consumer at once in a file none of them mention.

    waf?: Input<boolean>

    Whether the WAF will be enabled for the deployed stage,

    true