Public API
Push potential-owner leads into the system from external sources (landing pages, CRMs, lead aggregators). Single endpoint, JSON in / JSON out, Bearer-token auth, idempotent — duplicates return the existing record instead of erroring.
Content-Type: application/json · CORS: *
Pass your API token in the Authorization header using the Bearer scheme. Alternatively send it in the X-Api-Token header.
Authorization: Bearer YOUR_API_TOKENThe token is issued by the platform administrator. Keep it secret — never expose it in client-side code.
| Field | Type | Required | Description |
|---|---|---|---|
| name | string (1–200) | required | Contact / property name. |
| body | string (≤5000) | optional | Free-form description. |
| address | string (≤500) | optional | Postal address. |
| country | string (ISO 3166-1 alpha-2) | optional | Country code, e.g. "HR", "CZ". |
| language | string (ISO 639-1) | optional | Language code, e.g. "en", "cs". |
| www | string (URL) | optional | Website URL. |
| string (email) | optional | Contact email. | |
| phone | string (≤30) | optional | Primary phone. |
| phone_sec | string (≤30) | optional | Secondary phone. |
| whats_app | string (≤30) | optional | WhatsApp number. |
| capacity | integer ≥ 0 | optional | Number of guests / sleeping places. |
| validity | integer (0–5) | optional | Info validity rating (stars, integer 0–5). |
| category | string (slug) | optional | Property category slug from the admin-managed list. Default: apartment. |
| notice | string (≤2000) | optional | Internal note. |
| image_url | string (URL, ≤1000) | optional | URL of the main photo. |
Each prospect gets a dedup_key computed in this priority order:
host:<hostname> — derived from www (without protocol / www. prefix)email:<lowercased email>name:<lowercased name>|<country>If a prospect with the same key already exists, the endpoint returns 200 OK with the existing record and deduplicated: true — no duplicate is created. New prospects return 201 Created with deduplicated: false.
curl -X POST https://aperia.app/api/public/prospects \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Villa Marina",
"email": "owner@example.com",
"country": "HR",
"language": "en",
"category": "villa-luxury",
"capacity": 6,
"validity": 4,
"www": "https://villa-marina.example",
"image_url": "https://cdn.example.com/villa-marina/cover.jpg",
"notice": "Lead from landing page"
}'await fetch("https://aperia.app/api/public/prospects", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Villa Marina",
email: "owner@example.com",
country: "HR",
language: "en",
category: "villa-luxury",
capacity: 6,
validity: 4,
image_url: "https://cdn.example.com/villa-marina/cover.jpg",
}),
});Drop the snippet below into Swagger UI, Postman, Insomnia, or any OpenAPI-compatible tool to generate clients, mocks or interactive docs.
openapi: 3.1.0
info:
title: Prospects Import API
version: "1.1.1"
description: Public REST API for importing prospect leads. Idempotent — duplicates return the existing record.
servers:
- url: https://aperia.app
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
apiTokenHeader:
type: apiKey
in: header
name: X-Api-Token
schemas:
Prospect:
type: object
required: [name]
properties:
name: { type: string, minLength: 1, maxLength: 200 }
body: { type: string, maxLength: 5000, nullable: true }
address: { type: string, maxLength: 500, nullable: true }
country: { type: string, minLength: 2, maxLength: 2, description: "ISO 3166-1 alpha-2", nullable: true }
language: { type: string, minLength: 2, maxLength: 2, description: "ISO 639-1", nullable: true }
www: { type: string, format: uri, maxLength: 500, nullable: true }
email: { type: string, format: email, maxLength: 255, nullable: true }
phone: { type: string, maxLength: 30, nullable: true }
phone_sec: { type: string, maxLength: 30, nullable: true }
whats_app: { type: string, maxLength: 30, nullable: true }
capacity: { type: integer, minimum: 0, maximum: 100000, nullable: true }
validity: { type: integer, minimum: 0, maximum: 5, nullable: true, description: "Info validity rating (stars)" }
category:
type: string
description: "Category slug from the admin-managed list of property categories"
default: apartment
notice: { type: string, maxLength: 2000, nullable: true }
image_url: { type: string, format: uri, maxLength: 1000, nullable: true, description: "URL of the main photo" }
ProspectResult:
type: object
required: [id, date_created, deduplicated]
properties:
id: { type: string, format: uuid }
date_created: { type: string, format: date-time }
deduplicated: { type: boolean, description: "true = existing record returned, false = newly created" }
Error:
type: object
required: [error]
properties:
error: { type: string }
details: { type: object, additionalProperties: true }
security:
- bearerAuth: []
- apiTokenHeader: []
paths:
/api/public/prospects:
post:
summary: Create or deduplicate a prospect
operationId: createProspect
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/Prospect" }
responses:
"200":
description: Existing prospect returned (deduplicated)
content:
application/json:
schema: { $ref: "#/components/schemas/ProspectResult" }
"201":
description: Created
content:
application/json:
schema: { $ref: "#/components/schemas/ProspectResult" }
"400":
description: Validation failed
content:
application/json:
schema: { $ref: "#/components/schemas/Error" }
"401":
description: Unauthorized
content:
application/json:
schema: { $ref: "#/components/schemas/Error" }
"500":
description: Internal error
content:
application/json:
schema: { $ref: "#/components/schemas/Error" }HTTP/1.1 201 Created
Content-Type: application/json
{
"id": "0f9e1c5a-1b2d-4a3e-9c44-7e2d1a3b5c6d",
"date_created": "2026-05-05T12:34:56.000Z",
"deduplicated": false
}HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "0f9e1c5a-1b2d-4a3e-9c44-7e2d1a3b5c6d",
"date_created": "2026-05-05T12:34:56.000Z",
"deduplicated": true
}HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "validation_failed",
"details": {
"fieldErrors": { "email": ["Invalid email"] },
"formErrors": []
}
}HTTP/1.1 401 Unauthorized
Content-Type: application/json
{ "error": "unauthorized" }HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{ "error": "internal_error" }category valuesUse any active slug from the admin Property categories list:
Loading…
Default if omitted or unknown: apartment.