Investor import API

The investor import API allows to create investor accounts and start an investment process for them to make use of already existing investor data.

Investors imported via the API will be presented a welcome screen where they are able to check and complete the imported data. After that, they can proceed with the investment.

By importing investors it's possible to transfer sensitive data about the user. Make sure to retrieve explicit approval of the user if necessary that you are allowed to transfer the data.

Importing customers using the API

Importing customers using the API follows these steps:

  1. Collect data about the investor in your application.

  2. Create an investor account via the API and start an investment for this investor and a campaign. As a response, you'll get an "invest now" URL.

  3. Present the user with the "invest now" URL as a link or as the target of a button in your application.

  4. The user follows that link and is redirected to a welcome screen where they can check and edit the transferred data.

We don't use any uniqueness constraints on email addresses. The email address from the API call is only used to prefill an input field on the welcome screen. Make sure that you only redirect users to the registration page if they don't already have an account.

Register webhooks

You can also register webhooks so that we can notify your application about the user's activities in the investment process. To use that please provide your point of contact at Cashlink with the following data:

  • URL to be used as the target for webhooks

  • OAuth client credentials or bearer authentication token so that we can authenticate at your server.

How to use the investor import API

Authentication

Every call to the API must be authenticated via OAuth client credentials. More information.

During setup, your point of contact will provide you with your client ID and client secret.

Idempotency

For certain POST endpoints, the API uses client-supplied idempotency keys to ensure that requests can be retried safely without performing the same operation twice. Repeated calls to those endpoints with the same idempotency key will return the cached response from the first call.

The recommended approach for interacting with such endpoints is as follows:

  1. Create a unique, random key, such as a UUIDv4.

  2. Store the intent to call an endpoint along with the generated key in the client database.

  3. Flush to disk (e.g. commit an SQL transaction).

  4. Perform the call to the endpoint.

This ensures that even if the response from the API to a client gets lost, or the client experiences any sort of outage between steps 3 and 4, the client can always safely retry the same action.

If you already have objects corresponding to tasks in the Cashlink API in your client-side database, you can use their internal IDs as idempotency keys!

Starting an investment

To start an investment for a user registered on your side, follow these steps:

  1. Obtain an access token via the OAuth authorization endpoint using the client id and client secret provided to you.

  2. Upload all documents you want to use for the registration.

  3. Create an investor object.

  4. Create an investment object using your created investor and the campaign ID provided to you.

Example

import binascii
import os
import requests
URL = 'https://test.cashlink-staging.de/api/external'
CLIENT_ID = 'testid'
CLIENT_SECRET = 'testsecret'
CAMPAIGN_ID = 'e519c807-f094-4810-92e0-fbf12b7aede4'
def authenticate():
r = requests.post(f'{URL}/oauth/token', data={
'grant_type': 'client_credentials',
'scope': 'investors:write investments:write investments: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()}',
}
# Note: in normal usage, you would use an idempotency key that correlates
# to the internal object on the customer side.
r = requests.post(
f'{URL}/v1/documents/',
files={'file': ('foo.pdf', b'ASD', 'application/pdf')},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
doc_id = r.json()['id']
r = requests.post(
f'{URL}/v1/investors/',
json={
"natural_person": {
"salutation": "MR",
# "title": '',
"forename": "Peter",
"surname": "Parker",
"birth_date": "2020-08-10",
"birth_place": "New York",
"citizenship": "DEU",
"street": "Hauptstr. 37",
"city": "Frankfurt",
"zip": "60316",
"country": "DEU",
"phone": "+49 123 456 789"
},
"identification": {
"document_type": "PERSONAL_ID",
"document_id": "ID123456",
"document_issuer": "Buergeramt Frankfurt",
"document_valid_from": "2020-08-10",
"document_valid_to": "2025-08-10",
"identity_provider": "POSTIDENT",
"legitimation_process_id": "string",
"legitimation_protocol": doc_id,
"verified_at": "2020-08-10T14:06:22.134Z"
},
"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": True
},
"communication": {
"email": "external_investor@cashlink.de",
"email_confirmed": True
}
},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
investor_id = r.json()['id']
r = requests.post(
f'{URL}/v1/investments/',
json={
'campaign_id': str(CAMPAIGN_ID),
'investor_id': investor_id,
},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300
print(r.json()['start_url'])

Full API reference

See full documentation of the API here.