Quickstart guide
This guide should give you a quick start for the integration of the Distributor API. We'll use easy to read python code. For HTTP interactions we will use the requests package.
At the end of this guide, you will also find the complete python script for you to copy, paste and try out.
Getting started
Before you get started make sure that you've installed an up-to-date python version. To run the script you will also need the requests package.
First we are going to import the packages that we need and set up some basic variables. The client ID and client secret will be provided to you during the set up of the demo environment.
import binascii
import os
import requests
URL = 'https://demo.cashlink.de/api/external'
CLIENT_ID = '...'
CLIENT_SECRET = '...'
Now we write a helper function that follows the OAuth protocol to retrieve an access token that we will use to authenticate the API calls.
def authenticate():
r = requests.post(f'{URL}/oauth/token', data={
'grant_type': 'client_credentials',
'scope': 'investors:write investors:read documents:write',
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET
})
assert r.status_code == 200, r.content
return r.json()['access_token']
We add an additional helper function that with every call creates a random idempotency key. In your application, you would use an idempotency key that correlates to the internal object on your side.
def make_key():
return binascii.hexlify(os.urandom(8)).decode()
Create an investor
As we now can access the API we use it to create an investor.
if __name__ == '__main__':
headers = {
'Authorization': f'Bearer {authenticate()}',
}
# Upload document
r = requests.post(
f'{URL}/distributor/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']
# Upload document
r = requests.post(
f'{URL}/distributor/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}/distributor/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(f'Investor: {investor_id}')
Add identification
Besides personal data, the investor also needs a valid identification to be able to invest.
# Upload the legitimation protocol document to reference it later
r = requests.post(
f'{URL}/distributor/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 the identification for the investor
r = requests.post(
f'{URL}/distributor/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(f'Identification : {identification_id}')
Add benefiting persons
Because we are creating a legal person investor, we also need to add data about the benefiting persons of the investor account.
# Add benefiting persons for the investor account
r = requests.post(
f'{URL}/distributor/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}')
Create a wallet
Investors need a wallet to receive and hold crypto securities. In this example, we create a managed wallet for the investor. That means that the private key will remain in secure custody with Cashlink as the crypto custodian.
# Create wallet for investor
r = requests.post(
f'{URL}/distributor/v2/investors/{investor_id}/wallets/eth-managed/',
json={
'receiving_enabled': True
},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
wallet_id = r.json()['id']
print(f'Managed wallet: {wallet_id}')
Full script
Below you find the full script for you to copy, paste and try out.
import binascii
import os
import requests
URL = 'https://demo.cashlink.de/api/external'
CLIENT_ID = '...'
CLIENT_SECRET = '...'
def authenticate():
r = requests.post(f'{URL}/oauth/token', data={
'grant_type': 'client_credentials',
'scope': 'investors:write investors:read documents:write',
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET
})
assert r.status_code == 200, r.content
return r.json()['access_token']
def make_key():
return binascii.hexlify(os.urandom(8)).decode()
if __name__ == '__main__':
headers = {
'Authorization': f'Bearer {authenticate()}',
}
r = requests.post(
f'{URL}/distributor/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}/distributor/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}/distributor/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(f'Investor: {investor_id}')
# Upload the legitimation protocol document to reference it later
r = requests.post(
f'{URL}/distributor/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 the identification for investor
r = requests.post(
f'{URL}/distributor/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(f'Identification : {identification_id}')
# Add benefiting persons for the investor account
r = requests.post(
f'{URL}/distributor/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}')
r = requests.post(
f'{URL}/distributor/v2/investors/{investor_id}/wallets/eth-managed/',
json={
'receiving_enabled': True
},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
wallet_id = r.json()['id']
print(f'Managed wallet: {wallet_id}')
Last updated
Was this helpful?