Developer Documentation
Platform Overview
Authentication
API Services
Overview Accounts Accounts: Associations Accounts: Metadata Accounts: Profile Appstore: Users Broker Distributions Broker Tours Consumers Consumers: Linked Agents Contacts Contacts: Activity Contacts: Export Contacts: Portal Accounts Contacts: Tags Developers: Authorizations Developers: Billing Summary Developers: Change History Developers: Domains Developers: Identities Developers: Keys Developers: Roles Developers: Syndications Developers: Templates Developers: Usage Detail Developers: Usage Summary Devices Flexmls: Email Links Flexmls: Listing Meta Field List Translations Flexmls: Listing Meta Origins Flexmls: Listing Meta Translations Flexmls: Listing Reports Flexmls: Mapping Layers Flexmls: Mapping Shapegen IDX IDX Links Incomplete Listings Incomplete Listings: Documents Incomplete Listings: Documents Metadata Incomplete Listings: Document Uploads Incomplete Listings: Floor Plans Incomplete Listings: Floor Plans Metadata Incomplete Listings: Floor Plan Uploads Incomplete Listings: Photos Incomplete Listings: Photos Metadata Incomplete Listings: Photo Uploads Incomplete Listings: Required Documents Incomplete Listings: Rooms Incomplete Listings: Tickets Incomplete Listings: Units Incomplete Listings: Videos Incomplete Listings: Videos Metadata Incomplete Listings: Virtual Tours Incomplete Listings: Virtual Tours Metadata Listing Carts Listing Carts: Portal/VOW Carts Listings Listings: Clusters Listings: Documents Listings: Documents Metadata Listings: Document Uploads Listings: Floor Plans Listings: Floor Plans Metadata Listings: Floor Plan Uploads Listings: Historical Listings: History Listings: Hot Sheet Parameters Listings: Notes Listings: Open Houses Listings: Photos Listings: Photos Metadata Listings: Photo Uploads Listings: Rental Calendar Listings: Required Documents Listings: Rooms Listings: Rules Listings: Search Parameters Listings: Tickets Listings: Tour of Homes Listings: Units Listings: Validation Listings: Videos Listings: Videos Metadata Listings: Virtual Tours Listings: Virtual Tours Metadata Listing Meta: Custom Field Groups Listing Meta: Custom Fields Listing Meta: Field Order Listing Meta: Field Relations Listing Meta: Property Types Listing Meta: Rooms Listing Meta: Standard Fields Listing Meta: Units Market Statistics News Feed: Groups Notifications Open Houses Overlays Overlays: Geometries Portals Portals: Listing Categories Portals: Metadata Preferences Registered Listings Saved Searches Saved Searches: Provided Saved Searches: Restrictions Saved Searches: Tags Search Templates: Quick Searches Search Templates: Sorts Search Templates: Views Shared Links System Info System Info: Languages System Info: Search Templates
Webhooks
Supporting Documentation
Examples
RESO Web API
RETS
Terms of Use

Webhooks Overview

Describes how the Spark platform delivers outbound webhook events to third-party systems when resources change in an upstream MLS.

 
  1. Overview
  2. Event Types
  3. Payload Format
  4. Verifying Authenticity
  5. Request Headers
  6. Delivery, Retries, and Timeouts
  7. Filtering
  8. Catch-up Replay
  9. Getting Subscribed
  10. Quick Reference
 

Overview

When a resource changes in an upstream MLS (a listing, a member/agent record, or an office record), the Spark platform emits an entity event. For every webhook subscription that matches the event's MLS and resource type, an HTTP POST request is delivered to your configured URL containing a RESO-formatted payload.

Events follow the RESO Web API Entity Event convention, so the payload shape will be familiar if you already consume RESO data.

TransportHTTPS POST
Content-Typeapplication/json
AuthenticityHMAC-SHA256 signature in the Signature header
RetriesUp to 3 attempts with exponential backoff
TimeoutYour endpoint must respond within ~2 seconds
SuccessAny 2xx response
User-AgentNotificationSystem/0.1
 

Event Types

Every event describes a change to one of three resource types:

Resource Type ResourceName Value Description
Property Property A listing was created or modified
Member Member A member/agent record was created or modified
Office Office An office record was created or modified

A single subscription can receive any combination of these. Which events you receive is controlled by resource-type and MLS filters on your subscription (see Filtering).

 

Payload Format

Each delivery is a JSON document containing a RESO entity-event envelope. The top-level object carries a @reso.context marker and a value array. Note that value can contain multiple events within the same POST body — each of them should be consumed and processed by your system. When a single event is delivered, value still uses the bulk delivery format even if it only contains a single entry.

{
  "@reso.context": "urn:reso:metadata:2.0:resource:entityevent",
  "value": [
    {
      "EntityEventSourceSystemInternalID": "123",
      "EntityEventTimestamp": "2026-06-10T14:32:05+00:00",
      "ResourceName": "Property",
      "ResourceRecordKey": "20260514111441584701000000",
      "ResourceRecordModificationTimestamp": "2026-06-10T14:31:58+00:00",
      "SystemID": "M1000123456"
    }
  ]
}

Event Object Fields

Field Type Always Present Description
ResourceName string yes Resource type: Property, Member, or Office.
ResourceRecordKey string yes The unique key of the changed record in the source system. Use this to fetch the full record.
EntityEventTimestamp string (ISO 8601) yes When the event was generated.
EntityEventSourceSystemInternalID string yes Internal identifier of the source MLS/system that produced the event.
SystemID string | null yes RESO OUID value.
ResourceRecordModificationTimestamp string (ISO 8601) no When the underlying record was last modified. Included only when known.

Forward Compatibility

Event objects may carry extra fields beyond those listed above, and keys are serialized in alphabetical order. Treat the payload as forward-compatible: ignore fields you do not recognize rather than rejecting the payload, and do not depend on key ordering.

What a Webhook Does Not Contain

Events are notifications of change, not full data records. They tell you what changed (ResourceRecordKey) and when. To obtain the current state of the record, fetch it from the source system or data API using the ResourceRecordKey.

 

Verifying Authenticity

Every request includes a Signature header so you can confirm it came from the Spark platform and was not tampered with in transit.

To verify, compute the HMAC-SHA256 of the raw request body bytes using your shared secret and compare it to the Signature header using a constant-time comparison.

Use the Raw Body

Compute the signature against the raw body before any JSON parsing or re-serialization. Re-encoding the JSON can change whitespace or key order and will produce a different digest.

Example: PHP

$payload   = file_get_contents('php://input');
$signature = $_SERVER['HTTP_SIGNATURE'] ?? '';
$expected  = hash_hmac('sha256', $payload, $yourSecret);

if (! hash_equals($expected, $signature)) {
    http_response_code(401);
    exit;
}

Example: Node.js

const crypto = require('crypto');

function verify(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody) // rawBody must be the exact bytes received
    .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Example: Python

import hmac, hashlib

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_header or "")
 

Request Headers

Every delivery includes at least the following headers:

Header Value Notes
Content-Type application/json
Signature <hex HMAC-SHA256> See Verifying Authenticity.
User-Agent NotificationSystem/0.1
Authorization Bearer <token> Optional. Sent only if a bearer token was configured for your subscription.

Additional custom headers can be configured per subscription on request (for example, an API key your gateway expects). If configured, these are included on every delivery.

 

Delivery, Retries, and Timeouts

Idempotency & Ordering

Circuit Breaker

To protect both sides during sustained outages, a subscription that accumulates a large number of consecutive final failures will be automatically disabled, and the contact email on the subscription will be notified. Once your endpoint is healthy again, contact us to re-enable the subscription. Missed events during the outage can often be recovered via Catch-up Replay.

 

Filtering

Each subscription has two independent filters:

You only receive an event if it matches both filters on your subscription.

 

Catch-up Replay

If your endpoint was unavailable for a period (maintenance, an outage, or a tripped circuit breaker), historical events can be replayed for a given time window so you can backfill what you missed. Replayed deliveries are identical in shape to live deliveries. Contact api-support@sparkplatform.com with the affected time range to request a replay.

 

Getting Subscribed

Webhook subscriptions are provisioned by our team. We do not currently offer a self-service registration API. To get started, contact api-support@sparkplatform.com with the following information:

  1. Destination URL — the HTTPS endpoint that will receive POST requests.
  2. Resource typesProperty, Member, Office, or any combination.
  3. MLS scope — the MLS code(s) you should receive events for.
  4. Contact email — for delivery-failure and circuit-breaker notifications.
  5. Auth needs (optional) — a bearer token and/or any custom headers your endpoint requires.

Once provisioned, you will receive the signing secret used to verify the Signature header. Keep this secret confidential; treat it like a password.

 

Quick Reference

POST <your-endpoint>
Content-Type: application/json
Signature: <hex hmac-sha256 of raw body, keyed with your secret>
User-Agent: NotificationSystem/0.1
Authorization: Bearer <token>        # only if configured

{
  "@reso.context": "urn:reso:metadata:2.0:resource:entityevent",
  "value": [
    {
      "ResourceName": "Property",
      "ResourceRecordKey": "20260514111441584701000000",
      "EntityEventTimestamp": "2026-06-10T14:32:05+00:00",
      "EntityEventSourceSystemInternalID": "123",
      "SystemID": "M1000123456",
      "ResourceRecordModificationTimestamp": "2026-06-10T14:31:58+00:00"
    }
  ]
}