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

# Add Items to a Proposal

> POST /public/proposals/{ProposalId}/items — Adds one or more catalog, custom, labor, or fee items to a proposal area option.

Adds items to a proposal. Each entry in `Items` names a source item, its type, and the area options to put it in — one item is created per area option the entry targets. Area option ids come from [get proposal](/api-reference/proposals/get-proposal); see [reading proposal items](/concepts/proposal-item-model) for where to find them.

The response is the standard [`List<PublicAreaItemModel>`](/api-reference/proposals/items/overview#response-shape) array of created items.

## Item types and their ids

`CatalogItemId` means a different thing for each `ItemType`, and each type takes the item's name in a different field:

| `ItemType`   | `CatalogItemId` refers to                                                                   | Send the name in | Read back as |
| ------------ | ------------------------------------------------------------------------------------------- | ---------------- | ------------ |
| `Part`       | A catalog item, searchable with [search catalog items](/api-reference/catalog/search-items) | `Model`          | `model`      |
| `CustomItem` | A custom item in the account's library                                                      | `Model`          | `model`      |
| `Labor`      | A labor item in the account's library                                                       | `Name`           | `name`       |
| `Fee`        | A fee item in the account's library                                                         | `Name`           | `model`      |

Supplying the wrong one for the type — `Name` on a `Part`, `Model` on a `Labor` item — returns `400`. `SupplierId` applies to `Part` items only; omit it to use the part's default supplier.

<Warning>
  Only `Part` ids are discoverable through the public API today. There is no public endpoint that lists the account's labor, custom, or fee items, so ids for those types have to come from elsewhere until one ships.
</Warning>

## Pricing a new item

Omit `SellPrice` and `SellPercentage` and the item inherits the catalog item's default sell price; the same applies to `Cost` and `CostPercentage`. Supply one of each pair to override — an absolute amount, or a whole-percent value plus its basis (`15` means 15%).

Send exactly one member of each pair. Unlike [update sell price](/api-reference/proposals/items/update-item-sell-price) and [update cost](/api-reference/proposals/items/update-item-cost), this endpoint does not check the pair for you, so a mistake here fails quietly instead of returning a `400`. Send both members and the percentage is used, ignoring the absolute amount. Send a percentage without its basis and there is nothing to calculate it from, so the item is created with no price at all.

Per-item `SetDefault` saves this item's sell price as the default on the catalog item, so later proposals start with it. It persists nothing else on the item.

<Note>
  This endpoint is not idempotent. A retried call adds another copy of the item rather than reconciling with the first, so confirm with [get proposal](/api-reference/proposals/get-proposal) before repeating a request that may have already landed.
</Note>

## Nesting and attachments

`ParentProposalItemIds` nests the new item under an existing one. Matching is per area option: in each target option, the new item nests under whichever listed parent lives in that same option, and sits at top level where none does. Nesting is read back as `parentId`, and children's prices can be folded into the parent's displayed total with [set combined pricing](/api-reference/proposals/items/set-item-combined-pricing).

`IncludeAttachments` is separate — it adds the items the catalog item is configured to bring along, nested under the new item. **It defaults to `true`**; pass `false` to skip them. Attachment items are not part of this call's response and may take a moment to appear, so re-fetch the proposal to read them.

## Adding several items at once

`items` takes any number of entries, and they do not have to be the same type. This call adds a `Part` and a `Labor` line to area option `201`, with the part nested under the existing item `5000`:

```json theme={null}
{
  "items": [
    {
      "catalogItemId": 88213,
      "itemType": "Part",
      "parentProposalItemIds": [5000],
      "proposalAreaOptions": [{ "proposalAreaOptionId": 201, "quantity": 2 }]
    },
    {
      "catalogItemId": 730,
      "itemType": "Labor",
      "name": "Installation Labor",
      "proposalAreaOptions": [{ "proposalAreaOptionId": 201, "quantity": 5.5 }]
    }
  ]
}
```

The part nests under `5000` because that item sits in option `201`; where none of the listed parents lives in a target option, the new item is added at that option's top level instead. The labor line names no parent, so it starts at top level. Each entry is created once per area option it lists, so adding further `proposalAreaOptions` entries places copies across several options in the same call.

<Snippet file="hmac-signing.mdx" />


## OpenAPI

````yaml POST /public/proposals/{ProposalId}/items
openapi: 3.1.1
info:
  title: Portal.io Public API
  version: '1.0'
servers:
  - url: https://sandbox.api.portal.io/
security:
  - mssApiAppId: []
    mssApiUserKey: []
    mssCustomDate: []
    mssSignature: []
paths:
  /public/proposals/{ProposalId}/items:
    post:
      tags:
        - Proposals/Item
      summary: Add Proposal Items
      description: >-
        Adds one or more items to a proposal, creating one item per area option
        each entry targets. In each of those area options, the new item nests
        under whichever item from parentProposalItemIds sits in that same area
        option, and is added at top level when none does. Repeating a call adds
        further copies - the endpoint is not idempotent. Returns the created
        items using the standard proposal-item response shape.
      operationId: postPublicProposalsByProposalIdItems
      parameters:
        - name: ProposalId
          in: path
          required: true
          explode: true
          schema:
            type: integer
            description: Unique ID of the proposal (not the proposal number).
            format: int32
            x-default: 11538
        - $ref: '#/components/parameters/Accept'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicProposalItemAddRequest'
            example:
              items:
                - catalogItemId: 86570
                  itemType: Part
                  proposalAreaOptions:
                    - proposalAreaOptionId: 57002
                      quantity: 9.6
                  supplierId: 92492
                  sellPrice: 583.78
                  sellPercentage: 52.31
                  sellPercentageBasis: PartTotal
                  cost: 759.6
                  costPercentage: 63.6
                  costPercentageBasis: PartTotal
                  model: Amp
                  name: Installation Labor
                  msrp: 901.23
                  isRecurringService: false
                  isTaxExempt: false
                  setDefault: false
                  parentProposalItemIds:
                    - 77287
                    - 10024
              includeAttachments: true
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: ListOfPublicAreaItemModel
                type: array
                items:
                  $ref: '#/components/schemas/PublicAreaItemModel'
              example:
                - id: 81376
                  parentId: 42344
                  itemType: Part
                  referencedItemId: 63140
                  createdDate: '2030-08-21T00:22:19Z'
                  lastModifiedDate: '2031-02-06T00:22:19Z'
                  brand: Sonos
                  model: Amp
                  name: Installation Labor
                  shortDescription: Sonos Amp
                  clientNote: Mounted in the rack behind the display.
                  imageUrl: https://images.portal.io/catalog/sonos-amp.jpg
                  msrp: 901.23
                  sellPrice: 583.78
                  cost: 759.6
                  costUpdateDate: '2030-11-10T00:22:19Z'
                  supplier: Sonos Inc.
                  quantity: 9.6
                  total:
                    amount: 982.49
                    currency:
                      code: USD
                      symbol: $
                    isCombinedPrice: false
                  isTaxExempt: false
                  isRecurringService: false
                  linkedOrders:
                    - orderId: 21802
                      orderNumber: 182
                      orderNumberSuffix: A
                      supplier: Sonos Inc.
                      supplierRef: SN-10420
                      orderName: Living Room Sonos Amp
                      orderStatus: Submitted
        '400':
          description: >-
            Invalid item payload, or name/model supplied on an item type that
            does not accept it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                responseStatus:
                  errorCode: BadRequest
                  message: >-
                    Invalid item payload, or name/model supplied on an item type
                    that does not accept it.
        '401':
          description: >-
            Not Authorized. Ensure a valid session cookie or HMAC authentication
            headers are provided.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                responseStatus:
                  errorCode: Unauthorized
                  message: >-
                    Not Authorized. Ensure a valid session cookie or HMAC
                    authentication headers are provided.
        '402':
          description: >-
            The dealer's subscription is inactive or expired. An active
            subscription is required to use this endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                responseStatus:
                  errorCode: PaymentRequired
                  message: >-
                    The dealer's subscription is inactive or expired. An active
                    subscription is required to use this endpoint.
        '403':
          description: You do not have permission for this action.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                responseStatus:
                  errorCode: Forbidden
                  message: You do not have permission for this action.
        '404':
          description: Proposal or proposal area option not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                responseStatus:
                  errorCode: NotFound
                  message: Proposal or proposal area option not found.
        '409':
          description: Proposal state prevents editing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                responseStatus:
                  errorCode: Conflict
                  message: Proposal state prevents editing
components:
  parameters:
    Accept:
      name: Accept
      in: header
      description: Accept Header
      required: true
      schema:
        enum:
          - application/json
        type: string
  schemas:
    PublicProposalItemAddRequest:
      title: PublicProposalItemAddRequest
      required:
        - items
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/PublicProposalItemModel'
          description: New items to add to the proposal. At least one item is required.
        includeAttachments:
          type: boolean
          description: >-
            (Optional) Also add the items attached to each newly created item,
            nested under it. Defaults to true - pass false to skip. Attachment
            items are not part of this call's response and may take a moment to
            appear; re-fetch the proposal to read them.
      description: Proposals Service
    PublicAreaItemModel:
      title: PublicAreaItemModel
      required:
        - id
        - referencedItemId
        - createdDate
        - quantity
      type: object
      properties:
        id:
          type: integer
          description: Unique numeric identifier of the proposal item
          format: int32
        parentId:
          type: integer
          description: >-
            Identifier of the parent item when this item is nested under another
            item
          format: int32
        itemType:
          $ref: '#/components/schemas/ItemTypes'
        referencedItemId:
          type: integer
          description: >-
            Identifier of the catalog, labor, custom, or fee item this proposal
            item was created from
          format: int32
        createdDate:
          type: string
          description: UTC timestamp when the proposal item was created
          format: date-time
        lastModifiedDate:
          type: string
          description: UTC timestamp when the proposal item was last modified
          format: date-time
        brand:
          type: string
          description: Brand name of the item
        model:
          type: string
          description: Model value of the item
        name:
          type: string
          description: Name of a Labor item
        shortDescription:
          type: string
          description: Short description shown for the proposal item
        clientNote:
          type: string
          description: Client-facing note for the proposal item
        imageUrl:
          type: string
          description: Image URL of the item
        msrp:
          type: number
          description: MSRP amount of the item
          format: double
        sellPrice:
          type: number
          description: Sell price for the proposal item
          format: double
        cost:
          type: number
          description: Supplier cost for the proposal item
          format: double
        costUpdateDate:
          type: string
          description: UTC timestamp when the supplier cost was last updated
          format: date-time
        supplier:
          type: string
          description: Display name of the supplier assigned to the item.
        quantity:
          type: number
          description: Quantity of the proposal item
          format: double
        total:
          $ref: '#/components/schemas/PublicItemTotalModel'
        isTaxExempt:
          type: boolean
          description: Whether the proposal item is tax exempt
        isRecurringService:
          type: boolean
          description: Whether the proposal item is a recurring service
        linkedOrders:
          type: array
          items:
            $ref: '#/components/schemas/PublicLinkedOrderModel'
          description: Linked orders created from this proposal item
      description: Proposal area item
    ErrorResponse:
      title: ErrorResponse
      type: object
      properties:
        responseStatus:
          $ref: '#/components/schemas/ResponseStatus'
      description: ErrorResponse
    PublicProposalItemModel:
      title: PublicProposalItemModel
      required:
        - catalogItemId
        - proposalAreaOptions
      type: object
      properties:
        catalogItemId:
          type: integer
          description: >-
            Identifier of the source item the new item is created from, matching
            itemType
          format: int32
        itemType:
          $ref: '#/components/schemas/ItemTypes'
        proposalAreaOptions:
          type: array
          items:
            $ref: '#/components/schemas/PublicProposalItemAreaOptionModel'
          description: Target proposal area option(s) and quantity
        supplierId:
          type: integer
          description: >-
            (Optional) Supplier to assign to a Part item. Omit to use the part's
            default supplier.
          format: int32
        sellPrice:
          type: number
          description: >-
            Absolute sell price. Omit both sellPrice and sellPercentage to
            inherit the catalog default sell price.
          format: double
        sellPercentage:
          type: number
          description: >-
            Sell price as a whole percent of sellPercentageBasis (15 means 15%).
            Alternative to sellPrice; requires sellPercentageBasis.
          format: double
        sellPercentageBasis:
          $ref: '#/components/schemas/PercentageBasisTypes'
        cost:
          type: number
          description: >-
            Absolute supplier cost. Omit both cost and costPercentage to inherit
            the catalog cost.
          format: double
        costPercentage:
          type: number
          description: >-
            Supplier cost as a whole percent of costPercentageBasis (15 means
            15%). Alternative to cost; requires costPercentageBasis.
          format: double
        costPercentageBasis:
          $ref: '#/components/schemas/PercentageBasisTypes'
        model:
          type: string
          description: >-
            Model value. Part and CustomItem items only - supplying it for a
            Labor or Fee item returns 400.
        name:
          type: string
          description: >-
            Display name. Labor and Fee items only - supplying it for a Part or
            CustomItem returns 400.
        msrp:
          type: number
          description: MSRP amount
          format: double
        isRecurringService:
          type: boolean
          description: Whether the item is a recurring service
        isTaxExempt:
          type: boolean
          description: Whether the item is tax exempt
        setDefault:
          type: boolean
          description: >-
            Saves this item's sell price as the default on the catalog item, so
            later proposals start with it.
        parentProposalItemIds:
          type: array
          items:
            type: integer
            format: int32
          description: Existing proposal items to nest the new item under
      description: New proposal item to add via the public API
    ItemTypes:
      enum:
        - Part
        - Labor
        - CustomItem
        - Fee
      type: string
    PublicItemTotalModel:
      title: PublicItemTotalModel
      required:
        - amount
      type: object
      properties:
        amount:
          type: number
          description: Per-unit sell price multiplied by quantity, before tax
          format: double
        currency:
          $ref: '#/components/schemas/PublicCurrencyModel'
        isCombinedPrice:
          type: boolean
          description: >-
            Whether the totals of nested items are included in this item's
            amount
      description: Total amount for a proposal item
    PublicLinkedOrderModel:
      title: PublicLinkedOrderModel
      required:
        - orderId
        - orderNumber
        - orderNumberSuffix
        - orderName
      type: object
      properties:
        orderId:
          type: integer
          description: Unique numeric identifier of the order
          format: int32
        orderNumber:
          type: integer
          description: Order number shown to the dealer
          format: int32
        orderNumberSuffix:
          type: string
          description: Order number suffix used for supplier-specific numbering
        supplier:
          type: string
          description: Supplier display name
        supplierRef:
          type: string
          description: Supplier reference or supplier order number
        orderName:
          type: string
          description: Display name of the order
        orderStatus:
          $ref: '#/components/schemas/OrderStatuses'
      description: Order summary linked to a proposal item
    ResponseStatus:
      title: ResponseStatus
      type: object
      properties:
        errorCode:
          type:
            - 'null'
            - string
        message:
          type:
            - 'null'
            - string
        stackTrace:
          type:
            - 'null'
            - string
        errors:
          type:
            - 'null'
            - array
          items:
            $ref: '#/components/schemas/ResponseError'
        meta:
          title: DictionaryOfStringAndString
          type:
            - 'null'
            - object
          additionalProperties:
            type:
              - 'null'
              - string
      description: ResponseStatus
    PublicProposalItemAreaOptionModel:
      title: PublicProposalItemAreaOptionModel
      required:
        - proposalAreaOptionId
        - quantity
      type: object
      properties:
        proposalAreaOptionId:
          type: integer
          description: Identifier of the target proposal area option
          format: int32
        quantity:
          type: number
          description: Quantity of the item to add to this area option
          format: double
      description: Target proposal area option and quantity for a new proposal item
    PercentageBasisTypes:
      enum:
        - ProposalTotal
        - PartTotal
        - LaborTotal
        - AreaTotal
        - PartsInAreaTotal
        - LaborInAreaTotal
        - CostOfSellPrice
      type: string
    PublicCurrencyModel:
      title: PublicCurrencyModel
      required:
        - code
        - symbol
      type: object
      properties:
        code:
          type: string
          description: ISO 4217 code that identifies the currency used for the amount.
        symbol:
          type: string
          description: Symbol displayed alongside monetary values in this currency.
      description: Currency code and symbol used to display money values.
    OrderStatuses:
      enum:
        - Draft
        - Submitted
        - ViewedBySupplier
        - Accepted
        - Received
        - EmailFailed
        - Undefined
      type: string
    ResponseError:
      title: ResponseError
      type: object
      properties:
        errorCode:
          type:
            - 'null'
            - string
        fieldName:
          type:
            - 'null'
            - string
        message:
          type:
            - 'null'
            - string
        meta:
          title: DictionaryOfStringAndString
          type:
            - 'null'
            - object
          additionalProperties:
            type:
              - 'null'
              - string
      description: ResponseError
  securitySchemes:
    mssApiAppId:
      type: apiKey
      description: >-
        Portal application identifier used as part of HMAC-authenticated API
        requests.
      name: X-MSS-API-APPID
      in: header
    mssApiUserKey:
      type: apiKey
      description: Portal user API key used together with the HMAC signature headers.
      name: X-MSS-API-USERKEY
      in: header
    mssCustomDate:
      type: apiKey
      description: >-
        Timestamp header included in the signed request to prevent replay
        attacks.
      name: X-MSS-CUSTOM-DATE
      in: header
    mssSignature:
      type: apiKey
      description: >-
        HMAC-SHA256 signature for the request, calculated over the canonical
        string defined in the authentication docs.
      name: X-MSS-SIGNATURE
      in: header

````