Component layer

Everything under components/ wraps an external system. Tasks (Domain model: processes & tasks) and endpoint handlers (API overview) compose these clients; they never call requests or boto3 directly.

Inventory

Area Modules Page

Au10tix identity documents

au10tix/

Au10tix

Entrust / Onfido identity verification

entrust/

Entrust / Onfido

HubSpot CRM sync

hubspot/

HubSpot

Supplier event stream (internal EventLogger)

event_logger/

Supplier event logger

Thomson Reuters CLEAR

thomson_reuters/

Thomson Reuters CLEAR

AWS: DynamoDB, S3, SWF, EventBridge

aws/, task_execution_table.py, s3_client.py, s3_purgable/, swf_client.py, event_bridge/

AWS clients

Payability internal services

payability_internal_api.py, pyb_documents.py, b2b_task_manager.py

Payability internal services

Auth and permissions

auth/, permissions/, process_source.py

Auth & permissions

Cross-cutting helpers

retry/, logger.py, plus utils/

Utilities

AWS services used

Service Wrapped by Documented in

DynamoDB

aws/dynamodb/*, task_execution_table.py

DynamoDB tables

S3

s3_client.py, s3_purgable/s3_object_purger.py

S3 datalake

SWF

swf_client.py (worker side), b2b_task_manager.py (start side)

SWF orchestration

EventBridge

event_bridge/*

EventBridge events

Secrets Manager

config_setup.py (build time only)

Configuration & secrets

CloudWatch

utils/metric_utils.py via pyb-sdk

Observability

Where clients are instantiated

Worker (swf_app.py) builds one TaskFactory at startup with every client injected, shared across all five polling threads:

task_factory = TaskFactory(
    s3_client=S3Client(),
    thomson_reuters_client=ThomsonReutersClient(),
    au10tix_client=Au10tixClient(),
    b2b_task_manager=B2BTaskManager(),
    b2b_documents=PYBDocuments(),
    task_execution_table=TaskExecutionTable(),
    final_status_table=FinalStatusTable(ChangelogTable()),
    tool_table=ToolTable(ChangelogTable()),
    event_bridge_client=EventBridgeClient(),
    retry_client=Retry(total=20),
    payability_internal_api=PayabilityInternalAPI(),
    entrust_client=EntrustClient(),
    hubspot_client=HubSpotClient(),
    supplier_event_logger=SupplierEventLogger()
)

Web API handlers construct the clients they need per request. execute_task.py builds a full TaskFactory (including entrust_client, hubspot_client and supplier_event_logger, all needed for the Entrust reprocess path); get_task_result.py and delete_task_results.py build partial factories — enough for the process steps they touch.

A partial TaskFactory will raise KeyError if it is asked to build a task whose dependencies were not injected. When you add a client to a task, check every place that constructs a TaskFactory, not just swf_app.py.

The worker’s Retry(total=20) versus the API’s default Retry() (total=10, delay=10) is deliberate: vendor polling happens on the worker, where waiting is free.

Rules for new clients

  • One module (or package) per external system, under components/.

  • Read configuration from config.config.settings only — never os.getenv.

  • Reuse a requests.Session and set explicit timeouts; use components/retry/ or an HTTPAdapter retry for polling.

  • Raise on failure (raise_for_status()), and where the upstream body carries the reason, surface it in the exception message — see the _raise_with_body helper in payability_internal_api.py.

  • Inject the client through TaskFactory rather than constructing it inside a task.

  • Document it here and in Settings & secrets reference if it needs new settings or secrets.