---
title: "From Property to Listing"
description: "The complete path: create a property, add media, generate a listing, retrieve the PDF, publish it."
url: "https://real-estate-agency.apim.eu/guides/listing-workflow"
image: "https://real-estate-agency.apim.eu/_og/d/c_Ocean.takumi,title_From+Property+to+Listing,description_~VGhlIGNvbXBsZXRlIHBhdGg6IGNyZWF0ZSBhIHByb3BlcnR5LCBhZGQgbWVkaWEsIGdlbmVyYXRlIGEgbGlzdGluZywgcmV0cmlldmUgdGhlIFBERiwgcHVibGlzaCBpdC4,props_eyJ0aGVtZSI6eyJtb2RlIjoiZGFyayIsImNvbG9ycyI6eyJwcmltYXJ5IjoiI0Q0QTI0QyJ9fX0,p_Ii9ndWlkZXMvbGlzdGluZy13b3JrZmxvdyI,s_XFtQvIinl7OUH494.png"
---

## From Property to Listing

Five steps that can be fully automated - from an empty record to a published document.

EPC Disclosures Property Data

## [Step 1: Create a Property](#step-1-create-a-property)

A listing never comes out of nowhere - it always references a property. Create it first via **Property Management**.

```bash
curl -X POST https://api.realestateagency.example/v1/properties \
  -H "apikey: $REAL_ESTATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "type": "apartment",
        "title": "3-Bedroom Period Home near Riverside Park",
        "address": { "street": "148 Elm Street", "postal_code": "10001", "city": "Springfield" },
        "living_area_sqm": 86.5,
        "rooms": 3,
        "year_built": 1908,
        "purchase_price_eur": 645000,
        "commission": { "buyer_percent": 3.57, "note": "includes applicable sales tax" },
        "energy_certificate": {
          "type": "consumption",
          "final_energy_demand_kwh": 118.4,
          "energy_source": "district_heating",
          "efficiency_class": "D",
          "valid_until": "2031-04-30"
        }
      }'
```

The `energy_certificate` block is optional when creating a property, but mandatory for later publication. See [Mandatory Disclosures](https://real-estate-agency.apim.eu/guides/mandatory-disclosures).

## [Step 2: Upload Media](#step-2-upload-media)

Photos and floor plans are attached to the property, not to a listing. That way they're available to every listing and template generated later.

```bash
curl -X POST https://api.realestateagency.example/v1/properties/prop_8f2c1a/media \
  -H "apikey: $REAL_ESTATE_API_KEY" \
  -F "file=@living-room.jpg" \
  -F "category=interior" \
  -F "order=1"
```

At least one cover photo (`category=cover_photo`) and one floor plan (`category=floor_plan`) are recommended. If the cover photo is missing, the template falls back to a neutral placeholder graphic.

## [Step 3: Generate a Listing](#step-3-generate-a-listing)

Want to see the flow without code first? The [Listing Generator](https://real-estate-agency.apim.eu/listing-generator) accepts the same property data through a form and produces a finished listing text via a language model - the call runs through the Kong AI Gateway.

```bash
curl -X POST https://api.realestateagency.example/v1/listings \
  -H "apikey: $REAL_ESTATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "property_id": "prop_8f2c1a",
        "template_id": "tpl_classic",
        "sections": ["location", "features", "energy", "floor_plan", "commission"]
      }'
```

```json
{
  "id": "lst_4d9b2e",
  "property_id": "prop_8f2c1a",
  "template_id": "tpl_classic",
  "status": "in_progress",
  "created_at": "2026-08-20T09:41:12Z"
}
```

Generation is asynchronous because images need to be resized and the PDF laid out. Usually takes two to ten seconds.

## [Step 4: Retrieve the Result](#step-4-retrieve-the-result)

Two paths lead to the finished document:

**Event-driven (recommended).** Subscribe to `listing.completed`; the webhook carries the listing ID and the download link. See [Webhooks](https://real-estate-agency.apim.eu/guides/webhooks).

**Polling.** Poll `GET /listings/{id}` with increasing backoff (1s, 2s, 4s, 8s) until `status` reaches `completed`:

```bash
curl https://api.realestateagency.example/v1/listings/lst_4d9b2e/pdf \
  -H "apikey: $REAL_ESTATE_API_KEY" \
  -o listing-elm-street.pdf
```

The link in `pdf_url` is valid for 24 hours. Store the document instead of passing the link around.

## [Step 5: Publish](#step-5-publish)

```bash
curl -X POST https://api.realestateagency.example/v1/listings/lst_4d9b2e/publish \
  -H "apikey: $REAL_ESTATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channels": ["portal", "website", "newsletter"] }'
```

Before publishing, the API checks the mandatory disclosures. If something's missing, you get `422`:

```json
{
  "code": "mandatory_disclosure_missing",
  "message": "Mandatory disclosures from the energy certificate are missing for publication.",
  "details": [
    { "field": "energy_certificate.final_energy_demand_kwh", "reason": "missing" }
  ]
}
```

Add the missing figure on the **property**, regenerate the listing, and publish again. A listing that's already been generated is never changed after the fact - that keeps the history traceable.

## [Common Pitfalls](#common-pitfalls)

-   **A listing with no property reference.** `property_id` is required; standalone listings deliberately don't exist.
-   **A template from a different tenant.** `template_id` must belong to your tenant, otherwise `404`.
-   **PDF retrieved too early.** Before `status: "completed"`, `/pdf` returns `409`.
-   **Price change without regenerating.** Updating a property doesn't automatically update existing listings.