# Brochures

> Create, share and manage interactive brochures of up to 25 listings from any source.

A brochure is a branded web page of 1 to 25 listings from any source, with an optional recipient name and note, maps, galleries, view tracking and an expiry. Creating one returns a link at `https://www.fondaro.com/b/:slug` to send to a buyer.

The `/brochures` routes take a signed-in dashboard session with an active organization (`Authorization: Bearer <session token>`). For automation with a key, use the brochure tools of the [MCP server](https://www.fondaro.com/docs/api/mcp.md), which return the same link.

**Access.** A member works with the brochures they created; an admin with every brochure in the organization. A non-admin's theme and colour follow the organization's locked brochure preset.

## Create a brochure

### `POST /brochures`

| Field | Type | Rules |
|---|---|---|
| `listings` | array | Required: 1 to 25 `{ source, sourcePropertyId, sourceCountry? }`, in display order |
| `title` | string | Up to 255 characters |
| `recipientName` | string | Shown on the cover |
| `recipientNote` | string | Up to 4,096 characters |
| `leadIds` | integer[] | Up to 25 CRM leads to connect; not shown publicly |
| `themeName` | string | A theme from `GET /properties/themes` |
| `accentColorOverride` | string | Hex colour |
| `showLocations` | boolean | Show addresses and maps publicly; defaults to the organization's setting |
| `agentId` | string | The agent shown on the brochure (roster id or user id) |
| `expiresInDays` | integer | 1 to 365, default 90 |
| `idempotencyId` | string | Repeat a create safely |

`source` is `internal_mls` (Fondaro listings), `resales_online`, `zoddak`, `inmobalia`, or a property site id (`idealista`, `rightmove`, `zoopla`, `immobiliare`, `immoscout`, `immowelt`, `funda`, `seloger`, `otodom`, `realtor`, `homes`, `trulia`, `zillow`, `redfin`, `loopnet`, `propertyfinder`, `bayut`, `dubizzle`, `centris`). `sourceCountry` (`es`, `it` or `pt`) says which country an Idealista listing is in.

```bash
curl -X POST https://api.fondaro.com/brochures \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Estepona homes for Andrew",
    "recipientName": "Andrew",
    "recipientNote": "Five homes selected for you.",
    "listings": [
      { "source": "internal_mls", "sourcePropertyId": "0d24495c-33ac-4b24-9b6b-48dcad68ef65" },
      { "source": "idealista", "sourcePropertyId": "105432100", "sourceCountry": "es" }
    ]
  }'
```

```json
{
  "id": "9d48015b-c4df-40be-a9db-388d1a1fa3f1",
  "slug": "unguessable-random-slug",
  "title": "Estepona homes for Andrew",
  "showLocations": true,
  "expiresAt": "2026-12-31T10:00:00.000Z",
  "listings": [{ "id": "7cb69216-21f2-42ee-9b34-5bd48488e161", "source": "internal_mls", "title": "Villa in Estepona" }],
  "linkedLeads": [],
  "publicUrl": "https://www.fondaro.com/b/unguessable-random-slug"
}
```

Each listing is copied into the brochure when it is added, so later source changes do not alter it until you refresh. Photos and map positions may finish processing after the response; the link works at once.

## List and read

### `GET /brochures`

| Query | Meaning |
|---|---|
| `q` | Text in the title, the recipient or a connected lead's name |
| `createdById`, `createdByIds` | Brochures made by these people (admins) |
| `leadId` | Brochures connected to this lead |
| `source`, `sourcePropertyId` | Brochures containing this listing |
| `expired`, `revoked` | Filter by state |
| `page`, `limit` | Send either to get pages (up to 20 per page) |

### `GET /brochures/:id`

The brochure with its listings, live branding, the organization preset and `publicUrl`.

## Change a brochure

### `PATCH /brochures/:id`

Changes `title`, `recipientName`, `recipientNote`, `themeName`, `accentColorOverride`, `showLocations` or `leadIds`. A field you leave out is unchanged; `null` clears a text field or the colour. `leadIds` replaces the whole set (`[]` clears it).

```bash
curl -X PATCH https://api.fondaro.com/brochures/9d48015b-c4df-40be-a9db-388d1a1fa3f1 \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"recipientNote":"I moved our two favourites to the top.","showLocations":false}'
```

### `POST /brochures/:id/duplicate`

Copies a brochure, its listings, corrections and lead connections into a new one owned by you, with a new link and a fresh 90-day expiry. No body.

## Manage the listings

`:listingId` below is the brochure listing's id from create or read, not the source listing's id.

| Method and path | Purpose |
|---|---|
| `POST /brochures/:id/listings` | Add one (`{ source, sourcePropertyId, sourceCountry?, position? }`, position from 0) |
| `DELETE /brochures/:id/listings/:listingId` | Remove one |
| `POST /brochures/:id/listings/order` | Reorder: `{ "listingIds": [...] }` with every current id exactly once |
| `POST /brochures/:id/listings/:listingId/refresh` | Read the source again, keeping your corrections |
| `PATCH /brochures/:id/listings/:listingId/details` | Correct what the brochure shows |
| `PATCH /brochures/:id/listings/:listingId/location` | Correct the address and map position |

**Details** take `titleOverride`, `referenceNumberOverride`, `descriptionOverride`, `propertyTypeOverride`, `priceOverride`, `priceToOverride`, `bedroomsOverride`, `bedroomsToOverride`, `bathroomsOverride`, `bathroomsToOverride`, `priceHidden` ("Price on request") and `factsHidden` (no bed and bath facts). `null` clears an override back to the source value.

**Location** takes `latitude`, `longitude`, `addressLine1`, `addressLine2`, `city`, `region`, `provinceState`, `postalCode`, `countryCode` and `community`.

## Revoke, renew and delete

| Method and path | Purpose |
|---|---|
| `POST /brochures/:id/revoke` | Turn the link off; nothing is deleted |
| `POST /brochures/:id/renew` | Turn it back on with a new expiry: `{ "expiresInDays": 90, "refreshListings": true }` |
| `DELETE /brochures/:id` | Delete the brochure for good |

Renewing keeps the same link. If some listings fail to refresh, the response lists them in `failedRefreshes` and the brochure stays valid.

## Organization preset

`GET /brochures/organization-preset` reads the agency's defaults; `PATCH` (admins only) sets `themeName`, `accentColorOverride`, `locked` (members cannot change theme or colour), `showLocationsByDefault`, `coverImageUrl` and up to 3 `resourceLinks` (`{ label, url }`).

## Themes

### `GET /properties/themes`

The brochure themes in picker order, with a property API key (`properties:read`) or a session.

```json
[
  {
    "name": "default",
    "label": "Atelier",
    "description": "Warm paper, soft ink, quietly designed.",
    "preview": {
      "light": { "primary": "#...", "background": "#...", "foreground": "#..." },
      "dark": { "primary": "#...", "background": "#...", "foreground": "#..." }
    },
    "fontFamily": "..."
  }
]
```

## The public page

`GET /public/brochures/:slug` (no authentication) returns what the brochure page shows and records a view; `GET /public/brochures/:slug/exists` only checks the link. Both answer `410 Gone` once a brochure is revoked or expired, and allow 60 requests a minute.

With `showLocations: false`, every public listing's address fields and coordinates are `null`; your own read keeps them. Connected leads never appear publicly. Anyone with the link sees the note and listings until it expires or is revoked, so keep private details out of the recipient fields.

## Errors

| Status | When |
|---|---|
| `400` | Invalid input, more than 25 listings, an incomplete reorder, or a lead you cannot use |
| `403` | Changing another member's brochure, or the preset as a member |
| `404` | Not in your organization, or not yours to see |
| `410` | Public reads of a revoked or expired brochure |

Source: https://www.fondaro.com/docs/api/properties/brochures
