curl --request GET \
--url https://api.openagent.to/api/v1/reports/control-book \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.openagent.to/api/v1/reports/control-book"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.openagent.to/api/v1/reports/control-book', 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.openagent.to/api/v1/reports/control-book",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.openagent.to/api/v1/reports/control-book"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.openagent.to/api/v1/reports/control-book")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.openagent.to/api/v1/reports/control-book")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"docs": [
{
"asOfDate": "2026-09-01T09:15:32.104Z",
"audit": {
"lastUpdatedBy": null,
"version": null
},
"authorization": {
"authorizationEffectiveDate": null,
"authorizationSource": "board_resolution",
"authorizedShares": "1000000",
"parValuePerShare": "1.25"
},
"controlBookId": "69abe39c-4e57-43e0-b210-46f5dd30314c",
"periodSummary": {
"cancellations": {
"count": 0,
"shareVolume": 0
},
"corporateActionAdjustments": {
"count": 0,
"shareVolume": 0,
"type": null
},
"issuances": {
"count": 0,
"shareVolume": 0
},
"lastTransactionDate": "2025-01-02T00:00:00.000Z",
"period": "all_time",
"transfers": {
"count": 2,
"shareVolume": 45000
},
"treasuryRepurchases": {
"count": 0,
"shareVolume": 0
}
},
"reconciliation": {
"differenceAgeBusinessDays": null,
"lastReconciliationDate": null,
"mshfTotal": 45000,
"onChainDifference": 0,
"onChainTokenTotalSupply": 45000,
"reconciliationStatus": "matched",
"recordDifference": 0,
"threeWayMatch": true
},
"security": {
"blockchain": "ethereum-sepolia",
"cusip": "CUSIP-SEED-001",
"isin": null,
"issuerEin": "00-0000000",
"issuerName": "Acme Tokenization LLC",
"securityClass": "equity",
"securityId": "ccc000000000000000000001",
"securityName": "Seed Preferred Equity",
"tokenAddress": "0x000000000000000000000000000000000000abcd"
},
"shareAccounting": {
"canceledRetiredShares": 0,
"cumulativeIssuedShares": 45000,
"effectiveIssuanceHeadroom": 945000,
"formula": "outstanding = cumulativeIssued - treasury - multiSig",
"outstandingShares": 45000,
"overIssuedShares": 0,
"reservedShares": 10000,
"treasuryShares": 0,
"unissuedShares": 955000
}
}
],
"hasNextPage": true,
"hasPrevPage": true,
"limit": 0,
"nextPage": 0,
"page": 0,
"pagingCounter": 0,
"prevPage": 0,
"totalDocs": 0,
"totalPages": 0
},
"message": "Success",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 200,
"success": true
}{
"error": {
"code": "Validation error",
"details": [
{
"field": "email",
"message": "Invalid email format",
"allowedValues": [
"<string>"
]
}
]
},
"message": "Validation error",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 422,
"success": false
}{
"error": {
"code": "Validation error",
"details": [
{
"field": "email",
"message": "Invalid email format",
"allowedValues": [
"<string>"
]
}
]
},
"message": "Validation error",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 422,
"success": false
}{
"error": {
"code": "Validation error",
"details": [
{
"field": "email",
"message": "Invalid email format",
"allowedValues": [
"<string>"
]
}
]
},
"message": "Validation error",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 422,
"success": false
}{
"error": {
"code": "Validation error",
"details": [
{
"field": "email",
"message": "Invalid email format",
"allowedValues": [
"<string>"
]
}
]
},
"message": "Validation error",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 422,
"success": false
}Control book
One row per security with its full share accounting and a three-way reconciliation between the authorized total, the sum of holder balances in the MSHF, and the on-chain token supply. Computed on request — asOfDate is the time of the call, not a stored date, and there is no date-range parameter. periodSummary.period is therefore always all_time. Two different numeric contracts in one row, deliberately. authorization.authorizedShares and authorization.parValuePerShare are exact decimal strings at whatever scale was stored; every value under shareAccounting, reconciliation and periodSummary is a JSON number, exact only below 2^53. Do not parse one as the other. reconciliation.reconciliationStatus is matched when both differences are zero; threeWayMatch is true when outstanding shares, the MSHF total and on-chain supply all agree. shareAccounting.formula is a literal string for the report footer, not data. securityId changes the response shape. Without it you get the standard page of rows. With it the service pins the page to one row and returns that row on its own, with no page wrapper — or null when the id matches no security in scope. page and limit are ignored in that mode. Branch on whether data has a docs array. Issuer-scoped as /mshf is. Defaults: page 1, 20 per page. search matches name, cusip, isin, ticker, tokenAddress, chain and issuer.legalName.
curl --request GET \
--url https://api.openagent.to/api/v1/reports/control-book \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.openagent.to/api/v1/reports/control-book"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.openagent.to/api/v1/reports/control-book', 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.openagent.to/api/v1/reports/control-book",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.openagent.to/api/v1/reports/control-book"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.openagent.to/api/v1/reports/control-book")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.openagent.to/api/v1/reports/control-book")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"docs": [
{
"asOfDate": "2026-09-01T09:15:32.104Z",
"audit": {
"lastUpdatedBy": null,
"version": null
},
"authorization": {
"authorizationEffectiveDate": null,
"authorizationSource": "board_resolution",
"authorizedShares": "1000000",
"parValuePerShare": "1.25"
},
"controlBookId": "69abe39c-4e57-43e0-b210-46f5dd30314c",
"periodSummary": {
"cancellations": {
"count": 0,
"shareVolume": 0
},
"corporateActionAdjustments": {
"count": 0,
"shareVolume": 0,
"type": null
},
"issuances": {
"count": 0,
"shareVolume": 0
},
"lastTransactionDate": "2025-01-02T00:00:00.000Z",
"period": "all_time",
"transfers": {
"count": 2,
"shareVolume": 45000
},
"treasuryRepurchases": {
"count": 0,
"shareVolume": 0
}
},
"reconciliation": {
"differenceAgeBusinessDays": null,
"lastReconciliationDate": null,
"mshfTotal": 45000,
"onChainDifference": 0,
"onChainTokenTotalSupply": 45000,
"reconciliationStatus": "matched",
"recordDifference": 0,
"threeWayMatch": true
},
"security": {
"blockchain": "ethereum-sepolia",
"cusip": "CUSIP-SEED-001",
"isin": null,
"issuerEin": "00-0000000",
"issuerName": "Acme Tokenization LLC",
"securityClass": "equity",
"securityId": "ccc000000000000000000001",
"securityName": "Seed Preferred Equity",
"tokenAddress": "0x000000000000000000000000000000000000abcd"
},
"shareAccounting": {
"canceledRetiredShares": 0,
"cumulativeIssuedShares": 45000,
"effectiveIssuanceHeadroom": 945000,
"formula": "outstanding = cumulativeIssued - treasury - multiSig",
"outstandingShares": 45000,
"overIssuedShares": 0,
"reservedShares": 10000,
"treasuryShares": 0,
"unissuedShares": 955000
}
}
],
"hasNextPage": true,
"hasPrevPage": true,
"limit": 0,
"nextPage": 0,
"page": 0,
"pagingCounter": 0,
"prevPage": 0,
"totalDocs": 0,
"totalPages": 0
},
"message": "Success",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 200,
"success": true
}{
"error": {
"code": "Validation error",
"details": [
{
"field": "email",
"message": "Invalid email format",
"allowedValues": [
"<string>"
]
}
]
},
"message": "Validation error",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 422,
"success": false
}{
"error": {
"code": "Validation error",
"details": [
{
"field": "email",
"message": "Invalid email format",
"allowedValues": [
"<string>"
]
}
]
},
"message": "Validation error",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 422,
"success": false
}{
"error": {
"code": "Validation error",
"details": [
{
"field": "email",
"message": "Invalid email format",
"allowedValues": [
"<string>"
]
}
]
},
"message": "Validation error",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 422,
"success": false
}{
"error": {
"code": "Validation error",
"details": [
{
"field": "email",
"message": "Invalid email format",
"allowedValues": [
"<string>"
]
}
]
},
"message": "Validation error",
"meta": {
"timestamp": "2026-09-01T09:15:32.104Z",
"requestId": "3f1c9d2e-6b7a-4f18-9c53-0a2b6d4e8f10"
},
"statusCode": 422,
"success": false
}Authorizations
Staff access token. Sent as Authorization: Bearer <token>, or — when that header is absent — read from the accessToken cookie, which is how the admin app authenticates. Tokens carry a tokenVersion; logout and password reset bump it, revoking every outstanding token for that account.
Query Parameters
Narrow the report to one security. Omit for every security in scope.
^[a-f\d]{24}$1-based page number. Defaults to 1.
x <= 9007199254740991Records per page, 1-100. Defaults to 20.
x <= 100Field names separated by spaces (or commas, which are normalized to spaces), - prefix for descending. Defaults to -createdAt.
^-?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?:[\s,]+-?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)*$Case-insensitive substring matched against name, cusip, isin, ticker, tokenAddress, chain, issuer.legalName.
Comma-separated subset of name, cusip, isin, ticker, tokenAddress, chain, issuer.legalName to match search against. Names outside that list are ignored, and an empty intersection falls back to all of them.
Response
A page of rows, or — when securityId is given — the single row unwrapped, which is null if nothing matched.

