Parcel

API reference

Every Parcel tool is one POST to https://parcel.westwardconsulting.ai/api/v1 with a JSON body. The machine-readable spec is at /api/v1/openapi.json.

Authentication

Create a key in your workspace under API keys and send it as a bearer token. Keys start with pcl_live_. Keep them on your server; never ship them in a browser or mobile app.

Authorization: Bearer pcl_live_...

Responses

Every success has the same envelope. The tool's result is in data; its fields are listed under each tool below. Calls typically take 3 to 30 seconds; set client timeouts to at least 120 seconds.

{
  "id": "run_mh0x1k2a9f3c...",
  "object": "tool.result",
  "tool": "lead-qualifier",
  "created": 1793318400,
  "model": "claude-opus-5-5",
  "data": { ... },
  "usage": { "input_tokens": 1240, "output_tokens": 610 }
}

Errors and limits

Each key is limited to 60 requests a minute by default, and each workspace has a monthly usage limit. Both can be raised on request.

400invalid_request / invalid_jsonThe body is not valid for this tool. `details` lists each field problem.
401missing_api_key / invalid_api_keyNo key, a wrong key, or a revoked key.
402monthly_limit_reachedThe workspace reached its monthly usage limit.
413payload_too_largeBodies are limited to 12 MB (images 5 MB, inline PDFs about 3 MB).
422ai_errorThe model declined or could not finish; rephrase or shorten the input.
429rate_limitedToo many requests for this key. Honor the Retry-After header.
502 / 503ai_errorThe AI service was unreachable or busy. Safe to retry.
Marketing

Listing Writer

POST /api/v1/listings/write

Turns raw property facts into a headline, a full description, MLS-ready public remarks within your character limit, highlights, a social caption, hashtags and an email subject. Uses only the facts you give it and screens every word for fair-housing risk.

curl https://parcel.westwardconsulting.ai/api/v1/listings/write \
  -H "Authorization: Bearer $PARCEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "property": {
    "address": "418 Alder Lane, Portland, OR 97214",
    "property_type": "single_family",
    "price": 849000,
    "beds": 4,
    "baths": 2.5,
    "sqft": 2380,
    "lot_size": "5,000 sq ft",
    "year_built": 1926,
    "features": [
      "Renovated kitchen with quartz counters and a 36-inch gas range",
      "Original fir floors refinished in 2024",
      "Primary suite with walk-in closet and heated bathroom floors",
      "Covered front porch",
      "Detached garage converted to a studio with power and skylights",
      "New roof (2023) and seismic retrofit"
    ],
    "neighborhood": "Three blocks to Laurelhurst Park; Belmont Street cafes and shops a short walk away."
  },
  "tone": "warm",
  "mls_char_limit": 1000
}'

Request body

propertyrequired
object
property.address
string
property.property_type
"single_family" | "condo" | "townhouse" | "multi_family" | "land" | "commercial" | "other"
Default "single_family".
property.price
number
property.beds
number
property.baths
number
property.sqft
number
property.lot_size
string
property.year_built
integer
property.features
string[]
Default [].
property.neighborhood
string
Nearby places and amenities, by name.
property.notes
string
Anything else the agent wants mentioned.
tone
"warm" | "luxury" | "modern" | "concise" | "investor"
Default "warm".
mls_char_limit
integer
Default 1000.
language
string
Default "English".

Response data

headlinerequired
string
Under 70 characters.
descriptionrequired
string
Full marketing description, 120-250 words, 2-3 short paragraphs separated by blank lines.
mls_remarksrequired
string
Plain-text public remarks for the MLS, within the character limit.
highlightsrequired
string[]
4-6 short highlight phrases.
social_captionrequired
string
1-3 sentences for Instagram/Facebook. At most 2 emojis.
hashtagsrequired
string[]
5-8 hashtags without the # sign.
email_subjectrequired
string
Under 60 characters.
compliancerequired
object
compliance.passedrequired
boolean
False when the automated screen found a high-severity phrase.
compliance.flagsrequired
object[]
compliance.flags[].phraserequired
string
compliance.flags[].categoryrequired
string
compliance.flags[].severityrequired
"high" | "medium" | "low"
compliance.flags[].noterequired
string
Compliance

Fair Housing Check

POST /api/v1/compliance/fair-housing

Reviews any listing, ad, post, email or rental criteria for fair-housing risk. Returns each problem phrase with the protected class involved, why it matters, compliant wording, and a fully revised version. A deterministic phrase screen runs first; the AI then confirms or dismisses each hit in context.

curl https://parcel.westwardconsulting.ai/api/v1/compliance/fair-housing \
  -H "Authorization: Bearer $PARCEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Charming 2BR bungalow in a quiet, exclusive neighborhood. Perfect for a young couple or empty nesters! Walking distance to St. Mary'\''s church. Master suite with garden views. Sorry, no Section 8.",
  "context": "listing",
  "jurisdiction": "Washington State"
}'

Request body

textrequired
string
context
"listing" | "advertisement" | "social_post" | "email" | "rental_criteria" | "other"
Default "listing".
jurisdiction
string
State or city, so local protected classes (e.g. source of income) are considered.

Response data

risk_levelrequired
"none" | "low" | "medium" | "high"
summaryrequired
string
Two or three sentences a busy agent can act on.
issuesrequired
object[]
issues[].quoterequired
string
The exact words from the text.
issues[].categoryrequired
"race" | "color" | "religion" | "sex" | "disability" | "familial_status" | "national_origin" | "age" | "source_of_income" | "sexual_orientation_gender_identity" | "marital_status" | "steering" | "other"
issues[].severityrequired
"low" | "medium" | "high"
issues[].explanationrequired
string
issues[].suggestionrequired
string
Compliant replacement wording.
revised_textrequired
string
The full text rewritten to remove every issue, otherwise unchanged in meaning and style.
screen_hitsrequired
object[]
Phrases caught by the deterministic screen, before AI review.
screen_hits[].phraserequired
string
screen_hits[].categoryrequired
string
screen_hits[].severityrequired
"high" | "medium" | "low"
screen_hits[].noterequired
string
disclaimerrequired
string
Media

Photo Intelligence

POST /api/v1/photos/analyze

Classifies a listing photo, lists visible features and finishes, rates condition and photo quality (with a hero-shot score), writes alt text and a caption, suggests staging fixes, and flags anything that should not be published, like people, family photos or license plates.

curl https://parcel.westwardconsulting.ai/api/v1/photos/analyze \
  -H "Authorization: Bearer $PARCEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "image_url": "https://example.com/listings/418-alder/kitchen.jpg",
  "context": "1926 craftsman, renovated kitchen with quartz counters."
}'

Request body

image_url
string
Public https URL of the photo.
image_base64
string
Base64-encoded image bytes (no data: prefix). 5 MB max.
media_type
"image/jpeg" | "image/png" | "image/webp" | "image/gif"
Required with image_base64.
context
string
Optional listing facts, so captions match the property.

Response data

room_typerequired
"exterior_front" | "exterior_back" | "aerial" | "kitchen" | "living_room" | "dining_room" | "family_room" | "bedroom" | "primary_bedroom" | "bathroom" | "primary_bathroom" | "office" | "laundry" | "garage" | "basement" | "hallway_entry" | "outdoor_space" | "pool" | "view" | "amenity" | "floor_plan" | "other"
room_labelrequired
string
Short human label, e.g. 'Kitchen with island'.
featuresrequired
string[]
Visible features a buyer would care about.
materials_and_finishesrequired
string[]
conditionrequired
"excellent" | "good" | "fair" | "needs_work" | "unclear"
photo_qualityrequired
object
photo_quality.lightingrequired
"poor" | "fair" | "good" | "excellent"
photo_quality.compositionrequired
"poor" | "fair" | "good" | "excellent"
photo_quality.hero_scorerequired
number
1-10: how well this would work as the lead listing photo.
photo_quality.issuesrequired
string[]
Fixable problems: tilt, clutter, dark corners, photographer reflection...
alt_textrequired
string
Accessible alt text, under 125 characters, describing what is visible.
captionrequired
string
One-sentence marketing caption using only what is visible.
staging_suggestionsrequired
string[]
privacy_flagsrequired
string[]
Things to remove before publishing: people, family photos, license plates, documents, valuables, security codes.
Leads

Lead Qualifier

POST /api/v1/leads/qualify

Reads an inbound email, text, portal inquiry or call transcript and returns intent, a 0-100 score, timeline, budget, financing, locations and must-haves, contact details, scam signals, the next best action and a ready-to-send reply. Built for CRMs, inboxes and speed-to-lead automations.

curl https://parcel.westwardconsulting.ai/api/v1/leads/qualify \
  -H "Authorization: Bearer $PARCEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "message": "Hi! Saw the Alder Lane house on Zillow. We'\''re relocating from Denver for my wife'\''s job in late June and are pre-approved up to $900k. Need 4 beds and a yard for the dog, would love Laurelhurst or Sunnyside. Can we see it this weekend? - Marcus, 720-555-0148",
  "channel": "portal",
  "listing": "418 Alder Lane, 4 bd / 2.5 ba, 2,380 sq ft, $849,000",
  "agent_name": "Dana"
}'

Request body

messagerequired
string
The inbound message, form submission or call transcript.
channel
"email" | "sms" | "web_form" | "portal" | "phone_transcript" | "social_dm" | "other"
Default "email".
lead_name
string
listing
string
The listing the lead asked about, if any.
agent_name
string
brokerage
string

Response data

intentrequired
"buy" | "sell" | "buy_and_sell" | "rent" | "lease_out" | "invest" | "other" | "unclear"
temperaturerequired
"hot" | "warm" | "cold"
scorerequired
number
0-100 likelihood of transacting in the next 90 days.
timelinerequired
string | null
Their stated or implied timeline, in their terms.
budget_minrequired
number | null
budget_maxrequired
number | null
financingrequired
"cash" | "preapproved" | "needs_financing" | "unknown"
locationsrequired
string[]
must_havesrequired
string[]
contactrequired
object
contact.namerequired
string | null
contact.emailrequired
string | null
contact.phonerequired
string | null
summaryrequired
string
One or two sentences for the CRM.
red_flagsrequired
string[]
Spam, scam or wire-fraud signals; empty if none.
next_best_actionrequired
string
suggested_replyrequired
string
Ready-to-send reply in the agent's voice, matched to the channel.
follow_up_questionsrequired
string[]
Transactions

Contract Extractor

POST /api/v1/documents/extract

Reads a purchase agreement, lease, listing agreement, disclosure or inspection report (PDF or text) and returns structured parties, financial terms, every date and deadline with its source quote, contingencies, obligations, red flags and unfilled blanks. Feed it to your transaction-management system or calendar.

curl https://parcel.westwardconsulting.ai/api/v1/documents/extract \
  -H "Authorization: Bearer $PARCEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "RESIDENTIAL PURCHASE AGREEMENT\nDate: October 3, 2026\nBuyer: Marcus and Elena Reyes. Seller: Thomas Whitfield.\nProperty: 418 Alder Lane, Portland, OR 97214.\n1. PRICE. Purchase price $835,000. Earnest money of $25,000 to be deposited with Cascade Title within 3 business days of acceptance.\n2. FINANCING. Buyer to obtain a conventional loan of 80% of the price. Financing contingency expires 21 days after acceptance.\n3. INSPECTION. Buyer may inspect within 10 days after acceptance and deliver a repair request; Seller to respond within 3 days.\n4. APPRAISAL. If the property appraises below the price, Buyer may terminate within 5 days of receiving the appraisal.\n5. CLOSING. Closing on or before November 14, 2026. Possession at closing.\n6. SELLER CREDIT. Seller to credit Buyer $_______ toward closing costs.\n7. ADDENDA. Lead-based paint disclosure (Addendum A) attached.\nBuyer signature: ____________  Seller signature: ____________",
  "document_type": "auto"
}'

Request body

text
string
Document text, if you already have it.
pdf_base64
string
Base64-encoded PDF (no data: prefix).
document_type
"auto" | "purchase_agreement" | "lease" | "listing_agreement" | "buyer_agreement" | "disclosure" | "inspection_report" | "addendum" | "title_or_escrow" | "other"
Default "auto".
focus
string
Anything specific to look for.

Response data

document_typerequired
"purchase_agreement" | "lease" | "listing_agreement" | "buyer_agreement" | "disclosure" | "inspection_report" | "addendum" | "title_or_escrow" | "other"
titlerequired
string
summaryrequired
string
What this document does, in 3-5 plain sentences.
property_addressrequired
string | null
partiesrequired
object[]
parties[].rolerequired
string
parties[].namerequired
string
financialsrequired
object[]
financials[].labelrequired
string
e.g. Purchase price, Earnest money, Monthly rent, Security deposit, Seller credit.
financials[].amountrequired
number | null
financials[].textrequired
string
As written in the document.
key_datesrequired
object[]
key_dates[].labelrequired
string
key_dates[].daterequired
string | null
YYYY-MM-DD when a calendar date can be determined.
key_dates[].rulerequired
string | null
Relative rule as written, e.g. '10 days after acceptance'.
key_dates[].source_quoterequired
string
contingenciesrequired
object[]
contingencies[].typerequired
string
contingencies[].deadlinerequired
string | null
contingencies[].detailsrequired
string
obligationsrequired
object[]
obligations[].partyrequired
string
obligations[].obligationrequired
string
red_flagsrequired
object[]
red_flags[].issuerequired
string
red_flags[].why_it_mattersrequired
string
red_flags[].source_quoterequired
string | null
blanks_and_missingrequired
string[]
Unfilled blanks, missing signatures or initials, referenced but missing addenda.
Valuation

CMA Builder

POST /api/v1/valuations/cma

Takes a subject property and your comparables and returns price-per-square-foot statistics (computed exactly, not by the AI), line-item adjustments and weights for each comp, a low/mid/high value range, a suggested price, a rationale for the agent and a summary to send the client.

curl https://parcel.westwardconsulting.ai/api/v1/valuations/cma \
  -H "Authorization: Bearer $PARCEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "subject": {
    "address": "418 Alder Lane",
    "beds": 4,
    "baths": 2.5,
    "sqft": 2380,
    "year_built": 1926,
    "lot_size": "5,000 sq ft",
    "condition": "excellent",
    "features": [
      "Renovated kitchen",
      "Studio in converted garage",
      "New roof 2023"
    ]
  },
  "comps": [
    {
      "address": "502 Alder Ln",
      "price": 815000,
      "date": "2026-08-12",
      "beds": 4,
      "baths": 2,
      "sqft": 2250,
      "year_built": 1924,
      "condition": "good",
      "distance_miles": 0.1
    },
    {
      "address": "1130 SE 37th Ave",
      "price": 872000,
      "date": "2026-07-02",
      "beds": 4,
      "baths": 3,
      "sqft": 2510,
      "year_built": 1931,
      "condition": "excellent",
      "distance_miles": 0.4
    },
    {
      "address": "3315 SE Ankeny St",
      "price": 769000,
      "date": "2026-09-20",
      "beds": 3,
      "baths": 2,
      "sqft": 2050,
      "year_built": 1912,
      "condition": "average",
      "distance_miles": 0.5
    },
    {
      "address": "455 SE 41st Ave",
      "status": "pending",
      "price": 899000,
      "beds": 4,
      "baths": 3,
      "sqft": 2600,
      "year_built": 1928,
      "condition": "excellent",
      "distance_miles": 0.6
    }
  ],
  "purpose": "listing_price"
}'

Request body

subjectrequired
object
subject.address
string
subject.property_type
string
Default "single family".
subject.bedsrequired
number
subject.bathsrequired
number
subject.sqftrequired
number
subject.year_built
integer
subject.lot_size
string
subject.condition
"new" | "excellent" | "good" | "average" | "fair" | "poor"
Default "good".
subject.features
string[]
Default [].
compsrequired
object[]
comps[].addressrequired
string
comps[].status
"sold" | "pending" | "active"
Default "sold".
comps[].pricerequired
number
Sold price, or list price for pending/active.
comps[].date
string
Sold or listed date.
comps[].bedsrequired
number
comps[].bathsrequired
number
comps[].sqftrequired
number
comps[].year_built
integer
comps[].lot_size
string
comps[].distance_miles
number
comps[].condition
"new" | "excellent" | "good" | "average" | "fair" | "poor"
comps[].notes
string
purpose
"listing_price" | "buyer_offer" | "seller_estimate"
Default "listing_price".

Response data

adjusted_compsrequired
object[]
adjusted_comps[].addressrequired
string
adjusted_comps[].adjusted_pricerequired
number
adjusted_comps[].weightrequired
number
0-1; weights across comps sum to 1.
adjusted_comps[].adjustmentsrequired
object[]
adjusted_comps[].adjustments[].factorrequired
string
adjusted_comps[].adjustments[].amountrequired
number
adjusted_comps[].adjustments[].reasonrequired
string
value_lowrequired
number
value_midrequired
number
value_highrequired
number
suggested_pricerequired
number
Recommended list price or offer, depending on purpose.
confidencerequired
"low" | "medium" | "high"
rationalerequired
string
How the comps were weighed, for the agent.
client_summaryrequired
string
Plain-language summary an agent can send to a client.
caveatsrequired
string[]
statsrequired
object
stats.comps_usedrequired
number
stats.median_price_per_sqftrequired
number
stats.mean_price_per_sqftrequired
number
stats.low_price_per_sqftrequired
number
stats.high_price_per_sqftrequired
number
stats.baseline_valuerequired
number
Subject square footage times the median price per square foot of sold comps.
disclaimerrequired
string
Agents

Property Agent

POST /api/v1/agents/property-chat

A conversational agent grounded in one listing. Send the listing facts and the conversation so far; get the next reply, the lead details captured so far, and a flag when a human should take over (offers, negotiation, legal or financing questions). Stateless: you keep the transcript, so it drops into any website chat, SMS line or portal inbox.

curl https://parcel.westwardconsulting.ai/api/v1/agents/property-chat \
  -H "Authorization: Bearer $PARCEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "listing": "418 Alder Lane, Portland, OR 97214. $849,000. 4 bed / 2.5 bath, 2,380 sq ft, built 1926, 5,000 sq ft lot. Renovated kitchen (quartz, 36in gas range). Refinished original fir floors. Primary suite with heated bath floors. Detached studio with power and skylights. New roof 2023, seismic retrofit. Gas forced-air heat, central AC. Property taxes $9,412 (2025). No HOA. Open house Saturday 1-3pm.",
  "messages": [
    {
      "role": "user",
      "content": "Hi, is there AC? And could we see it before the open house?"
    }
  ],
  "agent_name": "Dana Whitaker",
  "brokerage": "Northwest Realty"
}'

Request body

listingrequired
string
Everything the assistant may say about the property, as text or JSON.
messagesrequired
object[]
messages[].rolerequired
"user" | "assistant"
messages[].contentrequired
string
agent_name
string
brokerage
string
showing_link
string
Booking link the assistant can offer.

Response data

replyrequired
string
The assistant's next message, plain text, 1-4 sentences.
leadrequired
object
lead.namerequired
string | null
lead.emailrequired
string | null
lead.phonerequired
string | null
lead.intentrequired
"buy" | "rent" | "sell" | "just_browsing" | "unknown"
lead.timelinerequired
string | null
lead.preapprovedrequired
boolean | null
lead.wants_showingrequired
boolean
handoff_to_humanrequired
boolean
handoff_reasonrequired
string | null