Agents
List, update, and delete real estate agents. Manage avatars and list public agent profiles.
Manage real estate agent profiles for your organization.
The Identity Model
An agent is a per-organization roster row. Every member of a Clerk organization has exactly one agent row in that organization, created automatically the moment the membership exists. A user who belongs to three organizations has three independent rows, one per organization, and each carries that organization's own role fields.
Clerk is canonical for the person. firstName, lastName, and the avatar come from the member's Fondaro (Clerk) account. When the account profile changes, every one of that user's agent rows is updated. When you change one of those fields through PUT /agents/:id on a Clerk-linked row, the API writes the change through to the Clerk account first and then persists the canonical result, so the change is visible in every organization that person belongs to. Those write-through actions are restricted to organization admins and to the member themselves.
The organization is canonical for the role. title, displayEmail, phoneNumber, whatsappNumber, description, and isActive are owned by the organization, stored per row, and never synced anywhere.
Membership changes flow automatically: joining creates the row (or reactivates an existing one), leaving deactivates it while keeping the data, and rejoining reactivates it with the organization's fields intact.
Standalone agents (POST /agents/standalone) are the escape hatch for a public-facing face who has no Fondaro account, for example an external collaborator. They live in the same table, are owned entirely by the organization, and are never linked to a Clerk user.
Every agent reference is a UUID. agents.id is the only identifier any endpoint accepts or returns for an agent, including property_listings.agent_id and brochure agent fields. Clerk user IDs (user_...) are never stored in an agent reference column.
Removed Endpoints
These endpoints no longer exist. The behaviour they provided is now automatic.
| Removed | Replacement |
|---|---|
POST /agents (create a Clerk-linked agent) | Rows are created automatically from Clerk membership. Use POST /agents/standalone for a non-member face |
POST /agents/sync | The membership webhook keeps the roster converged; operators can re-run the reconciler if needed |
GET /agents/:id/account-profile | There is nothing to reconcile: Clerk values are written to the row unconditionally |
POST /agents/:id/adopt-account-field | Same as above |
Create a Standalone Agent
POST /agents/standalone
Authentication: Required (JWT)
Creates an agent profile that is not linked to a Clerk user account. Use this for external agents or collaborators who don't have a Fondaro login.
Request Body
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
firstName | string | Yes | First name | Max 100 chars, non-empty |
lastName | string | No | Last name | Max 100 chars |
displayEmail | string | No | Public email | Valid email format |
phoneNumber | string | No | Phone number | — |
whatsappNumber | string | No | WhatsApp number | — |
description | string | No | Agent bio | — |
title | string | No | Job title | — |
isActive | boolean | No | Active status | Default: true |
additionalInfo | object | No | Custom data | — |
Example
curl -X POST https://api.fondaro.com/agents/standalone \
-H "Authorization: Bearer <jwt-session-token>" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Miguel",
"lastName": "Fernández",
"displayEmail": "miguel@example.com",
"phoneNumber": "+34698765432",
"title": "Independent Agent"
}'List Agents
GET /agents
Authentication: Required (JWT)
Returns all agents for your organization.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
isActive | boolean | — | Filter by active status |
search | string | — | Search by name |
Example
curl "https://api.fondaro.com/agents?isActive=true&search=sarah" \
-H "Authorization: Bearer <jwt-session-token>"Get an Agent
GET /agents/:id
Authentication: Required (JWT)
The :id is the agent's UUID. Agents are scoped to your organization: an ID belonging to another organization returns 404.
Example
curl https://api.fondaro.com/agents/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d \
-H "Authorization: Bearer <jwt-session-token>"Errors
| Status | Condition |
|---|---|
404 | Agent not found |
Update an Agent
PUT /agents/:id
Authentication: Required (JWT)
All fields are optional: include only what you want to change.
On a Clerk-linked agent, firstName, lastName, and avatarUrl are written through to the member's Fondaro account and therefore change everywhere that person appears. Those fields may only be changed by an organization admin or by the member themselves. The remaining fields are owned by your organization and stay local to this row.
Request Body
| Field | Type | Description | Constraints |
|---|---|---|---|
firstName | string | First name | Max 100 chars |
lastName | string | Last name | Max 100 chars |
avatarUrl | string | Avatar URL | Max 500 chars |
avatarThumbnailUrl | string | Thumbnail URL | Max 500 chars |
displayEmail | string | Public email | Valid email |
phoneNumber | string | Phone number | — |
whatsappNumber | string | WhatsApp number | — |
description | string | Bio / description | — |
title | string | Job title | — |
isActive | boolean | Active status | — |
additionalInfo | object | Custom data | — |
Example
curl -X PUT https://api.fondaro.com/agents/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d \
-H "Authorization: Bearer <jwt-session-token>" \
-H "Content-Type: application/json" \
-d '{
"title": "Director of Sales",
"description": "Specializing in luxury beachfront properties on the Costa del Sol."
}'Delete an Agent
DELETE /agents/:id
Authentication: Required (JWT)
Example
curl -X DELETE https://api.fondaro.com/agents/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d \
-H "Authorization: Bearer <jwt-session-token>"Response
{
"success": true
}Upload Agent Avatar
POST /agents/:id/avatar
Authentication: Required (JWT)
Upload a profile photo for an agent. On a Clerk-linked agent the photo becomes the member's account photo and appears in every organization they belong to (organization admins and the member themselves only). On a standalone agent the image is resized, stored, and written to the row.
Request
Send the file as multipart/form-data with the field name avatar.
| Constraint | Value |
|---|---|
| Max file size | 5 MB |
| Accepted formats | JPEG, JPG, PNG, WebP, GIF |
Example
curl -X POST https://api.fondaro.com/agents/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d/avatar \
-H "Authorization: Bearer <jwt-session-token>" \
-F "avatar=@photo.jpg"Response
Returns the updated agent object with new avatarUrl and avatarThumbnailUrl fields.
Errors
| Status | Condition |
|---|---|
404 | Agent not found |
422 | File too large or invalid format |
Delete Agent Avatar
DELETE /agents/:id/avatar
Authentication: Required (JWT)
Removes the agent's avatar photo.
Example
curl -X DELETE https://api.fondaro.com/agents/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d/avatar \
-H "Authorization: Bearer <jwt-session-token>"Public: Active Agents with Listings
GET /properties/agents?organizationId=<uuid>
Authentication: None required (public endpoint)
Returns active agents that have at least one active property listing. This is intended for public-facing websites to display agent directories.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
organizationId | UUID | Yes | Organization ID to list agents for |
Example
curl "https://api.fondaro.com/properties/agents?organizationId=a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d"Response
Returns an array of public agent profiles (no internal IDs or timestamps):
[
{
"id": "f1e2d3c4-...",
"firstName": "Sarah",
"lastName": "Johnson",
"avatarUrl": "https://cdn.fondaro.com/avatars/...",
"avatarThumbnailUrl": "https://cdn.fondaro.com/avatars/...",
"displayEmail": "sarah@luxuryestates.com",
"phoneNumber": "+34612345678",
"whatsappNumber": "+34612345678",
"description": "Specializing in luxury beachfront properties.",
"title": "Senior Property Consultant"
}
]Response Format Note
Agent responses include both snake_case and camelCase field names for backwards compatibility:
first_name/firstNamelast_name/lastNameimage_url/avatarUrl
Prefer the camelCase versions in new integrations. The public endpoint only returns camelCase fields.