Inserate aus deinem CRM synchronisieren
Pushe und upserte Inserate aus deinem eigenen CRM/ERP nach WOHNO — mit external_ref, Idempotency-Keys und Signed-URL-Bild-Uploads.
Dieser Guide zeigt, wie du WOHNO mit den Inseraten synchron hältst, die du bereits in deinem eigenen CRM oder ERP verwaltest — Inserate von einem Server erstellen, aktualisieren und entduplizieren, Bilder hochladen und per Webhooks auf Änderungen reagieren.
Welches Problem das löst: Deine Single Source of Truth ist dein CRM. Du möchtest, dass jede Inserats-Änderung dort automatisch und idempotent nach WOHNO fließt, ohne bei Retries Duplikate zu erzeugen.
Beta / dark-shipped. Die Write-Oberfläche für Inserate (Plan 58) ist hinter dem Feature-Flag
LISTINGS_WRITE_API_ENABLEDgegated. Solange das Flag aus ist, liefern diese Endpoints404. Bitte deinen WOHNO-Ansprechpartner, es für deine Organisation zu aktivieren. Die Write-Operationen sindx-internalund noch nicht in der öffentlichen Referenz sichtbar.
Voraussetzungen
- Ein Secret Key (
sk_live_…) — diese Endpoints sind immer sk-only. Publishable Keys scheitern mit403 INSUFFICIENT_SCOPE. - Scope
listings:write(impliziertlistings:read);listings:delete, falls du auch Inserate entfernst. LISTINGS_WRITE_API_ENABLEDfür deine Organisation aktiviert.
Halte deinen Secret Key auf dem Server. Liefere ihn niemals in einem Browser-Bundle aus.
Schritt 1 — Inserat per eigener ID upserten
Sende den stabilen Identifier deines CRM als external_ref. Das erste POST
erstellt das Inserat; spätere POSTs mit der gleichen external_ref
aktualisieren das bestehende (org-scoped Upsert), sodass du nie Duplikate
erzeugst.
curl -X POST https://wohno.de/api/v1/listings \
-H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7c9e6a3d-1f2b-4c8a-9d10-abc123def456" \
-d '{
"external_ref": "CRM-48217",
"title": "Bright 2-room apartment",
"street": "Beispielstraße",
"house_number": "12",
"zip_code": "10115",
"city": "Berlin",
"property_type": "apartment",
"living_area": 58,
"rooms": 2,
"rent_cold": 980,
"status": "draft"
}'Antworten:
201 Createdmit{ "data": ListingPublicDto }bei einem frischen Create.200 OKmit{ "data": ListingPublicDto }, wenn dieexternal_refbereits existiert (Update/Upsert).
status ist draft (Standard) oder active; nur draft → active
veröffentlicht das Inserat. Bei jedem Write läuft dieselbe Moderations- und
Geocoding-Pipeline wie im Dashboard-Wizard.
Optional — Inserat einem bestimmten Owner zuweisen
Standardmäßig gehört ein über die API erstelltes Inserat dem Nutzer, der den
API-Key erstellt hat. Um es stattdessen einem bestimmten Team-Mitglied
zuzuweisen — z. B. dem Makler, der es in deinem CRM betreut — gib dessen E-Mail
als owner_email an:
curl -X POST https://wohno.de/api/v1/listings \
-H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"external_ref": "CRM-48217",
"title": "Bright 2-room apartment",
"street": "Beispielstraße",
"zip_code": "10115",
"city": "Berlin",
"property_type": "apartment",
"living_area": 58,
"rooms": 2,
"rent_cold": 980,
"owner_email": "makler@example.com"
}'- Die E-Mail muss zu einem Mitglied derselben Organisation gehören (Rolle
owner,adminodermember). - Eine unbekannte Adresse oder ein Nicht-Mitglied wird mit
403 FORBIDDENabgelehnt — bewusst mit derselben Meldung, damit der Endpoint nicht dazu genutzt werden kann, zu prüfen, welche E-Mails ein WOHNO-Konto haben. owner_emailwirkt nur beim Create. Bei einemexternal_ref-Upsert auf ein bestehendes Inserat wird es ignoriert — der Owner wird nie umgehängt.- Weglassen → Fallback auf den Ersteller des API-Keys.
Tipp: Richte deinen Secret Key auf einen dedizierten technischen Nutzer aus. Der Inserats-Owner ist ein echtes Konto; wird ein persönliches Owner-Konto später entfernt, geht der Besitz nicht automatisch über. Ein
owner_emailpro Datensatz hält jedes Inserat am richtigen Makler — unabhängig davon, welcher Key es gepusht hat.
Schritt 2 — Retries mit Idempotency-Key absichern
Übergib eine clientseitig generierte UUID v4 im Idempotency-Key-Header (wie
oben) und verwende sie bei Netzwerk-Retries unverändert wieder.
- Der Key ist 24 Stunden gültig. Ein wiederholter Request mit demselben Key liefert die gleiche Antwort, ohne die Aktion erneut auszuführen.
- Denselben Key mit einem anderen Body zu senden, liefert
409 IDEMPOTENCY_KEY_REUSED— ein Key ist an genau einen Request-Body gebunden.
Generiere einen Key pro logischer CRM-Sync-Operation.
Schritt 3 — Lifecycle aktualisieren oder umschalten
Nutze PATCH für partielle Updates. external_ref kann per PATCH nicht
geändert werden. Die Lifecycle-Status rented und archived sind hier
erreichbar:
curl -X PATCH https://wohno.de/api/v1/listings/9f1c2a3b-1111-2222-3333-444455556666 \
-H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "rent_cold": 950, "status": "active" }'Re-Geocoding läuft nur, wenn du die Adresse änderst. Ein draft → active-Übergang
emittiert listing.published.
Schritt 4 — Bilder per Signed-URL hochladen
Bild-Uploads nutzen einen zweistufigen Signed-URL-Flow — keine große Binärdatei läuft durch die API. Fordere zuerst eine Upload-URL an:
curl -X POST https://wohno.de/api/v1/listings/9f1c2a3b-1111-2222-3333-444455556666/images \
-H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "filename": "living-room.jpg", "content_type": "image/jpeg", "size": 842133, "sort_order": 0 }'Antwort (201):
{
"data": {
"image_id": "img_5f4e3d2c",
"upload_url": "https://storage.wohno.de/...signed...",
"upload_token": "...",
"expires_at": "2026-06-04T12:10:00.000Z"
}
}Lade dann die Binärdatei vor expires_at per PUT direkt an upload_url.
Erlaubte Typen sind image/jpeg, image/png, image/webp; max. 5 MB. Entferne
ein Bild mit DELETE /api/v1/listings/{id}/images?image_id=img_5f4e3d2c.
Schritt 5 — Per Webhooks synchron bleiben
Abonniere listing.*-Events, damit dein CRM von Moderations-Ergebnissen und
Publish-Status-Änderungen erfährt, die WOHNO pusht. Das vollständige Setup
beschreibt Auf Events mit Webhooks reagieren;
die relevanten Event-Typen für diesen Flow sind listing.created,
listing.published, listing.updated, listing.deleted, listing.moderated
und listing.image.uploaded.
Fehlerbehandlung
| Code | HTTP | Was passiert ist | Lösung |
|---|---|---|---|
INSUFFICIENT_SCOPE | 403 | pk_-Key genutzt oder listings:write fehlt | Secret Key mit Write-Scope verwenden. |
IDEMPOTENCY_KEY_REUSED | 409 | Gleicher Key, anderer Body | Frischen Key pro abweichendem Payload verwenden. |
VALIDATION_ERROR | 400 | Fehlende/ungültige Felder (siehe details.fields) | Body korrigieren; die Antwort listet die fehlerhaften Felder. |
FORBIDDEN | 403 | Plan-/Quota-Limit (z. B. Free-Inserats-Cap) | Plan upgraden oder Volumen reduzieren. |
FORBIDDEN | 403 | owner_email ist kein Mitglied deiner Org | E-Mail eines bestehenden Org-Mitglieds nutzen (owner/admin/member). |
NOT_FOUND | 404 | Flag aus, oder Inserat gehört zu anderer Org | Flag und Inserats-Besitz prüfen. |
Idempotenz- und Fehler-Details findest du in der Konventions-Referenz.
Best Practices
- Eine
external_refpro CRM-Datensatz. Genau das macht die Synchronisation über vollständige Re-Importe hinweg idempotent. - Sorgfältig batchen. Das Rate-Limit liegt bei 1000 Requests/Stunde pro Key;
respektiere
Retry-Afterbei429. - Bilder hochladen, nachdem das Inserat existiert, dann
statusaufactiveumstellen.
Nächste Schritte
- Auf Events mit Webhooks reagieren — Updates gepusht bekommen statt zu pollen.
- Inserate auf deiner Website einbetten — die synchronisierten Inserate öffentlich anzeigen.
- API-Referenz — Eingabefelder für Listing-Writes.