You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#266 — Twitch EventSub: ingestão de eventos via webhook (data lake)
What to build
A public HTTPS endpoint that receives Twitch EventSub webhook notifications, verifies their HMAC-SHA256 signature, and stores the raw payload in a twitch_event_logs table. Pure data lake — no processing on write.
End-to-end behavior
After this slice, Twitch can POST EventSub notifications to /api/webhooks/twitch/eventsub. The endpoint handles three message types:
Verification challenge: returns the challenge string (200, text/plain) to activate new subscriptions
Notification: stores the full payload in twitch_event_logs and returns 204
Revocation: stores the revocation payload and returns 204
Invalid signatures, missing headers, expired timestamps (>10 min), and duplicate messages are all rejected or handled gracefully.
TwitchEventLog in Models/ — simple model with payload cast to array, mirrors DiscordEventLog
New — Middleware:
VerifyTwitchSignature in Http/Middleware/ — verifies Twitch-Eventsub-Message-Signature header using HMAC-SHA256 (hash_hmac + hash_equals), rejects missing headers (403), rejects timestamps older than 10 minutes (403)
New — Controller:
TwitchWebhookController in Http/Controllers/ — single __invoke method handling all three message types. Extracts event_type, broadcaster_user_id, user_id from the payload envelope. Deduplication via twitch_message_id unique constraint (catches unique violation, returns 204).
New — Route:
routes/twitch-webhook-routes.php — POST /api/webhooks/twitch/eventsub with VerifyTwitchSignature middleware. Auto-discovered by internachi/modular.
Acceptance criteria
twitch_event_logs table created with correct schema (event_type indexed, broadcaster_user_id indexed, twitch_message_id unique, payload jsonb)
TwitchEventLog model persists and casts payload to array
Parent
#266 — Twitch EventSub: ingestão de eventos via webhook (data lake)
What to build
A public HTTPS endpoint that receives Twitch EventSub webhook notifications, verifies their HMAC-SHA256 signature, and stores the raw payload in a
twitch_event_logstable. Pure data lake — no processing on write.End-to-end behavior
After this slice, Twitch can POST EventSub notifications to
/api/webhooks/twitch/eventsub. The endpoint handles three message types:twitch_event_logsand returns 204Invalid signatures, missing headers, expired timestamps (>10 min), and duplicate messages are all rejected or handled gracefully.
What changes
New — Migration:
twitch_event_logstable:id(bigint PK),event_type(string, indexed),broadcaster_user_id(string, nullable, indexed),user_id(string, nullable),twitch_message_id(string, nullable, unique),payload(jsonb),timestampsNew — Model:
TwitchEventLoginModels/— simple model withpayloadcast to array, mirrorsDiscordEventLogNew — Middleware:
VerifyTwitchSignatureinHttp/Middleware/— verifiesTwitch-Eventsub-Message-Signatureheader using HMAC-SHA256 (hash_hmac+hash_equals), rejects missing headers (403), rejects timestamps older than 10 minutes (403)New — Controller:
TwitchWebhookControllerinHttp/Controllers/— single__invokemethod handling all three message types. Extractsevent_type,broadcaster_user_id,user_idfrom the payload envelope. Deduplication viatwitch_message_idunique constraint (catches unique violation, returns 204).New — Route:
routes/twitch-webhook-routes.php—POST /api/webhooks/twitch/eventsubwithVerifyTwitchSignaturemiddleware. Auto-discovered byinternachi/modular.Acceptance criteria
twitch_event_logstable created with correct schema (event_type indexed, broadcaster_user_id indexed, twitch_message_id unique, payload jsonb)TwitchEventLogmodel persists and casts payload to arrayVerifyTwitchSignaturemiddleware rejects: missing headers (403), invalid signature (403), expired timestamp >10 min (403)VerifyTwitchSignaturepasses valid HMAC-SHA256 signed requestswebhook_callback_verificationTwitchEventLogand returns 204 fornotificationtypeTwitchEventLogand returns 204 forrevocationtypeevent_type,broadcaster_user_id,user_idfrom payload into dedicated columnstwitch_message_idreturns 204 without error (idempotent)POST /api/webhooks/twitch/eventsubvendor/bin/pint --dirty --format agentpassesphp artisan test --compact --filter=TwitchpassesBlocked by
eventsub_secretandeventsub_callbackmust exist)