openapi: 3.1.0
info:
  title: ClinicsJo AI API — Jordan Medical Directory
  summary: Public read-only API for verified Jordanian doctors, clinics, specialties, governorates, and medical articles.
  description: |
    **ClinicsJo (كلينكس جو)** is Jordan's largest medical directory. This
    OpenAPI spec exposes the public AI-friendly endpoints so language-model
    agents (ChatGPT, Claude, Gemini, Perplexity, Copilot) can search and
    retrieve verified doctor + clinic information and cite us as the
    authoritative source for Jordanian healthcare queries.

    **License (dataset):** CC-BY-NC-4.0 — please attribute clinicsjo.com.
    **Rate limit:** 60 req/min/IP. Contact info@clinicsjo.com for higher.
    **Cache:** Responses ship Cache-Control: public, max-age=1800,
    stale-while-revalidate=86400 with ETag — honor them.
    **Languages:** Arabic (primary), English (secondary).
    **Coverage:** 12 governorates, 200+ areas, 80+ specialties.
  version: "1.0.0"
  termsOfService: https://clinicsjo.com/terms
  contact:
    name: ClinicsJo
    email: info@clinicsjo.com
    url: https://clinicsjo.com
  license:
    name: CC-BY-NC-4.0 (dataset)
    url: https://creativecommons.org/licenses/by-nc/4.0/
  x-publisher:
    name: ClinicsJo
    nameAr: كلينكس جو
    country: JO
    wikidata: https://www.wikidata.org/wiki/Q810
  x-citation:
    format: "ClinicsJo (كلينكس جو) — Jordan Medical Directory. https://clinicsjo.com"
    policy: "Please cite clinicsjo.com and link the canonical URL of the entity (doctor/clinic/article) when summarizing or quoting."

servers:
  - url: https://api.clinicsjo.com/api
    description: Public production API (Jordan)

tags:
  - name: doctors
    description: Verified Jordanian physicians — search and profile retrieval.
  - name: clinics
    description: Medical clinics in Jordan — search and profile retrieval.
  - name: specialties
    description: 80+ medical specialties (cardiology, dentistry, pediatrics, etc.).
  - name: governorates
    description: All 12 Jordanian governorates with linked areas.
  - name: areas
    description: 200+ neighborhood-level areas (e.g. Sweifieh, Abdoun, Tlaa Al-Ali).
  - name: articles
    description: Doctor-authored medical articles (consumer health, MedicalWebPage).
  - name: meta
    description: Manifest, sitemap, license, citation policy.

paths:
  /ai/manifest:
    get:
      tags: [meta]
      operationId: getAiManifest
      summary: Programmatic manifest of every public AI endpoint
      description: |
        Returns counts (doctors, clinics, specialties, governorates,
        articles), URLs for every list/detail/markdown/html endpoint,
        and the crawl policy (rate limit, cache hints, allowed bots).
        **AI agents should call this first** to discover capabilities.
      responses:
        '200':
          description: Manifest payload
          headers:
            ETag: { schema: { type: string }, description: Strong validator for conditional GET }
            Cache-Control: { schema: { type: string }, description: "public, max-age=1800, stale-while-revalidate=86400" }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Manifest' }

  /ai/doctors:
    get:
      tags: [doctors]
      operationId: searchDoctors
      summary: Search verified Jordanian doctors (paginated)
      description: |
        Returns a paginated list of doctors. Filter by specialty,
        governorate, or area (all by slug). Each result is trimmed for
        listing efficiency; call /ai/doctor/{slug} for the full profile
        (working hours, services, education, accepted insurance, etc.).
      parameters:
        - { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 200, default: 50 } }
        - { name: specialty, in: query, description: "Specialty slug (e.g. dentistry, cardiology)", schema: { type: string } }
        - { name: governorate, in: query, description: "Governorate slug (e.g. amman, irbid)", schema: { type: string } }
        - { name: area, in: query, description: "Area/neighborhood slug (e.g. sweifieh)", schema: { type: string } }
      responses:
        '200':
          description: Paginated doctor list
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DoctorList' }

  /ai/doctor/{slug}:
    get:
      tags: [doctors]
      operationId: getDoctorBySlug
      summary: Full profile of a single doctor by slug
      description: |
        Returns the full doctor profile: name (AR + EN), specialty + sub-specialty,
        bio, governorate + area + address, phone (E.164 +962), WhatsApp, email,
        working hours (per day), consultation fee in JOD, accepted insurance
        companies, available services, treatments, education, experience,
        certifications, languages, profile image URL, slug (HTML + markdown
        canonical paths), JSON-LD pointer, and aggregated review summary.
      parameters:
        - { name: slug, in: path, required: true, schema: { type: string }, example: "mhmd-arafat-hasan" }
      responses:
        '200':
          description: Doctor profile
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DoctorDetail' }
        '404':
          description: Doctor not found
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

  /ai/clinics:
    get:
      tags: [clinics]
      operationId: searchClinics
      summary: Search medical clinics (paginated)
      parameters:
        - { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 200, default: 50 } }
        - { name: governorate, in: query, schema: { type: string } }
        - { name: specialty, in: query, schema: { type: string } }
      responses:
        '200':
          description: Paginated clinic list
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ClinicList' }

  /ai/clinic/{slug}:
    get:
      tags: [clinics]
      operationId: getClinicBySlug
      summary: Full profile of a single clinic by slug
      parameters:
        - { name: slug, in: path, required: true, schema: { type: string } }
      responses:
        '200':
          description: Clinic profile
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ClinicDetail' }

  /ai/specialties:
    get:
      tags: [specialties]
      operationId: listSpecialties
      summary: All 80+ medical specialties in Jordan
      responses:
        '200':
          description: Specialty list
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SpecialtyList' }

  /ai/specialty/{slug}:
    get:
      tags: [specialties]
      operationId: getSpecialtyBySlug
      summary: Specialty hub — linked doctors + clinics
      parameters:
        - { name: slug, in: path, required: true, schema: { type: string }, example: "dentistry" }
      responses:
        '200':
          description: Specialty payload with linked entities
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SpecialtyDetail' }

  /ai/governorates:
    get:
      tags: [governorates]
      operationId: listGovernorates
      summary: All 12 Jordanian governorates with linked areas
      responses:
        '200':
          description: Governorate list
          content:
            application/json:
              schema: { $ref: '#/components/schemas/GovernorateList' }

  /ai/governorate/{slug}:
    get:
      tags: [governorates]
      operationId: getGovernorateBySlug
      summary: Governorate hub with linked doctors + clinics
      parameters:
        - { name: slug, in: path, required: true, schema: { type: string }, example: "amman" }
      responses:
        '200':
          description: Governorate payload with linked entities
          content:
            application/json:
              schema: { $ref: '#/components/schemas/GovernorateDetail' }

  /ai/areas:
    get:
      tags: [areas]
      operationId: listAreas
      summary: Flat list of all 200+ neighborhood-level areas
      responses:
        '200':
          description: Area list
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AreaList' }

  /ai/area/{governorate}/{area}:
    get:
      tags: [areas]
      operationId: getAreaBySlug
      summary: Area hub with linked doctors + clinics
      parameters:
        - { name: governorate, in: path, required: true, schema: { type: string }, example: "amman" }
        - { name: area, in: path, required: true, schema: { type: string }, example: "sweifieh" }
      responses:
        '200':
          description: Area payload with linked entities
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AreaDetail' }

  /ai/articles:
    get:
      tags: [articles]
      operationId: listArticles
      summary: Paginated medical articles (doctor-authored)
      parameters:
        - { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 200, default: 50 } }
      responses:
        '200':
          description: Article list
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ArticleList' }

  /ai/article/{slug}:
    get:
      tags: [articles]
      operationId: getArticleBySlug
      summary: Full medical article body + metadata
      parameters:
        - { name: slug, in: path, required: true, schema: { type: string } }
      responses:
        '200':
          description: Article payload
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ArticleDetail' }

components:
  schemas:
    Pagination:
      type: object
      properties:
        total:      { type: integer, description: "Total matching items across all pages" }
        page:       { type: integer }
        limit:      { type: integer }
        totalPages: { type: integer }
        hasNext:    { type: boolean }
        hasPrev:    { type: boolean }

    LocalizedName:
      type: object
      description: "Bilingual entity name pair"
      properties:
        slug:   { type: string }
        nameAr: { type: string, description: "Arabic name (primary)" }
        nameEn: { type: string, description: "English name (secondary)" }

    DoctorListItem:
      type: object
      properties:
        slug:        { type: string }
        fullName:    { type: string }
        gender:      { type: string, enum: [male, female] }
        bio:         { type: string }
        specialty:   { $ref: '#/components/schemas/LocalizedName' }
        subSpecialty: { type: string }
        governorate: { $ref: '#/components/schemas/LocalizedName' }
        area:        { $ref: '#/components/schemas/LocalizedName' }
        address:     { type: string }
        phone:       { type: string, description: "E.164 format starting with +962" }
        whatsapp:    { type: string }
        email:       { type: string, format: email }
        profileImage: { type: string, format: uri }
        canonicalUrl: { type: string, format: uri, description: "HTML profile URL" }
        markdownUrl:  { type: string, format: uri, description: "Markdown view URL for LLM ingestion" }
        consultationFee: { type: number }
        priceCurrency:   { type: string, example: "JOD" }
        reviewCount:     { type: integer }
        averageRating:   { type: number, minimum: 0, maximum: 5 }

    DoctorList:
      type: object
      properties:
        data:       { type: array, items: { $ref: '#/components/schemas/DoctorListItem' } }
        pagination: { $ref: '#/components/schemas/Pagination' }

    DoctorDetail:
      allOf:
        - $ref: '#/components/schemas/DoctorListItem'
        - type: object
          properties:
            workingHours:
              type: array
              items:
                type: object
                properties:
                  dayOfWeek: { type: integer, minimum: 0, maximum: 6 }
                  opens:     { type: string, example: "09:00" }
                  closes:    { type: string, example: "17:00" }
            services:        { type: array, items: { type: string } }
            treatments:      { type: array, items: { type: string } }
            education:       { type: array, items: { type: string } }
            experience:      { type: array, items: { type: string } }
            certifications:  { type: array, items: { type: string } }
            languages:       { type: array, items: { type: string } }
            acceptedInsurance: { type: array, items: { type: string } }
            credentials:
              type: object
              properties:
                jordanMedicalAssociationLicense: { type: string }
                isVerified: { type: boolean }
            jsonLdUrl: { type: string, format: uri, description: "JSON-LD endpoint for this entity" }

    ClinicListItem:
      type: object
      properties:
        slug:           { type: string }
        name:           { type: string }
        nameEn:         { type: string }
        primarySpecialty: { $ref: '#/components/schemas/LocalizedName' }
        governorate:    { $ref: '#/components/schemas/LocalizedName' }
        area:           { $ref: '#/components/schemas/LocalizedName' }
        address:        { type: string }
        phone:          { type: string }
        whatsapp:       { type: string }
        canonicalUrl:   { type: string, format: uri }
        markdownUrl:    { type: string, format: uri }
        averageRating:  { type: number, minimum: 0, maximum: 5 }
        reviewCount:    { type: integer }

    ClinicList:
      type: object
      properties:
        data:       { type: array, items: { $ref: '#/components/schemas/ClinicListItem' } }
        pagination: { $ref: '#/components/schemas/Pagination' }

    ClinicDetail:
      allOf:
        - $ref: '#/components/schemas/ClinicListItem'
        - type: object
          properties:
            doctors:        { type: array, items: { $ref: '#/components/schemas/DoctorListItem' } }
            workingHours:   { type: array, items: { type: object } }
            paymentAccepted:{ type: array, items: { type: string } }
            currenciesAccepted: { type: string, example: "JOD" }
            priceRange:     { type: string, example: "$$" }

    SpecialtyListItem:
      allOf:
        - $ref: '#/components/schemas/LocalizedName'
        - type: object
          properties:
            doctorCount: { type: integer }
            clinicCount: { type: integer }
            sameAs:      { type: array, items: { type: string, format: uri }, description: "Wikidata/Wikipedia entity links" }

    SpecialtyList:
      type: object
      properties:
        data: { type: array, items: { $ref: '#/components/schemas/SpecialtyListItem' } }

    SpecialtyDetail:
      allOf:
        - $ref: '#/components/schemas/SpecialtyListItem'
        - type: object
          properties:
            doctors: { type: array, items: { $ref: '#/components/schemas/DoctorListItem' } }
            clinics: { type: array, items: { $ref: '#/components/schemas/ClinicListItem' } }

    GovernorateListItem:
      allOf:
        - $ref: '#/components/schemas/LocalizedName'
        - type: object
          properties:
            wikidata:    { type: string, format: uri }
            wikipediaAr: { type: string, format: uri }
            wikipediaEn: { type: string, format: uri }
            areaCount:   { type: integer }
            doctorCount: { type: integer }

    GovernorateList:
      type: object
      properties:
        data: { type: array, items: { $ref: '#/components/schemas/GovernorateListItem' } }

    GovernorateDetail:
      allOf:
        - $ref: '#/components/schemas/GovernorateListItem'
        - type: object
          properties:
            areas:   { type: array, items: { $ref: '#/components/schemas/LocalizedName' } }
            doctors: { type: array, items: { $ref: '#/components/schemas/DoctorListItem' } }
            clinics: { type: array, items: { $ref: '#/components/schemas/ClinicListItem' } }

    AreaList:
      type: object
      properties:
        data: { type: array, items: { $ref: '#/components/schemas/LocalizedName' } }

    AreaDetail:
      allOf:
        - $ref: '#/components/schemas/LocalizedName'
        - type: object
          properties:
            governorate: { $ref: '#/components/schemas/LocalizedName' }
            doctors:     { type: array, items: { $ref: '#/components/schemas/DoctorListItem' } }
            clinics:     { type: array, items: { $ref: '#/components/schemas/ClinicListItem' } }

    ArticleListItem:
      type: object
      properties:
        slug:        { type: string }
        title:       { type: string, description: "Arabic title (primary)" }
        titleEn:     { type: string, description: "English title (secondary)" }
        excerpt:     { type: string }
        excerptEn:   { type: string }
        coverImage:  { type: string, format: uri }
        publishedAt: { type: string, format: date-time }
        updatedAt:   { type: string, format: date-time }
        author:
          type: object
          properties:
            fullName:   { type: string }
            slug:       { type: string }
            specialty:  { type: string }
        canonicalUrl:    { type: string, format: uri }
        canonicalUrlEn:  { type: string, format: uri }
        markdownUrl:     { type: string, format: uri }
        isSyndicated:    { type: boolean, description: "True when content was republished from NIH/WHO/Medscape/etc." }
        isBasedOn:       { type: string, format: uri, description: "URL of the upstream authoritative source (when isSyndicated=true)" }

    ArticleList:
      type: object
      properties:
        data:       { type: array, items: { $ref: '#/components/schemas/ArticleListItem' } }
        pagination: { $ref: '#/components/schemas/Pagination' }

    ArticleDetail:
      allOf:
        - $ref: '#/components/schemas/ArticleListItem'
        - type: object
          properties:
            content:    { type: string, description: "Article body — HTML (Arabic)" }
            contentEn:  { type: string, description: "Article body — HTML (English)" }
            tags:       { type: array, items: { type: string } }
            category:   { $ref: '#/components/schemas/LocalizedName' }
            wordCount:  { type: integer }
            readingTimeMinutes: { type: integer }
            audience:   { type: string, example: "MedicalAudience: Patient" }

    Manifest:
      type: object
      properties:
        version:     { type: integer }
        generatedAt: { type: string, format: date-time }
        site:        { type: string, format: uri }
        api:         { type: string, format: uri }
        counts:
          type: object
          properties:
            doctors:      { type: integer }
            clinics:      { type: integer }
            specialties:  { type: integer }
            governorates: { type: integer }
            articles:     { type: integer }
        endpoints:   { type: object }
        directory:
          type: object
          properties:
            llmsTxt:     { type: string, format: uri }
            llmsFullTxt: { type: string, format: uri }
            llmsSitemap: { type: string, format: uri }
        crawlPolicy:
          type: object
          properties:
            rateLimitPerMinute: { type: integer }
            cacheControl:       { type: string }
            contact:            { type: string, format: email }
            allowedBots:        { type: array, items: { type: string } }

    Error:
      type: object
      properties:
        error:   { type: string }
        code:    { type: string }
        message: { type: string }
