> ## Documentation Index
> Fetch the complete documentation index at: https://api.housera.ro/llms.txt
> Use this file to discover all available pages before exploring further.

# Primul anunț în 5 minute

> De la cheia de sandbox la un anunț cu poze, trimis la moderare.

În ghidul ăsta publici un apartament în sandbox: îți verifici datele, creezi anunțul, îl citești înapoi și îi adaugi poze. Ai nevoie doar de un cont Housera cu emailul confirmat.

<Steps>
  <Step title="Creează o cheie de sandbox">
    Intră pe mediul de sandbox, la **Setări → API** (`https://staging.housera.ro/setari/api`), și creează o cheie cu drepturile `listings:read`, `listings:write` și `media:write`.

    Cheia arată așa: `12|hsr_test_…`. O vezi **o singură dată**, imediat după creare, așa că o copiezi pe loc într-un loc sigur. Detaliile sunt în [Autentificare](/ro/authentication).

    În exemplele de mai jos, cheia stă în variabila de mediu `HOUSERA_KEY`:

    ```bash theme={null}
    export HOUSERA_KEY="12|hsr_test_..."
    ```
  </Step>

  <Step title="Validează anunțul">
    `POST /listings/validate` aplică exact regulile publicării, dar nu scrie nimic. E cel mai rapid mod să-ți verifici maparea câmpurilor.

    <CodeGroup>
      ```bash curl theme={null}
      curl https://staging.housera.ro/api/v1/listings/validate \
        -H "Authorization: Bearer $HOUSERA_KEY" \
        -H "Accept: application/json" \
        -H "Content-Type: application/json" \
        -d @apartament.json
      ```

      ```php PHP theme={null}
      <?php
      // composer require guzzlehttp/guzzle
      $client = new GuzzleHttp\Client([
          'base_uri' => 'https://staging.housera.ro/api/v1/',
          'headers' => [
              'Authorization' => 'Bearer '.getenv('HOUSERA_KEY'),
              'Accept' => 'application/json',
          ],
          'http_errors' => false,
      ]);

      $listing = json_decode(file_get_contents('apartament.json'), true);
      $response = $client->post('listings/validate', ['json' => $listing]);

      echo $response->getStatusCode(), PHP_EOL, $response->getBody();
      ```

      ```javascript JavaScript theme={null}
      import { readFile } from 'node:fs/promises';

      const BASE = 'https://staging.housera.ro/api/v1';
      const headers = {
        Authorization: `Bearer ${process.env.HOUSERA_KEY}`,
        Accept: 'application/json',
        'Content-Type': 'application/json',
      };

      const listing = JSON.parse(await readFile('apartament.json', 'utf8'));
      const res = await fetch(`${BASE}/listings/validate`, {
        method: 'POST',
        headers,
        body: JSON.stringify(listing),
      });

      console.log(res.status, await res.json());
      ```

      ```python Python theme={null}
      import json, os, requests

      BASE = "https://staging.housera.ro/api/v1"
      session = requests.Session()
      session.headers.update({
          "Authorization": f"Bearer {os.environ['HOUSERA_KEY']}",
          "Accept": "application/json",
      })

      with open("apartament.json", encoding="utf-8") as f:
          listing = json.load(f)

      res = session.post(f"{BASE}/listings/validate", json=listing)
      print(res.status_code, res.json())
      ```
    </CodeGroup>

    Conținutul fișierului `apartament.json`:

    ```json apartament.json theme={null}
    {
      "type": "apartment",
      "subtype": "standard",
      "transaction": "sale",
      "title": "Apartament 3 camere decomandat, Copou, Iași",
      "description": "Apartament luminos cu trei camere decomandate, renovat în 2022, la cinci minute de Parcul Copou. Bucătărie închisă, centrală proprie pe gaz, ferestre noi și boxă la subsol.",
      "city": "Iași",
      "county": "Iași",
      "area": "Copou",
      "street": "Strada Vasile Lupu",
      "street_number": "12",
      "latitude": 47.1752,
      "longitude": 27.5701,
      "price": 98000,
      "price_currency": "EUR",
      "negotiable": true,
      "rooms": 3,
      "bedrooms": 2,
      "bathrooms": 1,
      "floor": "floor_2",
      "total_floors": 4,
      "usable_area_sqm": 65.5,
      "built_area_sqm": 72,
      "partitioning": "detached",
      "year_built": 1985,
      "condition": "renovated",
      "heating_types": ["gas"],
      "energy_certificate": "b",
      "features": ["ac", "elevator", "intercom"]
    }
    ```

    Dacă totul e în regulă, primești `200` cu `{"valid": true}`. Altfel primești `422`, cu fiecare problemă pe câmpul ei:

    ```json theme={null}
    {
      "type": "https://housera.ro/problems/validation",
      "title": "Datele trimise nu sunt valide.",
      "status": 422,
      "errors": [
        { "pointer": "/street", "code": "required", "detail": "Completează strada." }
      ]
    }
    ```

    <Tip>
      Telefonul de contact al anunțului e, implicit, cel din contul tău. Dacă în cont nu ai un telefon, trimite câmpul `contact_phone`. Numele și emailul de contact sunt mereu cele ale contului.
    </Tip>
  </Step>

  <Step title="Creează anunțul">
    Același corp, la `POST /listings`. Adaugă antetul `Idempotency-Key` cu o valoare unică pentru această operație (de exemplu un UUID): dacă cererea se pierde pe drum și o repeți cu aceeași cheie, primești același anunț, nu două.

    <CodeGroup>
      ```bash curl theme={null}
      curl https://staging.housera.ro/api/v1/listings \
        -H "Authorization: Bearer $HOUSERA_KEY" \
        -H "Accept: application/json" \
        -H "Content-Type: application/json" \
        -H "Idempotency-Key: 6f1c2b0e-8a4d-4c3e-9b7a-2d5f1e0c9a11" \
        -d @apartament.json
      ```

      ```php PHP theme={null}
      $response = $client->post('listings', [
          'json' => $listing,
          'headers' => ['Idempotency-Key' => '6f1c2b0e-8a4d-4c3e-9b7a-2d5f1e0c9a11'],
      ]);
      $created = json_decode((string) $response->getBody(), true);
      $code = $created['data']['code'];
      ```

      ```javascript JavaScript theme={null}
      const created = await fetch(`${BASE}/listings`, {
        method: 'POST',
        headers: { ...headers, 'Idempotency-Key': crypto.randomUUID() },
        body: JSON.stringify(listing),
      }).then((r) => r.json());

      const code = created.data.code;
      ```

      ```python Python theme={null}
      import uuid

      res = session.post(
          f"{BASE}/listings",
          json=listing,
          headers={"Idempotency-Key": str(uuid.uuid4())},
      )
      code = res.json()["data"]["code"]
      ```
    </CodeGroup>

    Răspunsul e `201 Created`. Anunțul are starea `pending`: așteaptă moderarea.

    ```json theme={null}
    {
      "data": {
        "code": "HEWPUTA8",
        "url": "https://housera.ro/anunt/hewputa8/iasi/apartament-de-vanzare-3-camere-65-mp-copou",
        "type": "apartment",
        "subtype": "standard",
        "transaction": "sale",
        "title": "Apartament 3 camere decomandat, Copou, Iași",
        "price": 98000,
        "currency": "EUR",
        "price_period": "total",
        "price_declared": { "amount": 98000, "currency": "EUR" },
        "external_ref": null,
        "status": "pending",
        "moderation": { "edit_pending": false, "rejection_reason": null, "rejection_codes": [] },
        "media": { "processing": false },
        "etag": "\"9b2f6c1e4a7d3b8f0c5e2a1d6f4b7c9e\""
      },
      "warnings": []
    }
    ```

    Am scurtat obiectul: răspunsul complet conține și locația, `facts`, `features`, `images` și datele de publicare. Păstrează `code`: e identificatorul anunțului pe Housera.
  </Step>

  <Step title="Citește anunțul">
    `GET /account/listings/{code}` îți arată anunțul cu starea lui curentă, în orice stare ar fi.

    <CodeGroup>
      ```bash curl theme={null}
      curl https://staging.housera.ro/api/v1/account/listings/HEWPUTA8 \
        -H "Authorization: Bearer $HOUSERA_KEY" \
        -H "Accept: application/json"
      ```

      ```php PHP theme={null}
      $response = $client->get("account/listings/{$code}");
      $etag = $response->getHeaderLine('ETag');
      ```

      ```javascript JavaScript theme={null}
      const res = await fetch(`${BASE}/account/listings/${code}`, { headers });
      const etag = res.headers.get('ETag');
      const { data } = await res.json();
      ```

      ```python Python theme={null}
      res = session.get(f"{BASE}/account/listings/{code}")
      etag = res.headers["ETag"]
      print(res.json()["data"]["status"])
      ```
    </CodeGroup>

    Antetul `ETag` (și câmpul `etag`) e versiunea anunțului. Îl trimiți înapoi în `If-Match` când editezi, ca să nu suprascrii o modificare făcută între timp. Vezi [Editarea](/ro/concepts/editing).
  </Step>

  <Step title="Adaugă poze">
    `PUT /listings/{code}/photos` primește lista completă a pozelor, în ordine. Prima e coperta. Cel mai simplu e să dai URL-uri publice HTTPS, pe care Housera le descarcă.

    <CodeGroup>
      ```bash curl theme={null}
      curl -X PUT https://staging.housera.ro/api/v1/listings/HEWPUTA8/photos \
        -H "Authorization: Bearer $HOUSERA_KEY" \
        -H "Accept: application/json" \
        -H "Content-Type: application/json" \
        -d '{
          "photos": [
            { "url": "https://cdn.exemplu.ro/anunturi/1029/living.jpg", "rooms": ["living"] },
            { "url": "https://cdn.exemplu.ro/anunturi/1029/bucatarie.jpg", "rooms": ["kitchen"] },
            { "url": "https://cdn.exemplu.ro/anunturi/1029/dormitor.jpg", "rooms": ["bedroom"] }
          ]
        }'
      ```

      ```php PHP theme={null}
      $response = $client->put("listings/{$code}/photos", ['json' => ['photos' => [
          ['url' => 'https://cdn.exemplu.ro/anunturi/1029/living.jpg', 'rooms' => ['living']],
          ['url' => 'https://cdn.exemplu.ro/anunturi/1029/bucatarie.jpg', 'rooms' => ['kitchen']],
      ]]]);
      ```

      ```javascript JavaScript theme={null}
      await fetch(`${BASE}/listings/${code}/photos`, {
        method: 'PUT',
        headers,
        body: JSON.stringify({
          photos: [
            { url: 'https://cdn.exemplu.ro/anunturi/1029/living.jpg', rooms: ['living'] },
            { url: 'https://cdn.exemplu.ro/anunturi/1029/bucatarie.jpg', rooms: ['kitchen'] },
          ],
        }),
      });
      ```

      ```python Python theme={null}
      session.put(f"{BASE}/listings/{code}/photos", json={"photos": [
          {"url": "https://cdn.exemplu.ro/anunturi/1029/living.jpg", "rooms": ["living"]},
          {"url": "https://cdn.exemplu.ro/anunturi/1029/bucatarie.jpg", "rooms": ["kitchen"]},
      ]})
      ```
    </CodeGroup>

    Răspunsul vine imediat, `202 Accepted`, cu `{"queued": 3}`. Pozele se descarcă și se procesează în fundal. Când `media.processing` din `GET /account/listings/{code}` redevine `false`, pozele sunt pe anunț. Pentru fișiere din sistemul tău, fără URL public, vezi [Pozele](/ro/guides/photos).
  </Step>
</Steps>

## Ce urmează

Anunțul rămâne `pending` până când un moderator îl aprobă. Atunci devine `active` și apare în căutare. Ca să afli fără să întrebi mereu, configurează un webhook pentru `listing.published` și `listing.rejected`.

<CardGroup cols={2}>
  <Card title="Moderarea" icon="shield-check" href="/ro/concepts/moderation">
    Stările anunțului și tranzițiile.
  </Card>

  <Card title="Webhook-uri" icon="bell" href="/ro/webhooks">
    Afli imediat când un anunț e publicat sau respins.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.