---
title: "Update Mob"
method: PATCH
path: "/mobs/{id}"
tags: ["Mobs"]
---

# Update Mob

`PATCH /mobs/{id}`

## Request

- Base URL: `https://api.purplegrain.com/api/v1`
- URL: `https://api.purplegrain.com/api/v1/mobs/{id}`
- Auth: HTTP bearer

Renaming a mob, or changing its head count, writes a record on its
timeline — "Renamed from Merino wethers · Head count set to 1,240 — was
980" — composed on the server, because three write surfaces edit a mob
and only the server sees before and after atomically. An update that
changes neither field writes nothing.

**Archive and restore are this endpoint**: `archived: true` hides the mob
from the map and the lists while its records stay on the property;
`archived: false` brings it back. There is no `/archive` route.
Archiving writes no record.

The owning property cannot be changed — a `property` relationship sent here
is ignored, so a mob can never be re-parented and take its records with
it. Where the mob IS cannot be set here either; see `moveMob`.

Requires organization edit rights.

## Path parameters

- `id` string, required

## Query parameters

- `include` string

## Request body

- MobUpdateModelAttributes: Flat attributes-adapter request body (Content-Type application/json). Attribute fields live directly under `data` (no nested `attributes` envelope). Every field is optional — a partial write. Equivalent JSON:API (nested) shape: `MobUpdateModel` sent as application/vnd.api+json.
  - `data` object, required
    - `name` string
    - `head_count` integer, nullable
    - `species` string, nullable: Stored lowercase. One of sheep, cattle or goat. Blank leaves it unset.
    - `stock_class` string, nullable: Class of stock (ewes, wethers, steers). The wire name is stock_class.
    - `sex` string, nullable: Stored lowercase. One of female, male or mixed. Blank leaves it unset.
    - `birth_year` integer, nullable: Year of birth. Blank leaves it unset.
    - `eid_status` string, nullable: Stored lowercase. One of visual, eid or device_free. Blank leaves it unset.
    - `archived` boolean, nullable: `true` archives the mob, `false` restores it.

## Response `200`

Updated

- MobsShowResponseAttributes
  - `data` MobModelAttributes: Single mob in attributes-adapter shape (Accept application/json).
    - `id` string
    - `type` string
    - `slug` string: Unique within its parent (a property per owner; a paddock or mob per property); generated from the name and renamed with it. The old slug keeps resolving. See `resolveLink`.
    - `web_path` string: The address on the web. Present when read directly.
    - `url` string: The whole link; carries the property's `?share=` token when it is shared by link. Present when read directly.
    - `name` string
    - `head_count` integer, nullable: How many head are in the mob, as a number — never a display string. Null is a real state.
    - `species` 'sheep' | 'cattle' | 'goat', nullable: Livestock species. Null when unset.
    - `stock_class` string, nullable: Class of stock (ewes, wethers, lambs, steers, cows). The wire name is stock_class.
    - `sex` 'female' | 'male' | 'mixed', nullable: female, male or mixed. Null when unset.
    - `birth_year` integer, nullable: Year of birth. Null when unset.
    - `eid_status` 'visual' | 'eid' | 'device_free', nullable: NLIS or eID status. visual, eid or device_free. Null when unset.
    - `withholding` object, nullable: Is the mob under a withholding period or export slaughter interval right now? The latest drench record whose safe-for-slaughter or export date is still ahead, or null. **Derived, not stored.** Read-only.
      - `task_id` string
      - `record_id` string: The DrenchRecord id.
      - `safe_for_slaughter_on` string, date, nullable
      - `export_eligible_on` string, date, nullable
      - `products` string[]
    - `current_paddock_id` string, nullable: Where the mob is right now, derived from the most recent completed record carrying a destination (see `MobModel`). Read-only — move a mob with `moveMob`.
    - `archived` boolean: An archived mob leaves the map and the lists; its records stay on the property.
    - `archived_at` string, date-time, nullable
    - `record_count` integer: Completed rows on this mob — its diary.
    - `open_task_count` integer: Pending rows on this mob.
    - `last_record_at` string, date-time, nullable
    - `created_at` string, date-time
    - `updated_at` string, date-time
    - `property` object: Present when include=property
  - `meta` PaginationMeta
    - `current_page` number
    - `next_page` number, nullable
    - `prev_page` number, nullable

## Other responses

- `400`: The `include` query parameter names a relationship path this endpoint does not take (JSON:API 1.1, "Inclusion of Related Resources"). One error with `code: "unknown_include"` and `source.parameter: "include"`; its `detail` names the refused path(s) and the paths the endpoint allows (the parameter's `x-include-paths`). An endpoint whose serializer whitelists no relationship refuses any path. Rolling out as monitor then enforce: until the API's `JSONAPI_UNKNOWN_INCLUDE` is `reject`, an unknown path is dropped and logged instead.
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not Found
- `409`: Conflict — the JSON:API request document names a different resource from the URL (JSON:API 1.1 §9.2 and §9.3). `errors[0].code` is `type_mismatch` when `data.type` is not one of this endpoint's resource types (`source.pointer` is `/data/type`), or `id_mismatch` when, on an update, `data.id` is not the id in the URL (`/data/id`). Only a body sent as `application/vnd.api+json` is checked; the flat `application/json` body has no resource object. Nothing was changed. Request validation runs first: a `data.type` outside the operation's documented enum, or missing where the schema requires it, is a `400` before this check, so the 409 is seen where the documented type is a free string, or when validation is off.
- `415`: Unsupported Media Type — the request's `Content-Type` is `application/vnd.api+json` with a media type parameter the JSON:API media type does not allow (anything other than `ext` and `profile`; `; charset=utf-8` is the usual one). Send the bare media type. The error's `code` is `unsupported_media_type` and `source.header` is `Content-Type`. Until the switch is on (`JSONAPI_CONTENT_TYPE_PARAMS=reject`) such a request is let through and logged; the operation documents the response it will have.
- `422`: A blank name, a name already used on the property (live or archived), or a negative head count

## Changes

- **2026-10-31** `370fe2f3ed4f`: 1 info
  - added the optional property `data/relationships/current_paddock/links` to the response with the `200` status
- **2026-10-30** `e32f34ba218b`: 1 info
  - added the optional property `data/relationships/current_paddock` to the response with the `200` status
- **2026-10-30** `143d65e52617`: 3 info
  - added the non-success response with the status `400`
  - added the non-success response with the status `409`
  - added the non-success response with the status `415`
- **2026-10-30** `92c4663a1266`: 12 warning, 12 info
  - removed the optional property `_meta` from the response with the `404` status (media type: application/json)
  - removed the optional property `_meta` from the response with the `404` status (media type: application/vnd.api+json)
  - removed the optional property `_meta` from the response with the `422` status (media type: application/json)
  - removed the optional property `_meta` from the response with the `422` status (media type: application/vnd.api+json)
  - …20 more
- **2026-10-30** `c1bc18b1cdbc`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/purplegrain/apis/purple-grain-api/changes/mobs/:id/patch.md)

---

[API](https://skmtc.dev/purplegrain/apis/purple-grain-api.md) - [All operations](https://skmtc.dev/purplegrain/apis/purple-grain-api/llms.txt) - [OpenAPI document](https://skmtc.dev/purplegrain/apis/purple-grain-api/revisions/370fe2f3ed4f?raw)
