Listings and synchronization
On this page
Synchronize advertisements and edit their supported content fields.
REST operations and key permissions
| Task | Key permissions | Start here |
|---|---|---|
| Read published and unpublished listings; synchronize adverts | read:properties; write:properties for the corresponding mutation | GET /api/v1/landlord/listings, GET /api/v1/landlord/listings/changes, PATCH /api/v1/landlord/listings/{unitId}/content, POST /api/v1/landlord/listings/{unitId}/media/uploads, POST /api/v1/landlord/listings/{unitId}/media/uploads/{uploadId}/complete, PATCH /api/v1/landlord/listings/{unitId}/media, PATCH /api/v1/landlord/listings/{unitId}/publication |
Bootstrap and replay listing changes
For listing full sync, retain the syncCursor from the first bootstrap page unchanged until every bootstrap page has been applied to a temporary dataset. Follow the bootstrap nextCursor through all pages; only then start /api/v1/landlord/listings/changes from that retained first cursor. Apply each change page atomically with its nextCursor; continue while hasMore=true. Replace the previous cache only after the full bootstrap and initial change replay succeed. Webhooks wake that change reader; periodic polling recovers missed notifications.
Edit an existing listing's name or unit text
Use a unit ID returned by the company listing feed. With write:properties, send PATCH /api/v1/landlord/listings/{unitId}/content with an Idempotency-Key and this JSON body:
{"name":"Bright two-room apartment","otherFacts":"Two rooms and a kitchen with a balcony."}The response is the same {"listing": ...} projection as the listing detail read. name and otherFacts are the only accepted fields; both are optional strings and trimmed, and at least one must be supplied. The limits are 200 and 10000 characters respectively, with a 64 KiB request-body cap. Omitted fields stay unchanged. Empty name removes the optional unit name. Empty otherFacts clears the unit text, so public description falls back to legacy unit facts and then the property's description. It does not erase those fallback sources. null and unknown fields are rejected.
Read the result back and apply listing-change events as usual. Replays still require the key's current permission and ownership; a later denial does not undo an earlier successful operation. Different operation keys changing the same field follow last-writer semantics; listingVersion is not a write precondition here.
Media, publication, availability and private tenant fields have no path through this content operation. Their separate administration boundaries still apply. The content write, required semantic audit and replay result commit atomically. The audit records key/operation IDs and changed field names, without storing your credential or the advertisement text in that audit record.
Upload listing images and publish through the API
Activate an explicit listing delegation for the exact integration key in Settings → API & agents. The administrator signs in again with password, passkey or an eID method enabled in Homito and grants media and/or publication with an expiry. Activation uses the currently authenticated person's own company membership and configured publication trust. createdBy, wildcard permission, a replacement key or a person ID supplied by a client cannot replace that delegation. It can be read or revoked at the authenticated web-app control plane /api/v1/settings/api-keys/{keyId}/listing-grant; that address is not on the external API origin.
Every operation below requires write:properties, its delegated capability and a distinct Idempotency-Key. Keep the exact key and body after an interrupted response; replay rechecks current authority before returning a receipt.
- Send
POST /api/v1/landlord/listings/{unitId}/media/uploadswith{"images":[{"bytes":12345,"sha256":"<64 lowercase hexadecimal characters>","mimeType":"image/png"}]}. Compute size and SHA-256 from the actual original bytes. Limits: ten images per reservation and five MiB per image. The reservation expires in fifteen minutes; a returned PUT URL lasts at most five minutes. - PUT those exact bytes to the returned
upload.url, with every returnedupload.headersvalue and no Homito Authorization header. Use binary HTTP upload, reject redirects, and check the HTTP result before completion. This private staging object is not a listing image. Replaying the reservation does not extend its URLs; after expiry create a new reservation with a new operation key. - Send
POST /api/v1/landlord/listings/{unitId}/media/uploads/{uploadId}/completewith{}or{"setAsFront":true}. The server verifies actual length, SHA-256, MIME, decoding and pixel limits, writes a different immutable public image, then commits the gallery, audit, change journal, durable webhook intent and receipt together. Read the returned listing back. GIF and animated WebP may retain embedded metadata; completion does not promise complete metadata stripping. - Send
PATCH /api/v1/landlord/listings/{unitId}/publicationwith{"isPublic":true,"allocationMethod":"individual_assessment","creditCheckMode":"without_credit_check"}to publish, or{"isPublic":false}to unpublish. Allocation can also befirst_come. Maintenance, company freeze, round rules and the delegated person's configured publication trust still apply. Unpublishing keeps its existing publication-trust exemption and still needs a live key, delegation, person, membership and ownership. Arbitrary slugs, credit checks and private tenant fields are not accepted.
Follow listing.changed/listing.unpublished notifications through the ordered changes feed. Abandoned raw uploads and unattached derived objects have durable scheduled cleanup. A successful write receipt records the mutation and delivery intent; verify actual webhook delivery separately.
After an approved exact MCP distribution advertises them, homito.reserve_listing_media, homito.complete_listing_media and homito.set_listing_publication call these same REST operations with reservation, completion and publication objects respectively. Each also takes unitId and idempotencyKey; completion takes uploadId. Enable the ordinary local write gate; you approve each call in your MCP client before it is sent. The binary PUT remains a separate HTTP upload using the returned instructions.
Manage existing images with PATCH /api/v1/landlord/listings/{unitId}/media: {"removeUrls":["<exact URL from the current gallery>"],"frontImageUrl":"<exact retained gallery URL>"}. Either field can be supplied separately; use frontImageUrl:null to clear the explicit front selection. The operation can only remove or select existing images, never import a new URL. Managed images removed from the gallery become eligible for fenced physical cleanup. The corresponding approved MCP tool is homito.update_listing_gallery with a gallery object and the ordinary write gates.
Continue reads safely
Follow nextCursor until it is null, keeping the same list operation and filters. Use cursors only with the same company and list operation. Import previews, jobs and their history belong to the API key that created them, including access by job ID. A green public /health response alone does not verify your key or company access.
Keep private data out of public caches
Keep personal data, receipts and upload capabilities out of public listing caches. Tenant and application reads intentionally return minimized operational projections; do not infer missing private fields or screening conclusions. Keep nullable/unknown values distinct from zero or a completed result.
Use the published contract version
Use the API version and lifecycle shown in the API reference, the schemas for each operation, and any required form or resource version when updating an integration. Preserve opaque versions unchanged and pin an approved MCP release. Event names come from the same catalog as the API; arrival order and a product version are not substitutes for current resource readback.