Overview

The Tilt Portfolio Optimization Platform enables you to create personalized portfolio optimizations for your clients. This guide walks through the complete workflow from initial organization setup to generating optimized trade proposals.

Step 1: Organization Setup

Configure Asset Classes

We’ll start by defining the asset classes available in your organization. An asset class consists of a name, description, and a benchmark that represents it. We’ll first search for available benchmark indexes, and then use that to create the asset class.

Search Available Benchmarks

curl --request GET \
  --url https://api.tilt.io/api/v1/custom/benchmark_indexes/ \
  --header 'X-Api-Key: <api-key>'
View API Reference →

Create Asset Classes

Now we can create an asset class using one of our benchmarks. The example below uses M75BNK-R, which is the identifier for the SPY ETF.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/asset_classes/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "name": "US Equities",
  "description": "Example US Equities Asset Class",
  "benchmark_index_identifier": "M75BNK-R"
}'
View API Reference →

Step 2. Portfolio Setup

Now that we have an asset class, let’s create a portfolio which uses that asset class. We can make an example portfolio with a single allocation to US equities, although it is possible to blend multiple asset classes together. We reference the asset class using it’s UUID that was returned when creating it. We can always list available asset classes to retrieve all asset classes. At this point, we can enable direct indexing per asset class, for this portfolio. Later, we can override this setting per client too.
Direct indexing only takes affect if the underlying benchmark index also supports direct indexing.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/portfolio_configs/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "name": "Example Portfolio",
  "asset_allocations": [
    {
      "asset_class_uuid": "asset-class-uuid",
      "target_allocation": "1",
      "direct_indexing_enabled": true
    }
  ]
}'
View API Reference →

Step 3: Client Setup

Create a Client

Next let’s create a new client instance, which represents a client in your system. The external_id should be a string that represents this client in your system, but clients are referenced throughout the API using a UUID which is returned up client creation.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "external_id": "<string>",
}'
When creating a client, we can also provide configuration and holdings information, but we’ll update those separately in this guide.
View API Reference →

Update Client Holdings

Now, let’s set assign the client to a portfolio, and update their holdings. We use the portfolio UUID which was returned when creating the portfolio, or we can find it by listing the available portfolios.
curl --request PATCH \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/{uuid}/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "portfolio_config_uuid": "portfolio-config-uuid",
    "holdings": {
      "tax_lots": [
        {
          "tilt_asset_id": "MH33D6-R",
          "quantity": "100",
          "cost_basis": "250",
          "trade_date": "2025-01-01",
          "wash_sale_date": "2025-01-01",
          "last_sold_for_loss_date": "2025-01-01"
        },
        {
          "tilt_asset_id": "P8R3C2-R",
          "quantity": "50",
          "cost_basis": "120",
          "trade_date": "2025-01-01",
          "wash_sale_date": "2025-01-01",
          "last_sold_for_loss_date": "2025-01-01"
        }
      ],
      "cash_balance": "50000",
      "non_tradable_assets_value": "5000"
    }
  }'
MH33D6-R and P8R3C2-R are the identifiers for Apple and Microsoft respectively. More information on asset identifiers is in the Tilt Asset IDs section.

Update Client Direct Indexing Preference

We may override the direct indexing preferences for a client by updating asset_class_settings. For example, we can disable direct indexing for a particular asset class:
curl --request PATCH \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/{uuid}/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "asset_class_settings": [
      {
        "asset_class_uuid": "asset-class-uuid",
        "direct_indexing_enabled": false
      }
    ]
  }'
View API Reference →

Step 4: Portfolio Optimization

We can run optimization either for a single client, or in bulk. To start with, let’s run the optimization for a single client.

Single Client Optimization

Run an optimization for an individual client to get customized portfolio weights:
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/{uuid}/optimize/ \
  --header 'X-Api-Key: <api-key>'
View API Reference →

Generate Trade Proposals

Convert the optimized weights into actionable trade proposals.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/{uuid}/proposal/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "optimized_weights": {
      "MH33D6-R": "0.5",
      "P8R3C2-R": "0.5"
    }
  }'
View API Reference →

Step 5: Bulk Operations

Bulk Optimization and Proposals

We can optimize clients in bulk by calling the bulk optimize endpoint, passing in a list of client UUIDs. This responds with a list of proposal UUID’s, that we can then check the result of.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/optimized_proposals/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "client_uuids": [
      "client-uuid-1",
      "client-uuid-2",
      "client-uuid-3"
    ]
  }'
View API Reference →

Check Task Results

We can get the status for each proposal by calling the check status endpoint:
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/optimized_proposals/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "proposal_uuids": [
      "proposal-uuid-1",
      "proposal-uuid-2",
      "proposal-uuid-3"
    ]
  }'
View API Reference → To get the result for a generated proposal, we can call the get task result endpoint. This will return both the optimized weights and generated trades for that proposal.
curl --request GET \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/optimized_proposals/{proposal_uuid}/ \
  --header 'X-Api-Key: <api-key>'
View API Reference → We can also search all generated proposals, and filter them based on criteria such as age, drift or turnover.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/optimized_proposals/list/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "status": "pending",
  "created_after": "2025-10-01",
  "min_average_drift": "0.1"
}'

Configuration Hierarchy

Optimization configuration can be set on the client-level, portfolio-level, and organization level, and are merged together based on this order of precedence:
  1. Client config (highest priority)
  2. Portfolio config
  3. Organization config (fallback defaults)
The configs are represented by the same schema, so all config values can be overriden at each level. The client and organization config is set in the customization_config field in their respective schemas. Note that client and organization values outside of customization_config are unique to the client or organization, and are not merged. For example, factor_values is a top-level field in the organization schema. It is set globally for the organization, and cannot be overridden. Most properties are set via config however, and can be flexibly overridden as needed.
For list values (e.g., no_trade assets), the system uses the most specific non-empty list available. For example, if a client has any no_trade assets defined, these completely replace any organization-level no_trade assets rather than merging.

Tilt Asset IDs

We use asset IDs throughout the API to identify assets, rather than symbols. We can use the search tickers endpoint to find the tilt asset ID corresponding to a certain ticker symbol.
curl --request GET \
  --url https://api.tilt.io/api/v1/tickers/search?symbols=MSFT&ticker_types=common_stock \
  --header 'X-Api-Key: <api-key>'