EventBridge events

Outbound notifications are published with EventBridgeClient.put_event() to the bus EVENT_BUS_NAME (prod_workers in release, the environment name in dev). A non-zero FailedEntryCount raises ConnectionError, which fails the task.

def put_event(self, event: EventBridgeEvent):
    event_body = event.get_body()
    result = self.client.put_events(Entries=[event_body])

    if isinstance(result, dict) and result.get('FailedEntryCount', 1):
        raise ConnectionError('Cannot put event to Event Bridge.')

Event catalog

Event class Source DetailType Emitted by When

ProcessFinishedEvent

pyb.underwriting.events

the uppercased process name

Au10tix ID-doc, Entrust ID-verification, personal-info and personal-background handle tasks

the process completed with a terminal result

ThomsonReutersDataMatchEvent

pyb.internal.admin.events

THOMSON_REUTERS_DATA_MATCH

personal-info (KYC) handle task

every PII field matched

ThomsonReutersDataNotMatchEvent

pyb.internal.admin.events

THOMSON_REUTERS_DATA_NOT_MATCH

personal-info (KYC) handle task

one or more PII fields did not match (carries data_points)

ThomsonReutersNotWorkingEvent

pyb.internal.admin.events

THOMSON_REUTERS_NOT_WORKING

personal-info (KYC) handle task

the CLEAR call failed — the platform may let the customer proceed without the check

DetailType values for ProcessFinishedEvent are therefore VALIDATE_IDENTITY_DOCUMENT, VALIDATE_PERSONAL_INFORMATION and DOWNLOAD_PERSONAL_BACKGROUND_REPORT.

download_business_background_report emits no events — it only archives raw results. The Entrust flow emits nothing while a run is under review; the event follows the later terminal completion.

Payload shapes

Every event is a put_events entry whose Detail is a JSON string and always includes project_name (automatic-uw-tool). Consumers route on Source + DetailType.

ProcessFinishedEvent

{
    'Source': 'pyb.underwriting.events',
    'EventBusName': settings.EVENT_BUS_NAME,
    'DetailType': process_name.upper(),
    'Detail': json.dumps({
        'project_name': settings.PROJECT_NAME,
        'message': f'The process {process_name} is finished',
        'supplier_key': task_input['supplier_key'],
        'person_id': task_input['person_id'],
        'result_s3_path': result_s3_path,
        'status': TaskExecutionTable.PROCESSED_STATUS
    })
}
{
  "Source": "pyb.underwriting.events",
  "EventBusName": "prod_workers",
  "DetailType": "VALIDATE_IDENTITY_DOCUMENT",
  "Detail": "{\"project_name\": \"automatic-uw-tool\", \"message\": \"The process validate_identity_document is finished\", \"supplier_key\": \"9b1c4d3e-6f2a-4b8c-9d0e-1f2a3b4c5d6e\", \"person_id\": \"44f2a1b0-7c3d-4e5f-8a9b-0c1d2e3f4a5b\", \"result_s3_path\": \"entrust/identity_document_validation/supplier_key=9b1c.../person_id=44f2.../2026-08-12T09:15:11.204812.json\", \"status\": \"processed\"}"
}

status is always the literal processed — the event is only emitted on success. The result_s3_path points into the datalake (S3 datalake).

task_input must carry supplier_key and person_id, so a handle task has to pass the reloaded supplier data (not just {correlation_id}) when constructing the event. The Thomson Reuters tasks pass the row’s input; the Entrust task passes input_.to_dict().

ThomsonReutersDataNotMatchEvent

{
    'Source': 'pyb.internal.admin.events',
    'EventBusName': settings.EVENT_BUS_NAME,
    'DetailType': 'THOMSON_REUTERS_DATA_NOT_MATCH',
    'Detail': json.dumps({
        'project_name': settings.PROJECT_NAME,
        'message': 'Customer data is not a match with Thomson Reuters. '
                   'Incorrect data points are {}'.format(data_points),
        'task_input': task_input
    })
}

data_points is the list of mismatched field names (both PARTIAL MATCH and NO MATCH count as mismatched), rendered into message. The full task_input — including PII — travels inside Detail.

ThomsonReutersDataMatchEvent and ThomsonReutersNotWorkingEvent

Same shape (Source: pyb.internal.admin.events, project_name, message, task_input), differing only in DetailType and message text:

  • match — "All customer data is match".

  • not working — "Due to error with Thomson Reuters, could not validate data. Allowing customer to proceed without data check."

Not on this bus: supplier events

The Entrust flow also emits supplier events to Payability’s internal EventLogger service — a separate, supplier-timeline channel, not EventBridge. It is best effort and carries the verification lifecycle status rather than a process completion. See Supplier event logger.

Consuming these events

Consumers live outside this repository. Subscribe an EventBridge rule on the bus for the relevant Source and DetailType. The pyb.underwriting.events events are the primary "a result is ready" signal; the pyb.internal.admin.events events drive internal KYC review and notification flows.

Adding an event

  1. Add a class under components/event_bridge/events/ extending EventBridgeEvent with a stable Source and DetailType.

  2. Emit it from the relevant handle task through the injected event_bridge_client.

  3. Keep it idempotent-friendly: a replayed activity will re-emit it, so consumers must tolerate duplicates.

  4. Document it in the catalog above.