SAM template

lambdas/template.yaml declares the entire serverless half of the platform in one file — functions, layers, queues, schedules, the shared secret, and one alarm. Deployment configuration lives beside it in lambdas/samconfig.toml.

Setting Value

Transform

AWS::Serverless-2016-10-31

Stack name

morpheus-sam (samconfig.toml)

Capabilities

CAPABILITY_IAM

Build

cached = true, parallel = true

Deploy

confirm_changeset = false, resolve_s3 = true

Physical resource names are built from ${AWS::StackName}, so every function is morpheus-sam-<name> and every queue morpheus-sam-<name>. Renaming the stack renames everything, including the queues — which would orphan any message in flight.

Parameters

Parameter Default Allowed

LogLevel

INFO

DEBUG, INFO, WARNING, ERROR, CRITICAL

One parameter, and it is the operational lever worth knowing: deploying with --parameter-overrides LogLevel=DEBUG turns on per-record logging across all five functions without a code change. Turn it back down afterwards — the debug paths log per-contact detail.

Globals

Globals:
  Function:
    Runtime: python3.12
    Architectures: [x86_64]
    Environment:
      Variables:
        LOG_LEVEL: !Ref LogLevel
        SENTRY_DSN: ""
        SENTRY_ENVIRONMENT: "prod"

Timeout and memory are not global — each function sets its own, because they differ by an order of magnitude between a 120-second Bedrock call and a 300-second BigQuery sync.

SENTRY_DSN is set to the empty string, which disables Sentry entirely. configure_sentry() still runs at cold start in every handler, sees no DSN, and returns without initialising. A real DSN is present in the template as a comment. Until it is set, unhandled exceptions are visible only in CloudWatch. See Observability.

Tracing: Active is absent, so X-Ray is off for all five functions.

Functions

Function Timeout Memory Reserved Layers, in order

morpheus-sam-enrichment-scraper

300s

256 MB

—

common, data-layer, bigquery

morpheus-sam-enrichment-scorer

120s

512 MB

5

common, data-layer, llm, pyb-hubspot

morpheus-sam-hubspot-sync

300s

256 MB

—

common, data-layer, pyb-hubspot, bigquery

morpheus-sam-email-agent

120s

512 MB

5

common, data-layer, llm

morpheus-sam-phone-agent

120s

256 MB

2

common, data-layer, bigquery

Every handler is handler.lambda_handler. MorpheusCommonLayer is always first, because Sentry must be configurable before anything else imports.

ReservedConcurrentExecutions is a database protection, not a cost control. Each concurrent invocation holds its own PostgreSQL connection — the data layer runs with NullPool — so the ceiling on concurrency is the ceiling on connections. Phone-agent’s limit of 2 is tighter still because each invocation also spends Twilio Lookup quota.

The three functions with the higher memory allocations are the ones that call Bedrock; the extra memory buys proportionally more CPU for JSON handling around a call that is mostly waiting on the network.

enrichment-scraper

Environment variable Value

SQS_QUEUE_URL

EnrichmentPipelineQueue

AGENTCORE_SECRET_ARN

AgentcoreSecret

SMARTSCOUT_BASE_URL

https://smartscoutapi-east.azurewebsites.net

SUBSCRIPTION_ID

b191a905-5d19-4fa0-941b-d8eb81c0c477

Policies: SQSSendMessagePolicy on the enrichment queue, plus secretsmanager:GetSecretValue and PutSecretValue on the shared secret. It is the only function that can write the secret — it rotates the SmartScout session token it obtains at login.

Active trigger: BigQueryContactSync at rate(2 hours). The Amazon and Walmart daily discovery schedules are commented out.

enrichment-scorer

Environment variable Value

AGENTCORE_SECRET_ARN

AgentcoreSecret

SUBSCRIPTION_ID

b191a905-5d19-4fa0-941b-d8eb81c0c477

CRM_SYNC_QUEUE_URL

CrmSyncQueue

Policies: SQSPollerPolicy on the enrichment queue, SQSSendMessagePolicy on the CRM queue, secretsmanager:GetSecretValue, and bedrock:InvokeModel / InvokeModelWithResponseStream on *.

enrichment-scorer currently has no active triggers. Its SQS event source, the Seller.EnrichPending backfill at rate(60 hours), and the Seller.ScorePending backfill at rate(4 minutes) are all commented out.

The function is deployed and its permissions are live, but nothing invokes it: messages accumulate in EnrichmentPipelineQueue until they age out at four days. Anything that appears to be a scorer bug should start by confirming whether the trigger is enabled.

The commented SQS event carries ScalingConfig.MaximumConcurrency: 50 with a note explaining it must be raised only once RDS Proxy sits in front of the database.

morpheus-hubspot

Environment variable Value

AGENTCORE_SECRET_ARN

AgentcoreSecret

SUBSCRIPTION_ID

b191a905-5d19-4fa0-941b-d8eb81c0c477

CONTACT_HUBSPOT_RESOLVE_BATCH_SIZE

25

Policies: SQSPollerPolicy on CrmSyncQueue and secretsmanager:GetSecretValue. It writes to HubSpot over HTTPS and to PostgreSQL, neither of which needs an IAM policy.

The busiest function in the template: one SQS trigger plus ten schedules. See EventBridge schedules.

email-agent

Environment: AGENTCORE_SECRET_ARN only — it derives its subscription from the row it loads rather than from configuration.

Policies: SQSPollerPolicy on EmailAgentQueue, secretsmanager:GetSecretValue, and Bedrock invoke on *.

Trigger: EmailAgentQueue with BatchSize: 1, ReportBatchItemFailures, and MaximumConcurrency: 5.

phone-agent

Environment variable Value

AGENTCORE_SECRET_ARN

AgentcoreSecret

SUBSCRIPTION_ID

b191a905-5d19-4fa0-941b-d8eb81c0c477

CRM_SYNC_QUEUE_URL

CrmSyncQueue

EMAIL_AGENT_QUEUE_URL

EmailAgentQueue

PHONE_AGENT_BATCH_SIZE

25

SALES_SLACK_CHANNEL_ID

sales-tasks

Policies: SQSSendMessagePolicy on both the CRM and email queues, plus secretsmanager:GetSecretValue. It is a producer to two queues and a consumer of none — all three of its triggers are schedules.

Other resources

Logical id Type Purpose

AgentcoreSecret

AWS::SecretsManager::Secret

The shared secret agentcore-configs, created empty. See Secrets Manager.

BedrockTokenAlarm

AWS::CloudWatch::Alarm

Bedrock InputTokenCount above 5,000,000 in an hour

ApplicationResourceGroup

AWS::ResourceGroups::Group

Groups the stack for Application Insights

ApplicationInsightsMonitoring

AWS::ApplicationInsights::Application

AutoConfigurationEnabled: true

There is no API Gateway, no function URL, and no HTTP endpoint anywhere in the template. Nothing in the Lambda half of the platform accepts an inbound request — every invocation comes from a queue or a schedule. HubSpot webhooks reach the system by writing to CrmSyncQueue through infrastructure outside this stack. See HubSpot webhooks.

Outputs

Queue URLs and ARNs for EnrichmentPipelineQueue, CrmSyncQueue, and both FIFO dead-letter queues; function ARNs and generated role ARNs for scraper, scorer, hubspot, and phone-agent; and AgentcoreSecretArn.

The five EmailAgent* outputs are commented out even though the function is deployed — an oversight rather than a decision.

Commented-out configuration

The template carries a substantial amount of disabled configuration: the scorer’s triggers, the two discovery schedules, the email-agent outputs, and production cron(…​) expressions sitting next to the development rate(…​) expressions that are actually deployed.

Read the template as the record of what is deployed, and treat a commented block as history rather than intent. The schedule pages call out each place where a commented production expression differs from the live one — see EventBridge schedules.