AvalloneAPI

Related companies

Read and write the ownership relations between one company and others, including each relation's company roles.

A relation links two companies: a parent and a subsidiary. It can carry an ownership percentage, a voting percentage, notes, and a list of company roles and reasons.

These endpoints manage the relations of one company, given by companyId in the path:

EndpointWhat it does
GET /companies/{companyId}/related-companiesLists the company's relations, in both directions
POST /companies/{companyId}/related-companiesCreates relations
PUT /companies/{companyId}/related-companiesReplaces all of the company's relations, in both directions
GET /companies/{companyId}/related-companies/{relationId}Returns one relation
PATCH /companies/{companyId}/related-companies/{relationId}Changes some fields of one relation
PUT /companies/{companyId}/related-companies/{relationId}Replaces the fields of one relation
DELETE /companies/{companyId}/related-companies/{relationId}Deletes one relation

companyId is the company's Avallone ID. relationId is the relation's own ID, returned as id. It is not the ID of either company.

Before you start

Reading relations needs the VIEW_COMPANY permission. Writing them needs UPDATE_COMPANY. See Permissions.

Company roles are set up in Avallone, under Settings → Company Roles. A relation can only use roles that already exist in your tenant.

The relation object

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "parent",
  "companyReferenceId": "acme-holding",
  "companyId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "ownership": 51,
  "voting": 51,
  "notes": "Majority shareholder",
  "details": [
    { "role": "General Partner", "reason": "Manages the fund" },
    { "reason": "Common management" }
  ],
  "reasons": ["Common management"],
  "url": "/api/v1/companies/7c9e6679-7425-40de-944b-e07fc1f90ae7/related-companies/3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
FieldDescription
idThe relation's ID
typeWhere the other company sits, seen from companyId. parent means it is above, as an owner. subsidiary means it is below.
companyIdThe other company's Avallone ID
companyReferenceIdThe other company's reference ID, when it has one
ownershipOwnership percentage, from 0 to 100
votingVoting percentage, from 0 to 100
notesFree text
detailsThe relation's company roles and reasons. See Roles and reasons.
reasonsDeprecated. The reasons that have no role. details lists them too. Removed on 2027-06-30.
urlThe path of this relation

A field without a value is left out of the response.

Roles and reasons

details is a list. Each entry is a company role, a reason, or a role with a reason:

"details": [
  { "role": "General Partner", "reason": "Manages the fund" },
  { "role": "Guarantor" },
  { "reason": "Common management" }
]

These rules apply when you send details:

  • role is the name of a company role in your tenant. Case, surrounding spaces, and the difference between a space and a dash are ignored, so general-partner matches General Partner.
  • A role that doesn't exist returns 404, and nothing is written.
  • A relation can have each role only once. Listing a role twice returns 400.
  • Each entry needs a role, a reason, or both.

How each method uses details:

MethodWith detailsWithout details
POSTThe relation gets these roles and reasons.The relation has no roles.
PUTReplaces every role and reason on the relation.The roles are kept. reasons replaces the reasons that have no role.
PATCHReplaces every role and reason on the relation. Send null or [] to remove them all.The roles are kept. reasons, when sent, replaces the reasons that have no role.

Deprecated reasons

reasons is a list of strings, and still works. It is deprecated and will be removed on 2027-06-30. Use details instead.

When a request sends both, reasons must list the same reasons as the entries in details that have no role. Otherwise the request returns 400, and in sync the relation fails. Reasons are compared without regard to case and surrounding spaces. A response lists those reasons in both fields, so you can send a relation back unchanged.

List relations

GET /api/v1/companies/{companyId}/related-companies

Returns the company's relations in both directions: its parents and its subsidiaries. Use type to tell them apart.

Permission: VIEW_COMPANY

Query parameters:

ParamDescription
typeOptional. parent or subsidiary returns only that direction. Any other value returns 400.

Response: 200 with a list of relation objects.

Create relations

POST /api/v1/companies/{companyId}/related-companies

Creates a relation for each item in the body. If one item fails, none are created.

Permission: UPDATE_COMPANY

Request body: a list of items with these fields:

FieldRequiredDescription
companyIdOne of the two IDsThe other company's Avallone ID
companyReferenceIdOne of the two IDsThe other company's reference ID
typeYesparent or subsidiary: where the other company sits
ownershipNoOwnership percentage, from 0 to 100
votingNoVoting percentage, from 0 to 100
notesNoFree text
detailsNoCompany roles and reasons. See Roles and reasons.
reasonsNoDeprecated. Use details.

When you send both companyId and companyReferenceId, they must name the same company.

POST /api/v1/companies/{companyId}/related-companies
Content-Type: application/json

[
  {
    "companyReferenceId": "acme-holding",
    "type": "parent",
    "ownership": 51,
    "voting": 51,
    "details": [{ "role": "General Partner", "reason": "Manages the fund" }]
  }
]

Response: 201 with the relations that were created.

A relation that already exists returns 409. So does one that would make a company own itself, directly or through other companies.

Replace all relations

PUT /api/v1/companies/{companyId}/related-companies

Makes the company's relations match the body, in both directions. The body has the same items as Create relations.

  • An item that matches an existing relation, with the same other company and type, updates it. The relation keeps its id.
  • An item with no match creates a relation.
  • An existing relation that isn't in the body is deleted.

Sending [] deletes every relation of the company.

Permission: UPDATE_COMPANY

Response: 200 with all of the company's relations after the change.

Get one relation

GET /api/v1/companies/{companyId}/related-companies/{relationId}

Permission: VIEW_COMPANY

Response: 200 with the relation object.

Change one relation

PATCH /api/v1/companies/{companyId}/related-companies/{relationId}
PUT /api/v1/companies/{companyId}/related-companies/{relationId}

PATCH changes only the fields you send. PUT replaces all of them, and clears any field you leave out. The exception is the roles: a PUT without details keeps them.

The two companies and the direction can't change. These fields can:

FieldDescription
ownershipOwnership percentage, from 0 to 100
votingVoting percentage, from 0 to 100
notesFree text
detailsCompany roles and reasons. See Roles and reasons.
reasonsDeprecated. Use details.

delete on a details entry returns 400. To remove a role, send the roles to keep.

Permission: UPDATE_COMPANY

Response: 200 with the relation object.

Delete one relation

DELETE /api/v1/companies/{companyId}/related-companies/{relationId}

Permission: UPDATE_COMPANY

Response: 204 with no body.

Relations in PUT /sync

PUT /sync also writes relations, in relatedCompanies on each company. It accepts the same details, with these differences:

  • Without cleanup, the entries are added to the roles and reasons the relation already has. An entry with delete: true removes one.
  • With cleanup, details replaces every role and reason, as PUT does here.
  • A role listed twice keeps the last entry, instead of returning 400.

Errors

StatusWhen
400The body or a parameter is invalid. This includes a missing type, two IDs that name different companies, reasons and details that disagree, and a role listed twice.
401The token is missing, malformed, expired or revoked.
403The token doesn't have the permission.
404companyId doesn't exist, a company or a role named in the body doesn't exist, or relationId isn't a relation of this company.
409The relation already exists, or it would make a company own itself.

These endpoints don't follow every API design guideline yet. For example, a list is returned as a bare array, and errors don't use the new error format.

On this page