External system plane
Integration Center
External systems provision, orchestrate, embed, and receive events through OAuth2 machine contracts — never by replaying host links.
OAuth2 clients
Tenant-bound machine contracts| Client | Control profile | Scopes | Auth | Status | Actions |
|---|---|---|---|---|---|
| Town Hall Eventscli_townhall · cli_9f2a… | provisioner | poll.session.createpoll.session.schedule | client-credentials | Active | |
| Academy LMScli_lms · cli_3b7c… | provisioner dynamic_author | poll.session.createpoll.question.authorpoll.results.read | client-credentials | Active | |
| Meeting Orchestratorcli_meet · cli_a1d4… | orchestrator | poll.session.activatepoll.question.controlpoll.results.read | mTLS | Active | |
| Insights Warehousecli_bi · cli_6e08… | observe_only | poll.results.readpoll.event.subscribe | client-credentials | Active | |
| Command Bridge (exceptional)cli_bridge · cli_0c55… | full_live_control | poll.session.commandpoll.question.control | mTLS | Suspended |
Control profiles
Least-privilege presets bound at registrationobserve_only
Read aggregate results and subscribe to events. No write path to sessions.
provisioner
Create and schedule sessions ahead of time. Cannot activate or drive a live session.
orchestrator
Activate sessions and drive question flow in step with an external meeting timeline.
dynamic_author
Author questions programmatically from an allowlisted schema. Cannot change governance.
full_live_control
Issue unsafe live commands: pause, close, finalize. Reserved for certified bridges.
Webhooks are signed with an HMAC over the payload plus a timestamp and event id, retried with exponential backoff, and dead-lettered after exhaustion. High-frequency raw vote events are NOT delivered as webhooks — consumers receive throttled aggregates or pull the results API.
Webhook catalog
Delivered event typesSigned webhook example
poll.results.revealed# Headers X-Basirah-Signature: sha256=t=1752670800,v1=9f2a3c…d41b X-Basirah-Timestamp: 1752670800 X-Basirah-Event-Id: evt_4f7c22a9 { "eventId": "evt_4f7c22a9", "sessionId": "poll-8842", "sequence": 1042, "aggregateVersion": 57, "eventType": "poll.results.revealed", "occurredAt": "2026-07-16T14:41:00Z", "data": { "questionId": "q_03", "totalResponses": 3088 } }
Approved embed origins
CSP frame-ancestors allowlistA per-tenant, per-session Content-Security-Policy frame-ancestors directive is generated at activation, so only allowlisted origins may frame the participant and stage views.
Embeddable views
Generated CSP
poll-8842# Per-tenant, per-session · SAMA–SCB Content-Security-Policy: frame-ancestors 'self' https://events.sama.sa https://portal.academy.sa;Tokens are never placed in iframe query params after activation.
Session command contract
Unsafe live commands# POST /v1/sessions/poll-8842/commands Idempotency-Key: cmd_7b19e0c4 If-Match: "aggregateVersion:57" X-Correlation-Id: corr_a3f1… { "commandId": "cmd_7b19e0c4", "expectedVersion": 57, "requestedAt": "2026-07-16T14:42:10Z", "reason": "host requested pause", "payload": { "command": "session.pause" } }
A stale expectedVersion returns 409 SESSION_VERSION_CONFLICT. Responses return the new aggregateVersion plus the latest event sequence so the caller can reconcile.
SessionRealtimeEvent envelope
Delivered to subscribers{
"eventId": "evt_4f7c22a9",
"sessionId": "poll-8842",
"sequence": 1043,
"aggregateVersion": 58,
"eventType": "poll.session.paused",
"occurredAt": "2026-07-16T14:42:11Z",
"correlationId": "corr_a3f1…",
"data": {
"pausedBy": "cli_meet"
}
}
Every event carries a monotonic sequence and the aggregateVersion it produced, so consumers detect gaps, order events deterministically, and reconcile against a command response.