Tree Inventory AI

Trees

Individual trees captured in the field, with their measurements and the confidence attached to each one. Read-only: capture happens in the mobile app, where the camera is.

gethttps://app.treeinventory.ai/api/v1/sites/{siteId}/trees

List the trees captured at a site

Not paginated. A site is one property walked by one arborist, so this is tens of records; a cursor here would cost you a loop and gain you nothing.

Scope: inventory:read

Path parameters

FieldTypeNotes
siteIdrequireduuidThe site's id.

Each item in data.items

FieldTypeNotes
iduuid
siteIduuid
commonNamestring or nullNull until identified.
scientificNamestring or null
speciesConfidencenumber or null0-1. Published beside the value it qualifies: a measurement without its confidence reads as certainty nobody has.
speciesSourcestringWho decided the species: ai, user-confirmed, user-corrected or live-confirmed. READ THIS BEFORE YOU PRINT A SPECIES NAME ON ANYTHING A CUSTOMER SEES. Vision species identification is right about a third of the time on its own, so ai with a low speciesConfidence is a guess, and an arborist who sees their own guess quoted back as fact stops trusting the whole quote. Anything but ai means a person looked at the tree and said so.
dbhInchesnumber or nullDiameter at breast height.
dbhConfidencenumber or null0-1 confidence in dbhInches. Null means it was not estimated.
heightFeetnumber or null
heightConfidencenumber or null0-1 confidence in heightFeet.
canopySpreadFeetnumber or null
canopyConfidencenumber or null0-1 confidence in canopySpreadFeet.
trunkCountnumber or null
quantitynumberHow many physical trees this record stands for. Usually 1.
healthConditionstring or nullgood | fair | poor | dead | hazardous.
hazardRatingnumber or null
riskAssessmentstring or null
defectsstring[]
observationsstring[]
recommendationsstring[]
tagsstring[]
latitudenumber or null
longitudenumber or null
gpsAccuracyMetersnumber or null
capturedAtstring
createdAtstring
updatedAtstring

Example response 200

{
  "success": true,
  "message": "Trees retrieved",
  "data": {
    "items": [
      {
        "id": "c04f8a55-1d2e-4b39-8a71-6f5c2d9e3b40",
        "siteId": "3a7c9e21-5b48-4f0a-9d33-71c6ee204b18",
        "commonName": "Northern Red Oak",
        "scientificName": "Quercus rubra",
        "speciesConfidence": 0.91,
        "speciesSource": "user-confirmed",
        "dbhInches": 22,
        "dbhConfidence": 0.44,
        "heightFeet": 58,
        "heightConfidence": 0.6,
        "canopySpreadFeet": 34,
        "canopyConfidence": 0.55,
        "trunkCount": 1,
        "quantity": 1,
        "healthCondition": "fair",
        "hazardRating": 3,
        "riskAssessment": "Deadwood over the driveway; included bark at the main union.",
        "defects": [
          "included bark"
        ],
        "observations": [
          "Deadwood concentrated on the south side"
        ],
        "recommendations": [
          "Crown clean",
          "Reduce the driveway-side limb"
        ],
        "tags": [],
        "latitude": 39.19124,
        "longitude": -86.58471,
        "gpsAccuracyMeters": 4,
        "capturedAt": "2026-08-20T15:41:02.000Z",
        "createdAt": "2026-08-20T15:41:05.220Z",
        "updatedAt": "2026-08-20T15:41:05.220Z"
      }
    ]
  }
}

Failures

  • 401 Missing, malformed, revoked or expired credential. The WWW-Authenticate header names the scheme, which is ApiKey. Carries no RateLimit-* headers: the credential was never resolved, so there is no bucket to report.
  • 403 The key is valid but does not carry the scope this operation requires. The message names the scope, so you can ask for exactly the grant you need. Carries no RateLimit-* headers: a request refused for scope is not counted against your limit.
  • 404 No such record in your organization. A record belonging to somebody else is indistinguishable from one that does not exist.
  • 422 Validation failed. Every bad field is named, at once.
  • 429 Rate limit exceeded.
  • 500 Something failed on our side. Retry with the SAME Idempotency-Key; a 5xx is the one status where retrying is correct.
gethttps://app.treeinventory.ai/api/v1/trees/{treeId}

Get one tree

A tree in another organization answers 404, identically to one that does not exist.

Scope: inventory:read

Path parameters

FieldTypeNotes
treeIdrequireduuidThe tree's id.

data

FieldTypeNotes
iduuid
siteIduuid
commonNamestring or nullNull until identified.
scientificNamestring or null
speciesConfidencenumber or null0-1. Published beside the value it qualifies: a measurement without its confidence reads as certainty nobody has.
speciesSourcestringWho decided the species: ai, user-confirmed, user-corrected or live-confirmed. READ THIS BEFORE YOU PRINT A SPECIES NAME ON ANYTHING A CUSTOMER SEES. Vision species identification is right about a third of the time on its own, so ai with a low speciesConfidence is a guess, and an arborist who sees their own guess quoted back as fact stops trusting the whole quote. Anything but ai means a person looked at the tree and said so.
dbhInchesnumber or nullDiameter at breast height.
dbhConfidencenumber or null0-1 confidence in dbhInches. Null means it was not estimated.
heightFeetnumber or null
heightConfidencenumber or null0-1 confidence in heightFeet.
canopySpreadFeetnumber or null
canopyConfidencenumber or null0-1 confidence in canopySpreadFeet.
trunkCountnumber or null
quantitynumberHow many physical trees this record stands for. Usually 1.
healthConditionstring or nullgood | fair | poor | dead | hazardous.
hazardRatingnumber or null
riskAssessmentstring or null
defectsstring[]
observationsstring[]
recommendationsstring[]
tagsstring[]
latitudenumber or null
longitudenumber or null
gpsAccuracyMetersnumber or null
capturedAtstring
createdAtstring
updatedAtstring

Example response 200

{
  "success": true,
  "message": "Tree retrieved",
  "data": {
    "id": "c04f8a55-1d2e-4b39-8a71-6f5c2d9e3b40",
    "siteId": "3a7c9e21-5b48-4f0a-9d33-71c6ee204b18",
    "commonName": "Northern Red Oak",
    "scientificName": "Quercus rubra",
    "speciesConfidence": 0.91,
    "speciesSource": "user-confirmed",
    "dbhInches": 22,
    "dbhConfidence": 0.44,
    "heightFeet": 58,
    "heightConfidence": 0.6,
    "canopySpreadFeet": 34,
    "canopyConfidence": 0.55,
    "trunkCount": 1,
    "quantity": 1,
    "healthCondition": "fair",
    "hazardRating": 3,
    "riskAssessment": "Deadwood over the driveway; included bark at the main union.",
    "defects": [
      "included bark"
    ],
    "observations": [
      "Deadwood concentrated on the south side"
    ],
    "recommendations": [
      "Crown clean",
      "Reduce the driveway-side limb"
    ],
    "tags": [],
    "latitude": 39.19124,
    "longitude": -86.58471,
    "gpsAccuracyMeters": 4,
    "capturedAt": "2026-08-20T15:41:02.000Z",
    "createdAt": "2026-08-20T15:41:05.220Z",
    "updatedAt": "2026-08-20T15:41:05.220Z"
  }
}

Failures

  • 401 Missing, malformed, revoked or expired credential. The WWW-Authenticate header names the scheme, which is ApiKey. Carries no RateLimit-* headers: the credential was never resolved, so there is no bucket to report.
  • 403 The key is valid but does not carry the scope this operation requires. The message names the scope, so you can ask for exactly the grant you need. Carries no RateLimit-* headers: a request refused for scope is not counted against your limit.
  • 404 No such record in your organization. A record belonging to somebody else is indistinguishable from one that does not exist.
  • 422 Validation failed. Every bad field is named, at once.
  • 429 Rate limit exceeded.
  • 500 Something failed on our side. Retry with the SAME Idempotency-Key; a 5xx is the one status where retrying is correct.
gethttps://app.treeinventory.ai/api/v1/trees/{treeId}/photos

Look at the photographs of one tree

The photographs are the arborist's actual account of the visit, and everything else published about a tree is a text summary a vision model wrote from ONE of them. The defect close-up somebody stopped to take a second photo of has never been read by anything. If you can see images, look before you price: a 24in maple with included bark in defects is a different job depending on whether the union is cabled, and the photograph settles it and the summary does not. Not paginated. url is a SHORT-LIVED SIGNED URL: fetch it now, do not store it, do not put it in a document, and do not hand it to a customer. Ask again when it expires.

Scope: inventory:read

Path parameters

FieldTypeNotes
treeIdrequireduuidThe tree's id.

Each item in data.items

FieldTypeNotes
iduuid
treeIduuid
categorystringWhat the arborist was photographing: primary, trunk, canopy, defect, context, markup or general. primary is the one the species and measurements were inferred from. defect is the one worth looking at hardest, because it is the shot somebody chose to take a second photo for.
captionstring or null
hasMarkupbooleanThe arborist drew on this photo: an arrow at the limb, a circle round the union. A true here means a person pointed at something.
widthinteger or null
heightinteger or null
capturedAtstring
urlstring or nullA short-lived signed URL. Fetch it now or ask again later. Null means the file could not be signed, which is a storage problem and not an empty photograph.

Example response 200

{
  "success": true,
  "message": "Photos retrieved",
  "data": {
    "items": [
      {
        "id": "d1e2f3a4-b5c6-4d7e-8f90-1a2b3c4d5e6f",
        "treeId": "c04f8a55-1d2e-4b39-8a71-6f5c2d9e3b40",
        "category": "defect",
        "caption": "Included bark at the main union",
        "hasMarkup": true,
        "width": 3024,
        "height": 4032,
        "capturedAt": "2026-08-20T15:41:04.000Z",
        "url": "https://storage.treeinventory.ai/signed/..."
      }
    ]
  }
}

Failures

  • 401 Missing, malformed, revoked or expired credential. The WWW-Authenticate header names the scheme, which is ApiKey. Carries no RateLimit-* headers: the credential was never resolved, so there is no bucket to report.
  • 403 The key is valid but does not carry the scope this operation requires. The message names the scope, so you can ask for exactly the grant you need. Carries no RateLimit-* headers: a request refused for scope is not counted against your limit.
  • 404 No such record in your organization. A record belonging to somebody else is indistinguishable from one that does not exist.
  • 422 Validation failed. Every bad field is named, at once.
  • 429 Rate limit exceeded.
  • 500 Something failed on our side. Retry with the SAME Idempotency-Key; a 5xx is the one status where retrying is correct.