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

# Get a pharmacy fill

> Firm keys read their own cases; a provider key reads its own referral. Admin keys receive 403 wrong_scope. Restricted-access practices receive 403 restricted_access. Reads are allowed in any referral status. Includes the fill's transactions and its own totals (billed, paid, adjustments, computedBalance).



## OpenAPI

````yaml /api-reference/openapi.yml get /med-cases/{caseId}/referrals/{referralId}/pharmacy/fills/{fillId}
openapi: 3.0.3
info:
  title: CaseXchange Public API
  version: 1.0.0
  description: >-
    The CaseXchange Public API enables law firms to integrate referral
    management

    directly into their own systems.


    ## Authentication

    All requests must include an `X-API-Key` header containing a valid API key:
    `cxp_<hex>` (firm-scoped) or

    `cxpp_<hex>` (provider-scoped). Keys are scoped to an owner — a firm or a
    Med Xchange provider — and an

    access tier. Salesforce-issued tokens (`cxsf_<hex>`) authenticate requests
    as firm context but are not a

    named, revocable public API key; a handful of actions (e.g. managing webhook
    subscriptions) require an

    actual `cxp_`/`cxpp_` key and reject an `sf_token` context with `403
    wrong_scope`.


    ## Access Tiers

    | Tier | Description |

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

    | `read_only` | GET endpoints — read data without mutation |

    | `standard` | read_only + create/update case referrals |

    | `full` | standard + delete, routing rules, bulk import, firm mutation |


    ## Rate Limiting

    Rate limits are enforced per API key. Exceeded limits return `429 Too Many
    Requests`.


    ## Pagination

    List endpoints accept `page` (default 1) and `limit` (default 20, max 100)
    query params.


    ## Response Format

    Successful responses are wrapped in `{ data: T, meta: { requestId, timestamp
    } }`.

    Errors are wrapped in `{ error: { code, message }, meta: { requestId,
    timestamp } }`.


    ## Webhooks


    CaseXchange can push referral lifecycle events to an HTTPS endpoint you
    control, so you do not have to poll.

    Webhooks are notifications that something changed — not a synchronisation
    feed. Treat them as a signal to

    reconcile against `GET /status-updates`, `GET /referrals` and the other read
    endpoints, which remain the

    authority. There is no backfill: a subscription only ever receives events
    that happen after it is created.


    ### Subscribing


    Create a subscription with `POST /webhooks` using a firm-scoped `cxp_` key
    at `standard` tier or above; the

    GET endpoints need only `read_only`. Admin keys, Salesforce `cxsf_` tokens
    and MCP-issued keys cannot own a

    subscription and are rejected with `403 wrong_scope`, whatever their tier.
    Provider-scoped `cxpp_` keys manage

    their own Med Xchange subscriptions through the same endpoints, but those
    deliveries use a different body

    shape — everything below describes firm subscriptions.


    - A firm may hold **5 active** subscriptions. Creating a sixth, or
    re-activating one when five are already
      active, returns `400 subscription_limit_reached`.
    - The URL must be `https://` **on port 443**, carry no `user:password@`
    credentials, and resolve to a public IP
      address (a hostname that will not resolve, or that resolves to a private or reserved range, is rejected with
      `400 invalid_webhook_url`). Deliveries do not follow redirects: a 3xx counts as a failed attempt.
    - **Every active subscription receives every event for your firm.** There is
    no per-subscription event filter
      yet, so branch on the `event` field (or the `X-CaseXchange-Event` header) and ignore what you do not handle.
      Unknown event names should be ignored rather than treated as errors — new ones are added over time.
    - The signing `secret` is returned exactly twice: by `POST /webhooks` and by
    `POST /webhooks/{id}/rotate-secret`.
      It cannot be read back.

    ### What a delivery looks like


    Each delivery is a single HTTPS `POST` of a JSON body, with a 10-second
    timeout and no redirect following.


    | Header | Meaning |

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

    | `X-CaseXchange-Event` | The event name — same value as `event` in the body
    |

    | `X-CaseXchange-Event-Id` | Identifies the *fact*. Shared by the deliveries
    to both firms and to all of your subscriptions |

    | `X-CaseXchange-Delivery` | Identifies *this* delivery. Resent unchanged on
    every retry — use it as your idempotency key |

    | `X-CaseXchange-Signature` | `t=<unix-seconds>,v1=<hex>` — see below |


    The body is the `CasexWebhookEnvelope` schema: a self-describing envelope so
    a low-code receiver can route and

    deduplicate without reading headers. `data` varies by event; the
    `x-webhook-events` extension at the root of

    this document maps every event name to its `data` schema, the perspectives
    that can receive it, and whether it

    can arrive masked.


    ```json

    {
      "schemaVersion": 1,
      "event": "referral.accepted",
      "eventId": "3f9d2c1e-7b44-4a1c-9e0b-2c5f1a8d6e21",
      "occurredAt": "2026-09-03T14:12:09.401Z",
      "perspective": "sent",
      "piiMasked": false,
      "recipientFirmId": "a1b2c3d4-0000-4000-8000-000000000001",
      "referralId": "6f0b8c2a-1d3e-4f5a-8b7c-9d0e1f2a3b4c",
      "baseCaseId": "2a91c4d6-8e7f-4a1b-9c2d-3e4f5a6b7c8d",
      "referenceNumber": "ACME-0042-BLF",
      "referringFirm": { "id": "a1b2c3d4-0000-4000-8000-000000000001", "name": "Acme Injury Law" },
      "referentFirm":  { "id": "a1b2c3d4-0000-4000-8000-000000000002", "name": "Bay Legal Firm" },
      "isTestCase": false,
      "actor": { "firmId": "a1b2c3d4-0000-4000-8000-000000000002", "source": "manual" },
      "data": { "...": "see x-webhook-events" }
    }

    ```


    ### Verifying the signature


    The `v1` value is `HMAC-SHA256` of `${t}.${rawBody}`, keyed with your
    subscription secret and hex-encoded,

    where `t` is the timestamp from the same header and `rawBody` is the exact
    request body as bytes. Verify

    before parsing the JSON — re-serialising changes the bytes and the signature
    will not match. Compare with a

    constant-time function (`crypto.timingSafeEqual` in Node) and reject a `t`
    more than about 5 minutes old to

    blunt replays.


    ```javascript

    const crypto = require("crypto");


    // `rawBody` MUST be the exact bytes we sent — verify before any JSON.parse.

    function verify(rawBody, signatureHeader, secret) {
      const parts = Object.fromEntries(
        signatureHeader.split(",").map((p) => p.split("=", 2))
      );
      const t = Number(parts.t);
      if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;

      const expected = crypto
        .createHmac("sha256", secret)
        .update(`${t}.${rawBody}`)
        .digest("hex");

      const a = Buffer.from(expected, "hex");
      const b = Buffer.from(parts.v1 ?? "", "hex");
      return a.length === b.length && crypto.timingSafeEqual(a, b);
    }

    ```


    ### Retries, the circuit breaker, and what your endpoint should return


    Return a `2xx` as soon as you have durably accepted the body, and do the
    work asynchronously. **Any** non-2xx

    response, timeout or connection failure counts as a failed attempt. A
    delivery is attempted up to **5 attempts**

    in total: a failed attempt is retried after 30 s, 2 min, 10 min and 30 min
    (about 43 minutes end to end), and

    after the fifth it is marked `EXHAUSTED` and never retried. `GET
    /webhooks/{id}/deliveries` shows the attempt

    count, last status code and last error — never the payload.


    After **10 consecutive** exhausted deliveries the subscription trips a
    circuit breaker and is deactivated

    (`isActive: false`). It stays visible on `GET /webhooks/{id}`; re-enable it
    with `PATCH /webhooks/{id}`

    `{ "isActive": true }` once your endpoint is healthy, subject to the
    5-active cap.


    ### Rotating the secret


    `POST /webhooks/{id}/rotate-secret` returns a new secret and starts signing
    with it immediately, including

    retries of deliveries created before the rotation. There is no dual-signing
    window: switch your verifier over

    at the moment you rotate, or briefly accept either secret on your side.


    ### Trying it out


    `POST /webhooks/{id}/test` queues a synthetic `webhook.test` delivery. It is
    not a CaseX firm event and does

    not use the envelope: its body is `{ event, timestamp, data }`, and its
    `eventId` travels only in the header.


    Echo mode points a subscription at a CaseXchange-hosted endpoint instead of
    yours, so you can read back the

    exact headers, raw body and a signature check for what would have been
    delivered. Enable it with

    `POST /webhooks/{id}/echo/enable`, read `GET /webhooks/{id}/echo/captures`,
    and restore your URL with

    `POST /webhooks/{id}/echo/disable`. Captures keep the **full** body, are
    limited to the 50 most recent per

    subscription, and are discarded after 1 hour.


    The echo URL and its token come back from `echo/enable` only — that call is
    idempotent, so call it again if you

    need them a second time. Reads never disclose them: while echo mode is on,
    `GET /webhooks` and

    `GET /webhooks/{id}` keep reporting **your** endpoint in `url` (and repeat
    it in `originalUrl`) with

    `isEchoActive: true`. The token is a credential, not a label — the echo
    endpoint accepts unauthenticated posts

    on it, and anyone holding it could push the 50-capture cap over and evict
    the deliveries you were inspecting.


    ### Client information and the receiving firm


    A firm that is **sent** a referral sees a masked snapshot until it
    acknowledges receipt itself. While it is

    masked, every delivery to that firm carries `piiMasked: true` and
    `data.case` is the `MaskedCaseSnapshot`:

    only `id`, `baseCaseId`, `referenceNumber`, `status`, `closingStatus`,
    `referringFirmId`, `referentFirmId`,

    `caseType`, `jurisdiction`, `caseCounty`, `sentDate`, `isRetainerSigned`,
    `createdAt` and `updatedAt`. Client

    name, contact details, date of birth, incident date, case title,
    description, lead attorney, warm-transfer

    text, the money block and custom fields are **absent from the object**, not
    present-and-null, and `message`

    is forced to null.


    - Only an action by the receiving firm unmasks it — its own user
    acknowledging in the app, via the
      acknowledgment email, or through a bulk status update it uploads itself; its connected case-management
      system; or its own Salesforce org. A platform administrator forcing a status, a historical CSV import, or
      the sending firm acting on its behalf never does.
    - In the normal flow the first unmasked delivery is `referral.accepted`.
    Until then, only the status family
      (`referral.received`, `referral.accepted`, `referral.rejected`, `case.status_changed`) is delivered at all:
      `case.updated`, `note.*` and `document.*` are withheld entirely from an unacknowledged receiving firm.
    - A referral withdrawn or rejected before it was ever acknowledged never
    unmasks, and earlier deliveries
      cannot be retracted.
    - A receiving firm never receives `case.created`, `case.rated`, the tier
    fields, or the sending firm's
      `source`. `document.*` additionally stops for the counterparty once the referral is CLOSED, where
      `case.updated` and `note.*` keep flowing; all three stop at the other terminal statuses.
    - Echo-mode captures return the full delivered body to the subscription
    owner, so treat echo mode the same
      way you treat the payload itself.

    ### Multi-hop cases


    A case can be referred onward, so one `baseCaseId` may carry several
    referrals. Each firm only ever sees its

    own hop: `referralId` identifies that hop and `referringFirm`/`referentFirm`
    name its two parties. Fields that

    belong to a hop — fees, settlement figures, lead attorney, phase — are
    delivered only to the two firms on the

    hop that changed; other firms on the same case are told only about
    case-level changes, and only when something

    they can see actually changed.


    ### Ordering, duplicates, and your own writes


    - **Deduplicate on `X-CaseXchange-Delivery`.** It is stable across retries.
    `eventId` is *not* unique per
      delivery: it is shared by the same fact across firms and subscriptions, which is what makes it useful for
      correlation.
    - There is **no ordering guarantee**. Dispatch is concurrent and a retried
    delivery can trail a later one by
      the better part of an hour, so deliveries arrive out of order. Apply the delivery with the greatest
      `occurredAt`.
    - One transaction can commit two status rows (for example CLOSING then
    CLOSED), and those two deliveries carry
      a byte-identical `occurredAt`. There is deliberately no tiebreaker field: chain them with
      `data.previousStatus` → `data.status`.
    - CaseXchange does not distinguish a change made in its own UI from one made
    through this API, so your own
      writes come back to you as events. `actor.firmId` tells you which firm acted, not through which channel; to
      suppress your own echo, compare the delivered state against what you last sent.

    ### Known gaps


    These are deliberate omissions today, listed so you can plan around them
    rather than discover them:


    - Historical CSV import creates and updates cases silently — no events. A
    bulk status update by CSV does emit,
      one event per row.
    - Status changes written by the Clio Manage pull bypass the status pipeline
    and produce no event for either
      firm. Reconcile with `GET /status-updates`.
    - Status changes written by the DearLegal inbound webhook produce no event
    and do not count as the receiving
      firm's acknowledgement.
    - Notes written automatically alongside a status transition, a case creation
    or a CSV import are not published
      as `note.created`. A note supplied to `PATCH /sent-cases/{id}` is a real note and does produce one.
    - `note.deleted` does not exist: nothing in CaseXchange produces it, so it
    is not in the catalog.

    - Archiving a case or a referral stops **all** events for it, and there is
    no event announcing the archive;
      unarchiving resumes them. Revoking your firm's `public_api` integration or deactivating the firm has the
      same effect, while `GET /webhooks/{id}` still reports `isActive: true`.
    - Partner messages and document metadata edits produce no events.

    - There is no per-subscription event filter and no endpoint to replay a
    delivery.

    - Delivery payloads are retained on our side for 30 days once delivered (90
    if exhausted) and hard-deleted
      after 180; `GET /webhooks/{id}/deliveries` never returns them.
  contact:
    name: CaseXchange API Support
    url: https://casexchange.com
servers:
  - url: https://api.casexchange.com/api/public/v1
    description: Production Public API base path
  - url: /api/public/v1
    description: Relative base path (for proxied docs/local environments)
security: []
tags:
  - name: Account
    description: Identity of the calling API key and its firm
  - name: Referrals
    description: >-
      Referral records visible to the caller's firm as sender or receiver, and
      the lifecycle transitions on them
  - name: Received Referrals
    description: >-
      Receiver-centric view of referrals sent TO the caller's firm, addressable
      by UUID, reference number or CMS id
  - name: Sent Cases
    description: 'Sender-side case management: create, track, and refer/route cases you own'
  - name: Status Updates
    description: Status-history and partner-message entries across the caller's referrals
  - name: Messages
    description: Partner messaging on a referral
  - name: Cases
    description: >-
      Legacy referral endpoints keyed by referral UUID, served from either
      party's perspective. Prefer Referrals, Received Referrals and Sent Cases
      for new integrations.
  - name: Documents
    description: Document upload, download, and management
  - name: Reference Data
    description: Case types, jurisdictions, and counties
  - name: Firms
    description: Firm directory and profile management
  - name: Network
    description: >-
      Referral relationships between the caller's firm and other firms.
      `outbound` is a relationship the caller sends referrals through (caller →
      target firm); `inbound` is one where the caller receives (source firm →
      caller). Requires a firm-scoped API key.
  - name: Routing
    description: Referral routing rules
  - name: Analytics
    description: Dashboard and case analytics
  - name: Export
    description: >-
      XLSX/CSV downloads of analytics and case data. Responses are binary
      attachments rather than the JSON envelope. Export endpoints carry an
      additional limit of 10 requests per API key per sliding minute, on top of
      the tier rate limits.
  - name: Import
    description: Bulk case import/update via CSV
  - name: Notifications
    description: Notification preferences and email management
  - name: Users
    description: User management within the caller's firm
  - name: External Links
    description: >-
      Links between a CaseXchange referral and a record in an external
      case-management system. Supported systems: `cloudlex`, `morgan_morgan`,
      `litify`. Requires a firm-scoped API key.
  - name: Webhooks
    description: >-
      Outbound event subscriptions. Available to both firm-scoped (`cxp_*`) and
      provider-scoped (`cxpp_*`) API keys — a subscription belongs to whichever
      kind of key created it, and each key can only see and manage its own
      subscriptions (up to 5 active). Admin keys, Salesforce-issued tokens
      (`cxsf_*` — these authenticate as firm context but are not a named,
      revocable key) and MCP-issued keys cannot manage webhooks and receive `403
      wrong_scope`.


      Each delivery is an HTTPS POST of a JSON body with headers
      `X-CaseXchange-Event` (event name), `X-CaseXchange-Event-Id` (identifies
      the fact), `X-CaseXchange-Delivery` (identifies the delivery, stable
      across retries — use it as your idempotency key) and
      `X-CaseXchange-Signature` in the form `t=<unix-seconds>,v1=<hex>`, where
      `v1` is HMAC-SHA256(secret, `<t>.<rawBody>`). Failed deliveries are
      retried with back-off up to 5 attempts (10-second request timeout); after
      that the delivery is marked `EXHAUSTED`.


      Firm-owned subscriptions receive `WebhookEventType` events (CaseXchange
      referral lifecycle) and their body is the self-describing
      `CasexWebhookEnvelope`; the `x-webhook-events` extension at the root of
      this document catalogues every event with its `data` schema and PII rules,
      and the `## Webhooks` section of the introduction is the narrative guide.
      Provider-owned subscriptions receive `MedexWebhookEventType` events (Med
      Xchange case lifecycle) and their body is the bare entity payload, with no
      envelope. Both kinds may also receive the synthetic `webhook.test` event
      from the "send test event" action, whose body is `{ event, timestamp, data
      }`.
  - name: Med Cases
    description: Med Xchange cases and their nested referrals to medical providers
  - name: Med Appointments
    description: >-
      Appointments on a Med Xchange case. Providers read and write their own;
      firms read.
  - name: Med Billing
    description: >-
      Billing lines (visit fees, procedures, other charges) attached to an
      appointment.
  - name: MedEx Documents
    description: Med Xchange case document listing and download
  - name: SSO
    description: >-
      Partner-initiated Med Xchange single sign-on. Requires a provider-scoped
      API key (`cxpp_*`).
  - name: Salesforce
    description: Org-facing endpoints consumed by the CaseXchange managed package
  - name: Extension
    description: Telemetry reported by the CaseXchange browser extension
  - name: Health
    description: Unauthenticated liveness probe
  - name: Med Pharmacy Ledger
    description: >-
      Pharmacy billing ledger on a pharmacy referral: dispensed fills (what the
      pharmacy billed), payments and adjustments against them, and the
      pharmacy's own reported balance. Pharmacy provider keys write; firm keys
      and the referral's pharmacy read.


      Happy path for a pharmacy integration:

      1. A firm refers a case to your pharmacy: you receive referral.sent (it
      carries medCaseId and the referral id), or list them with GET
      /med-cases/{id}/referrals.

      2. Acknowledge and accept the referral (POST
      /med-cases/{id}/referrals/{referralId}/acknowledge, then /accept). Billing
      opens once the referral is accepted.

      3. POST .../pharmacy/fills for each dispensed fill (optionally linked to a
      prescription from GET /med-cases/{caseId}/prescriptions).

      4. POST .../pharmacy/transactions for each payment, adjustment, write-off,
      refund or reversal.

      5. POST .../pharmacy/balance-snapshots with the balance your system
      reports, and read GET .../pharmacy/balance to compare.


      Always send an externalId so retries are safe. Money in responses is
      always a decimal string with exactly two fractional digits (e.g. "40.00",
      "-15.00"), whatever precision was sent. pharmacy_* webhooks, and
      prescription.* webhooks about prescriptions routed to a pharmacy, go only
      to that pharmacy practice's subscriptions (never to the firm), and they
      echo the practice's own writes, so an integration should ignore events for
      records it just wrote (match on externalId).
  - name: Med Prescriptions
    description: >-
      Prescriptions routed to a pharmacy on a Med Xchange case, so a pharmacy
      integration can link a fill to a prescription.
paths:
  /med-cases/{caseId}/referrals/{referralId}/pharmacy/fills/{fillId}:
    get:
      tags:
        - Med Pharmacy Ledger
      summary: Get a pharmacy fill
      description: >-
        Firm keys read their own cases; a provider key reads its own referral.
        Admin keys receive 403 wrong_scope. Restricted-access practices receive
        403 restricted_access. Reads are allowed in any referral status.
        Includes the fill's transactions and its own totals (billed, paid,
        adjustments, computedBalance).
      operationId: getPharmacyFill
      parameters:
        - in: path
          name: caseId
          required: true
          schema:
            type: string
            format: uuid
          description: >-
            Med case UUID: medCaseId in the referral.* webhook payload, or the
            id from GET /med-cases.
        - in: path
          name: referralId
          required: true
          schema:
            type: string
            format: uuid
          description: >-
            Med referral UUID: the referral of this case to your pharmacy
            practice. Find it with GET /med-cases/{id}/referrals, or take it
            from the referral.* webhook payload.
        - in: path
          name: fillId
          required: true
          schema:
            type: string
            format: uuid
          description: Pharmacy fill UUID
      responses:
        '200':
          description: Fill with its transactions and totals
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/PharmacyFillDetail'
                  meta:
                    type: object
                    properties:
                      requestId:
                        type: string
                        example: req_f2dbe1d3f6ad4a7bb5b4c9f2
                      timestamp:
                        type: string
                        format: date-time
                    required:
                      - requestId
                      - timestamp
                required:
                  - data
                  - meta
        '400':
          description: >-
            Request validation failed (`validation_error`; error.details is `{
            endpoint, errors: [{ path, message }] }`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PharmacyLedgerError'
        '401':
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                  meta:
                    type: object
                    properties:
                      requestId:
                        type: string
                        example: req_f2dbe1d3f6ad4a7bb5b4c9f2
                      timestamp:
                        type: string
                        format: date-time
                    required:
                      - requestId
                      - timestamp
                required:
                  - error
                  - meta
        '403':
          description: >-
            `wrong_scope` (admin key), `restricted_access`, or the API key tier
            does not permit this action.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PharmacyLedgerError'
        '404':
          description: >-
            Case, referral or record not found. A referral that belongs to
            another practice is reported as 404.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PharmacyLedgerError'
      security:
        - apiKeyAuth: []
components:
  schemas:
    PharmacyFillDetail:
      allOf:
        - $ref: '#/components/schemas/PharmacyFill'
        - type: object
          properties:
            transactions:
              type: array
              items:
                $ref: '#/components/schemas/PharmacyTransaction'
              description: Transactions linked to this fill, oldest first.
            totals:
              allOf:
                - $ref: '#/components/schemas/PharmacyBalance'
              description: >-
                Ledger totals for this fill alone, by the same rules as the
                referral balance. billed is "0.00" for a voided fill. `reported`
                and `difference` are always null (balance snapshots are per
                referral, not per fill).
          required:
            - transactions
            - totals
    PharmacyLedgerError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: >-
                Machine-readable code. New with the pharmacy ledger:
                pharmacy_only, referral_not_writable, fill_voided,
                external_id_conflict, immutable_field, invalid_reference. Others
                are the standard public API codes (validation_error, not_found,
                wrong_scope, restricted_access, ...).
            message:
              type: string
            details:
              type: object
              additionalProperties: true
              description: Present for the codes listed above.
          required:
            - code
            - message
        meta:
          type: object
          properties:
            requestId:
              type: string
              example: req_f2dbe1d3f6ad4a7bb5b4c9f2
            timestamp:
              type: string
              format: date-time
          required:
            - requestId
            - timestamp
      required:
        - error
        - meta
      description: >-
        Standard error envelope. `error.details` is present for some codes:
        `validation_error` carries `{ endpoint, errors: [{ path, message }] }`
        (path is the dotted field path, e.g. "body.amount" or "query.limit"),
        `external_id_conflict` carries `{ existingId }`, `invalid_reference`
        carries `{ field }`, `immutable_field` carries `{ fields }`,
        `referral_not_writable` carries `{ status, isArchived }`.
    PharmacyFill:
      type: object
      description: >-
        One dispensed prescription fill on a pharmacy referral. billedAmount is
        what the pharmacy billed for it.
      properties:
        id:
          type: string
          format: uuid
        medCaseId:
          type: string
          format: uuid
        medReferralId:
          type: string
          format: uuid
        externalId:
          type: string
          nullable: true
          maxLength: 200
          description: The externalId sent when the record was created, or null.
        rxNumber:
          type: string
          maxLength: 50
        fillDate:
          type: string
          format: date
          description: YYYY-MM-DD, a real calendar date.
        ndc:
          type: string
          nullable: true
          pattern: ^\d{11}$
          description: 11-digit NDC with hyphens and spaces removed.
        drugName:
          type: string
          maxLength: 500
        quantity:
          type: string
          nullable: true
          description: Decimal string with up to 3 fractional digits, e.g. "30.000".
        daysSupply:
          type: integer
          nullable: true
          minimum: 0
          maximum: 3650
        prescriberName:
          type: string
          nullable: true
          maxLength: 300
        billedAmount:
          type: string
          pattern: ^\d{1,10}(\.\d{1,2})?$
          description: Non-negative decimal string, e.g. "40.00".
        prescriptionId:
          type: string
          format: uuid
          nullable: true
          description: >-
            Routed prescription this fill dispenses, if linked. Find ids with
            GET /med-cases/{caseId}/prescriptions.
        claimDocumentId:
          type: string
          format: uuid
          nullable: true
          description: Case document (e.g. a claim form) attached to this fill, if any.
        notes:
          type: string
          nullable: true
          maxLength: 2000
        voidedAt:
          type: string
          format: date-time
          nullable: true
          description: >-
            Set when the fill was voided. A voided fill stays listed (unless
            includeVoided=false) but no longer counts toward billed.
        voidReason:
          type: string
          nullable: true
        createdVia:
          type: string
          description: How the record was created, e.g. "api" or "web".
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    PharmacyTransaction:
      type: object
      description: >-
        A payment, adjustment, write-off, refund or reversal against the
        pharmacy's billing. Immutable once recorded except payer, reference,
        note and fillId: to correct an amount, post a REVERSAL.
      properties:
        id:
          type: string
          format: uuid
        medCaseId:
          type: string
          format: uuid
        medReferralId:
          type: string
          format: uuid
        externalId:
          type: string
          nullable: true
          maxLength: 200
          description: The externalId sent when the record was created, or null.
        fillId:
          type: string
          format: uuid
          nullable: true
          description: >-
            Fill this transaction applies to; null when it applies to the
            referral as a whole.
        type:
          type: string
          enum:
            - PAYMENT
            - ADJUSTMENT
            - WRITE_OFF
            - REFUND
            - REVERSAL
          description: >-
            ADJUSTMENT and WRITE_OFF count under `adjustments`; PAYMENT and
            REFUND count under `paid`. A REVERSAL counts under the same bucket
            as the transaction it reverses. A REVERSAL with no
            reversesTransactionId, or whose target is not in the ledger, counts
            under paid. Sign convention: a positive amount reduces the balance,
            a negative amount increases it. The API stores the amount exactly as
            sent and does not check the sign against the type, so post the sign
            you mean. Examples: a payment received is PAYMENT "40.00"; a payer
            taking a payment back is PAYMENT "-40.00" or REVERSAL "-40.00"; a
            contractual write-down is ADJUSTMENT "15.00"; a write-off is
            WRITE_OFF with a positive amount; a refund the pharmacy sends out is
            REFUND "-10.00" (REFUND "10.00" would reduce the balance).
        amount:
          type: string
          pattern: ^-?\d{1,10}(\.\d{1,2})?$
          description: >-
            Sign convention: a positive amount reduces the balance, a negative
            amount increases it. The API stores the amount exactly as sent and
            does not check the sign against the type, so post the sign you mean.
            Examples: a payment received is PAYMENT "40.00"; a payer taking a
            payment back is PAYMENT "-40.00" or REVERSAL "-40.00"; a contractual
            write-down is ADJUSTMENT "15.00"; a write-off is WRITE_OFF with a
            positive amount; a refund the pharmacy sends out is REFUND "-10.00"
            (REFUND "10.00" would reduce the balance).


            | What happened | type | amount |

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

            | Payment received | PAYMENT | "40.00" |

            | Payer took a payment back | PAYMENT or REVERSAL | "-40.00" |

            | Contractual write-down | ADJUSTMENT | "15.00" |

            | Balance written off | WRITE_OFF | "25.00" |

            | Refund the pharmacy sent out | REFUND | "-10.00" |
        transactionDate:
          type: string
          format: date
          description: YYYY-MM-DD, a real calendar date.
        payer:
          type: string
          nullable: true
          maxLength: 300
        reference:
          type: string
          nullable: true
          maxLength: 200
          description: Check number, EOB or remittance reference.
        reversesTransactionId:
          type: string
          format: uuid
          nullable: true
          description: 'Set only on a REVERSAL: the transaction being reversed.'
        note:
          type: string
          nullable: true
          maxLength: 2000
        createdVia:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    PharmacyBalance:
      type: object
      description: >-
        Ledger totals for a pharmacy referral. computedBalance = billed (sum of
        billedAmount over non-voided fills) − paid − adjustments. A positive
        transaction amount reduces the balance; a negative one (a take-back)
        increases it. `reported` is the latest snapshot by asOf (ties broken by
        creation time) and difference = reported.balance − computedBalance, so a
        positive difference means the pharmacy reports more outstanding than the
        ledger computes. A REVERSAL is bucketed with the transaction it
        reverses; one with no reversesTransactionId, or whose target is not in
        the ledger, counts as paid. All money values are decimal strings with
        two fractional digits.
      properties:
        billed:
          type: string
          description: Sum of billedAmount over non-voided fills.
        paid:
          type: string
          description: >-
            Sum of PAYMENT and REFUND amounts, plus REVERSALs of those, signed
            as posted. A REVERSAL with no reversesTransactionId, or whose target
            is not in the ledger, counts under paid.
        adjustments:
          type: string
          description: >-
            Sum of ADJUSTMENT and WRITE_OFF amounts, plus REVERSALs of those,
            signed as posted.
        computedBalance:
          type: string
          description: billed − paid − adjustments. May be negative (overpaid).
        reported:
          type: object
          nullable: true
          description: Latest reported balance snapshot; null when none has been posted.
          properties:
            balance:
              type: string
            asOf:
              type: string
              format: date-time
            externalId:
              type: string
              nullable: true
          required:
            - balance
            - asOf
            - externalId
        difference:
          type: string
          nullable: true
          description: >-
            reported.balance − computedBalance; null when there is no reported
            balance.
        fillCount:
          type: integer
          description: Number of non-voided fills.
        transactionCount:
          type: integer
          description: Number of transactions, including reversals.
      required:
        - billed
        - paid
        - adjustments
        - computedBalance
        - reported
        - difference
        - fillCount
        - transactionCount
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        Public API key: `cxp_<hex>` (firm-scoped) or `cxpp_<hex>`
        (provider-scoped). Required on every request.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.