Skip to main content
All Docs
API ReferencemyProp (AgentOS People Portal)Updated April 4, 2026

Landlord Dashboard API

Landlord Dashboard API

The landlord API provides all data needed to power the landlord portal dashboard. It is exposed as a tRPC router under the landlord namespace and backed by the AgentOS (letmc.com) service layer.

All endpoints require an authenticated session (protectedProcedure). Inputs are validated with Zod.


Endpoints

landlord.status

Health check. Returns whether the AgentOS API is configured for the current agency context.

Input: none


landlord.insights

Returns aggregated KPIs for the landlord dashboard summary panel.

Input: { shortName: string, landlordId: string }

Response: LandlordInsights

{
  totalProperties: number;
  occupiedProperties: number;
  vacantProperties: number;
  occupancyRate: number;          // 0–100, as a percentage
  totalRentExpected: number;
  totalArrearsAmount: number;
  activeMaintenanceJobs: number;
  completedMaintenanceJobs: number;
  totalTenancies: number;
  currentTenancies: number;
}

Partial failure resilience: If one underlying data source fails (e.g. arrears or maintenance), the remaining KPIs are still returned. Individual errors are captured for observability.


landlord.portfolio

Paginated list of properties in the landlord's portfolio.

Input:

{
  shortName: string;
  landlordId: string;
  offset?: number;   // default: 0
  count?: number;    // default: 20
}

Response: LandlordPortfolio

{
  properties: LandlordProperty[];
  totalCount: number;
}

LandlordProperty fields:

{
  propertyId: string;
  address: string;              // Full address, assembled from parts
  addressLine1: string | null;
  addressLine2: string | null;
  addressLine3: string | null;
  addressLine4: string | null;
  postcode: string | null;
  propertyType: string | null;
  bedrooms: number | null;
  bathrooms: number | null;
  receptions: number | null;
  rentAmount: number | null;
  rentFrequency: string | null;
  isManaged: boolean;
  isVacant: boolean;
  branchId: string | null;
  currentTenancyId: string | null;
  imageUrl: string | null;
  metadata: Record<string, unknown>;
}

landlord.propertyDetails

Full details for a single property, with optional block/development information.

Input:

{
  shortName: string;
  landlordId: string;
  propertyId: string;
}

Response:

{
  property: LandlordProperty | null;
  block: PropertyBlock | null;    // null if no block or block fetch fails
}

PropertyBlock fields:

{
  blockId: string;
  name: string;
  address: string | null;
  totalUnits: number | null;
  managingAgent: string | null;
  metadata: Record<string, unknown>;
}

Block info is fetched as a non-critical sub-request. If it fails, block is null and the property record is still returned.


landlord.maintenanceJobs

Paginated list of maintenance jobs across the landlord's portfolio.

Input:

{
  shortName: string;
  landlordId: string;
  offset?: number;
  count?: number;
  status?: string;    // Optional status filter (e.g. "Active", "Completed")
}

Response:

{
  jobs: MaintenanceJob[];
  totalCount: number;
}

MaintenanceJob fields:

{
  jobId: string;
  propertyId: string;
  propertyAddress: string | null;
  title: string;
  description: string | null;
  status: string;
  priority: string | null;
  reportedDate: string | null;
  completedDate: string | null;
  estimatedCost: number | null;
  actualCost: number | null;
  contractorName: string | null;
  category: string | null;
  metadata: Record<string, unknown>;
}

landlord.maintenanceJobDetails

Full details for a single maintenance job, including any attached notes.

Input:

{
  shortName: string;
  landlordId: string;
  jobId: string;
}

Response:

{
  job: MaintenanceJob | null;
  notes: JobNote[];    // Empty array if notes fetch fails
}

JobNote fields:

{
  noteId: string;
  jobId: string;
  text: string;
  createdAt: string | null;
  createdBy: string | null;
  type: string | null;
  metadata: Record<string, unknown>;
}

landlord.arrears

All outstanding rent arrears entries across the landlord's portfolio.

Input:

{
  shortName: string;
  landlordId: string;
}

Response:

{
  arrears: RentArrearsEntry[];
  totalArrearsAmount: number;
}

RentArrearsEntry fields:

{
  tenancyId: string;
  propertyId: string | null;
  propertyAddress: string | null;
  tenantName: string | null;
  arrearsAmount: number;
  rentAmount: number | null;
  lastPaymentDate: string | null;
  daysBehind: number | null;
  status: string | null;
  metadata: Record<string, unknown>;
}

landlord.tenancies

Paginated list of tenancies (current and historical).

Input:

{
  shortName: string;
  landlordId: string;
  offset?: number;
  count?: number;
  currentOnly?: boolean;   // If true, returns only active tenancies
}

Response:

{
  tenancies: LandlordTenancy[];
  totalCount: number;
}

LandlordTenancy fields:

{
  tenancyId: string;
  propertyId: string;
  propertyAddress: string | null;
  tenantNames: string[];
  startDate: string | null;
  endDate: string | null;
  rentAmount: number | null;
  rentFrequency: string | null;
  tenancyType: string | null;
  status: string | null;
  depositAmount: number | null;
  isCurrent: boolean;
  branchId: string | null;
  metadata: Record<string, unknown>;
}

landlord.tenancyDetails

Full details for a single tenancy.

Input:

{
  shortName: string;
  landlordId: string;
  tenancyId: string;
}

Response: LandlordTenancy | null


landlord.statements

Financial statement entries for the landlord, with optional date range filtering.

Input:

{
  shortName: string;
  landlordId: string;
  dateFrom?: string;   // ISO 8601 date string
  dateTo?: string;     // ISO 8601 date string
  offset?: number;
  count?: number;
}

Response: LandlordStatement

{
  entries: StatementEntry[];
  openingBalance: number | null;
  closingBalance: number | null;
  totalDebits: number | null;
  totalCredits: number | null;
  periodStart: string | null;
  periodEnd: string | null;
}

StatementEntry fields:

{
  entryId: string;
  date: string | null;
  description: string;
  debit: number | null;
  credit: number | null;
  balance: number | null;
  propertyAddress: string | null;
  category: string | null;
  metadata: Record<string, unknown>;
}

Error Handling

ScenarioBehaviour
AgentOS returns 404Returns null or empty array — no error thrown
Non-critical sub-request fails (block info, job notes)Returns null/[] for that field; parent response succeeds
AgentOS API not configuredlandlord.status returns false; other endpoints throw LetmcNotConfiguredError
Other API errorsCaptured via captureError() for observability; error propagated to caller

Field Mapping

AgentOS API responses use PascalCase field names, some of which have alternative names depending on the endpoint version. The service layer normalises all fields to camelCase and handles fallbacks automatically. Examples:

AgentOS field(s)Mapped to
RentAmount | RentrentAmount
IsManaged | ManagedisManaged
IsVacant | VacantisVacant
OID | PropertyIDpropertyId
MainPhoto | PhotoURLimageUrl
JobTitle | Titletitle
ReportedDate | DateReportedreportedDate
TenancyStart | StartDatestartDate

Unmapped fields are preserved in the metadata object on each response type.