Error response format
Every response the API itself produces carries anerrors array. It is empty when the request succeeded, and otherwise holds one entry per problem, alongside the endpoint’s resource field set to null or an empty array:
message is human-readable, and field is the request field at fault, as a dot-separated path (data.type, property.externalSystemId). A single response can report several problems at once, so read the whole array rather than the first entry:
401 is plain text, and a firewall 403 or 429 is HTML.
Status codes
Each endpoint documents the codes it returns, with the body shape for each. A successful create returns
200, not 201, and a delete returns 200 with a body rather than 204.
A missing id does not always produce a
404. SI/SD answers one with 400 and
Improvement not found, and a malformed id is a 400 everywhere.Request blocked by the firewall (403)
Clear or replace theUser-Agent header before sending:
- Postman - Headers, then delete
User-Agentor set it to an empty value - Python -
requestssendspython-requests/x.y.z; set your ownUser-Agent - cURL - not blocked; if you need to, pass
-H "User-Agent:"
Rate limits (429)
The API itself does not throttle by API key. Our CDN caps requests per source IP address over a rolling five-minute window and answers anything over the cap with a429 and an HTML body. The ceiling is high enough that an integration paging through data sequentially will not reach it, but the bucket is shared by everything leaving your network, including staff using the Forerunner app. Spread bulk work out rather than firing it in parallel, and back off when you see a 429.
Common validation errors
Invalid UUID format for id parameter
Invalid UUID format for id parameter
A path
id is not a UUID. Check for a truncated or URL-encoded value. This
body carries errors alone, with no resource field.Document upload already exists with external system id ...
Document upload already exists with external system id ...
A file already uses that
externalSystemId, so the upload returned 409 and
nothing was created. Look the file up with GET /api/v1/files?externalSystemId=... before retrying.Log not found for this account
Log not found for this account
A
404 from updating or deleting a Log. The id either does not exist or
belongs to another account.Improvement not found
Improvement not found
The SI/SD equivalent, returned as a
400 rather than a 404.Address, parcelId, or coordinates must be provided
Address, parcelId, or coordinates must be provided
A file or SI/SD record arrived with no way to locate a property. Related
failures name what went wrong more precisely:
Parcel with id ... not found,
Provided point is outside of the account boundary., and Could not determine address from the given data. Send as many of address, parcel ID, and
coordinates as you have — together they match far more reliably than any one
alone.MIME type ... is not allowed for document type ...
MIME type ... is not allowed for document type ...
Each document type accepts its own set of media types, and the file’s media type is not one of them. Check the document type’s allowed types under File types, or upload under a document type that accepts this format.
File ... exceeds the maximum size of ... MB
File ... exceeds the maximum size of ... MB
Limits are per media type and are listed on Submit File. Compress the file, or split a large document, before retrying.
Retrying
Retry429, 500, 502, 503, and 504 with exponential backoff.
The client errors this API returns — 400, 401, 403, 404, and 409 — are deterministic: the same request fails the same way until you change it, so retrying only adds load. A 409 in particular can never succeed on retry, because the resource it conflicts with already exists.
Getting help
Every response from the API carries anx-request-id header. Include it when you contact support: it identifies the exact request in our logs and is far faster to chase than a timestamp.