HubSpot
components/hubspot/client.py mirrors a supplier’s Entrust verification lifecycle onto their
HubSpot contact, so Growth & Retention can see in the CRM whether a supplier has started,
passed, been denied, or is under review for ID verification.
Contract
Auth |
HubSpot Private App token — |
Matching property |
|
Written property |
|
class EntrustVerificationStatus:
IN_PROGRESS = 'in_progress'
VERIFIED = 'verified'
DENIED = 'denied'
IN_REVIEW = 'in_review'
| Value | Set when |
|---|---|
|
|
|
The workflow run resolved to |
|
The run is held for manual review. |
|
Any other terminal status — declined, abandoned, error. |
update_entrust_verification_status(supplier_key, status, logger=None)
Finds the contact whose supplierkey equals supplier_key and PATCHes its
entrust_verification_status. Returns True when a contact was found and updated, False
otherwise.
Best effort by design. HubSpot is a downstream, non-authoritative sync target, so:
-
a missing contact logs a warning and returns
False; -
any API error is logged, reported to Sentry via
sentry_sdk.capture_exception(), and returnsFalse; -
the method never raises, so it can never fail session creation or the SWF aftermath task.
Multiple matches are unexpected (the key is meant to be unique) — the first is taken.
An optional correlation-aware logger can be passed so the lines join the rest of the request
or task log; otherwise a module logger is used.
Why a local client instead of pyb-hubspot
The shared pyb-hubspot library requires Python >= 3.12, while this service is pinned to Python
3.9 (its pyb-sdk dependency supports only 3.9). Rather than import a 3.12-only
package, this client builds directly on hubspot-api-client (Python >= 3.7, no conflicting
dependencies) and reproduces just the two calls needed: a CRM contact search by property, and a
partial property PATCH.
Callers
-
interface/web_api/endpoint_handling/create_entrust_session.py—in_progresson create. -
task/entrust/handle_identity_document_validation_response/implementation.py—verified/in_review/deniedon completion, via__sync_external_status.
Both receive the client through construction (swf_app.py injects it into TaskFactory; the
endpoint handler builds its own). Both call sites emit a matching supplier event through
SupplierEventLogger at the same moment, with the same status
value — in the aftermath task the two are driven together by __sync_external_status.
Tests
development/unit_tests/components_/test_hubspot_client.py and
development/unit_tests/interface_/web_api/endpoint_handling/test_create_entrust_session_hubspot.py.
Operational notes
-
The two custom contact properties (
supplierkey,entrust_verification_status) must exist in the HubSpot portal; a typo in either name silently degrades to "no contact found" or a PATCH error, both of which only appear in logs and Sentry. -
Because failures are swallowed, HubSpot drift is not visible in the underwriting data. If the CRM looks stale, check Sentry for
HubSpotClienterrors rather than the task status.