Positions

Positions are the edges in Addepar's ownership graph. A position connects two entities: an owner (the entity that holds something) and an owned (the entity being held). Every financial relationship in the system is modeled as a position: a client owns a trust, a trust owns an account, an account holds a stock. Positions are how Addepar knows who owns what, and they are the foundation for every portfolio calculation, performance return, and allocation report.

How positions work

A position answers one question: "Entity A owns something in Entity B." The nature of that ownership depends on the owned entity's ownership type:

Owned entity's ownership typeWhat the position tracksRequired attributes at creation
PERCENT_BASEDA percentage of the owned entity (e.g., 60% of a trust)incepting_open_position_date, incepting_open_position_ownership_percentage
SHARE_BASEDA number of shares (established by transactions, not set on the position)None (position is a link; share count comes from transactions)
VALUE_BASEDA currency amount (established by transactions, not set on the position)None (position is a link; value comes from transactions)

This is the critical distinction. For percent-based entities (trusts, accounts, vehicles), the position itself carries the ownership stake. For share-based and value-based entities (stocks, bonds, funds), the position is a link and the actual quantity is determined by the sum of transactions recorded against it.

Multiple positions between the same entities: The system allows more than one open position between the same owner and owned entity (e.g., different lots of the same stock in the same account). The Positions API always reads and writes the incepting open position, which is the original position created between those two entities.

Position lifecycle: A position is "open" from its inception date until closed. For share-based entities, the position closes when transactions reduce the share count to zero. For percent-based entities, the position remains open as long as the ownership percentage is greater than zero.

Overview

Base route/v1/positions
ProducesJSON
PaginationYes
OAuth scopesPOSITIONS or POSITIONS_WRITE

📘

Access requirements

  • API Access: Create, edit, and delete.
  • Portfolio Access: Required to retrieve, update, and delete positions.
  • Transaction permission: "Transactions: Full permission (view, create, and edit)" is required to create, update, and delete positions.
  • Manage Attributes: Required to apply, edit, or delete attributes on positions.

Resource attributes

AttributeDescription
nameString -- Position name. Required for cash positions. Must be unique among cash positions with the same owner/owned pair. Example: "GOOG"
display_nameString -- User-facing display name for the position. Example: "Google Class A"
incepting_open_position_dateString -- Date ownership was first established, formatted YYYY-MM-DD. Required for percent-based positions. For accounts that directly own share-based assets, use a date one day before the first transaction. Example: "2015-12-31"
incepting_open_position_ownership_percentageNumber -- Ownership percentage as a decimal. Required for percent-based positions. 1.0 = 100%. Example: 0.5
incepting_open_position_valueNumber -- Starting value for value-based positions. Example: 5
created_atString -- Read-only. ISO 8601 timestamp. Example: "2023-07-28T02:24:30Z"
modified_atString -- Read-only. ISO 8601 timestamp. Example: "2023-07-30T10:43:21Z"

Relationships:

RelationshipDescription
ownerThe entity that holds the position (e.g., a client, account, or trust).
ownedThe entity being held (e.g., a stock, fund, or sub-account).

Custom Addepar Attributes with position_custom_attributes usage can also be applied to positions via PATCH.

Get a position

Returns the specified position with its inception attributes and owner/owned relationships.

GET /v1/positions/:id

curl -X GET "https://{firm}.addepar.com/api/v1/positions/100" \
  -H "Authorization: Basic {credentials}" \
  -H "Addepar-Firm: 1" \
  -H "Accept: application/vnd.api+json"
{
  "data": {
    "id": "100",
    "type": "positions",
    "attributes": {
      "incepting_open_position_date": "2010-01-01",
      "incepting_open_position_ownership_percentage": 1.0,
      "created_at": "2023-07-28T02:24:30Z",
      "modified_at": "2023-07-30T10:43:21Z"
    },
    "relationships": {
      "owner": {
        "links": {
          "self": "/v1/positions/100/relationships/owner",
          "related": "/v1/positions/100/owner"
        },
        "data": { "type": "entities", "id": "1" }
      },
      "owned": {
        "links": {
          "self": "/v1/positions/100/relationships/owned",
          "related": "/v1/positions/100/owned"
        },
        "data": { "type": "entities", "id": "2" }
      }
    },
    "links": { "self": "/v1/positions/100" }
  },
  "included": []
}

Related entity endpoints:

EndpointReturns
GET /v1/positions/:id/ownerFull entity details for the owner
GET /v1/positions/:id/ownedFull entity details for the owned entity
GET /v1/positions/:id/relationships/ownerOwner entity type and ID only
GET /v1/positions/:id/relationships/ownedOwned entity type and ID only

Get all positions

Returns all positions accessible to the authenticated user. Results are paginated.

GET /v1/positions

curl -X GET "https://{firm}.addepar.com/api/v1/positions" \
  -H "Authorization: Basic {credentials}" \
  -H "Addepar-Firm: 1" \
  -H "Accept: application/vnd.api+json"

Filter parameters:

ParameterDescription
fields[positions]Return only specified attributes. Pass fields[positions]=[] to omit all attributes (returns only relationships). Example: fields[positions]=incepting_open_position_date
filter[created_before]Positions created on or before date. Format: YYYY-MM-DD
filter[created_after]Positions created on or after date. Format: YYYY-MM-DD
filter[modified_before]Positions modified on or before date. Format: YYYY-MM-DD
filter[modified_after]Positions modified on or after date. Format: YYYY-MM-DD
filter[owner_model_types]Positions whose owner entity has one of the specified model types. Example: filter[owner_model_types]=PERSON_NODE,TRUST
filter[owned_model_types]Positions whose owned entity has one of the specified model types. Example: filter[owned_model_types]=STOCK,BOND
filter[owner_entity_id]Positions owned by the specified entity IDs. Example: filter[owner_entity_id]=24,26
filter[owned_entity_id]Positions holding the specified entity IDs. Example: filter[owned_entity_id]=23,25

The owner_entity_id and owned_entity_id filters are the most common for integration use. To find all holdings in a specific account: filter[owner_entity_id]=<account_id>. To find all accounts that hold a specific security: filter[owned_entity_id]=<security_id>.

Create positions

Creates one or more positions linking an owner entity to an owned entity.

POST /v1/positions

Share-based or value-based position (account owns a stock):

curl -X POST "https://{firm}.addepar.com/api/v1/positions" \
  -H "Authorization: Basic {credentials}" \
  -H "Addepar-Firm: 1" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Accept: application/vnd.api+json" \
  -d '{
    "data": {
      "type": "positions",
      "attributes": {},
      "relationships": {
        "owner": {
          "data": { "type": "entities", "id": "27" }
        },
        "owned": {
          "data": { "type": "entities", "id": "29" }
        }
      }
    }
  }'
{
  "data": {
    "id": "338",
    "type": "positions",
    "relationships": {
      "owner": {
        "links": {
          "self": "/v1/positions/338/relationships/owner",
          "related": "/v1/positions/338/owner"
        },
        "data": { "type": "entities", "id": "27" }
      },
      "owned": {
        "links": {
          "self": "/v1/positions/338/relationships/owned",
          "related": "/v1/positions/338/owned"
        },
        "data": { "type": "entities", "id": "29" }
      }
    },
    "links": { "self": "/v1/positions/338" }
  },
  "included": []
}

Percent-based position (person owns a trust):

For percent-based owned entities, you must include the inception date and ownership percentage:

curl -X POST "https://{firm}.addepar.com/api/v1/positions" \
  -H "Authorization: Basic {credentials}" \
  -H "Addepar-Firm: 1" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Accept: application/vnd.api+json" \
  -d '{
    "data": {
      "type": "positions",
      "attributes": {
        "incepting_open_position_date": "2001-01-01",
        "incepting_open_position_ownership_percentage": 1.0
      },
      "relationships": {
        "owner": {
          "data": { "type": "entities", "id": "22" }
        },
        "owned": {
          "data": { "type": "entities", "id": "23" }
        }
      }
    }
  }'

Multiple positions in a single request:

Pass an array in data to create several positions at once. Each element follows the same structure as a single create.

Response codes:

  • 201 Created -- Position created successfully
  • 400 Bad Request -- Invalid payload or missing required fields
  • 403 Forbidden -- Insufficient permissions or scope not granted
  • 404 Not Found -- Owner or owned entity does not exist or is not accessible
  • 409 Conflict -- type field must be "positions"

Edit positions

Updates the incepting open position. Only include the attributes you want to change.

PATCH /v1/positions/:id (single) or PATCH /v1/positions (bulk)

curl -X PATCH "https://{firm}.addepar.com/api/v1/positions/104" \
  -H "Authorization: Basic {credentials}" \
  -H "Addepar-Firm: 1" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Accept: application/vnd.api+json" \
  -d '{
    "data": {
      "type": "positions",
      "id": "104",
      "attributes": {
        "incepting_open_position_date": "2010-01-01",
        "incepting_open_position_ownership_percentage": 0.2
      }
    }
  }'

You can also apply custom position-level attributes:

curl -X PATCH "https://{firm}.addepar.com/api/v1/positions/998" \
  -H "Authorization: Basic {credentials}" \
  -H "Addepar-Firm: 1" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Accept: application/vnd.api+json" \
  -d '{
    "data": {
      "type": "positions",
      "id": "998",
      "attributes": {
        "_custom_balance_sheet_12345": [
          { "value": "Below The Line" }
        ],
        "display_name": "My Cash Asset"
      }
    }
  }'

Custom attributes on positions use the same time-varying format as entity attributes.

Response codes:

  • 200 OK -- Position updated successfully
  • 400 Bad Request -- Invalid payload
  • 403 Forbidden -- Insufficient permissions or scope not granted
  • 404 Not Found -- Position does not exist or not accessible
  • 409 Conflict -- ID mismatch between URL and payload, or type is not "positions"

Delete positions

Permanently removes one or more positions. This operation is irreversible. A position cannot be deleted if it has transactions recorded against it. Delete or reassign those transactions first.

DELETE /v1/positions/:id (single) or DELETE /v1/positions (bulk)

curl -X DELETE "https://{firm}.addepar.com/api/v1/positions/100" \
  -H "Authorization: Basic {credentials}" \
  -H "Addepar-Firm: 1" \
  -H "Accept: application/vnd.api+json"
204 No Content

For bulk delete, pass an array of { "type": "positions", "id": "<id>" } objects in the request body.

Response codes:

  • 204 No Content -- Position deleted successfully
  • 400 Bad Request -- Position is referenced by transactions and cannot be deleted
  • 403 Forbidden -- Insufficient permissions or scope not granted
  • 404 Not Found -- Position does not exist or not accessible

Common integration patterns

Building the ownership graph: After creating entities, establish the ownership hierarchy through positions. A typical structure: Person (client) → Trust → Holding Account → Securities. Create positions top-down so that portfolio queries on the Person aggregate all downstream holdings.

Custodian reconciliation: Use filter[owner_entity_id] to retrieve all positions in an account, compare against the custodian feed, and create/close positions to match. For share-based positions, the share count is determined by transactions, so reconciliation usually means posting buy/sell transactions rather than editing the position itself.

Inception date guidance: For percent-based positions, the inception date determines when performance calculations begin. For accounts that directly own share-based assets, set the inception date one day before the first transaction date. If the first transaction is a buy on 2023-01-15, set incepting_open_position_date to 2023-01-14. Using a historical date like 1900-01-01 is valid for hierarchical positions (person owns trust) where the relationship predates your data.


📘

Related

  • Entities -- The nodes that positions connect
  • Transactions -- Record value changes against positions (buys, sells, contributions)
  • Addepar Attributes -- Apply custom metadata to positions
  • Portfolio Query -- Aggregate position data across the ownership graph
  • Groups -- Organize entities for permission scoping and reporting

Did this page help you?