> For the complete documentation index, see [llms.txt](https://synthesys-2.gitbook.io/synthesys-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://synthesys-2.gitbook.io/synthesys-docs/network-overview/api-reference/investors.md).

# Investors

End-investors in the Synthesys network are modelled as **investors** — entities with the `IN` ID prefix that always belong to exactly one **distributor**. A distributor's backend is the only thing that should call `POST /investor/register`: the call captures the partner's own user identifier (`externalInvestorId`), produces a Synthesys-side `investorId`, and persists the link between the two.

This page covers the registration contract in depth, the relationship between `distributorId` and `externalInvestorId`, and the prerequisites that need to be in place before an investor can place an order.

### Onboarding flow

Registering an investor is the first step in onboarding, but it is not sufficient on its own. Before an investor can place a subscription order, three additional pieces of state must be configured:

1. **A wallet** for the investor on the share class's default chain.
2. **A share-class grant** giving the investor access to the share class they want to subscribe into.
3. **A live NAV** for the share class (set by the fund administrator).

Wallet setup and share-class grants are handled by **Synthesys operations** — typically as part of the onboarding process when a new investor or share class is provisioned for your distributor. You don't need to call any additional endpoints from your application after `POST /investor/register`. If a prerequisite is missing when you later try to place an order, the order endpoint returns a clear error such as *"Investor not granted access to SC000001"* or *"No wallet configured for investor on chain 1"* — coordinate with Synthesys operations to resolve.

### The investor model

An investor is the smallest unit of identity that can hold tokens or be associated with subscription/redemption activity. Two facts about the model are worth internalising:

1. **Every investor belongs to exactly one distributor.** There is no concept of a free-floating investor in the network. Registration requires a `distributorId`, and that linkage is permanent — investors do not migrate between distributors. If a partner relationship ends, the data stays under the original distributor.
2. **The partner's own user identifier travels with the record.** `externalInvestorId` is what your platform already calls the user (a UUID, a numeric ID, an opaque token — whatever you use internally). Storing it alongside the Synthesys `investorId` gives you a stable two-way mapping without your platform having to learn new identifiers.

The implication is that **your platform owns the source-of-truth identity, and the Synthesys API issues a Synthesys-side handle** that you persist back into your own user record. After registration, your investor is identifiable in three ways: by your `externalInvestorId`, by the issued `investorId`, and (if supplied) by `email` via the email-lookup index.

### Register investor

**`POST /investor/register`**

#### Request body

| Field                | Type   | Required | Description                                                                                                                                           |
| -------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `distributorId`      | string | Yes      | The distributor under which this investor is being registered. Format `DT######`                                                                      |
| `externalInvestorId` | string | Yes      | The partner's own user ID. Stored verbatim and used as the join key on your side. The API does not validate format — any non-empty string is accepted |
| `email`              | string | No       | If provided, used to populate the email-lookup index for cross-referencing investors by email. Recommended to send                                    |
| `countryCode`        | string | No       | ISO country code (e.g. `US`, `GB`, `JP`)                                                                                                              |

#### Response

Returns the full investor record wrapped in the standard envelope:

json

```json
{
  "isError": false,
  "errorMsg": "NA",
  "data": {
    "countryCode": "US",
    "distributorId": "DT000001",
    "email": "alice@apex.example",
    "externalInvestorId": "apex-user-1",
    "grantedSC": ["SC000003"],
    "status": "active"
  }
}
```

| Field                | Notes                                                                                                                                                          |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `distributorId`      | The distributor under which the investor is registered                                                                                                         |
| `externalInvestorId` | The partner-side identifier supplied on the request                                                                                                            |
| `email`              | Echoed back if provided                                                                                                                                        |
| `countryCode`        | Echoed back if provided                                                                                                                                        |
| `status`             | Lifecycle status. New investors land in `"active"`                                                                                                             |
| `grantedSC`          | Array of share-class IDs the investor has been granted access to. Empty initially — entries appear as Synthesys operations grant share classes to the investor |

Persist these fields against your platform's user record immediately. Re-read this object whenever you need to confirm the investor's current grants before placing an order.

#### Examples

**curl:**

bash

```bash
curl -sX POST $BASE/investor/register \
  -H 'Content-Type: application/json' \
  -H 'x-actor: apex-onboarding' \
  -d '{
    "distributorId": "DT000001",
    "externalInvestorId": "apex-user-1",
    "email": "alice@apex.example",
    "countryCode": "US"
  }'
```

**Node.js:**

javascript

```javascript
async function registerInvestor({ distributorId, externalInvestorId, email, countryCode }) {
  const res = await fetch(`${BASE}/investor/register`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-actor': process.env.ACTOR ?? 'system',
    },
    body: JSON.stringify({ distributorId, externalInvestorId, email, countryCode }),
  });
  const payload = await res.json();
  if (payload.isError) throw new Error(payload.errorMsg);
  return payload.data;
}

const investor = await registerInvestor({
  distributorId: 'DT000001',
  externalInvestorId: 'apex-user-1',
  email: 'alice@apex.example',
  countryCode: 'US',
});

// investor →
// {
//   countryCode: 'US',
//   distributorId: 'DT000001',
//   email: 'alice@apex.example',
//   externalInvestorId: 'apex-user-1',
//   grantedSC: ['SC000003'],
//   status: 'active'
// }
console.log(`Registered ${investor.externalInvestorId}, grants: ${investor.grantedSC.join(', ') || 'none yet'}`);
```

**Python:**

python

```python
import os, requests

def register_investor(distributor_id, external_investor_id, *, email=None, country_code=None):
    body = {
        "distributorId": distributor_id,
        "externalInvestorId": external_investor_id,
    }
    if email:
        body["email"] = email
    if country_code:
        body["countryCode"] = country_code

    r = requests.post(
        f"{BASE}/investor/register",
        json=body,
        headers={"x-actor": os.getenv("ACTOR", "system")},
        timeout=10,
    )
    payload = r.json()
    if payload["isError"]:
        raise RuntimeError(payload["errorMsg"])
    return payload["data"]

investor = register_investor(
    "DT000001",
    "apex-user-1",
    email="alice@apex.example",
    country_code="US",
)

# investor →
# {
#   "countryCode": "US",
#   "distributorId": "DT000001",
#   "email": "alice@apex.example",
#   "externalInvestorId": "apex-user-1",
#   "grantedSC": ["SC000003"],
#   "status": "active"
# }
grants = ", ".join(investor["grantedSC"]) or "none yet"
print(f"Registered {investor['externalInvestorId']}, grants: {grants}")
```

### Idempotency and re-registration

Repeated calls with the same `(distributorId, externalInvestorId)` pair will produce a new Synthesys `investorId` each time. Your application must guard against duplicates by tracking whether you have already registered a given user.

The recommended pattern:

1. Before registering, check whether your platform's user record already has a Synthesys `investorId` persisted.
2. If yes, skip the API call.
3. If no, call `POST /investor/register`, then persist the returned `investorId` immediately.

A small wrapper:

**Node.js:**

javascript

```javascript
async function ensureInvestor(user) {
  if (user.synthesysInvestorId) return user.synthesysInvestorId;

  const { investorId } = await registerInvestor({
    distributorId: process.env.DISTRIBUTOR_ID,
    externalInvestorId: user.id,
    email: user.email,
    countryCode: user.country,
  });

  await db.users.update(user.id, { synthesysInvestorId: investorId });
  return investorId;
}
```

**Python:**

python

```python
def ensure_investor(user):
    if user.get("synthesys_investor_id"):
        return user["synthesys_investor_id"]

    investor = register_investor(
        distributor_id=os.environ["DISTRIBUTOR_ID"],
        external_investor_id=user["id"],
        email=user.get("email"),
        country_code=user.get("country"),
    )
    db.users.update(user["id"], synthesys_investor_id=investor["investorId"])
    return investor["investorId"]
```

### Common errors

* **`distributorId not found: DT…`** — the distributor doesn't exist. Either it was never created in this environment or the ID is wrong. Coordinate with Synthesys operations.
* **`distributorId is required` / `externalInvestorId is required`** — one of the two required fields was missing or empty. The API treats empty strings as missing.
* **No error, but the wrong data was stored** — easy to introduce when copy-pasting `externalInvestorId` between environments. Always log the returned `investorId` against the request payload so you have a forensic trail.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://synthesys-2.gitbook.io/synthesys-docs/network-overview/api-reference/investors.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
