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

# Create a digital product

> Creates a digital product in a digital store. Apps cannot attach the private file, so the product is saved unlisted.

Creates a digital product. `shop`, `price`, `file` and `deleted` are set by the server; any such keys in the body are dropped. Requires the `WRITE_INVENTORY` scope.

**Free preview.** `preview: { enabled, pages }` controls the free sample of a PDF book (default `{ enabled: true, pages: 3 }`, 1 to 20 pages, never more than half of the book). Out-of-range values return `400`. Send only the key you change on an update: `{ "preview": { "pages": 5 } }` keeps the switch as it was.

**Pricing rules.** `access: "purchase"` needs `pricing.oneTime`, `access: "subscription"` needs `pricing.monthly`, and `access: "both"` needs both. A needed price must be above 0. `price` in the response is derived: the cheapest way in.

<Warning>
  **An app cannot attach the file.** The book itself is uploaded to the store's private file storage with a store file token that app tokens cannot obtain. So a product created through the Apps API has no file: it is saved with `listed: false`, and asking to list it returns `400` (`Upload a file before listing this product`) until the merchant attaches a file from the Digital page of the dashboard. Use this endpoint to prepare listings, not to publish them.
</Warning>

<Note>
  These endpoints only work on a store whose type is `digital`. On any other store they return `403` with `This is only available on digital stores.` See [Digital stores](/digital-stores).
</Note>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.salesive.com/api/v1/digital-products \
    -H "Authorization: Bearer app_<your-app-access-token>" \
    -H "Content-Type: application/json" \
    -d '{"name":"The Lagos Cookbook","author":"Ngozi Okafor","access":"both","pricing":{"oneTime":4500,"monthly":1500},"inPass":true}'
  ```
</CodeGroup>


## OpenAPI

````yaml apps-api/specs/digital.json POST /digital-products
openapi: 3.1.0
info:
  title: Salesive Apps API , Digital products
  description: >-
    Digital store endpoints (products, the all-access pass and buyer access)
    callable by an installed third-party app using its app access token. They
    only work on stores whose type is `digital`. The target store is bound to
    the token server-side by the OAuth install flow.
  version: 1.0.0
servers:
  - url: https://api.salesive.com/api/v1
    description: Production
security:
  - AppToken: []
paths:
  /digital-products:
    post:
      tags:
        - digital-products
      summary: Create a digital product
      description: >-
        Creates a digital product. `shop`, `price`, `file` and `deleted` are set
        by the server and any such keys in the body are dropped. `price` is
        derived from `pricing`. The private file cannot be uploaded with an app
        token, so a product created by an app has no file, is saved unlisted,
        and cannot be listed until a file is attached from the Salesive
        dashboard. Requires the `WRITE_INVENTORY` scope. The store must be a
        digital store, otherwise the call returns 403.
      operationId: createDigitalProduct
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  description: Product name (required, max 120 characters).
                description:
                  type: string
                  description: Description (max 10,000 characters).
                author:
                  type: string
                  description: Author or publisher (max 120 characters).
                images:
                  type: array
                  items:
                    type: string
                    format: uri
                  description: Cover image links (http or https, max 10).
                category:
                  type: string
                  nullable: true
                  description: >-
                    Category ObjectId, which must belong to this store. `null`
                    or an empty string clears it.
                access:
                  type: string
                  enum:
                    - purchase
                    - subscription
                    - both
                  description: How buyers get the product. Default `purchase`.
                pricing:
                  type: object
                  properties:
                    oneTime:
                      type: number
                      nullable: true
                      minimum: 0
                      description: >-
                        One-time price. Required (and above 0) when `access` is
                        `purchase` or `both`.
                    monthly:
                      type: number
                      nullable: true
                      minimum: 0
                      description: >-
                        Price for a 30-day pass. Required (and above 0) when
                        `access` is `subscription` or `both`.
                  description: >-
                    Prices in the store currency. A price for a way of buying
                    that `access` does not offer is discarded.
                inPass:
                  type: boolean
                  description: Include the product in the store's all-access pass.
                preview:
                  $ref: '#/components/schemas/DigitalPreview'
                fileId:
                  type: string
                  description: >-
                    Id of a private file already uploaded to the store's file
                    storage. See the note on this page: an app cannot upload
                    that file itself.
                listed:
                  type: boolean
                  description: >-
                    Show the product in the storefront. A product with no file
                    is saved unlisted, and asking to list it returns 400.
                featured:
                  type: boolean
              example:
                name: The Lagos Cookbook
                description: >-
                  120 everyday Nigerian recipes, with a shopping list for each
                  week.
                author: Ngozi Okafor
                images:
                  - https://cdn.salesive.com/d/lagos-cookbook-cover.jpg
                category: 66a2a0aae1b3a40012cd0001
                access: both
                pricing:
                  oneTime: 4500
                  monthly: 1500
                inPass: true
                preview:
                  enabled: true
                  pages: 5
      responses:
        '201':
          description: 'Create a digital product: success.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - status
                  - success
                  - message
                  - data
                properties:
                  status:
                    type: integer
                    example: 201
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Digital product created successfully
                  data:
                    allOf:
                      - $ref: '#/components/schemas/DigitalProduct'
                    example:
                      _id: 6702c1a4e1b3a40012cd2001
                      shop: 66a1eee0e1b3a40012ab0001
                      name: The Lagos Cookbook
                      description: >-
                        120 everyday Nigerian recipes, with a shopping list for
                        each week.
                      author: Ngozi Okafor
                      images:
                        - https://cdn.salesive.com/d/lagos-cookbook-cover.jpg
                      category:
                        _id: 66a2a0aae1b3a40012cd0001
                        name: Cookbooks
                        slug: cookbooks
                      access: both
                      pricing:
                        oneTime: 4500
                        monthly: 1500
                      price: 1500
                      inPass: true
                      file:
                        name: ''
                        size: 0
                        format: null
                        mimetype: ''
                        attached: false
                      preview:
                        enabled: true
                        pages: 3
                      listed: false
                      featured: false
                      createdAt: '2026-10-11T09:12:00.000Z'
                      updatedAt: '2026-10-11T09:30:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
components:
  schemas:
    DigitalPreview:
      type: object
      description: >-
        The free preview of a PDF book: the first `pages` pages, cut on demand.
        A product with no stored preview setting reads as `{ enabled: true,
        pages: 3 }`.
      properties:
        enabled:
          type: boolean
          description: Whether buyers can open the free preview. Default `true`.
        pages:
          type: integer
          minimum: 1
          maximum: 20
          description: >-
            How many pages to show, 1 to 20 (default 3). The storefront never
            shows more than half of the book. A value outside 1 to 20, or a
            fraction, returns 400 and nothing changes.
    DigitalProduct:
      type: object
      properties:
        _id:
          type: string
        shop:
          type: string
          description: The store id (always the installing store).
        name:
          type: string
          description: Product name (max 120 characters).
        description:
          type: string
          description: Description (max 10,000 characters).
        author:
          type: string
          description: Author or publisher (max 120 characters).
        images:
          type: array
          items:
            type: string
          description: Cover image links (max 10). The first is the main cover.
        category:
          description: >-
            Category. Populated as `{ _id, name, slug }` on reads, `null` when
            none.
          nullable: true
          oneOf:
            - type: string
              description: Category ObjectId.
            - type: object
              properties:
                _id:
                  type: string
                name:
                  type: string
                slug:
                  type: string
        access:
          type: string
          enum:
            - purchase
            - subscription
            - both
          description: >-
            How buyers get the product: `purchase` (one-time payment, permanent
            access), `subscription` (a 30-day pass for this product) or `both`
            (the buyer picks).
        pricing:
          type: object
          properties:
            oneTime:
              type: number
              nullable: true
              description: >-
                One-time price in the store currency. `null` unless `access` is
                `purchase` or `both`.
            monthly:
              type: number
              nullable: true
              description: >-
                Price for a 30-day pass. `null` unless `access` is
                `subscription` or `both`.
        price:
          type: number
          description: >-
            Derived, read-only: the cheapest way in (the lower of
            `pricing.oneTime` and `pricing.monthly`). Storefront cards show
            this.
        inPass:
          type: boolean
          description: Whether the product is covered by the store's all-access pass.
        file:
          type: object
          description: >-
            The private file buyers download. The file id is never returned:
            `attached` says whether one is set.
          properties:
            name:
              type: string
              description: Original file name.
            size:
              type: integer
              description: Size in bytes.
            format:
              type: string
              enum:
                - pdf
                - epub
                - mobi
                - zip
              nullable: true
            mimetype:
              type: string
            attached:
              type: boolean
              description: '`true` when a file is attached.'
        preview:
          $ref: '#/components/schemas/DigitalPreview'
        listed:
          type: boolean
          description: >-
            Whether the product shows in the storefront. A product without a
            file cannot be listed.
        featured:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
  responses:
    BadRequest:
      description: The body or query failed validation. `message` says which rule.
      content:
        application/json:
          schema:
            type: object
            properties:
              status:
                type: integer
                example: 400
              success:
                type: boolean
                example: false
              message:
                type: string
                example: A one-time price is required
              data:
                type: object
                nullable: true
    Unauthorized:
      description: Missing or invalid app access token.
      content:
        application/json:
          schema:
            type: object
            properties:
              status:
                type: integer
                example: 401
              success:
                type: boolean
                example: false
              message:
                type: string
                example: App token expired or invalid.
              data:
                type: object
                nullable: true
    Forbidden:
      description: >-
        The app token lacks the required scope (`READ_INVENTORY` for reads,
        `WRITE_INVENTORY` for writes), or the store is not a digital store
        (`This is only available on digital stores.`).
      content:
        application/json:
          schema:
            type: object
            properties:
              status:
                type: integer
                example: 403
              success:
                type: boolean
                example: false
              message:
                type: string
                example: >-
                  This app is missing the required permission(s):
                  WRITE_INVENTORY
              data:
                type: object
                nullable: true
    NotFound:
      description: The record does not exist in this store.
      content:
        application/json:
          schema:
            type: object
            properties:
              status:
                type: integer
                example: 404
              success:
                type: boolean
                example: false
              message:
                type: string
                example: Product not found
              data:
                type: object
                nullable: true
    Conflict:
      description: The request conflicts with the current state.
      content:
        application/json:
          schema:
            type: object
            properties:
              status:
                type: integer
                example: 409
              success:
                type: boolean
                example: false
              message:
                type: string
                example: That file is already attached to another product
              data:
                type: object
                nullable: true
  securitySchemes:
    AppToken:
      type: http
      scheme: bearer
      description: >-
        Installed-app access token (prefix app_), issued by the OAuth install
        flow. The store is bound to the token server-side , never send a shop
        id.

````

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