Addepar Compatibility Guide
This guide describes how the INHERIT open estate data standard maps to Addepar's data model, based on publicly available documentation. All field mappings and API details are proposed and have not been validated against a live Addepar instance.
Addepar is a trademark of Addepar, Inc.. This guide is not endorsed by, affiliated with, or sponsored by Addepar. Get in touch to collaborate on an official integration.
How to map Addepar's wealth management API — entities, positions, and attributes — to INHERIT schemas for estate data interchange.
How to map Addepar’s wealth management API to INHERIT schemas for estate planning data interchange.
1. Overview#
Addepar ↗ is a wealth management technology platform managing over $9 trillion AUM across 1,000+ RIAs, private banks, and family offices. Its core strength is an ownership graph — a hierarchical model that tracks who owns what, through which structures, and at what percentage.
Addepar’s REST API (documented at developers.addepar.com ↗ ) exposes:
- Entities — clients, households, legal entities, accounts, and investments
- Positions — the ownership graph, with percentage-based ownership at every level
- Attributes — 300+ data fields across entity types
- Files — document attachments
This guide maps Addepar’s entity hierarchy and position model to INHERIT v4 schemas, enabling wealth managers to export structured estate data in INHERIT format.
2. Entity Hierarchy Mapping#
Addepar’s entity hierarchy follows a top-down ownership model:
Household
└── Client (natural person)
└── Legal Entity (trust, LLC, foundation)
└── Account (brokerage, bank, custody)
└── Investment (individual security/holding)2.1 Household → Family Group#
Addepar Households group related clients (typically a married couple and dependants) for consolidated reporting.
INHERIT does not have a dedicated “household” schema. Map households using the estate document itself — an INHERIT document naturally groups all people, trusts, and assets related to an estate. For multi-generational family offices, each household maps to a separate INHERIT document, or use the companionEstateId field to link spouse estates.
| Addepar Field | INHERIT Mapping | Notes |
|---|---|---|
household.id |
Document-level reference | Store as external identifier |
household.name |
Document metadata | e.g. “The Davies Family” |
household.members[] |
persons[] array |
Each member becomes a Person entity |
2.2 Client → person.json#
Addepar Clients are natural persons — the beneficial owners of wealth.
| Addepar Field | INHERIT Field | Schema |
|---|---|---|
client.id |
person.identifiers[].value (system: urn:addepar:client) |
person.json |
client.name |
person.givenName + person.familyName |
person.json |
client.date_of_birth |
person.dateOfBirth |
person.json |
client.date_of_death |
person.dateOfDeath |
person.json |
client.email |
person.contact.email |
person.json |
client.phone |
person.contact.phone |
person.json |
client.address |
person.contact.address |
person.json |
client.tax_id |
person.taxResidency[].taxpayerIdentifier |
person.json |
client.citizenship |
person.domicile |
person.json — note: domicile and citizenship differ legally |
client.roles |
person.roles[] |
Map: “beneficiary” → beneficiary, “trustee” → trustee |
Name parsing note: Addepar stores names as a single string in many configurations. Use the Attributes API to fetch structured name components where available. If only a single name string is available, apply heuristic splitting and set person.provenance.method to "automated" with appropriate confidence.
2.3 Legal Entity → trust.json / organisation.json#
Addepar Legal Entities represent trusts, LLCs, family limited partnerships, private foundations, and other structures.
Routing logic:
Addepar legal_entity.type |
INHERIT Schema | Notes |
|---|---|---|
trust, revocable_trust, irrevocable_trust |
trust.json |
Map trust type to trustType enum |
llc, corporation, partnership |
organisation.json |
Set organisationType to relevant value |
foundation |
organisation.json |
Set organisationType to "charity" if charitable, else "other" |
| [TBC] Other entity types | Case-by-case | Addepar supports custom entity types |
Trust mapping:
| Addepar Field | INHERIT Field | Schema |
|---|---|---|
legal_entity.id |
Store as external identifier | trust.json |
legal_entity.name |
trust.name |
trust.json |
legal_entity.type = revocable_trust |
trust.trustType = "discretionary" + trust.revocability = "revocable" |
trust.json |
legal_entity.type = irrevocable_trust |
trust.trustType = [determine from attributes] + trust.revocability = "irrevocable" |
trust.json |
legal_entity.inception_date |
trust.createdAt |
trust.json |
legal_entity.governing_jurisdiction |
trust.governingLaw |
trust.json |
| Ownership positions (settlor) | trust.settlor → Person.id |
trust.json |
| Ownership positions (beneficiaries) | trust.beneficiaries[] |
trust.json |
| Ownership positions (trustees) | trust.trustees[] |
trust.json |
Organisation mapping:
| Addepar Field | INHERIT Field | Schema |
|---|---|---|
legal_entity.id |
Store as external identifier | organisation.json |
legal_entity.name |
organisation.name |
organisation.json |
legal_entity.type = llc |
organisation.organisationType = "other" |
organisation.json |
legal_entity.tax_id (EIN) |
organisation.registrations[].number |
organisation.json |
legal_entity.jurisdiction |
organisation.jurisdiction |
organisation.json |
2.4 Account → asset.json (financial category)#
Addepar Accounts are financial containers — brokerage accounts, bank accounts, custody accounts, retirement accounts.
| Addepar Field | INHERIT Field | Schema |
|---|---|---|
account.id |
asset.identifiers[].value (system: urn:addepar:account) |
asset.json — [TBC] identifiers not yet on asset.json |
account.name |
asset.name |
asset.json |
account.type |
asset.category = "financial" + asset.subcategory |
asset.json |
account.custodian |
asset.notes or linked organisation.json |
asset.json |
account.account_number |
asset.notes (sensitive — handle with care) |
asset.json |
account.inception_date |
[TBC] No direct mapping | asset.json |
Subcategory mapping:
| Addepar Account Type | INHERIT subcategory |
|---|---|
| Brokerage | "investment_account" |
| IRA / 401(k) / Roth | "pension" |
| Bank account | "bank_account" |
| Custody | "investment_account" |
| [TBC] Others | Map on a case-by-case basis |
2.5 Investment → asset.json (specific holding)#
Addepar Investments are individual securities or holdings within an account.
| Addepar Field | INHERIT Field | Schema |
|---|---|---|
investment.id |
Store as external identifier | asset.json |
investment.ticker |
asset.name (include ticker) |
asset.json |
investment.asset_class |
asset.category = "financial" + asset.subcategory |
asset.json |
investment.quantity |
asset.notes |
asset.json |
investment.market_value |
asset.estimatedValue |
asset.json |
investment.cost_basis |
asset.notes |
asset.json |
investment.currency |
asset.estimatedValue.currency |
common/money.json |
3. Position Mapping — Ownership Percentages#
Addepar’s Position model is its most powerful feature for estate planning. Positions express the ownership graph:
Client (person) ──[owns 50%]──► Trust ──[holds 100%]──► Account ──[contains]──► Investment3.1 Positions to Beneficiary Shares#
Addepar ownership percentages map to INHERIT beneficiary structures:
// Addepar position: Client owns 33.33% of a trust
const addepar_position = {
owner_id: "client-123",
owned_id: "trust-456",
ownership_percentage: 33.33
};
// INHERIT mapping
const trust_beneficiary: TrustBeneficiary = {
personId: "f47ac10b-...", // mapped from client-123
interestType: "both", // or "income"/"capital" if known
// Note: INHERIT trust beneficiaries do not currently have a
// percentage field — this is a known gap. Store the percentage
// in notes or use x-inherit-addepar extension.
};3.2 Ownership Percentage Gap#
INHERIT’s trust.json beneficiary model uses interestType (income/capital/both/discretionary) but does not currently carry a numeric ownership percentage. Addepar’s positions are percentage-based.
Recommended approaches:
-
Extension field: Use
x-inherit-addeparon the trust or person to store the percentage:json{ "x-inherit-addepar": { "ownershipPercentage": 33.33, "positionId": "pos-789" } } -
Notes field: Record the percentage in
trust.notesas human-readable text. -
Proposal: A future INHERIT version may add
shareorpercentagetoTrustBeneficiary. [TBC]
4. Attribute Mapping#
Addepar exposes 300+ attributes across entity types. Most are custom-configured per firm. The Attributes API (GET /v1/attributes) returns the attribute catalogue for a given firm.
4.1 Common Attributes to INHERIT Fields#
| Addepar Attribute | INHERIT Field | Notes |
|---|---|---|
first_name |
person.givenName |
|
last_name |
person.familyName |
|
middle_name |
person.additionalName |
|
date_of_birth |
person.dateOfBirth |
|
date_of_death |
person.dateOfDeath |
|
ssn / tax_id |
person.taxResidency[].taxpayerIdentifier |
Sensitive — PII handling required |
citizenship |
person.domicile |
Caveat: citizenship ≠ domicile |
email |
person.contact.email |
|
phone |
person.contact.phone |
|
trust_type |
trust.trustType |
Map to INHERIT enum values |
trust_date |
trust.createdAt |
|
jurisdiction |
trust.governingLaw or organisation.jurisdiction |
|
entity_type |
Routing field — determines target schema | |
market_value |
asset.estimatedValue |
Convert to minor units |
cost_basis |
asset.notes |
No dedicated INHERIT field |
asset_class |
asset.subcategory |
|
account_type |
asset.subcategory |
|
custodian_name |
Linked organisation.json |
|
| [TBC] Custom attributes | x-inherit-addepar extension |
Firm-specific |
4.2 Currency and Money Conversion#
Addepar stores monetary values as decimal numbers with a currency code. INHERIT uses integer minor units (pennies/cents).
function addepar_to_inherit_money(
value: number,
currency: string
): { amount: number; currency: string } {
// Addepar: 1234.56 USD
// INHERIT: { amount: 123456, currency: "USD" }
const minor_units = Math.round(value * 100);
return { amount: minor_units, currency: currency.toUpperCase() };
}Warning: Not all currencies use 2 decimal places. JPY uses 0, KWD uses 3. Use a currency-aware conversion library in production.
5. Valuation Data#
Addepar positions include market values that map to INHERIT’s valuation.json.
5.1 Position Values to Valuations#
| Addepar Field | INHERIT Field | Schema |
|---|---|---|
position.market_value |
valuation.valuedAmount |
valuation.json |
position.as_of_date |
valuation.valuationDate |
valuation.json |
| — | valuation.entityType = "asset" |
valuation.json |
| — | valuation.entityId → mapped Asset.id |
valuation.json |
| — | valuation.providerType = "other" |
valuation.json |
| — | valuation.provider = "Addepar" |
valuation.json |
| — | valuation.method = "market_listing" |
valuation.json |
| — | valuation.valuationPurpose = "current_estimate" |
valuation.json |
| — | valuation.confidence = "medium" |
valuation.json |
5.2 Date-of-Death Valuations#
For estate administration, request positions as at the date of death:
GET /v1/positions?as_of_date=2026-01-15&entity_id=household-123Set valuation.valuationPurpose to "date_of_death" for these valuations.
6. Code Examples#
6.1 Fetching Entities and Building an INHERIT Document#
import { v4 as uuid } from "uuid";
interface AddeparEntity {
id: string;
type: string;
attributes: Record<string, unknown>;
}
interface InheritPerson {
id: string;
givenName: string;
familyName?: string;
roles: string[];
dateOfBirth?: string;
contact?: {
email?: string;
phone?: string;
};
identifiers?: Array<{
system: string;
value: string;
type: string;
}>;
}
/**
* Convert an Addepar client entity to an INHERIT Person.
*/
function mapClientToPerson(client: AddeparEntity): InheritPerson {
const attrs = client.attributes;
return {
id: uuid(),
givenName: String(attrs.first_name ?? ""),
familyName: attrs.last_name ? String(attrs.last_name) : undefined,
roles: ["beneficiary"], // refine from positions
dateOfBirth: attrs.date_of_birth
? String(attrs.date_of_birth)
: undefined,
contact: {
email: attrs.email ? String(attrs.email) : undefined,
phone: attrs.phone ? String(attrs.phone) : undefined,
},
identifiers: [
{
system: "urn:addepar:client",
value: client.id,
type: "platform_id",
},
],
};
}6.2 Fetching Positions with Rate-Limit Handling#
interface AddeparPosition {
owner_id: string;
owned_id: string;
ownership_percentage: number;
market_value: number;
currency: string;
as_of_date: string;
}
class AddeparClient {
private base_url: string;
private auth_header: string;
private request_count = 0;
private window_start = Date.now();
constructor(firm_id: string, api_key: string, api_secret: string) {
this.base_url = `https://api.addepar.com/v1/firms/${firm_id}`;
this.auth_header =
"Basic " + Buffer.from(`${api_key}:${api_secret}`).toString("base64");
}
/**
* Rate-limit-aware fetch. Addepar allows 50 requests per 15 minutes.
* Backs off automatically when approaching the limit.
*/
private async rate_limited_fetch(path: string): Promise<unknown> {
const WINDOW_MS = 15 * 60 * 1000; // 15 minutes
const MAX_REQUESTS = 50;
const SAFETY_MARGIN = 5; // leave headroom
const elapsed = Date.now() - this.window_start;
if (elapsed > WINDOW_MS) {
this.request_count = 0;
this.window_start = Date.now();
}
if (this.request_count >= MAX_REQUESTS - SAFETY_MARGIN) {
const wait_ms = WINDOW_MS - elapsed + 1000;
console.log(
`Rate limit approaching — waiting ${Math.ceil(wait_ms / 1000)}s`
);
await new Promise((resolve) => setTimeout(resolve, wait_ms));
this.request_count = 0;
this.window_start = Date.now();
}
const response = await fetch(`${this.base_url}${path}`, {
headers: {
Authorization: this.auth_header,
Accept: "application/vnd.api+json",
},
});
this.request_count++;
if (response.status === 429) {
const retry_after = Number(response.headers.get("Retry-After")) || 60;
console.log(`Rate limited — retrying in ${retry_after}s`);
await new Promise((resolve) =>
setTimeout(resolve, retry_after * 1000)
);
return this.rate_limited_fetch(path);
}
if (!response.ok) {
throw new Error(
`Addepar API error: ${response.status} ${response.statusText}`
);
}
return response.json();
}
async get_entities(entity_type: string): Promise<AddeparEntity[]> {
const data = (await this.rate_limited_fetch(
`/entities?filter[entity_type]=${entity_type}`
)) as { data: AddeparEntity[] };
return data.data;
}
async get_positions(
entity_id: string,
as_of_date: string
): Promise<AddeparPosition[]> {
const data = (await this.rate_limited_fetch(
`/positions?entity_id=${entity_id}&as_of_date=${as_of_date}`
)) as { data: AddeparPosition[] };
return data.data;
}
}6.3 Full Import Pipeline#
async function import_from_addepar(
client: AddeparClient,
household_id: string,
as_of_date: string
): Promise<void> {
// 1. Fetch all clients in the household
const clients = await client.get_entities("client");
// 2. Map to INHERIT persons
const persons = clients.map(mapClientToPerson);
// 3. Fetch legal entities (trusts, LLCs)
const legal_entities = await client.get_entities("legal_entity");
// 4. Route to trust.json or organisation.json
const trusts = legal_entities
.filter((e) => ["trust", "revocable_trust", "irrevocable_trust"]
.includes(String(e.attributes.entity_type)))
.map(mapLegalEntityToTrust);
const organisations = legal_entities
.filter((e) => ["llc", "corporation", "partnership"]
.includes(String(e.attributes.entity_type)))
.map(mapLegalEntityToOrganisation);
// 5. Fetch accounts → asset.json (financial)
const accounts = await client.get_entities("account");
const assets = accounts.map(mapAccountToAsset);
// 6. Fetch positions → valuation.json + ownership
const positions = await client.get_positions(household_id, as_of_date);
const valuations = positions.map(mapPositionToValuation);
// 7. Assemble the INHERIT document
const inherit_document = {
"$schema": "https://openinherit.org/v4/inherit.json",
persons,
trusts,
organisations,
assets,
valuations,
};
console.log(JSON.stringify(inherit_document, null, 2));
}7. Rate Limit Strategy#
Addepar enforces tight rate limits:
| Limit | Window | Notes |
|---|---|---|
| 50 requests | 15 minutes | Per API key |
| 1,000 requests | 24 hours | Per API key |
7.1 Strategies#
-
Cache aggressively. Entity data (persons, trusts) rarely changes. Cache for 24 hours minimum. Only positions/valuations need fresh data.
-
Batch by entity type. Fetch all clients in one paginated request rather than fetching each individually.
-
Use
includeparameters. Addepar’s JSON:API implementation supportsincludeto sideload related entities, reducing round trips. -
Schedule overnight syncs. Run full imports during off-peak hours. Use incremental updates (filtered by
modified_since) during the day. -
Implement exponential backoff. When receiving
429 Too Many Requests, respect theRetry-Afterheader. Never retry immediately. -
Budget your requests. A typical household import requires approximately:
- 1 request for household members
- 1 request for client entities
- 1 request for legal entities
- 1 request for accounts
- 1 request for positions
- Total: ~5 requests per household (before pagination)
At 50 requests/15 minutes, you can import roughly 10 households per window.
7.2 Webhook Alternative#
Addepar does not currently offer webhooks. Polling is the only option. [TBC] — check for webhook support in future API versions.
8. Authentication#
Addepar supports two authentication methods:
8.1 Basic Auth#
Authorization: Basic base64(API_KEY:API_SECRET)Suitable for server-to-server integrations. API credentials are issued per firm by Addepar’s support team.
8.2 OAuth 2.0#
For user-facing applications, Addepar supports OAuth 2.0 with authorisation code flow. Contact Addepar for client credentials and scope details. [TBC] — exact OAuth scopes for entity/position access.
8.3 Firm-Scoped Requests#
All API requests are scoped to a firm. The firm ID appears in the URL path:
GET https://api.addepar.com/v1/firms/{firm_id}/entitiesA single API key can access only one firm. Multi-firm integrations require separate credentials per firm.
9. Edge Cases and Limitations#
9.1 What Addepar’s Ownership Model Cannot Express in INHERIT#
| Addepar Concept | INHERIT Gap | Workaround |
|---|---|---|
| Fractional ownership percentages | TrustBeneficiary has no percentage field |
Use x-inherit-addepar extension or notes |
| Multi-level ownership chains | INHERIT is flat — person owns asset, not person → trust → account → investment | Flatten the chain: link the person directly to the end asset, note the chain in provenance |
| Account-level grouping | INHERIT has no “account” entity — assets are individual | Group related assets using assetCollections or naming conventions |
| Custodian relationships | No dedicated custodian field on asset.json |
Link to an organisation.json in notes or use extension |
| Performance/return data | INHERIT has no performance tracking | Out of scope — INHERIT captures point-in-time valuations, not time-series returns |
| Tax lot accounting | INHERIT does not track cost basis lots | Store in asset.notes if needed for estate tax calculations |
9.2 Data Quality Considerations#
- Name parsing: Addepar may store names as a single string. Always validate parsed components.
- Currency mismatches: A single household may hold assets in multiple currencies. Ensure each
money.jsoninstance carries the correct currency code. - Stale positions: Addepar positions are as-of-date snapshots. Always record
valuation.valuationDateand check freshness. - Custom entity types: Firms may define custom entity types beyond the standard set. These will not match the routing logic in section 2.3 — log and flag for manual review.
- PII handling: Addepar attributes include SSN, tax IDs, and contact details. Ensure INHERIT documents carrying this data are handled according to the relevant data protection regime (GDPR, CCPA, etc.). Use
writeOnly: truefields andvisibilitycontrols.
9.3 Missing Mappings#
The following Addepar concepts have no direct INHERIT equivalent and are out of scope for v4:
- Investment performance attribution
- Rebalancing targets and drift
- Fee schedules and billing
- Compliance screening results
- Document management (beyond file references)