Webhooks

HomeDash POSTs JSON to URLs you register, signed with HMAC-SHA256 so you can trust the body. There are 43 event types, and one endpoint can subscribe to up to 32 of them. Subscriptions are managed in the Developer Dashboard, not via the API.

New here? Register an endpoint in Dashboard → Developer → Webhooks and use the Test button to fire a sample payload at it before going live.

The envelope

Every webhook delivery has the same outer shape:

JSON
{
  "id": "evt_<uuid>",
  "event": "rent.overdue",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "<uuid>",
  "data": { ... event-specific fields ... }
}
  • id — stable per event, prefixed evt_. Use it as your idempotency key — HomeDash may retry the same id up to 3 times.
  • event — one of the 9 types listed below.
  • _test: true at the top level (and inside data) marks a test delivery from the Developer Dashboard. Real deliveries omit this.

Request headers

HTTP
POST /your-endpoint HTTP/1.1
Content-Type: application/json
X-HomeDash-Signature-256: sha256=<64-char-hex>
X-HomeDash-Event: rent.overdue
X-HomeDash-Delivery: <uuid>
  • X-HomeDash-Signature-256 — HMAC-SHA256 of the raw request body, hex-encoded, prefixed with sha256=. Always verify before trusting.
  • X-HomeDash-Event — same as event in the body, useful for routing without parsing JSON.
  • X-HomeDash-Delivery — the same UUID embedded in id (without the evt_ prefix).

Verifying the signature

Always compute the HMAC over the raw request body — before any framework reparses or reformats it. Use a timing-safe comparison.

verify.js
// Node.js
import crypto from 'node:crypto';

function verify(rawBody, headerValue, secret) {
  const sig = (headerValue || '').replace(/^sha256=/i, '');
  if (!/^[a-f0-9]{64}$/i.test(sig)) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('hex');

  const a = Buffer.from(sig.toLowerCase(), 'hex');
  const b = Buffer.from(expected, 'hex');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
verify.py
# Python
import hmac, hashlib

def verify(raw_body: bytes, header_value: str, secret: str) -> bool:
    sig = (header_value or '').replace('sha256=', '', 1).lower()
    if len(sig) != 64:
        return False

    expected = hmac.new(
        secret.encode('utf-8'),
        raw_body,
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(sig, expected)
verify.php
<?php
// PHP
function verify(string $rawBody, string $headerValue, string $secret): bool {
  $sig = strtolower(preg_replace('/^sha256=/i', '', $headerValue ?? ''));
  if (!preg_match('/^[a-f0-9]{64}$/', $sig)) return false;

  $expected = hash_hmac('sha256', $rawBody, $secret);
  return hash_equals($expected, $sig);
}
The signing secret is shown once when you create the subscription. Store it as an environment variable on your server — never hard-code it.

Delivery, retries, and timeouts

  • HomeDash calls your URL with a 10-second timeout.
  • Any 2xx response counts as success.
  • On failure (non-2xx, timeout, network error) HomeDash retries up to 2 more times: once after ~15 minutes, once after ~60 minutes (with jitter to avoid thundering herds). After the third attempt the delivery is marked permanently failed.
  • Each retry carries the same id, so de-duplicate on id.
  • Multiple subscriptions for the same event each receive an independent delivery with the same id.
  • Delivery history is visible in the Developer Dashboard, including response code, latency, and the option to manually retry failed deliveries.

Event catalog

The payloads below are real fixtures from the HomeDash test-delivery system, with a stable id and timestamp so you can copy-paste reliably.

Rent overdue

rent.overdue

Fired when scheduled rent is past its due date and still unpaid.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "rent.overdue",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "rent_payment_id": "rent_test_1",
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "amount": 1200,
    "due_date": "2026-01-01",
    "days_overdue": 7
  }
}

Contract expiring

contract.expiring

Fired ahead of a contract end date so you can prompt for renewal.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contract.expiring",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "end_date": "2026-03-01",
    "days_until_expiry": 30
  }
}

Contract created

contract.created

Fired when a new contract is added to a property.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contract.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "tenancy_id": "tenancy_test_1",
    "main_tenant_id": "tenant_test_1",
    "status": "Active",
    "start_date": "2026-01-01",
    "end_date": null,
    "rent_amount": 1500
  }
}

Contract updated

contract.updated

Fired when an existing contract is updated.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contract.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "tenancy_id": "tenancy_test_1",
    "main_tenant_id": "tenant_test_1",
    "status": "Active",
    "start_date": "2026-01-01",
    "end_date": "2027-01-01",
    "rent_amount": 1575
  }
}

Contract deleted

contract.deleted

Fired when a contract is deleted.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contract.deleted",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "tenancy_id": "tenancy_test_1",
    "main_tenant_id": "tenant_test_1",
    "status": "Deleted",
    "start_date": "2026-01-01",
    "end_date": "2027-01-01",
    "deleted_at": "2026-02-01T10:00:00.000Z"
  }
}

Contract ended

contract.ended

Fired when a contract transitions to the Ended state.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contract.ended",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "status": "Ended",
    "start_date": "2025-01-01",
    "end_date": "2026-01-01"
  }
}

Maintenance issue created

maintenance.created

Fired when a tenant or staff member opens a new maintenance issue.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "maintenance.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "issue_id": "issue_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "title": "Leaking tap",
    "description": "Tap is leaking under the sink",
    "priority": "Medium",
    "status": "Open"
  }
}

Maintenance issue updated

maintenance.updated

Fired when a maintenance issue changes state or details.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "maintenance.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "issue_id": "issue_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "title": "Leaking tap",
    "description": "Plumber scheduled for next week",
    "priority": "High",
    "status": "In_Progress"
  }
}

Maintenance issue deleted

maintenance.deleted

Fired when a maintenance issue is deleted.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "maintenance.deleted",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "issue_id": "issue_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "title": "Leaking tap",
    "priority": "High",
    "status": "Deleted",
    "deleted_at": "2026-02-01T10:00:00.000Z"
  }
}

Inspection scheduled

inspection.scheduled

Fired when an inspection is booked into the calendar.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "inspection.scheduled",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "inspection_id": "insp_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "inspection_date": "2026-02-01",
    "inspection_type": "routine",
    "status": "in_progress"
  }
}

Inspection updated

inspection.updated

Fired when an inspection is updated.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "inspection.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "inspection_id": "insp_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "inspection_date": "2026-02-01",
    "inspection_type": "routine",
    "status": "in_progress",
    "landlord_actions": "Arrange follow-up visit"
  }
}

Inspection deleted

inspection.deleted

Fired when an inspection is deleted.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "inspection.deleted",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "inspection_id": "insp_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "inspection_date": "2026-02-01",
    "inspection_type": "routine",
    "status": "deleted",
    "deleted_at": "2026-02-01T10:00:00.000Z"
  }
}

Inspection completed

inspection.completed

Fired when an inspection is marked complete and the report is available.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "inspection.completed",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "inspection_id": "insp_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "inspection_date": "2026-02-01",
    "inspection_type": "routine",
    "status": "completed"
  }
}

Property created

property.created

Fired when a property record is created.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "property.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "property_id": "property_test_1",
    "address": "12 Test Street",
    "short_name": "Test House",
    "status": "Vacant",
    "rent_amount": 1500,
    "city": "London",
    "postcode": "SW1A 1AA"
  }
}

Property updated

property.updated

Fired when a property record is updated.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "property.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "property_id": "property_test_1",
    "address": "12 Test Street",
    "short_name": "Test House",
    "status": "Occupied",
    "rent_amount": 1575,
    "city": "London",
    "postcode": "SW1A 1AA"
  }
}

Property deleted

property.deleted

Fired when a property record is deleted.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "property.deleted",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "property_id": "property_test_1",
    "address": "12 Test Street",
    "short_name": "Test House",
    "status": "Deleted",
    "deleted_at": "2026-02-01T10:00:00.000Z"
  }
}

Tenant created

tenant.created

Fired when a tenant record is created.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "tenant.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "tenant_id": "tenant_test_1",
    "property_id": "property_test_1",
    "first_name": "Alex",
    "last_name": "Tenant",
    "email": "alex.tenant@example.com",
    "phone": "+447700900000",
    "status": "INVITED"
  }
}

Tenant updated

tenant.updated

Fired when a tenant record is updated.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "tenant.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "tenant_id": "tenant_test_1",
    "property_id": "property_test_1",
    "first_name": "Alex",
    "last_name": "Tenant",
    "email": "alex.tenant@example.com",
    "phone": "+447700900001",
    "status": "ACTIVE"
  }
}

Tenant deleted

tenant.deleted

Fired when a tenant record is deleted.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "tenant.deleted",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "tenant_id": "tenant_test_1",
    "property_id": "property_test_1",
    "first_name": "Alex",
    "last_name": "Tenant",
    "email": "alex.tenant@example.com",
    "status": "DELETED",
    "deleted_at": "2026-02-01T10:00:00.000Z"
  }
}

Contact created

contact.created

Fired when a contact is created.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contact.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "contact_id": "contact_test_1",
    "name": "Ace Plumbing",
    "company": "Ace Ltd",
    "email": "ace@example.com",
    "mobile": "+447700900100",
    "categories": [
      "Plumbing"
    ]
  }
}

Contact updated

contact.updated

Fired when a contact is updated.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contact.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "contact_id": "contact_test_1",
    "name": "Ace Plumbing",
    "company": "Ace Services Ltd",
    "email": "ace@example.com",
    "mobile": "+447700900100",
    "categories": [
      "Plumbing",
      "Emergency"
    ]
  }
}

Contact deleted

contact.deleted

Fired when a contact is deleted.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contact.deleted",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "contact_id": "contact_test_1",
    "name": "Ace Plumbing",
    "company": "Ace Services Ltd",
    "deleted_at": "2026-02-01T10:00:00.000Z"
  }
}

Chat message sent

chat.message_sent

Fired when a landlord sends a chat message.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "chat.message_sent",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "message_id": "message_test_1",
    "chat_id": "chat_test_1",
    "property_id": "property_test_1",
    "tenant_id": "tenant_test_1",
    "text": "Hello, the contractor will visit tomorrow.",
    "attachment_type": null,
    "attachment_name": null,
    "sent_at": "2026-02-01T10:00:00.000Z"
  }
}

Mortgage created

mortgage.created

Fired when a mortgage is created.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "mortgage.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "mortgage_id": "mortgage_test_1",
    "lender": "Test Bank",
    "reference": "M-001",
    "balance": 250000,
    "interest_rate": 4.25,
    "monthly_payment": 1200,
    "property_ids": [
      "property_test_1"
    ],
    "status": "Active"
  }
}

Mortgage updated

mortgage.updated

Fired when a mortgage is updated.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "mortgage.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "mortgage_id": "mortgage_test_1",
    "lender": "Test Bank",
    "reference": "M-001",
    "balance": 245000,
    "interest_rate": 4.1,
    "monthly_payment": 1185,
    "property_ids": [
      "property_test_1"
    ],
    "status": "Active"
  }
}

Mortgage deleted

mortgage.deleted

Fired when a mortgage is deleted.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "mortgage.deleted",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "mortgage_id": "mortgage_test_1",
    "lender": "Test Bank",
    "reference": "M-001",
    "balance": 245000,
    "property_ids": [
      "property_test_1"
    ],
    "status": "Deleted",
    "deleted_at": "2026-02-01T10:00:00.000Z"
  }
}

Rent created

rent.created

Fired when a rent payment is created.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "rent.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "rent_payment_id": "rent_test_1",
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "tenant_id": "tenant_test_1",
    "amount": 1500,
    "due_date": "2026-02-05",
    "status": "Pending",
    "period": "monthly"
  }
}

Rent updated

rent.updated

Fired when a rent payment is updated.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "rent.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "rent_payment_id": "rent_test_1",
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "tenant_id": "tenant_test_1",
    "amount": 1500,
    "due_date": "2026-02-05",
    "status": "Paid",
    "date_paid": "2026-02-06",
    "amount_received": 1500,
    "period": "monthly"
  }
}

Rent invoice sent

rent.invoice_sent

Fired when an invoice or receipt email is sent to a tenant via the public API. Delivery is asynchronous via the webhook outbox.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "rent.invoice_sent",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "rent_payment_id": "rent_test_1",
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "tenant_id": "tenant_test_1",
    "tenant_email": "tenant@example.com",
    "document_type": "invoice",
    "period": "February 2026",
    "amount": 1500,
    "due_date": "2026-02-05",
    "sent_via": "public_api"
  }
}

Rent due soon

rent.due_soon

Fired a few days ahead of a pending rent payment due date.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "rent.due_soon",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "rent_payment_id": "rent_test_1",
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "amount": 1200,
    "due_date": "2026-01-08",
    "days_until_due": 3
  }
}

Property document created

property_document.created

Fired when a property document is uploaded.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "property_document.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "document_id": "prop_doc_test_1",
    "property_id": "property_test_1",
    "name": "EPC Certificate",
    "type": "EPC_Certificate",
    "expiry_date": "2029-01-01",
    "uploaded_at": "2026-02-01T10:00:00.000Z",
    "size_bytes": 204800
  }
}

Property document deleted

property_document.deleted

Fired when a property document is deleted.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "property_document.deleted",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "document_id": "prop_doc_test_1",
    "property_id": "property_test_1",
    "name": "EPC Certificate",
    "type": "EPC_Certificate",
    "deleted_at": "2026-02-01T10:00:00.000Z"
  }
}

Contract document created

contract_document.created

Fired when a contract document is uploaded.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contract_document.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "document_id": "contract_doc_test_1",
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "name": "Tenancy Agreement",
    "type": "Contract",
    "uploaded_at": "2026-02-01T10:00:00.000Z",
    "size_bytes": 307200
  }
}

Contract document deleted

contract_document.deleted

Fired when a contract document is deleted.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "contract_document.deleted",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "document_id": "contract_doc_test_1",
    "contract_id": "contract_test_1",
    "property_id": "property_test_1",
    "name": "Tenancy Agreement",
    "type": "Contract",
    "deleted_at": "2026-02-01T10:00:00.000Z"
  }
}

Note created

note.created

Fired when a note is added to a maintenance issue.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "note.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "note_id": "note_test_1",
    "issue_id": "issue_test_1",
    "property_id": "property_test_1",
    "text": "Tenant confirmed access for tomorrow.",
    "created_at": "2026-02-01T10:00:00.000Z"
  }
}

Document signed

document.signed

Fired when an e-signature request is fully signed.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "document.signed",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "signature_request_id": "sigreq_test_1",
    "document_name": "Tenancy Agreement.pdf",
    "signer_email": "tenant@example.com",
    "document_type": "tenancy_agreement",
    "status": "signed",
    "signed_document_url": "https://example.com/signed.pdf"
  }
}

Document declined / unsigned

document.unsigned

Fired when an e-signature request is declined or expires unsigned.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "document.unsigned",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "signature_request_id": "sigreq_test_1",
    "document_name": "Tenancy Agreement.pdf",
    "signer_email": "tenant@example.com",
    "document_type": "tenancy_agreement",
    "status": "declined"
  }
}

Deposit updated

deposit.updated

Fired when a deposit reaches an outcome — returned, partially returned after deductions, retained, or disputed.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "deposit.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "deposit_id": "deposit_test_1",
    "contract_id": "contract_test_1",
    "tenant_id": "tenant_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "amount": 1500,
    "status": "partial_return",
    "deduction_amount": 250,
    "deduction_reason": "Cleaning £150 (End of tenancy clean); Damage £100 (Broken door handle)",
    "days_to_return": 10
  }
}

Payment plan created

payment_plan.created

Fired when a landlord agrees a schedule for a tenant to clear rent arrears.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "payment_plan.created",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "plan_id": "plan_test_1",
    "tenant_id": "tenant_test_1",
    "property_id": "property_test_1",
    "property_name": "12 Test Street",
    "outstanding_amount": 1200,
    "plan_description": "6 instalments of £200.00, from 2026-09-01 to 2027-02-01",
    "agreed_date": "2026-08-21"
  }
}

Meter registered

meter.registered

Fired when a utility meter is recorded at a property.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "meter.registered",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "meter_id": "meter_test_1",
    "property_id": "property_test_1",
    "utility_type": "electricity",
    "unit": "kWh",
    "tariff_structure": "economy_7",
    "payment_mode": "credit",
    "mpan": "1200023305408",
    "location_description": "Cupboard under the stairs",
    "removed_date": null
  }
}

Meter updated

meter.updated

Fired when a meter’s details change — supply number, unit, location.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "meter.updated",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "meter_id": "meter_test_1",
    "property_id": "property_test_1",
    "utility_type": "electricity",
    "unit": "kWh",
    "tariff_structure": "economy_7",
    "payment_mode": "credit",
    "mpan": "1200023305408",
    "location_description": "Cupboard under the stairs",
    "removed_date": null
  }
}

Meter removed

meter.removed

Fired when a meter is taken off the wall. Its readings stay attached to it, so historic consumption is still attributed to the hardware that produced it.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "meter.removed",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "meter_id": "meter_test_1",
    "property_id": "property_test_1",
    "utility_type": "electricity",
    "unit": "kWh",
    "tariff_structure": "economy_7",
    "payment_mode": "credit",
    "mpan": "1200023305408",
    "location_description": "Cupboard under the stairs",
    "removed_date": "2026-09-05"
  }
}

Meter reading recorded

meter_reading.recorded

Fired when a reading is taken. A reading on a changeover date carries two tenancy links — one check-out and one check-in — because it is one physical visit serving both.

JSON
{
  "id": "evt_00000000-0000-4000-8000-000000000000",
  "event": "meter_reading.recorded",
  "created_at": "2026-01-15T10:30:00.000Z",
  "landlord_id": "11111111-1111-4111-8111-111111111111",
  "_test": true,
  "data": {
    "_test": true,
    "reading_id": "reading_test_1",
    "meter_id": "meter_test_1",
    "property_id": "property_test_1",
    "utility_type": "electricity",
    "reading_date": "2026-09-05",
    "reason": "changeover",
    "status": "actual",
    "payment_mode": "credit",
    "registers": [
      {
        "register": "day",
        "value": 12345.5,
        "unit": "kWh"
      },
      {
        "register": "night",
        "value": 9876.25,
        "unit": "kWh"
      }
    ],
    "contracts": [
      {
        "contract_id": "contract_test_1",
        "role": "check_out"
      },
      {
        "contract_id": "contract_test_2",
        "role": "check_in"
      }
    ],
    "warnings": []
  }
}

Tips for writing a robust receiver

  • Reply 2xx fast. Acknowledge in under a second by enqueuing the payload for asynchronous processing.
  • Verify the signature first. Reject anything you cannot verify with401 — do not even parse the body.
  • Idempotency: store seen id values for at least 24 hours and skip duplicates.
  • Test deliveries: branch on _test === true if your handler has external side effects (e.g. don't actually send an email on test events).