> ## Documentation Index
> Fetch the complete documentation index at: https://docs.andteam.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Making changes

> A write is a request like any other: it meets the same locks, approvals, audit and notifications as the console.

The API can change things: personal and contact details, job changes, pay changes and leave
requests. Every write calls the same code the andteam console calls. So the tenant's HRIS locks,
approval chains, the self-approval guard, notifications and audit apply, because they are already in
the path. There is no flag to skip approval.

## What a key needs

* **A Write switch** on the field group the write changes. Write implies Read. Write exists for
  Personal contact, Personal details, Job history, Pay and Leave, and for Hires, which has a Write switch
  only. A key with the Read switch alone answers [`write-not-allowed`](https://docs.andteam.com/api/problems/write-not-allowed).
  See [Field groups](https://docs.andteam.com/concepts/field-groups.md).
* **A named requester.** Every write key names a console user who holds the permission for each write
  the key may make, for example requesting a pay change. Approval chains that route to "the requester's
  manager" start from this person. Nobody approves their own request, so the requester can never approve
  what the key files.
* **`requestedBy`, optionally.** A request body may name another requester: an andteam user id or work
  email, such as the hiring manager in your ATS. That person is checked for the same permission over
  that employee. If the check fails the request is refused with
  [`requester-not-allowed`](https://docs.andteam.com/api/problems/requester-not-allowed). It is never put back on the key's
  requester.

If the requester loses the permission or leaves, the key's writes answer `requester-not-allowed` and its
reads keep working.

## 202 or 201

A write never approves itself.

| Answer | When | What happened |
| - | - | - |
| **202** | The tenant has an approval chain for the action | The request waits. `status` is `awaiting_approval` and `steps` lists the chain. Nothing is written until the last step approves. |
| **201** | The tenant has no chain for the action | The change is applied at once, as it is in the console. A date today or earlier is `applied`, a later date is `scheduled`. |

With a chain, a `*.decided` webhook is sent when the chain ends or the request is withdrawn:
`job_change.decided`, `pay_change.decided` or `leave_request.decided`. Its `data.requestedVia.keyId`
is the key that filed the request, so you can match a decision to your own call. See
[Webhooks](https://docs.andteam.com/guides/webhooks.md) and [Employee events](https://docs.andteam.com/concepts/employee-events.md).

## Everything the console does

Because a write is the console's own request:

* **HRIS locks.** A field the tenant's HRIS owns for this person is refused with
  [`field-locked`](https://docs.andteam.com/api/problems/field-locked), which names every locked field. Nothing is written.
  Correct the field in the HRIS, which owns the record.
* **Approval chains.** The tenant's chain for the action decides. A step that resolves to nobody is
  [`approval-chain-unresolvable`](https://docs.andteam.com/api/problems/approval-chain-unresolvable).
* **Audit.** The employee's history and the request's approval history record the key as the actor,
  with the requester it acted for. Every request also has its own row in the tenant's API audit.
* **Notifications.** The employee is told what changed or what was requested for them, and by which
  system.
* **Letters.** The API never sends a letter. After an approved job or pay change, HR sees a letter to
  send in andteam.

## Check before you send

Every POST that creates something takes `?validate_only=true`. It answers **200** with what would
happen and writes nothing: the approval steps and who they resolve to, the knock-ons ("3 reports also
move") and, for leave, the balance after the request.

```bash theme={null}
curl -X POST "https://api.v3.andteam.com/v1/employees/emp_3kTq…/leave-requests?validate_only=true" \
  -H "Authorization: Bearer atk_live_…" \
  -H "Idempotency-Key: 6a2f6a5e-0c1e-4c0b-9b53-1a52c6f3d2aa" \
  -H "Content-Type: application/json" \
  -d '{ "leaveTypeId": "annual", "startDate": "2027-04-12", "endDate": "2027-04-16" }'
```

## Every write needs two headers

A POST needs an `Idempotency-Key`. A PATCH needs `If-Match`. See [Retries and versions](https://docs.andteam.com/guides/idempotency.md).

A body that is wrong is one 422 [`validation-failed`](https://docs.andteam.com/api/problems/validation-failed). Its `errors`
lists every field at once, each with a `pointer`, a `code` and a `detail`, so you fix them in one
round.

## Change personal details

`PATCH /v1/employees/{id}` is a JSON Merge Patch (RFC 7396) of `preferredName`, `personalEmail`,
`mobile`, `homeAddress`, `dateOfBirth` and `nationality`. Each field needs the Write switch of its
group: Personal contact for `personalEmail`, `mobile` and `homeAddress`, Personal details for the
rest. Read the employee first and send its `ETag` as `If-Match`.

```bash theme={null}
curl -X PATCH "https://api.v3.andteam.com/v1/employees/emp_3kTq…" \
  -H "Authorization: Bearer atk_live_…" \
  -H "If-Match: \"2f9c1d\"" \
  -H "Content-Type: application/merge-patch+json" \
  -d '{ "mobile": "+27 82 555 0142", "homeAddress": { "suburb": "Sandton" } }'
```

The answer is 200 with the employee and a new `ETag`. A part of `homeAddress` sent as `null` is removed;
a part left out is kept. Legal first and last names, work email, Directory and Positions fields and
Special categories are not writable.

## Request leave

`POST /v1/employees/{id}/leave-requests` submits leave in the employee's name through the leave approval
chain. The balance and per-request caps are checked as in the portal.

```json theme={null}
{
  "leaveTypeId": "annual",
  "startDate": "2027-04-12",
  "endDate": "2027-04-16",
  "note": "Family holiday"
}
```

The answer is 202 with `awaiting_approval`, or 201 with `approved` when the chain has no steps. A
refusal is 422 `validation-failed` with a code in `errors`, such as `insufficient-balance` or
`over-cap`. Sick leave is refused (`sick-leave-in-portal`): the sick note belongs in the portal. The
employee is told which system made the request and can withdraw it in the portal. `leave_request.submitted`
is sent.

## File a job change with pay

`POST /v1/employees/{id}/job-changes` files a `promotion`, `transfer` or `regrade` with an
`effectiveDate`. Send the facts that change, and a `pay` block when pay rides along. The pay change is
filed with the job change in one transaction, so there is never a promotion without its pay change. The
`pay` block needs the Write switch on Pay as well.

```json theme={null}
{
  "type": "promotion",
  "effectiveDate": "2027-05-01",
  "jobTitleId": "jt_senior_analyst",
  "gradeId": "gr_e2",
  "managerId": "emp_8rWm…",
  "pay": {
    "components": [{ "componentId": "base", "values": { "amount": 52000, "frequency": "monthly" } }],
    "note": "Promotion to senior analyst"
  }
}
```

Corrections and changes of terms are made in andteam. The API answers 422 with the code
`not-supported`. A change already waiting for the person is
[`pending-change-exists`](https://docs.andteam.com/api/problems/pending-change-exists), with the `existingId`.

## File a pay change

`POST /v1/employees/{id}/pay-changes` takes a `kind` (`increase`, `promotion` or `adjustment`), an
`effectiveDate`, the whole package as `components[]`, and a `reason` kept as the change's note. The
package is checked against the tenant's compensation rules.

```json theme={null}
{
  "kind": "increase",
  "effectiveDate": "2027-03-01",
  "components": [{ "componentId": "base", "values": { "amount": 48000, "frequency": "monthly" } }],
  "reason": "Annual review"
}
```

A correction or a removal is made in andteam. A person in a protected group is not found unless the
key's Pay approval includes protected groups. See [Approvals](https://docs.andteam.com/concepts/approvals.md).

## Withdraw a request

`POST /v1/job-changes/{id}/withdraw`, `POST /v1/pay-changes/{id}/withdraw` and
`POST /v1/leave-requests/{id}/withdraw` take back a request while it waits. Only the key that filed it
may withdraw it. Anything else is [`not-withdrawable`](https://docs.andteam.com/api/problems/not-withdrawable). Each withdraw
needs an `Idempotency-Key`. The matching `*.decided` event is sent with `status: "withdrawn"`.

Read a request back at any time with `GET /v1/job-changes/{id}`, `GET /v1/pay-changes/{id}` or
`GET /v1/leave-requests/{id}`.

## New hires

An ATS starts a hire with `POST /v1/hires`. That is a draft offer for HR, not a starter. See
[New hires from an ATS](https://docs.andteam.com/guides/new-hires-from-an-ats.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.