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.

Make sure Python and the requests package are installed.

Remember to insert your account specific information before running the script.

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?