Users
The signed-in person, as distinct from the organization they act in. These routes are about one human account: their name, their email, their language, and their picture.
All four routes here are not organization-scoped. They answer without X-Organization-Id,
because they are the routes a client calls before it knows which organization to ask about.
If you are writing a server integration with an API key, you will rarely need this page. A key is
not a person. GET /v1/users/me answers with the person who created the key.
Routes
| Method | Path | Needs |
|---|---|---|
| GET | /v1/users/me |
Any credential |
| POST | /v1/users/me/avatar |
Any credential. Acts on the caller only |
| DELETE | /v1/users/me/avatar |
Any credential. Acts on the caller only |
| POST | /v1/onboarding/session |
A session token, right after signing in |
GET /v1/users/me
The current person.
| Field | Type | Means |
|---|---|---|
id |
uuid | The Syllevo user id |
email |
string | Their address |
firstName |
string or null | Null until they fill it in |
lastName |
string or null | Null until they fill it in |
locale |
string | en or ro |
avatarUrl |
string or null | Picture, 256 px. A short-lived link |
avatarThumbnailUrl |
string or null | Picture, 64 px. A short-lived link |
GET /v1/users/me HTTP/1.1
Host: api.syllevo.com
X-Api-Key: syl_live_examplekeynotreal000000000000
{
"id": "66666666-6666-4666-8666-666666666666",
"email": "maria@example.com",
"firstName": "Maria",
"lastName": "Popescu",
"locale": "ro",
"avatarUrl": null,
"avatarThumbnailUrl": null
}
This person's locale decides the language of the interface and of anything Syllevo sends to them
directly. It is separate from the organization's locale, which decides the language of what clients
receive. See Organizations.
POST and DELETE /v1/users/me/avatar
Sets or removes the caller's own picture.
The upload is multipart/form-data with one part named file, at most 5 MB, stored as WebP at
256 px with a 64 px thumbnail. The same rules as the organization logo. See
Organizations.
There is no route for setting somebody else's picture, and that is deliberate. The row the picture
lands on is decided by the route and by the credential, never by a field in the form, so
/v1/users/me/avatar has no way to name a colleague.
Both answer 200 with the person. The delete is idempotent: removing a picture that is already
gone answers 200, not 404.
POST /v1/onboarding/session
The first call a web client makes after sign-in. It creates the person's row if this is their first visit, then answers with everything a client needs to decide what to show next.
Send no body. On production hosts a body is ignored entirely, and an empty POST is accepted rather than refused.
| Field | Type | Means |
|---|---|---|
user |
object | The same shape as GET /v1/users/me |
organizations |
array | The same shape as GET /v1/organizations |
pendingInvitations |
array | Invitations waiting for this person. See below |
Each pending invitation:
| Field | Type | Means |
|---|---|---|
id |
uuid | Accept or decline it with this |
organizationId |
uuid | The firm inviting them |
organizationName |
string | Its name |
organizationKind |
OrganizationKind |
0 firm, 1 business |
role |
OrganizationRole |
The role they would get |
invitedByName |
string or null | Who sent it, when that is known |
expiresAt |
date-time | After this the invitation is dead |
hasOrganizationWideAccess |
boolean | Whether the invitation grants every client |
grantedProfileCount |
integer | How many clients it grants, when it is not org-wide |
POST /v1/onboarding/session HTTP/1.1
Host: api.syllevo.com
Authorization: Bearer <session token>
Content-Length: 0
{
"user": {
"id": "66666666-6666-4666-8666-666666666666",
"email": "maria@example.com",
"firstName": "Maria",
"lastName": "Popescu",
"locale": "ro",
"avatarUrl": null,
"avatarThumbnailUrl": null
},
"organizations": [],
"pendingInvitations": [
{
"id": "77777777-7777-4777-8777-777777777777",
"organizationId": "11111111-1111-4111-8111-111111111111",
"organizationName": "Example Contabil SRL",
"organizationKind": 0,
"role": 0,
"invitedByName": "Andrei Ionescu",
"expiresAt": "2026-09-25T09:00:00Z",
"hasOrganizationWideAccess": false,
"grantedProfileCount": 3
}
]
}
Three outcomes are worth branching on:
organizationsis empty andpendingInvitationsis empty. Send them to create an organization.organizationsis empty and there is an invitation. Send them to accept it. See Team, roles, and invitations.organizationshas entries. Pick one and start sendingX-Organization-Id.