> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gu1.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Materialize relationships

> Create or update shareholders and related entities from normalized enrichment (Brazil and supported countries) — in the gu1 universal entity model for KYC.

## Overview

Materializes **relationships and related entities** for an existing **person** or **company** using the latest row in `normalized_enrichment`. Typical use cases:

* **Brazil — company**: create/update the shareholder chain (QSA) from normalized data.
* **Brazil — person**: create/update related companies and related persons from normalized data.

The endpoint can optionally **re-run enrichments on the root dossier** first (`runEnrichmentFirst`): only the **country strategy** provider(s) needed to refresh shareholders/relationships in normalized data (not the full marketplace list). Root enrichment always requests **fresh** provider data for that step. Then it runs the same style of shareholder/related-entity pipeline as [automatic creation](/en/api-reference/company/create-automatic).

<Note>
  The entity **must** be `company` or `person`. Other types return **400**. The dossier **country** must have an automatic-creation strategy (today this flow is primarily used for **Brazil**; other countries may return **400** if defaults are missing or depth/providers are invalid).
</Note>

## Endpoint

```
POST http://api.gu1.ai/entities/{entityId}/relationships/materialize
```

## Authentication

Requires a valid API key:

```bash theme={null}
Authorization: Bearer YOUR_API_KEY
```

## Path Parameters

<ParamField path="entityId" type="string" required>
  UUID of the root entity (person or company) whose relationships you want to materialize.
</ParamField>

## Request Body

<ParamField body="runEnrichmentFirst" type="boolean" default="false">
  When `true`, the API re-executes on the **root** dossier only the enrichment provider(s) the **country strategy** requires for that entity type to populate shareholders/relationships in normalized data (e.g. Brazil company vs person). It does **not** run all marketplace enrichments on the root.

  When `false`, a **`normalized_enrichment`** row must already exist for the entity; otherwise the API returns **422** (`NO_NORMALIZED_ENRICHMENT`).
</ParamField>

<ParamField body="depth" type="integer" default="1">
  How many levels of shareholders or related entities to process. Allowed range **0–5** (schema default **1**).
</ParamField>

<ParamField body="autoExecuteIntegrationsShareholders" type="object">
  Same shape as **`POST /entities/automatic`** → `autoExecuteIntegrationsShareholders`: `executeAllActiveEnrichments`, `enrichments: { company, person }`, optional **`enrichmentGroupRefs`**, and **`excludeEnrichments`** (applied to both company and person lists). Ignored when **`executeOnlyRootData`** is `true`.

  ```json theme={null}
  {
    "executeAllActiveEnrichments": true,
    "excludeEnrichments": ["br_bdc_shareholders_enrichment"],
    "enrichmentGroupRefs": ["child_entities_group_slug"]
  }
  ```
</ParamField>

<ParamField body="executeOnlyRootData" type="boolean" default="false">
  When `true`, each **new** child is created from the **root dossier normalized** data only (name + tax ID) and **no** child integration pipeline runs (`autoExecuteIntegrationsShareholders` is not applied). Applies to **company** (QSA shareholders) and **person** dossiers in Brazil (related companies/persons from normalized). Cannot be combined with a non-empty `autoExecuteIntegrationsShareholders` pipeline.
</ParamField>

<ParamField body="runInBackground" type="boolean" default="true">
  When **`true`** (default), the API returns **202** immediately with `data.status: "processing"` and runs enrichment, materialization, and optional risk matrix on the server. Completion is signaled over **Socket.IO** (see below).

  When **`false`**, the server waits for the full pipeline and returns **200** with counts and flags in `data`.
</ParamField>

<ParamField body="riskMatrix" type="object">
  Optional. After the pipeline finishes, run the **rules / risk matrix** on the **root** entity only.

  * **`execute`** (boolean, required when the object is sent): set `true` to run the matrix after materialization.
  * **`riskMatrixId`** (UUID | null, optional): **`null`** or omitted → use the matrix **assigned to the entity**; a UUID → run that matrix for **this execution only** (override, does not change the entity row).

  ```json theme={null}
  "riskMatrix": { "execute": true, "riskMatrixId": null }
  ```
</ParamField>

<ParamField body="refreshEnrichShareholders" type="boolean">
  When `true`, if a discovered child entity (shareholder / related company or person) **already exists** in your org (same tax ID), the pipeline **re-runs** configured enrichments for that child instead of skipping refresh.
</ParamField>

## Async behavior (default): HTTP 202 + Socket.IO

When `runInBackground` is `true`, the response is **202**:

```json theme={null}
{
  "success": true,
  "data": {
    "status": "processing",
    "entityId": "550e8400-e29b-41d4-a716-446655440000",
    "countryCode": "BR",
    "entityType": "company"
  }
}
```

Subscribe to your existing **Socket.IO** connection (same as the dashboard). Events:

| Event                                       | When                            | Payload (high level)                                                                                                                                         |
| ------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `entity:relationship-materialize-started`   | Pipeline queued                 | `entityId`, `mainEntityName`, `mainEntityTaxId`, `mainEntityType`, `userId`                                                                                  |
| `entity:relationship-materialize-completed` | Success or partial pipeline end | `entityId`, `success`, `entitiesCreated`, `relationshipsCreated`, `enrichmentExecuted`, `enrichmentProviders`, `riskMatrixExecuted`, optional `error` string |
| `entity:relationship-materialize-failed`    | Unhandled failure               | `entityId`, `error: { code, message, details? }`, `userId`                                                                                                   |

After completion, refetch the entity, relationships, and list endpoints as needed (e.g. `GET /entities/:id`, list queries).

## Synchronous response (runInBackground: false): HTTP 200

```json theme={null}
{
  "success": true,
  "data": {
    "entityId": "550e8400-e29b-41d4-a716-446655440000",
    "countryCode": "BR",
    "entityType": "company",
    "entitiesCreated": 3,
    "relationshipsCreated": 5,
    "enrichmentExecuted": true,
    "enrichmentProviders": ["br_bdc_shareholders_enrichment"],
    "riskMatrixExecuted": true
  }
}
```

If the materialization step finishes with a **pipeline warning** (e.g. partial graph), `success` may be `true` while `data.error` carries a human-readable message—check both.

## Example: Brazil company, async + risk matrix

```bash theme={null}
curl -X POST "http://api.gu1.ai/entities/550e8400-e29b-41d4-a716-446655440000/relationships/materialize" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "runEnrichmentFirst": true,
    "depth": 2,
    "autoExecuteIntegrationsShareholders": {
      "executeAllActiveEnrichments": false,
      "enrichments": {
        "company": ["br_bdc_shareholders_enrichment"],
        "person": ["br_cpfcnpj_complete_person_enrichment"]
      },
      "enrichmentGroupRefs": ["child_entities_group_slug"]
    },
    "runInBackground": true,
    "riskMatrix": { "execute": true, "riskMatrixId": null }
  }'
```

## Example: use a specific matrix once (override)

```json theme={null}
{
  "runEnrichmentFirst": false,
  "depth": 1,
  "runInBackground": false,
  "riskMatrix": {
    "execute": true,
    "riskMatrixId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
  }
}
```

## Error responses (selection)

| HTTP | Code (typical)                           | Meaning                                                                            |
| ---- | ---------------------------------------- | ---------------------------------------------------------------------------------- |
| 400  | `INVALID_ENTITY_TYPE`                    | Entity is not `company` or `person`                                                |
| 400  | `COUNTRY_NOT_SUPPORTED`                  | No strategy / materialization not supported for this country                       |
| 400  | `NO_DEFAULT_RELATIONSHIP_ENRICHMENTS`    | Country strategy defines no relationship enrichment providers for this entity type |
| 404  | `ENTITY_NOT_FOUND`                       | Unknown `entityId` for the organization                                            |
| 422  | `NO_NORMALIZED_ENRICHMENT`               | `runEnrichmentFirst: false` but no normalized row exists                           |
| 422  | `INVALID_RELATIONSHIP_ENRICHMENT_CONFIG` | Invalid child enrichment config / depth for the strategy                           |
| 500  | `ENRICHMENT_FAILED`                      | Enrichment failed when `runEnrichmentFirst` is `true` (details in error payload)   |

## See Also

* [Analyze entity](/en/api-reference/entities/analyze) — manual rules run on demand
* [Create person](/en/api-reference/person/create) / [Create company](/en/api-reference/company/create)
* [Create automatic (person)](/en/api-reference/person/create-automatic) / [Create automatic (company)](/en/api-reference/company/create-automatic)
* [Person provider codes](/en/api-reference/integrations/person-provider-codes) / [Company provider codes](/en/api-reference/integrations/company-provider-codes)
* [Tax ID formats](/en/api-reference/entities/tax-id-formats)
