> ## Documentation Index
> Fetch the complete documentation index at: https://vaquill.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Export a reviewed contract with redlines applied

> Mint a short-lived URL to the contract with its redlines applied.

A POST rather than a GET because it creates something: a bearer URL over a
client document, and a rendering that did not exist before. Recording that as
a creation is what lets an audit answer who took a copy and when.



## OpenAPI

````yaml https://api.vaquill.ai/workspace/openapi/v1.json post /v1/matters/{matterId}/reviews/{reviewId}/exports
openapi: 3.1.0
info:
  title: Vaquill Legal Workspace API
  description: >-
    Organization-scoped API for driving your legal workspace from your own
    backend: matters, documents, drafting, review, compare and matrices.


    **Authentication**: `Authorization: Bearer vq_ws_...`. Credentials are
    issued by an organization owner from the automation console at `/automation`
    and are shown once. The organization is resolved from the credential and can
    never be named in a request, so there is no `organizationId` field anywhere
    in this API and sending one is refused.


    This is NOT the Vaquill Data API. That one holds a `vq_key_` credential and
    serves the public legal corpus; the two share no credential, host or
    endpoint, and sending a `vq_key_` here is refused with an error naming the
    other product.


    ## Long-running work


    Anything that costs real work answers `202` with an **operation**. Poll `GET
    /v1/operations/{operationId}` until `status` is `succeeded`, `failed` or
    `cancelled`, honouring `Retry-After` between polls. There are no webhooks,
    so polling is the only completion signal. The five statuses are `queued`,
    `running`, `succeeded`, `failed`, `cancelled`, and there is no sixth: every
    capability reports through the same envelope.


    Send an `Idempotency-Key` on any launch. A retry with the same key and the
    same body returns the FIRST operation rather than starting a second billable
    job, which is what makes recovering from a timeout free.


    ## Identifiers


    Every public id is `<prefix>_<32 lowercase hex>`, for example
    `mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6`. The prefix names the resource type.
    Ids are opaque: take them from responses rather than building them, and
    never pass a bare database uuid.


    ## Lists


    Every collection answers `{data, pagination}`, never a bare array. Page with
    `limit` and `offset`, and read `pagination.total` to size a job before
    running it. `pagination.hasMore` is the reliable signal that rows remain.


    ## Errors


    Errors are RFC 9457 problem documents (`application/problem+json`),
    including for a wrong path and a wrong method. Branch on `type`; it is the
    stable identifier and it resolves to a page describing the failure. `title`
    and `detail` are written for people and may be reworded at any time. Every
    response carries `X-Request-ID`, and every error repeats it as `requestId`:
    quote it when contacting support.


    A `404` is returned identically for a resource that does not exist, one
    belonging to another organization, and one outside your installation's
    matter allowlist. That is deliberate, so the status code cannot be used to
    discover which ids exist elsewhere.


    ## Rate limits


    Limits are per credential and are set by what an operation COSTS rather than
    by its HTTP method, so a `GET` that runs a retrieval is not a read. Each
    operation's tier is fixed; a `429` carries `Retry-After`.


    | Tier | Per minute | Per hour | Per day |

    |---|---|---|---|

    | `poll` | 120 | 3000 | 30000 |

    | `read` | 60 | 1200 | 15000 |

    | `write` | 30 | 600 | 5000 |

    | `launch` | 10 | 200 | 2000 |

    | `download` | 20 | 300 | 3000 |

    | `upload` | 5 | 100 | 500 |


    ## Scopes


    A credential carries an explicit scope set, chosen when it is issued. A call
    outside them is `403` with `insufficient-scope`, naming the scopes the
    operation needed. Read and run are separate throughout, so a credential can
    be allowed to read results without being allowed to spend money producing
    them.


    `chronology:read`, `chronology:write`, `clients:read`, `clients:write`,
    `compare:read`, `compare:run`, `compliance:read`, `compliance:run`,
    `credentials:rotate`, `documents:download`, `documents:read`,
    `documents:write`, `drafting:read`, `drafting:run`, `exports:create`,
    `exports:read`, `facts:read`, `facts:run`, `matrices:read`, `matrices:run`,
    `matrices:write`, `matters:read`, `matters:write`, `nda:read`, `nda:run`,
    `operations:read`, `playbooks:read`, `playbooks:run`, `playbooks:write`,
    `research:read`, `research:run`, `review:read`, `review:run`,
    `summaries:read`, `summaries:run`, `webhooks:read`, `webhooks:write`,
    `workflows:read`, `workflows:run`, `workflows:write`
  version: 1.0.0
servers:
  - url: https://api.vaquill.ai/workspace
    description: Vaquill Legal Workspace API (production)
  - url: /workspace
    description: Vaquill Legal Workspace API (relative to the mount)
security:
  - WorkspaceAuth: []
tags:
  - name: Operations
    description: >-
      The one job envelope. Every long-running call answers `202` with an
      operation, and polling this resource is the only way to learn it finished:
      there are no webhooks. Five statuses, no synonyms.
  - name: Clients
    description: The people and companies matters are filed under. Synchronous CRUD.
  - name: Matters
    description: >-
      The unit of work everything else hangs off. A matter scopes documents,
      drafts, reviews, comparisons and matrices, and it is what a credential's
      access is checked against.
  - name: Folders
    description: >-
      Organization inside and across matters. The same folders the web app
      shows, so one created here appears in the customer's browser.
  - name: Documents
    description: >-
      Files in a matter: their metadata, their ingested text, and the original
      bytes. A document is only usable by retrieval, drafting and review once
      its status is `succeeded`.
  - name: Uploads
    description: >-
      Getting files in. One presigned multipart flow at every size, with the
      bytes going straight to storage and never through this API. Initiate, PUT
      each part, then complete.
  - name: Research
    description: >-
      Asking legal questions and getting grounded, cited answers. This API does
      NOT stream: an ask answers `202` and you poll the operation, then read the
      answer off the message it points at. Conversation state is kept here, so
      you hold a chat id rather than replaying a transcript. Also covers the
      matter settings behind an answer, the skills you can ask through, and the
      standalone web-research tools.
  - name: Drafting
    description: >-
      Generating, revising and exporting draft documents. Bodies come out as
      sections and go in as markdown; the editor's own format is never on the
      wire. Rendered files come from the export route.
  - name: Playbooks
    description: >-
      The organization's negotiating positions, per contract type, and the
      starter templates to build them from. A playbook is the input a contract
      review is measured against.
  - name: Reviews
    description: >-
      Reviewing one contract against a playbook: clause analysis, redlines,
      flags, liability exposure and a reported sign-off gate. Export applies the
      redlines as native Word tracked changes.
  - name: Facts
    description: >-
      The cross-document fact ledger for a matter: what every document in it
      asserts, coalesced and cited back to its source passages.
  - name: Matter summary
    description: >-
      A citation-backed summary of everything filed to a matter, generated on
      demand and readable claim by claim.
  - name: Chronology
    description: >-
      The dated events across a matter's documents, as one timeline, with
      duplicate and date-conflict flags.
  - name: NDA triage
    description: >-
      Screening one inbound NDA against ten standard criteria and, where you
      name one, your own NDA playbook. Answers `green`, `yellow` or `red` with
      the reasoning per criterion, plus a report you can forward.
  - name: Compliance
    description: >-
      Checking one document against one regulation's requirement checklist: a
      verdict per requirement with the article it comes from, the gaps, and what
      to do about them. Only regulations with a real checklist behind them are
      accepted.
  - name: Comparisons
    description: >-
      Diffing two documents in a matter into structural changes, with a
      substantive-versus-cosmetic judgment and a downloadable redline.
  - name: Matrices
    description: >-
      Spreadsheet-style extraction across many documents: rows are documents,
      columns are questions, cells are answers with verified citations. Building
      a grid is free; running it is what costs.
  - name: Workflows
    description: >-
      Multi-step analyses from a fixed catalogue. Read a definition to learn
      what documents and inputs it takes, launch a run inside a matter, then
      download the artifacts it produces.
externalDocs:
  description: Getting started guide and error reference
  url: https://vaquill.ai/docs/workspace-api
paths:
  /v1/matters/{matterId}/reviews/{reviewId}/exports:
    post:
      tags:
        - Reviews
      summary: Export a reviewed contract with redlines applied
      description: >-
        Mint a short-lived URL to the contract with its redlines applied.


        A POST rather than a GET because it creates something: a bearer URL over
        a

        client document, and a rendering that did not exist before. Recording
        that as

        a creation is what lets an audit answer who took a copy and when.
      operationId: reviews.export
      parameters:
        - name: matterId
          in: path
          required: true
          schema:
            type: string
            title: Matterid
          description: >-
            `mat_` identifier of the matter to work inside. Everything in this
            API hangs off a matter, and the matter in the path is what the
            authorization boundary is checked against. Take it from `GET
            /v1/matters`.
        - name: reviewId
          in: path
          required: true
          schema:
            type: string
            title: Reviewid
          description: >-
            `rev_` identifier of the contract review. Returned on the operation
            that launched it, at either depth.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReviewExportRequest'
            example:
              redlines:
                - clauseName: Limitation of Liability
                  currentLanguage: Supplier's total liability shall be unlimited.
                  replacementLanguage: >-
                    Supplier's total liability shall not exceed the fees paid in
                    the preceding twelve months.
                  sectionReference: '8.2'
                  comment: value
              trackedChanges: true
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReviewExport'
              example:
                format: docx
                filename: msa-acme-v3.docx
                url: >-
                  https://storage.vaquill.ai/exports/msa-acme-v3-redline.docx?signature=...
                expiresAt: '2026-08-19T14:32:10Z'
                sizeBytes: 248193
                redlineCount: 9
                trackedChanges: false
                approvalGate:
                  required: false
                  level: partner
                  dealBreakerCount: 1
                  reasons:
                    - clauseName: Limitation of Liability
                      approvalLevel: partner
                      isDealBreaker: false
                      note: 'Escalated to GC: deal value over $1M.'
                  summary: >-
                    Twelve substantive changes, seven of them in the liability
                    and indemnity sections.
        '401':
          description: >-
            The credential is missing, malformed, unknown, revoked or expired.
            `type` is `invalid-credential`, or `wrong-product-credential` when a
            `vq_key_` Data API key was sent to this API.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
            WWW-Authenticate:
              description: RFC 9110 authentication challenge.
              schema:
                type: string
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '403':
          description: >-
            The credential does not carry a scope this operation requires
            (`insufficient-scope`), or the organization's installation is
            suspended (`installation-inactive`).
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
            WWW-Authenticate:
              description: RFC 9110 authentication challenge.
              schema:
                type: string
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '404':
          description: >-
            The resource does not exist, is not this organization's, or is
            outside this installation's matter allowlist. The three are
            deliberately indistinguishable, so the status code cannot be used to
            discover which ids exist in another organization. Each resource has
            its own `type`.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '422':
          description: >-
            The request does not match the published schema. `errors` names each
            rejected field and why. The submitted value is never echoed back.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ValidationProblem'
        '429':
          description: Too many requests for this credential's tier. Honour `Retry-After`.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '500':
          description: >-
            An unexpected error on our side. The body carries a stable `type`
            and the request id and nothing else; the cause is in our logs.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '503':
          description: >-
            A dependency this request needs is unavailable, so nothing was done.
            Retryable. Authentication fails closed rather than admitting the
            request, so this is never a statement about your credential.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
components:
  schemas:
    ReviewExportRequest:
      properties:
        redlines:
          anyOf:
            - items:
                $ref: '#/components/schemas/AcceptedRedline'
              type: array
              maxItems: 500
            - type: 'null'
          title: Redlines
          description: >-
            Which edits to apply. Omit to apply EVERY redline the review
            produced, which is the common case for an integration that triaged
            them elsewhere. Supply them to apply a subset, or edits you wrote
            yourself.
        trackedChanges:
          type: boolean
          title: Trackedchanges
          description: >-
            True renders native Word tracked changes, so the counterparty
            accepts or rejects each edit in the Review pane. False renders
            colored strikethrough and underline, which is viewable anywhere but
            is not real revisions.
          default: true
          examples:
            - true
      additionalProperties: false
      type: object
      title: ReviewExportRequest
      description: >-
        Which of a review's redlines to apply, and how to mark them up.


        Omitting `redlines` applies EVERY redline the review produced, which is
        the

        common case for an integration that has already triaged them elsewhere.

        Supplying them is how a caller applies a subset, or applies edits it
        wrote

        itself after reading the review.
    ReviewExport:
      properties:
        format:
          type: string
          const: docx
          title: Format
          description: Format of the exported file. Only DOCX is produced today.
          default: docx
          examples:
            - docx
        filename:
          type: string
          title: Filename
          description: Suggested filename for the download.
          examples:
            - msa-acme-v3.docx
        url:
          type: string
          title: Url
          description: >-
            Short-lived signed URL to download the marked-up contract. Fetch it
            promptly and do not store it.
          examples:
            - >-
              https://storage.vaquill.ai/exports/msa-acme-v3-redline.docx?signature=...
        expiresAt:
          type: string
          format: date-time
          title: Expiresat
          description: When the URL stops working (RFC 3339).
          examples:
            - '2026-08-19T14:32:10Z'
        sizeBytes:
          type: integer
          title: Sizebytes
          description: Size of the exported file in bytes.
          examples:
            - 248193
        redlineCount:
          type: integer
          title: Redlinecount
          description: How many redlines were applied to produce this file.
          examples:
            - 9
        trackedChanges:
          type: boolean
          title: Trackedchanges
          description: Whether the export uses native Word tracked changes.
          examples:
            - false
        approvalGate:
          anyOf:
            - $ref: '#/components/schemas/ReviewApprovalGate'
            - type: 'null'
          description: >-
            Repeated from the review on purpose. An integration that exports
            without re-reading the review is exactly the one that would send an
            unapproved redline to a counterparty, so the gate travels with the
            bytes.
      additionalProperties: false
      type: object
      required:
        - filename
        - url
        - expiresAt
        - sizeBytes
        - redlineCount
        - trackedChanges
      title: ReviewExport
      description: >-
        A short-lived URL to the marked-up contract.


        The same shape as a comparison export, and for the same reason: the
        route

        declares a response model, and a DOCX is not one. The bytes live in
        object

        storage and the customer fetches them directly.


        `approvalGate` is repeated here rather than left on the review. An

        integration that exports without re-reading the review is exactly the
        one

        that would send an unapproved redline to a counterparty, and the gate is

        cheap to carry at the point where content actually leaves the workspace.
    Problem:
      type: object
      title: Problem
      description: >-
        An RFC 9457 problem document. Branch on `type`, which is stable; `title`
        and `detail` are written for people and may be reworded. Some problems
        carry extra members (`requiredScopes`, `limit`, `expectedVersion`),
        which is why this object is open.
      required:
        - type
        - title
        - status
        - detail
        - instance
      properties:
        type:
          type: string
          format: uri
          description: >-
            The stable identifier for this error, and the one field to branch
            on. Resolves to a page describing it.
          examples:
            - https://vaquill.ai/docs/workspace-api/errors/insufficient-scope
        title:
          type: string
          description: A short human-readable summary.
          examples:
            - Insufficient scope
        status:
          type: integer
          description: The HTTP status code, repeated.
          examples:
            - 403
        detail:
          type: string
          description: >-
            What went wrong on this specific request. May be reworded at any
            time.
          examples:
            - >-
              This credential carries matters:read. This operation needs
              matters:write.
        instance:
          type: string
          description: The path this problem occurred on.
          examples:
            - /workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
        requestId:
          type: string
          description: >-
            The id of this request, identical to the `X-Request-ID` response
            header. Quote it when contacting support.
          examples:
            - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
      additionalProperties: true
    ValidationProblem:
      type: object
      title: ValidationProblem
      description: >-
        A problem document for a schema rejection. `errors` lists the fields
        that were refused. The value you submitted is deliberately not echoed,
        so a validation failure cannot copy your content into an error response
        or into either side's logs.
      required:
        - type
        - title
        - status
        - detail
        - instance
        - errors
      properties:
        type:
          type: string
          format: uri
          description: >-
            The stable identifier for this error, and the one field to branch
            on. Resolves to a page describing it.
          examples:
            - https://vaquill.ai/docs/workspace-api/errors/insufficient-scope
        title:
          type: string
          description: A short human-readable summary.
          examples:
            - Insufficient scope
        status:
          type: integer
          description: The HTTP status code, repeated.
          examples:
            - 403
        detail:
          type: string
          description: >-
            What went wrong on this specific request. May be reworded at any
            time.
          examples:
            - >-
              This credential carries matters:read. This operation needs
              matters:write.
        instance:
          type: string
          description: The path this problem occurred on.
          examples:
            - /workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
        requestId:
          type: string
          description: >-
            The id of this request, identical to the `X-Request-ID` response
            header. Quote it when contacting support.
          examples:
            - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
        errors:
          type: array
          description: One entry per rejected field.
          items:
            type: object
            required:
              - location
              - message
              - type
            properties:
              location:
                type: string
                description: >-
                  Dotted path to the rejected field, for example
                  `body.contentMarkdown`.
              message:
                type: string
                description: Why it was rejected.
              type:
                type: string
                description: The validation rule that failed.
      additionalProperties: true
    AcceptedRedline:
      properties:
        clauseName:
          type: string
          maxLength: 200
          title: Clausename
          description: Which clause this edit applies to.
          examples:
            - Limitation of Liability
        currentLanguage:
          type: string
          maxLength: 200000
          title: Currentlanguage
          description: >-
            The exact text to replace. Must match the contract verbatim or the
            edit cannot be anchored.
          examples:
            - Supplier's total liability shall be unlimited.
        replacementLanguage:
          type: string
          maxLength: 200000
          title: Replacementlanguage
          description: The text to put in its place.
          examples:
            - >-
              Supplier's total liability shall not exceed the fees paid in the
              preceding twelve months.
        sectionReference:
          anyOf:
            - type: string
              maxLength: 200
            - type: 'null'
          title: Sectionreference
          description: Where the clause sits in the contract, for example `8.2`.
          examples:
            - '8.2'
        comment:
          anyOf:
            - type: string
              maxLength: 2000
            - type: 'null'
          title: Comment
          description: >-
            Attached to the inserted text as a native Word comment, which is
            where a negotiation rationale belongs: in the margin of the document
            the other side opens.
          examples:
            - >-
              Our standard cap. Happy to discuss a supercap for data-security
              breaches.
      additionalProperties: false
      type: object
      required:
        - clauseName
        - currentLanguage
        - replacementLanguage
      title: AcceptedRedline
      description: One edit to apply to the contract text in an export.
    ReviewApprovalGate:
      properties:
        required:
          type: boolean
          title: Required
          description: >-
            Whether a human should sign this off before it goes to the
            counterparty. REPORTED, never enforced: it does not block the review
            or the export. Implement the gate on your side using this field.
          default: false
          examples:
            - false
        level:
          anyOf:
            - type: string
            - type: 'null'
          title: Level
          description: >-
            The highest sign-off any gating clause needs: `manager`, `partner`
            or `gc`. Absent when `required` is false.
          examples:
            - partner
        dealBreakerCount:
          type: integer
          title: Dealbreakercount
          description: How many clauses sit at or below the walk-away floor.
          default: 0
          examples:
            - 1
        reasons:
          items:
            $ref: '#/components/schemas/ReviewApprovalReason'
          type: array
          title: Reasons
          description: Which clauses drive the gate, and why each one does.
        summary:
          type: string
          title: Summary
          description: One-line explanation of the gate, suitable to show a reviewer.
          default: ''
          examples:
            - >-
              Twelve substantive changes, seven of them in the liability and
              indemnity sections.
      additionalProperties: false
      type: object
      title: ReviewApprovalGate
      description: >-
        Whether a human has to sign this off before it goes to the counterparty.


        **Reported, never enforced.** The gate is computed deterministically
        from the

        playbook's own `approvalLevel` and `dealBreaker` on clauses that
        actually

        deviated, and it is published as a fact about the result. It does not
        block

        the review, it does not block the export, and the operation reaches a

        terminal status either way.


        That is the same decision the acting-user header already carries (docs
        07.3):

        we record what we know and build no enforcement machinery we cannot
        honour.

        Enforcing would mean an approval workflow, an enrolled approver
        directory and

        a state a review can sit in indefinitely, which is precisely the "do not
        let

        it hang" failure the handoff for this track warned about. A caller that
        wants

        a gate has everything it needs to implement one: `required` says
        whether,

        `level` says who, and `reasons` says why.
    ReviewApprovalReason:
      properties:
        clauseName:
          type: string
          title: Clausename
          description: The clause driving this part of the gate.
          examples:
            - Limitation of Liability
        approvalLevel:
          anyOf:
            - type: string
            - type: 'null'
          title: Approvallevel
          description: Sign-off this clause requires.
          examples:
            - partner
        isDealBreaker:
          type: boolean
          title: Isdealbreaker
          description: True when this clause is at or below the walk-away floor.
          default: false
          examples:
            - false
        note:
          anyOf:
            - type: string
            - type: 'null'
          title: Note
          description: >-
            Set when a conditional rule RAISED this clause's sign-off, for
            example 'Escalated to GC: deal value over $1M'. Absent when the
            level came straight from the playbook position.
          examples:
            - 'Escalated to GC: deal value over $1M.'
      additionalProperties: false
      type: object
      required:
        - clauseName
      title: ReviewApprovalReason
      description: One clause driving the review-level sign-off gate.
  securitySchemes:
    WorkspaceAuth:
      type: http
      scheme: bearer
      bearerFormat: vq_ws_*
      description: >-
        Workspace credential issued from the automation console at
        `/automation`. Send it as `Authorization: Bearer vq_ws_...`. This is NOT
        a Data API key: a `vq_key_` credential is refused here and names the
        other product in the error.

````