CleanedWeb Docs

AI agent integration

CleanedWeb integration guide for AI agents

Integrate AI agents with CleanedWeb using runtime discovery, bounded property searches, canonical records, explicit Unit ceilings, and deterministic acceptance checks.

By CleanedWeb Published Updated

Agent control loop

Discover, decide, then act.

Stop whenever runtime capability, authority, request bounds, or delivery acceptance is unresolved.

Capability-discovery decision path
  1. 01DiscoverRead capabilities
  2. 02GateMachine access enabled?
  3. 03AuthorizeTrusted client owns key
  4. 04BoundMarket, limit, Units
  5. 05AcceptValidate and persist
Agent stop and continue policy
Runtime stateDecisionNext action
Capability absentStopDo not infer an operation
Location ambiguousPauseAsk for a candidate choice
Bounded response acceptedContinuePersist response and receipt
Schema or count mismatchRejectKeep evidence and escalate

This guide describes the current public contract. The API base URL is https://app.cleanedweb.com.

1. Discover before acting

Fetch the uncached runtime manifest before composing a request:

curl --fail-with-body --silent --show-error \
  https://app.cleanedweb.com/v1/capabilities

Treat available_markets as runtime readiness. A market mentioned on a marketing page or in static coverage is not necessarily ready or entitled. If capabilities returns 503, availability is unknown; do not replace it with an empty market list.

Load the authoritative operation contract and referenced schemas:

  • https://cleanedweb.com/openapi.json
  • https://cleanedweb.com/schemas/property-profile-v2.json
  • https://cleanedweb.com/schemas/property-event-v1.json
  • https://cleanedweb.com/schemas/error-v1.json
  • https://cleanedweb.com/schemas/property-profile-field-catalog-v2.json
  • https://cleanedweb.com/arazzo.json
  • https://cleanedweb.com/asyncapi.json
  • https://cleanedweb.com/changelog.json

These static artifacts are intentionally hosted on cleanedweb.com and do not require a portal session. Their OpenAPI operations still target the API base at https://app.cleanedweb.com.

Reject unknown major contract or schema versions instead of guessing. Use the field catalog for semantics and units; JSON Schema remains authoritative for structure. Use Arazzo for deterministic operation order and the workflow guide for human decisions and stop conditions.

2. Authenticate without exposing credentials to the model

GET /v1/capabilities is public at the API base. OpenAPI, schemas, and examples are public at cleanedweb.com. Browser property operations use the production workspace session cookie named __Host-cw_session. Machine clients may use a workspace Bearer API key only when capabilities returns machine_access.enabled: true.

A human signs in at https://app.cleanedweb.com. The HTTP client or trusted tool adapter owns the resulting cookie. Never place the cookie in a prompt, model-visible tool result, log, URL, or generated code. For protected POST requests, send Origin: https://app.cleanedweb.com; the server enforces same-origin submission.

If the session is missing or expired, return an authentication-required action to the user. Do not automate magic-link inbox access unless the user has separately authorized that mailbox workflow.

API-key secrets use the cw_<environment>_... form, are shown once, and must stay outside model context. They have only the properties:read scope. A machine search must include a bounded limit, max_units, and an Idempotency-Key header.

3. Confirm workspace markets

With the session cookie, call:

curl --fail-with-body --silent --show-error \
  --cookie "__Host-cw_session=SESSION_VALUE" \
  https://app.cleanedweb.com/v1/markets

This endpoint returns only markets that are both enabled for the workspace and ready in the maintained serving projection. Use data_updated_at and available_records as snapshot metadata, not as a promise of source-real-time data.

4. Resolve locations before searching

For user-entered location text, request canonical candidates:

curl --fail-with-body --silent --show-error \
  --cookie "__Host-cw_session=SESSION_VALUE" \
  "https://app.cleanedweb.com/v1/properties/locations?market=LU&q=Belair&limit=8"

Present ambiguous candidates to the user or select one only when the user's intent is unambiguous. Send both location and the selected location_place_id in search. The default candidate limit is 8 and the maximum is 20.

For a question that only asks "how many", call the exact aggregate instead of searching and discarding rich records:

curl --fail-with-body --silent --show-error \
  --request POST \
  --cookie "__Host-cw_session=SESSION_VALUE" \
  --header "Origin: https://app.cleanedweb.com" \
  --header "Content-Type: application/json" \
  --data '{"market":"ES","response_schema":"property-profile-v2","deal_type":["buy"],"location":"Madrid","max_price":250000,"min_bedrooms":3}' \
  https://app.cleanedweb.com/v1/properties/count

min_bedrooms is inclusive, so "more than two" is 3. The response contains total_matched, whether the projection-bound exact count was cached, the resolved query, and the snapshot identity; it contains no property records and does not consume delivery Units.

5. Search the maintained snapshot

Use the published example as a starting point: https://cleanedweb.com/examples/search-request.json.

curl --fail-with-body --silent --show-error \
  --request POST \
  --cookie "__Host-cw_session=SESSION_VALUE" \
  --header "Origin: https://app.cleanedweb.com" \
  --header "Content-Type: application/json" \
  --data '{"market":"LU","response_schema":"property-profile-v2","deal_type":["buy"],"limit":100}' \
  https://app.cleanedweb.com/v1/properties/search

Bounded searches return has_more and an opaque signed next_cursor. Send the cursor with the unchanged query and positive limit to read the next page. If the projection advances, snapshot_changed requires a restart from page one. Omit limit to request every match. Before committing results, require all of the following:

  1. returned === results.length.
  2. Every result has schema_version === "property-profile-v2".
  3. Every property_entity_id is present and unique within the response.
  4. snapshot.dataUpdatedAt satisfies the workflow's freshness policy.
  5. has_more === false after aggregating cursor pages, or use one complete unbounded response, when a complete snapshot is required.

Use property_entity_id as the canonical upsert key. Retain sources as provenance; do not turn a source listing ID into the canonical property identity.

Machine clients cannot omit limit. They must also add max_units to the JSON body and send a unique Idempotency-Key. A successful response includes usage plus X-CleanedWeb-Request-Id, X-CleanedWeb-Units, and X-CleanedWeb-Unit-Balance headers.

6. Retrieve one property or its changes

The authenticated OpenAPI contract publishes:

  • GET /v1/properties/{property_entity_id} for the current canonical property.
  • GET /v1/properties/{property_entity_id}/changes?limit=50 for newest-first, finalized observed changes. The change limit must be between 1 and 200.

Suspected transitions and incomplete observer runs are excluded from the finalized change stream. Notification delivery is not the system of record; re-read the property endpoint when a workflow needs current state.

7. Metering and receipts

Read unit_costs from GET /v1/capabilities. An empty object means no operation cost is published through this runtime contract; do not invent a cost from pricing copy, an HTTP status, or a webhook field. Workspace receipts or the Unit ledger are the billing source of truth when they are available.

8. Failure policy

Parse the error-v1 envelope for operations covered by OpenAPI. Do not retry authentication, entitlement, not-found, or validation failures unchanged. Retry a 503 with bounded exponential backoff and jitter. See https://cleanedweb.com/docs/errors.md for the exact boundary and recovery table.

9. Deterministic acceptance policy

The reference acceptance policy distinguishes successful and empty searches from unsafe responses. It stops on unsupported markets, unknown contract or property schema versions, duplicate canonical IDs, response-count mismatches, stale snapshots, and unexpected truncation. It requests user input for ambiguous locations, retries bounded transient failures and rate limits, reuses matching idempotent receipts, and acknowledges already-processed webhook IDs without running their business effect twice.

Unit-budget, durable idempotency-receipt, and shared rate-limit cases apply only when runtime machine_access.enabled is true and unit_costs is non-empty. Otherwise, do not invent an estimate or apply the machine budget path.

Data boundary

CleanedWeb returns maintained delivery-safe canonical records, not raw acquisition artifacts. Optional values can be absent. Do not infer a missing value, legal right, freshness level, or real-world event that the response does not state.

Standards and further reading

Continue with the primary sources.

MCP Connection lifecycle and capability negotiation OpenAPI Initiative Arazzo workflow specification