# Onboarding API

***

## Authentication

All endpoints require API key authentication with the `OnboardingApi` role. Contact Superstate for an API key.

***

## Sending a Request

For information on how to send a request, please see the [API key section on the API page](/investors/api.md#using-api-key-authenticated-endpoints).

***

## Endpoints

| Method | Path                                     | Description                      |
| ------ | ---------------------------------------- | -------------------------------- |
| POST   | `/v1/accounts/onboard/svm`               | Create a new entity on Solana    |
| POST   | `/v1/accounts/onboard/svm/mock`          | Mock create on Solana (testing)  |
| POST   | `/v1/accounts/onboard/svm/add-allowlist` | Add allowlist address on Solana  |
| POST   | `/v1/accounts/onboard/evm`               | Create a new entity on EVM       |
| POST   | `/v1/accounts/onboard/evm/mock`          | Mock create on EVM (testing)     |
| POST   | `/v1/accounts/onboard/evm/add-allowlist` | Add allowlist address on EVM     |
| PUT    | `/v1/accounts/onboard/update`            | Update an existing entity's data |

***

## Create Individual - SVM

`POST /v1/accounts/onboard/svm`

Creates a new entity and adds the user's Solana wallet address to the allowlist. Returns a JSON-serialized partially-signed transaction that the user must sign and broadcast to complete the allowlist addition.

#### Request Body

| Field           | Type    | Required | Description                                                                                                       |
| --------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `version`       | integer | Yes      | The version of this API call (currently `1`)                                                                      |
| `userData`      | object  | No       | User's KYC data. If not provided, the KYC provider will be queried. See Individual User Data and Entity User Data |
| `walletAddress` | string  | Yes      | The user's Solana wallet address (base58-encoded public key)                                                      |
| `kycProvider`   | string  | Yes      | The KYC provider used to verify the user. See KYC Providers                                                       |
| `kycProviderId` | string  | Yes      | The unique identifier or share token from the KYC provider's verification result                                  |
| `forInstrument` | string  | No       | Reserved for private allowlist onboarding. Must be omitted or `null`                                              |

#### Example Request

```json
{
  "version": 1,
  "userData": {
    "firstName": "John",
    "middleName": "Michael",
    "lastName": "Doe",
    "ssnOrTin": "123-45-6789",
    "emailAddress": "john.doe@example.com",
    "streetAddress": "123 Main Street",
    "city": "New York",
    "state": "NY",
    "zipcode": "10010",
    "country": "US"
  },
  "walletAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
  "kycProvider": "Sumsub",
  "kycProviderId": "01234567890123456789"
}
```

#### Response

**Success (200)**

```json
{
  "entityId": 598,
  "walletAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
  "encodedTransaction": "<JSON-serialized partially-signed Solana transaction>"
}
```

| Field                | Type    | Description                                                                                                  |
| -------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `entityId`           | integer | The newly created (or existing) entity ID                                                                    |
| `walletAddress`      | string  | The wallet address that was added to the allowlist                                                           |
| `encodedTransaction` | string  | JSON-serialized Solana transaction. The user must sign and broadcast this to complete the allowlist addition |

#### Validation Rules

* Wallet address must be a valid Solana public key (base58)
* Wallet address must not already be on the allowlist for the environment (production, staging, etc)
* Wallet address must not be a program/contract address
* If the email address already exists in Superstate's system, the existing entity will be reused

***

## Create Individual - EVM

`POST /v1/accounts/onboard/evm`

Creates a new entity and adds the user's EVM wallet address to the allowlist. Returns JSON-serialized EIP-712 signature parameters for the `setUserPermissionForInstrument` contract function.

#### Request Body

| Field           | Type    | Required | Description                                                                                                       |
| --------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `version`       | integer | Yes      | The version of this API call (currently `1`)                                                                      |
| `userData`      | object  | No       | User's KYC data. If not provided, the KYC provider will be queried. See Individual User Data and Entity User Data |
| `chainId`       | integer | Yes      | The EVM chain ID. See Supported Chain IDs                                                                         |
| `walletAddress` | string  | Yes      | The user's EVM wallet address (0x-prefixed hex)                                                                   |
| `kycProvider`   | string  | Yes      | The KYC provider used to verify the user. See KYC Providers                                                       |
| `kycProviderId` | string  | Yes      | The unique identifier or share token from the KYC provider's verification result                                  |
| `forInstrument` | string  | No       | Reserved for private allowlist onboarding. Must be omitted or `null`                                              |

#### Example Request

```json
{
  "version": 1,
  "userData": {
    "firstName": "Jane",
    "lastName": "Smith",
    "ssnOrTin": "987-65-4321",
    "emailAddress": "jane.smith@example.com",
    "streetAddress": "456 Oak Avenue",
    "city": "Los Angeles",
    "state": "CA",
    "zipcode": "90210",
    "country": "US"
  },
  "chainId": 1,
  "walletAddress": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
  "kycProvider": "Sumsub",
  "kycProviderId": "98765432109876543210"
}
```

#### Response

**Success (200)**

```json
{
  "entityId": 598,
  "walletAddress": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
  "encodedTransaction": "<JSON-serialized EIP-712 signature parameters for setUserPermissionForInstrument>"
}
```

| Field                | Type    | Description                                                                                                                |
| -------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------- |
| `entityId`           | integer | The newly created (or existing) entity ID                                                                                  |
| `walletAddress`      | string  | The wallet address that was added to the allowlist                                                                         |
| `encodedTransaction` | string  | JSON-serialized EIP-712 signature parameters. Use these to call `setUserPermissionForInstrument` on the allowlist contract |

#### Validation Rules

Same as SVM, but for EVM addresses:

* Wallet address must be a valid EVM address (0x-prefixed, 40 hex characters)
* Wallet address must not already be on the allowlist for the specified chain
* Wallet address must not be a smart contract address
* Chain ID must be a supported EVM chain

***

## Mock Endpoints

`POST /v1/accounts/onboard/svm/mock`

`POST /v1/accounts/onboard/evm/mock`

Mock endpoints for testing your integration. They accept the same request bodies as their non-mock counterparts but:

* **Skip all database operations** — no entities, users, or organizations are created
* **Skip input validation** — wallet address and chain checks are bypassed
* **Return mock data** — `entityId` is always `0`

**SVM mock**: Returns a JSON-serialized Solana transaction with an invalid blockhash (all zeros) and unsigned signatures. This transaction cannot be broadcast.

**EVM mock**: Returns JSON-serialized `setUserPermissionForInstrument` parameters with `deadline` set to `0` (expired) and zero-value signatures. This cannot be used on-chain.

#### Example Response (SVM Mock)

```json
{
  "entityId": 0,
  "walletAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
  "encodedTransaction": "<JSON-serialized mock Solana transaction>"
}
```

#### Example Response (EVM Mock)

```json
{
  "entityId": 0,
  "walletAddress": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
  "encodedTransaction": "<JSON-serialized mock EIP-712 parameters with expired deadline>"
}
```

***

## Add Allowlist Address - SVM

`POST /v1/accounts/onboard/svm/add-allowlist`

Adds a new Solana wallet address to the allowlist for an existing user. The user is identified by either their email address or an existing allowlisted wallet address.

#### Request Body

| Field                   | Type    | Required    | Description                                                                                       |
| ----------------------- | ------- | ----------- | ------------------------------------------------------------------------------------------------- |
| `version`               | integer | Yes         | The version of this API call (currently `1`)                                                      |
| `email`                 | string  | Conditional | Email of the user. Mutually exclusive with `existingWalletAddress` — exactly one must be provided |
| `existingWalletAddress` | string  | Conditional | An existing allowlisted wallet address to identify the user. Mutually exclusive with `email`      |
| `entityId`              | integer | Conditional | An existing `entityId` of a previously API-onboarded entity.                                      |
| `walletAddress`         | string  | Yes         | The new wallet address to add to the allowlist                                                    |
| `forInstrument`         | string  | No          | Reserved for private allowlist onboarding. Must be omitted or `null`                              |

**Note:** The SVM chain is determined automatically from the server's configured environment. There is no `cluster` field on this endpoint.

#### Example Request (using email)

```json
{
  "version": 1,
  "email": "john.doe@example.com",
  "walletAddress": "BKgGvQR3TmEy8zNBJfwDEVGZmt7dRUXVZDiPKYCFaLWd"
}
```

#### Example Request (using existing wallet address)

<pre class="language-json"><code class="lang-json"><strong>{
</strong>  "version": 1,
  "existingWalletAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
  "walletAddress": "BKgGvQR3TmEy8zNBJfwDEVGZmt7dRUXVZDiPKYCFaLWd"
}
</code></pre>

#### Example Request (using entity ID)

```json
{
  "version": 1,
  "entityId": 598,
  "walletAddress": "BKgGvQR3TmEy8zNBJfwDEVGZmt7dRUXVZDiPKYCFaLWd"
}
```

#### Response

**Success (200)**

```json
{
  "entityId": 598,
  "walletAddress": "BKgGvQR3TmEy8zNBJfwDEVGZmt7dRUXVZDiPKYCFaLWd",
  "encodedTransaction": "<JSON-serialized partially-signed Solana transaction>"
}
```

#### Authorization

The requesting partner must be the same partner that originally onboarded the entity. If a different partner attempts to add an allowlist address, the request will be rejected with `403 Forbidden`.

***

## Add Allowlist Address - EVM

`POST /v1/accounts/onboard/evm/add-allowlist`

Adds a new EVM wallet address to the allowlist for an existing user. The user is identified by either their email address or an existing allowlisted wallet address.

#### Request Body

| Field                   | Type    | Required    | Description                                                                                       |
| ----------------------- | ------- | ----------- | ------------------------------------------------------------------------------------------------- |
| `version`               | integer | Yes         | The version of this API call (currently `1`)                                                      |
| `email`                 | string  | Conditional | Email of the user. Mutually exclusive with `existingWalletAddress` — exactly one must be provided |
| `existingWalletAddress` | string  | Conditional | An existing allowlisted wallet address to identify the user. Mutually exclusive with `email`      |
| `entityId`              | integer | Conditional | An existing `entityId` of a previously API-onboarded entity.                                      |
| `chainId`               | integer | Yes         | The EVM chain ID for the new address. See Supported Chain IDs                                     |
| `walletAddress`         | string  | Yes         | The new EVM wallet address to add to the allowlist                                                |
| `forInstrument`         | string  | No          | Reserved for private allowlist onboarding. Must be omitted or `null`                              |

#### Example Request (using email)

```json
{
  "version": 1,
  "email": "jane.smith@example.com",
  "chainId": 1,
  "walletAddress": "0x0987654321abcdef1234567890abcdef87654321"
}
```

#### Example Request (using existing wallet address)

```json
{
  "version": 1,
  "existingWalletAddress": "0x1234567890abcdef1234567890abcdef12345678",
  "chainId": 1,
  "walletAddress": "0x0987654321abcdef1234567890abcdef87654321"
}
```

#### Example Request (using entity ID)

```json
{
  "version": 1,
  "entityId": 598,
  "chainId": 1,
  "walletAddress": "0x0987654321abcdef1234567890abcdef87654321"
}
```

#### Response

**Success (200)**

```json
{
  "entityId": 598,
  "walletAddress": "0x1234567890abcdef1234567890abcdef12345678",
  "encodedTransaction": "<JSON-serialized EIP-712 signature parameters>"
}
```

#### Authorization

Same as SVM — the requesting partner must be the same partner that originally onboarded the entity.

***

## Update Entity

`PUT /v1/accounts/onboard/update`

Updates an existing entity's onboarding data. Only the fields provided in the request will be updated — all other fields remain unchanged (partial update). The request body must include a `type` field to indicate whether this is an individual or entity update. An individual cannot be converted to an entity and vice versa. Only the company who onboarded the user initially can update the user's data.

#### Request Body

| Field      | Type    | Required | Description                                                                   |
| ---------- | ------- | -------- | ----------------------------------------------------------------------------- |
| `entityId` | integer | Yes      | The entity ID to update                                                       |
| `userData` | object  | Yes      | The fields to update. Must include `type` set to `"individual"` or `"entity"` |

**Individual Update Fields**

All fields are optional. Only provided fields will be updated.

| Field           | Type   | Description                                              |
| --------------- | ------ | -------------------------------------------------------- |
| `type`          | string | Must be `"individual"`                                   |
| `firstName`     | string | First name (1-100 characters)                            |
| `middleName`    | string | Middle name (1-100 characters)                           |
| `lastName`      | string | Last name (1-100 characters)                             |
| `ssnOrTin`      | string | SSN or Tax ID (digits and hyphens only, 1-20 characters) |
| `emailAddress`  | string | Valid email address                                      |
| `streetAddress` | string | Street address (1-100 characters)                        |
| `city`          | string | City (1-40 characters)                                   |
| `state`         | string | State or province abbreviation (1-3 characters)          |
| `zipcode`       | string | ZIP or postal code (1-11 characters)                     |
| `country`       | string | ISO 3166-1 alpha-2 country code (exactly 2 characters)   |

**Entity Update Fields**

All fields are optional. Only provided fields will be updated.

| Field           | Type   | Description                                                                |
| --------------- | ------ | -------------------------------------------------------------------------- |
| `type`          | string | Must be `"entity"`                                                         |
| `entityName`    | string | Entity name (1-100 characters)                                             |
| `entityType`    | string | Investor type (e.g., `"Corporation"`, `"LLC"`, `"Partnership"`, `"Trust"`) |
| `ssnOrTin`      | string | Tax ID (digits and hyphens only, 1-20 characters)                          |
| `emailAddress`  | string | Valid email address                                                        |
| `streetAddress` | string | Street address (1-100 characters)                                          |
| `city`          | string | City (1-40 characters)                                                     |
| `state`         | string | State or province abbreviation (1-3 characters)                            |
| `zipcode`       | string | ZIP or postal code (1-11 characters)                                       |
| `country`       | string | ISO 3166-1 alpha-2 country code (exactly 2 characters)                     |

#### Example Request (Individual)

```json
{
  "entityId": 598,
  "userData": {
    "type": "individual",
    "firstName": "Jonathan",
    "streetAddress": "789 New Street"
  }
}
```

#### Example Request (Entity)

```json
{
  "entityId": 599,
  "userData": {
    "type": "entity",
    "entityName": "Updated Corp LLC"
  }
}
```

#### Response

**Success (200)**

Returns an empty response body on success.

#### Authorization

The requesting partner must be the same partner that originally onboarded the entity. If a different partner attempts to update the entity, the request will be rejected with `403 Forbidden`.

***

## Reference

#### Individual User Data

Used in the `userData` field of create endpoints when onboarding an individual.

| Field           | Type   | Required | Validation                                             |
| --------------- | ------ | -------- | ------------------------------------------------------ |
| `firstName`     | string | Yes      | 1-100 characters                                       |
| `middleName`    | string | No       | 1-100 characters                                       |
| `lastName`      | string | Yes      | 1-100 characters                                       |
| `ssnOrTin`      | string | Yes      | Digits and hyphens only (`^[0-9-]+$`), 1-20 characters |
| `emailAddress`  | string | Yes      | Valid email address                                    |
| `streetAddress` | string | Yes      | 1-100 characters                                       |
| `city`          | string | Yes      | 1-40 characters                                        |
| `state`         | string | No       | 1-3 characters (state/province abbreviation)           |
| `zipcode`       | string | Yes      | 1-11 characters                                        |
| `country`       | string | Yes      | Exactly 2 characters (ISO 3166-1 alpha-2 country code) |

#### Entity User Data

Used in the `userData` field of create endpoints when onboarding a non-individual entity (corporation, LLC, etc.).

| Field           | Type   | Required | Validation                                                                 |
| --------------- | ------ | -------- | -------------------------------------------------------------------------- |
| `entityName`    | string | Yes      | 1-100 characters                                                           |
| `entityType`    | string | Yes      | Investor type (e.g., `"Corporation"`, `"LLC"`, `"Partnership"`, `"Trust"`) |
| `ssnOrTin`      | string | Yes      | Digits and hyphens only (`^[0-9-]+$`), 1-20 characters                     |
| `emailAddress`  | string | Yes      | Valid email address                                                        |
| `streetAddress` | string | Yes      | 1-100 characters                                                           |
| `city`          | string | Yes      | 1-40 characters                                                            |
| `state`         | string | No       | 1-3 characters (state/province abbreviation)                               |
| `zipcode`       | string | Yes      | 1-11 characters                                                            |
| `country`       | string | Yes      | Exactly 2 characters (ISO 3166-1 alpha-2 country code)                     |

**Note:** The `userData` field on create endpoints uses untagged deserialization. The API will attempt to parse as an individual first, then as an entity. Ensure your payload matches one of the two schemas above.

#### Supported EVM Chain IDs

| Chain ID | Network                  |
| -------- | ------------------------ |
| 1        | Ethereum Mainnet         |
| 11155111 | Ethereum Sepolia Testnet |
| 98866    | Plume Mainnet            |
| 98867    | Plume Testnet            |

For SVM, use the `cluster` field with values `MainnetBeta` or `Devnet` instead of a chain ID.

#### Error Responses

| Status Code | Description                                                                                          |
| ----------- | ---------------------------------------------------------------------------------------------------- |
| 400         | Bad request — invalid input, validation failure, wallet already on allowlist, or API key not enabled |
| 401         | Unauthorized — invalid or missing API key                                                            |
| 403         | Forbidden — partner did not onboard this entity (for update and add-allowlist endpoints)             |
| 500         | Internal server error                                                                                |


---

# Agent Instructions: 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:

```
GET https://docs.superstate.com/integration-partners/onboarding-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
