PlatformXeDocs
Get API Key

Federated Search

Cross-app search index for tenant-owned resources.

The Federated Search service maintains an org-scoped search index that traverses application boundaries. Each Caldera app upserts the resources it owns (properties, bookings, partners, journeys, payouts) and consumers query a single endpoint that returns results across all of them.

When to use

  • A unified "global search" UI in xAdmin that surfaces matches across Lettings + Concierge.
  • An autocomplete dropdown in a partner portal that needs to find a property by partial name.
  • A WhatsApp bot that helps an agent locate a guest's booking by typing a name fragment.

The platform performs an ILIKE search over title + keywords. Results are at most 20 per query, with no guaranteed order. Queries shorter than 2 characters return an empty array.

Endpoints

POST /api/v1/search/index   — Upsert a resource into the index
GET  /api/v1/search/query   — Query the index

Index a resource

{
  "app": "lettings",
  "entityType": "PROPERTY",
  "entityId": "prop_abc",
  "title": "Lekki Penthouse",
  "subtitle": "3-bed, oceanfront, Lagos",
  "keywords": "lekki lagos penthouse 3 bedroom oceanfront luxury",
  "rank": 100,
  "metadata": { "pricePerNightNGN": 75000 }
}
FieldRequiredStored as
appyesapp — part of the upsert key
entityType, entityIdyespart of the upsert key
titleyestitle (searched)
keywordsnokeywords (searched)
subtitlenodescription. description is accepted as an alias.
urlnourl — deep link to the resource
metadatanometadata

Other fields are ignored. rank is accepted but not currently persisted.

subtitle is used only when it is a non-empty string; an empty subtitle falls through to description, then to the legacy content, and finally to null.

Accepted asynchronously (HTTP 202). The actual write is performed by the inngest worker; results may take a few seconds to appear in queries.

Re-indexing is a full replace

Re-indexing the same (app, entityType, entityId) upserts in place — no duplicates accumulate — and the upsert replaces every indexed field of that row: title, description, keywords, url and metadata all take the values of the latest request. A field you omit is cleared, not carried over from the previous version.

Send the complete record every time. To change one field, re-send the whole document with that field changed; there is no partial index update.

Query

GET /api/v1/search/query?q=lekki&app=lettings
Query paramDescription
q (required)Search term — minimum 2 characters.
app (optional)Restrict to a single app.

Response:

{
  "results": [
    {
      "id": "idx_abc",
      "organizationId": "org_xyz",
      "app": "lettings",
      "entityType": "PROPERTY",
      "entityId": "prop_abc",
      "title": "Lekki Penthouse",
      "subtitle": "3-bed, oceanfront, Lagos",
      "keywords": "lekki lagos penthouse …",
      "rank": 100,
      "metadata": { "pricePerNightNGN": 75000 },
      "indexedAt": "2026-05-01T12:00:00Z"
    }
  ]
}

Indexing tips

  • Re-index whenever the underlying record's display fields change (title rename, status transitions that affect visibility).
  • Drop the metadata object as small as practical — it round-trips on every query result.
  • Result order is not guaranteed. rank is not yet stored, so do not rely on it to order results.
  • Queries are case-insensitive. Both keywords and the search term are folded.

SDK usage

See the SDK reference for typed examples in TypeScript, Python, and Go.

Scopes required

EndpointScope
POST /api/v1/search/indexsearch:write
GET /api/v1/search/querysearch:read