Custom Property API
Version1.0 · Updated on 2 mai 2026
Overview
The Custom Property API lets housing providers push properties to Toppla from their own tools (internal PMS, property hub or website), without manual entry. Pushed properties automatically appear in the corporate housing marketplace.
Typical use cases
- Sync a PMS or internal tool with the Toppla marketplace.
- Update prices and availability through a nightly cron job.
- Automatically deactivate a property rented outside Toppla.
- Connect an external website to the Toppla catalogue.
Authentication
All requests use a Bearer token in the Authorization :
Authorization: Bearer sk_immo_eDc72cng7f3EM_yBjyieyt--i35TGjV5-qW1HoFyeIA
Generation. From the Toppla dashboard → Connect → Custom Property API→ “Generate a key”. The user must have the Admin or Developer role in their organisation.
Format. sk_immo_ followed by 43 base64url characters (32 bytes of random entropy). The key is displayedonly oncewhen created — Toppla stores only its SHA-256 hash.
Rotation.Multiple active keys per organisation are supported (labelled by environment: Production, Staging, CI…). To revoke one, click “Revoke” on its row. Any request with that key then returns 401 immediately.
Endpoints
/api/v1/propertiesCreates a property if the external_id is unknown to Toppla; otherwise updates it. Idempotent.
201 · creation200 · updated/api/v1/propertiesPaginated list of properties pushed by your organisation through the API. Query parameters: ?limit=50&offset=0 (maximum 200).
200 · OK/api/v1/properties/:external_idRetrieves an individual property with all its attributes.
200 · OK404 · property not found/api/v1/properties/:external_idSoft delete: sets status to 'inactive'. The property disappears from the marketplace but remains in the database.
200 · OK404 · property not foundPayload schema
All fields are optional except external_id. Values omitted from an update POST request are not reset— to clear a field, explicitly send null.
| Field | Type | Description |
|---|---|---|
external_idRequired | string ≤200 | Your unique property identifier, used as the upsert key. |
status | "draft" | "active" | "inactive" | Marketplace visibility. Default: draft. |
property_type | string | Free-form property type (e.g. "T2", "Studio", "House"). |
location_text | string | Display address or city. |
is_exact_address | boolean | If true, the exact address is disclosed publicly. |
address_latitude | number ∈ [-90, 90] | WGS84 latitude for map placement. |
address_longitude | number ∈ [-180, 180] | WGS84 longitude. |
capacity | object | Free-form fields: { bedrooms, bathrooms, surface_m2, max_guests }. |
amenities | string[] | Array of amenities (e.g. ["wifi", "parking"]). |
photos | array | Array of objects { url, alt? }. Publicly accessible HTTPS URLs. |
description | string | Free-form description. |
pricing | object | Free-form fields: { monthly_eur, charges_eur, deposit_eur, ... }. |
promotion | object | Free-form fields for temporary offers: { discount_percent, end_date }. |
Examples
curl -X POST https://app.toppla.io/api/v1/properties \
-H "Authorization: Bearer sk_immo_..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "BIEN-2024-001",
"status": "active",
"property_type": "T2",
"location_text": "Lyon 7e",
"pricing": { "monthly_eur": 950, "charges_eur": 60 },
"capacity": { "bedrooms": 1, "surface_m2": 42 },
"amenities": ["wifi", "lave-linge"],
"photos": [{ "url": "https://cdn.exemple.fr/p1.jpg" }]
}'const res = await fetch('https://app.toppla.io/api/v1/properties', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TOPPLA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
external_id: 'BIEN-2024-001',
status: 'active',
property_type: 'T2',
location_text: 'Lyon 7e',
pricing: { monthly_eur: 950, charges_eur: 60 },
}),
});
const { property } = await res.json();
console.log(property.id);import os, requests
r = requests.post(
"https://app.toppla.io/api/v1/properties",
headers={
"Authorization": f"Bearer {os.environ['TOPPLA_API_KEY']}",
"Content-Type": "application/json",
},
json={
"external_id": "BIEN-2024-001",
"status": "active",
"property_type": "T2",
"location_text": "Lyon 7e",
"pricing": {"monthly_eur": 950, "charges_eur": 60},
},
timeout=10,
)
r.raise_for_status()
print(r.json()["property"]["id"])Error codes
- 400Invalid payloadThe body returns a specific message. Correct the issue and retry.
- 401Missing authentication or invalid/revoked keyCheck the Authorization header and that the key has not been revoked.
- 403Invalid scopeThe key is for HRIS but you are calling a Housing endpoint (or vice versa). Generate a key with the correct scope.
- 404Property not foundThe external_id is case-sensitive and scoped to your organisation.
- 500Server errorRetry; if the issue persists, contact support with the timestamp.
Known limitations
- No outgoing webhooks — Toppla does not notify integrators of marketplace changes; integrators must poll
GET /properties. - No dedicated sandbox — test with a separate pre-production account.
- No documented rate limiting at present (coming soon).
- No batch endpoint — one POST request = one property.
Support
A question, bug or specific need? Write to support@toppla.fr with the request timestamp and returned ID, if available.