Links

Registry-as-a-Service API

This guide shows you how to import a signed investment into the registry.
We'll assume that you have created an investor as part of following the guide for creating an investor and have stored an investor ID that we will use in this guide.
In the course of this guide, we will create a simple script that shows the basic interactions with the API. At the end of this guide, you will also find the complete script for you to copy, paste and try out. Remember to insert your account specific information before running the script.

Getting started

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. The campaign ID and product ID is set up for you as well during the product creation.
import binascii
import os
import requests
URL = 'https://staging.cashlink-staging.de/api/external'
CLIENT_ID = '<your client ID>'
CLIENT_SECRET = '<your client secret>'
CAMPAIGN_ID = '<campaign id>'
PRODUCT_ID = '<product_id>'
INVESTOR_ID = '<investor id from previous guide>'
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 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']
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 investment

With the investor ID at hand, we create an investment for this investor with two signed documents.
# create an investment with units, disagio (if applicable) and signed documents
# 1. upload signed documents to reference them later
r = requests.post(
f'{URL}/v2/documents/',
files={'file': ('signed_doc_1.pdf', b'ASD', 'application/pdf')},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
doc_1_id = r.json()['id']
r = requests.post(
f'{URL}/v2/documents/',
files={'file': ('signed_doc_2.pdf', b'ASD', 'application/pdf')},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
doc_2_id = r.json()['id']
# 2. create investment
r = requests.post(
f'{URL}/v2/investments/',
json={
'campaign_id': CAMPAIGN_ID,
'investor_id': INVESTOR_ID,
'units': 300,
'signed_at': '2021-03-12T07:14:13.630308+00:00',
'accepted_at': '2021-03-12T07:14:13.630308+00:00',
'paid_at': '2021-03-12T07:14:13.630308+00:00',
'signed_documents': [{
'document_id': doc_1_id,
'category': 'category 1',
'subject': 'subject 1',
}, {
'document_id': doc_2_id,
'category': 'category 2',
'subject': 'subject 2',
}]
},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
investment_id = r.json()['id']

Add wallet information to the investment

For the investment to finish, we need to associate the wallet of the investor with the investment.
# get wallet for investor
r = requests.get(
f'{URL}/v2/investors/{INVESTOR_ID}/wallets/',
headers={**headers}
)
assert r.status_code < 300, r.content
# investor must at least have a single wallet
investor_wallets = r.json()
assert len(investor_wallets) > 0
wallet_id = investor_wallets[0]['id']
# patch investment to set wallet
r = requests.patch(
f'{URL}/v2/investments/{investment_id}/',
json={
'wallet': wallet_id
},
headers={**headers}
)
assert r.status_code < 300, r.content

Credit digital securities

After the wallet has been set for the investment the credit of digital securities can also be trigged via the API. Note that this action can also be performed via the Cashlink Studio frontend and don't necessarily need to be controlled via the API.
# Initiate credit of digital securities
r = requests.post(
f'{URL}/v2/investments/{investment_id}/credits/',
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content

Retrieve status information

We can now use the investments endpoint to retrieve all information about the investment.
r = requests.get(
f'{URL}/v2/investments/{investment_id}/',
headers={**headers}
)
assert r.status_code < 300, r.content
status = r.json()['status']
print(investment_id)
print(status)

Create redemption

When the contract has ended or the contract’s nominal amounts needs to be reduced because of some legal construct in the contract, the balance for that investor needs to be reduced. Such a transaction is called redemption and can be created via the redemption endpoint.
# Create redemption for investor and product
r = requests.post(
f'{URL}/v2/redemptions/',
json={
'investor_id': INVESTOR_ID,
'product_id': PRODUCT_ID,
'wallet_id': wallet_id,
'units: 300
}
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300

Full script

This is how the full script will look like.
import binascii
import os
import requests
URL = 'https://staging.cashlink-staging.de/api/external'
CLIENT_ID = '<your client ID'
CLIENT_SECRET = '<your client secret>'
CAMPAIGN_ID = '<campaign id>'
PRODUCT_ID = '<product_id>'
INVESTOR_ID = '<investor id from previous guide>'
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()}',
}
# create an investment with units, disagio (if applicable) and signed documents
# 1. upload signed documents to reference them later
r = requests.post(
f'{URL}/v2/documents/',
files={'file': ('signed_doc_1.pdf', b'ASD', 'application/pdf')},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
doc_1_id = r.json()['id']
r = requests.post(
f'{URL}/v2/documents/',
files={'file': ('signed_doc_2.pdf', b'ASD', 'application/pdf')},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
doc_2_id = r.json()['id']
# 2. create investment
r = requests.post(
f'{URL}/v2/investments/',
json={
'campaign_id': CAMPAIGN_ID,
'investor_id': INVESTOR_ID,
'units': 300,
'signed_at': '2021-03-12T07:14:13.630308+00:00',
'accepted_at': '2021-03-12T07:14:13.630308+00:00',
'paid_at': '2021-03-12T07:14:13.630308+00:00',
'signed_documents': [{
'document_id': doc_1_id,
'category': 'category 1',
'subject': 'subject 1',
}, {
'document_id': doc_2_id,
'category': 'category 2',
'subject': 'subject 2',
}]
},
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
investment_id = r.json()['id']
# get wallet for investor
r = requests.get(
f'{URL}/v2/investors/{INVESTOR_ID}/wallets/',
headers={**headers}
)
assert r.status_code < 300, r.content
# investor must at least have a single wallet
investor_wallets = r.json()
assert len(investor_wallets) > 0
wallet_id = investor_wallets[0]['id']
# ... and patch investment to set wallet
r = requests.patch(
f'{URL}/v2/investments/{investment_id}/',
json={
'wallet': wallet_id
},
headers={**headers}
)
assert r.status_code < 300, r.content
# Initiate credit of digital securities
r = requests.post(
f'{URL}/v2/investments/{investment_id}/credits/',
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300, r.content
r = requests.get(
f'{URL}/v2/investments/{investment_id}/',
headers={**headers}
)
assert r.status_code < 300, r.content
status = r.json()['status']
print(investment_id)
print(status)
# Create redemption for investor and product
r = requests.post(
f'{URL}/v2/redemptions/',
json={
'investor_id': INVESTOR_ID,
'product_id': PRODUCT_ID,
'wallet_id': wallet_id,
'units: 300
}
headers={**headers, 'X-Idempotency-Key': make_key()}
)
assert r.status_code < 300