# WellFred agent guide

WellFred connects customers with services and providers in Costa Rica. Public agents can search, browse, inspect offerings, submit guest RFQs and prepare booking requests without an integration key.

Customer overview: [Using AI assistants on WellFred](https://app.wellfred.com/help-center/customers/booking/using-ai-assistants).

## Read the contract first

[Authentication and registration discovery](/auth.md) documents anonymous agent access, customer provisioning, and credential use for the email-verification handoff.

[OpenAPI JSON](https://app.wellfred.com/openapi.json) contains the request and response schemas generated from application code. [API reference](https://app.wellfred.com/docs/api) renders that same contract. Combine its server URL with each operation path. These public endpoints live under `/api/agent`. Send `Accept: application/json`; POST bodies use `Content-Type: application/json`.

[API catalog](https://app.wellfred.com/.well-known/api-catalog) (`/.well-known/api-catalog`, RFC 9727 `application/linkset+json`) links this API's contract, documentation, and status endpoint for automated discovery.

Use only the operations listed in this public contract. Discovery and RFQs are account-free; booking intents return a confirmation link for the customer.

## Find a service

### A2A discovery search

The [A2A Agent Card](/.well-known/agent-card.json) advertises the JSON-RPC
interface at `/api/agent/a2a`. Use its `supportedInterfaces` URL and protocol
version. Send `A2A-Version: 1.0` and `Content-Type: application/json` with a
JSON-RPC `SendMessage` request containing a unique `id` and
`params.message` (`messageId`, `role: "ROLE_USER"`, and `parts`). A text part
such as `{"text":"massage"}` supplies the search query; a data part carries
the structured search constraints below. Results return in
`result.message.parts`, with structured data and a text recommendation.

This interface supports synchronous discovery search. Catalog operations,
RFQs, and booking intents use the REST operations below. Streaming, push
notifications, and extended Agent Cards are not supported.

### REST discovery search

- `GET /categories`: category and subcategory IDs for browsing.
- `GET /locations?query=Uvita`: candidate places with coordinates. Resolve ambiguity with the customer. A town's center is suitable for search, not an exact booking address.
- `POST /search`: ranked service offerings, their providers, structured pricing and a short recommendation. Omit `query` to browse with category/location filters.
- `GET /providers` and `GET /providers/search`: browse providers or resolve provider names/IDs.
- `POST /providers/profile`: inspect a public provider by business UUID.
- `POST /availability`: inspect a service's available slots for an explicit date. Request-based services may require an RFQ or provider follow-up rather than expose slots.

Interpret natural language into explicit constraints before searching. WellFred's existing text/semantic search ranks the service need; it does not reliably extract dates, currencies or headcounts from prose. For “mobile massage in Uvita tomorrow for 2 people, under ₡40k each,” resolve the location and tomorrow's date in `America/Costa_Rica`, then send:

```json
{
  "query": "massage",
  "delivery": "in-home",
  "partySize": 2,
  "maxPrice": 40000,
  "currency": "CRC",
  "priceUnit": "person",
  "limit": 5
}
```

Add `lat`, `lng` from the selected location and `date` as `YYYY-MM-DD`. No sample date or guessed coordinates are defaults. Tell the customer the actual date you used.

Currency filters match the offering's currency; they do not convert prices. `priceUnit` distinguishes per-person from per-session prices. `price` is the same starting price the offering page shows, so quoting it will not contradict the page you link to; for an offering with options it is the cheapest option, and `maxPrice` filters against that same number. A starting price is not a final quote. Date checks do not establish party capacity, and nearby providers are not proof of coverage for an exact address. Explain unresolved requirements rather than claiming every constraint has been verified. A search examines a bounded set of ranked candidates; an empty filtered result is not proof that no provider exists.

Return a few relevant choices with their canonical URLs. Follow `nextAction`: services with options should be opened on their offering page for option selection; supported single-offering services can use booking intents. Never invent IDs, quotes or availability.

## Guest quote requests

An RFQ needs no customer account:

1. `POST /rfq/questions` with `requestedService` or `subcategoryUuid`. Use the returned question keys/options, taxonomy IDs and the user's answers. No question set means use free-text intake, not “service unavailable.”
2. Collect name, phone country code and phone number, work requested, location and timing. Email and budget are optional. A supplied budget requires an explicit currency.
3. Show the request summary and obtain permission to send it to WellFred for provider follow-up.
4. `POST /rfqs`. Preserve `requestedService`, resolved taxonomy, answers and any `preferredProviderUuid`. Supply an `idempotencyKey` and reuse the same key and payload on retries.
5. Give the customer the returned reference. An RFQ starts staff-assisted quote follow-up; it is not a confirmed appointment.

Example free-text request after the customer has provided these details and authorized submission:

```json
{
  "requestedService": "Repair a leaking kitchen pipe",
  "location": "Uvita",
  "timing": "The explicit date range agreed with the customer",
  "contactName": "Customer-provided name",
  "contactPhoneCountryCode": "+506",
  "contactPhoneNumber": "Customer-provided phone number",
  "idempotencyKey": "A unique identifier for this request"
}
```

Replace the descriptive placeholders with real user-provided values. RFQ success responses contain `success`, `message`, `rfqUuid`, `reference` and `created` at the top level. `created: false` means the submission was replayed.

## Booking with email verification

1. Choose an offering and a specific time. Obtain approval for the request and known price/currency.
2. `POST /booking-intents` with `serviceId`, `startIso` (ISO 8601 with timezone offset), optional `partySize`, and the customer's exact `origin` coordinates for in-home visits. An intent is an expiring request, not a booking or a held slot. Repeated preparation creates separate intents: reuse the returned confirmation link rather than prepare repeatedly.
3. Return `confirmUrl` to the customer. On WellFred, an existing verified customer can confirm directly. Otherwise the customer supplies their name/email and receives the existing secure email login link. A free account is created if needed, with disclosure on the form.
4. The email link returns to the saved request. Only the verified customer can submit it. WellFred rechecks time and price and uses the normal booking service. Repeated confirmation of the same intent returns the same booking result.
5. The confirmation page reports the outcome. A pending request still needs provider acceptance. No payment is collected by this API.

Do not ask for passwords or claim that supplying an email proves ownership. The public API never returns a login token or exposes customer booking history. The email and final confirmation steps take place on WellFred; no ChatGPT account connection is required for this handoff.

Existing booking rules still apply, including verified-phone requirements after the first booking. If an intent expires, prices change, or the offering needs option selection, return to the offering or prepare a fresh request and obtain approval for the new details. Do not silently substitute services or prices.

## Errors and retries

Every operation answers in the same envelope: `success` plus `message`, with the operation's own fields beside them at the top level. A failure sets `success: false` and names the reason in `error_code` — read that, not the prose in `message`, when branching.

Read JSON validation errors on 422 and correct only the invalid inputs. Respect 429 responses and `Retry-After`. Requests have bounded sizes and rate limits. A 503 with `error_code: CONCIERGE_DAILY_CAP_REACHED` means the discovery and quote-question operations are paused for the day; the rest of the API still answers, and the customer can continue on the website. Inspect `success` and the operation-specific outcome fields as well as the HTTP status before reporting success. Never retry a write with a different RFQ key or a fresh booking intent just because the original response was lost.
