# Expense and spend tools

Use this recipe when a finance or operations workflow needs clean people,
employment, department, cost-centre, legal-entity, and leaver context from
HollyHR while a specialist tool owns expenses, cards, receipt capture,
reimbursements, VAT, and accounting reconciliation.

HollyHR is not a native expense-management product and this is not a first-party
connector. It is the launch-safe integration pattern for teams using tools such
as [Xero Expenses](https://www.xero.com/uk/accounting-software/claim-expenses/),
[FreeAgent](https://www.freeagent.com/features/expenses/),
[Dext](https://dext.com/business/product/manage-business-expenses),
[Pleo](https://www.pleo.io/en),
[Soldo](https://www.soldo.com/en-gb/), [Expensify](https://www.expensify.com/),
or [Zoho Expense](https://www.zoho.com/uk/expense/).

## What HollyHR should own

| Data                  | HollyHR source                                                 | Use in a spend tool                                                             |
| --------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Person identity       | `GET /people`, `GET /people/{personId}`                        | Create or match employee/user records.                                          |
| Work email            | `people:read`                                                  | Primary matching key when the spend tool also keys users by work email.         |
| Employment status     | `status`, `start_date`, `end_date`                             | Decide who can receive access or a card.                                        |
| Job title and manager | People detail and employment context                           | Approval-routing context where the spend tool supports it.                      |
| Departments           | `GET /org-units?type=department` and People projections        | Reporting and approval grouping.                                                |
| Cost centres          | `GET /org-units?type=cost_centre`                              | Accounting dimensions, budget ownership, and spend reporting.                   |
| Legal entities        | `GET /org-units?type=legal_entity`                             | Entity-aware accounting setup; not payroll registration data.                   |
| Provider ids          | Org-unit provider identity fields and `GET /provider-mappings` | Reconcile HollyHR resources with downstream ids without overloading `staff_id`. |

## What the spend tool should own

- expense claims, mileage, receipt capture, OCR, and evidence retention;
- card issuance, spend limits, merchant rules, subscriptions, and transaction
  feeds;
- reimbursement workflow and payment status;
- VAT, MTD, ledger posting, account codes, and bank reconciliation;
- expense-policy approval semantics and exception handling;
- spend analytics and finance-close reporting.

Do not store card numbers, bank details, receipt images, reimbursable amounts,
merchant data, or accounting-ledger payloads in HollyHR custom fields.

## Recommended scopes

Create the smallest API key that fits the sync:

```text
people:read org_units:read provider_mappings:read webhooks:manage
```

Add `people:write` or `org_units:write` only if your integration creates safe
setup data in HollyHR. Do not add `people:personal:read`,
`payroll_exports:read`, payroll-financial grants, custom sensitive fields, or
document scopes for a spend-tool user lifecycle sync.

`provider_mappings:read` also requires the matching resource read scope. For
example, reading org-unit mappings requires `provider_mappings:read` and
`org_units:read`.

## Readiness check

Call `GET /integration-readiness` before building or activating a spend sync.
The endpoint gives one aggregate answer for whether HollyHR is ready to act as
the HR source of truth for tools such as Xero Expenses, FreeAgent, Dext, Pleo,
and Soldo.

The response includes provider cards with `native_connector: false`, because
v1 is a readiness/playbook/API handoff, not a first-party connector. Treat these
statuses as operational setup guidance:

- `not_configured`: fix blockers first, especially archived members without an
  employment end date;
- `needs_attention`: the handoff can be useful, but cost-centres, departments,
  or webhook health need review;
- `ready`: HollyHR has the people, org-structure, and webhook posture expected
  for that provider pattern.

For Pleo and Soldo-style card tools, `person.ended` webhook coverage matters:
that is the event a downstream system needs to deactivate a leaver. Keep a
scheduled reconciliation job as a backstop even when webhooks are healthy.

## Initial sync

1. Fetch people with `GET /people?limit=100`.
2. Store HollyHR `person_id` as the stable source id.
3. Store work email as the human-readable matching key.
4. Fetch departments, cost centres, and legal entities with `GET /org-units`.
5. Fetch `GET /people/{personId}/org-links` when the downstream tool supports
   multiple memberships or needs cost-centre/legal-entity context.
6. Store downstream ids in the downstream tool, or use HollyHR provider-mapping
   reads where mappings already exist. Do not repurpose `staff_id` as a
   connector id.

Use cursor pagination and `updated_since` for repeat syncs.

## Change handling

Subscribe to these events where the current event source covers your workflow:

```text
person.created
person.updated
person.ended
person.reactivated
org_unit.created
org_unit.updated
org_unit.archived
org_unit.reactivated
```

Webhook deliveries are notifications, not the integration's database. Verify
the signature, deduplicate by `HollyHR-Webhook-Id`, then fetch current state
through REST before updating the spend tool.

Current source coverage is mixed:

- public API writes emit the matching public webhook events for implemented
  people, employment, org-unit, time-off, working-pattern, and time-off
  configuration writes;
- in-app mutations emit `source: "app"` events for person creation/person
  profile updates and time-off create/update/cancel flows wired through the
  domain-event spine;
- some UI-origin paths, including access-removal/offboarding without a recorded
  employment end date, still need incremental `updated_since` polling or a
  follow-up product slice before they can be treated as real-time card-disable
  automation.

For Pleo or Soldo-style company-card tools, run a daily reconciliation job even
if you also subscribe to webhooks. A leaver should lose spend access based on a
recorded end date or inactive status, not solely on a single webhook delivery.

## Provider patterns

| Tool type                  | Examples                 | Practical HollyHR role                                                                                                  |
| -------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| Accounting-native expenses | Xero Expenses, FreeAgent | Provide employee, department, cost-centre, and legal-entity context; the accounting product owns claims and posting.    |
| Receipt/accountant capture | Dext                     | Provide who-submitted and cost-centre context for accountant workflows; Dext owns receipt capture and extraction.       |
| Card-first spend control   | Pleo, Soldo              | Provide joiner/mover/leaver and cost-centre context; the spend platform owns cards, limits, receipts, and transactions. |
| Dedicated T&E              | Expensify, Zoho Expense  | Provide HR identity/org context; the T&E platform owns travel, claim workflow, policy, and reimbursement.               |

## Boundaries

- This recipe does not mean HollyHR has a native connector to any named spend
  product.
- HollyHR does not expose an expenses API, card API, receipt API, reimbursement
  API, or accounting-ledger API.
- Home address, date of birth, government identifiers, compensation, bank
  details, document bytes, and payroll export grants are not needed for this
  pattern.
- Legal-entity org units are structural context only. They are not payroll or
  Companies House registration records.
- Native connector work should be demand-gated by a named buyer, bureau,
  accountant, or provider partner.

## Demand gate for a native connector

Open a connector/product slice only when a named opportunity can answer:

1. Which spend tool is the buyer actually using?
2. Which data must move that cannot be handled by API, webhooks,
   `updated_since`, or a no-code tool?
3. Who owns support when a card is not disabled, a cost centre is wrong, or a
   claim posts to the wrong ledger account?
4. Which sensitive fields cross the boundary, and which scopes/audit/DSAR rules
   cover them?
5. Is this a one-customer convenience or a repeatable channel bet?

Until those answers are strong, keep expenses in the specialist system and use
HollyHR as the clean HR source of truth.
