Skip to main content
Portfolios group H3 cell locations into named collections for ongoing monitoring and scoring. Each portfolio can hold up to 2,000 locations, apply a custom scoring formula, and track weighted composite scores with 30-day movement trends. Portfolios support three visibility levels — private (owner only), team, and org — so you can share scoring results across your workspace.

GET /api/v1/locus/portfolios

Requires session authentication.
List all portfolios you have access to, including aggregate scoring metrics. Response
{
  "portfolios": [
    {
      "id": "p_abc123",
      "name": "West Coast QSR Sites",
      "description": "Quick-service restaurant expansion candidates",
      "asset_class": "qsr",
      "active_formula_id": "f_def456",
      "visibility": "team",
      "member_count": 42,
      "weighted_composite": 71.8,
      "formula": {
        "id": "f_def456",
        "name": "QSR v2",
        "asset_class": "qsr",
        "version": 2
      },
      "latest_refresh_run": {
        "status": "completed",
        "requested_at": "2026-04-19T10:00:00Z",
        "completed_at": "2026-04-19T10:02:34Z"
      },
      "metrics": {
        "weighted_composite_30d_start": 69.2,
        "weighted_composite_30d_end": 71.8,
        "weighted_delta_30d": 2.6,
        "coverage_pct": 95.2,
        "weighted_coverage_pct": 96.1,
        "severe_exposure_pct": 4.8,
        "elevated_exposure_pct": 14.3,
        "stable_exposure_pct": 80.9
      },
      "created_at": "2026-03-01T08:00:00Z",
      "updated_at": "2026-04-19T10:02:34Z"
    }
  ]
}
FieldTypeDescription
member_countintNumber of locations in the portfolio.
weighted_compositefloat | nullWeight-adjusted composite score across all locations.
formulaobject | nullActive scoring formula applied to this portfolio.
metrics.weighted_delta_30dfloat | null30-day score movement (positive = improving).
metrics.coverage_pctfloatPercentage of locations with scoring data.
metrics.severe_exposure_pctfloatWeighted percentage of locations scoring below 30.
metrics.elevated_exposure_pctfloatWeighted percentage of locations scoring 30–50.
metrics.stable_exposure_pctfloatWeighted percentage of locations scoring 50+.
Example
curl https://axiomlocus.io/api/v1/locus/portfolios \
  -H "Authorization: Bearer YOUR_TOKEN"

POST /api/v1/locus/portfolios

Requires session authentication.
Create a new portfolio. If you provide an asset_class, Locus automatically assigns the matching system scoring formula. Parameters
NameTypeRequiredDescription
namestringPortfolio name.
descriptionstringHuman-readable description.
asset_classstringAsset class (e.g. qsr, retail, office, industrial, self_storage, data_center). Auto-assigns the system formula.
visibilitystringprivate (default), team, or org.
org_idstringWorkspace org id. Required when visibility is team or org.
team_idstringWorkspace team id. Optional, used with team visibility.
Example
curl -X POST https://axiomlocus.io/api/v1/locus/portfolios \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Midwest Industrial Sites",
    "asset_class": "industrial",
    "description": "Distribution center candidates in the Midwest corridor"
  }'
Response
{
  "portfolio": {
    "id": "p_new789",
    "name": "Midwest Industrial Sites",
    "asset_class": "industrial",
    "active_formula_id": "f_sys_industrial_v1",
    "visibility": "private",
    "member_count": 0,
    "weighted_composite": null,
    "created_at": "2026-04-20T12:00:00Z"
  }
}

Portfolio locations

Manage the H3 cell locations within a portfolio. Each location is identified by an h3_index and profile pair.

GET /api/v1/locus/portfolios/{portfolioId}/locations

Requires session authentication.
List all locations in a portfolio with their current scores. If the portfolio has an active formula, scores are computed using the formula weights rather than the default composite. Response
{
  "locations": [
    {
      "id": "loc_001",
      "h3_index": "882a100d63fffff",
      "profile": "qsr",
      "weight": 1.5,
      "asset_class": "qsr",
      "source": "manual",
      "address_input": "123 Market St, San Francisco, CA",
      "latitude": 37.7749,
      "longitude": -122.4194,
      "composite": 78.4,
      "metro_slug": "sf",
      "neighborhood": "Financial District",
      "formula_id_used": "f_def456",
      "formula_name_used": "QSR v2",
      "formula_version_used": 2,
      "created_at": "2026-03-15T09:00:00Z"
    }
  ]
}
FieldTypeDescription
weightfloatRelative importance of this location in portfolio aggregations (default 1).
sourcestringHow the location was added: manual or import.
compositefloat | nullScore computed with the active formula, or the default composite if no formula is set.

POST /api/v1/locus/portfolios/{portfolioId}/locations

Requires session authentication.
Add a location to the portfolio. If a location with the same h3_index and profile already exists, it is updated (upsert). Parameters
NameTypeRequiredDescription
h3_indexstringH3 cell id at resolution 8.
profilestringScoring profile (default general).
weightnumberRelative weight for portfolio aggregation (default 1).
asset_classstringAsset class label for this location.
address_inputstringOriginal address for reference.
latitudenumberLatitude for display.
longitudenumberLongitude for display.
Example
curl -X POST https://axiomlocus.io/api/v1/locus/portfolios/p_abc123/locations \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "h3_index": "882a100d63fffff",
    "profile": "qsr",
    "weight": 1.5,
    "address_input": "123 Market St, San Francisco, CA",
    "latitude": 37.7749,
    "longitude": -122.4194
  }'

PATCH /api/v1/locus/portfolios/{portfolioId}/locations

Requires session authentication.
Update a location’s weight or asset class. Parameters
NameTypeRequiredDescription
h3_indexstringH3 cell id to update.
profilestringScoring profile (default general).
weightnumberNew weight value (must be positive).
asset_classstringNew asset class label.

DELETE /api/v1/locus/portfolios/{portfolioId}/locations

Requires session authentication.
Remove a location from the portfolio. Query parameters
NameTypeRequiredDescription
h3_indexstringH3 cell id to remove.
profilestringScoring profile (default general).
Example
curl -X DELETE "https://axiomlocus.io/api/v1/locus/portfolios/p_abc123/locations?h3_index=882a100d63fffff&profile=qsr" \
  -H "Authorization: Bearer YOUR_TOKEN"

Bulk import

POST /api/v1/locus/portfolios/{portfolioId}/locations/import

Requires session authentication.
Import multiple locations into a portfolio in a single request. Accepts a JSON array of location objects. Existing locations (matching h3_index + profile) are updated; new ones are created. Request body
{
  "locations": [
    { "h3_index": "882a100d63fffff", "profile": "retail", "weight": 2.0, "address_input": "123 Market St" },
    { "h3_index": "882a100d65fffff", "profile": "retail", "weight": 1.0, "address_input": "456 Oak Ave" }
  ]
}

Scoring refresh

GET /api/v1/locus/portfolios/{portfolioId}/refresh

Requires session authentication.
List recent scoring refresh runs for the portfolio. Query parameters
NameTypeRequiredDescription
limitnumberMax runs to return (default 20, max 100).
Response
{
  "runs": [
    {
      "id": "run_001",
      "status": "completed",
      "requested_at": "2026-04-19T10:00:00Z",
      "started_at": "2026-04-19T10:00:05Z",
      "completed_at": "2026-04-19T10:02:34Z",
      "error_message": null
    }
  ]
}

POST /api/v1/locus/portfolios/{portfolioId}/refresh

Requires session authentication.
Queue a scoring refresh. The refresh is processed asynchronously — scores for all locations in the portfolio are recomputed with the latest data and the active formula. Response (HTTP 202)
{
  "run": {
    "id": "run_002",
    "status": "queued",
    "requested_at": "2026-04-20T12:00:00Z"
  }
}
StatusDescription
queuedWaiting for the next processing cycle.
runningScoring is in progress.
completedAll locations have been rescored.
failedAn error occurred. Check error_message.