Virtual intercom — directory, call, unlock
A virtual intercom replaces or augments a lobby panel: visitors scan a QR code or open a public portal URL, browse a building directory, call a resident, and optionally unlock the door. OpenApp implements this through Virtual Access integrations, access portals, and public session APIs — no rip-and-replace hardware required when you already have relays or cloud gate APIs.
This guide targets building operators and integrators automating apartment and office entry. For end-user portal concepts, see Access Portals and Virtual Access integration.
Flow overview
Section titled “Flow overview”Visitor scans portal QR (/p/{public_id}) │ ├─ Directory mode → pick resident → POST portal/sessions (call/video) │ └─ Resident answers → POST session/open │ └─ Guest with invite → quick open (invite execute)Setup (one-time)
Section titled “Setup (one-time)”- Add a Virtual Access integration and link a door device to a portal (portals user guide).
- Note
publicPortalIdfrom the portal’s public URL and thetarget_entity_idfor the door entity.
- Optional: go2rtc for live camera on video calls.
Fill the building directory from photos
Section titled “Fill the building directory from photos”If the physical intercom or mailbox bank already lists unit numbers and resident names but apartment display names in OpenApp are empty, use Enrich (Virtual Intercom → Enrich): upload panel photos, Analyze, review, and Commit. This fills the visitor directory labels visitors see when browsing the portal.
See Virtual Access — automatic directory filling from photos for privacy, API key setup, and manual JSON fallback.
Directory + call session
Section titled “Directory + call session”Start a call or video session from the public portal. Body PublicPortalCreateSessionRequest requires target_entity_id and mode ("call" or "video").
SDK
session = await client.public_access.portal_start_session( portal_id, target_entity_id=entity_id, mode="call",)session_id = session["session_id"]const session = await fetch( `${apiBase}/public/access/portals/${portalId}/sessions`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ target_entity_id: entityId, mode: "call", }), },).then((r) => r.json() as Promise<{ session_id: string }>);
const sessionId = session.session_id;use openapp_sdk::Client;use serde_json::json;
let client = Client::builder() .api_key("https://api.openapp.house/api/v1_openapp_YOUR_SECRET") .build()?;
let session = client .public_access() .portal_start_session( portal_id, &json!({ "target_entity_id": entity_id, "mode": "call" }), ) .await?;let session_id = session["session_id"].as_str().unwrap();body := *openapiclient.NewPublicPortalCreateSessionRequest(entityID, "call")resp, httpResp, err := client.PublicAccessAPI.PostPublicPortalSessions(ctx, portalID). PublicPortalCreateSessionRequest(body). Execute()if err != nil { return err}defer httpResp.Body.Close()sessionID := resp.GetSessionId()The response includes session_id, caller_token, and WebRTC routing hints. Poll GET /public/access/sessions/{sessionId} until the callee accepts.
HTTP API (curl)
export OPENAPP_API_BASE='https://api.openapp.house/api/v1'export PUBLIC_PORTAL_ID='01HPUBLICPORTAL000000000000'export TARGET_ENTITY_ID='01HENTITY000000000000000000'
curl -sS -X POST \ -H "Content-Type: application/json" \ -d '{ "target_entity_id": "'"${TARGET_ENTITY_ID}"'", "mode": "call" }' \ "${OPENAPP_API_BASE}/public/access/portals/${PUBLIC_PORTAL_ID}/sessions"Unlock after answer
Section titled “Unlock after answer”When policy allows, open from the active session:
SDK
await client.public_access.session_open(session_id)await fetch(`${apiBase}/public/access/sessions/${sessionId}/open`, { method: "POST",});use openapp_sdk::Client;use serde_json::json;
let client = Client::builder() .api_key("https://api.openapp.house/api/v1_openapp_YOUR_SECRET") .build()?;
client.public_access().session_open(session_id, &json!({})).await?;_, httpResp, err := client.PublicAccessAPI.PostPublicSessionOpen(ctx, sessionID).Execute()if err != nil { return err}defer httpResp.Body.Close()See Public Access.
HTTP API (curl)
curl -sS -X POST \ -H "Content-Type: application/json" \ -d '{}' \ "${OPENAPP_API_BASE}/public/access/sessions/${SESSION_ID}/open"Residents may also receive Web Push notifications; configure subscriptions via Me — push.
Guest quick-open (invitation path)
Section titled “Guest quick-open (invitation path)”Visitors with a valid invite link skip the directory and use invite execute. Pair with Time-bound guest invitation for STR and hotel stays.
Agent automation tips
Section titled “Agent automation tips”| Step | API surface |
|---|---|
| List portals | GET /integrations/{id}/access-portals |
| Probe reachability | GET /public/access/portals/{id}/reachable |
| Callable targets | GET /public/access/portals/{id}/targets |
| Audit-friendly reason | Pass reason on portal open when supported |
Full operation index: Agent-relevant API.