# EMEA Tenants Scoring Task

SQS-triggered Lambda for recalculating `emea_active_tenants.total_score` and
`emea_active_tenants.priority`, then writing the result to the matching
HubSpot Contact.

The monitor Lambda sends one SQS message after each active or new signup tenant
is written to `emea_active_tenants`. This Lambda reads the current tenant row
from the target DB, applies `EMEA-Signal-Model-v0.html`, and updates only:

- `total_score`
- `priority`

After the database transaction commits, the Lambda normalizes the tenant email
and performs a partial update of the existing HubSpot Contact identified by
that exact email. It writes only `emea_pql_priority_tier` and never creates a
Contact.

- HubSpot `404` is recorded as `contact_not_found` and does not fail scoring.
- Empty or invalid tenant emails are recorded as `invalid_email` and skipped.
- An empty writeback token records `disabled` and leaves local scoring enabled.
- `429`, `5xx`, and network failures use bounded retries and then fail the SQS
  record, allowing the queue retry/DLQ policy to apply.
- Other `4xx` responses fail immediately.

Logs include tenant ID, priority, and outcome, but never the tenant email,
HubSpot token, or HubSpot response body. Retried writes are idempotent because
they set the same property on the same Contact.

Configuration:

- `HUBSPOT_WRITEBACK_TOKEN` (empty disables Contact writeback; deploy with the
  same private-app token used by the APAC scoring Lambda)
- `HUBSPOT_WRITEBACK_PROPERTY_NAME` (default `emea_pql_priority_tier`)
- `HUBSPOT_WRITEBACK_TIMEOUT_SECONDS` (default `30`)
- `HUBSPOT_WRITEBACK_RETRY_COUNT` (default `3`)

Set the Lambda timeout to at least 180 seconds, matching APAC, so the default
request timeout and retry budget fit inside one invocation.

Messages only need:

```json
{
  "tenant_id": "1372813089209295124",
  "source_type": "active_candidate",
  "event_type": "active_processed",
  "processed_at": "2026-05-25T02:00:00Z"
}
```
