---
title: "Resolve a PurpleGrain link"
method: GET
path: "/resolve"
tags: ["Public", "Resolve"]
---

# Resolve a PurpleGrain link

`GET /resolve`

## Request

- Base URL: `https://api.purplegrain.com/api/v1`
- URL: `https://api.purplegrain.com/api/v1/resolve`
- Auth: one of:
  - none
  - HTTP bearer

The first step when someone pastes a PurpleGrain web link. Turns its slugs into
the business or person, property, paddock or mob it names, and says what the
caller may do there.

`/organizations/hillcrest/properties/yarrabee/paddocks/north-block?share=k9f2qa` is
`kind=organization&owner=hillcrest&property=yarrabee&paddock=north-block&share=k9f2qa`.
`kind` `organization` is a business and `kind` `user` is a person. Send those
wire values as they are. `resolved.owner.kind` comes back the same way.

* `access.can_edit` comes from the session's role in the owner, never from the address.
* A slug a property, paddock or mob used to have resolves to it with `redirect: true`
  and the current `canonical_path`, so a link shared before a rename still arrives.
* Public (credentials optional). A caller with no credentials who may not see the
  property gets a **404**, the same as for one that does not exist, so a stranger
  cannot confirm that a private property is there. A signed-in caller who may not
  gets a **403** (`You don't have access…`).
* `resolved.preview` carries the facts behind a link preview (an SMS or chat card, a
  search snippet, the Share panel): the owner's name and, for the deepest thing the
  address names, its counts, area and (a mob) withholding. It is built after the
  access check, so a viewer and a link holder read the same facts; a stranger who
  gets the 404 never sees any.

## Query parameters

- `kind` 'organization' | 'user', required
- `owner` string, required
- `property` string
- `paddock` string
- `mob` string
- `share` string

## Response `200`

What the address names and what the caller may do there.

- ResolveResponse
  - `resolved` object
    - `owner` object
      - `kind` 'organization' | 'user'
      - `id` string
      - `slug` string
      - `name` string
      - `contact_name` string: Who to ask for a change of role or an invite (a name only).
      - `path` string
    - `property` object, nullable
      - `id` string
      - `slug` string
      - `name` string
      - `visibility` 'private' | 'link' | 'public'
    - `paddock` ResolveSummary
      - `id` string
      - `slug` string
      - `name` string
    - `mob` ResolveSummary
      - `id` string
      - `slug` string
      - `name` string
    - `canonical_path` string: The address with every slug current.
    - `redirect` boolean: True when a slug in the request was an old one; the page should go to `canonical_path`.
    - `access` object
      - `signed_in` boolean
      - `member` boolean: Whether the caller is a member of the owner (or the person).
      - `can_edit` boolean
      - `role` string, nullable: The caller's role in the business, when they have one.
      - `via` 'member' | 'public' | 'link': Why they can see it.
    - `preview` ResolvePreview: The facts behind a link preview (an SMS or chat card, a search snippet, the Share panel). Built after the access check, so it holds only what the caller may already see; a viewer and a link holder read the same facts. Counts are of live (non-archived) assets. Nothing in it identifies a person beyond the owner's display name. A paddock or mob block is null when the address names an archived one.
      - `kind` 'owner' | 'property' | 'paddock' | 'mob', required: What the address names, by its deepest segment.
      - `owner` object, required
        - `name` string, required: The business name, or the person's name (their handle if they have none; never an email or phone).
        - `kind` 'organization' | 'user', required
      - `property` object, nullable, required: Present whenever the address names a property (also for its paddocks and mobs).
        - `name` string, required
        - `location` string, nullable, required: The town and state of the property's place label ("Currawarna NSW"), never the street or postcode, or null. A link preview is read by people who were only sent a link, so it never names a street.
        - `area_ha` number, nullable, required
        - `paddock_count` integer, required: Live paddocks.
        - `mob_count` integer, required: Live mobs.
        - `open_task_count` integer, required: Pending tasks on the property.
      - `paddock` object, nullable, required
        - `name` string, required
        - `area_ha` number, nullable, required
        - `land_use` 'cropping' | 'grazing' | 'mixed' | 'fallow', nullable, required
        - `crop` string, nullable, required
        - `mob_names` string[], required: Live mobs currently in the paddock, by name.
        - `open_task_count` integer, required: Pending tasks on the paddock.
      - `mob` object, nullable, required
        - `name` string, required
        - `head_count` integer, nullable, required
        - `paddock_name` string, nullable, required: The paddock the mob is in now; null while it is unplaced.
        - `open_task_count` integer, required: Pending tasks on the mob.
        - `withholding` object, nullable, required: Present only while a withholding or export date is still ahead.
          - `safe_for_slaughter_on` string, date, nullable
          - `export_eligible_on` string, date, nullable

## Other responses

- `403`: Signed in, but not allowed to see the property.
- `404`: Not found, or not visible to a caller with no credentials.

## Changes

- **2026-10-20** `4bedbc1c784c`: 1 info
  - api operation id `resolveAddress` removed and replaced with `resolveLink`

[Operation history](https://skmtc.dev/purplegrain/apis/purple-grain-api/changes/resolve/get.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/29c5c990d66e?raw)
