Custodian Authorities
Data Model
Custodian Authority
A custodian authority represents the lifecycle of granting Aleta access to a custodian on behalf of a legal entity.
status(string, required): Current status in the custodian authority lifecycle. One of:waiting-for-files— The custodian authority has been created. Required files (listed inpendingFiles) must be uploaded viaPOST /custodian-authorities/:id/files.waiting-for-consent— All required files have been uploaded. A signed consent JWT must now be submitted viaPOST /custodian-authorities/:id/consent.awaiting-aleta— Consent has been submitted (or the custodian has responded). Aleta is reviewing / processing.awaiting-custodian— Aleta has forwarded the request to the custodian. Waiting for the custodian to act. The status may alternate betweenawaiting-aletaandawaiting-custodianmultiple times.active— The custodian authority is fully established and dataflow is operational.revoke-in-progress— A revocation has been requested. Aleta is removing dataflow links with the custodian.revoked— The custodian authority has been fully revoked. Dataflow has stopped.cancelled— The custodian authority was cancelled before becoming active (e.g. rejected by the custodian).
pendingFiles(array, required): List of file names that still need to be uploaded before consent can be submitted. Only populated when status iswaiting-for-files. Empty array otherwise. File names correspond to those defined in the custodian auth flow'sexpectedFiles.linkedDepositories(array, required): Depositories linked to this custodian authority. Each entry contains:depositoryNumber(string): The depository number as it appears at the custodian.depositoryId(string, nullable): The Aleta depository ID. Null while the depository is being set up (statusin-progress), populated onceactive.status(string): Status of the linked depository. One of:in-progress— The depository link has been requested. Aleta is setting up the dataflow connection with the custodian. ThedepositoryIdis null during this state.active— The depository is fully linked and dataflow is operational. ThedepositoryIdis populated.
linkedAccounts(array, required): Accounts linked to this custodian authority. Each entry contains:accountNumber(string): The account number as it appears at the custodian.accountId(string, nullable): The Aleta account ID. Null while the account is being set up (statusin-progress), populated onceactive.status(string): Status of the linked account. One of:in-progress— The account link has been requested. Aleta is setting up the dataflow connection with the custodian. TheaccountIdis null during this state.active— The account is fully linked and dataflow is operational. TheaccountIdis populated.
Relationships
custodian: The custodian that the authority relates to.custodianAuthFlow: The custodian auth flow used to create the authority.legalEntity: The legal entity that the authority is granted for.
Endpoints
GET /legal-entities/:legalEntityId/custodian-authorities
Not yet available.
List all custodian authorities for a given legal entity.
Parameters
legalEntityId(path, required): Identifier for the legal entity.
Examples
200 OK
# Request
GET /api/v2/legal-entities/67b01234a5bc678d90e12345/custodian-authorities HTTP/1.1
Accept: application/vnd.api+json
Authorization: Bearer <access token>
Host: platform.aleta.io
# Response
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": "2668e473-b2a2-47a9-a90b-a5ec0c812d44",
"type": "custodian-authority",
"attributes": {
"status": "active",
"pendingFiles": [],
"linkedDepositories": [
{
"depositoryNumber": "1234-5678901234",
"depositoryId": "67c01234a5bc678d90e12345",
"status": "active"
},
{
"depositoryNumber": "1234-5678901235",
"depositoryId": null,
"status": "in-progress"
}
],
"linkedAccounts": [
{
"accountNumber": "1234-0011223344",
"accountId": "67c01234a5bc678d90e12346",
"status": "active"
}
]
},
"relationships": {
"custodian": {
"data": {
"type": "custodian",
"id": "678e9012f3ab456c78d90123"
}
},
"custodianAuthFlow": {
"data": {
"type": "custodian-auth-flow",
"id": "nordea-20260630"
}
},
"legalEntity": {
"data": {
"type": "legal-entity",
"id": "67b01234a5bc678d90e12345"
}
}
}
},
{
"id": "ac8fa31f-b9bd-414c-a56d-8d95848ebbcf",
"type": "custodian-authority",
"attributes": {
"status": "waiting-for-files",
"pendingFiles": [
"power-of-attorney",
"consent"
],
"linkedDepositories": [],
"linkedAccounts": []
},
"relationships": {
"custodian": {
"data": {
"type": "custodian",
"id": "679a2345b6cd789e01f23456"
}
},
"custodianAuthFlow": {
"data": {
"type": "custodian-auth-flow",
"id": "saxo-20260630"
}
},
"legalEntity": {
"data": {
"type": "legal-entity",
"id": "67b01234a5bc678d90e12345"
}
}
}
}
]
}
POST /legal-entities/:legalEntityId/custodian-authorities
Not yet available.
Create a custodian authority for a given legal entity using the specified custodian auth flow.
The new custodian authority is created in the waiting-for-files status. Upload the required
files (listed in pendingFiles) using the POST /custodian-authorities/:id/files endpoint to
advance to waiting-for-consent.
Parameters
legalEntityId(path, required): Identifier for the legal entity.custodianAuthFlowId(query, required): The custodian auth flow identifier.
Examples
201 Created
# Request
POST /api/v2/legal-entities/67b01234a5bc678d90e12345/custodian-authorities
?custodianAuthFlowId=nordea-20260630 HTTP/1.1
Accept: application/vnd.api+json
Authorization: Bearer <access token>
Host: platform.aleta.io
# Response
HTTP/1.1 201 Created
Content-Type: application/vnd.api+json
{
"data": {
"id": "2668e473-b2a2-47a9-a90b-a5ec0c812d44",
"type": "custodian-authority",
"attributes": {
"status": "waiting-for-files",
"pendingFiles": [
"power-of-attorney",
"consent"
],
"linkedDepositories": [],
"linkedAccounts": []
},
"relationships": {
"custodian": {
"data": {
"type": "custodian",
"id": "678e9012f3ab456c78d90123"
}
},
"custodianAuthFlow": {
"data": {
"type": "custodian-auth-flow",
"id": "nordea-20260630"
}
},
"legalEntity": {
"data": {
"type": "legal-entity",
"id": "67b01234a5bc678d90e12345"
}
}
}
}
}
400 Bad Request
Returned when the custodian auth flow ID is outdated. Use the latest custodian auth flow for onboarding.
404 Not Found
Returned when the custodian auth flow ID or legal entity ID does not exist.
GET /custodian-authorities/:id
Not yet available.
Get a custodian authority by its id.
Parameters
id(path, required): Custodian authority identifier.
Examples
200 OK
# Request
GET /api/v2/custodian-authorities/2668e473-b2a2-47a9-a90b-a5ec0c812d44 HTTP/1.1
Accept: application/vnd.api+json
Authorization: Bearer <access token>
Host: platform.aleta.io
# Response
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "2668e473-b2a2-47a9-a90b-a5ec0c812d44",
"type": "custodian-authority",
"attributes": {
"status": "active",
"pendingFiles": [],
"linkedDepositories": [
{
"depositoryNumber": "1234-5678901234",
"depositoryId": "67c01234a5bc678d90e12345",
"status": "active"
}
],
"linkedAccounts": [
{
"accountNumber": "1234-0011223344",
"accountId": "67c01234a5bc678d90e12346",
"status": "active"
}
]
},
"relationships": {
"custodian": {
"data": {
"type": "custodian",
"id": "678e9012f3ab456c78d90123"
}
},
"custodianAuthFlow": {
"data": {
"type": "custodian-auth-flow",
"id": "nordea-20260630"
}
},
"legalEntity": {
"data": {
"type": "legal-entity",
"id": "67b01234a5bc678d90e12345"
}
}
}
}
}
POST /custodian-authorities/:id/files
Not yet available.
Upload a file for a custodian authority. The file name is specified via the name query parameter.
Supported names are defined by the custodian auth flow (e.g. power-of-attorney, consent, back-office-login).
The returned hash is prefixed with the algorithm used (e.g. sha2-256: or sha3-256:) and serves as proof of file content for the POST /custodian-authorities/:id/consent endpoint.
Once all required files have been uploaded, the custodian authority status transitions
from waiting-for-files to waiting-for-consent and pendingFiles becomes empty.
Parameters
id(path, required): Custodian authority identifier.name(query, required): Name of the file (e.g.power-of-attorney,consent,back-office-login). Must match one of the names defined in the custodian auth flow'sexpectedFiles.
Request Body
The request body must be multipart/form-data with a single file field containing the file to upload.
Examples
201 Created
# Request
POST /api/v2/custodian-authorities/2668e473-b2a2-47a9-a90b-a5ec0c812d44/files
?name=power-of-attorney HTTP/1.1
Authorization: Bearer <access token>
Content-Type: multipart/form-data; boundary=----FormBoundary
Host: platform.aleta.io
------FormBoundary
Content-Disposition: form-data; name="file"; filename="poa_nordea_2026.pdf"
Content-Type: application/pdf
<binary file content>
------FormBoundary--
# Response
HTTP/1.1 201 Created
Content-Type: application/json
{
"hash": "sha2-256:7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730",
"name": "power-of-attorney",
"uploadedAt": "2026-03-01T10:00:00Z",
"filename": "poa_nordea_2026.pdf"
}
GET /custodian-authorities/:id/files
Not yet available.
List all files uploaded for a custodian authority, including their name, hash, upload timestamp, and original filename. Files cannot be deleted, as they serve as proof of access.
Parameters
id(path, required): Custodian authority identifier.
Examples
200 OK
# Request
GET /api/v2/custodian-authorities/2668e473-b2a2-47a9-a90b-a5ec0c812d44/files HTTP/1.1
Authorization: Bearer <access token>
Host: platform.aleta.io
# Response
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"name": "power-of-attorney",
"hash": "sha2-256:7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730",
"uploadedAt": "2026-03-01T10:00:00Z",
"filename": "poa_nordea_2026.pdf"
},
{
"name": "consent",
"hash": "sha2-256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"uploadedAt": "2026-03-01T10:01:00Z",
"filename": "consent_nordea_2026.pdf"
}
]
POST /custodian-authorities/:id/consent
Not yet available.
Submit a signed JWT containing the consent details. The custodian authority must be in waiting-for-consent status (i.e. all required files have been uploaded). On success, the status transitions to awaiting-aleta.
The JWT must be signed using the ES256 algorithm (ECDSA with P-256 curve). The JWT header must include a kid (key ID) identifying the signing key.
JWT Header
{
"alg": "ES256",
"typ": "JWT",
"kid": "<key ID>"
}
JWT Payload
custodianAuthorityId(string, required): The custodian authority ID.files(array, required): Array of objects, each with aname(matching the file name used during upload) and exactly one hash property. Usehash-sha2-256for SHA-2 256 orhash-sha3-256for SHA-3 256.legalEntityIdentifier(string, required): Identifier of the legal entity (e.g. CVR, CPR).custodianIdentifier(string, required): Theidentifierattribute from the custodian object.consentType(string, required): Type of consent. Possible values:"all","specific".depositoryNumbers(array, optional): List of depository numbers. Used whenconsentTypeis"specific".accountNumbers(array, optional): List of account numbers. Used whenconsentTypeis"specific".iat(number, required): Standard JWT "issued at" claim — Unix epoch, seconds since 1970-01-01T00:00:00Z.
Parameters
id(path, required): Custodian authority identifier.
Request Body
The request body must be an ES256-signed JWT with content type application/jwt.
Examples
204 No Content — Specific consent
The decoded JWT payload for this example:
{
"custodianAuthorityId": "2668e473-b2a2-47a9-a90b-a5ec0c812d44",
"files": [
{
"name": "consent",
"hash-sha2-256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
{
"name": "power-of-attorney",
"hash-sha2-256": "7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730"
}
],
"legalEntityIdentifier": "12345678",
"custodianIdentifier": "nordea-dk",
"consentType": "specific",
"depositoryNumbers": ["2345-6789012345", "2345-6789012346"],
"accountNumbers": ["2345-0011223344", "2345-0011223345"],
"iat": 1772359200
}
# Request
POST /api/v2/custodian-authorities/2668e473-b2a2-47a9-a90b-a5ec0c812d44/consent HTTP/1.1
Authorization: Bearer <access token>
Content-Type: application/jwt
Host: platform.aleta.io
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15LWtleS1pZCJ9.eyJjdXN0b2RpYW5BdXRob3JpdHlJZCI6IjI2NjhlNDczLWIyYTItNDdhOS1hOTBiLWE1ZWMwYzgxMmQ0NCIsImZpbGVzIjpbeyJuYW1lIjoiY29uc2VudCIsImhhc2gtc2hhMi0yNTYiOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1In0seyJuYW1lIjoicG93ZXItb2YtYXR0b3JuZXkiLCJoYXNoLXNoYTItMjU2IjoiN2Q4NjVlOTU5YjI0NjY5MThjOTg2M2FmY2E5NDJkMGZiODlkN2M5YWMwYzk5YmFmYzM3NDk1MDRkZWQ5NzczMCJ9XSwibGVnYWxFbnRpdHlJZGVudGlmaWVyIjoiMTIzNDU2NzgiLCJjdXN0b2RpYW5JZGVudGlmaWVyIjoibm9yZGVhLWRrIiwiY29uc2VudFR5cGUiOiJzcGVjaWZpYyIsImRlcG9zaXRvcnlOdW1iZXJzIjpbIjIzNDUtNjc4OTAxMjM0NSIsIjIzNDUtNjc4OTAxMjM0NiJdLCJhY2NvdW50TnVtYmVycyI6WyIyMzQ1LTAwMTEyMjMzNDQiLCIyMzQ1LTAwMTEyMjMzNDUiXSwiaWF0IjoxNzcyMzU5MjAwfQ.LG3elqKqCZX6__wRgJOck2EcwU1nw-sJCGaPr-5BumnAVvaqXrO7CaxyEdM0Ymo9JxLCqtzQBtfIpKY3JNCz7w
# Response
HTTP/1.1 204 No Content
400 Bad Request — Duplicate "all" consent
A legal entity may only have one custodian authority with consent type all per custodian.
# Request
POST /api/v2/custodian-authorities/2668e473-b2a2-47a9-a90b-a5ec0c812d44/consent HTTP/1.1
Authorization: Bearer <access token>
Content-Type: application/jwt
Host: platform.aleta.io
<signed JWT>
# Response
HTTP/1.1 400 Bad Request
Content-Type: application/vnd.api+json
{
"errors": [
{
"status": 400,
"detail": "A custodian authority with consent type 'all' already exists for this legal entity and custodian."
}
]
}
400 Bad Request — Overlapping "specific" consent
For consent type specific, multiple custodian authorities are allowed per legal entity and custodian combination, but the depository numbers and account numbers must not overlap with any existing custodian authority.
# Request
POST /api/v2/custodian-authorities/2668e473-b2a2-47a9-a90b-a5ec0c812d44/consent HTTP/1.1
Authorization: Bearer <access token>
Content-Type: application/jwt
Host: platform.aleta.io
<signed JWT>
# Response
HTTP/1.1 400 Bad Request
Content-Type: application/vnd.api+json
{
"errors": [
{
"status": 400,
"detail": "One or more depository numbers or account numbers overlap with an existing custodian authority for this legal entity and custodian."
}
]
}
POST /custodian-authorities/:id/link
Not yet available.
Link a depository or account to a custodian authority. This is only supported for custodian authorities with consent type all.
If you want to add depositories or accounts to a custodian authority with consent type specific, create a new custodian authority with the additional depositories or accounts instead.
Parameters
id(path, required): Custodian authority identifier.type(query, required): The type of resource to link. Possible values:"depository","account".resourceNumber(query, required): The depository number or account number to link to this custodian authority.
Examples
204 No Content
# Request
POST /api/v2/custodian-authorities/2668e473-b2a2-47a9-a90b-a5ec0c812d44/link
?type=depository
&resourceNumber=1234-5678901235 HTTP/1.1
Authorization: Bearer <access token>
Host: platform.aleta.io
# Response
HTTP/1.1 204 No Content
400 Bad Request — Invalid consent type
Returned when attempting to link a depository or account to a custodian authority that does not have consent type all.
# Request
POST /api/v2/custodian-authorities/ac8fa31f-b9bd-414c-a56d-8d95848ebbcf/link
?type=depository
&resourceNumber=1234-5678901235 HTTP/1.1
Authorization: Bearer <access token>
Host: platform.aleta.io
# Response
HTTP/1.1 400 Bad Request
Content-Type: application/vnd.api+json
{
"errors": [
{
"status": 400,
"detail": "Linking is only supported for custodian authorities with consent type 'all'."
}
]
}
POST /custodian-authorities/:id/revoke
Not yet available.
Submit an ES256-signed JWT to initiate revocation of a custodian authority. The custodian authority must be in active status.
Returns a signed JWT from Aleta confirming the revocation request. After this call, the custodian authority status transitions to revoke-in-progress. Aleta then removes the dataflow links between the depositories/accounts and the custodian. Once complete, the status transitions to revoked.
Poll GET /custodian-authorities/:id to track the status.
JWT Header (Request)
{
"alg": "ES256",
"typ": "JWT",
"kid": "<key ID>"
}
JWT Payload (Request)
custodianAuthorityId(string, required): The custodian authority ID.iat(number, required): Standard JWT "issued at" claim — Unix epoch, seconds since 1970-01-01T00:00:00Z.
JWT Payload (Response)
The response JWT from Aleta contains the same fields:
custodianAuthorityId(string): The custodian authority ID.iat(number): Timestamp of the confirmation.
The response JWT is signed with Aleta's ES256 private key. The header includes Aleta's kid.
Parameters
id(path, required): Custodian authority identifier.
Request Body
The request body must be an ES256-signed JWT with content type application/jwt.
Examples
200 OK
The decoded request JWT payload:
{
"custodianAuthorityId": "2668e473-b2a2-47a9-a90b-a5ec0c812d44",
"iat": 1772359200
}
The decoded response JWT payload:
{
"custodianAuthorityId": "2668e473-b2a2-47a9-a90b-a5ec0c812d44",
"iat": 1772359200
}
# Request
POST /api/v2/custodian-authorities/2668e473-b2a2-47a9-a90b-a5ec0c812d44/revoke HTTP/1.1
Authorization: Bearer <access token>
Content-Type: application/jwt
Host: platform.aleta.io
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15LWtleS1pZCJ9.eyJjdXN0b2RpYW5BdXRob3JpdHlJZCI6IjI2NjhlNDczLWIyYTItNDdhOS1hOTBiLWE1ZWMwYzgxMmQ0NCIsImlhdCI6MTc3MjM1OTIwMH0.Yiri0k38sbRDvlPj8nycpCf3_TbVk9jI6aC_7AJH95tAA6z0PrxA9nQ9bS1dxrZvbtzRhuIrAmYpX04Qenhimg
# Response
HTTP/1.1 200 OK
Content-Type: application/jwt
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFsZXRhLWtleS1pZCJ9.eyJjdXN0b2RpYW5BdXRob3JpdHlJZCI6IjI2NjhlNDczLWIyYTItNDdhOS1hOTBiLWE1ZWMwYzgxMmQ0NCIsImlhdCI6MTc3MjM1OTIwMH0.HjDhOxDPjz7HbVrf8_abl7blfZs3IbnH4OxEMpO2M4AyYcDxvveWCnSBcsbHkyLSsvNVhCjm_8494r6b2QCi5g