---
title: "Save my property setup draft"
method: PATCH
path: "/property_setup_draft"
tags: ["PropertySetupDrafts"]
---

# Save my property setup draft

`PATCH /property_setup_draft`

## Request

- Base URL: `https://api.purplegrain.com/api/v1`
- URL: `https://api.purplegrain.com/api/v1/property_setup_draft`
- Auth: HTTP bearer

Creates or replaces the caller's draft for the owner named in the body.
Only the keys sent change; `null` clears one. The map editor writes on
every change (debounced), so this is an upsert keyed by the caller and
the owner. `ring` is an open array of up to 2000 `[lng, lat]` positions;
`location` is `{ label, lng?, lat? }`; `step` is the checklist row.
Anything else is refused with 422 on the field.

## Request body

- PropertySetupDraftUpdateModelAttributes: Flat attributes-adapter request body (Content-Type application/json) for the caller's property setup draft: the same fields directly under `data`. Equivalent JSON:API (nested) shape: `PropertySetupDraftUpdateModel` sent as application/vnd.api+json.
  - `data` object, required
    - `owner_type` 'User' | 'Organization', required
    - `owner_id` string, uuid, required
    - `name` string, nullable
    - `location` SetupDraftLocation, nullable: The place the editor found for the property. `label` is required; `lng` and `lat` are both present or both absent. No other keys.
      - `label` string, required
      - `lng` number
      - `lat` number
    - `ring` array[], nullable: The corners placed so far: an OPEN array of `[longitude, latitude]` pairs in RFC 7946 order — **longitude first** — 0 to 2000 of them. A draft may hold one or two corners, so this is neither a polygon nor GeoJSON; it becomes one only when the boundary is saved (`updatePropertyBoundary`, `updatePaddockBoundary`). Longitude within ±180, latitude within ±90.
      - number[]: [longitude, latitude]
    - `step` 'owner' | 'name' | 'location' | 'boundary', nullable

## Response `200`

The saved draft.

- PropertySetupDraftResponseAttributes: Attributes-adapter response (Accept application/json). `data` is null when the caller has no draft for the owner.
  - `data` PropertySetupDraftModelAttributes, nullable: The caller's unsaved property for one owner in attributes-adapter shape (Accept application/json), or null when there is none.
    - `id` string
    - `type` 'property_setup_drafts'
    - `owner_type` 'User' | 'Organization'
    - `owner_id` string, uuid
    - `name` string, nullable
    - `location` SetupDraftLocation, nullable: The place the editor found for the property. `label` is required; `lng` and `lat` are both present or both absent. No other keys.
      - `label` string, required
      - `lng` number
      - `lat` number
    - `ring` array[], nullable: The corners placed so far: an OPEN array of `[longitude, latitude]` pairs in RFC 7946 order — **longitude first** — 0 to 2000 of them. A draft may hold one or two corners, so this is neither a polygon nor GeoJSON; it becomes one only when the boundary is saved (`updatePropertyBoundary`, `updatePaddockBoundary`). Longitude within ±180, latitude within ±90.
      - number[]: [longitude, latitude]
    - `step` 'owner' | 'name' | 'location' | 'boundary', nullable
    - `updated_at` string, date-time

## Other responses

- `401`: Unauthorized — no session.
- `403`: Forbidden — the owner is someone else, or a business where the caller may not add properties; or an API key or an assistant's token (first-party only).
- `404`: No such person or business.
- `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 ring that is not `[lng, lat]` number pairs in range or holds more than 2000, a location without a label or with half a coordinate pair or other keys, an unknown `step`, a name over 255 characters, or an unknown owner type.

## Changes

- **2026-10-30** `143d65e52617`: 2 info
  - added the non-success response with the status `409`
  - added the non-success response with the status `415`
- **2026-10-30** `92c4663a1266`: 24 warning, 24 info
  - removed the optional property `_meta` from the response with the `401` status (media type: application/json)
  - removed the optional property `_meta` from the response with the `401` status (media type: application/vnd.api+json)
  - removed the optional property `_meta` from the response with the `403` status (media type: application/json)
  - removed the optional property `_meta` from the response with the `403` status (media type: application/vnd.api+json)
  - …44 more
- **2026-10-30** `c1bc18b1cdbc`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/purplegrain/apis/purple-grain-api/changes/property_setup_draft/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)
