Investors
Investors are natural or legal persons that hold crypto securities. Those investor accounts can be managed using the following endpoints:
/investors/ to provide investor data
/investors/{investor_id}/benefiting-persons/ to add data about beneficiaries for legal persons
/investors/{investor_id}/identifications/ to manage identifications
Custodian bank setup
In a collective entry the custodian bank is the investor. The end customers are onboarded by the custodian bank. Cashlink doesn't need the data of those investors and thus they don't need to onboarded via the Distributor API.
If you're using the Distributor API in the scope of a custodian bank, you can create a single investor account using the bank's data. If you need to, you can also create separate accounts using the same data if you need to.
Natural person investors
Natural person investors can be created via the investor endpoints. The information stored with an investor includes the following mandatory data:
Personal data, residence address and PEP status of the investor (
natural_person
)Flag to confirm that the investor acts on their own behalf (
is_beneficiary
)Tax information (
tax_information
)Bank account information (
bank_account_information
)Communication (
communication
)
Please not that we're currently not onboarding persons that are subject to US taxation. Make sure that you set the value of the is_subject_to_us_tax
field to False
accordingly. Otherwise creation of investors will fail.
Example:
r = requests.post(
f'{URL}/v2/investors/',
json={
'natural_person': {
'salutation': 'MR',
'forename': 'Peter',
'surname': 'Parker',
'birth_date': '2000-08-10',
'birth_place': 'New York',
'citizenship': 'DEU',
'street': 'Hauptstr. 37',
'city': 'Frankfurt',
'zip': '60316',
'country': 'DEU',
'phone': '+49 123 456 789',
'is_pep': False,
'is_subject_to_us_tax': False
},
'bank_account': {
'account_holder': 'Peter Parker',
'iban': 'DE53500105173569146251',
'bic': 'GENODEF1ERG',
'bank': 'Commerzbank Frankfurt',
'country': 'DEU',
'currency': 'EUR'
},
'tax_information': {
'tax_identification_number': '12345678',
'non_assessment_certificate': False,
'is_subject_to_german_tax': True
},
'communication': {
'email': '[email protected]',
'email_confirmed': True
},
'is_beneficiary': True,
},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
investor_id = r.json()['id']
print(investor_id)
Legal person investors
Legal person investors can be created via the investor endpoints. The following data is needed to create an account for a legal person:
Company data such as name and commercial register data (
legal_person
)Personal data, residence address and PEP status of the CEO (
natural_person
)Flag to confirm that the investor acts on their own behalf (
is_beneficiary
)Tax information used to manage capital gains tax in Germany, if applicable (
tax_information
)Bank account information used by issuers for payouts (
bank_account_information
)Communication (
communication
)
Example:
# Upload documents
r = requests.post(
f'{URL}/v2/documents/',
files={'file': ('identity_proof.pdf', b'ASD', 'application/pdf')},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
identity_proof_id = r.json()['id']
r = requests.post(
f'{URL}/v2/documents/',
files={'file': ('structure_proof.pdf', b'ASD', 'application/pdf')},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
structure_proof_id = r.json()['id']
# Create investor
r = requests.post(
f'{URL}/v2/investors/',
json={
'legal_person': {
'city': 'string',
'commercial_register': 'Amtsgericht München',
'commercial_register_number': 'HRB 12344',
'company_identity_proof_id': identity_proof_id,
'company_name': 'Test GmbH',
'company_structure_proof_id': structure_proof_id,
'country': 'DEU',
'phone': '0173 2093790',
'street': 'string',
'zip': 'string',
'is_subject_to_us_tax': False
},
'natural_person': {
'salutation': 'MR',
'forename': 'Peter',
'surname': 'Parker',
'birth_date': '2000-08-10',
'birth_place': 'New York',
'citizenship': 'DEU',
'street': 'Hauptstr. 37',
'city': 'Frankfurt',
'zip': '60316',
'country': 'DEU',
'phone': '+49 123 456 789',
'is_pep': False,
'is_subject_to_us_tax': False
},
'bank_account': {
'account_holder': 'Peter Parker',
'iban': 'DE53500105173569146251',
'bic': 'GENODEF1ERG',
'bank': 'Commerzbank Frankfurt',
'country': 'DEU',
'currency': 'EUR'
},
'tax_information': {
'tax_identification_number': '12345678',
'non_assessment_certificate': False,
'is_subject_to_german_tax': True
},
'communication': {
'email': '[email protected]',
'email_confirmed': True
},
'is_beneficiary': True,
},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
investor_id = r.json()['id']
print(investor_id)
Benefiting persons
If you have created an investor that is a legal person, information about its benefiting persons must be provided via the benefiting persons endpoint.
Example:
# Add benefiting persons for the investor account
r = requests.post(
f'{URL}/v2/investors/{investor_id}/benefiting-persons/',
json=
[
{
'salutation': 'MS',
'forename': 'Donna',
'surname': 'Duck',
'street': 'Teststr. 1',
'zip': '77777',
'city': 'Test',
'country': 'DEU',
'birth_date': '2005-09-18',
'birth_place': 'Test',
'citizenship': 'DEU',
'is_pep': False
}
],
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
person_id = r.json()[0]['person_id']
print(f'Person: {person_id}')
Please make sure that you're only onboarding investors where the benefiting persons are not "politically exposed persons" (PEP). During the KYC/AML process a background check is performed and PEPs may be rejected.
If the benefiting persons change, please add the new persons via a new POST request. You do not need to delete existing benefiting persons if they're not a benefiting person any more.
Identifications
Investors need valid identification data from an identification process. For legal persons, the CEO (added via the natural_person
field) needs to perform the identification.
Create new identifications
The process for creating a new identification is the following:
Via the identification endpoint, create a new identification with an empty JSON request. The API will return a URL of the identity provider and the ID of the identification process.
Redirect the investor to the provided URL. The investor can use the URL to complete the identification process.
You can use the identification endpoints and the identification ID to query the current status of the identification.
After the identification is completed, the final data can be retrieved via the identification endpoint.
Import existing identifications
If you have already existing and valid identifications of the investor you can also provide all data via the API directly.
For the identity provider you can use "IDNOW", "LIONWARE" and "POSTIDENT". Other identity providers are currently not supported.
The legitimation protocol that resulted from the identification process needs to be uploaded as a PDF document.
You also have to add the document type that was used. You can use the values "IDCARD" or "PASSPORT".
Example:
# Upload the legitimation protocol document
r = requests.post(
f'{URL}/v2/documents/',
files={'file': ('protocol.pdf', b'ASD', 'application/pdf')},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
protocol_doc_id = r.json()['id']
# Create identification for investor
r = requests.post(
f'{URL}/v2/investors/{investor_id}/identifications/',
json={
'document_type': 'IDCARD',
'document_id': 'ID123456',
'document_issuer': 'Buergeramt Frankfurt',
'document_valid_from': '2020-08-10',
'document_valid_to': '2030-08-10',
'identity_provider': 'POSTIDENT',
'legitimation_process_id': 'string',
'legitimation_protocol_id': protocol_doc_id,
'verified_at': '2025-08-10T14:06:22.134Z'
},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
identification_id = r.json()['id']
print(identification_id)
Last updated
Was this helpful?