Actor scopes
The CapxulActorScope argument that points address-book, requests, inbox, and insights hooks at either the personal account or one organization.
A CapxulActorScope names who is acting: the signed-in user's personal
account, or one organization. Four hook families operate on relationship state
that exists for both kinds of actor — the
Address book,
Payment requests,
Inbox, and
Insights families — so
instead of parallel personal/org hook sets, every hook in those families takes
a scope as its first argument. The same component works unchanged whether
it renders for a person or for an org.
Import
import {
capxulAccountScope,
capxulOrgScope,
type CapxulActorScope,
} from "@capxul/sdk-react";The scope type and helpers
type CapxulActorScope =
| { readonly kind: "account" }
| { readonly kind: "org"; readonly orgId: string };capxulAccountScope— the shared{ kind: "account" }constant for the signed-in user's personal account.capxulOrgScope(orgId)— builds{ kind: "org", orgId }. It is undefined-safe: whileorgIdisundefined(for example, still loading fromuseCapxulOrgs) it returnsundefinedinstead of a broken scope, so you can passcapxulOrgScope(orgs.data?.[0]?.orgId)straight through.
The core SDK exposes the same shape as ActorScopeRef; the React scope maps
onto it one-to-one.
Usage
"use client";
import {
capxulAccountScope,
capxulOrgScope,
useCapxulInbox,
useCapxulOrgs,
type CapxulActorScope,
} from "@capxul/sdk-react";
function InboxBadge({ actor }: { actor: CapxulActorScope | undefined }) {
const inbox = useCapxulInbox(actor);
const open = inbox.data?.filter((item) => item.status === "open") ?? [];
return <span>{open.length}</span>;
}
function WorkspaceNav() {
const orgs = useCapxulOrgs();
return (
<nav>
Personal <InboxBadge actor={capxulAccountScope} />
Studio <InboxBadge actor={capxulOrgScope(orgs.data?.[0]?.orgId)} />
</nav>
);
}InboxBadge never knows which kind of actor it renders for — that is the
point of the scope argument.
How hooks treat the scope
Query hooks (useCapxulAddressBook, useCapxulRequests,
useCapxulInbox, useCapxulInsightsSummary, …) accept
CapxulActorScope | undefined and stay disabled while the scope is
undefined. That makes org ids from other queries safe to pass directly — no
manual enabled gating.
Mutation hooks (useCapxulAddAddressBookEntry, useCapxulIssueRequest,
useCapxulApproveInboxRequest, …) default the scope to capxulAccountScope
when it is omitted.
Org mutations need a loaded org id
The mutation scope is a default parameter, so passing undefined explicitly
also falls back to the personal scope — and capxulOrgScope(orgId) returns
undefined while orgId is still loading. Keep org-scoped actions disabled
until the org id is present, or the mutation will act on the personal account.
Query keys are scoped per actor
Every query in these families is namespaced under the actor:
["capxul", "actor", "account", ...] // personal scope
["capxul", "actor", "org", orgId, ...] // one organization
["capxul", "actor", "pending", ...] // scope not known yet (query disabled)Caches never bleed across actors: approving a request as an org invalidates that org's inbox, requests, and insights — your personal queries (and other orgs') are untouched. Each hook page documents its exact key.
Client mapping
On the core SDK the scope picks which actor bundle a call routes to:
| Scope | Client surface |
|---|---|
capxulAccountScope | client.account.* |
capxulOrgScope(orgId) | client.org(orgId).* |
Both bundles expose the same relationship surface — addressBook,
requests, inbox, and insights — see the client reference.