# Onboard a new staff member

**Create a new staff member, optionally assigning them to a rate card**
Creates a staff record and, when `rateCardAssignment` is provided, atomically
creates the corresponding billing rate and average cost records in the same
transaction.
**Required fields:**
- `firstName` — staff member's first name
- `lastName` — staff member's last name
- `pmsRef` — practice management system reference
- `ref` — unique staff reference code

**Rate card assignment** (`rateCardAssignment`):
When provided, the rate card must already exist. Each entry in `billingRates`
requires a valid `rateTypeId`. All three records (staff, billing rates, average
cost) are created atomically — if any write fails the entire request is rolled back.
- `rateCardId` — ID of an existing rate card (validated before any writes)
- `billingRates` — one record per rate type; each requires a `rateTypeId`
- `averageCost` — single average hourly cost for this staff member (optional)

**Real Example:**

```bash
curl --location 'https://{{host}}/api/v1/staff' \
--header 'Authorization: Bearer {{token}}' \
--header 'Content-Type: application/json' \
--data '{
  "firstName": "Jane",
  "lastName": "Doe",
  "pmsRef": "EMP-1042",
  "ref": "doe-j",
  "email": "jane.doe@firm.com",
  "jobTitle": "Senior Associate",
  "active": true,
  "rateCardAssignment": {
    "rateCardId": 5,
    "billingRates": [
      { "rateTypeId": 12, "hourlyBillingRate": 150.00 },
      { "rateTypeId": 13, "hourlyBillingRate": 175.00 }
    ],
    "averageCost": 75.00
  }
}'
```

Endpoint: POST /api/v1/staff
Version: 1.0.0
Security: BearerAuth

## Request fields (application/json):

  - `firstName` (string, required)
    Staff member's first name
    Example: Jane

  - `lastName` (string, required)
    Staff member's last name
    Example: Doe

  - `pmsRef` (string, required)
    Practice management system reference identifier
    Example: EMP-1042

  - `ref` (string, required)
    Unique staff reference code
    Example: doe-j

  - `email` (string)
    Primary email address
    Example: jane.doe@firm.com

  - `jobTitle` (string)
    Job title (free-text)
    Example: Senior Associate

  - `jobTitleId` (integer)
    ID of a job title lookup record
    Example: 7

  - `departmentId` (integer)
    ID of the department this staff member belongs to
    Example: 3

  - `officeId` (integer)
    ID of the office this staff member is based in
    Example: 2

  - `orgId` (integer)
    ID of the organization
    Example: 1

  - `serviceLineId` (integer)
    ID of the service line
    Example: 4

  - `staffTypeId` (integer)
    ID of the staff type (e.g. Partner, Manager, Associate)
    Example: 2

  - `partnerStaffId` (integer)
    ID of the partner staff member assigned to this person
    Example: 50

  - `managerStaffId` (integer)
    ID of the manager staff member
    Example: 30

  - `supervisorStaffId` (integer)
    ID of the supervisor staff member
    Example: null

  - `prefix` (string)
    Name prefix (e.g. Dr., Ms.)
    Example: Ms.

  - `startDate` (string)
    Employment start date
    Example: 2024-01-15T00:00:00

  - `endDate` (string)
    Employment end date
    Example: null

  - `active` (boolean)
    Whether the staff member is active
    Example: true

  - `rateCardAssignment` (object)
    Optional rate card assignment. When provided, billing rate and average
cost records are created atomically with the staff record.
`rateCardId` and all `rateTypeId` values must reference existing records
or the entire request is rejected with HTTP 400.

  - `rateCardAssignment.rateCardId` (integer, required)
    ID of an existing rate card to assign this staff member to
    Example: 5

  - `rateCardAssignment.billingRates` (array, required)
    One billing rate record per rate type. Can be an empty array if only an average cost is being set.

  - `rateCardAssignment.billingRates.rateTypeId` (integer, required)
    ID of an existing rate type
    Example: 12

  - `rateCardAssignment.billingRates.hourlyBillingRate` (number)
    Hourly billing rate in the firm's currency. Defaults to 0 when omitted.
    Example: 150

  - `rateCardAssignment.averageCost` (number)
    Average hourly cost for this staff member. One record per rate card assignment.
    Example: 75

## Response 201 fields (application/json):

  - `data` (object)

  - `data.id` (integer)
    Unique identifier of the created staff member
    Example: 312

  - `data.pmsRef` (string)
    Example: EMP-1042

  - `data.ref` (string)
    Example: doe-j

  - `data.firstName` (string)
    Example: Jane

  - `data.lastName` (string)
    Example: Doe

  - `data.fullName` (string)
    Example: Jane Doe

  - `data.email` (string)
    Example: jane.doe@firm.com

  - `data.jobTitle` (string)
    Example: Senior Associate

  - `data.departmentId` (integer)
    Example: null

  - `data.officeId` (integer)
    Example: null

  - `data.active` (boolean)
    Example: true

  - `data.version` (integer)
    Example: 1

  - `data.createdAt` (string)
    Example: 2024-03-15T10:00:00Z

  - `data.updatedAt` (string)
    Example: 2024-03-15T10:00:00Z

  - `data.rateCardAssignment` (object)
    Present only when a rate card assignment was provided in the request

  - `data.rateCardAssignment.rateCardId` (integer)
    ID of the rate card the staff member was assigned to
    Example: 5

  - `data.rateCardAssignment.billingRateIds` (array)
    IDs of the created rate_card_staff_billing_rate records
    Example: [101,102]

  - `data.rateCardAssignment.averageCostId` (integer)
    ID of the created rate_card_staff_average_cost record, or null if averageCost was not provided
    Example: 201

  - `meta` (object)

  - `meta.success` (boolean)
    Example: true

  - `meta.message` (string)
    Example: Staff member created successfully

  - `meta.timestamp` (string)
    Example: 2024-03-15T10:00:00Z

  - `meta.version` (string)
    Example: v1

