Capxul Docs
HooksPayroll

useCapxulRunPayroll

Mutation hook that executes a payroll run — one payroll Payment per active roster line, for a given period.

Executes a payroll run: pays every "active" line on the organization's roster for one period, drawing from an org sub-account envelope. Unlike a Transfer, payroll is real money leaving the organization — each roster line becomes a Payment with paymentType: "payroll".

SDK-ready

Payroll is graded SDK-ready: the roster and run primitives are published and tested, but the scheduling / streaming payroll product experience is not SDK-complete yet. See Capability status.

Import

import { useCapxulRunPayroll } from "@capxul/sdk-react";

Usage

"use client";

import { useCapxulRunPayroll } from "@capxul/sdk-react";
import type { OrgId, SubAccountId } from "@capxul/sdk";

function RunPayrollButton({
  orgId,
  payrollEnvelopeId,
}: {
  orgId: OrgId;
  payrollEnvelopeId: SubAccountId;
}) {
  const runPayroll = useCapxulRunPayroll(orgId);

  return (
    <div>
      <button
        type="button"
        disabled={runPayroll.isPending}
        onClick={() => runPayroll.mutate({ from: payrollEnvelopeId, period: "2026-07" })}
      >
        {runPayroll.isPending ? "Running payroll…" : "Run July payroll"}
      </button>
      {runPayroll.data ? <p>{runPayroll.data.length} payments created</p> : null}
      {runPayroll.error ? <p>{runPayroll.error.message}</p> : null}
    </div>
  );
}

Parameters

orgId

OrgId | undefined

The organization running payroll. Unlike a query, a mutation cannot be disabled — firing it while orgId is still undefined rejects with a CapxulError.

The mutation takes a PayrollRunInput:

Prop

Type

from is the org sub-account envelope the run draws from. period names the pay period and must be a YYYY-MM string (for example "2026-07").

Re-running the same period with an unchanged roster is idempotent: instead of paying everyone twice, the run returns the payments already created for that period. Changing the roster (or the from envelope) makes it a new run.

Return type

import { type UseCapxulRunPayrollReturn } from "@capxul/sdk-react";
// UseMutationResult<readonly Payment[], CapxulError, PayrollRunInput>

On success, data is an array of Payment — one per active roster line, each with paymentType: "payroll" and the roster line's amount, recipient, and timing. Lines whose status is "paused" or "ended" are skipped.

The hook returns TanStack Query's UseMutationResult. The most-used fields:

FieldDescription
mutateFire the mutation (fire-and-forget; pair with onSuccess/onError callbacks).
mutateAsyncFire the mutation and get a Promise of the result. Rejects with a CapxulError on failure.
dataThe mutation result (typed per hook, shown above). undefined until the first success.
errorA CapxulError when the last attempt failed, otherwise null.
isPendingtrue while the mutation is in flight. Use it to disable submit buttons.
resetClear the mutation state (data, error) back to idle.

Mutations do not retry by default. The full field list is in the TanStack Query useMutation reference; see also the TanStack Query integration guide.

Cache behavior

On success this mutation invalidates the org's roster (["capxul", "org", orgId, "payroll", "roster"]) and the org's insights summary and history. Then, because a run creates real payments, it also invalidates the money state for each returned Payment: the payments list, that payment's detail, the org's treasury and org account, and the org's address book.

Errors

Failures surface as a CapxulError on error / thrown from mutateAsync — notable modes:

  • orgId is still undefined when the mutation fires.
  • period is not a YYYY-MM string.
  • The roster has no "active" lines — a run needs at least one.
  • The run's total exceeds the signed-in member's org spending cap. The whole run is rejected; there are no partial payments.
  • The mutation fires while <CapxulProvider> is still bootstrapping.

Client method

This hook wraps client.org(orgId).payroll.run(input) — the entity-scoped org bundle on the core SDK. (The lower-level client.org(orgId).batchPayroll(input) pays an ad-hoc list of recipients without a roster; see the client reference.)

On this page