# Bohita Presence complete agent-readable corpus Canonical installation, product, evaluation, measurement, and security material in one response. This file is compiled from the built product pages and canonical agent-specific documents listed below. Use `https://presence.bohita.com/llms.txt` for progressive discovery and this file when one complete response is preferable. Public contract release: v0.1.5 Published: 2026-09-18 Pinned commit: 5d1d6f4d2276253dc3f037e0c83e3af4cc405a42 Repository: https://github.com/virevolai/presence Release-source verification performed during this build: - service integration source SHA-256: `ac5b408fb55ccf8e145f4f886b074857dcfacc9f9439069cafecb7918271c3dd` - canonical OpenAPI source SHA-256: `7ab5feb1972f3f1ca69137949622360e007dfac22760b415f812ddf5b059897f` - changelog source SHA-256: `ff4950f3323c2bcc9625ec95d3a231e6779a373944fbf1a5cc62b95527cb5579` - version source SHA-256: `800cf1c0392b24de7c0a1c6ea6778ecb433dec71c49a150bce96a98477527b2f` The repository contains implementation and future-contract material. The customer API contract is the public Presence surface described by the release fragment and OpenAPI document in this corpus. The web Session path is the shortest integration path; authoring, tools, and other surfaces are available progressively through the capability guide and schema. Do not treat other repository routes as publicly available. ## Corpus contents - install-index: https://presence.bohita.com/llms.txt - install-web: https://presence.bohita.com/llms/install-web.txt - presence-openapi: https://presence.bohita.com/openapi.json - capability-availability: https://presence.bohita.com/llms/capabilities.txt - evaluation-index: https://presence.bohita.com/llms/evaluate.txt - product: https://presence.bohita.com/ - product-trials: https://presence.bohita.com/try-your-product - benchmarks: https://presence.bohita.com/benchmarks - forty-turn-benchmark: https://presence.bohita.com/posts/what-happens-on-turn-forty - coding-agents: https://presence.bohita.com/coding-agents - coding-agent-evidence: https://presence.bohita.com/evidence/clerk-auth-evidence-v1.txt - security: https://presence.bohita.com/security - changelog: https://presence.bohita.com/changelog.txt --- # Bohita Presence > Presence is a realtime audio-and-video agent that can notice the person it > is talking to, remain interruptible, and keep the conversation moving while > tools run asynchronously. ## Install Presence If a human sent you here to add Presence to a product, start with the small web installation path. Load the complete corpus only if you prefer one request: - [Current public API contract: hosted web Sessions](https://presence.bohita.com/llms/install-web.txt) - [Curated Presence OpenAPI](https://presence.bohita.com/openapi.json) - [Capability availability](https://presence.bohita.com/llms/capabilities.txt) - [Complete one-request corpus](https://presence.bohita.com/llms-full.txt) - [Create or manage an API key](https://presence.bohita.com/account/api-keys) - [Buy Presence minutes](https://presence.bohita.com/#pricing) The human signs in once and creates an API key. Creating a key establishes application identity but grants no usage and cannot access the free demo. Paid Session creation reserves Presence credits and returns HTTP `402` when the account does not have enough. Purchasing credits activates existing keys immediately; no rotation is required. Never ask the human to paste an API key into frontend source, chat logs, a URL, or a browser-exposed environment variable. The `presence_server` customer API key supports Presence drafts and publishing, custom role/guidance, knowledge, and tools, plus web, voice, meeting, and approved outbound-phone Sessions. Inbound phone remains carrier-originated; managed outbound phone requires Bohita approval. Trusted local agents, CLIs, desktop applications, servers, and browser BFFs use: ```bash export BOHITA_API_BASE_URL=https://api.bohita.com export BOHITA_API_KEY=bohita_sk_live_... ``` The self-serve integration requires a trusted place to hold that key: an existing BFF, serverless function, traditional server, trusted local coding agent process, or desktop host. Framework choice is not the constraint; a Vite or other static frontend works when it calls such a boundary. A browser-only production SPA with no trusted process cannot use the secret API key directly. The browser-safe, origin-scoped bootstrap credential for that topology is not self-serve yet. [Contact Bohita](mailto:hello@bohita.com) before choosing that architecture. ## Minimal web Session Create short-lived web Sessions through: ```http POST https://api.bohita.com/v1/sessions Authorization: Bearer $BOHITA_API_KEY Idempotency-Key: Content-Type: application/json { "external_id": "your-opaque-user-or-guest-id", "surface": {"type": "web"}, "startup": "managed" } ``` Keep the idempotency key stable for network retries of the same user action. Generate a new key only when the user deliberately starts a new Session. The response contains an opaque `embed_url`. Return only the safe session fields to the authenticated browser and mount that URL unchanged: ```html ``` Keep `display-capture` in the permission policy so the hosted client can offer screen sharing after an explicit user action. Screen sharing remains optional. For local testing, serve the page from `http://localhost`; do not double-click the HTML file or open it with a `file://` URL. For example, run `python3 -m http.server 8000` in its directory and open `http://localhost:8000`. A `file://` page can leave the hosted frame blank in a way that looks like a Presence failure. Treat `embed_url` as opaque. The full contract covers response fields, readiness, typed errors, privacy, session cleanup, sharing, and payments. After a Session ends, read its owner-authorized durable results from `GET /v1/sessions/{session_id}/outputs`. Summaries and notes retained by the published Presence appear as artifacts alongside any transcript or recording manifest. Outputs may finish after `/end`; do not assume the first read is final. Public HTTP errors include `400`, `401`, `402`, `403`, `404`, `409`, `422`, `429`, `500`, and `503`. `Idempotency-Key` is required for Session creation and explicit Session end; omitting it returns `400`. Error bodies have a top-level `detail` containing either a string, a typed `{code,message}` object, or validation details. Branch on HTTP status first, then use a typed code when present. Use bounded backoff for `429`, `500`, and `503`; `Retry-After` is not currently guaranteed. ## Evaluating rather than integrating - [Evaluation index](https://presence.bohita.com/llms/evaluate.txt) - [Product and live demo](https://presence.bohita.com/) - [Product trials and onboarding](https://presence.bohita.com/try-your-product) - [Benchmarks](https://presence.bohita.com/benchmarks) - [Coding agents](https://presence.bohita.com/coding-agents) - [Security and privacy](https://presence.bohita.com/security) - [Forty-turn voice benchmark](https://presence.bohita.com/posts/what-happens-on-turn-forty) - [Recorded coding-agent run](https://presence.bohita.com/evidence/clerk-auth-evidence-v1.txt) ## Release and changelog This site is pinned to Presence public contract release `v0.1.5`, commit `5d1d6f4d2276253dc3f037e0c83e3af4cc405a42`. - [Changelog](https://presence.bohita.com/changelog.txt) - [Repository](https://github.com/virevolai/presence) ## Demo and pricing The launch demo includes five sessions per enrolled account, each capped at five connected minutes. Paid Presence Sessions are billed by occupied time. Public offers are $9 for 60 minutes and $100 for 1,000 minutes; credits do not expire. - [Try Presence and see pricing](https://presence.bohita.com/#pricing) ## Support - Product availability and integration support: [email Bohita](mailto:hello@bohita.com) --- # Install Presence on the web This is the shortest complete public integration path for the default hosted Presence. The browser needs no Presence package and never receives your API key. Machine-readable operation and schema definitions: https://presence.bohita.com/openapi.json ## Before you start: browser-only static apps The self-serve path requires a trusted place to hold the API key and create short-lived Sessions. This can be an existing same-origin BFF, a serverless function, a traditional server, a trusted local coding-agent process, or a desktop host. It does not require deploying a conventional server or public website. Framework choice is not the boundary. A Vite or other static frontend can mount Presence when it calls one of those trusted processes. A browser-only production SPA with no BFF or trusted process cannot safely use the secret API key. Bohita's tenant BFF is available to applications deployed on Bohita infrastructure; it is not automatically mounted on an arbitrary customer domain. A browser-safe, origin-scoped bootstrap credential is required for a purely static external deployment. That credential mode is not self-serve today. Do not put `BOHITA_API_KEY` in frontend code or a `VITE_*` variable to work around this limitation. [Contact Bohita](mailto:hello@bohita.com) before choosing this topology. For local evaluation, a trusted local process can hold the key while the static page is served over HTTP. Do not double-click the HTML file or use a `file://` URL; run `python -m http.server 8000` (or `python3 -m http.server 8000`) and open `http://localhost:8000`. ## 1. Create an API key and buy minutes Create or manage an API key at: https://presence.bohita.com/account/api-keys Buy occupied-time credits at: https://presence.bohita.com/#pricing Creating a key establishes customer and application identity. It grants no usage and cannot access the free demo. Paid Session creation reserves Presence credits and returns HTTP `402` when the balance is insufficient. Existing keys work immediately after credits are purchased; no rotation is required. The key starts with `bohita_sk_live_`. Keep it in one of the trusted processes described above. Never put it in browser JavaScript, source control, a URL, analytics, or a browser-exposed environment variable such as `VITE_*`. Current controls require a verified email, allow up to 10 active keys per customer, and allow 120 requests per minute per key. Principals and individual keys can be revoked. Configure the trusted process with: ```bash export BOHITA_API_BASE_URL=https://api.bohita.com export BOHITA_API_KEY=bohita_sk_live_... ``` ## 2. Create a web Session Create the Session from your trusted process. For the default published Presence, omit `presence_id`: ```http POST https://api.bohita.com/v1/sessions Authorization: Bearer $BOHITA_API_KEY Idempotency-Key: Content-Type: application/json { "external_id": "your-opaque-user-or-guest-id", "surface": {"type": "web"}, "startup": "managed" } ``` Keep `Idempotency-Key` stable for network retries of this user action. Generate a new key only for a new deliberate Session attempt. A pre-ready replacement triggered by `restart_required` is a new attempt and therefore uses a new key. `external_id` should be your own opaque identifier. Do not send an email address or another unnecessary personal identifier. A successful response includes the admitted Session and a short-lived hosted client URL: ```json { "session_id": "psn_...", "presence_id": "presence/default", "version": 1, "external_id": "your-opaque-user-or-guest-id", "surface": {"type": "web"}, "state": "ready", "embed_url": "https://...short-lived-scoped-url...", "expires_at_unix": 1787000000, "max_seconds": 300, "replayed": false, "created_at_unix": 1786999700.25, "recording": { "available": true, "policy_version": "presence.recording.v1", "disclosure": "Server-owned participant disclosure text" } } ``` Relay only the browser-safe fields, normally `session_id`, `embed_url`, and `expires_at_unix`. Never relay the API key or the complete upstream response. The create response is this direct object, not an envelope. `replayed: true` means the service returned the result previously stored for the same `Idempotency-Key` and identical input. Handle HTTP `402` as insufficient credits and `429` as rate or capacity pressure. Do not retry either in a tight loop. The request body itself is required, but all its fields have defaults. It also accepts `presence_id`, `version`, and `requested_max_seconds`. The requested duration must be `1..86400`; the current product clamps an admitted Session to at most 3,600 seconds. Insufficient credits return `402` rather than silently reducing the request to the available balance. Unknown fields return `422`. ### Session lifetime `max_seconds` and `expires_at_unix` describe different limits: - `max_seconds` is the maximum connected media time admitted for this Session. A launch demo permits 300 connected seconds. Paid requests are clamped to product policy and available credit. - `expires_at_unix` is the absolute Unix-time expiry of the scoped connection capability and any unused reservation. It may include bounded admission and reconnect grace. Do not derive it from `max_seconds`. Mount `embed_url` immediately. If its capability expires before the first media connection, the URL becomes unusable and the unconsumed reservation is released. Reaching the connected-time limit closes media and finalizes the Session normally. Capability expiry does not terminate an already-open media connection, but a transport loss after expiry cannot reconnect. Reconnecting while the capability remains valid does not reset connected usage. There is no refresh or extend operation. Continuing requires a newly admitted Session with a new idempotency key. A host may make one bounded replacement when the iframe reports the documented pre-ready `restart_required` phase. Otherwise, create another Session only after an explicit user action. Session expiry is independent of Firebase login and API-key expiry. ## 3. Mount the returned URL unchanged ```html ``` Keep `display-capture` in the iframe permission policy so the hosted client can offer screen sharing after an explicit user action. Screen sharing is optional and transient Session input. Some mobile browsers do not support display capture and leave the control unavailable. Treat `embed_url` as opaque. It is an HTTPS document containing the complete hosted client, not a WebSocket address. Do not parse, rewrite, persist, proxy, construct, or replace it. The hosted client owns media, character rendering, audio playback, captions, expressions, in-frame artifacts, interruption, reconnection, and cleanup. Explain why camera and microphone access are useful before requesting browser permission. Apply your own authentication, eligibility, quota, and abuse controls to the endpoint that creates Sessions. Camera behavior belongs to the published Presence version, not the Session creation request. A `required` policy makes camera readiness part of startup. The default `on_request` policy starts voice without prompting for a new camera grant; it may reuse an existing grant or ask conversationally when visual context would help. Refusing camera does not block audio in `on_request` mode. Do not add a camera-policy field to `POST /v1/sessions`. Use `startup: "managed"` for the managed arrival treatment. `startup: "host"` is also accepted for a host that implements the readiness contract below. A successful reservation means the scoped URL can be mounted; it does not mean first audio is already playing. When enabled, conversational readiness is posted by the iframe: ```json {"type":"presence.ready","protocol":"bohita.embed.v1","session_id":"ses_..."} ``` Accept messages only from the iframe's `contentWindow`, the exact origin of the returned `embed_url`, and the current Session ID. ### Pre-ready recovery If the hosted runtime can no longer recover before becoming conversationally ready, it posts: ```json { "type": "presence.startup", "protocol": "bohita.embed.v1", "session_id": "ses_...", "phase": "restart_required" } ``` Accept this message only from the current iframe's `contentWindow`, the exact origin of its returned `embed_url`, protocol `bohita.embed.v1`, and the current Session ID. The host may make one automatic replacement for the user-started launch. It must create a new Session as a new attempt with a new idempotency key, then replace the iframe with the new opaque `embed_url`. A browser BFF obtains fresh user authentication and abuse proof when required; a customer server uses its existing API key. This creates a replacement Session and does not refresh the old one. Permit at most one automatic replacement per user action. Do not retry the old Session, loop replacements, or ask the iframe to create its own Session. After the bounded replacement, another attempt requires an explicit user action. Other startup progress, including `phase: "stalled"`, may still be resolved by permission or an in-frame retry and must not trigger Session replacement. ## 4. Artifacts and sharing The iframe may emit `presence.text`, `artifact_available`, and `presence.share.requested`. `presence.text` and `artifact_available` require no host UI. When durable sharing is enabled, handle `presence.share.requested` through the authenticated share endpoint and reply with `presence.share.ready` or `presence.share.failed`. The authenticated host, not the iframe, grants the durable permalink. ## 5. End the Session Remove the iframe when the host surface closes. Also end the Session from your trusted process when practical: ```http GET https://api.bohita.com/v1/sessions/{session_id} GET https://api.bohita.com/v1/sessions/{session_id}/events?after=evt_...&limit=100 GET https://api.bohita.com/v1/sessions/{session_id}/outputs Authorization: Bearer $BOHITA_API_KEY ``` End the Session with a separate stable idempotency key for this cleanup attempt. The header is required; omitting it returns HTTP `400`. ```http POST https://api.bohita.com/v1/sessions/{session_id}/end Authorization: Bearer $BOHITA_API_KEY Idempotency-Key: Content-Type: application/json {"reason":"customer_ended"} ``` The end body is optional. `customer_ended` is the default reason, and the reason may contain at most 100 characters. Session end is explicit and idempotent. Preserve this cleanup key for retries with identical input. Reusing it with changed input returns `409`; use a new key for a different end attempt. The event log returns `events`, `has_more`, and an optional `next_after` cursor. Pass `next_after` as the next `after` value when `has_more` is true. The default page limit is 100 and the allowed range is `1..500`. An empty `events` array means the reader is caught up. ## 6. Read what the Session left behind Ending a Session and reading its results are separate operations. `/end` requests shutdown and returns Session status. The owner-authorized `/outputs` endpoint returns the durable output manifest for that Session: ```http GET https://api.bohita.com/v1/sessions/{session_id}/outputs Authorization: Bearer $BOHITA_API_KEY ``` ```json { "session_id": "psn_...", "state": "ended", "outputs": [ { "kind": "transcript", "output_id": "transcript", "schema_version": "1", "content": [ {"t_ms": 1200, "role": "user", "text": "Hello"}, {"t_ms": 1840, "role": "assistant", "text": "Hi"} ] }, { "kind": "artifact", "output_id": "session-summary", "artifact_id": "session-summary", "artifact_ref": "urn:presence:artifact/...", "renderer": "presence.board", "schema_version": "1", "shareable": true }, { "kind": "recording", "recording_id": "rec_...", "schema_version": "1", "manifest_asset_ref": "opaque-do-not-parse" } ] } ``` Ask the Presence to leave behind the outcome the product needs: a session summary, meeting notes, what someone got stuck on, what it helped with, or what still needs attention. Those retained outcomes appear as artifacts in this manifest. Their content and shape follow the published Presence rather than a single universal summary schema. For example, a Presence that sits with people trying a developer product can be authored to retain a `session-summary` artifact with this body: ```json { "where_they_got_stuck": [ { "area": "Clerk quickstart", "signal": "Paused after a 500 error" } ], "what_it_helped_with": [ { "issue": "Dashboard route was excluded", "resolution": "Updated the middleware matcher" } ], "what_it_left_behind": [ { "kind": "artifact", "title": "Working middleware rule" } ] } ``` This is an example authored artifact body, not a fixed response schema. A tutoring Presence, support Presence, or meeting Presence can retain a different shape. The `/outputs` response remains the stable manifest that tells the owner which retained transcript, artifact, and recording outputs exist. To post that summary body directly to an endpoint, publish the Presence with a strict end-of-session function and a server-side tool webhook: ```json { "tools": [{ "type": "function", "name": "report_product_trial", "description": "Call once when the product trial ends with the useful outcome.", "parameters": { "type": "object", "properties": { "where_they_got_stuck": {"type": "array", "items": {"type": "object"}}, "what_it_helped_with": {"type": "array", "items": {"type": "object"}}, "what_it_left_behind": {"type": "array", "items": {"type": "object"}} }, "required": [ "where_they_got_stuck", "what_it_helped_with", "what_it_left_behind" ], "additionalProperties": false }, "strict": true }], "tool_runtime": { "type": "webhook", "url": "https://example.com/presence/tools" } } ``` Tell the Presence in its role to call `report_product_trial` once when closing the trial and to retain the same outcome as its `session-summary` artifact. Bohita delivers the function arguments to the configured tool webhook; the durable artifact remains discoverable through `/outputs`. An abruptly expired or externally terminated Session may not complete a conversational tool call, so use lifecycle subscriptions when the receiver must also learn that the Session itself ended. To receive these outcomes without polling, configure lifecycle subscriptions on the published Presence for `artifact.created`, `transcript.finalized`, and `session.ended`: ```json { "event_subscriptions": [{ "type": "webhook", "url": "https://example.com/presence/events", "events": ["artifact.created", "transcript.finalized", "session.ended"] }] } ``` Bohita stores the destination server-side and owns signing, delivery, retry, audit, and deduplication. Verify the webhook signature and process deliveries idempotently. Lifecycle delivery tells the receiver that a durable outcome or Session transition exists; the authenticated `/outputs` manifest remains the owner-authorized source of truth. Small transcripts may be returned inline. Artifact and recording entries use opaque references; recording entries do not contain audio or video bytes. Never construct a download or public URL from a reference. Sharing remains a separate authorized operation. Outputs can appear after `/end` while transcript, artifact, or recording work finishes. Treat `output_id`, `artifact_id`, and `recording_id` as stable identities, and do not assume the first response is final. If nothing was retained, the response contains `"outputs": []`. ## HTTP errors - `400`: malformed request or missing required `Idempotency-Key`. Do not retry unchanged. - `401`: `Authorization` is missing, malformed, invalid, expired, or revoked. - `402`: insufficient Presence credits. Purchase credits, then retry with the existing API key and a new `Idempotency-Key`. - `403`: the authenticated principal is not authorized for the operation. - `404`: the Session does not exist or is not visible to this authenticated customer. - `409`: request conflicts with current state, including reusing an idempotency key with changed input. - `422`: JSON or query validation failed, including unknown request fields. - `429`: request-rate or live-capacity limit. Use bounded backoff; `Retry-After` is not currently guaranteed. - `500`: internal failure. Retry only with bounded backoff. - `503`: temporary unavailability. Retry with bounded backoff and preserve the `Idempotency-Key` for the same mutation attempt. Error bodies currently use a top-level `detail`. Its value can be a string, a typed `{code,message}` object, or an array of validation details containing `type`, `loc`, `msg`, and optional `field_path`. Branch on HTTP status first, then use a typed code when present. The exact union, request, response, authentication, and header schemas are at https://presence.bohita.com/openapi.json. ## Privacy and retention The `presence_server` customer API key supports Presence drafts and publishing, custom role/guidance, knowledge, and tools, plus web, voice, meeting, and approved outbound-phone Sessions. Inbound phone remains carrier-originated; managed outbound phone requires Bohita approval. Use the public Presence API contract rather than inferring routes from the repository. - Camera and microphone use require participant permission. - Live media may be processed without being retained. - Recording and durable media are off by default. Retention requires Session-scoped authority and applicable participant consent. - Browser clients receive Session-scoped credentials, never API keys. - Treat transcripts, recordings, artifacts, and visible-screen content as private customer data unless an explicit sharing policy says otherwise. - The launch demo at presence.bohita.com is not recorded. Public API compatibility boundary: `/v1`. Public contract release: `v0.1.5`, commit `5d1d6f4d2276253dc3f037e0c83e3af4cc405a42`. - Changelog: https://presence.bohita.com/changelog.txt - Support: mailto:hello@bohita.com --- ```json { "components": { "schemas": { "HTTPValidationError": { "properties": { "detail": { "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail", "type": "array" } }, "title": "HTTPValidationError", "type": "object" }, "PublicPresenceArtifactOutput": { "additionalProperties": false, "description": "A small Presence artifact addressed by an opaque artifact reference.", "properties": { "artifact_id": { "title": "Artifact Id", "type": "string" }, "artifact_ref": { "title": "Artifact Ref", "type": "string" }, "kind": { "const": "artifact", "title": "Kind", "type": "string" }, "output_id": { "title": "Output Id", "type": "string" }, "renderer": { "title": "Renderer", "type": "string" }, "schema_version": { "title": "Schema Version", "type": "string" }, "shareable": { "title": "Shareable", "type": "boolean" } }, "required": [ "kind", "output_id", "artifact_id", "artifact_ref", "renderer", "schema_version", "shareable" ], "title": "PublicPresenceArtifactOutput", "type": "object" }, "PublicPresenceAudioFormat": { "additionalProperties": false, "properties": { "channels": { "const": 1, "default": 1, "title": "Channels", "type": "integer" }, "encoding": { "const": "pcm_s16le", "default": "pcm_s16le", "title": "Encoding", "type": "string" }, "sample_rate_hz": { "const": 16000, "default": 16000, "title": "Sample Rate Hz", "type": "integer" } }, "title": "PublicPresenceAudioFormat", "type": "object" }, "PublicPresenceDraftResponse": { "additionalProperties": true, "description": "Safe draft projection; Presence may add public definition fields.", "properties": { "event_subscriptions": { "anyOf": [ { "items": { "$ref": "#/components/schemas/PublicPresenceEventSubscriptionResponse" }, "type": "array" }, { "type": "null" } ], "title": "Event Subscriptions" }, "presence_id": { "title": "Presence Id", "type": "string" } }, "required": [ "presence_id" ], "title": "PublicPresenceDraftResponse", "type": "object" }, "PublicPresenceEventPageResponse": { "additionalProperties": false, "properties": { "events": { "items": { "additionalProperties": true, "type": "object" }, "title": "Events", "type": "array" }, "has_more": { "title": "Has More", "type": "boolean" }, "next_after": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Next After" } }, "required": [ "events", "has_more" ], "title": "PublicPresenceEventPageResponse", "type": "object" }, "PublicPresenceEventSubscriptionResponse": { "additionalProperties": false, "properties": { "events": { "items": { "type": "string" }, "title": "Events", "type": "array" }, "signing_secret": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Signing Secret" }, "type": { "const": "webhook", "default": "webhook", "title": "Type", "type": "string" }, "url": { "title": "Url", "type": "string" } }, "required": [ "url", "events" ], "title": "PublicPresenceEventSubscriptionResponse", "type": "object" }, "PublicPresenceOutputsResponse": { "additionalProperties": false, "examples": [ { "outputs": [ { "content": [], "kind": "transcript", "output_id": "transcript", "schema_version": "1" }, { "artifact_id": "meeting-notes", "artifact_ref": "urn:presence:artifact/...", "kind": "artifact", "output_id": "meeting-notes", "renderer": "presence.board", "schema_version": "1", "shareable": true }, { "kind": "recording", "manifest_asset_ref": "urn:rome:asset/...", "recording_id": "rec_...", "schema_version": "1" } ], "session_id": "psn_...", "state": "ended" } ], "properties": { "outputs": { "items": { "discriminator": { "mapping": { "artifact": "#/components/schemas/PublicPresenceArtifactOutput", "recording": "#/components/schemas/PublicPresenceRecordingOutput", "transcript": "#/components/schemas/PublicPresenceTranscriptOutput" }, "propertyName": "kind" }, "oneOf": [ { "$ref": "#/components/schemas/PublicPresenceTranscriptOutput" }, { "$ref": "#/components/schemas/PublicPresenceArtifactOutput" }, { "$ref": "#/components/schemas/PublicPresenceRecordingOutput" } ] }, "title": "Outputs", "type": "array" }, "session_id": { "title": "Session Id", "type": "string" }, "state": { "enum": [ "ready", "launching", "ended", "failed", "canceled", "expired" ], "title": "State", "type": "string" } }, "required": [ "session_id", "state", "outputs" ], "title": "PublicPresenceOutputsResponse", "type": "object" }, "PublicPresencePhoneResponse": { "additionalProperties": true, "properties": { "direction": { "enum": [ "inbound", "outbound" ], "title": "Direction", "type": "string" }, "state": { "title": "State", "type": "string" } }, "required": [ "direction", "state" ], "title": "PublicPresencePhoneResponse", "type": "object" }, "PublicPresenceRealtimeResponse": { "additionalProperties": false, "properties": { "input_audio": { "$ref": "#/components/schemas/PublicPresenceAudioFormat" }, "output_audio": { "$ref": "#/components/schemas/PublicPresenceAudioFormat" }, "protocol": { "const": "bohita.realtime.v1", "default": "bohita.realtime.v1", "title": "Protocol", "type": "string" }, "url": { "title": "Url", "type": "string" } }, "required": [ "url", "input_audio", "output_audio" ], "title": "PublicPresenceRealtimeResponse", "type": "object" }, "PublicPresenceRecordingOutput": { "additionalProperties": false, "description": "A retained recording manifest; media bytes are never returned here.", "properties": { "kind": { "const": "recording", "title": "Kind", "type": "string" }, "manifest_asset_ref": { "title": "Manifest Asset Ref", "type": "string" }, "recording_id": { "title": "Recording Id", "type": "string" }, "schema_version": { "title": "Schema Version", "type": "string" } }, "required": [ "kind", "recording_id", "schema_version", "manifest_asset_ref" ], "title": "PublicPresenceRecordingOutput", "type": "object" }, "PublicPresenceRecordingResponse": { "additionalProperties": false, "properties": { "available": { "title": "Available", "type": "boolean" }, "disclosure": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Disclosure" }, "policy_version": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Policy Version" } }, "required": [ "available" ], "title": "PublicPresenceRecordingResponse", "type": "object" }, "PublicPresenceSessionCreate": { "additionalProperties": false, "properties": { "external_id": { "anyOf": [ { "maxLength": 255, "type": "string" }, { "type": "null" } ], "title": "External Id" }, "knowledge": { "anyOf": [ { "items": { "type": "string" }, "maxItems": 20, "type": "array" }, { "type": "null" } ], "title": "Knowledge" }, "knowledge_use_case": { "anyOf": [ { "maxLength": 4000, "minLength": 3, "type": "string" }, { "type": "null" } ], "title": "Knowledge Use Case" }, "presence_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Presence Id" }, "requested_max_seconds": { "anyOf": [ { "maximum": 86400, "minimum": 1, "type": "integer" }, { "type": "null" } ], "title": "Requested Max Seconds" }, "startup": { "default": "managed", "enum": [ "managed", "host" ], "title": "Startup", "type": "string" }, "surface": { "$ref": "#/components/schemas/PublicPresenceSurface" }, "version": { "anyOf": [ { "type": "integer" }, { "const": "latest", "type": "string" }, { "type": "null" } ], "default": "latest", "title": "Version" } }, "title": "PublicPresenceSessionCreate", "type": "object" }, "PublicPresenceSessionEnd": { "additionalProperties": false, "properties": { "reason": { "default": "customer_ended", "maxLength": 100, "title": "Reason", "type": "string" } }, "title": "PublicPresenceSessionEnd", "type": "object" }, "PublicPresenceSessionResponse": { "additionalProperties": false, "properties": { "created_at_unix": { "title": "Created At Unix", "type": "number" }, "embed_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Embed Url" }, "expires_at_unix": { "title": "Expires At Unix", "type": "integer" }, "external_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "External Id" }, "max_seconds": { "minimum": 1, "title": "Max Seconds", "type": "integer" }, "phone": { "anyOf": [ { "$ref": "#/components/schemas/PublicPresencePhoneResponse" }, { "type": "null" } ] }, "presence_id": { "title": "Presence Id", "type": "string" }, "realtime": { "anyOf": [ { "$ref": "#/components/schemas/PublicPresenceRealtimeResponse" }, { "type": "null" } ] }, "recording": { "anyOf": [ { "$ref": "#/components/schemas/PublicPresenceRecordingResponse" }, { "type": "null" } ] }, "replayed": { "title": "Replayed", "type": "boolean" }, "session_id": { "title": "Session Id", "type": "string" }, "state": { "enum": [ "ready", "launching", "ended", "failed", "canceled", "expired" ], "title": "State", "type": "string" }, "surface": { "additionalProperties": true, "title": "Surface", "type": "object" }, "version": { "minimum": 1, "title": "Version", "type": "integer" } }, "required": [ "session_id", "presence_id", "version", "surface", "state", "expires_at_unix", "max_seconds", "replayed", "created_at_unix" ], "title": "PublicPresenceSessionResponse", "type": "object" }, "PublicPresenceSurface": { "additionalProperties": false, "properties": { "direction": { "anyOf": [ { "enum": [ "inbound", "outbound" ], "type": "string" }, { "type": "null" } ], "title": "Direction" }, "meeting_url": { "anyOf": [ { "format": "uri", "minLength": 1, "type": "string" }, { "type": "null" } ], "title": "Meeting Url" }, "to": { "anyOf": [ { "maxLength": 50, "type": "string" }, { "type": "null" } ], "title": "To" }, "type": { "default": "web", "enum": [ "web", "voice", "meeting", "phone" ], "title": "Type", "type": "string" } }, "title": "PublicPresenceSurface", "type": "object" }, "PublicPresenceTranscriptOutput": { "additionalProperties": false, "description": "A retained transcript projected into the Session output manifest.", "properties": { "content": { "items": { "additionalProperties": true, "type": "object" }, "title": "Content", "type": "array" }, "kind": { "const": "transcript", "title": "Kind", "type": "string" }, "output_id": { "title": "Output Id", "type": "string" }, "schema_version": { "title": "Schema Version", "type": "string" } }, "required": [ "kind", "output_id", "schema_version", "content" ], "title": "PublicPresenceTranscriptOutput", "type": "object" }, "PublicPresenceVersionResponse": { "additionalProperties": true, "description": "Safe immutable-version projection with private compilation fields removed.", "properties": { "created_at_unix": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Created At Unix" }, "definition": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "title": "Definition" }, "event_subscriptions": { "anyOf": [ { "items": { "$ref": "#/components/schemas/PublicPresenceEventSubscriptionResponse" }, "type": "array" }, { "type": "null" } ], "title": "Event Subscriptions" }, "presence_id": { "title": "Presence Id", "type": "string" }, "version": { "minimum": 1, "title": "Version", "type": "integer" } }, "required": [ "presence_id", "version" ], "title": "PublicPresenceVersionResponse", "type": "object" }, "ValidationError": { "properties": { "ctx": { "title": "Context", "type": "object" }, "input": { "title": "Input" }, "loc": { "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location", "type": "array" }, "msg": { "title": "Message", "type": "string" }, "type": { "title": "Error Type", "type": "string" } }, "required": [ "loc", "msg", "type" ], "title": "ValidationError", "type": "object" } }, "securitySchemes": { "PresenceApiKey": { "bearerFormat": "Bohita Presence API key", "description": "Customer-owned Presence API key.", "scheme": "bearer", "type": "http" } } }, "info": { "description": "Customer-facing Presence authoring and Session API. A customer Presence API key may author custom definitions, authenticated tool webhooks, and use web, voice, meeting, and approved managed outbound-phone Sessions; inbound phone is carrier-originated and managed outbound phone requires Bohita approval.", "title": "Bohita Presence API", "version": "1.0.0" }, "openapi": "3.1.0", "paths": { "/v1/presences": { "post": { "operationId": "create_presence_v1_presences_post", "parameters": [ { "in": "header", "name": "Idempotency-Key", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Idempotency-Key" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "requestBody": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Request", "type": "object" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceDraftResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Create Presence", "tags": [ "presence-public-api" ] } }, "/v1/presences/{presence_id}": { "get": { "operationId": "get_presence_v1_presences__presence_id__get", "parameters": [ { "in": "path", "name": "presence_id", "required": true, "schema": { "title": "Presence Id", "type": "string" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceDraftResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Presence", "tags": [ "presence-public-api" ] }, "patch": { "operationId": "update_presence_v1_presences__presence_id__patch", "parameters": [ { "in": "path", "name": "presence_id", "required": true, "schema": { "title": "Presence Id", "type": "string" } }, { "in": "header", "name": "Idempotency-Key", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Idempotency-Key" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "requestBody": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Request", "type": "object" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceDraftResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Update Presence", "tags": [ "presence-public-api" ] } }, "/v1/presences/{presence_id}/publish": { "post": { "operationId": "publish_presence_v1_presences__presence_id__publish_post", "parameters": [ { "in": "path", "name": "presence_id", "required": true, "schema": { "title": "Presence Id", "type": "string" } }, { "in": "header", "name": "Idempotency-Key", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Idempotency-Key" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceVersionResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Publish Presence", "tags": [ "presence-public-api" ] } }, "/v1/presences/{presence_id}/versions/{version}": { "get": { "operationId": "get_presence_version_v1_presences__presence_id__versions__version__get", "parameters": [ { "in": "path", "name": "presence_id", "required": true, "schema": { "title": "Presence Id", "type": "string" } }, { "in": "path", "name": "version", "required": true, "schema": { "title": "Version", "type": "integer" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceVersionResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Presence Version", "tags": [ "presence-public-api" ] } }, "/v1/sessions": { "post": { "operationId": "create_presence_session_v1_sessions_post", "parameters": [ { "in": "header", "name": "Idempotency-Key", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Idempotency-Key" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceSessionCreate" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceSessionResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Create Presence Session", "tags": [ "presence-public-api" ] } }, "/v1/sessions/{session_id}": { "get": { "operationId": "get_presence_session_v1_sessions__session_id__get", "parameters": [ { "in": "path", "name": "session_id", "required": true, "schema": { "title": "Session Id", "type": "string" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceSessionResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Presence Session", "tags": [ "presence-public-api" ] } }, "/v1/sessions/{session_id}/end": { "post": { "operationId": "end_presence_session_v1_sessions__session_id__end_post", "parameters": [ { "in": "path", "name": "session_id", "required": true, "schema": { "title": "Session Id", "type": "string" } }, { "in": "header", "name": "Idempotency-Key", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Idempotency-Key" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "requestBody": { "content": { "application/json": { "schema": { "anyOf": [ { "$ref": "#/components/schemas/PublicPresenceSessionEnd" }, { "type": "null" } ], "title": "Request" } } } }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceSessionResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "End Presence Session", "tags": [ "presence-public-api" ] } }, "/v1/sessions/{session_id}/events": { "get": { "operationId": "get_presence_session_events_v1_sessions__session_id__events_get", "parameters": [ { "in": "path", "name": "session_id", "required": true, "schema": { "title": "Session Id", "type": "string" } }, { "in": "query", "name": "after", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "After" } }, { "in": "query", "name": "limit", "required": false, "schema": { "default": 100, "maximum": 500, "minimum": 1, "title": "Limit", "type": "integer" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceEventPageResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Presence Session Events", "tags": [ "presence-public-api" ] } }, "/v1/sessions/{session_id}/outputs": { "get": { "operationId": "get_presence_session_outputs_v1_sessions__session_id__outputs_get", "parameters": [ { "in": "path", "name": "session_id", "required": true, "schema": { "title": "Session Id", "type": "string" } }, { "in": "header", "name": "Authorization", "required": true, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Authorization" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPresenceOutputsResponse" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Presence Session Outputs", "tags": [ "presence-public-api" ] } } }, "security": [ { "PresenceApiKey": [] } ], "servers": [ { "url": "https://api.bohita.com" } ] } ``` --- # Presence API capability availability This document distinguishes the complete Presence product model from the capabilities currently granted to a customer API key. ## Public now A verified customer can create a `bohita_sk_live_*` API key and use it against the public Presence contract. The `presence_server` customer key supports Presence drafts and publishing, custom role/guidance, knowledge, and tools, plus web, voice, meeting, and approved outbound-phone Sessions. The public web path includes: - Creating, reading, and ending a Session - Mounting the returned opaque `embed_url` - Managed or host-observed startup - Session readiness and bounded pre-ready replacement - Connected-time accounting and explicit lifetime semantics - In-frame artifact rendering - Explicit artifact sharing when sharing is enabled for the deployment Install it: https://presence.bohita.com/llms/install-web.txt ### Function calling and durable events Customer tools use OpenAI-style function declarations and a server-side webhook configured with Bohita. Bohita stores the destination and owns signing, delivery, retry, audit, and deduplication. Verify the webhook signature and process each delivery idempotently; the live Presence runtime never receives the destination URL or its secret. ## Browser-only production SPAs The secret API key cannot be shipped in a browser bundle. External production SPAs therefore need a BFF, serverless function, server, desktop host, or other trusted process to create Sessions. A purely static external SPA with no such boundary requires a browser-safe, origin-scoped bootstrap credential that is not currently self-serve. Bohita's tenant BFF is not automatically present on an arbitrary customer domain. This is a hosting-topology constraint, not a framework constraint. Vite and other static frontends work when they call a trusted Session-creation boundary. ## Enabled only when projected for the deployment Artifact sharing requires Bohita to enable the share-grant capability. The iframe may request a share, but the authenticated host grants the durable permalink. Do not invent a share endpoint or construct a public URL from an artifact reference. The host-message startup contract is also enablement-dependent. Use managed startup unless the deployment explicitly supports host-observed readiness. No support intervention is required for ordinary browser authentication or Session creation. The host obtains a fresh Firebase ID token for each mounted BFF call. Customer server integrations authenticate with their existing API key. Neither credential refreshes an expired `embed_url`; the same BFF or public Session API creates a new Session when the user deliberately continues. ## Gated or separately originated Inbound phone Sessions remain carrier-originated and require the authorized number-to-tenant mapping. Managed outbound phone requires Bohita approval (or an approved bring-your-own-provider path). Do not call private controller, carrier, or runtime endpoints directly; use the public Presence API. These capabilities are not an alternate token-refresh path. Do not construct a route for them from repository code. Product availability questions can go to: - mailto:hello@bohita.com --- # Evaluating Bohita Presence Evaluating Presence rather than integrating it? - Product and live demo: https://presence.bohita.com/ - Product trials and onboarding: https://presence.bohita.com/try-your-product - Benchmarks: https://presence.bohita.com/benchmarks - Coding agents: https://presence.bohita.com/coding-agents - Meetings: https://presence.bohita.com/use-cases/meetings - Security and privacy: https://presence.bohita.com/security - Pricing: https://presence.bohita.com/#pricing Installing Presence instead? - https://presence.bohita.com/llms.txt --- # Every AI you’ve built is *blind.* Yours *doesn’t* have to be AI shouldn’t need another prompt to know something changed Presence ambiently notices confusion, absence, attention changes, and shared visual context, then responds without interrupting work. Try Presence free [Talk to the founder](https://presence.bohita.com/#book) 90 seconds · camera on · Google sign-in Image: A stylized woman working quietly at a table in a warm room She paused. Looked away. Sighed. **It noticed** You’ve seen it notice. [Talk to Bohita](https://presence.bohita.com/#book) Try it free Product trials and onboarding ## Sit with everyone who tries your product Your best prospects get a founder watching them install, stepping in at the right moment, and learning what made the product difficult. Presence makes that available to everyone. [See how product trials work](https://presence.bohita.com/try-your-product) After the Session 01 **Where they got stuck** 02 **What it helped with** 03 **What it left behind** ## It sees the person, not just the screen Notices when someone goes quiet, looks away, or gets stuck. Then it speaks without being asked. ## It keeps talking while it thinks Slow tools and deep reasoning run in the background. No dead air. ## Long conversations don't get more expensive Minute 60 costs the same as minute 1. Meetings, debugging, onboarding, and support can keep going. Ambient interaction ## The prompt is not the only channel Presence notices what people don’t put into prompts. It knows when to help, wait, or keep working. [How Presence works](https://presence.bohita.com/#method) You look confused **It offers help** You step away **It stops expecting a response** You return **It knows you are back** You share something **It can react to what you are working on** Privacy, plainly ## It sees enough to notice Presence reads coarse signals such as present, looking away, screen shared, and engaged. It does not send a continuous video feed. When the reasoning model needs to look, it receives a low-resolution frame. Higher resolution is available when the task calls for it. Frames are not retained in later conversation history. The launch demo is not recorded. Other sessions require separate, session-specific consent before recording. [Read security and privacy](https://presence.bohita.com/security) You are building… Choose one to see the setup. ### Onboarding or support It steps in when someone gets stuck, then reports where they got stuck, what it helped with, and what it left behind. You’d write “Help when someone gets stuck. Do not wait to be asked.” What comes back Where they got stuck, what Presence helped with, and what it left behind. [See the workflow](https://presence.bohita.com/try-your-product) ### An AI product that needs a face Drop a character into your app that sees your user and reacts. You’d write “You are attentive, concise, and present.” ``` POST /v1/sessions { "external_id": "your-user-id", "surface": { "type": "web" }, "startup": "managed" } ``` [Build this](https://presence.bohita.com/#integrate) ### Something that joins meetings Same agent in Teams, Meet, or Zoom. You’d write “Follow the meeting. Speak only when you can move it forward.” What you provide A Teams, Meet, or Zoom meeting URL. Presence joins with the same prompt and tools. [Build this](https://presence.bohita.com/#integrate) ### Something that calls people Approved outbound calls with the same agent. You’d write “Keep the call warm, brief, and useful.” What you configure Choose an approved outbound call path, then choose who Presence should call. Inbound calls remain carrier-originated. [Build this](https://presence.bohita.com/#integrate) One URL. One iframe ## Put Presence in your product [Get your API key](https://presence.bohita.com/account/api-keys) Buy 60 minutes for $9 WEB · THE WHOLE CLIENT INTEGRATION Copy ```