Capxul Docs
Hooks

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: while orgId is undefined (for example, still loading from useCapxulOrgs) it returns undefined instead of a broken scope, so you can pass capxulOrgScope(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:

ScopeClient surface
capxulAccountScopeclient.account.*
capxulOrgScope(orgId)client.org(orgId).*

Both bundles expose the same relationship surface — addressBook, requests, inbox, and insights — see the client reference.

On this page