Skip to main content

Tenancy and workspaces

Identifiers you will see

IdentifierMeaning
tenant_id / organization_idCustomer organization (ACS often uses organization id as tenant scope for connections)
workspace_idAI / ops workspace within the org
connection_idExternal SDK Connection row id
installation_idManaged solution installation id
solution_idMarketplace / package id (e.g. restaurant-pro)
conversation_idChat / session
request_idProtocol / platform request id
trace_idDistributed trace (x-qefro-trace-id)
tool_invocation_id / execution_idRuntime execution headers (connector-manager)
identity / personEnd-user / Customer Hub person

Where they appear

Protocol body

organization_id, conversation_id, identity, person, platform.

Headers (connector-manager egress)

Examples: x-qefro-tenant-id, x-qefro-organization-id, x-qefro-workspace-id, x-qefro-request-id, x-qefro-conversation-id, x-qefro-trace-id, tool invocation / execution / idempotency headers.

platform.*.context

When injected:

// platform.storage.context
{
tenant_id, workspace_id, installation_id, solution_id,
identity_id?, capabilities[], source?
}

Customer / marketing / organization contexts use similar tenant/workspace/installation/solution fields.

External SDK connection vs managed install

ValueExternal SDK ConnectionManaged Marketplace App
Routing keysdk_connection_id on Business Tools; webhook URLInstallation binding (install:{solution})
connection_idYes (ACS connection)No (install-centric)
installation_idOnly if invoke scope provides itYes
solution_idOnly if invoke scope provides itYes
workspace_idFrom tool invoke auth context when presentFrom install / tenant context
platform.storageOmitted unless install/solution scope existsPresent when storage-service configured
Domain data defaultYour ERP/DBctx.storage collections

Implementation detail (ACS): build_platform_storage returns None when there is no installation_id and no non-empty solution_id, so pure org SDK tools fail closed on ctx.storage.

Isolation rules

  • Never trust client-supplied tenant ids without platform context.
  • Scope all writes using platform.storage.context (managed) or your own per-tenant credentials (external ERP).
  • Do not call other applications’ tools directly — use Organization workflows across apps.

Workspace entitlements (max_workspaces)

Workspace capacity is a platform / billing entitlement, not an application concern.

Tenant
→ Plan / subscription
→ entitlements.max_workspaces
→ Workspace Service (create check)
Planmax_workspaces
Trial1
Starter3
Pro10
Growth25
Enterprisecustom (workspaces_total quota) or unlimited

Active workspaces = rows in workspaces for the tenant. Hard-deleted workspaces no longer count.

Create path: lock tenant row → count workspaces → compare to entitlement → insert (or workspace_limit_reached).

Downgrade: existing workspaces are kept; over_limit is true and new creates are blocked until count ≤ limit or the plan is upgraded.

Applications must not implement restaurant-pro.maxWorkspaces (or similar). One primary application per workspace remains a separate workspace-level rule.

Portal reads usage from GET /api/v1/tenant/billing (entitlements / usage / workspace_entitlement).

User / execution context

  • identity — channel-resolved attributes (phone, email, …)
  • person — Hub Person snapshot when linked
  • authentication — prior SDK auth payload
  • channel — whatsapp / widget / …
  • trace_id on ctx.trace_id when x-qefro-trace-id is passed through handleRaw (default listen path does not forward headers into dispatch today — use body request_id for correlation)