---
title: "Create Portfolio Block"
method: POST
path: "/extensions/roboinvestor/{graph_id}/operations/create-portfolio-block"
tags: ["RoboInvestor"]
---

# Create Portfolio Block

`POST /extensions/roboinvestor/{graph_id}/operations/create-portfolio-block`

## Request

- Base URL: `https://api.robosystems.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.robosystems.ai/extensions/roboinvestor/{graph_id}/operations/create-portfolio-block`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Create a portfolio with optional initial positions in a single atomic envelope. Each position references an existing security; this operation never mints securities (use `create-security`). Whole envelope validates before any write.

**Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict.

## Path parameters

- `graph_id` string, required

## Headers

- `Idempotency-Key` string, nullable

## Request body

- CreatePortfolioBlockRequest: CQRS body for `POST /operations/create-portfolio-block`. Whole envelope validated before any DB write; the portfolio + initial positions land in one transaction. Each `position` references an existing `security_id` — the operation does not mint securities.
  - `portfolio` PortfolioBlockPortfolioFields, required: Fields settable on the portfolio core when creating a block.
    - `name` string, required: Display name for the portfolio. 1-200 characters.
    - `description` string, nullable: Free-text description of the portfolio.
    - `strategy` string, nullable: Free-text strategy classification (e.g. `value`, `growth`, `income`). Open vocabulary.
    - `inception_date` string, date, nullable: Date the portfolio was established (YYYY-MM-DD).
    - `base_currency` string: ISO 4217 currency code used for portfolio-level aggregates (e.g. `total_cost_basis_dollars`).
    - `entity_id` string, nullable: ID of the owning entity (e.g. fund, trust, or person). Optional — leave unset for unattributed portfolios.
  - `positions` PortfolioBlockPositionAdd[]: Initial positions to mint inside the new portfolio. Each references an existing security; pass `[]` to create an empty portfolio.
    - `security_id` string, required: ID of the existing security this position holds. Securities are minted via `create-security`; the operation returns 404 if the ID is unknown.
    - `quantity` number, required: Quantity held, in units defined by `quantity_type` (e.g. share count for `shares`, face value for `principal`).
    - `quantity_type` string: Unit basis for `quantity`. Common values: `shares` (equity units), `units` (generic), `principal` (debt face value).
    - `cost_basis` integer: Total cost basis for this lot, in **cents** of `currency`. Stored as integer cents to avoid float precision drift; $1,250.00 USD is `125000`.
    - `currency` string: ISO 4217 currency code for `cost_basis` and `current_value`.
    - `current_value` integer, nullable: Latest mark-to-market value in **cents** of `currency`, or `null` if unmarked. Pair with `valuation_date` and `valuation_source` when set.
    - `valuation_date` string, date, nullable: Date `current_value` was sourced (YYYY-MM-DD).
    - `valuation_source` string, nullable: Free-text source attribution for `current_value` (e.g. `manual`, `broker_statement`, vendor name).
    - `acquisition_date` string, date, nullable: Date the position was originally acquired (YYYY-MM-DD).
    - `notes` string, nullable: Free-text notes attached to the position.

## Response `200`

Successful Response

- OperationEnvelopePortfolioBlockEnvelope
  - `operation` string, required: Kebab-case operation name
  - `operationId` string, required: op_-prefixed ULID for audit and SSE correlation
  - `status` 'completed' | 'pending' | 'failed', required: Operation lifecycle state
  - `result` PortfolioBlockEnvelope: Molecular response shape for portfolio-block operations. Bundles the portfolio core, its embedded positions, and pre-computed totals into a single payload — the contract for `create-portfolio-block`, `update-portfolio-block`, and the read-side `get-portfolio-block`. Cents-precision values aren't surfaced here; use `PositionResponse` / `PortfolioResponse` for those.
    - `id` string, required: Portfolio ID (`port_*` ULID).
    - `name` string, required: Display name.
    - `description` string, nullable: Free-text description.
    - `strategy` string, nullable: Free-text strategy classification.
    - `inception_date` string, date, nullable: Date the portfolio was established.
    - `base_currency` string, required: ISO 4217 currency code for portfolio aggregates.
    - `owner` EntityLite: Lightweight entity projection for embedding in portfolio-block / position envelopes. Carries identity-only fields; full entity data lives behind the Master Data entity APIs.
      - `id` string, required: Entity ID (`ent_*` ULID).
      - `name` string, required: Display name of the entity.
      - `source_graph_id` string, nullable: Tenant graph this entity is anchored to, when known. `null` for entities not yet linked to a graph.
    - `positions` PositionBlock[], required: All positions in this portfolio, including disposed ones (filter by `status` for active-only display).
      - `id` string, required: Position ID (`pos_*` ULID).
      - `quantity` number, required: Quantity held in `quantity_type` units.
      - `quantity_type` string, required: Unit basis (`shares`, `units`, `principal`).
      - `cost_basis_dollars` number, required: Cost basis in dollars (pre-converted from cents).
      - `current_value_dollars` number, nullable: Latest mark-to-market value in dollars. `null` when the position has not been marked.
      - `valuation_date` string, date, nullable: Date the current value was sourced.
      - `valuation_source` string, nullable: Free-text source attribution for the valuation.
      - `acquisition_date` string, date, nullable: Date the position was acquired.
      - `status` string, required: Lifecycle state (`active`, `disposed`, `archived`). See `PositionResponse.status` for the full vocabulary.
      - `notes` string, nullable: Free-text notes attached to the position.
      - `security` SecurityLite, required: Lightweight security projection for embedding in position envelopes. Skips `terms`, `outstanding_shares`, etc. — fetch the full `SecurityResponse` when those are needed.
        - `id` string, required: Security ID (`sec_*` ULID).
        - `name` string, required: Display name of the security.
        - `security_type` string, required: Instrument family (e.g. `common_stock`, `preferred_stock`, `warrant`).
        - `security_subtype` string, nullable: Optional subtype refinement (e.g. `class_a`).
        - `is_active` boolean, required: `true` when the security is in active status.
        - `issuer` EntityLite: Lightweight entity projection for embedding in portfolio-block / position envelopes. Carries identity-only fields; full entity data lives behind the Master Data entity APIs.
          - `id` string, required: Entity ID (`ent_*` ULID).
          - `name` string, required: Display name of the entity.
          - `source_graph_id` string, nullable: Tenant graph this entity is anchored to, when known. `null` for entities not yet linked to a graph.
        - `source_graph_id` string, nullable: Tenant graph the security is pre-associated to, if any.
    - `total_cost_basis_dollars` number, required: Sum of `cost_basis_dollars` across every position.
    - `total_current_value_dollars` number, nullable: Sum of `current_value_dollars` across every position. `null` when any active position lacks a mark.
    - `active_position_count` integer, required: Count of positions with `status='active'`.
    - `created_at` string, date-time, required: Row creation timestamp (UTC).
    - `updated_at` string, date-time, required: Last-modified timestamp (UTC).
  - `at` string, required: ISO-8601 UTC timestamp
  - `createdBy` string, nullable: User ID that initiated the operation
  - `idempotentReplay` boolean: True when this envelope came from the idempotency cache — the underlying command did not execute again. False on fresh executions.

## Other responses

- `400`: Invalid request
- `401`: Authentication required
- `403`: Access denied
- `404`: Resource not found
- `409`: Idempotency-Key conflict — key reused with different body
- `422`: Validation error
- `429`: Rate limit exceeded
- `500`: Internal server error

---

[API](https://skmtc.dev/robosystems/apis/robosystems-api.md) · [All operations](https://skmtc.dev/robosystems/apis/robosystems-api/llms.txt) · [OpenAPI document](https://skmtc.dev/robosystems/apis/robosystems-api/revisions/e661e2d5ca6a?raw)
