# APAC monitor fan-out dispatcher

Handler: `dispatcher_function.lambda_handler`

## Responsibility

This function:

1. refreshes the complete local mirror of HubSpot target list `19544`;
2. selects APAC active and previous-day new-signup tenants from the DevOps DB;
3. synchronously invokes Effective Owner Sync with that exact deduplicated
   Tenant ID batch;
4. serializes one tenant plus HubSpot/Owner snapshot and run metadata per message;
5. sends messages to the monitor SQS queue in batches of 10.

The HubSpot refresh is atomic. Existing Companies are overwritten, Companies
that left the list are deleted, and a failed refresh preserves the previous
successful snapshot and does not block fan-out. Dispatcher does not enrich or
match individual tenants and does not call Billing, Clinic, or OpenAI. Effective
Owner Sync failure is blocking: Dispatcher sends no monitor messages unless the
whole selected batch has an explicit Owner decision row.

`dispatcher_function.py` contains only selection, fan-out, and handler
orchestration. HubSpot API, normalization, validation, snapshot persistence,
and fallback behavior live in `hubspot_sync.py`.

## Default selection

Active candidates must be active external APAC tenants, must not be in Japan,
and must not use the `free_trial`, `new_user`, or `closing` plan.

Previous-day new signups use `country_region = 'APAC'` and exclude Japan. Their
plans are not filtered because `new_user` and `free_trial` are expected in this
flow.

## Configuration

Required environment variables:

- `DEVOPS_DB_HOST`
- `DEVOPS_DB_USER`
- `DEVOPS_DB_PASSWORD`
- `DEVOPS_DB_NAME`
- `TARGET_DATABASE_URL`
- `MONITOR_QUEUE_URL`
- `EFFECTIVE_OWNER_SYNC_FUNCTION_NAME`

Optional environment variables:

- `DEVOPS_DB_PORT` (default `3306`)
- `HUBSPOT_PRIVATE_APP_TOKEN` (an empty value makes refresh fail non-blockingly)
- `HUBSPOT_TARGET_LIST_ID` (default `19544`)
- `HUBSPOT_REQUEST_TIMEOUT_SECONDS` (default `30`)
- `HUBSPOT_RETRY_COUNT` (default `3`)
- `TARGET_TIDB_SSL_CA_PATH`
- `EFFECTIVE_OWNER_SYNC_INVOKE_TIMEOUT_SECONDS` (default `720`)

The IAM role needs CloudWatch Logs, `sqs:SendMessage` for the monitor queue, and
`lambda:InvokeFunction` for the exact Owner Sync function. The deployed timeout
is 900 seconds with 256 MB memory.

## Manual invocation controls

The event can contain:

- `include_active` (default `true`)
- `include_new_signup` (default `true`)
- `limit` (default unlimited, applied independently to each source)
- `run_id` (default current UTC timestamp)
