curl --request POST \
--url https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"firstName": "Jane",
"lastName": "Doe",
"roles": [
"Beneficial Owner",
"Director"
],
"email": "jane@betalogistics.com",
"phone": "+13025550124",
"dateOfBirth": "1985-09-21",
"taxId": "123456789",
"ownershipPercentage": 60,
"currentAddress": {
"street": "5 Oak St",
"city": "Wilmington",
"state": "DE",
"postalCode": "19801",
"country": "US"
}
}
'import requests
url = "https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons"
payload = {
"firstName": "Jane",
"lastName": "Doe",
"roles": ["Beneficial Owner", "Director"],
"email": "jane@betalogistics.com",
"phone": "+13025550124",
"dateOfBirth": "1985-09-21",
"taxId": "123456789",
"ownershipPercentage": 60,
"currentAddress": {
"street": "5 Oak St",
"city": "Wilmington",
"state": "DE",
"postalCode": "19801",
"country": "US"
}
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
firstName: 'Jane',
lastName: 'Doe',
roles: ['Beneficial Owner', 'Director'],
email: 'jane@betalogistics.com',
phone: '+13025550124',
dateOfBirth: '1985-09-21',
taxId: '123456789',
ownershipPercentage: 60,
currentAddress: {
street: '5 Oak St',
city: 'Wilmington',
state: 'DE',
postalCode: '19801',
country: 'US'
}
})
};
fetch('https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'firstName' => 'Jane',
'lastName' => 'Doe',
'roles' => [
'Beneficial Owner',
'Director'
],
'email' => 'jane@betalogistics.com',
'phone' => '+13025550124',
'dateOfBirth' => '1985-09-21',
'taxId' => '123456789',
'ownershipPercentage' => 60,
'currentAddress' => [
'street' => '5 Oak St',
'city' => 'Wilmington',
'state' => 'DE',
'postalCode' => '19801',
'country' => 'US'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons"
payload := strings.NewReader("{\n \"firstName\": \"Jane\",\n \"lastName\": \"Doe\",\n \"roles\": [\n \"Beneficial Owner\",\n \"Director\"\n ],\n \"email\": \"jane@betalogistics.com\",\n \"phone\": \"+13025550124\",\n \"dateOfBirth\": \"1985-09-21\",\n \"taxId\": \"123456789\",\n \"ownershipPercentage\": 60,\n \"currentAddress\": {\n \"street\": \"5 Oak St\",\n \"city\": \"Wilmington\",\n \"state\": \"DE\",\n \"postalCode\": \"19801\",\n \"country\": \"US\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"firstName\": \"Jane\",\n \"lastName\": \"Doe\",\n \"roles\": [\n \"Beneficial Owner\",\n \"Director\"\n ],\n \"email\": \"jane@betalogistics.com\",\n \"phone\": \"+13025550124\",\n \"dateOfBirth\": \"1985-09-21\",\n \"taxId\": \"123456789\",\n \"ownershipPercentage\": 60,\n \"currentAddress\": {\n \"street\": \"5 Oak St\",\n \"city\": \"Wilmington\",\n \"state\": \"DE\",\n \"postalCode\": \"19801\",\n \"country\": \"US\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"firstName\": \"Jane\",\n \"lastName\": \"Doe\",\n \"roles\": [\n \"Beneficial Owner\",\n \"Director\"\n ],\n \"email\": \"jane@betalogistics.com\",\n \"phone\": \"+13025550124\",\n \"dateOfBirth\": \"1985-09-21\",\n \"taxId\": \"123456789\",\n \"ownershipPercentage\": 60,\n \"currentAddress\": {\n \"street\": \"5 Oak St\",\n \"city\": \"Wilmington\",\n \"state\": \"DE\",\n \"postalCode\": \"19801\",\n \"country\": \"US\"\n }\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"message": "Related person added successfully",
"success": true,
"data": {
"id": "a3d4f21e-f499-488f-8508-43228dfea485",
"firstName": "Jane",
"lastName": "Doe",
"roles": [
"Beneficial Owner",
"Director"
],
"email": "jane@betalogistics.com",
"phone": "+13025550124",
"dateOfBirth": "1985-09-21",
"ownershipPercentage": 60,
"currentAddress": {
"street": "5 Oak St",
"city": "Wilmington",
"state": "DE",
"postalCode": "19801",
"country": "US"
},
"taxId": "123456789"
}
}{
"status": 400,
"message": "Related persons can only be added to business accounts"
}{
"status": 400,
"message": "Validation failed",
"code": "ACCOUNT_RESTRICTED",
"errors": [
{
"path": [
"amount"
],
"message": "amount must be a whole number of smallest currency units (e.g. cents)"
}
]
}{
"status": 400,
"message": "Validation failed",
"code": "ACCOUNT_RESTRICTED",
"errors": [
{
"path": [
"amount"
],
"message": "amount must be a whole number of smallest currency units (e.g. cents)"
}
]
}Add Related Person
Adds a beneficial owner or director to a business account’s application. Unlike the other application endpoints this one rejects an incomplete person: the required fields must be present in the request itself. Each related person must also complete identity verification via a KYC link before the application can be submitted.
curl --request POST \
--url https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"firstName": "Jane",
"lastName": "Doe",
"roles": [
"Beneficial Owner",
"Director"
],
"email": "jane@betalogistics.com",
"phone": "+13025550124",
"dateOfBirth": "1985-09-21",
"taxId": "123456789",
"ownershipPercentage": 60,
"currentAddress": {
"street": "5 Oak St",
"city": "Wilmington",
"state": "DE",
"postalCode": "19801",
"country": "US"
}
}
'import requests
url = "https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons"
payload = {
"firstName": "Jane",
"lastName": "Doe",
"roles": ["Beneficial Owner", "Director"],
"email": "jane@betalogistics.com",
"phone": "+13025550124",
"dateOfBirth": "1985-09-21",
"taxId": "123456789",
"ownershipPercentage": 60,
"currentAddress": {
"street": "5 Oak St",
"city": "Wilmington",
"state": "DE",
"postalCode": "19801",
"country": "US"
}
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
firstName: 'Jane',
lastName: 'Doe',
roles: ['Beneficial Owner', 'Director'],
email: 'jane@betalogistics.com',
phone: '+13025550124',
dateOfBirth: '1985-09-21',
taxId: '123456789',
ownershipPercentage: 60,
currentAddress: {
street: '5 Oak St',
city: 'Wilmington',
state: 'DE',
postalCode: '19801',
country: 'US'
}
})
};
fetch('https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'firstName' => 'Jane',
'lastName' => 'Doe',
'roles' => [
'Beneficial Owner',
'Director'
],
'email' => 'jane@betalogistics.com',
'phone' => '+13025550124',
'dateOfBirth' => '1985-09-21',
'taxId' => '123456789',
'ownershipPercentage' => 60,
'currentAddress' => [
'street' => '5 Oak St',
'city' => 'Wilmington',
'state' => 'DE',
'postalCode' => '19801',
'country' => 'US'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons"
payload := strings.NewReader("{\n \"firstName\": \"Jane\",\n \"lastName\": \"Doe\",\n \"roles\": [\n \"Beneficial Owner\",\n \"Director\"\n ],\n \"email\": \"jane@betalogistics.com\",\n \"phone\": \"+13025550124\",\n \"dateOfBirth\": \"1985-09-21\",\n \"taxId\": \"123456789\",\n \"ownershipPercentage\": 60,\n \"currentAddress\": {\n \"street\": \"5 Oak St\",\n \"city\": \"Wilmington\",\n \"state\": \"DE\",\n \"postalCode\": \"19801\",\n \"country\": \"US\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"firstName\": \"Jane\",\n \"lastName\": \"Doe\",\n \"roles\": [\n \"Beneficial Owner\",\n \"Director\"\n ],\n \"email\": \"jane@betalogistics.com\",\n \"phone\": \"+13025550124\",\n \"dateOfBirth\": \"1985-09-21\",\n \"taxId\": \"123456789\",\n \"ownershipPercentage\": 60,\n \"currentAddress\": {\n \"street\": \"5 Oak St\",\n \"city\": \"Wilmington\",\n \"state\": \"DE\",\n \"postalCode\": \"19801\",\n \"country\": \"US\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rolla.xyz/api/v1/external/accounts/{accountId}/related-persons")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"firstName\": \"Jane\",\n \"lastName\": \"Doe\",\n \"roles\": [\n \"Beneficial Owner\",\n \"Director\"\n ],\n \"email\": \"jane@betalogistics.com\",\n \"phone\": \"+13025550124\",\n \"dateOfBirth\": \"1985-09-21\",\n \"taxId\": \"123456789\",\n \"ownershipPercentage\": 60,\n \"currentAddress\": {\n \"street\": \"5 Oak St\",\n \"city\": \"Wilmington\",\n \"state\": \"DE\",\n \"postalCode\": \"19801\",\n \"country\": \"US\"\n }\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"message": "Related person added successfully",
"success": true,
"data": {
"id": "a3d4f21e-f499-488f-8508-43228dfea485",
"firstName": "Jane",
"lastName": "Doe",
"roles": [
"Beneficial Owner",
"Director"
],
"email": "jane@betalogistics.com",
"phone": "+13025550124",
"dateOfBirth": "1985-09-21",
"ownershipPercentage": 60,
"currentAddress": {
"street": "5 Oak St",
"city": "Wilmington",
"state": "DE",
"postalCode": "19801",
"country": "US"
},
"taxId": "123456789"
}
}{
"status": 400,
"message": "Related persons can only be added to business accounts"
}{
"status": 400,
"message": "Validation failed",
"code": "ACCOUNT_RESTRICTED",
"errors": [
{
"path": [
"amount"
],
"message": "amount must be a whole number of smallest currency units (e.g. cents)"
}
]
}{
"status": 400,
"message": "Validation failed",
"code": "ACCOUNT_RESTRICTED",
"errors": [
{
"path": [
"amount"
],
"message": "amount must be a whole number of smallest currency units (e.g. cents)"
}
]
}Mandatory and optional fields
A business must declare at least one beneficial owner or director. Unlike the other update endpoints, this one rejects an incomplete person. The fields marked Required below must be present in the request itself. The rest are accepted now and checked before the application can be submitted.| Field | Notes | |
|---|---|---|
firstName | Required in request | |
lastName | Required in request | |
email | Required in request | Where their KYC link is sent |
phone | Required in request | International format |
dateOfBirth | Required in request | YYYY-MM-DD. Must be 18 or older |
currentAddress | Required in request | All five fields — see below |
sourceOfWealthExplanation | Required to submit | Accepted empty here, blocks submission if left so |
| Identifier | Required to submit | taxId, or nin for Nigerian residents — see below |
ownershipPercentage | Optional | 0–100. Write 60 for 60%, never 0.6 |
roles | Optional | Beneficial Owner (the default) or Director |
bvn | Optional | Nigerian identifier, 11 digits |
currentAddress
Required in the request, and all five fields must be present together.
| Field | Notes | |
|---|---|---|
street | Required | |
city | Required | |
state | Required | State, province or region |
postalCode | Required | |
country | Required | ISO-2. Also decides which identifier format applies |
Documents and verification
Both documents are mandatory before submission, uploaded with the person’srelatedPersonId:
proof_of_address and source_of_wealth_doc. Each person must also complete a
KYC link.
Get Requirements reports anything outstanding per person under relatedPersons[].missingFields and relatedPersons[].missingDocuments.
Government identifier
Every person needs one, and our USD banking partner validates it in the format their country of residence issues — so the value differs per person, not per account.| Country of residence | Field | Format | Example |
|---|---|---|---|
| Nigeria | nin | 11 digits | 12345678901 |
| Mainland China | taxId | 18-character resident ID | 440301199209153216 |
| Hong Kong | taxId | HKID | UH123456(A) |
| United States | taxId | SSN or ITIN | 123456789 |
| Elsewhere | taxId | national ID or tax number as issued | — |
nin is accepted as a taxId for people added before this field existed, so nothing needs re-sending. New integrations should use taxId.related_persons.<id>.taxId in Get Requirements and blocks submission. Supplying the wrong format for the country is only caught later, when the USD account is issued.Example Request
curl -X POST "https://api.rolla.xyz/api/v1/external/accounts/eec3cbed-79d8-4370-87a0-b6be9e287337/related-persons" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Jane",
"lastName": "Doe",
"roles": ["Beneficial Owner", "Director"],
"email": "jane@betalogistics.com",
"phone": "+13025550124",
"dateOfBirth": "1985-09-21",
"taxId": "123456789",
"ownershipPercentage": 60,
"currentAddress": {
"street": "5 Oak St",
"city": "Wilmington",
"state": "DE",
"postalCode": "19801",
"country": "US"
}
}'
Example Response
{
"status": 201,
"message": "Related person added successfully",
"success": true,
"data": {
"id": "a3d4f21e-f499-488f-8508-43228dfea485",
"firstName": "Jane",
"lastName": "Doe",
"roles": ["Beneficial Owner", "Director"],
"email": "jane@betalogistics.com",
"phone": "+13025550124",
"dateOfBirth": "1985-09-21",
"ownershipPercentage": 60,
"currentAddress": {
"street": "5 Oak St",
"city": "Wilmington",
"state": "DE",
"postalCode": "19801",
"country": "US"
},
"taxId": "123456789"
}
}
id plus exactly the fields you sent: anything you left out is absent until you supply it, and roles is filled in with ["Beneficial Owner"] when omitted. Use id as the personId on the update and delete calls, and as the relatedPersonId when uploading this person’s documents.
Errors
| Status | When |
|---|---|
400 | Validation failed, or the account is an individual account (Related persons can only be added to business accounts) |
404 | Account not found |
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Yes | First name |
lastName | string | Yes | Last name |
roles | string[] | No | Beneficial Owner and/or Director (defaults to Beneficial Owner) |
email | string | Yes | Email address |
phone | string | Yes | Phone number |
dateOfBirth | string | Yes | Date of birth (YYYY-MM-DD) |
ownershipPercentage | number | No | Ownership stake, 0–100 |
currentAddress | object | Yes | Residential address (street, city, state, postalCode, country) |
sourceOfWealthExplanation | string | No (required before submission) | Free-text source of wealth |
taxId | string | No (required before submission) | The person’s government identifier, in the format their country of residence issues. Nigerian residents send nin instead. Can also be patched later via Update Related Person |
bvn / nin | string | No | Nigerian identifiers. nin (11 digits) is what Nigerian residents send in place of taxId. Both are checked for 11 digits only when currentAddress.country is NG |
Authorizations
Your Rolla API key
Path Parameters
Identifier of a business account owned by the same user as your API key's business
Body
1"Jane"
1"Doe"
Where this person’s KYC link is sent
"jane@betalogistics.com"
International format
1"+13025550124"
YYYY-MM-DD. The person must be 18 or older
"1985-09-21"
Residential address. All-or-nothing: whenever the object is sent, every one of the five fields must be present.
Show child attributes
Show child attributes
The person's capacities on the business. Defaults to ["Beneficial Owner"] when omitted or empty, so the stored record always has at least one role.
Beneficial Owner, Director ["Beneficial Owner", "Director"]
Ownership stake as a percentage, not a fraction: send 60 for 60%. A value between 0 and 1 is rejected.
0 <= x <= 10060
Free-text explanation of where the person’s wealth comes from. Accepted empty here; required before the application can be submitted.
"Salary and dividends from Beta Logistics LLC"
The person's government identifier, in the format their country of residence issues — Mainland China: 18-character resident ID; Hong Kong: HKID; United States: SSN or ITIN; elsewhere: the national ID or tax number as issued. Nigerian residents send nin instead. Required before the application can be submitted, and can be patched onto an existing person. The format is validated by the USD provider when the account is issued.
"123456789"
Bank Verification Number. Nigerian residents only; must be 11 digits when the person's currentAddress.country is NG.
"22345678901"
National Identification Number, accepted in place of taxId for Nigerian residents; must be 11 digits when the person's currentAddress.country is NG.
"12345678901"