POST /cert/deliver delegates to deliver_cert_internal

What changed (2026-08-05, entirely-api cc591c4, completing 3868129): The API-key route POST /cert/deliver no longer carries its own copy of the cert send pipeline. It predated email tracking, so API-initiated sends went out with no pixel and no tokened link while portal sends were fully tracked. It is now a thin wrapper over deliver_cert_internal — the single send path — so every cert send gets identical per-recipient tracking (PER_RECIPIENT_CERT_TRACKING), preflight gating, idempotency, and post-send hardening.

Preserved wrapper semantics (pinned by TestDeliverRouteDelegation in tests/test_cert_pipeline.py): dry_run preview (same response shape, no preflight/idempotency/tokens/send), to_override/cc_override, X-Idempotency-Key replay, force bypass, 400 on no-route, project/account/pdf response keys, initiated_by="api" in sent_log. deliver_cert_internal gained dry_run + initiated_by params; the cert_delivered timeline event moved into it (portal sends now log one too).

Test-suite side effects:

  • pillow_heif installed on the host → tests/test_cert_pipeline.py (57 tests) is back in the suite after being --ignore’d since session 71.
  • Three latent failures the ignore was masking, all fixed: routing e2e tests now pin per-recipient tracking off (they assert routing, not delivery mode); scope-closeout _run uses asyncio.run (get_event_loop dies after pytest-asyncio files on py3.12); idempotency tests patch redis.asyncio as a parent-package attribute too (tb26’s permanent stub bypassed the sys.modules fake).
  • cert_preflight._check_no_active_duplicate now accepts raw SmartSuite status IDs alongside resolved labels (value-ID convention).

Suite 483 passed; deployed (api + worker + scheduler rebuilt — note: worker/scheduler have their own image tags, restart alone does NOT pick up a rebuilt image); live-verified via dry_run on R373 Token Forest Drive.