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

# Process orders and HR decisions

> Retrieve orders under HR review, validate one final decision, reconcile timeouts, and download order documents.

The Public API exposes read access to orders and one HR decision operation for an order in `under_review`.

## Status model

The order schema can return:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
under_review
rejected
approved
active
archived
withdrawn
```

The public decision operation controls only the documented transition from `under_review` to `approved` or `rejected`. Other lifecycle transitions are server-managed or performed outside this operation.

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
stateDiagram-v2
    [*] --> under_review
    under_review --> approved: decision=approved
    under_review --> rejected: decision=rejected
    approved --> active: outside decision operation
    active --> archived: outside decision operation
    under_review --> withdrawn: outside decision operation
```

## HR review swimlane

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
sequenceDiagram
    participant Portal as JobHandy
    participant INT as HR integration
    participant HR as HR reviewer
    participant API as JobHandy API
    participant NOTIFY as Notification process

    Portal->>Portal: Order reaches under_review
    INT->>API: GET /orders?filter=status=under_review
    API-->>INT: Orders requiring review
    INT->>HR: Present employee, products, dates, and documents
    HR-->>INT: Approve or reject
    INT->>API: POST /orders/{id}/decision?dryRun=true
    API-->>INT: Projected order or validation error
    INT->>API: POST /orders/{id}/decision
    API->>NOTIFY: Trigger corresponding notifications
    API-->>INT: Updated order + request ID
```

The public contract confirms that approval sends corresponding notifications, but it does not enumerate recipients or delivery channels. Do not build customer logic that depends on an undocumented recipient list.

## Retrieve work

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --get 'https://api.jobhandy.io/v1/orders' \
  --header 'X-API-Key: YOUR_API_KEY' \
  --header 'X-Tenant-ID: {{tenantId}}' \
  --data-urlencode 'filter=status=under_review' \
  --data-urlencode 'sort=createdAt'
```

## Review current state

Immediately before a decision, call `GET /orders/{id}` and verify:

* status is still `under_review`
* the employee matches the expected record and the request uses the intended tenant context
* product model, manufacturer, article group, VAT, gross and net amounts, service amounts, and accessory classification match the review
* contract fields are acceptable
* required attachments are available

<Note>
  `vatRate`, `grossAmount`, `netAmount`, `grossService`, and `netService` are decimal strings. Preserve their precision and use an exact decimal type for calculations. `model`, `manufacturerName`, `articleGroupName`, `service`, `grossService`, and `netService` can be `null` as defined by the schema.
</Note>

<Info>
  Order responses no longer contain `tenant` or `divisionId`. HR review information is returned in `hrHistory`, and `decisionBy` is a nullable user ID rather than the previous actor-category value.
</Info>

## Validate and apply

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
POST /orders/{id}/decision?dryRun=true
Content-Type: application/json
```

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "decision": "approved"
}
```

After a successful projection, send the same request without `dryRun=true`.

## Concurrency and finality

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
flowchart TD
    A[Reviewer submits decision] --> B[Read current order]
    B --> C{status = under_review?}
    C -- No --> D[Reconcile existing final state]
    C -- Yes --> E[Dry run decision]
    E --> F[Apply decision]
    F --> G{Response received?}
    G -- Yes --> H[Store result]
    G -- Timeout --> I[Re-read order]
    I --> J{Desired decision present?}
    J -- Yes --> H
    J -- No --> K[Manual or controlled retry decision]
```

## Conflict handling

| Error                      | Meaning                                          | Action                            |
| -------------------------- | ------------------------------------------------ | --------------------------------- |
| `ORDER_ALREADY_DECIDED`    | A final HR decision already exists               | Re-read and reconcile             |
| `INVALID_STATE_TRANSITION` | Order is not eligible for the requested decision | Stop retries and refresh workflow |
| `EMPLOYEE_BLOCKED`         | Employee state prevents the operation            | Resolve employee/business state   |
| `409` after dry run        | State changed before persistence                 | Re-read the order                 |

## Download order documents

Use the attachment ID returned by an order:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
GET /orders/attachments/{id}
```

Inspect status and `Content-Type` before treating the response as a file. Retry transient download failures with bounded backoff.
