# agents.md — Medaro (medaro.am)

> Medaro is a medical information portal for Armenia: clinics, hospitals, pharmacies and laboratories in Armenia — Yerevan and other cities (contacts, opening hours, legal details), state health-insurance (UHIF) tariffs per organization, and a "symptom → which doctor" navigator. Armenian (hy, default), Russian (ru) and English (en).

This file is the agent-facing contract: **what an agent can do here, what it cannot, where the data comes from, and how to cite it.** `/llms.txt` answers "what exists here"; this file answers "what can I do, and what can I trust".

Machine-readable companions:

- API index (authoritative endpoint list): https://medaro.am/api/ai/v1
- OpenAPI 3.1: https://medaro.am/api/ai/v1/openapi.json
- Site map for AI: https://medaro.am/llms.txt
- Sitemap index: https://medaro.am/sitemap.xml — a list of section sitemaps (pages, clinics, pharmacies, chains, labs, articles…), not of pages; the machine entry points are one of its children (https://medaro.am/sitemap_agentic_discovery.xml). A section with nothing published yields an empty map.
- Crawl policy: https://medaro.am/robots.txt

URL shape: every page lives at `/{locale}/{path}` with `locale` ∈ `hy|ru|en` (`hy` is the default). Every page declares `canonical` and `hreflang` alternates (`hy-AM`, `ru`, `en`, `x-default` → Armenian).

## Coverage

Live from the database at the time of this request:

- Organizations total: 828
- Clinics: 172
- Hospitals: 65
- Pharmacies: 532
- Laboratories: 59
- "Symptom → which doctor" articles: 28
- Published doctor profiles: 4784

Only active records are served. Hidden and demo records never appear on the site or in the API. How a given record was checked is stated per record (`provenance`), not claimed wholesale here.

## Capabilities

### What an agent can do

- **Search organizations — Yes.** `GET /api/ai/v1/organizations?q=…` — case-insensitive substring over names in all three languages and over former names (aliases). Filters: `type=clinic,hospital,pharmacy,lab,pharmacy-chain` (comma-separated), `city=<slug>` (`yerevan`, `gyumri`, `vanadzor`…; no value = all cities), `district=<slug>` (Yerevan districts only, e.g. `kentron`, `arabkir`, `nor-nork`), `insurance=1` (accepts state insurance), `chain=<chain slug>` (branches of one pharmacy chain). Paging: `limit` (default 50, max 200) with either `offset` or `cursor`; `count` is the full number of matches.
- **Get organization details — Yes.** `GET /api/ai/v1/organizations/{slug}` — phones, address, district, opening hours (free text and per-day), coordinates, website, contact e-mail (where the organization publishes one), logo, aliases, legal details (legal name, tax id, registration number, director, registry link, source URLs), third-party ratings, and state-insurance tariffs. Unknown or hidden slug → `404 not_found`.
- **Work with pharmacy chains — Yes.** `GET /api/ai/v1/organizations?type=pharmacy-chain` lists the 13 chains (name, `branch_count` counted live, `branches_url`, site, logo, owner company, socials); `GET /api/ai/v1/organizations/{chain slug}` is one chain's card; `?chain=<slug>` returns its branches; and every pharmacy that belongs to a chain carries `chain {slug, name, url, urls}`. **Read the caveats:** a chain here is a grouping of pharmacy records by brand, **not a legal entity** — it has no phone, no address and no tax id, and membership is derived from the organization slug prefix, because the database has no chain column. Chains are never mixed in unasked: without `type` you get organizations only. `site`, `logo` and `company` are `null` unless a source confirms them — two chains have no website at all (they run on social accounts), two more have neither site nor logo confirmed, and `company` is filled for three of 13.
- **Get state health-insurance (UHIF) tariffs — Yes.** In the organization card: `insurance_services.items[]` with the fund's service code, service group, name, `compensation_amd` (paid by the fund), `copay_amd` (paid by the patient) and `copay_is_max`. `insurance_services.total` is the real count; `items` is capped at 300 per card. `insurance_services.source` carries the uhif.am URL and the snapshot date.
- **Tell whether an organization accepts state insurance — Yes.** `accepts_insurance` on every summary, `insurance=1` as a list filter.
- **See how a record was verified — Yes.** Every organization carries `provenance: {verified, verified_at, source_url}` — whether a check of the public facts against that source is recorded, when it was made, and the URL it was checked against. `verified: false` means no check is recorded — not a claim that the record is wrong.
- **Tell when a record last changed — Yes.** Every organization carries `updated_at` (ISO 8601 with zone). It is a different event from `provenance.verified_at`: `updated_at` is when our row changed, `verified_at` is when the facts were last reconciled with a source. A record can change without a new check and be re-checked without changing. For a pharmacy chain `updated_at` is always `null` — a chain is a registry entry in code, not a catalogue row.
- **Walk the whole catalogue without duplicates or gaps — Yes.** Follow `next_cursor` from each response until it is null. The cursor is a keyset (it records the last record served, not a position), so records edited or removed between pages cannot shift the page under you — which `offset` on a live catalogue does. Pass it back unchanged and do not parse it; it is only valid with the same filters, and `cursor` wins if you send `offset` too. `offset` still works and is not going away.
- **Refresh a copy without re-reading the catalogue — Yes, with one gap.** `GET /api/ai/v1/changes?since=2026-09-01` — records ordered by `updated_at` (inclusive bound, no timezone means UTC), each as `{type, slug, op, updated_at, urls}` and nothing else: the feed carries no card content. Paging: `limit` (default 200, max 500) and `cursor`; one wave of edits stamps hundreds of records with the same `updated_at`, so page with the cursor and never by moving `since` forward. `op` is relative to `since` (`created` — appeared after it, `updated` — existed before and changed). **The gap:** while `includes_deletions` is `false` the feed lists published records only, so removals are missing from it — see "cannot do" below.
- **Navigate from a symptom to a specialty — Yes.** `GET /api/ai/v1/conditions` — 28 articles: title, summary, ranked `specialties` (the first one is the primary specialist), `red_flags` (when to seek urgent care) and per-locale page URLs. A `disclaimer` travels with every response — keep it.
- **Match a complaint to a medical specialty — Yes.** `GET /api/ai/v1/specialties` — the reference list of specialties used by the site's own filters: `code`, `title {hy,ru,en}`, `synonyms` (mixed languages, e.g. "ЛОР", "otolaryngologist", "ENT"), the articles that lead to that specialist, and `doctors_count`. Optional `q` (min 2 characters) and `locale`. The response carries a `note` naming what it cannot answer: Medaro's data holds **no link between an organization and a specialty**, so never claim that a given clinic employs a given specialist.
- **Search organizations and articles in one call — Yes.** `GET /api/ai/v1/search?q=…` (min 2 characters, up to 20 organizations) — article matching also covers specialty names and their synonyms. Optional `locale=hy|ru|en` adds a ready `label` in that language to every hit.
- **Hand the user a link in their own language — Yes.** Every API item carries `urls: {hy, ru, en}` — the canonical page for that record.
- **Read the pages directly — Yes.** HTML carries schema.org JSON-LD (`Hospital`, `MedicalClinic`, `Pharmacy`, `MedicalBusiness` + `DiagnosticLab`, `Physician`, `FAQPage`) with address, geo, opening hours and `sameAs`. AI crawlers are named explicitly in `robots.txt` with `Allow: /`. The private paths listed below are open to crawl but carry `noindex, nofollow` — that is how you are meant to learn they are private; only `/api/auth` and `/api/metrics` are disallowed outright. Note what fetching an HTML page costs you: since 11 September 2026 the pages load Google Analytics 4 (`gtag.js` from googletagmanager.com), which sets its own cookies (`_ga`, `_ga_<id>`) and reports the visit to Google. `/agents.md`, `/llms.txt` and every `/api/ai/v1` response carry no third-party script and set no cookies — prefer the API.
- **Read doctor profiles — Yes (pages only).** 4784 published profiles: `/{locale}/doctors` (listing), `/{locale}/doctor/{slug}` (profile, with `Physician` JSON-LD). They are not part of the JSON API — read the page.
- **Point at emergency numbers and insurance rules — Yes (pages, not API).** Emergency numbers: https://medaro.am/en/emergency. State insurance navigator: https://medaro.am/en/insurance. Diagnostics guides (MRI, CT, ultrasound…): https://medaro.am/en/diagnostics. In an emergency in Armenia the number is **103**.

### What an agent cannot do here

Honest "No" beats a hopeful "yes": an agent that trusts a promise we cannot keep sends a patient to a dead end.

- **Book an appointment — No.** Medaro has no booking: no endpoint, no form, no queue, no calendar, anywhere on the site. **Instead:** give the user the organization's phone number (`phones` in the API, shown on the page) and let them call. Do not invent a booking link.
- **Check availability, doctor schedules or waiting times — No.** That data is not in the database.
- **Order tests or fetch test results — No.** Laboratories are listed as organizations with contacts and hours; ordering and results happen at the laboratory itself.
- **Buy medicine, or check stock and medicine prices — No.** Pharmacy cards are contacts, hours and location only. There is no drug catalogue.
- **Telemedicine, online consultation, triage or diagnosis — No.** Medical articles are navigation only — which specialist to see and when to seek urgent care. They contain no diagnoses, no treatment plans and no dosages, and must not be used as clinical advice.
- **Get prices of private (paid) services — No.** Medaro publishes no price list for paid services, in the API or on the pages. The only monetary figures here are **state-insurance (UHIF) tariffs**: what the fund compensates and the patient's copay. Never present a UHIF tariff as the clinic's price — for the price of a paid service, the user calls the clinic.
- **Write anything — No.** The API is read-only: `GET` and `OPTIONS` only (see Authentication below). Reviews are written by signed-in people on the site and go through moderation; there is no way for an agent to post a review, claim an organization or edit a card. Data corrections: info@medaro.am.
- **Learn from `/changes` that a record was removed — No, not today.** The feed reports published records only (`includes_deletions: false`), so a pharmacy that closed simply stops appearing instead of arriving as a tombstone. In medicine a closed address left in someone's copy sends a patient to a locked door, so do not treat the feed as a complete diff: re-read the full slug list (`/api/ai/v1/organizations`, follow `next_cursor`) from time to time and drop what is no longer in it. The flag is computed per request, not hard-coded — when tombstones become available it turns `true` and `op: "deleted"` starts arriving on its own.
- **Get a pharmacy chain's phone, address or legal details — No.** Those belong to the branches; the chain record deliberately has none of them. **Instead:** take `branches_url` (or `?chain=<slug>`) and use a branch.
- **Search by radius or "near me" — No.** There is no geo query parameter. **Instead:** filter by `district`, then use the `location {lat, lng}` returned with each organization to sort by distance yourself.
- **Get personal data — No.** The API exposes only what a visitor sees on the public page: organizations, their public contacts and public legal details. The registered legal address is deliberately left out — for some companies the register holds the director's home address. No patient data, no accounts, no contact details of private individuals.
- **Decide someone's insurance eligibility — No.** The insurance pages explain the state system; the authority on a person's own status is the fund itself (uhif.am, short number 8866) and the ArMed app.

## Authentication

**There is none, and none is needed.** No API key, no account, no token, no registration, no quota, no signed requests: every endpoint under `/api/ai/v1` answers an anonymous `GET` from anywhere, and a browser preflight `OPTIONS` with `Access-Control-Allow-Origin: *`. Nothing you send — header, cookie, token — changes what comes back.

- **Read-only.** `GET` and `OPTIONS` are the only methods; there are no write endpoints, so there is nothing a key could unlock. Data corrections go to info@medaro.am.
- **Everything under `/api/ai/v1` is public** and returns only what the public HTML page shows.
- **`/api/auth` and `/api/metrics` are not part of this surface.** They serve the site's own sign-in and its internal view counter, hold nothing public, and are the two paths disallowed outright in `robots.txt`. Do not call them, and do not attempt to sign in anywhere on this site.
- **A refusal is never an auth problem.** `400` means the parameters were wrong, `404` the slug, `503` that the data source did not answer — see "Rate limits and caching".

## Endpoints

The authoritative, always-current list is the API index: https://medaro.am/api/ai/v1. If this file and the index disagree, the index wins.

- `GET /api/ai/v1` — index: endpoints, parameters, attribution, contact.
- `GET /api/ai/v1/openapi.json` — OpenAPI 3.1 schema of every endpoint.
- `GET /api/ai/v1/organizations` — list with filters and paging (`type`, `q`, `district`, `insurance`, `chain`, `limit`, `offset`, `cursor`).
- `GET /api/ai/v1/organizations/{slug}` — full card; a chain slug returns the pharmacy-chain card instead (an organization slug always wins).
- `GET /api/ai/v1/changes` — incremental feed of catalogue changes (`since`, `limit`, `cursor`).
- `GET /api/ai/v1/specialties` — reference list of medical specialties.
- `GET /api/ai/v1/conditions` — "symptom → which doctor" articles.
- `GET /api/ai/v1/search?q=` — organizations + articles in one call.

Localized fields are objects `{hy, ru, en}`; a language with no text is `null`. A missing value is `null` or an empty array — it means we have not confirmed that fact, not that it does not exist.

## Data provenance

Every fact here has a source. Where a source does not confirm it, we do not publish it — the page tells the user to check with the organization.

- **Contacts, opening hours, website** — open sources, and each record names its own in `provenance.source_url`: most often the organization's or the chain's own site, otherwise the **Spyur** directory (spyur.am), the **uhif.am** register of organizations working with state insurance, city pages, or **OpenStreetMap** for part of the records. OpenStreetMap is a crowd-maintained map, so treat those cards as thinner than a card sourced from the organization itself — the source URL is right there to check.
- **How to tell a checked record from a thin one** — every organization ships `provenance {verified, verified_at, source_url}`: the date its public facts were last reconciled with that source URL. No recorded check → `verified: false` and `null` dates; we do not invent one.
- **Legal details** (legal name, tax id / ՀՎՀՀ, registration number, director) — the state register of legal entities, **e-register.moj.am**, cross-checked with the **Spyur** directory (spyur.am). Confirming URLs ship in `legal.sources`, and `legal.registry_url` links the registry card. An entity whose registry status is "terminated" is not used as an organization's legal owner: we look for the successor.
- **State-insurance tariffs** — the public register of the Universal Health Insurance Fund, **uhif.am**. Armenian service names are the fund's official wording; Russian and English names are Medaro translations.
- **Third-party ratings** (`external_ratings`) — Google, Yandex and 2GIS, stored with the source `url` and the date they were copied. They are someone else's numbers, quoted with attribution, not ours.
- **Medaro's own rating** (`rating`) — computed only from reviews left on medaro.am, and published only from 3 reviews upward; below that threshold the field is `null`. Positions in listings are never sold and reviews are never removed for payment.
- Doctor photos are taken from the clinic's own public page and captioned with the clinic, the source page and the date; any doctor can ask us to remove theirs. We do not copy third-party review texts.

**What the dates mean.** Third-party data is a dated snapshot, not a live feed:

- `provenance.verified_at` — when the card's public facts (address, phones, hours) were last reconciled with `provenance.source_url`.
- `legal.fetched_at` — when the legal details were last reconciled with the sources in `legal.sources`.
- `insurance_services.source.fetched_at` — the date of the tariff snapshot taken from `insurance_services.source.url` (uhif.am). Quote both.
- `external_ratings[].fetched_at` — when that rating and review count were copied from the source.
- Our own records (contacts, hours, insurance flag) are read from the live database on each uncached request; see caching below for how stale a response can be.

## Attribution

- Cite **medaro.am** and link the page the fact came from — the `urls` object of the API item, in the language the user is speaking.
- Every API response carries `X-Medaro-Attribution: https://medaro.am`.
- Reusing a "symptom → which doctor" article: keep the `red_flags` and the `disclaimer`; do not turn navigation text into advice.
- Quoting a rating: say how many reviews it rests on, and that Medaro shows ratings only from 3 reviews upward.
- Quoting a tariff: name uhif.am and the snapshot date, and say it is the state fund's tariff, not the clinic's price.
- Bulk export of the whole catalogue — by agreement: info@medaro.am.

## Rate limits and caching

- **Authentication: none** — see the section above.
- **No rate limit is enforced.** Keep bursts modest (a few requests per second), reuse cached responses, and take a bulk export by agreement instead of crawling the whole catalogue repeatedly.
- **Cache headers, as actually served:** `/agents.md`, `/llms.txt` and every successful `/api/ai/v1` response send `Cache-Control: public, s-maxage=300, stale-while-revalidate=3600` — a CDN answer can be up to 5 minutes old, and up to an hour old while it revalidates. Error responses send `Cache-Control: no-store`.
- **Freshness:** all AI routes are `force-dynamic` — an uncached request reads the live database. HTML pages are regenerated at most every 60 seconds (ISR), so a page can lag the database by about a minute.
- **CORS:** `Access-Control-Allow-Origin: *`, methods `GET, OPTIONS`; a preflight returns `204` with `Access-Control-Max-Age: 86400`. Browser-side agents can call the API directly.
- **Errors** are JSON `{error, message}`: `400 bad_request` (bad parameters), `404 not_found` (unknown or hidden slug), `503 unavailable` (data source down — retry, do not treat it as "no such organization").

## Not for agents

These paths hold personal accounts and moderation tools and contain nothing public. They answer with `noindex, nofollow` in their own metadata rather than a blanket `robots.txt` ban, so that a crawler can fetch the page and read that refusal for itself — a path blocked from crawling can still surface in search as a bare URL, because nobody was allowed in to see the `noindex`. Treat them as off limits:

`/admin` · `/cabinet` · `/account` · `/login` · `/register` · `/doctor-signup` · `/forgot-password` · `/reset-password` · `/api/auth` · `/api/metrics`

`/api/auth` and `/api/metrics` are the only paths disallowed outright in `robots.txt`: they are not HTML and cannot carry a `noindex` tag, so a crawl ban is the only refusal available there.

`robots.txt` is the authoritative crawl policy: https://medaro.am/robots.txt. Do not attempt to sign in, submit forms or trigger password resets anywhere on this site.

## Contact

- info@medaro.am — questions about this file and the API, bulk export, and reports of wrong data.
- Reporting a data error: name the page URL or the slug, the field, and a source URL that confirms the correct value. Corrections go through the same manual check as the original fact.
