openapi: 3.1.0
info:
  title: DoesItARM API
  version: 1.0.0
  summary: Read-only Apple Silicon compatibility verdicts for Mac apps and games.
  description: |
    The read-only compatibility-verdict API for Mac apps and games on Apple Silicon.

    **Preview — documented before it ships.** This is the API's spec, written as if
    it works (like Stripe's docs). Endpoints return the shapes documented here. If
    you are an early integrator and a response differs from this page, that is drift
    — report it. Get notified at https://docs.doesitarm.com/api/access/free-signup/.

    **Read-only by design.** Every operation is a query. Nothing writes, buys,
    emails, or mutates state — safe to include in an autonomous agent's toolset.

    **Free data, paid capability.** The single-title lookup and search are free
    (anonymous and IP-rate-limited, or with a free key for higher limits). A paid
    key unlocks paid *capability* axes — bulk/export, the change feed, a freshness
    SLA, a commercial-redistribution license, and enterprise reports — never
    per-call volume of the single lookup.

    Verdict facts are published under **CC BY 4.0** — reuse them with attribution.

    `info.version` (1.0.0) is the published *contract* version. The URL carries a
    separate *path* version, `/v1`. They are deliberately two numbers — see
    https://docs.doesitarm.com/api/versioning/.
  license:
    name: CC BY 4.0 (verdict data)
    url: https://creativecommons.org/licenses/by/4.0/
  contact:
    name: DoesItARM
    url: https://docs.doesitarm.com/api/

servers:
  - url: https://api.doesitarm.com/v1
    description: Production (v1)

security:
  - {}
  - ApiKeyAuth: []

tags:
  - name: Verdicts
    description: Look up the current compatibility verdict for a title.
  - name: Search
    description: Resolve a fuzzy name or identifier to candidate titles.
  - name: Categories
    description: Browse paginated verdicts within a category.
  - name: Changes
    description: The diff feed — what changed since a date. Paid capability.

paths:
  /verdicts/{identifier}:
    get:
      operationId: lookupVerdict
      tags: [Verdicts]
      summary: Look up a verdict
      description: |
        Return the one current `Verdict` for a title on Apple Silicon. The
        `identifier` is polymorphic: pass `slug:<slug>`, `steam:<appId>`,
        `bundle:<bundleId>`, or a bare fuzzy name.

        An unknown title is **still `200`** with `status: "unknown"` ("Needs more
        data") plus `alternatives` — never an error, never a fabricated verdict. A
        stale answer is **still `200`** with `stale: true`. Only a genuinely
        ambiguous name (multiple strong matches) returns `409`.

        Mirrors the MCP tool `lookup_verdict`.
      x-mcp-tool: lookup_verdict
      x-doesitarm-tier: free
      parameters:
        - name: identifier
          in: path
          required: true
          description: >-
            `slug:<slug>`, `steam:<appId>`, `bundle:<bundleId>`, or a bare name.
            Reserved deterministic test identifiers: `slug:429-demo` (trips the
            rate limit), `slug:some-ancient-installer` (returns `unknown`),
            `slug:final-cut-pro` (returns `stale: true`).
          schema: { type: string }
          example: "slug:adobe-photoshop"
        - $ref: "#/components/parameters/MacosVersion"
        - $ref: "#/components/parameters/Chip"
      responses:
        "200":
          description: The current verdict (possibly `unknown` or `stale`).
          headers:
            RateLimit-Limit: { $ref: "#/components/headers/RateLimit-Limit" }
            RateLimit-Remaining: { $ref: "#/components/headers/RateLimit-Remaining" }
            RateLimit-Reset: { $ref: "#/components/headers/RateLimit-Reset" }
            ETag: { $ref: "#/components/headers/ETag" }
            Last-Modified: { $ref: "#/components/headers/Last-Modified" }
            X-DoesItARM-Stale: { $ref: "#/components/headers/X-DoesItARM-Stale" }
            Cache-Control: { $ref: "#/components/headers/Cache-Control" }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Verdict" }
              example:
                slug: adobe-photoshop
                title: "Adobe Photoshop"
                kind: app
                status: native
                confidence: high
                basis: [binary, vendor, automated_test]
                macosVersion: "26"
                chip: apple_silicon
                caveats: []
                breaksAtRosettaEol: false
                lastVerified: "2026-06-21"
                stale: false
                signals:
                  - id: sig_01
                    kind: test
                    status: native
                    confidence: high
                    macosVersion: "26"
                    chip: m3
                    at: "2026-06-21T00:00:00Z"
                    ref: "https://doesitarm.com/adobe-photoshop#test-01"
                alternatives: []
        "409": { $ref: "#/components/responses/Ambiguous" }
        "422": { $ref: "#/components/responses/InvalidRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /titles:
    get:
      operationId: searchTitles
      tags: [Search]
      summary: Search titles
      description: |
        Resolve a fuzzy query to ranked candidate titles, each with its best-known
        status. This is the disambiguation surface; `GET /verdicts/{identifier}` is
        the cacheable answer surface. Search never returns `409` — ambiguity is its
        normal `200`.

        Mirrors the MCP tool `search_titles`.
      x-mcp-tool: search_titles
      x-doesitarm-tier: free
      parameters:
        - name: query
          in: query
          required: true
          description: The search string.
          schema: { type: string }
          example: photoshop
        - name: kind
          in: query
          required: false
          description: Restrict to apps or games.
          schema: { type: string, enum: [app, game] }
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: Ranked candidate titles.
          headers:
            RateLimit-Limit: { $ref: "#/components/headers/RateLimit-Limit" }
            RateLimit-Remaining: { $ref: "#/components/headers/RateLimit-Remaining" }
            RateLimit-Reset: { $ref: "#/components/headers/RateLimit-Reset" }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/SearchResultPage" }
              example:
                data:
                  - { slug: figma, title: "Figma", kind: app, status: native, score: 0.97 }
                  - { slug: figment, title: "Figment", kind: app, status: unknown, score: 0.41 }
                pagination: { nextCursor: null, hasMore: false }
        "422": { $ref: "#/components/responses/InvalidRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /categories/{category}/verdicts:
    get:
      operationId: listByCategory
      tags: [Categories]
      summary: List verdicts in a category
      description: |
        Return a paginated list of verdicts within a category (for example
        `productivity`, `games`, `developer-tools`), optionally filtered by status.

        Mirrors the MCP tool `list_by_category`.
      x-mcp-tool: list_by_category
      x-doesitarm-tier: free
      parameters:
        - name: category
          in: path
          required: true
          description: The category slug.
          schema: { type: string }
          example: productivity
        - name: status
          in: query
          required: false
          description: Filter to one verdict status.
          schema:
            type: string
            enum: [native, rosetta2, translation, unsupported, unknown]
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: A page of verdicts in the category.
          headers:
            RateLimit-Limit: { $ref: "#/components/headers/RateLimit-Limit" }
            RateLimit-Remaining: { $ref: "#/components/headers/RateLimit-Remaining" }
            RateLimit-Reset: { $ref: "#/components/headers/RateLimit-Reset" }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/VerdictPage" }
              example:
                data:
                  - slug: figma
                    title: "Figma"
                    kind: app
                    status: native
                    confidence: high
                    basis: [binary, vendor]
                    macosVersion: "26"
                    chip: apple_silicon
                    caveats: ["Electron app; large memory footprint."]
                    breaksAtRosettaEol: false
                    lastVerified: "2026-06-20"
                    stale: false
                    signals: []
                    alternatives: []
                pagination: { nextCursor: "eyJ2IjoyfQ", hasMore: true }
        "422": { $ref: "#/components/responses/InvalidRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /changes:
    get:
      operationId: whatsChangedSince
      tags: [Changes]
      summary: What changed since a date
      description: |
        The diff feed: every verdict change since `since`, newest first. Use it to
        keep a local cache or a fleet inventory current without re-polling each
        title. **Paid capability** — requires an API key with the change-feed
        entitlement.

        Mirrors the MCP tool `whats_changed_since`.
      x-mcp-tool: whats_changed_since
      x-doesitarm-tier: paid
      security:
        - ApiKeyAuth: []
      parameters:
        - name: since
          in: query
          required: true
          description: ISO date; changes at or after this date are returned.
          schema: { type: string, format: date }
          example: "2026-06-01"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
      responses:
        "200":
          description: A page of change records.
          headers:
            RateLimit-Limit: { $ref: "#/components/headers/RateLimit-Limit" }
            RateLimit-Remaining: { $ref: "#/components/headers/RateLimit-Remaining" }
            RateLimit-Reset: { $ref: "#/components/headers/RateLimit-Reset" }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ChangeRecordPage" }
              example:
                data:
                  - slug: cyberpunk-2077
                    title: "Cyberpunk 2077"
                    changedAt: "2026-06-25T09:10:00Z"
                    field: status
                    from: unsupported
                    to: translation
                pagination: { nextCursor: null, hasMore: false }
        "401": { $ref: "#/components/responses/AuthRequired" }
        "403": { $ref: "#/components/responses/PlanRequired" }
        "422": { $ref: "#/components/responses/InvalidRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }

components:
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: "dia_live_… / dia_test_… / dia_free_…"
      description: |
        Send `Authorization: Bearer dia_live_…`. Three prefixes: `dia_free_`
        (no-card free signup, higher limits than anonymous), `dia_test_` (sandbox),
        `dia_live_` (paid). The prefix makes a leaked key greppable and makes
        test / live / free unmistakable.

  parameters:
    MacosVersion:
      name: macosVersion
      in: query
      required: false
      description: Scope the verdict to a macOS major version (for example "26", "27"). Defaults to the latest.
      schema: { type: string }
      example: "26"
    Chip:
      name: chip
      in: query
      required: false
      description: Scope the verdict to a chip family. Defaults to Apple Silicon generally.
      schema:
        type: string
        enum: [apple_silicon, m1, m2, m3, m4]
      example: apple_silicon
    Limit:
      name: limit
      in: query
      required: false
      description: Page size (default 50, max 100).
      schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
    Cursor:
      name: cursor
      in: query
      required: false
      description: Opaque pagination cursor from the previous page's `pagination.nextCursor`. Do not parse it.
      schema: { type: string }

  headers:
    RateLimit-Limit:
      description: Requests allowed in the current window.
      schema: { type: integer }
    RateLimit-Remaining:
      description: Requests left in the current window.
      schema: { type: integer }
    RateLimit-Reset:
      description: Seconds until the window resets (IETF ratelimit-headers, pre-08 three-header form).
      schema: { type: integer }
    Retry-After:
      description: Seconds to wait before retrying. Sent on 429.
      schema: { type: integer }
    ETag:
      description: Stable hash over (slug, macosVersion, chip, lastVerified). Use with If-None-Match for 304s.
      schema: { type: string }
    Last-Modified:
      description: Mirrors the verdict's lastVerified date.
      schema: { type: string }
    X-DoesItARM-Stale:
      description: True when the verdict is past its freshness threshold and a re-test is queued.
      schema: { type: boolean }
    Cache-Control:
      description: >-
        Caching policy. Verdicts: `public, max-age=3600, stale-while-revalidate=86400`.
        The paid change feed: `private`. Search: `no-cache`.
      schema: { type: string }

  responses:
    Ambiguous:
      description: Multiple strong matches on a lookup — disambiguate and re-query. (HTTP 409, not a 3xx redirect.)
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
          example:
            error:
              type: invalid_request
              code: ambiguous
              message: '12 titles match "office". Pass a slug: or steam: identifier.'
              docsUrl: "https://docs.doesitarm.com/api/errors/#ambiguous"
              suggestions:
                - { slug: microsoft-office, title: "Microsoft Office", score: 0.82 }
              requestId: "req_01J5K6Q8Z9"
    InvalidRequest:
      description: A parameter was invalid. (HTTP 422.)
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
          example:
            error:
              type: invalid_request
              code: invalid_param
              message: "Unknown chip 'm9'. Allowed: apple_silicon, m1, m2, m3, m4."
              docsUrl: "https://docs.doesitarm.com/api/errors/"
              requestId: "req_01J5K6Q8ZA"
    AuthRequired:
      description: A paid-only route with no key, or a bad/revoked key. (HTTP 401.)
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
          example:
            error:
              type: auth
              code: auth_required
              message: "Add an API key to call the change feed."
              docsUrl: "https://docs.doesitarm.com/api/authentication/"
              requestId: "req_01J5K6Q8ZB"
    PlanRequired:
      description: Valid key, but the capability is not in the plan. (HTTP 403.)
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
          example:
            error:
              type: auth
              code: plan_required
              message: "The change feed is a paid capability."
              docsUrl: "https://docs.doesitarm.com/api/pricing/"
              upgradeUrl: "https://doesitarm.com/early-access"
              requestId: "req_01J5K6Q8ZC"
    RateLimited:
      description: Rate limit exceeded. (HTTP 429.)
      headers:
        Retry-After: { $ref: "#/components/headers/Retry-After" }
        RateLimit-Limit: { $ref: "#/components/headers/RateLimit-Limit" }
        RateLimit-Remaining: { $ref: "#/components/headers/RateLimit-Remaining" }
        RateLimit-Reset: { $ref: "#/components/headers/RateLimit-Reset" }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
          example:
            error:
              type: rate_limited
              code: rate_limited
              message: "Free tier allows 60 requests/min. Retry in 30s or upgrade."
              docsUrl: "https://docs.doesitarm.com/api/rate-limits/"
              upgradeUrl: "https://doesitarm.com/early-access"
              requestId: "req_01J5K6Q8ZD"

  schemas:
    Verdict:
      type: object
      description: The one current answer for a title on a (macosVersion, chip) scope.
      required:
        - slug
        - title
        - kind
        - status
        - confidence
        - basis
        - macosVersion
        - chip
        - caveats
        - breaksAtRosettaEol
        - lastVerified
        - stale
        - signals
      properties:
        slug: { type: string, description: Canonical URL id on doesitarm.com. }
        title: { type: string }
        kind: { type: string, enum: [app, game] }
        status:
          type: string
          enum: [native, rosetta2, translation, unsupported, unknown]
          description: '`unknown` means "Needs more data" — a real verdict, not a failure.'
        translationLayer:
          type: [string, "null"]
          enum: [gptk, crossover, whisky, vm, null]
          description: Set only when status is "translation"; otherwise null or absent.
        confidence: { type: string, enum: [high, medium, low] }
        basis:
          type: array
          description: How we know — one or more evidence kinds.
          items:
            type: string
            enum: [binary, automated_test, crowd, vendor, curator]
        macosVersion: { type: string, description: macOS major version this verdict is scoped to. }
        chip: { type: string, description: Chip family this verdict is scoped to. }
        caveats:
          type: array
          description: Human-readable qualifiers ("no online play", "low FPS").
          items: { type: string }
        breaksAtRosettaEol:
          type: boolean
          description: True if the title relies on Rosetta 2 and will break when it is removed (macOS 28).
        lastVerified: { type: string, format: date, description: ISO date of the freshest contributing signal. }
        stale: { type: boolean, description: True when past the freshness threshold; a re-test is queued. }
        signals:
          type: array
          description: Contributing reports/tests, newest first.
          items: { $ref: "#/components/schemas/Signal" }
        alternatives:
          type: array
          description: Native equivalents, surfaced mainly when status is "unsupported" or "unknown".
          items: { $ref: "#/components/schemas/Alternative" }
      examples:
        - slug: adobe-photoshop
          title: "Adobe Photoshop"
          kind: app
          status: native
          confidence: high
          basis: [binary, vendor, automated_test]
          macosVersion: "26"
          chip: apple_silicon
          caveats: []
          breaksAtRosettaEol: false
          lastVerified: "2026-06-21"
          stale: false
          signals:
            - id: sig_01
              kind: test
              status: native
              confidence: high
              macosVersion: "26"
              chip: m3
              at: "2026-06-21T00:00:00Z"
              ref: "https://doesitarm.com/adobe-photoshop#test-01"
          alternatives: []
        - slug: final-cut-pro
          title: "Final Cut Pro"
          kind: app
          status: native
          confidence: medium
          basis: [vendor, crowd]
          macosVersion: "26"
          chip: apple_silicon
          caveats: ["Last verified before macOS 26.2; re-check pending."]
          breaksAtRosettaEol: false
          lastVerified: "2025-09-02"
          stale: true
          signals:
            - id: sig_fcp1
              kind: source
              status: native
              confidence: medium
              macosVersion: "25"
              chip: m2
              at: "2025-09-02T00:00:00Z"
              ref: "https://doesitarm.com/final-cut-pro#source-1"
          alternatives: []
        - slug: some-ancient-installer
          title: "Some Ancient Installer"
          kind: app
          status: unknown
          confidence: low
          basis: []
          macosVersion: "26"
          chip: apple_silicon
          caveats: ["No binary or report on file. Submit one to improve this verdict."]
          breaksAtRosettaEol: false
          lastVerified: "2026-06-10"
          stale: false
          signals: []
          alternatives:
            - forSlug: some-ancient-installer
              altSlug: a-maintained-equivalent
              reason: "Actively maintained, native on Apple Silicon."
        - slug: quicken-2017            # ROSETTA branch — runs translated, breaks at the EOL
          title: "Quicken 2017"
          kind: app
          status: rosetta2
          confidence: high
          basis: [binary, crowd]
          macosVersion: "26"
          chip: apple_silicon
          caveats: ["x86_64 only; runs under Rosetta 2 today."]
          breaksAtRosettaEol: true
          lastVerified: "2026-06-18"
          stale: false
          signals:
            - id: sig_qk1
              kind: report
              status: rosetta2
              confidence: high
              macosVersion: "26"
              chip: m2
              at: "2026-06-18T00:00:00Z"
              ref: "https://doesitarm.com/quicken-2017#report-qk1"
          alternatives: []
        - slug: aperture                # UNSUPPORTED branch — doesn't run, with alternatives
          title: "Aperture"
          kind: app
          status: unsupported
          confidence: high
          basis: [binary, crowd]
          macosVersion: "26"
          chip: apple_silicon
          caveats: ["Discontinued by Apple; reproduced failure to launch on macOS 26."]
          breaksAtRosettaEol: false
          lastVerified: "2026-06-15"
          stale: false
          signals:
            - id: sig_ap1
              kind: test
              status: unsupported
              confidence: high
              macosVersion: "26"
              chip: m3
              at: "2026-06-15T00:00:00Z"
              ref: "https://doesitarm.com/aperture#test-ap1"
          alternatives:
            - forSlug: aperture
              altSlug: lightroom-classic
              reason: "Native on Apple Silicon; closest pro photo library and RAW editor."

    Signal:
      type: object
      description: A single piece of evidence behind a verdict — a report, a test, or a known fact.
      required: [id, kind, status, confidence, macosVersion, chip, at, ref]
      properties:
        id: { type: string }
        kind: { type: string, enum: [report, test, source] }
        status:
          type: string
          enum: [native, rosetta2, translation, unsupported, unknown]
        confidence: { type: string, enum: [high, medium, low] }
        macosVersion: { type: string }
        chip: { type: string }
        at: { type: string, format: date-time }
        ref: { type: string, format: uri, description: Link to the underlying report/test on doesitarm.com. }
      examples:
        - id: sig_8f3
          kind: report
          status: translation
          confidence: medium
          macosVersion: "26"
          chip: m2
          at: "2026-06-19T14:02:00Z"
          ref: "https://doesitarm.com/cyberpunk-2077#report-8f3"

    Alternative:
      type: object
      description: A native equivalent suggested when a title is unsupported or unknown.
      required: [forSlug, altSlug, reason]
      properties:
        forSlug: { type: string, description: The title this alternative is for. }
        altSlug: { type: string, description: The suggested native equivalent. }
        reason: { type: string }
      examples:
        - forSlug: cyberpunk-2077
          altSlug: baldurs-gate-3
          reason: "Runs natively on Apple Silicon; similar open-world RPG."

    SearchResult:
      type: object
      description: One candidate from a search, with its best-known status and a match score.
      required: [slug, title, kind, score]
      properties:
        slug: { type: string }
        title: { type: string }
        kind: { type: string, enum: [app, game] }
        status:
          type: string
          enum: [native, rosetta2, translation, unsupported, unknown]
          description: Best-known status for the candidate (may be unknown).
        score: { type: number, format: float, description: "Match score from 0 to 1." }
      examples:
        - { slug: figma, title: "Figma", kind: app, status: native, score: 0.97 }

    ChangeRecord:
      type: object
      description: One field changing on one title's verdict, from the diff feed.
      required: [slug, title, changedAt, field, from, to]
      properties:
        slug: { type: string }
        title: { type: string }
        changedAt: { type: string, format: date-time }
        field:
          type: string
          enum: [status, confidence, translationLayer, stale, breaksAtRosettaEol]
          description: Which Verdict field changed.
        from: { type: [string, boolean, "null"] }
        to: { type: [string, boolean, "null"] }
      examples:
        - slug: cyberpunk-2077
          title: "Cyberpunk 2077"
          changedAt: "2026-06-25T09:10:00Z"
          field: status
          from: unsupported
          to: translation
        - slug: photoshop-cs6
          title: "Adobe Photoshop CS6"
          changedAt: "2026-06-26T08:00:00Z"
          field: breaksAtRosettaEol
          from: false
          to: true

    Pagination:
      type: object
      description: Cursor-based pagination envelope. The cursor is opaque — never parse it.
      required: [nextCursor, hasMore]
      properties:
        nextCursor:
          type: [string, "null"]
          description: Opaque cursor for the next page; null at the end.
        hasMore: { type: boolean }
      examples:
        - { nextCursor: "eyJ2IjoyfQ", hasMore: true }
        - { nextCursor: null, hasMore: false }

    SearchResultPage:
      type: object
      description: A page of search results.
      required: [data, pagination]
      properties:
        data:
          type: array
          items: { $ref: "#/components/schemas/SearchResult" }
        pagination: { $ref: "#/components/schemas/Pagination" }

    VerdictPage:
      type: object
      description: A page of verdicts.
      required: [data, pagination]
      properties:
        data:
          type: array
          items: { $ref: "#/components/schemas/Verdict" }
        pagination: { $ref: "#/components/schemas/Pagination" }

    ChangeRecordPage:
      type: object
      description: A page of change records.
      required: [data, pagination]
      properties:
        data:
          type: array
          items: { $ref: "#/components/schemas/ChangeRecord" }
        pagination: { $ref: "#/components/schemas/Pagination" }

    Error:
      type: object
      description: |
        The single error envelope used by every 4xx/5xx. Agents branch on `code`;
        humans read `message`. No 3xx ever carries this body.
      required: [error]
      properties:
        error:
          type: object
          required: [type, code, message, requestId]
          properties:
            type:
              type: string
              enum: [invalid_request, rate_limited, auth, server]
              description: "Coarse family. ('ambiguous' is a code, carried under type invalid_request.)"
            code:
              type: string
              enum: [ambiguous, invalid_param, auth_required, auth_invalid, plan_required, rate_limited, internal]
              description: Fine-grained branch value.
            message: { type: string, description: Human-readable explanation. }
            docsUrl: { type: string, format: uri }
            upgradeUrl: { type: string, format: uri }
            suggestions:
              type: array
              description: Present on `ambiguous` — candidate titles to disambiguate with.
              items:
                type: object
                required: [slug, title]
                properties:
                  slug: { type: string }
                  title: { type: string }
                  score: { type: number, format: float }
            requestId: { type: string, description: Quote this to support. }
      examples:
        - error:
            type: invalid_request
            code: ambiguous
            message: '12 titles match "office". Pass a slug: or steam: identifier.'
            docsUrl: "https://docs.doesitarm.com/api/errors/#ambiguous"
            suggestions:
              - { slug: microsoft-office, title: "Microsoft Office", score: 0.82 }
            requestId: "req_01J5K6Q8Z9"
