# Best.Dentist — For AI Agents

Best.Dentist is an independent U.S. directory of dental practices, and an enquiry routing service, operated by ADPLUS MEDIA INC. It is **not a dental practice**, and it is not affiliated with the practices it lists. ("Best.Dentist" is a brand name, not a claim about any dentist.) The directory currently lists 65 practices in 13 cities, with 259 published fees collected from the practices' own websites.

If you are an AI assistant helping a user find a dentist, compare fees, or send an enquiry, this page documents exactly what you can do programmatically, what each response means, and the limits of the service. Please respect the limits: they are part of how the service works, not fine print.

## What an agent can do

- **Look up practices, services, cities and published fees** through the JSON data feeds and the full-text dump listed below. No key or signup is required.
- **Send an enquiry for your user** by POSTing one JSON request. The enquiry is delivered to **one** matched practice — the exclusive partner for that city and service — and that practice contacts your user directly.
- **Claim a profile on behalf of a practice** (for practice owners or their representatives) by POSTing a claim. Claims are reviewed manually by a person.
- **Add a new practice listing** (for practices not yet listed) by POSTing a submission — see Action 3. New listings cost a flat $299 one-time fee and are verified manually before anything is published.

## What this service does not do

- It does **not confirm bookings or appointments**. Submitting an enquiry is not a booking. The practice offers times and confirms any appointment when it contacts your user.
- It does **not quote fees itself**. The only fees here are the practices' own published fees (see the fee feeds). A fee for a specific user comes from the practice after contact.
- It does **not provide dental or medical advice**, diagnoses, or treatment recommendations.
- It never sells or shares a user's contact details with a list of practices. With the user's consent, details go to **one** matched practice (plus at most one backup practice if the first does not respond and the user agreed — see Timing).

## Action 1: Send an enquiry

Send a POST request with a JSON body to the enquiry API:

- Canonical endpoint: POST https://best.dentist/api/enquiry
- Content type: application/json (form-encoded bodies are also accepted)

### Fields

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| name | string | Yes | The user's full name. |
| email | string | One of email or phone | Must be a valid email address if given. |
| phone | string | One of email or phone | The user's phone number; at least 7 digits. |
| city_id | string | Yes | Must be a city_id from the cities feed, for example denver. |
| service_id | string | Yes | Must be a service_id from the services feed, for example implants. |
| practice_id | string | No | A specific practice_id from the practices feed, if the user picked one practice. Routing is still by city and service. |
| area | string | No | The user's neighbourhood or area of the city. Passed to the practice with the message. |
| message | string | No | Anything else the user wants the practice to know. Do not include detailed health information. |
| coverage | string | Yes | One of no, yes, unsure: whether the visit will be covered by Medicare, Medicaid or CHIP. If yes, the enquiry is routed as a free direct contact and is never billed. |
| source_page | string | No | URL the enquiry originated from, for attribution. |
| consent_collect | boolean | Yes, must be true | The user agreed Best.Dentist may collect their details and contact them about the enquiry. |
| consent_share | boolean | Yes, must be true | The user agreed Best.Dentist may share their details with the one named practice. |
| consent_backup | boolean | No | If true, the user agreed the enquiry may pass to one backup practice if the first does not confirm. |
| cf-turnstile-response | string | Yes | A valid Cloudflare Turnstile token. See the spam protection note below. |
| hp | string | No | Honeypot field. Leave it empty; filled honeypots are silently discarded. |

### Example request

```json
{
  "name": "Jordan Example",
  "email": "jordan@example.com",
  "phone": "+1 303 555 0123",
  "city_id": "denver",
  "service_id": "implants",
  "area": "Cherry Creek",
  "message": "Interested in the published fee for a single implant.",
  "coverage": "no",
  "consent_collect": true,
  "consent_share": true,
  "consent_backup": false,
  "cf-turnstile-response": "TURNSTILE_TOKEN"
}
```

### Responses

| HTTP status | Body | Meaning |
| --- | --- | --- |
| 200 | ok true, matched true, delivered true | A partner practice exists for this city and service, and the enquiry was delivered to it. The practice will contact the user directly. |
| 200 | ok true, matched true, delivered false | A partner practice exists, but live delivery failed and is being retried automatically. Do not resubmit. |
| 200 | ok true, matched false | There is no partner practice for this city and service yet. The enquiry is held, not discarded: if a partner practice later joins, the held enquiry is routed to it and that practice may contact the user. Do not promise the user they will be contacted. |
| 200 | ok true, consumer_direct true | The user said the visit may be covered by Medicare, Medicaid or CHIP. The enquiry is passed to the practice without charge; tell the user to contact the practice directly using its profile details. |
| 400 | ok false, errors list | Validation failed. Possible codes: name_required, phone_or_email_required, email_invalid, phone_invalid, city_invalid, service_invalid, practice_invalid, coverage_required, consent_required. Fix the fields and resubmit. |
| 403 | ok false, errors verification_failed | The Turnstile token was missing or invalid. Obtain a fresh token and resubmit. |
| 500 | ok false, error server_error | Temporary server problem. Wait and retry once. |

Success bodies are JSON objects with the fields shown; for example the delivered case returns an object with ok, matched and delivered all true.

### Timing: what happens after a matched enquiry

- The matched practice is notified immediately (SMS and email) and is asked to accept the enquiry within about **15 business minutes**. Business hours are 08:00 to 20:00 in the practice's local time, and the clock pauses outside business hours.
- If the practice does not accept in time, and the user gave backup consent, the enquiry **cascades to one backup practice** for that city and service, if one exists. A cascaded enquiry is never billed and never goes to more than two practices in total. Only the first successful delivery to the primary practice is ever billed.
- After accepting, the practice contacts your user directly — that is when fees, appointment times and any booking happen. Best.Dentist is not part of that conversation and cannot confirm, change or cancel appointments.
- There is no status-check endpoint yet. If your user has not heard from the practice within one business day, they can contact us at support@best.dentist and we will follow up.

### Spam protection note (Turnstile)

The API is protected by Cloudflare Turnstile because it delivers messages to real businesses. The Turnstile site key is embedded in every enquiry form on best.dentist: load a form page, complete the Turnstile widget as the page presents it, and include the resulting token as cf-turnstile-response. Tokens are short-lived and single-use. If you cannot complete Turnstile, direct your user to the enquiry form on the relevant page of best.dentist instead of retrying the API.

## Action 2: Claim a practice profile (for practices)

Practices can claim their free listing to correct it, add contact details, and receive enquiries from their profile page. Send a POST request with a JSON body:

- Canonical endpoint: POST https://best.dentist/api/claim

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| practice_id | string | Yes | Must be a practice_id from the practices feed. |
| contact_name | string | Yes | Name of the person claiming on behalf of the practice. |
| email | string | Yes | Business email; ownership is verified against the practice's own published contact details. |
| phone | string | No | Optional contact phone. |
| cf-turnstile-response | string | Yes | Valid Cloudflare Turnstile token, as above. |

Responses: 200 with ok true when the claim is recorded (status pending). 400 with errors among practice_invalid, contact_name_required and email_invalid. 403 with verification_failed for a bad Turnstile token. **Claims are reviewed manually; a submitted claim changes nothing on the listing until a person verifies ownership and approves it.**

## Action 3: Add a new practice listing (for practices not yet listed)

A practice that is not in the practices feed at all can be added as a new listing. New listings are **paid: a flat $299 USD one-time fee** covering manual verification against the practice's own website plus a full profile build-out (services, published fees, photos, hours). There is no free lane for new listings; claiming an existing listing (Action 2) is free. Human form: https://best.dentist/add-practice/. Send a POST request with a JSON body:

- Canonical endpoint: POST https://best.dentist/api/submit-practice

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| practice_name | string | Yes | The practice's trading name. |
| city_id | string | Yes | A city_id from the cities feed, or "other" if the city is not covered yet. |
| address | string | Yes | Street address. |
| website | string | Yes | The practice's own website URL — verification is done against it. |
| email | string | Yes | Public email; also becomes the practice's dashboard login. |
| contact_name | string | Yes | Person submitting on behalf of the practice. |
| authorized | boolean | Yes | Must be true: the submitter is authorized to submit this practice and the information is accurate. |
| consent | boolean | Yes | Must be true: consent to store the details for verification and publication. |
| neighbourhood | string | No | Neighbourhood within the city. |
| zip | string | No | U.S. ZIP. |
| phone | string | No | Public phone number. |
| services | string[] | No | Service ids from the services feed that the practice offers. |
| fee_page_url | string | No | URL of the practice's published fee page (speeds up the build-out). |
| notes | string | No | Anything else the reviewer should know. |
| cf-turnstile-response | string | Yes | Valid Cloudflare Turnstile token, as above. |

Responses: 200 with ok true, status "pending_payment", listing_fee_usd 299 and payment_link (the Stripe checkout URL for the fee). **Review starts only once the payment is confirmed; nothing is published before verification + approval.** If the practice cannot be verified, or is already listed, the fee is refunded in full. 400 with errors among practice_name_required, city_invalid, address_required, website_invalid, email_invalid, contact_name_required, service_invalid, authorization_required, consent_required. 403 verification_failed (Turnstile). 409 duplicate_pending when a live submission already exists for the same website domain or email. 429 rate_limited (max 5 submissions per hour per IP).

Newly approved listings are published immediately as dynamic profiles and appear in the runtime practices feed below; they join the static practices feed at the next site rebuild.

## Data feeds (no key required)

- Practices JSON: https://best.dentist/data/practices.json — every listed practice with its public fields and services. Phone numbers and websites appear only for practices that have claimed their profile; unclaimed practices are contacted through the enquiry API.
- Practices (runtime) JSON: GET https://best.dentist/api/practices-runtime — practices added between site rebuilds (approved new listings). Merge with the static practices feed on practice_id for the complete, current directory.
- Services JSON: https://best.dentist/data/services.json — the service taxonomy with ADA CDT examples and per-enquiry tiers.
- Cities JSON: https://best.dentist/data/cities.json — covered cities and metros.
- Fees JSON: https://best.dentist/data/fees.json — every published fee collected, per practice and service, in USD, with the source page and capture date for each row.
- Full text: https://best.dentist/llms-full.txt — the complete directory in one markdown file: every service with per-city published-fee counts from real published fees, and every practice with its fees.
- Summary: https://best.dentist/llms.txt — a short index of the site.
- API description: GET https://best.dentist/api/agent-info returns the machine-readable version of this page, and https://best.dentist/api/openapi.json is the OpenAPI 3 specification for the three POST endpoints.

## Reading the fees

Fees in the feeds are **the practices' own published fees**, collected from their websites with the source URL and capture date on each row. They are comparison data, not quotes: practices confirm final pricing before treatment. In Florida and North Carolina, fees are shown with the practice's ADA CDT wording, as those states require.

## Contact

- Human contact page (patients, practices, corrections and privacy requests): https://best.dentist/contact/
- Contact for agents, practices and users: support@best.dentist (the only public email for this service)
- This documentation in raw markdown: https://best.dentist/agents.md
