Organizations
An organization is the firm or the business that holds an account. It owns the client companies, the documents, the billing, and the members. Every organization-scoped route acts inside exactly one of them. See Authentication.
The route prefix is singular for everything inside your own organization, /v1/organization, and
plural only for the two routes that work across organizations, /v1/organizations. That is not a
naming accident. There is no /v1/organizations/{id}, because which organization a request acts
in is decided by the credential and the X-Organization-Id header before a handler runs, never by
an id in the path.
For client companies, see Profiles. For members, see Team, roles, and invitations.
Routes
| Method | Path | Needs |
|---|---|---|
| GET | /v1/organizations |
Any credential. Not scoped |
| POST | /v1/organizations |
A session token. Not scoped |
| GET | /v1/organization |
Any member |
| PUT | /v1/organization |
Admin |
| POST | /v1/organization/avatar |
Admin |
| DELETE | /v1/organization/avatar |
Admin |
| GET | /v1/organization/settings |
Admin |
| PUT | /v1/organization/settings |
Admin, and Owner for two-factor |
| GET | /v1/organization/stats |
Any member |
| GET | /v1/organization/trend |
Any member |
| GET | /v1/setup-checklist-preferences |
Any member |
| PUT | /v1/setup-checklist-preferences |
Any member |
GET /v1/organizations
The organizations this person can act in, with the role they hold in each. Answers a plain array, not a paged envelope.
This is the route a web client calls first, to fill the organization switcher. An API key answers with the one organization it belongs to.
| Field | Type | Means |
|---|---|---|
id |
uuid | Send this as X-Organization-Id on later requests |
name |
string | The firm's name |
kind |
OrganizationKind |
0 firm, 1 business |
locale |
string | en or ro |
role |
OrganizationRole |
0 member, 1 admin, 2 owner |
joinedAt |
date-time | When this person joined |
memberCount |
integer | People in the organization |
visibleProfileCount |
integer | Client companies this person can see, not the total |
hasConnectedSource |
boolean | Whether anything is connected yet, anywhere in the firm |
package |
object | See below |
avatarUrl |
string or null | Logo, 256 px. A short-lived link |
avatarThumbnailUrl |
string or null | Logo, 64 px. A short-lived link |
package:
| Field | Type | Means |
|---|---|---|
state |
EntitlementState |
0 unsubscribed, 1 active, 2 grace, 3 lapsed |
lookupKey |
string or null | The price the organization is on, null if it is on none |
includedProfiles |
integer or null | Client companies the package covers, null for unlimited or none |
productAccess |
boolean | Whether paid routes answer. False means 402 on most of them |
GET /v1/organizations HTTP/1.1
Host: api.syllevo.com
Authorization: Bearer <session token>
[
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example Contabil SRL",
"kind": 0,
"locale": "ro",
"role": 2,
"joinedAt": "2026-01-15T09:00:00Z",
"memberCount": 4,
"visibleProfileCount": 25,
"hasConnectedSource": true,
"package": {
"state": 1,
"lookupKey": "firm_25_monthly",
"includedProfiles": 25,
"productAccess": true
},
"avatarUrl": null,
"avatarThumbnailUrl": null
}
]
POST /v1/organizations
Creates an organization and makes the caller its owner. Session tokens only in practice: an API key already belongs to an organization, and a new one would have no key.
| Field | Type | Required | Means |
|---|---|---|---|
name |
string | yes | The firm's name |
kind |
OrganizationKind |
no | 0 firm, 1 business. Defaults to firm |
locale |
string | no | en or ro. Defaults to the person's locale |
referralCode |
string | no | An affiliate code. See Affiliates |
Answers 201 with the same shape GET /v1/organizations returns for one row.
If the caller already has an organization sitting unpaid, this answers 409 with
provisioning_unpaid_organization_exists. Pay for that one or delete it first.
kind decides the wording of every email Syllevo sends on the organization's behalf, so a business
that files its own documents does not get copy written for an accounting firm. It is not a
permission boundary.
GET /v1/organization
The organization the request resolved to: id, name, locale, avatarUrl, and
avatarThumbnailUrl. Nothing else. The counts are on /v1/organization/stats and the package is
on /v1/organizations.
PUT /v1/organization
Renames the organization and sets the language it works in. Admin.
| Field | Type | Required | Means |
|---|---|---|---|
name |
string | yes | The new name |
locale |
string | no | en or ro. Omitting it leaves the locale as it was |
This is a full replace, not a patch. Answers the updated organization.
The locale decides the language of reminder emails, digests, and invitations sent from this organization, so changing it changes what clients receive.
POST and DELETE /v1/organization/avatar
Sets or removes the organization's logo. Admin.
The upload is multipart/form-data with one part named file.
- At most 5 MB. Over that is
413. - The stored image is converted to WebP at 256 px, with a 64 px thumbnail.
- The links in the response are short lived and are regenerated on every read, so store the route, not the URL.
POST /v1/organization/avatar HTTP/1.1
Host: api.syllevo.com
X-Api-Key: syl_live_examplekeynotreal000000000000
Content-Type: multipart/form-data; boundary=----example
------example
Content-Disposition: form-data; name="file"; filename="logo.png"
Content-Type: image/png
<bytes>
------example--
Both answer 200 with the organization. The delete is idempotent: removing a logo that is already
gone answers 200, not 404.
GET and PUT /v1/organization/settings
The organization's two security toggles. Admin reads and writes, but only an Owner can change
enforceTwoFactor.
| Field | Type | Means |
|---|---|---|
enforceTwoFactor |
boolean | Every member must carry a second factor to sign in |
mcpEnabled |
boolean | Agents may reach this organization over MCP |
isDefaulted |
boolean | True until somebody saves the settings once. Read only |
id |
uuid or null | Null while isDefaulted is true |
createdAt |
date-time, null | Null while isDefaulted is true |
lastUpdatedAt |
date-time, null | Null while isDefaulted is true |
The PUT takes enforceTwoFactor and mcpEnabled and replaces both. Sending only one field sets
the other to false.
mcpEnabled is one of four conditions the MCP surface needs. Turning it on here does not open MCP
on its own.
GET /v1/organization/stats
Five counts for the organization, across every client the caller can see.
| Field | Type | Means |
|---|---|---|
activeProfileCount |
integer | Client companies not archived |
archivedProfileCount |
integer | Client companies archived |
documentCount |
integer | Documents on file |
invoiceCount |
integer | Invoices read out of them |
supplierCount |
integer | Distinct suppliers seen |
GET /v1/organization/trend
Invoices and documents that arrived per day, one point per day with no gaps. A day with nothing still appears, with zeros, so you can chart it without filling holes yourself.
| Parameter | Type | Required | Means |
|---|---|---|---|
from |
date | no | First day, inclusive |
to |
date | no | Last day, inclusive |
- Omit both for the last 30 days.
- Omit
fromfor the 30 days ending atto. - Omit
tofor everything fromfromup to today. - A backwards range is
400. A range longer than 366 days is400.
GET /v1/organization/trend?from=2026-09-01&to=2026-09-03 HTTP/1.1
Host: api.syllevo.com
X-Api-Key: syl_live_examplekeynotreal000000000000
{
"from": "2026-09-01",
"to": "2026-09-03",
"points": [
{ "date": "2026-09-01", "documents": 12, "invoices": 9 },
{ "date": "2026-09-02", "documents": 0, "invoices": 0 },
{ "date": "2026-09-03", "documents": 4, "invoices": 4 }
]
}
GET and PUT /v1/setup-checklist-preferences
Which setup suggestions the caller has dismissed in this organization, so a card waved away on a laptop stays away on a phone. Per person, not per organization: two members of the same firm each have their own list.
| Field | Type | Means |
|---|---|---|
skippedItems |
array of string | The keys of the dismissed suggestions |
updatedAt |
date-time, null | Null until the caller dismisses something |
The PUT replaces the whole list. Sending {"skippedItems": []} brings every suggestion back. An
unknown key is refused with 400, and the response names it by its position in the array.
This is a convenience for a user interface. If you are writing a server integration, you can ignore it.