Lambda Functions

This page details the five Lambda functions that make up the Underwriting Service, including their configurations, environment variables, IAM permissions, and operational characteristics.

Overview

The Underwriting Service consists of five Lambda functions that work together in an event-driven architecture:

Lambda Trigger Purpose Concurrency

API

API Gateway

REST API for eligibility checks and profile management

High (synchronous)

Rule Runner

SQS

Executes individual rule evaluations

Medium (async batch)

Result Runner

SQS

Aggregates rule outcomes into final decisions

Medium (async batch)

Float Created Handler

SQS (EventBridge)

Creates permanent evaluation records

Low (event-driven)

Profile Handler

SQS (EventBridge)

Creates default profiles for new users

Low (event-driven)

Request Flow

The following diagram shows how requests flow through the Lambda functions:

API Request Flow

Three Processing Paths:

  1. Synchronous (Cache Hit): API Lambda returns cached EvaluationResult immediately (HTTP 200)

  2. Asynchronous (Cache Miss): Returns HTTP 200, triggers Rule Runner and Result Runner via SQS, client polls for results

  3. Instant (Synchronous): POST /instant invokes Rule Runner and Result Runner Lambdas directly (RequestResponse), returns a fresh result without polling

Common Configuration

All Lambdas Share:

  • Memory Size: 512 MB (configurable via local.lambda_memory_size)

  • Runtime: Go 1.20+

  • VPC: Deployed in private subnets (API and Rule Runner only)

  • Monitoring: Datadog integration enabled

  • Logging: CloudWatch Logs with structured logging (golflog)


API Lambda

Entry Point: [cmd/api/main.go](cmd/api/main.go)

Purpose

Serves as the REST API gateway for the Underwriting Service. Handles all synchronous requests for float/loan eligibility checks, profile management, and evaluation history retrieval.

Key Responsibilities

  1. Authentication & Authorization:

    • Validates AWS SigV4 signatures

    • Extracts user context from authorizer

  2. Float/Loan Eligibility Checks:

    • Routes: GET /{user_id}/float_check, /{user_id}/underwriting/eligibility, /{user_id}/underwriting/loan/check

    • Retrieves cached evaluations from DynamoDB

    • Applies bypass logic for data quality issues

    • Returns approval status with amounts and reasons

  3. Instant Synchronous Evaluation:

    • Route: POST /{user_id}/underwriting/instant

    • Invokes Rule Runner and Result Runner Lambdas synchronously (RequestResponse mode)

    • Returns a fresh evaluation result in a single request without async polling

    • Intended for signup and reactivation flows where data is known to be available

  4. Float Profile Management:

    • Routes: GET/POST /{user_id}/underwriting/profile, GET/POST /{user_id}/floats/temporary_profile

    • CRUD operations for float profiles

    • Temporary profile creation with TTL

  5. Evaluation History:

    • Routes: GET /{user_id}/underwriting/history, GET /{user_id}/underwriting/history/{evaluation_id}

    • Retrieves recent and historical evaluations

  6. Recalculation Requests:

    • Route: POST /{user_id}/recalculate

    • Publishes events to trigger async rule execution

  7. Rulebook Management:

    • Routes: GET /rulebooks, GET /rulebooks/changes

    • Retrieves active rulebooks and audit trails

Error Handling

  • Upstream Service Failures: Returns 500 with error details, implements circuit breaker patterns

  • DynamoDB Errors: Retries with exponential backoff (via AWS SDK)

  • Validation Errors: Returns 400 with detailed error messages

  • Not Found: Returns 404 for missing users/resources

Environment Variables (lambdadispatch)

The following variables are required to initialize the lambdadispatch client, which the API Lambda uses to invoke the Rule Runner and Result Runner synchronously on the POST /instant path. Both are set by deploy/lambda.tf.

Variable Required Purpose

AWS_ACCOUNT_ID

Yes

AWS account ID used to construct Lambda ARNs for cross-function invocation

AWS_ENVIRONMENT

Yes

Deployment environment (e.g. dev, staging, prod) used to resolve Lambda function names by environment prefix

Environment Variables (ML clients)

The following variables are required to initialize the Payback Predictor V2 model clients (initMLClients in cmd/api/main.go). All are set by deploy/lambda.tf.

Variable Required Purpose

PAYBACK_PREDICTOR_ENDPOINT_NAME

Yes

SageMaker endpoint name for the payback predictor v2 model. Consumed by sagemaker.New(…​). The Lambda execution role must have sagemaker:InvokeEndpoint on this endpoint ARN.

SM_UXCAM_NAME

Yes

AWS Secrets Manager secret name containing the UXCam Data Access API credentials (AppID, APIKey). Consumed by getSecretCfg via fmsdkConfig.RequireString. The Lambda execution role must have secretsmanager:GetSecretValue on this secret ARN.

INSIGHT_SERVICE_URL

Yes

Base URL of the Insight Service used to fetch next-payday data for the contract_lag feature. Consumed by insight.NewClientFromEnvVars.

INSIGHT_SERVICE_REGION

Yes

AWS region of the Insight Service, used for SigV4 request signing. Consumed by insight.NewClientFromEnvVars. The Lambda execution role must have execute-api:Invoke on the Insight Service API ARN.


Rule Runner Lambda

Entry Point: [cmd/rule-runner/main.go](cmd/rule-runner/main.go)

Purpose

Worker Lambda that executes individual rule evaluations. Fetches data from multiple services, runs rules against user data, and stores outcomes in DynamoDB.

Key Responsibilities

  1. Data Gathering:

    • Fetches user data from User Service

    • Retrieves transaction history from Transactions Service

    • Gets active floats from Float Service

    • Retrieves ML insights from Insight Service

    • Fetches subscription status from Subscriptions Service

    • Gets payment information from Payments Service

  2. Rule Execution:

    • Loads rulebooks from DynamoDB (with caching)

    • Filters rules by type (txns, insights, all)

    • Invokes individual Lambda rule functions

    • Collects rule results with features

  3. Outcome Storage:

    • Writes RuleOutcome to DynamoDB with 32-day TTL

    • Stores features map for debugging

    • Records calc_status (OK, NODATA, CALCERR)

  4. Result Runner Trigger:

    • Publishes event to Result Runner queue after all rules complete

  5. Early-Advance Candidate + Notification payload:

    • Identifies the in-processing float and its collection payment that qualify for an early advance (most recent float ACHSENT, no other outstanding, latest collection a T-1 day-before-ACH run — TOMORROW_FIRST_FLOAT, or TOMORROW_RETURNING_FLOAT behind the returning-float GrowthBook flag), via float-service collection history, and runs float_evaluation.EvaluateEarlyAdvance over the run’s transactions. Only when the user clears does it feed RuleGoodStanding (and, via the result runner, the stored early_advance_float_id/early_advance_payment_id/early_advance_day).

    • When the user clears, it builds the early_advance_eligible notification payload (email, float, day, rollover end_date, payment) and carries it on the rule-run-completed event. It does NOT send the notification itself — the result runner does, once it confirms the user passed underwriting.

Error Handling & Retry Strategy

  • Rule Execution Failures: Individual rules can fail without failing entire evaluation

  • Service Timeouts: Returns CALCERR status, bypasses with cached data

  • SQS Retry: Message returns to queue if Lambda fails (max 1 retry)

  • DLQ Processing: Failed messages moved to DLQ for manual investigation

  • Sagemaker Errors: Gracefully degrades, marks insights as unavailable


Result Runner Lambda

Entry Point: [cmd/result-runner/main.go](cmd/result-runner/main.go)

Purpose

Aggregates individual rule outcomes into final eligibility decisions. Applies rulebook priority, superseding logic, and A/B test filtering.

Key Responsibilities

  1. Outcome Retrieval:

    • Fetches all RuleOutcomes for user from DynamoDB

    • Loads rulebooks with caching

  2. Evaluation Algorithm (ProcessFloatCheck):

    • Filters rulebooks by type (float vs loan)

    • Applies A/B test filtering (apply_to percentage)

    • Sorts by priority (descending)

    • Evaluates sequentially until approval or superseding rulebook

  3. Result Storage:

    • Creates EvaluationResult with 32-day TTL

    • Stores rulebook_results array with individual outcomes

    • Records deciding_rulebook for approved users

    • Sets evaluation_status based on data quality

    • Persists early_advance_float_id/early_advance_payment_id/early_advance_day (lifted from RuleGoodStanding’s outcome) for the live early-advance check

  4. Early-Advance Notification:

    • When the float evaluation is approved (float_results.approved) AND the rule runner carried an early-advance nudge on the event, sends the Iterable early_advance_eligible event (and payments.ach.early_advance_float metric). Gating on approval here — rather than in the rule runner, which does not know the final decision — ensures a user who cleared early advance but was denied by another rule is never notified.

    • Requires the SM_ITERABLE_NAME secret (Iterable API key); failures are logged and never fail the result run

  5. CFI Evaluation:

    • Calculates float_rank and sub_rank

    • Determines eligible float tiers

    • Checks for limit increase eligibility

    • Updates FloatProfile if increase detected

  6. Event Publishing:

    • Publishes evaluation complete events

    • Triggers Segment tracking for CFI changes

Error Handling

  • Missing Outcomes: Returns NOEVAL status

  • Data Quality Issues: Sets appropriate status (NODATA, CALCERR)

  • Rulebook Errors: Logs and continues with available rulebooks

  • DynamoDB Errors: Retries with backoff, moves to DLQ on failure


Float Created Handler Lambda

Entry Point: [cmd/float-created-handler/main.go](cmd/float-created-handler/main.go)

Purpose

Event handler that creates permanent historical evaluation records when a float or loan is taken. Helps with compliance and preserves CFI calculations data.

Key Responsibilities

  1. Event Processing:

    • Receives float.created events from EventBridge via SQS

    • Extracts float_id, user_id, amount from event payload

  2. Historical Record Creation:

    • Retrieves corresponding EvaluationResult from DynamoDB

    • Creates HistoricalEvaluation entity (permanent, no TTL)

    • Stores complete evaluation snapshot:

      • Float/loan evaluation results

      • Rulebook outcomes

      • CFI state at time of float

      • Amount and product taken

  3. Compliance:

    • Preserves exact evaluation used for credit decision

    • Enables dispute resolution and auditing

  4. CFI Support:

    • Historical floats used to calculate float_rank

    • Enables "highest float ever taken" tracking

    • Supports reactivating user limit determination

  5. Rules Re-run:

    • After the historical eval is preserved, emits an underwriting_recalculate_user event (source underwriting.api) to the FloatMe event bus

    • This is the same event the API’s POST /{user_id}/recalculate endpoint emits, so it is matched by rule_runner_event_rule and triggers a fresh Rule Runner evaluation for the user

    • Fires for every created float — including when no prior eval existed to preserve — so the user’s evaluation always reflects the newly created float

Environment Variables

Variable Description

DYNAMO_REGION / DYNAMO_TABLE

Underwriting single-table location

TXN_SERVICE_ENDPOINT / TXN_SERVICE_REGION

Transactions service (primary account lookup)

EVENTBRIDGE_AWS_REGION

Region of the FloatMe event bus used to emit the recalculate event

IAM Permissions

  • dynamodb:BatchGetItem/GetItem/PutItem/Query on the underwriting table

  • sqs:ReceiveMessage/DeleteMessage/GetQueueAttributes on the float-created SQS tap

  • events:DescribeEventBus/PutEvents on the FloatMe event bus (recalculate event)

  • execute-api:Invoke on the transactions service

Error Handling

  • Missing Evaluation: Logs "no loan evaluation found", skips the historical record, and still emits the recalculate event

  • DynamoDB Errors: Retries with backoff

  • Malformed Events: Logs and moves to DLQ

  • Recalculate Emit Failure: Returns an error so the SQS message is not silently dropped

  • Service Timeouts: Retries with exponential backoff


Profile Handler Lambda

Entry Point: [cmd/profile-handler/main.go](cmd/profile-handler/main.go)

Purpose

Creates default float profiles for newly signed-up users. Ensures all users have a starting configuration for float eligibility.

Key Responsibilities

  1. Event Processing:

    • Receives user.signup.completed events

    • Extracts user_id from event payload

  2. Profile Creation:

    • Checks if profile already exists (idempotency)

    • Creates default FloatProfile:

      • Floats $10 and $20 enabled

      • Floats $30-$100 disabled

      • Standard loan tiers

      • cfi_enabled: true

    • Writes to DynamoDB with timestamp

  3. Idempotency:

    • Skips creation if profile already exists

    • Prevents duplicate profiles from event replays

Error Handling

  • Duplicate Profile: Logs and continues (idempotent)

  • DynamoDB Errors: Retries with backoff

  • Malformed Events: Logs and moves to DLQ


API Lambda IAM Permissions

The API Lambda has the following IAM policy statements:

Actions Resources

dynamodb:BatchGetItem, dynamodb:BatchWriteItem, dynamodb:GetItem, dynamodb:PutItem, dynamodb:Query

underwriting DynamoDB table and its indexes

events:DescribeEventBus, events:PutEvents

FloatMe EventBridge bus

execute-api:Invoke

Payments, Transactions, Float, LOC, User, Subscriptions service API Gateway endpoints

secretsmanager:GetSecretValue

GrowthBook secret

lambda:InvokeFunction

Rule Runner and Result Runner Lambda functions (for POST /instant synchronous path)


Monitoring & Observability

CloudWatch Metrics

All Lambdas emit standard metrics:

  • Invocations: Total invocation count

  • Duration: Execution time (ms)

  • Errors: Error count

  • Throttles: Throttling events

  • ConcurrentExecutions: Active executions

Datadog Metrics

Custom metrics tracked:

  • underwriting.evaluation.duration - Evaluation processing time

  • underwriting.rule.execution.duration - Individual rule execution time

  • underwriting.cfi.limit.increased - CFI limit increase events

  • underwriting.cfi.limit.decreased - CFI limit decrease events

  • underwriting.sagemaker.latency - ML inference latency

  • underwriting.service.call.duration - Downstream service latency

CloudWatch Logs

Log Structure: * Level: DEBUG, INFO, WARN, ERROR * Request ID: AWS request ID for tracing * User ID: User being evaluated * Correlation ID: Event correlation across Lambdas

Key Log Events: * Rule execution start/completion * Service call requests/responses * DynamoDB operations * Error stack traces * CFI calculations

X-Ray Tracing

AWS X-Ray enabled on all Lambdas:

  • Request flow visualization

  • Service dependency map

  • Latency breakdown by segment

  • Error analysis


See Also