Constructor

Constructor Partner API

Version v1. Push property and land records to Constructor, where builders and contractors see them as leads and submit proposals.

Getting started

Your Constructor contact will issue you an API key (it looks like cp_live_…). Send it on every request:

Authorization: Bearer cp_live_…
  • Base URL: https://www.gcmgm.com/api/v1/partner/v1
  • Rate limit: 120 requests per minute per API key. A separate 600/minute per-IP ceiling also applies (including to requests with a missing or invalid key).
  • Leads are keyed by your externalId. POSTing the same externalId again updates the existing lead (and re-opens it if it was closed) rather than creating a duplicate — so retries are safe.

POST/api/v1/partner/v1/leads

Create or update a property lead

Push a property or land record. Idempotent on externalId: the first call creates (201), subsequent calls update (200).

Request body
externalIdstring (1–80)requiredYour unique id for this record. It becomes a URL path segment on GET and DELETE, so percent-encode it there if it contains /, ? or #.
addressstring (1–240)requiredStreet address of the property.
titlestring (≤200)optionalShort headline; defaults to the address in the UI.
citystring (≤120)optionalCity.
statestring (≤120)optionalState / region.
postalCodestring (≤20)optionalPostal / ZIP code.
propertyTypestring (≤80)optionalYour own type label (e.g. land, single-family).
descriptionstring (≤4000)optionalFree-text details for the builder.
lotSizeSqftinteger (0–1000000000)optionalLot size in square feet.
budgetCentsinteger (0–2147483647)optionalBudget in cents (e.g. 25000000 = $250,000). Max is ~$21.5M.
attributesobjectoptionalAny extra key/values, stored verbatim. Not echoed back in responses.
Response
idstringrequiredConstructor's id for the lead.
externalIdstringrequiredYour id for the record (echoed back).
statusstringrequired"OPEN" or "CLOSED".
receivedAtstring (ISO 8601)requiredWhen first received.
updatedAtstring (ISO 8601)requiredWhen last updated.
resultstringrequired"created" or "updated".
Example
curl -X POST https://www.gcmgm.com/api/v1/partner/v1/leads \
  -H "Authorization: Bearer cp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "externalId": "LOT-48821",
  "title": "1.2-acre residential lot",
  "address": "4200 Bee Cave Rd",
  "city": "Austin",
  "state": "TX",
  "postalCode": "78746",
  "propertyType": "land",
  "description": "Cleared corner lot, utilities at street. Owner ready to build.",
  "lotSizeSqft": 52272,
  "budgetCents": 45000000,
  "attributes": {
    "zoning": "SF-2",
    "ownerContact": "listed agent"
  }
}'

GET/api/v1/partner/v1/leads/:externalId

Fetch a lead and its proposals

Returns the current lead plus any proposals builders have submitted.

Response
idstringrequiredConstructor's id for the lead.
externalIdstringrequiredYour id for the record (echoed back).
statusstringrequired"OPEN" or "CLOSED".
receivedAtstring (ISO 8601)requiredWhen first received.
updatedAtstring (ISO 8601)requiredWhen last updated.
proposalsarray of ProposalrequiredEvery proposal builders have submitted, newest first. See the Proposal table below.
Proposal
companystringrequiredName of the builder company that submitted it.
messagestringrequiredThe builder's pitch.
amountCentsinteger | nulloptionalProposed amount in cents. null when the builder did not quote a figure.
status"PENDING" | "WITHDRAWN" | "ACCEPTED" | "REJECTED"requiredWITHDRAWN proposals are still returned — filter them out if you only want live bids.
submittedAtstring (ISO 8601)requiredWhen the proposal was created.
Example
curl -X GET https://www.gcmgm.com/api/v1/partner/v1/leads/LOT-48821 \
  -H "Authorization: Bearer cp_live_YOUR_KEY"

DELETE/api/v1/partner/v1/leads/:externalId

Close a lead

Marks the lead CLOSED so builders can no longer propose on it. The record and its proposals are retained; nothing is hard-deleted.

Response
idstringrequiredConstructor's id for the lead.
externalIdstringrequiredYour id for the record (echoed back).
statusstringrequired"OPEN" or "CLOSED".
receivedAtstring (ISO 8601)requiredWhen first received.
updatedAtstring (ISO 8601)requiredWhen last updated.
Example
curl -X DELETE https://www.gcmgm.com/api/v1/partner/v1/leads/LOT-48821 \
  -H "Authorization: Bearer cp_live_YOUR_KEY"

Errors

400Request body failed validation — see the issues object. Also returned for malformed JSON.
401Missing or invalid API key.
404No lead with that externalId for your account.
413Request body too large.
429Rate limit exceeded — retry after the Retry-After header.
Error response body
errorstringrequiredMachine-readable slug, e.g. "validation_error", "unauthorized", "rate_limited".
messagestringrequiredHuman-readable explanation.
issuesobjectoptionalPresent on 400 validation failures: Zod's flattened fieldErrors / formErrors.