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
| externalId | string (1–80) | required | Your unique id for this record. It becomes a URL path segment on GET and DELETE, so percent-encode it there if it contains /, ? or #. |
| address | string (1–240) | required | Street address of the property. |
| title | string (≤200) | optional | Short headline; defaults to the address in the UI. |
| city | string (≤120) | optional | City. |
| state | string (≤120) | optional | State / region. |
| postalCode | string (≤20) | optional | Postal / ZIP code. |
| propertyType | string (≤80) | optional | Your own type label (e.g. land, single-family). |
| description | string (≤4000) | optional | Free-text details for the builder. |
| lotSizeSqft | integer (0–1000000000) | optional | Lot size in square feet. |
| budgetCents | integer (0–2147483647) | optional | Budget in cents (e.g. 25000000 = $250,000). Max is ~$21.5M. |
| attributes | object | optional | Any extra key/values, stored verbatim. Not echoed back in responses. |
Response
| id | string | required | Constructor's id for the lead. |
| externalId | string | required | Your id for the record (echoed back). |
| status | string | required | "OPEN" or "CLOSED". |
| receivedAt | string (ISO 8601) | required | When first received. |
| updatedAt | string (ISO 8601) | required | When last updated. |
| result | string | required | "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
| id | string | required | Constructor's id for the lead. |
| externalId | string | required | Your id for the record (echoed back). |
| status | string | required | "OPEN" or "CLOSED". |
| receivedAt | string (ISO 8601) | required | When first received. |
| updatedAt | string (ISO 8601) | required | When last updated. |
| proposals | array of Proposal | required | Every proposal builders have submitted, newest first. See the Proposal table below. |
Proposal
| company | string | required | Name of the builder company that submitted it. |
| message | string | required | The builder's pitch. |
| amountCents | integer | null | optional | Proposed amount in cents. null when the builder did not quote a figure. |
| status | "PENDING" | "WITHDRAWN" | "ACCEPTED" | "REJECTED" | required | WITHDRAWN proposals are still returned — filter them out if you only want live bids. |
| submittedAt | string (ISO 8601) | required | When 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
| id | string | required | Constructor's id for the lead. |
| externalId | string | required | Your id for the record (echoed back). |
| status | string | required | "OPEN" or "CLOSED". |
| receivedAt | string (ISO 8601) | required | When first received. |
| updatedAt | string (ISO 8601) | required | When 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
| 400 | Request body failed validation — see the issues object. Also returned for malformed JSON. |
| 401 | Missing or invalid API key. |
| 404 | No lead with that externalId for your account. |
| 413 | Request body too large. |
| 429 | Rate limit exceeded — retry after the Retry-After header. |
Error response body
| error | string | required | Machine-readable slug, e.g. "validation_error", "unauthorized", "rate_limited". |
| message | string | required | Human-readable explanation. |
| issues | object | optional | Present on 400 validation failures: Zod's flattened fieldErrors / formErrors. |