Appearance
Limitations & known behaviors
Things that look like bugs to pedantic QA but are by design or known product limits. Share this with integrators before stand testing.
Realtime
| Expectation | Reality |
|---|---|
| WebSocket / live push into the SDK | HTTP polling only (default 4s; comments thread ~5s while open) |
| Instant operator reply | Appears within ~one poll interval after webhook + cache invalidate |
| Real “operator typing…” | Not available from Intercom. Mock mode fakes it; live mode may show a short local cue after creating a request — not presence |
| Real read receipts | Inferred: double-check when an operator message follows yours |
See Behavior.
Product placeholders
| UI | Status |
|---|---|
| Hub Archive | Coming soon screen — not implemented |
| Full offline mode (RN) | Optional @react-native-community/netinfo; without it the SDK backs off polls |
Storage split (common confusion)
- Support messages → Intercom (not Postgres message rows).
- Channel comments → Postgres only.
- Closing the app clears SDK memory; history is reloaded from the server next open.
Full matrix: Data model.
Scale & ops
- Single API instance: rate limit, admin lockout, Intercom cache, and schedulers are in-memory. Multi-replica needs Redis + a single publisher for scheduled posts — Deployment.
- Intercom workspace rate limits (~10k API calls/min) are the real capacity ceiling for chat polling.
- Circuit breaker: after repeated Intercom failures the API fails fast (502) briefly, then probes again.
- Idempotent send + Intercom timeout: if the upstream outcome is unknown, the incomplete claim is kept (~24h,
IDEMPOTENCY_INCOMPLETE_TTL_MS) so a retry with the sameIdempotency-Key/ client id returns 409 instead of duplicating a message. Waiter for an in-flight twin is ~2s. Tradeoff: no duplicates, but a stuck send can feel “stuck” until TTL or a new key — SDK should surface “may already be sent” on that 409. DATA_RETENTION_DAYSprunes analytics/audit only (post_button_clicks,admin_logs). Complaints, CSAT, and moderation tables are not auto-purged.
Client / host footguns
| Issue | Fix |
|---|---|
| RN attach does nothing | Pass onPickFiles — without it the attach control is hidden |
| Files vanish after redeploy | Set S3 (REQUIRE_S3=1 in production); don’t rely on container disk |
| 401 / empty chat after login | Mint JWT with shared JWT_SECRET; handle onSessionExpired |
| Wrong user’s channel/comments after account switch | Remount <AWChat /> (or change userId/sessionToken so the store re-inits) |
| CORS errors in browser | Set CORS_ORIGINS to the host app (+ admin) origins |
Security / staging
- Leave
ENABLE_DEVTOOLS/ENABLE_DEVTOOLS_RESET_ALLempty in any shared or production environment. - Do not commit demo JWTs. Pass Live credentials via
?token=/VITE_DEMO_TOKENSonly on local stands. - Signed file URLs (
/files/:id?t=…) are bearer-like until expiry; don’t paste sensitive attachments into public channels.
What we do support
- Adaptive poll back-off, localized error panels (never a blank screen).
- Magic-byte upload allow-list; comment DLP / link blocking.
- Admin permissions, audit log, comment moderation.
- FCM + Telegram push when configured.
For threat-oriented detail see Security.