Une seule API, plusieurs CRM
L'API Ts-Immo expose un format unifié au format `snake_case` (JSON Jackson), quel que soit le CRM source. Que vous utilisiez Apimo, Hektor, Netty, Sweepbright, AdNov ou Transim, vous interrogez toujours les mêmes endpoints avec la même structure de réponse.
Endpoints principaux
| Méthode | Endpoint | Description |
|---|---|---|
| GET | /v1/gateway/public/properties/{agencySlug} | Liste publique des biens d'une agence (sans auth) |
| GET | /v1/gateway/public/properties/{agencySlug}/{titleSlug} | Détail public d'un bien |
| GET | /v1/gateway/public/properties/{id}/cover | Photo de couverture (302 vers URL pré-signée) |
| GET | /v1/gateway/public/properties/{id}/pdf | Fiche PDF du bien (addon `PROPERTY_SHEET`) |
| GET | /v1/gateway/properties | Liste des biens (token client) |
| GET | /v1/gateway/properties/{id} | Détail d'un bien (token client) |
| POST | /v1/gateway/properties/search/ai | Recherche en langage naturel |
| POST | /v1/gateway/estimations | Estimation de prix avec capture de lead |
| GET | /v1/gateway/leads | Liste paginée des leads capturés |
| GET | /v1/gateway/m2m/estates/agencies | Liste des agences publiées (clé M2M) |
| POST | /v1/gateway/m2m/estates/search/ai | Recherche IA cross-agences (clé M2M) |
Authentification
Trois régimes d'authentification : (1) Public — pas d'auth pour /public/**. (2) Client — `Authorization: Bearer <token>` ou en-tête `X-TS-IMMO-KEY: <token>`, token OAuth2 ou API token généré depuis le dashboard. (3) M2M — clé M2M dédiée pour les serveurs backend (BFF).
# Public — sans authentification
curl https://api.ts-immo.org/v1/gateway/public/properties/agence-cote-d-azur
# Client — avec API token
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
https://api.ts-immo.org/v1/gateway/propertiesConventions de nommage
- JSON bodies : `snake_case` (Jackson SNAKE_CASE)
- Query parameters : `camelCase`
- Dates : ISO 8601 UTC (`2026-04-18T10:00:00Z`)
- IDs : UUID v4
- Champs `null` omis des réponses (`@JsonInclude(NON_NULL)`)
- Enums : lowercase pour `PublicPropertyDto` (`house`, `sale`, `available`) — UPPERCASE pour `PropertySummary` M2M (`HOUSE`, `SALE`, `AVAILABLE`)
Format de réponse — bien public
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"uri": {
"fr": "agence-cote-d-azur/villa-4-pieces-vue-mer",
"en": "agence-cote-d-azur/4-room-villa-sea-view"
},
"type": "house",
"offer_type": "sale",
"status": "available",
"title": { "fr": "Villa 4 pièces vue mer", "en": "4-room villa sea view" },
"bedrooms": 3,
"financial": {
"transaction": {
"price": { "amount": 850000.00, "currency": "EUR" }
}
},
"location": {
"city": "Nice",
"postal_code": "06000",
"country": "France",
"country_code": "FR"
},
"images": [
{ "url": "https://cdn.example.com/photo1.jpg", "ordinal": 0 }
]
}
]Pagination
Pas de pagination par page pour le endpoint public mono-agence (renvoie tout le portefeuille). L'endpoint M2M cross-agences utilise `offset` + `limit` (limit max 100, défaut 20). L'endpoint `/leads` utilise `page` + `size` (page zero-based, size défaut 50, max 200).
Capture de leads
L'API ne propose pas de `POST /leads` direct. Les leads sont capturés via le endpoint d'estimation `POST /v1/gateway/estimations` (en passant un objet `lead { first_name, last_name, email, project_timeframe_months }`). Ils sont ensuite consultables via `GET /v1/gateway/leads`.
Webhooks entrants (inbound)
Ts-Immo expose deux endpoints qui RECOIVENT des webhooks depuis les CRM partenaires.
| CRM | Endpoint | En-tête de signature | Algo |
|---|---|---|---|
| Sweepbright | POST /v1/gateway/webhooks/sweepbright/{gatewayId} | X-Hook-Signature | HMAC-SHA1 |
| Whise | POST /v1/gateway/webhooks/whise/{gatewayId} | X-Whise-Signature | HMAC-SHA256 |
Webhooks sortants (output connector)
Le connecteur de sortie `WEBHOOK` permet de pousser le portefeuille normalisé Ts-Immo vers un endpoint cible (votre Next.js par exemple, pour déclencher une revalidation ISR). Configuration : `{ "url": "https://monsite.com/api/sync-webhook" }`.
Format d'erreur
| HTTP | code | Cause |
|---|---|---|
| 401 | AUTH_FAILED | Token manquant / invalide / expiré |
| 403 | FORBIDDEN / addon_required | Addon manquant sur la passerelle |
| 404 | NOT_FOUND | Ressource introuvable |
| 409 | CONFLICT | Conflit métier |
| 429 | quota_exceeded | Quota journalier dépassé (estimations) |
| 503 | DOWNSTREAM_ERROR | Service externe indisponible |
{
"message": "Message lisible par un humain",
"code": "ERROR_CODE",
"timestamp": "2026-04-18T10:00:00Z",
"detail": "Information complémentaire (champ fautif, etc.)"
}