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 |
|
Stack name |
|
Capabilities |
|
Build |
|
Deploy |
|
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 |
|---|---|---|
|
|
|
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.
|
|
Tracing: Active is absent, so X-Ray is off for all five functions.
Functions
| Function | Timeout | Memory | Reserved | Layers, in order |
|---|---|---|---|---|
|
300s |
256 MB |
— |
common, data-layer, bigquery |
|
120s |
512 MB |
5 |
common, data-layer, llm, pyb-hubspot |
|
300s |
256 MB |
— |
common, data-layer, pyb-hubspot, bigquery |
|
120s |
512 MB |
5 |
common, data-layer, llm |
|
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 |
|---|---|
|
|
|
|
|
|
|
|
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 |
|---|---|
|
|
|
|
|
|
Policies: SQSPollerPolicy on the enrichment queue, SQSSendMessagePolicy on the CRM queue,
secretsmanager:GetSecretValue, and bedrock:InvokeModel / InvokeModelWithResponseStream
on *.
|
The function is deployed and its permissions are live, but nothing invokes it: messages
accumulate in |
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 |
|---|---|
|
|
|
|
|
|
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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|---|---|---|
|
|
The shared secret |
|
|
Bedrock |
|
|
Groups the stack for Application Insights |
|
|
|
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. |