API Changelog

Stay up to date with API changes and improvements.

All notable changes to the Propstreet Public API are documented here. This project uses Semantic Versioning.


[1.6.0] - May 2026

Added

Note revisions (archive / unarchive / supersede):

  • PATCH /api/v1/network/contacts/{id}/notes/{noteId} — archive, unarchive, or supersede a note on a contact.
  • PATCH /api/v1/network/companies/{id}/notes/{noteId} — same for a company.
  • PATCH /api/v1/projects/{projectId}/prospects/{id}/notes/{noteId} — same for a prospect note inside a project.
  • PATCH /api/v1/projects/{id}/notes/{noteId} — same for a project-level note.
  • Body is body-discriminated on action: archive, unarchive, or supersede.
    • archive soft-hides the note and stamps archivedAt. Idempotent — re-archiving an already-archived note is a no-op 204.
    • unarchive clears archivedAt. Returns 400 when the note has already been superseded (unarchive the head of the chain instead).
    • supersede expires the predecessor and creates a new revision that points back at it. Send a supersede object with the fields to change; unset fields inherit from the predecessor. The supersede object also accepts contentFormat (plaintext or markdown) on every note type — contact, company, prospect, and project notes.
  • If-Match is required on these four endpoints. Unlike every other V1 PATCH/DELETE (where If-Match is best-effort), a missing or blank If-Match returns 428 Precondition Required (/help/precondition-required). A stale If-Match still returns 412 Precondition Failed (/help/precondition-failed). Fetch the resource, copy the ETag, then retry. A lost optimistic-concurrency race — a concurrent revision committed between your read and write — returns 409 Conflict (/help/concurrency-conflict); re-fetch the ETag and retry. These PATCH endpoints are not replay-cached by Idempotency-Key; use the ETag precondition for retry safety.

Project notes (create):

  • POST /api/v1/projects/{id}/notes — log a broker-authored note directly on a project, mirroring the existing prospect-note create endpoint.

Note response fields (read):

  • GET action/note responses now expose supersedesActivityId (the predecessor a revision replaced), supersededByActivityId (the successor that replaced this note), archivedAt (when the note was archived; null when active), and contentFormat (markdown for markdown notes; null for plain text).
  • NetworkAction.details is now a typed OpenAPI union. Use details.kind (note or audit) to discriminate the payload; generated clients expose the note revision fields on the note details type.
  • The contact/company action endpoints (GET /api/v1/network/contacts/{id}/actions, GET /api/v1/network/companies/{id}/actions), the project action endpoint (GET /api/v1/projects/{id}/actions), and the prospect action endpoint (GET /api/v1/projects/{projectId}/prospects/{id}/actions) accept two new opt-in query params: include_archived (default false) surfaces archived notes alongside their archivedAt, and include_superseded (default false) surfaces the full revision chain with the entries linked via supersedesActivityId / supersededByActivityId. Both default off, so existing integrations see no change.

Project & prospect activity timelines (new):

  • GET /api/v1/projects/{id}/actions and GET /api/v1/projects/{projectId}/prospects/{id}/actions are new full activity timelines. Each row carries a type of note, chat, or audit and a typed details union discriminated by details.kind (which mirrors type). chat rows are broker↔counterparty conversation entries (a message, a shared file, or a logged call) — the message body itself is private and is never returned; only a shared file is exposed (on details.kind = chat). audit rows carry a stable dot-notation event string (e.g. project.created, prospect.accepted, room.file_uploaded) — the action event vocabulary is a superset of the webhook event vocabulary, so the same names you subscribe to also appear inline on the timeline. (chat rows carry an event too, e.g. prospect.message_sent / prospect.file_shared / prospect.call_logged.)
  • New query params on both endpoints: type (all | notes | chat | audit, default all) filters by row type, and include_rollups (default true) controls cross-entity interleaving. On the project timeline include_rollups=true interleaves the project's prospect and deal-room activity (each rolled-up row carries a subject of prospect or room); on the prospect timeline it interleaves the prospect's deal-room activity and its linked contact/company notes (subject of room, contact, or company). Set include_rollups=false to scope the timeline to the entity's own actions (no subject).
  • The row id (and the supersedesActivityId / supersededByActivityId link fields) on every action timeline — project, prospect, offer, and the contact/company (network) endpoints — is now a globally-unique, opaque action id, uniform across the whole API and MCP. Treat it as opaque and pass it back verbatim — it is the same id the note PATCH endpoints accept and the same id MCP list_activities / update_activity use.

New endpoint — offer activity timeline (investor/seller-facing):

  • GET /api/v1/offers/{id}/actions — the recipient's read of an offer's activity timeline (the counterparty view of the prospect timeline the broker sees in-app). The offer is the caller's own identity, so its deal-room activity folds in with no subject, and conversation entries appear as type = chat. Only what the recipient is entitled to see appears — broker-private notes and broker-internal events never surface here. Read-only: rows carry no etag. Supports the same type, include_rollups, include_archived, and include_superseded params.

Changed

  • V1 ETag headers are now strong ("…") rather than weak (W/"…"). Strong ETags are usable with If-Match strong comparison per RFC 9110. If you previously stripped a leading W/ before sending If-Match, that step is no longer needed.

[1.5.0] - May 2026

Added

Teasers:

  • GET /api/v1/projects/{id}/teaser — broker-authored teaser for a project. Returns the visibility matrix (options), the allowed display modes per field (template), and the investor-facing projection.
  • GET /api/v1/properties/{id}/teaser — pre-market teaser for a property. Returns a thin property_added envelope (with nudge.code = no_pre_market_yet) when no pre-market record exists yet.
  • GET /api/v1/projects/{id}/rooms/{roomId}/teaser — per-room teaser inside a deal-room. Returns a virtual default room teaser plus nudge.code = room_teaser_not_designed when the room teaser hasn't been designed yet (the first PATCH pins a real row).
  • PATCH /api/v1/projects/{id}/teaser, PATCH /api/v1/properties/{id}/teaser, PATCH /api/v1/projects/{id}/rooms/{roomId}/teaser — design-time write surface. Standard PATCH semantics: send only the fields you want to change; omitted fields are unchanged. Auto-creates the pre-market record or room teaser on first PATCH. Free-plan callers receive clamped results.
  • Snapshot tags field — what investors see on the teaser. Send a list to write that snapshot verbatim (deduped and clamped to template.tags); [] writes an explicit empty snapshot; null resets the snapshot to the property's current template pool. Snapshots are byte-stable: subsequent property-tag edits do not change what investors see on an existing teaser — the author writes a new teaser to pick them up.
  • If-Match / ETag parity with the rest of V1 — every GET returns an ETag header and etag body field; every PATCH supports If-Match and returns 412 Precondition Failed on stale; omit If-Match to skip the precondition check (V1 convention).
  • All three routes return the same teaser shape: stage, timestamps (publishedUtc, verifiedUtc, communicatedUtc), options, template, projection, optional nudge, and an etag field.
  • Stage precedence: communicatedUtc > verifiedUtc > publishedUtc > drafting > property_added.
  • Lifecycle transitions (publish, unpublish, verify, communicate) are not available via the API. Perform them in the Propstreet app.

[1.4.0] - April 2026

Added

Identity & Teams:

  • GET /api/v1/auth/me — fetch the authenticated caller's identity, capabilities, primary team, and any other teams the caller belongs to or has been invited to — all in a single call. Returns primaryTeam: null and secondaryTeams: [] for callers with no active team (e.g. pre-onboarding or service accounts).
  • GET /api/v1/teams/{id} — fetch a team. Returns team details plus an inline roster (up to 50 active members) when the caller is an active member. Callers with a pending invite see the team's details without the member list.
  • GET /api/v1/teams/{id}/members — paginated list of a team's active members, filterable by role=admin|member. Stable keyset ordering for cursor pagination.

MCP Identity Tool:

  • get_me — same payload as /api/v1/auth/me, returned as structured content so AI agents can bootstrap with a single tool call.

Offers:

  • GET /api/v1/offers — list offers the investor has received. Filter by all, active, ongoing, inactive, removed, unread, or unanswered. filter=unanswered returns the single teaser currently awaiting a response.
  • GET /api/v1/offers/{id} — fetch a single offer with the anonymized teaser (asset categories, locality, country, price / yield / area / year ranges, zoning, tags, conversions). The broker contact card is populated after the investor accepts. Unknown or inaccessible ids return 404 Not Found.
  • teaser.title — a short human-readable label composed from asset categories and locality in the caller's UI language (e.g. "Industrial, Tullinge (SE)"). Null when both are unknown.
  • status: unanswered, accepted, rejected, expired, removed, closed.
  • capabilities: "chat" when broker messaging is available.
  • Teaser range fields expose min / max (and currency / scale where applicable). Exact values appear as min == max.
  • Cursor pagination via the shared data + page.{nextCursor, pageSize, hasMore} envelope.

MCP Offer Tools:

  • list_offers — list offers with the same filters as the REST endpoint.
  • get_offer — fetch a single offer by Smart Ref (offer id or id= / ext= selector).

Mandates:

  • /api/v1/mandates — full CRUD for investor acquisition mandates, including cursor pagination, delta sync (updated_since, include_deleted), ETag-based concurrency, and idempotent create support.
  • POST /api/v1/mandates/search — search mandates by geography or location.
  • POST /api/v1/mandates/batch/get — fetch up to 100 mandates by id in a single call.
  • Mandate responses expose asset criteria (categories, investmentSize, yearBuilt, parcelArea), geographic area, country, and two parallel tag arrays — positiveTags (desirable attributes) and negativeTags (deal-breakers) — which are mutually exclusive.
  • Geographic input accepts geography (GeoJSON Polygon / MultiPolygon), near + radius (point and radius), or location (free-text locality resolved server-side). Exactly one mode is allowed per request. Write responses echo a resolvedLocation hint; GET responses do not.
  • Mandate responses include the standard audit/sync fields: etag, changeType, changeOrigin, and deletedUtc.
  • External reference support: attach third-party ids via externalRefs (namespace + externalId pairs) for linking mandates to records in your CRM or other systems.
  • PATCH is partial: omitted fields are unchanged; sent fields replace. Categories are full-replace.

MCP Mandate Tools:

  • list_mandates — list mandates with optional country, category, tag, name, and updatedSince filters.
  • search_mandates — geography- or location-based search.
  • get_mandate — fetch a single mandate by Smart Ref (mandate id, name, id= / name= / ext= selector).
  • create_mandate — create a mandate with categories, ranges, tags, external refs, and geographic criteria. Supports idempotency.
  • update_mandate — partial update with the same semantics as the REST endpoint.
  • delete_mandate — delete a mandate.

Deprecated

Project field mandateexclusivity:

  • project.mandate is now aliased by the new project.exclusivity field. Both names carry identical values (exclusive, non_exclusive, other) and are populated on every read response. Webhook project.updated events list both names in changes when the value changes.
  • Write requests (POST /api/v1/projects, PATCH /api/v1/projects/{id}) accept either field. Sending both mandate and exclusivity with non-null values on the same request is rejected with 422 validation_failed; sending neither leaves the value unchanged.
  • MCP tools create_project and update_project accept only exclusivity on write. Reads still return both mandate and exclusivity on every project object for now.
  • mandate will be removed entirely in a future minor version. Migrate integrations to exclusivity now.

[1.3.0] - March 2026

Added

Properties:

  • /api/v1/properties - Full CRUD for property assets, including cursor pagination, delta sync (updated_since, include_deleted), ETag-based concurrency, and idempotent create support.
  • GET /api/v1/projects/{id}/properties - Returns properties linked to a project, using the same public property schema as the standalone property endpoints.
  • Property responses now expose audit/sync fields consistent with other versioned v1 resources: etag, changeType, changeOrigin, and deletedUtc.

Company Duplicate Detection:

  • POST /api/v1/companies/find-duplicates — Check for potential duplicate companies before creating. Accepts name (required), domain, countryCode, and linkedInUrl. Returns matching candidates ordered by relevance (same-country matches first when countryCode is provided). Each result includes id, name, domain, and countryCode.

Company Fields:

  • linkedInUrl on companies — Company LinkedIn URL, available on read (GET), create (POST), and update (PATCH). Searchable and tracked in audit history.

MCP Property Tools:

  • list_properties - List broker properties with optional country/category filters.
  • get_property - Fetch a property by smart reference (id, name, id=, name=).
  • create_property - Create a property with idempotency support, description, location, and tenure fields.
  • update_property - Update name, description, country, locality, coordinates, and tenure, including coordinate clearing.

MCP Server:

  • create_company now performs duplicate detection before creation and returns a conflict with candidate details unless force=true. This prevents accidental duplicate company creation by AI agents.

Changed

Property Contract Clarifications:

  • Area write payloads must include unit: "sqm" on parcelArea, zoning.gfa, rentedArea, and vacantArea. Read responses remain normalized to sqm.
  • Property tags are documented enum-backed snake_case values; unknown tags are rejected with 400 Validation failed instead of being silently dropped.
  • Property PATCH location updates preserve tri-state semantics for nested country and locality, and late write collisions now return 409 Conflict.

Prospect Tag Filters:

  • Breaking change: GET /api/v1/projects/{id}/prospects now accepts repeatable tags query parameters with OR semantics instead of a single tag string. Example: ?tags=vip&tags=nordic.

Property Tag Filters:

  • Breaking change: GET /api/v1/properties now accepts repeatable tags query parameters with OR semantics instead of repeatable tag. Example: ?tags=core&tags=value_add.

MCP Tool Tag Filters:

  • Breaking change: MCP list tools now use array-based tags parameters consistently. list_companies, list_contacts, and list_prospects no longer accept string/CSV tag filters.

See the MCP Guide for tool-level details and the generated OpenAPI schema for the full property resource contract.


[1.2.1] - February 2026

Changed

Contact/Company Deletion — Automatic Prospect Removal:

  • DELETE /api/v1/network/contacts/{id} and DELETE /api/v1/network/companies/{id} no longer reject deletion when the entity is a prospect on active projects. Instead, the entity is automatically removed as a prospect from all active projects before deletion.
  • Each prospect removal fires a prospect.updated webhook with action.type: "removed". The contact/company deletion itself fires contact.deleted / company.deleted.
  • Timeline history for affected projects is preserved — prospects remain visible in project activity logs.
  • MCP delete_contact / delete_company preview (preview=true) now includes affected_projects showing which projects will be affected.

Fixed

Delete Webhook Payloads:

  • project.deleted and prospect.deleted webhook payloads now include external_refs — the list of active external references at deletion time. CRM integrators need these to identify and clean up the corresponding record in their system, since GET returns 404 after deletion.
  • Prospects are now cascade-deleted when their parent project is deleted, ensuring prospect.deleted webhooks fire for each affected prospect.
  • Re-adding a previously removed prospect to a project now works correctly. Previously, the unique index blocked re-addition of deleted prospects.

[1.2.0] - February 2026

Added

Projects & Prospects:

  • /api/v1/projects - Full CRUD for real estate transaction projects
  • /api/v1/projects/{id}/prospects - Manage investor prospects on project prospect lists
  • Unified prospects API - Returns both prospect-list and communicated prospects in one endpoint
  • Capability flags - communicated, accepted, chat, invitation indicate prospect status
  • Classification pipeline - Track prospects: not_contactedcontactedinterestedbidder
  • Filtering - Query prospects by capability, classification, or tag
  • Cursor-based pagination with delta sync (updated_since, include_deleted)
  • Idempotency support via Idempotency-Key header
  • ETag-based optimistic concurrency control

Associated Projects:

  • GET /api/v1/network/contacts/{contactId}/projects — List projects where a contact is a prospect
  • GET /api/v1/network/companies/{companyId}/projects — List projects where a company is a prospect
  • Cursor-based pagination with page_size and cursor parameters
  • Filter by status (open/closed) and classification (prospect pipeline stage)

Webhook Events:

  • project.created - Fired when a new project is created
  • project.updated - Fired when project fields change (name, status, classification, etc.)
  • project.deleted - Fired when a project is deleted
  • prospect.created - Fired when a prospect is added to a project
  • prospect.updated - Fired when prospect classification or other fields change
  • prospect.deleted - Fired when a prospect is deleted from a project
  • Webhook payloads include action field when changes were triggered by business actions (e.g., {"action": {"type": "classified"}})

See the Projects & Prospects Guide for complete documentation. See the Webhooks Guide for webhook payload reference.

MCP Server:

  • Structured content — All MCP tool responses now include structuredContent (typed JSON element, MCP 2025-06-18+) alongside the existing text content. Agents can parse responses programmatically without re-deserializing from text. No client-side changes required.

Link Webhook Payloads:

  • Link event webhooks (link.created, link.updated, link.deleted) now include contact_id and company_id fields in the payload. This is critical for link.deleted events where the link resource is no longer retrievable via the API.
  • Re-creating a previously deleted link (same contact-company pair) now correctly triggers a link.created webhook event. Previously, reactivating a deleted link was silently treated as an update with no webhook fired.

Changed

OpenAPI Schema Cleanup:

  • Removed Dto suffix from schema component names: WebhookDtoWebhook, WebhookCreatedDtoWebhookCreated, WebhookDeliveryDtoWebhookDelivery, EmailEntryRequestDtoEmailEntryRequest, PhoneEntryRequestDtoPhoneEntryRequest, CompanyLinkDtoCompanyLink, ContactLinkDtoContactLink
  • Added property descriptions to all public API schema components
  • Cleaned up internal terminology in API descriptions

Note: JSON property names and HTTP contracts are unchanged. If your code generator produces class names from OpenAPI schema IDs, update your generated code.


[1.1.0] - February 2026

Added

Company Fields:

  • homepageUrl - Company homepage URL
  • domain - Company domain, can be:
    • Set directly via API (e.g., "domain": "acme-corp.com") when only domain is known
    • Auto-extracted from homepageUrl when URL is provided (e.g., "acme-corp.com" from "https://www.acme-corp.com/about")
    • When both are provided, homepageUrl takes precedence for domain derivation
  • Both fields now searchable and sortable via /api/v1/network/companies:search
  • Domain changes tracked in audit history and webhook payloads

Profile Pictures:

  • profilePictureUrl on contacts and companies - Download URL when profile picture is set
  • Profile picture changes now tracked in webhook changed_fields as profile_picture

Webhooks:

  • Real-time event notifications for contacts, companies, and links
  • 9 event types: contact.created/updated/deleted, company.created/updated/deleted, link.created/updated/deleted
  • HMAC-SHA256 signature verification via Standard Webhooks headers (webhook-id, webhook-timestamp, webhook-signature)
    • Note: Initially shipped as X-Propstreet-Signature; migrated to Standard Webhooks in v1.2.0
  • Exponential backoff retry (9 attempts over ~35 hours)
  • Auto-disable after 10 consecutive failures
  • Delivery history tracking via /api/v1/webhooks/{id}/deliveries
  • Secret regeneration endpoint
  • changed_fields reference documentation for all resource types

See the Webhooks Guide for complete documentation.


[1.0.0] - December 2025

Added

Core Resources:

  • Contacts - Full CRUD, search, batch operations
  • Companies - Full CRUD, search, batch operations
  • Links - Contact-Company relationships
  • Network Actions - Activity notes on contacts and companies
  • Files - Document attachments (up to 100MB)

Authentication:

  • OAuth 2.0 with Client Credentials flow for production integrations
  • Personal Access Tokens for testing and scripts
  • Bot Users for dedicated integration accounts

Sync Features:

  • Delta sync with updated_since and include_deleted
  • Cursor-based pagination (up to 500 per page)
  • ETag-based concurrency for safe updates
  • External references for CRM ID tracking

Rate Limits:

  • OAuth Clients: 600 requests/minute
  • Personal Access Tokens: 60 requests/minute

Documentation:

  • Comprehensive guides at docs.propstreet.com
  • Interactive API explorer
  • Code samples for TypeScript, C#, Python

Roadmap

Upcoming Features

We're continuously improving the API. Contact feedback@propstreet.com to share your integration needs.


Deprecation Policy

We follow a deprecation policy to ensure stability:

  1. Deprecation notice - Announced in changelog and API responses
  2. Migration period - Minimum 6 months before removal
  3. Removal - After migration period ends

We'll always provide migration guidance and support.


Subscribe to Updates

For API announcements: