Skip to content

H3: Tuvens S2S event create/link + promo push - #27

Merged
tuvens merged 1 commit into
developfrom
feat/tuvens-s2s-events
Jul 21, 2026
Merged

H3: Tuvens S2S event create/link + promo push#27
tuvens merged 1 commit into
developfrom
feat/tuvens-s2s-events

Conversation

@tuvens

@tuvens tuvens commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Implements #19 against the ratified contract (tuvens-api docs/integrations/hi-events-contract.md @ 04469c48). Stacked on #22 (H1a) — retarget to develop after it merges.

What's here

  • TuvensInternalApiMiddleware (contract §1.2 scheme): ±5 min timestamp window, HMAC-SHA256 over timestamp + "." + rawBody, hash_equals compare, 503 if the secret is unconfigured, X-On-Behalf-Of passthrough.
  • POST /tuvens/events (§3): resolves the on-behalf-of user via users.external_user_id, creates a DRAFT event with attributes: [{name: "tuvens_event_id", value, is_public: false}], auto-registers a per-event webhook at {main_backend.url}/api/webhooks/ticketing/hi-events (subscribed: product.*, order.created/refunded/cancelled, event.updated, event.archived), responds {event_id, event_url, widget_embed_url, webhook_secret}. Idempotent per (account, tuvens_event_id) — a repeat create returns the existing event and its original webhook secret (200 vs 201).
  • PUT /tuvens/events/{event_id} (§5): promo fields only; currency and all ticketing fields are unwritable through this channel (test-pinned). X-Idempotency-Key honored with a 24h replay window and an X-Idempotent-Replay header.
  • Fix riding along: CrossAppAccountMappingService now creates default organizer settings for auto-created organizers (via CreateDefaultOrganizerSettingsService) — without them the first event creation for any provisioned account 500s (getHomepageThemeSettings() on null), which would have hit the SSO flow too.

Contract notes for tuvens-api (also on #16)

  1. First-contact provisioning gap: §3's body has no user identity, but the create call happens before SSO, so the on-behalf-of user may not exist here yet. This endpoint accepts an optional user: {email, name} object and returns 422 USER_NOT_PROVISIONED when the user is unknown and no identity is supplied. Please add user to the §3 body in the contract doc.
  2. Paths: mounted at POST/PUT {hievents}/tuvens/events[...] — with the deployment's /api proxy prefix this externally matches the contract's /api/tuvens/events. The promo push lives on the same S2S prefix rather than the general /api/events/{id} (§5's sketch), keeping the HMAC surface in one route group.
  3. event_url is the organiser manage deep-link (/manage/event/{id}), widget_embed_url is the public /widget/{id} route.

Tests

9 feature tests green (signature/timestamp rejection, create+webhook+provisioning, idempotency, USER_NOT_PROVISIONED, field ownership, replay, 404) plus the full unit suite (435 tests) on this branch.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RrKvdjja8NtzqRKJnX7sA1

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@tuvens tuvens left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coordinator review — APPROVED (approval of record), contract-conformant to 04469c48

Verified: TuvensInternalApiMiddleware fails closed on missing secret, digit-checks the timestamp, enforces the ±window, recomputes HMAC over the RAW body and compares with hash_equals, and passes X-On-Behalf-Of through as an attribute resolved against users.external_user_id. Create is idempotent (replay returns the originally-registered webhook secret), unknown user → 422 USER_NOT_PROVISIONED, promo PUT honors X-Idempotency-Key with TTL'd replay. 9 feature tests + full suite green per the gate report. Your two contract asks (optional user:{email,name} on create; S2S mount path note) are being relayed to the api session for a contract revision — pin any follow-up commits to that rev.

Implements the server-to-server surface per the ratified contract
(tuvens-api docs/integrations/hi-events-contract.md @ 04469c48; #16, #19):

- TuvensInternalApiMiddleware: X-Tuvens-Timestamp (±5 min) +
  X-Tuvens-Signature = HMAC-SHA256(secret, timestamp + "." + rawBody),
  constant-time compare; X-On-Behalf-Of passed through for user resolution.
- POST /tuvens/events (contract §3): provisions/resolves the on-behalf-of
  user (optional body.user {email, name} identity for first contact),
  creates a DRAFT event with attributes[{name: tuvens_event_id}], auto-
  registers a per-event outgoing webhook at
  {main_backend.url}/api/webhooks/ticketing/hi-events subscribed to
  product.*, order.created/refunded/cancelled, event.updated/archived, and
  responds {event_id, event_url, widget_embed_url, webhook_secret}.
  Idempotent per (account, tuvens_event_id) — repeats return the existing
  link with the original webhook secret.
- PUT /tuvens/events/{event_id} (contract §5): tuvens-owned promo fields
  only (title, description, dates, timezone, venue); ticketing fields such
  as currency are never writable through this channel. Honors
  X-Idempotency-Key with 24h replay.
- CrossAppAccountMappingService now creates default organizer settings for
  auto-created organizers — without them, the first event creation for a
  provisioned account 500s (affects the SSO flow too).

Feature tests: 9 tests covering signature/timestamp rejection, create +
webhook registration + provisioning, idempotent create, USER_NOT_PROVISIONED,
missing X-On-Behalf-Of, promo-only field ownership, idempotent replay, 404
for non-linked events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RrKvdjja8NtzqRKJnX7sA1
@tuvens
tuvens force-pushed the feat/tuvens-s2s-events branch from 05f18f6 to c6bba77 Compare July 21, 2026 15:30
@tuvens
tuvens changed the base branch from feat/tuvens-account-mapping to develop July 21, 2026 15:31
@tuvens
tuvens merged commit 2b47ca7 into develop Jul 21, 2026
1 check failed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

1 participant