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

# Bulk upsert entities by ID

> Accepts an array of entities. For each: creates it if it does not exist, then writes ValueDiffs only for keys whose values have changed. Optionally links each entity as a downstream child of a parent. New children append in input order, including across successive requests. All diffs are written in a single DB round-trip for efficiency. Concurrent upserts matching the same external identifier (`idField`) are serialized server-side, so they converge on one entity instead of creating duplicates. Parents and children may be created atomically in one batch when `parentIds` contains Flow entity UUIDs, including UUIDs assigned to parent rows in that batch. `parentIds` does not resolve `idField` aliases; resolve or create such a parent first if its Flow UUID is unknown. The operation is atomic and safe to retry with the same stable IDs. If Claude Desktop reports "No result received ... after waiting 4 minutes", first read those IDs because the approval/dispatch may have been lost before Flow received the call; never replace the IDs merely because the result was lost. Field definition validation is intentionally skipped — callers may write arbitrary key names without pre-defining them in the schema.



## OpenAPI

````yaml /openapi/customer-api.json post /project/{projectId}/entities/upsert
openapi: 3.0.0
info:
  contact: {}
  description: >-
    Flow customer API reference. Start with the [quickstart](/api/quickstart)
    and [authentication guide](/api/authentication).
  title: Customer API
  version: 1.0.0
servers:
  - description: Flow API
    url: https://backend.branch.flowengineering.com
security: []
tags:
  - name: Automations
  - name: Branches
  - name: Comments
  - name: Convert
  - name: Data Model
  - name: Diagrams
  - name: Entities
  - name: Files
  - name: Import
  - name: Notifications
  - name: Projects
  - name: ReqIF
  - name: ReqIF Import
  - name: Shared Access Grants
  - name: User Groups
  - name: Users
paths:
  /project/{projectId}/entities/upsert:
    post:
      tags:
        - Entities
      summary: Bulk upsert entities by ID
      description: >-
        Accepts an array of entities. For each: creates it if it does not exist,
        then writes ValueDiffs only for keys whose values have changed.
        Optionally links each entity as a downstream child of a parent. New
        children append in input order, including across successive requests.
        All diffs are written in a single DB round-trip for efficiency.
        Concurrent upserts matching the same external identifier (`idField`) are
        serialized server-side, so they converge on one entity instead of
        creating duplicates. Parents and children may be created atomically in
        one batch when `parentIds` contains Flow entity UUIDs, including UUIDs
        assigned to parent rows in that batch. `parentIds` does not resolve
        `idField` aliases; resolve or create such a parent first if its Flow
        UUID is unknown. The operation is atomic and safe to retry with the same
        stable IDs. If Claude Desktop reports "No result received ... after
        waiting 4 minutes", first read those IDs because the approval/dispatch
        may have been lost before Flow received the call; never replace the IDs
        merely because the result was lost. Field definition validation is
        intentionally skipped — callers may write arbitrary key names without
        pre-defining them in the schema.
      operationId: EntitiesController_upsertEntities
      parameters:
        - in: path
          name: projectId
          required: true
          schema:
            type: string
        - description: >-
            Whether newly inserted entities in the upsert batch are assigned to
            the authenticated user. Defaults to true; existing entities are
            unaffected.
          in: query
          name: auto_assign_owner
          required: false
          schema:
            type: boolean
        - description: >-
            Branch to write all entities to. Omit or pass "master" for the base
            branch.
          in: query
          name: branch_id
          required: false
          schema:
            type: string
        - description: Workspace identifier
          in: header
          name: customer
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              items:
                $ref: '#/components/schemas/UpsertEntityDto'
              type: array
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/EntityStateResponseDto'
                type: array
          description: ''
      security:
        - api-key: []
        - bearer: []
components:
  schemas:
    UpsertEntityDto:
      properties:
        entityType:
          description: >-
            Entity type (e.g. "task", "bug"). Must match an existing type in the
            schema.
          type: string
        id:
          description: Entity ID (UUID). Must be unique within the project.
          type: string
        idField:
          description: >-
            Optional field key to match `id` against instead of treating `id` as
            the Flow entity id. When set, `id` is interpreted as an external
            identifier and matched against the current value of this field
            across existing entities on the branch: a match reuses that entity,
            a miss creates a new one with a freshly generated Flow id. This
            decouples the Flow entity id from any one external field (e.g. match
            on a custom "ReqIF ID" field). Omit (or leave the field empty) to
            keep the default behaviour where `id` is the Flow entity id
            verbatim.
          type: string
        parentIds:
          description: >-
            Optional list of parent entity IDs. For each parent, a downstream
            link is created from that parent to this entity if one does not
            already exist. Existing upstream links that are not listed here are
            left untouched.
          items:
            type: string
          type: array
        values:
          description: >-
            Key-value pairs to compare and write. Only keys whose values differ
            from the stored state will produce diffs.
          type: object
      required:
        - id
        - entityType
        - values
      type: object
    EntityStateResponseDto:
      properties:
        branchesChangedIn:
          items:
            type: string
          type: array
        branchesCreatedIn:
          items:
            type: string
          type: array
        createdAt:
          format: date-time
          type: string
        defaultSortKey:
          type: string
        deleted:
          type: boolean
        formulaTemplates:
          type: object
        formulas:
          additionalProperties:
            type: string
          type: object
        id:
          type: string
        links:
          $ref: '#/components/schemas/EntityLinksDto'
        type:
          type: string
        uniqueEntityNumber:
          type: number
        updatedAt:
          format: date-time
          type: string
        values:
          type: object
      required:
        - id
        - values
        - formulas
        - formulaTemplates
        - links
      type: object
    EntityLinksDto:
      properties:
        crosslinks:
          items:
            type: string
          type: array
        inbound:
          items:
            type: string
          type: array
        inboundOrder:
          additionalProperties:
            type: string
          type: object
        inboundTypes:
          additionalProperties:
            type: string
          type: object
        outbound:
          items:
            type: string
          type: array
        outboundOrder:
          additionalProperties:
            type: string
          type: object
        outboundTypes:
          additionalProperties:
            type: string
          type: object
        rootPlaced:
          type: boolean
      required:
        - inbound
        - outbound
        - crosslinks
        - inboundOrder
      type: object
  securitySchemes:
    api-key:
      in: header
      name: X-API-Key
      type: apiKey
    bearer:
      bearerFormat: JWT
      scheme: bearer
      type: http

````