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

# Upsert Matters

> Creates or updates matters under existing clients using your firm's client and matter identifiers. Submit 1 to 10,000 matters per request. Uses the delimiter configured for the API token's active workspace, or `.` when none is configured. If the workspace requires an explicit delimiter and none is configured, the whole request returns HTTP 400 with delimiter_required.

### Permissions

Requires **Client matters admin** (`write:client_matters`). Sending `allowed` also requires `use:disallowed_client_matters`, even when the value is `true`; otherwise the whole request returns HTTP 403.

### Before you start

Create the parent clients first. This endpoint never creates clients. If the workspace has matters created with full names, set its delimiter to match those names before using this endpoint. For an existing name such as `10022421-M001`, configure `-` and send `client_id: "10022421"` with `matter_id: "M001"`. The API uses the delimiter configured for the token's active workspace. If it is unset or empty, it uses `.` unless the workspace requires an explicit delimiter. In that case, the whole request returns HTTP 400 with `delimiter_required`; configure a delimiter before retrying. Neither path changes workspace settings.

### Matching and results

The stored name is `client_id` + the effective delimiter + `matter_id`. For client `10022421` and matter `M001`, the default produces `10022421.M001`; a configured `-` produces `10022421-M001`. Matching uses the identifier pair first, then the stored name. Matching soft-deleted matters are restored. Restoring a client does not restore its matters.

New matters appear in `created`. Matched matters appear in `updated`, including when their fields do not change. HTTP 200 can include row failures in `errors`; valid rows still succeed. Duplicate pairs and name collisions are row errors. Each successful row returns the full result. Use smaller batches to limit response size, especially with long descriptions or notices.

See the [Client Matters guide](/guides/client_matters) for examples, field update rules, and migration steps.


## OpenAPI

````yaml /client_matters_api.json post /api/v2/matters
openapi: 3.0.1
info:
  title: Harvey Client Matter API
  description: >-
    Create and update clients and matters by your firm identifiers. Retrieve and
    delete client matters through the v1 endpoints.
  version: 1.0.0
  contact:
    email: support@harvey.ai
servers:
  - url: https://api.harvey.ai
    description: Harvey API server
security: []
paths:
  /api/v2/matters:
    post:
      tags:
        - Upsert
      summary: Upsert Matters
      description: >-
        Creates or updates matters under existing clients using your firm's
        client and matter identifiers. Submit 1 to 10,000 matters per request.
        Uses the delimiter configured for the API token's active workspace, or
        `.` when none is configured. If the workspace requires an explicit
        delimiter and none is configured, the whole request returns HTTP 400
        with delimiter_required.
      operationId: upsertMatters
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpsertMattersRequest'
            example:
              matters:
                - client_id: '10022421'
                  matter_id: M001
                  matter_name: Acme Acquisition
                  description: Acquisition due diligence
                  notice: Confirm the engagement before selecting this matter.
      responses:
        '200':
          description: Per-row results for the submitted matters.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpsertMattersResponse'
              examples:
                created:
                  summary: Created row
                  value:
                    created:
                      - client_id: '10022421'
                        matter_id: M001
                        matter_name: Acme Acquisition
                        description: Acquisition due diligence
                        notice: Confirm the engagement before selecting this matter.
                        client_name: Acme Corp
                        allowed: true
                        harvey_id: 018d856f-0664-772c-9ed9-b2316fd881d3
                    updated: []
                    errors: []
                updated:
                  summary: Existing row, including a repeated request
                  value:
                    created: []
                    updated:
                      - client_id: '10022421'
                        matter_id: M001
                        matter_name: Acme Acquisition
                        description: Acquisition due diligence
                        notice: Confirm the engagement before selecting this matter.
                        client_name: Acme Corp
                        allowed: true
                        harvey_id: 018d856f-0664-772c-9ed9-b2316fd881d3
                    errors: []
                partialSuccess:
                  summary: Valid row succeeds while another row fails
                  value:
                    created:
                      - client_id: '10022421'
                        matter_id: M001
                        matter_name: Acme Acquisition
                        description: Acquisition due diligence
                        notice: Confirm the engagement before selecting this matter.
                        client_name: Acme Corp
                        allowed: true
                        harvey_id: 018d856f-0664-772c-9ed9-b2316fd881d3
                    updated: []
                    errors:
                      - client_id: UNKNOWN
                        matter_id: M002
                        error: client_not_found
                        detail: No client with this identifier exists in the workspace
        '400':
          description: >-
            The request was rejected as a whole: invalid_request,
            too_many_items, or delimiter_required when the workspace requires an
            explicit delimiter but none is configured.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientMatterBatchError'
              examples:
                invalidRequest:
                  summary: Invalid request body
                  value:
                    error: invalid_request
                    detail: Invalid request body
                    validation_errors:
                      - loc:
                          - matters
                        type: too_short
                delimiterRequired:
                  summary: Workspace requires an explicit delimiter
                  value:
                    error: delimiter_required
                    detail: >-
                      Set a client matter delimiter for this workspace before
                      upserting matters
        '401':
          description: >-
            Missing, invalid, or expired API token, or missing
            write:client_matters permission.
        '403':
          description: >-
            The request sends allowed without use:disallowed_client_matters
            permission. The whole batch is rejected.
        '429':
          description: >-
            Rate limit exceeded. These endpoints share the client matter rate
            limit, which defaults to 150 requests per minute per workspace.
        '500':
          description: Internal server error.
      security:
        - bearerAuth: []
components:
  schemas:
    UpsertMattersRequest:
      type: object
      required:
        - matters
      properties:
        matters:
          type: array
          minItems: 1
          maxItems: 10000
          items:
            $ref: '#/components/schemas/UpsertMatterInput'
    UpsertMattersResponse:
      type: object
      required:
        - created
        - updated
        - errors
      properties:
        created:
          type: array
          items:
            $ref: '#/components/schemas/MatterResult'
        updated:
          type: array
          description: >-
            Includes restored matters and matched matters whose fields did not
            change.
          items:
            $ref: '#/components/schemas/MatterResult'
        errors:
          type: array
          items:
            $ref: '#/components/schemas/MatterError'
    ClientMatterBatchError:
      type: object
      description: Body of a 400 response from `/api/v2/clients` and `/api/v2/matters`.
      required:
        - error
        - detail
      properties:
        error:
          $ref: '#/components/schemas/ClientMatterBatchErrorCode'
        detail:
          type: string
          description: Human-readable explanation.
        validation_errors:
          type: array
          description: Present for `invalid_request`. Field values are never echoed.
          items:
            $ref: '#/components/schemas/ClientMatterFieldError'
    UpsertMatterInput:
      type: object
      description: >-
        One matter keyed on the firm's client and matter identifiers. Omit
        `matter_name`, `description`, or `notice` to leave the stored value
        unchanged. Send null or a blank string to clear it. Values are stripped.
      required:
        - client_id
        - matter_id
      properties:
        client_id:
          type: string
          description: >-
            The firm's client identifier as sent to `POST /api/v2/clients`. Not
            the Harvey client UUID that `GET /api/v1/client_matters` returns in
            its own `client_id` field. Must resolve to a live client; this
            endpoint never creates clients. Empty after stripping fails per row
            with `invalid_client_id`; an unknown client fails per row with
            `client_not_found`.
        matter_id:
          type: string
          description: >-
            The firm's matter identifier. `GET /api/v1/client_matters` returns
            this value as `matter_number`. Empty after stripping fails per row
            with `invalid_matter_id`.
        matter_name:
          type: string
          nullable: true
          description: >-
            Matter label. Over 256 characters fails per row with
            `invalid_matter_name`.
        description:
          type: string
          nullable: true
          description: >-
            Matter description. More than 200 characters after trimming
            whitespace fails per row with description_too_long.
        allowed:
          type: boolean
          description: >-
            Whether users can select the matter. Omit to leave the stored value
            unchanged; a new matter defaults to true. Sending `allowed` requires
            the disallowed-matters permission; without it the whole batch is
            403. Must be a JSON boolean; null fails the whole request with
            `invalid_request`.
        notice:
          type: string
          nullable: true
          description: >-
            Selection notice shown to users when they pick the matter, at most
            2000 characters after stripping. A longer value fails per row with
            `notice_too_long`. Omit to leave the stored notice unchanged. Send
            null or a blank string to clear it. A whitespace-only value clears
            and never fails. Requires the same client matters write permission
            as the route.
    MatterResult:
      type: object
      required:
        - client_id
        - matter_id
        - client_name
        - matter_name
        - description
        - allowed
        - notice
        - harvey_id
      properties:
        client_id:
          type: string
          description: >-
            The stored, stripped firm client identifier. Not a Harvey UUID; see
            `harvey_id`.
        matter_id:
          type: string
          description: The stored, stripped firm matter identifier.
        client_name:
          type: string
          nullable: true
          description: The parent client's label.
        matter_name:
          type: string
          nullable: true
          description: >-
            The matter label. Not the stored client matter name, which this
            endpoint does not return; `GET /api/v1/client_matters` returns it as
            `name` and `DELETE /api/v1/client_matters` accepts it.
        description:
          type: string
          nullable: true
        allowed:
          type: boolean
        notice:
          type: string
          nullable: true
          description: >-
            The body of the matter's live notice after the write; null when the
            matter has none.
        harvey_id:
          type: string
          format: uuid
          description: >-
            The Harvey matter UUID, stable for the life of the matter. For
            example, pass this value as the client_matter_id form field in POST
            /api/v2/completion to associate an Assistant completion with the
            matter. GET /api/v1/client_matters returns the same value as id.
            DELETE /api/v1/client_matters accepts the stored matter name
            instead. Use your client_id and matter_id for later upserts.
    MatterError:
      type: object
      required:
        - client_id
        - matter_id
        - error
        - detail
      properties:
        client_id:
          type: string
          description: Echoes the raw request value.
        matter_id:
          type: string
          description: Echoes the raw request value.
        error:
          $ref: '#/components/schemas/MatterErrorCode'
        detail:
          type: string
          description: Human-readable explanation.
    ClientMatterBatchErrorCode:
      type: string
      description: >-
        Stable code for a rejected request. too_many_items: more than 10,000
        rows. invalid_request: invalid request shape, empty batch, missing
        required field, wrong type, allowed set to null, or a token user that
        cannot be found. delimiter_required: POST /api/v2/matters requires an
        explicit delimiter for the active workspace, but none is configured.
        Other workspaces use `.` when no delimiter is configured. POST
        /api/v2/clients does not emit delimiter_required. Duplicate identifiers
        and length limits on text fields produce per-row errors instead.
      enum:
        - too_many_items
        - invalid_request
        - delimiter_required
    ClientMatterFieldError:
      type: object
      required:
        - loc
        - type
      properties:
        loc:
          type: array
          description: >-
            Path to the offending field. Each entry is a string naming a field
            or an integer indexing an array.
          items: {}
        type:
          type: string
          description: The validation failure kind, for example `missing` or `string_type`.
    MatterErrorCode:
      type: string
      description: >-
        Stable code for a matter row that was not stored. Errors cover empty
        identifiers, labels over 256 characters, descriptions over 200
        characters, notices over 2000 characters, missing clients, and conflicts
        between identifiers. name_too_long means the derived client matter name
        exceeds 128 characters. name_failed_workspace_regex means that name does
        not match the workspace format. duplicate_pair means the client and
        matter identifiers repeat an earlier pair in the batch. name_collision
        means different pairs produce the same derived name. identity_conflict
        means an existing matter with that name belongs to a different pair.
        These name checks apply to the derived name, not the matter_name label.
        Each failed row reports one error.
      enum:
        - invalid_client_id
        - invalid_matter_id
        - invalid_matter_name
        - description_too_long
        - client_not_found
        - name_too_long
        - name_failed_workspace_regex
        - identity_conflict
        - duplicate_pair
        - name_collision
        - invalid_notice
        - notice_too_long
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Token

````

## Related topics

- [Upsert Clients](/api-reference/upsert/upsert-clients.md)
- [Get Client Matters](/api-reference/retrieve/get-client-matters.md)
- [Audit Logs](/guides/audit_logs.md)
