# Flats.ge for AI agents: list an owner's apartment

You are an AI assistant acting for a property owner. You can list their apartment on Flats.ge without an API key. The owner approves it themselves: Flats.ge sends them a WhatsApp message with an **Approve** link and a **Don't list** link. Nothing secret passes through you, and you never ask the owner for a code.

**Can't send HTTP requests** (a normal chat that only reads pages)? Build a ready listing link instead: https://flats.ge/developers/agents#link
For owners (how listing works, in every language): https://flats.ge/services/list-property

Base: `https://flats.ge/api/v1/agent`
Full data contract: [listings.md](https://flats.ge/developers/listings.md) (same JSON, media and validation rules)

## Rules

- **Apartments only** for now (`property_type: "apartment"`). Houses, cottages, offices, commercial, hotels and land will open later; until then they return `422 property_type_not_supported`. That error lists the open types in `property_types`.
- **One listing per WhatsApp number.** A second create returns `409 one_listing_per_number` with the existing `draft_id`; edit that one instead. A withdrawn listing, or one Flats.ge rejected, frees the slot.
- **Ask the owner: Direct or Accelerated.** Explain both in their language and let them choose; never choose for them. *Direct (0%)*: they sell or rent it themselves and buyers contact them directly. *Accelerated (3%)*: Flats.ge represents them, with wider advertising; once the listing has been reviewed, a Flats.ge representative will contact them to agree on a sales strategy and formalise a cooperation agreement. `private.owner_plan` is required (`422 owner_plan_required` without it).
- **Flats.ge reviews every listing** before it goes live. Never tell the owner it is published until GET reports `status: "published"`. Flats.ge also tells the owner the result on WhatsApp (published with the link, changes requested or rejected with the reason).
- The listing belongs to the owner's Flats.ge account (their WhatsApp number). They see it under **My properties** when they sign in on flats.ge with the same number, and can edit it there.
- **Verify first, before asking anything about the property.** Tell the owner: "Flats.ge needs to confirm it's you. What's your WhatsApp number? You'll get a message from Flats.ge: tap Approve." Collect the property details only after the owner approves. If they tap **Don't list**, stop.

## 1. Ask the owner to approve

```http
POST /api/v1/agent/verify/start
Content-Type: application/json
User-Agent: MyAssistant/1.0 (+https://example.com)

{"phone": "+995555123456", "language": "ka", "agent_name": "MyAssistant"}
```

- `phone`: the owner's WhatsApp number, international format. The only required field.
- `agent_name` (optional, 2–60 characters): your name as the owner knows you, shown in the message and on the approval page. No links, phone numbers or messenger names.
- `language`: the owner's language (`ka`, `ru`, `en`, `he`, `ja`). Only WhatsApp is available (Telegram later).

The owner receives: *"Your personal AI assistant "MyAssistant" is trying to list your property on Flats.ge at your request. If this was you, approve. If not, reject."* with an **Approve** and a **Don't list** link.

`202` → `{"method": "link", "verification_id": "…", "verification_secret": "…", "status": "pending", "expires_in": 86400}`. Keep both values private. The owner has 24 hours to answer.

## 2. Wait for the owner's answer

```http
POST /api/v1/agent/verify/status
Content-Type: application/json

{"verification_id": "…", "verification_secret": "…"}
```

Poll every 10–30 seconds while the owner is with you, less often later.

| `status` | Meaning |
| --- | --- |
| `pending` | Not answered yet |
| `approved` | The first answer includes `"token": "flats_agt_…"`. **It is given only once**: store it. Later answers say `token_collected: true`. |
| `declined` | The owner tapped **Don't list**. Stop and do not ask again. |
| `expired` | No answer in 24 hours. Ask the owner whether to try again. |
| `failed` | The WhatsApp message could not be sent. Try again later. |

Send the token as `Authorization: Bearer flats_agt_…` on every listing call. It works only for this owner's listing. It lasts 7 days, and every call renews it for 7 more days while the listing is a draft, waiting for review or being corrected.

## Location

You do not need Flats.ge catalogue ids. Give the town in `property.display_city` in the owner's language (for example `{"ka": "ქობულეთი"}`), the street and building in `property.display_address` and `property.building_number`, and `lat`/`lng` if the owner shares a map pin. Flats.ge places the listing on the map; if the address cannot be confirmed, `validate` returns `location_needs_review`: ask the owner for the exact street and building number or a map pin, then PATCH again.

## 3. Create, upload, submit

The same steps and JSON as [listings.md](https://flats.ge/developers/listings.md), with the base `https://flats.ge/api/v1/agent/listings`:

| Call | Purpose |
| --- | --- |
| `GET /listings` | `{"listings": [{"draft_id", "status"}]}`: this number's live listing, to resume |
| `POST /listings` | Create the draft (`flats.listing.v1`, `property_type: "apartment"`) |
| `GET /listings/{draft_id}` | Current state, revision, review status |
| `PATCH /listings/{draft_id}` + `If-Match` | Replace the whole draft |
| `POST /listings/{draft_id}/preflight` + `If-Match` | Check data and location before uploading; also says whether it answers a buyer request (below) |
| `POST /listings/{draft_id}/media/{media_id}/upload` + `If-Match` | Signed upload URL for one photo or the video |
| `POST /listings/{draft_id}/media/{media_id}/complete` + `If-Match` | Flats verifies the uploaded file |
| `POST /listings/{draft_id}/validate` + `If-Match` | Final check |
| `POST /listings/{draft_id}/submit` + `If-Match` | Send to Flats.ge review |
| `DELETE /listings/{draft_id}` + `If-Match` | Withdraw |

Write the title, district and description in the owner's language and set `private.owner_lang`; Flats.ge translates the rest on approval. Set `private.owner_role` to `owner` (or `agent` for a broker) and `private.owner_plan` to the owner's answer: `direct` (0% commission) or `premium` (Accelerated, 3%), see the rules above. Never put phone numbers, links or messenger names in public text.

## Buyer requests

`GET https://flats.ge/api/v1/requests` lists open buyer requests: `id`, `kind`, `budget` (USD), `radius_m`, `building`, `city`, `lat`, `lng`, `description`, `original_language`, `created_at`, `updated_at`, `expires_at`, `status`. Add `?since=2026-10-22T00:00:00Z` to get everything changed since then, closed requests included (`status: "closed"`); continue with the returned `next_since`. No buyer contact data is ever returned.

To answer a request, put its 10-digit id in `private.for_request_id`. `preflight` then also returns:

```json
"request_match": {"request_id": "6600629413", "matches": false, "reasons": [{"code": "price_outside_budget", "detail": {"min": 93000, "max": 107000, "price": 120000}}]}
```

A listing matches when the request is open, the deal is a sale, the kind fits (house and cottage count as the same), the building kind fits, the price is within ±7% of the budget, and the location is inside the request radius. Reason codes: `request_not_found`, `request_closed`, `deal_not_sale`, `kind_mismatch`, `building_mismatch`, `price_outside_budget`, `outside_radius`, `location_missing`. The buyer is alerted only after Flats.ge approves and publishes the listing.

## Limits

Up to 3 approval requests per number and 10 per internet address per day, one pending request per number at a time (a new one after 30 minutes). A number that answers **Don't list** twice in 30 days gets no more agent requests for 30 days (`403 owner_declined`). A blocked number gets nothing and its token stops working.

## Errors

**An HTML 403 page instead of JSON** means the Flats.ge firewall stopped the request before it reached the API. The firewall rejects JSON bodies shorter than 20 characters (such as `{}`): send calls that need no data (`preflight`, `validate`, `submit`, media `upload`/`complete`) with no body at all. Check that you sent the full JSON body with `Content-Type: application/json` and a descriptive `User-Agent`, then retry once. If it still happens, tell the owner to list on https://flats.ge (Add listing → Classic) instead.

Every API answer is JSON with `ok: false` and a stable `code`. Common ones: `invalid_phone`, `invalid_agent_name`, `approval_pending`, `owner_declined`, `rate_limited` (with `retry_after` seconds), `account_blocked`, `invalid_verification`, `delivery_unavailable`, `unauthorized`, `one_listing_per_number`, `property_type_not_supported`, `owner_plan_required` (ask the owner; the answer lists `plans`), `not_found`. Listing validation errors are the same as in listings.md.
