Download OpenAPI specification:
REST API for estate agent partners to submit referrals, track property progress, access documents, and receive real-time webhook notifications.
Your Bid account manager will provide a client_id and client_secret. See the
Quickstart guide to make your first API call in 5 minutes.
Your Bid account manager will provide:
client_id — UUID identifying your integrationclient_secret — starts with bid_cs_ (store securely, never expose in client-side code)curl -X POST https://api.thebid.uk/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"grant_type": "client_credentials"
}'
Response (RFC 6749 §5.1 — flat JSON, no data wrapper):
{
"access_token": "bid_at_...",
"token_type": "Bearer",
"expires_in": 3600,
"permissions": ["referrals:read", "referrals:write", "properties:read", "properties:write", "buyers:read"]
}
curl https://api.thebid.uk/v1/me \
-H "Authorization: Bearer bid_at_..."
Response:
{
"data": {
"agent_id": "uuid",
"agent_name": "Acme Estate Agents",
"branches": [
{ "branch_id": "uuid", "branch_name": "London Office", "phone_number": "020 7123 4567" }
],
"api_key_name": "Production Key",
"permissions": ["referrals:read", "referrals:write", "properties:read", "properties:write", "buyers:read"]
},
"request_id": "uuid"
}
Fetch enum values so your UI stays in sync with accepted values:
curl https://api.thebid.uk/v1/enums
Response includes referral_types, property_types, tenure_types, bedroom_options, property_status_categories, offer_statuses, document_types, webhook_event_types, and more. No auth required.
curl https://api.thebid.uk/v1/branches \
-H "Authorization: Bearer bid_at_..."
Response:
{
"data": [
{ "branch_id": "uuid", "branch_name": "London Office", "phone_number": "020 7123 4567" }
],
"request_id": "uuid"
}
Cache the branch_id values — you'll need one to submit referrals.
curl -X POST https://api.thebid.uk/v1/referrals \
-H "Authorization: Bearer bid_at_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"branch_id": "YOUR_BRANCH_ID",
"type": "new_instruction",
"property": {
"house_name_or_number": "42",
"street_name": "High Street",
"town_or_city": "London",
"post_code": "SW1A 1AA",
"property_type": "terraced",
"number_of_bedrooms": "3",
"number_of_bathrooms": "2",
"tenure": "freehold",
"vacant": true
},
"customers": [{
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "07700 900000",
"house_name_or_number": "10",
"street_name": "Oak Road",
"town_or_city": "London",
"post_code": "SW1A 2BB"
}],
"notes": "Seller wants a quick sale"
}'
Response (201):
{
"data": {
"referral_id": "uuid",
"status": "new",
"created_at": "2026-05-23T14:00:00Z"
},
"request_id": "uuid"
}
Once a referral is instructed and a property is created, you can track buyers and offers:
# Get property details (includes milestones)
curl "https://api.thebid.uk/v1/properties?property_id=PROPERTY_UUID" \
-H "Authorization: Bearer bid_at_..."
# Get buyers for a property
curl "https://api.thebid.uk/v1/buyers?property_id=PROPERTY_UUID" \
-H "Authorization: Bearer bid_at_..."
# Get a single buyer with full offer history
curl "https://api.thebid.uk/v1/buyers?buyer_id=BUYER_UUID" \
-H "Authorization: Bearer bid_at_..."
Receive real-time notifications instead of polling:
curl -X POST https://api.thebid.uk/v1/webhooks \
-H "Authorization: Bearer bid_at_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/bid",
"events": ["referral.status_changed", "property.status_changed", "buyer.offer_updated"]
}'
Store the returned secret immediately — it's shown once and used to verify payload signatures.
The Bid API uses OAuth 2.0 Client Credentials for machine-to-machine authentication.
client_id and client_secret from BidcURL:
curl -X POST https://api.thebid.uk/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"client_id": "your-uuid",
"client_secret": "bid_cs_your_secret",
"grant_type": "client_credentials"
}'
Node/TypeScript:
const res = await fetch("https://api.thebid.uk/v1/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client_id: process.env.BID_CLIENT_ID,
client_secret: process.env.BID_CLIENT_SECRET,
grant_type: "client_credentials",
}),
});
const token = await res.json();
// token.access_token, token.expires_in
C#:
var client = new HttpClient();
var response = await client.PostAsync(
"https://api.thebid.uk/v1/oauth/token",
new StringContent(JsonSerializer.Serialize(new {
client_id = Environment.GetEnvironmentVariable("BID_CLIENT_ID"),
client_secret = Environment.GetEnvironmentVariable("BID_CLIENT_SECRET"),
grant_type = "client_credentials"
}), Encoding.UTF8, "application/json"));
var result = await response.Content.ReadFromJsonAsync<TokenResponse>();
Tokens expire after 3600 seconds (1 hour). On receiving a 401 response, re-authenticate:
async function apiCall(url: string, options: RequestInit = {}) {
let token = await getToken(); // cached token
let res = await fetch(url, {
...options,
headers: { ...options.headers, Authorization: `Bearer ${token}` },
});
if (res.status === 401) {
token = await refreshToken(); // exchange credentials again
res = await fetch(url, {
...options,
headers: { ...options.headers, Authorization: `Bearer ${token}` },
});
}
return res;
}
client_secret to source controlclient_secret starts with bid_cs_ — if you see this in logs, rotate immediately429 Too Many Requests: back off and retry after the rate window resets (1 minute)Your client is issued with specific permissions. Calling an endpoint you don't have permission for returns 403 FORBIDDEN:
| Permission | Endpoints |
|---|---|
referrals:read |
GET /v1/referrals |
referrals:write |
POST /v1/referrals, PATCH /v1/referrals/{id} |
properties:read |
GET /v1/properties |
properties:write |
PATCH /v1/properties/{id} |
buyers:read |
GET /v1/buyers |
documents:read |
POST /v1/documents/{id}/access-url; also unlocks ?include=documents on GET /v1/properties (omitted silently without it) |
webhooks:manage |
GET/POST/DELETE /v1/webhooks |
The following endpoints require a valid token but no specific permission:
GET /v1/me — your account infoGET /v1/branches — your branch listThe following endpoints require no authentication:
GET /v1/health — API health checkGET /v1/enums — reference data / enum valuesAttribute marketplace buyers to your own campaigns, channels, or internal
IDs using the optional external_ref reference on every buyer.
Append ?external_ref=YOUR_REF to any marketplace or branded microsite
URL. A property URL works best, because the reference is attributed to that
specific property:
https://marketplace.thebid.uk/{slug}/property/{id}?external_ref=YOUR_REF
YOUR_REF is any free-text value you control (for example a campaign code
or your own internal customer ID). It is treated as opaque text, trimmed,
and capped at 128 characters.
Buyer record is stamped with your reference.The external_ref is returned on every buyer response, and you can list
all buyers carrying a reference:
curl https://api.thebid.uk/v1/buyers?external_ref=YOUR_REF \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Lookups are scoped to your own agent — you only ever see your own buyers, even if another partner happens to use the same reference value.
You can also set external_ref directly when creating a referral via the
API, and change it later with PATCH /v1/referrals/{id} or
PATCH /v1/properties/{id}; see the Referrals and Properties endpoints.
Receive near real-time notifications (typically within a minute) when referral status changes, properties reach milestones, or new buyers are registered.
curl -X POST https://api.thebid.uk/v1/webhooks \
-H "Authorization: Bearer bid_at_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/bid",
"events": ["referral.created", "referral.status_changed", "property.status_changed"]
}'
Requirements:
https://localhost, 127.0.0.1, 0.0.0.0, ::1, 10.*, 172.16-31.*, 192.168.*, 169.254.*, fe80:*) or .local domainssecret is returned once on creation — store it immediately| Event | Trigger |
|---|---|
referral.created |
New referral submitted |
referral.status_changed |
Referral status updated |
property.status_changed |
Property status category changes |
property.milestone_completed |
A milestone is marked complete |
property.document_added |
A document is added to a property |
property.marketplace_updated |
A property's marketplace listing is published/unpublished or its sale status changes |
buyer.created |
New buyer registered on a property |
buyer.offer_updated |
An offer is created (old_status null) or its status changes |
Every delivery uses this structure:
{
"version": "1",
"event_id": "uuid",
"event_type": "referral.status_changed",
"created_at": "2026-05-23T14:30:00Z",
"data": {
"referral_id": "uuid",
"old_status": "new",
"new_status": "in_progress"
}
}
Branch on version for forward compatibility. Treat missing version as "1".
No PII in payloads — only IDs and status values. Fetch full details via the GET endpoints.
Every delivery includes three headers:
X-Bid-Event — the event typeX-Bid-Timestamp — Unix epoch seconds at delivery timeX-Bid-Signature — sha256=<hex HMAC>The signed string is: timestamp + "." + raw_body
Node/TypeScript:
import crypto from "crypto";
function verifyWebhook(body: string, headers: Headers, secret: string): boolean {
const timestamp = headers.get("x-bid-timestamp")!;
const signature = headers.get("x-bid-signature")!;
// Reject stale deliveries (replay protection)
if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) {
return false;
}
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest("hex");
// timingSafeEqual throws on different lengths — guard first
const a = Buffer.from(signature ?? "");
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
C#:
bool VerifyWebhook(string body, string timestamp, string signature, string secret) {
if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - long.Parse(timestamp)) > 300)
return false;
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes($"{timestamp}.{body}"));
var expected = "sha256=" + BitConverter.ToString(hash).Replace("-", "").ToLower();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(signature),
Encoding.UTF8.GetBytes(expected));
}
Your endpoint has 5 seconds to respond. A non-2xx response or a timeout counts as a failed attempt and the event is retried with backoff. Retries are picked up by a once-a-minute scheduler, so each delay is a minimum:
| Failed attempt | Next retry no sooner than |
|---|---|
| 1 | 30 seconds |
| 2 | 2 minutes |
| 3 | 15 minutes |
| 4 | 1 hour |
| 5 | 4 hours |
After the 6th failed attempt (the initial delivery plus five retries) the event is marked as dead-letter (no further retries).
[Bid Webhook] → [Your HTTPS endpoint] → [Message Queue] → [Worker]
Accept the delivery immediately (return 200), queue it for processing. This avoids timeouts if your processing is slow.
Access property documents (legal packs, AML reports, etc.) via short-lived signed URLs.
Documents are included in property responses when you pass ?include=documents:
curl "https://api.thebid.uk/v1/properties?property_id=UUID&include=documents" \
-H "Authorization: Bearer bid_at_..."
Response includes:
{
"data": {
"property_id": "...",
"documents": [
{
"id": "document-uuid",
"name": "1716480000-abc123-legal-pack.pdf",
"type": "legal_pack",
"uploaded_at": "2026-05-23T10:00:00Z",
"modified_at": "2026-05-23T10:00:00Z"
}
]
}
}
Notes:
name is the verbatim last path segment from storage — do not assume a stable formatfile_size and mime_type are intentionally not included (not stored on the document record)properties:read and documents:read permissionscurl -X POST "https://api.thebid.uk/v1/documents/DOCUMENT_ID/access-url" \
-H "Authorization: Bearer bid_at_..."
Response:
{
"data": {
"url": "https://goiwmtesekbxfyxtsprl.supabase.co/storage/v1/object/sign/documents/...",
"expires_in": 120
}
}
The signed URL expires after 120 seconds. Download immediately:
const { data } = await bidApi.post(`/v1/documents/${docId}/access-url`);
const fileResponse = await fetch(data.url);
const buffer = await fileResponse.arrayBuffer();
fs.writeFileSync(`downloads/${filename}`, Buffer.from(buffer));
All errors return a JSON envelope with an error object containing code, message, and optionally details.
The one exception is POST /v1/oauth/token, which uses the OAuth 2.0 error shape (error, error_description) — see Authentication.
| HTTP | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR |
Request body failed validation or missing required header |
| 401 | UNAUTHORIZED |
Missing, invalid, or expired token |
| 403 | FORBIDDEN |
Token lacks required permission |
| 404 | NOT_FOUND |
Resource not found (or cross-agent access) |
| 405 | METHOD_NOT_ALLOWED |
HTTP method not supported on this endpoint |
| 409 | CONFLICT |
Duplicate referral detected |
| 429 | RATE_LIMITED |
Too many requests — wait and retry |
| 500 | INTERNAL_ERROR |
Server error (message is always "Internal server error") |
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": ["Missing required field: branch_id", "Missing required field: property.post_code"],
"request_id": "uuid"
}
}
details is only present on 4xx errors"Internal server error"When submitting a referral that matches an existing address:
Open referral exists:
{
"error": {
"code": "CONFLICT",
"message": "An open referral already exists for this address",
"details": {
"reason": "open_referral",
"referral_id": "existing-uuid",
"status": "in_progress"
}
}
}
Active property at address:
{
"error": {
"code": "CONFLICT",
"message": "An active property exists for this address",
"details": {
"reason": "active_property",
"property_id": "property-uuid",
"referral_id": "referral-uuid",
"status_category": "for_sale"
}
}
}
POST /referrals requires an Idempotency-Key header (max 255 characters, UUID recommended):
curl -X POST .../v1/referrals \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
...
POST /referrals = 400 VALIDATION_ERROR/webhooks, /documents) do not require idempotency keys409 CONFLICT with the message Request with this idempotency key is already being processed and no details — wait and retry201 and the two duplicate-address 409 responses are stored for replay. If the first attempt ended in any other error (400, 403, 500) the key stays claimed until it expires, so use a fresh key when you correct and resubmitconst res = await fetch(url, options);
if (res.status === 401) {
// Re-authenticate and retry
token = await getNewToken();
return retry(url, options, token);
}
if (res.status === 429) {
// Back off 60 seconds (rate window)
await sleep(60_000);
return retry(url, options, token);
}
if (res.status === 409) {
const { error } = await res.json();
// error.details.reason is open_referral or active_property.
// details is absent for the in-flight idempotency conflict — retry later.
}
if (res.status >= 500) {
// Retry with exponential backoff (Bid is having issues)
}
Returns the authenticated agent's details, branches, API key name, and permissions. Use this to verify your credentials and discover branch IDs.
{- "data": {
- "agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
- "agent_name": "Acme Estate Agents",
- "branches": [
- {
- "branch_id": "7a4e8e99-89f2-4a0f-b66c-fc595dda2dbc",
- "branch_name": "London Office",
- "phone_number": "020 7123 4567"
}
], - "api_key_name": "Production Key",
- "permissions": [
- "referrals:read",
- "referrals:write",
- "properties:read",
- "properties:write",
- "buyers:read"
]
}, - "request_id": "266ea41d-adf5-480b-af50-15b940c2b846"
}Returns all branches for the authenticated agent. Use the branch_id
values when creating referrals. No specific permission required — any
valid token works.
{- "data": [
- {
- "branch_id": "7a4e8e99-89f2-4a0f-b66c-fc595dda2dbc",
- "branch_name": "London Office",
- "phone_number": "020 7123 4567"
}
], - "request_id": "266ea41d-adf5-480b-af50-15b940c2b846"
}OAuth 2.0 Client Credentials grant (RFC 6749 §4.4).
Client credentials can be provided via:
Authorization: Basic base64(client_id:client_secret)client_id and client_secret in JSON or form-urlencoded bodyDo not use both methods simultaneously.
| grant_type required | string Value: "client_credentials" |
| client_id | string <uuid> Required unless using HTTP Basic auth |
| client_secret | string Required unless using HTTP Basic auth |
{- "access_token": "bid_at_a1b2c3d4e5f6...",
- "token_type": "Bearer",
- "expires_in": 3600,
- "permissions": [
- "referrals:read",
- "referrals:write",
- "properties:read"
]
}Returns referrals for the authenticated agent with pagination, newest first (created_at descending).
| page | integer >= 1 Default: 1 1-based page number. Must be a positive integer, otherwise 400 VALIDATION_ERROR. |
| per_page | integer [ 1 .. 200 ] Default: 50 Page size. Must be a positive integer (400 VALIDATION_ERROR otherwise); values above 200 are clamped to 200. |
| referral_id | string <uuid> Return a single referral by ID (or use |
| status | string |
| branch_id | string <uuid> Filter to one branch. A malformed (non-UUID) value returns an empty list, not an error. |
| created_after | string <date-time> Only referrals created at or after this ISO 8601 date-time. A malformed value returns 400 VALIDATION_ERROR. |
| external_ref | string Filter by exact external reference match. |
{- "data": {
- "referral_id": "2d946960-d63d-4137-b67f-4dc8d4a3067c",
- "created_at": "2019-08-24T14:15:22Z",
- "status": "new",
- "branch_id": "7a4e8e99-89f2-4a0f-b66c-fc595dda2dbc",
- "agent_owner": {
- "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",
- "name": "Kiersten Fewtrell",
- "email": "user@example.com"
}, - "type": "new_instruction",
- "property": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string",
- "property_type": "detached",
- "number_of_bedrooms": "string",
- "number_of_bathrooms": "string",
- "tenure": "freehold",
- "vacant": true,
- "lease_length": 0,
- "service_charge_ground_rent": 0
}, - "customers": [
- {
- "name": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string"
}, - "moving_abroad": "yes"
}
], - "notes": "string",
- "valuation_notes": "string",
- "external_ref": "string",
- "referral_agent_email": "string"
}, - "pagination": {
- "page": 0,
- "per_page": 0,
- "total": 0
}, - "request_id": "string"
}Submit a new property referral. Requires the Idempotency-Key header.
Returns 409 CONFLICT if a duplicate address is found.
The request body must contain property (object) and customers (array of 1–5 entries).
| Idempotency-Key required | string <= 255 characters Unique key to prevent duplicate submissions (UUID recommended) |
| branch_id required | string <uuid> |
| type required | string Enum: "new_instruction" "on_the_market_with_you_already" "sale_fallen_through" "valuation" |
required | object |
required | Array of objects [ 1 .. 5 ] items Array of customers (1-5 entries). Each entry requires name, phone, email, and address fields. |
| notes | string |
| valuation_notes | string |
| external_ref | string <= 128 characters Optional external reference (e.g. your own internal ID). Trimmed; an empty string is treated as absent. Can be changed later with |
| referral_agent_email | string Email of the user at your agency who made the referral. If it matches a user in your Bid agent account, the referral is assigned to them as agent owner; otherwise it is stored for reference only. |
{- "branch_id": "7a4e8e99-89f2-4a0f-b66c-fc595dda2dbc",
- "type": "new_instruction",
- "property": {
- "house_name_or_number": "string",
- "street_name": "string",
- "town_or_city": "string",
- "post_code": "string",
- "flat_number": "string",
- "locality": "string",
- "property_type": "detached",
- "number_of_bedrooms": "string",
- "number_of_bathrooms": "string",
- "tenure": "freehold",
- "vacant": true,
- "lease_length": 0,
- "service_charge_ground_rent": 0
}, - "customers": [
- {
- "name": "string",
- "email": "user@example.com",
- "phone": "string",
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string",
- "moving_abroad": "yes"
}
], - "notes": "string",
- "valuation_notes": "string",
- "external_ref": "string",
- "referral_agent_email": "string"
}{- "data": {
- "referral_id": "2d946960-d63d-4137-b67f-4dc8d4a3067c",
- "status": "new",
- "created_at": "2019-08-24T14:15:22Z"
}, - "request_id": "string"
}Same as GET /v1/referrals?referral_id= with the id in the path.
| referral_id required | string <uuid> The referral id |
{- "data": {
- "referral_id": "2d946960-d63d-4137-b67f-4dc8d4a3067c",
- "created_at": "2019-08-24T14:15:22Z",
- "status": "new",
- "branch_id": "7a4e8e99-89f2-4a0f-b66c-fc595dda2dbc",
- "agent_owner": {
- "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",
- "name": "Kiersten Fewtrell",
- "email": "user@example.com"
}, - "type": "new_instruction",
- "property": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string",
- "property_type": "detached",
- "number_of_bedrooms": "string",
- "number_of_bathrooms": "string",
- "tenure": "freehold",
- "vacant": true,
- "lease_length": 0,
- "service_charge_ground_rent": 0
}, - "customers": [
- {
- "name": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string"
}, - "moving_abroad": "yes"
}
], - "notes": "string",
- "valuation_notes": "string",
- "external_ref": "string",
- "referral_agent_email": "string"
}, - "request_id": "string"
}Update the external_ref on a referral. This is the only field that can be
changed through the API; null clears it. The change is recorded in the
referral audit log.
No Idempotency-Key is needed: repeating the request has the same result.
The linked property (once instructed) is not updated, so use its own PATCH if both should carry the reference.
The response is the same shape as GET /v1/referrals/{id}, so you see the current state.
The id may also be supplied as a query parameter, PATCH /v1/referrals?referral_id=…, with the same body and responses.
| referral_id required | string <uuid> The referral id |
| external_ref required | string or null <= 128 characters Your own reference for this record. Trimmed; |
{- "external_ref": "PB-123456"
}{- "data": {
- "referral_id": "2d946960-d63d-4137-b67f-4dc8d4a3067c",
- "created_at": "2019-08-24T14:15:22Z",
- "status": "new",
- "branch_id": "7a4e8e99-89f2-4a0f-b66c-fc595dda2dbc",
- "agent_owner": {
- "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",
- "name": "Kiersten Fewtrell",
- "email": "user@example.com"
}, - "type": "new_instruction",
- "property": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string",
- "property_type": "detached",
- "number_of_bedrooms": "string",
- "number_of_bathrooms": "string",
- "tenure": "freehold",
- "vacant": true,
- "lease_length": 0,
- "service_charge_ground_rent": 0
}, - "customers": [
- {
- "name": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string"
}, - "moving_abroad": "yes"
}
], - "notes": "string",
- "valuation_notes": "string",
- "external_ref": "string",
- "referral_agent_email": "string"
}, - "request_id": "string"
}Without property_id, returns the agent's properties newest first (created_at descending).
| page | integer >= 1 Default: 1 1-based page number. Must be a positive integer, otherwise 400 VALIDATION_ERROR. |
| per_page | integer [ 1 .. 200 ] Default: 50 Page size. Must be a positive integer (400 VALIDATION_ERROR otherwise); values above 200 are clamped to 200. |
| property_id | string <uuid> |
| status_category | string |
| branch_id | string <uuid> Filter to one branch. A malformed (non-UUID) value returns an empty list, not an error. |
| external_ref | string Filter by exact external reference match. |
| include | string Example: include=documents Set to |
{- "data": {
- "property_id": "05003a8a-8f3c-454b-8884-a906ec46f5f5",
- "created_at": "2019-08-24T14:15:22Z",
- "referral_id": "2d946960-d63d-4137-b67f-4dc8d4a3067c",
- "branch_id": "7a4e8e99-89f2-4a0f-b66c-fc595dda2dbc",
- "agent_owner": {
- "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",
- "name": "Kiersten Fewtrell",
- "email": "user@example.com"
}, - "status_category": "for_sale",
- "sale_type": "auction",
- "property": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string",
- "property_type": "string",
- "number_of_bedrooms": "string",
- "tenure": "string",
- "vacant": true
}, - "customers": [
- {
- "name": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string"
}, - "moving_abroad": "yes"
}
], - "external_ref": "string",
- "milestones": [
- {
- "milestone": "string",
- "completed_at": "2019-08-24T14:15:22Z",
- "is_skipped": true
}
], - "documents": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string",
- "type": "seller_agreement",
- "uploaded_at": "2019-08-24T14:15:22Z",
- "modified_at": "2019-08-24T14:15:22Z"
}
]
}, - "pagination": {
- "page": 0,
- "per_page": 0,
- "total": 0
}, - "request_id": "string"
}Same as GET /v1/properties?property_id= with the id in the path.
| property_id required | string <uuid> The property id |
| include | string Example: include=documents Set to |
{- "data": {
- "property_id": "05003a8a-8f3c-454b-8884-a906ec46f5f5",
- "created_at": "2019-08-24T14:15:22Z",
- "referral_id": "2d946960-d63d-4137-b67f-4dc8d4a3067c",
- "branch_id": "7a4e8e99-89f2-4a0f-b66c-fc595dda2dbc",
- "agent_owner": {
- "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",
- "name": "Kiersten Fewtrell",
- "email": "user@example.com"
}, - "status_category": "for_sale",
- "sale_type": "auction",
- "property": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string",
- "property_type": "string",
- "number_of_bedrooms": "string",
- "tenure": "string",
- "vacant": true
}, - "customers": [
- {
- "name": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string"
}, - "moving_abroad": "yes"
}
], - "external_ref": "string",
- "milestones": [
- {
- "milestone": "string",
- "completed_at": "2019-08-24T14:15:22Z",
- "is_skipped": true
}
], - "documents": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string",
- "type": "seller_agreement",
- "uploaded_at": "2019-08-24T14:15:22Z",
- "modified_at": "2019-08-24T14:15:22Z"
}
]
}, - "request_id": "string"
}Update the external_ref on a property. This is the only field that can be
changed through the API; null clears it. The change is recorded in the
property audit log.
No Idempotency-Key is needed: repeating the request has the same result.
The originating referral is not updated, so use its own PATCH if both should carry the reference.
The response is the same shape as GET /v1/properties/{id}, so you see the current state.
The id may also be supplied as a query parameter, PATCH /v1/properties?property_id=…, with the same body and responses.
| property_id required | string <uuid> The property id |
| include | string Example: include=documents As on GET — set to |
| external_ref required | string or null <= 128 characters Your own reference for this record. Trimmed; |
{- "external_ref": "PB-123456"
}{- "data": {
- "property_id": "05003a8a-8f3c-454b-8884-a906ec46f5f5",
- "created_at": "2019-08-24T14:15:22Z",
- "referral_id": "2d946960-d63d-4137-b67f-4dc8d4a3067c",
- "branch_id": "7a4e8e99-89f2-4a0f-b66c-fc595dda2dbc",
- "agent_owner": {
- "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",
- "name": "Kiersten Fewtrell",
- "email": "user@example.com"
}, - "status_category": "for_sale",
- "sale_type": "auction",
- "property": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string",
- "property_type": "string",
- "number_of_bedrooms": "string",
- "tenure": "string",
- "vacant": true
}, - "customers": [
- {
- "name": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "house_name_or_number": "string",
- "street_name": "string",
- "flat_number": "string",
- "locality": "string",
- "town_or_city": "string",
- "post_code": "string"
}, - "moving_abroad": "yes"
}
], - "external_ref": "string",
- "milestones": [
- {
- "milestone": "string",
- "completed_at": "2019-08-24T14:15:22Z",
- "is_skipped": true
}
], - "documents": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string",
- "type": "seller_agreement",
- "uploaded_at": "2019-08-24T14:15:22Z",
- "modified_at": "2019-08-24T14:15:22Z"
}
]
}, - "request_id": "string"
}Buyer and offer data endpoints, including the optional external_ref tracking reference (see the Buyer Tracking guide)
Three modes (provide one parameter; if several are supplied buyer_id takes precedence, then property_id, then external_ref):
?property_id=UUID — all buyers for a property (with latest offer for each)?buyer_id=UUID — single buyer with full offer history, address, company, and co-buyers?external_ref=STRING — all buyers carrying that external reference (with latest offer for each)In list modes (property_id or external_ref) you can additionally pass
?offer_status= to return only buyers whose latest offer has that status.
Ignored in buyer_id (single-buyer) mode, but must still be a valid value if supplied.
A buyer may be purchasing through a company and alongside additional
buyers (co-buyers). List modes include the company and a
co_buyer_count; the single-buyer mode additionally returns the full
co_buyers array.
Agent-scoped: only returns buyers for properties belonging to your agent.
Requires buyers:read permission.
| property_id | string <uuid> List all buyers for this property |
| buyer_id | string <uuid> Get a single buyer with full offer history |
| external_ref | string List all your buyers carrying this exact external reference. See the Buyer Tracking guide. |
| offer_status | string Enum: "pending" "accepted" "declined" "fee_paid" Optional. In list modes only, return only buyers whose latest offer has this status. Buyers with no offers are excluded when this filter is set. |
{- "data": [
- {
- "buyer_id": "2a8aae71-324e-470e-b3d1-589903cf2f45",
- "property_id": "05003a8a-8f3c-454b-8884-a906ec46f5f5",
- "first_name": "string",
- "last_name": "string",
- "phone_number": "string",
- "email_address": "string",
- "purchase_purpose": "string",
- "financing_method": "string",
- "has_property_to_sell": true,
- "money_from_abroad": true,
- "wants_solicitor_quote": true,
- "external_ref": "string",
- "company": {
- "name": "Acme Holdings Ltd",
- "number": "12345678"
}, - "co_buyer_count": 1,
- "created_at": "2019-08-24T14:15:22Z",
- "latest_offer": {
- "offer_id": "d5a7a5b7-a4a3-49e7-9c69-b44d2cbe15cf",
- "amount": 250000,
- "status": "pending",
- "created_at": "2019-08-24T14:15:22Z"
}
}
], - "request_id": "266ea41d-adf5-480b-af50-15b940c2b846"
}Returns every enum/list value the API accepts or returns. Use this to populate dropdowns, validate inputs, or stay in sync with supported values. No authentication required.
{- "data": {
- "referral_statuses": [
- "string"
], - "referral_types": [
- "string"
], - "property_types": [
- "string"
], - "tenure_types": [
- "string"
], - "sale_types": [
- "auction"
], - "bedroom_options": [
- "string"
], - "bathroom_options": [
- "string"
], - "property_status_categories": [
- "string"
], - "property_milestones": [
- "agreement_sent"
], - "offer_statuses": [
- "pending"
], - "document_types": [
- "seller_agreement"
], - "webhook_event_types": [
- "string"
]
}, - "request_id": "266ea41d-adf5-480b-af50-15b940c2b846"
}Returns a short-lived signed URL (120 seconds) for downloading the document. Each access is audit-logged.
Scope: documents:read. The document_id may be given as the path segment or as
?document_id= on POST /v1/documents/access-url. A malformed or unknown id, or a
document on another agent's property, returns 404.
| document_id required | string <uuid> |
{- "request_id": "string"
}Returns subscriptions scoped to the authenticated OAuth client. Each client manages its own independent set of webhook subscriptions.
{- "data": [
- {
- "subscription_id": "aa11a4c2-a467-43db-b413-c4ab0f5cf627",
- "events": [
- "string"
], - "secret": "string",
- "is_active": true,
- "created_at": "2019-08-24T14:15:22Z"
}
], - "request_id": "string"
}Register a URL to receive event notifications. The secret (prefixed whsec_) is returned
once on creation — store it securely for signature verification.
Subscriptions are scoped per OAuth client.
| url required | string <uri> Must be a public HTTPS URL (no loopback, private or link-local addresses, no |
| events required | Array of strings Items Enum: "referral.created" "referral.status_changed" "property.status_changed" "property.milestone_completed" "property.document_added" "property.marketplace_updated" "buyer.created" "buyer.offer_updated" |
{- "events": [
- "referral.created"
]
}{- "data": {
- "subscription_id": "aa11a4c2-a467-43db-b413-c4ab0f5cf627",
- "events": [
- "string"
], - "secret": "string",
- "is_active": true,
- "created_at": "2019-08-24T14:15:22Z"
}, - "request_id": "string"
}