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

# Set up the catalog (bulk)

> Declaratively merge a whole catalog in one call — the primitive for a one-off "set up my project's preferences" script. Each item is upserted by its natural key (`channel, topic, event, medium`): new entries are inserted, existing ones have their label and default updated. Entries **not** in the array are left untouched, unless `prune=true`.

Returns the full resulting catalog.

Declaratively merge a whole catalog in one call — the primitive for a one-off "set up my project's preferences" script. Each item is upserted by its natural key (`channel, topic, event, medium`): new entries are inserted, existing ones have their label and default updated. Entries **not** in the array are left untouched.

<Note>
  Pass `?prune=true` to also **delete** entries absent from the array, making it your entire desired catalog. Pruning un-catalogs a `(target, medium)`, which turns a non-`in_app` medium off for recipients relying on the default — so it is opt-in. Deliberate one-off removals go through [delete](/api-reference/endpoint/preferences/delete-project-preference).
</Note>


## OpenAPI

````yaml PUT /preferences
openapi: 3.0.3
info:
  title: Bodhveda Notifications API
  version: '1.0'
  description: API for sending notifications to recipients or broadcasting to all.
servers: []
security: []
paths:
  /preferences:
    put:
      tags:
        - Project Preferences
      summary: Bulk upsert the catalog
      description: >-
        Declaratively merge a whole catalog in one call — the primitive for a
        one-off "set up my project's preferences" script. Each item is upserted
        by its natural key (`channel, topic, event, medium`): new entries are
        inserted, existing ones have their label and default updated. Entries
        **not** in the array are left untouched, unless `prune=true`.


        Returns the full resulting catalog.
      operationId: upsertProjectPreferences
      parameters:
        - name: prune
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: >-
            When `true`, catalog entries absent from the array are **deleted**,
            making the array the entire desired catalog. Pruning un-catalogs a
            `(target, medium)`, which turns a non-`in_app` medium off for
            recipients relying on the default — so it is opt-in.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: '#/components/schemas/CreateProjectPreferencePayload'
      responses:
        '200':
          description: The full resulting catalog.
          content:
            application/json:
              examples:
                Upserted:
                  summary: Upserted Response
                  value:
                    message: Project preferences upserted
                    data:
                      - id: 12
                        project_id: 1
                        target:
                          channel: digest
                          topic: none
                          event: sent
                        medium: email
                        default_enabled: true
                        label: Daily digest
                        created_at: '2025-11-07T05:31:56Z'
                        updated_at: '2025-11-07T05:31:56Z'
      security:
        - BearerAuthWithAPIKeyWithFullAcessScope: []
      x-codeSamples:
        - lang: curl
          source: |-
            curl -X PUT https://api.bodhveda.com/preferences \
              -H "Authorization: Bearer bv_xxxxxxxxx" \
              -H "Content-Type: application/json" \
              -d '[
                { "channel": "digest", "topic": "none", "event": "sent", "medium": "email", "label": "Daily digest", "default_enabled": true }
              ]'
components:
  schemas:
    CreateProjectPreferencePayload:
      type: object
      properties:
        channel:
          type: string
          description: Broad category (e.g. "digest").
        topic:
          type: string
          description: >-
            Subcategory. `any` matches all topics under the channel/event;
            `none` is the base channel/event.
        event:
          type: string
          description: Event that triggers the notification (e.g. "sent").
        medium:
          type: string
          description: The medium this catalog entry gates. Defaults to `in_app`.
          enum:
            - in_app
            - email
          default: in_app
        label:
          type: string
          description: Human-readable label.
        default_enabled:
          type: boolean
          description: >-
            The project-level default for recipients who have set no preference
            of their own.
      required:
        - channel
        - topic
        - event
        - label
        - default_enabled
  securitySchemes:
    BearerAuthWithAPIKeyWithFullAcessScope:
      type: http
      scheme: bearer
      bearerFormat: APIKey
      description: >-
        Bearer authentication header of the form `Bearer bv_xxxxxxxxx`, where
        `bv_xxxxxxxxx` is your **API Key** with `Full access` scope.

````