> ## Documentation Index
> Fetch the complete documentation index at: https://developer.findity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Users

Manage user profiles, preferences, settings, and account details in the Expense API

Retrieve and manage the authenticated user's profile, preferences, settings, and account details — the starting point for any Expense API integration.

## Overview

The Expense API user endpoints operate through the `/me` context, meaning all operations apply to the currently authenticated user. After authenticating with a user-scoped token (via the `authorization_code` grant), the `/me` endpoint is your first call — it returns the user's identity, preferences, and capabilities.

The user context is essential because most Expense API endpoints (expenses, reports, approvals) are scoped to the authenticated user's permissions and organization memberships.

## Key concepts

| Concept | Description |
| - | - |
| **Person** | A unique individual in the Findity system. A person can have multiple users (employees) across multiple organizations |
| **Capabilities** | The `meta.capabilities` object indicates system-wide abilities like impersonation and admin access |
| **Impersonation** | Acting as another user — used by admin tools and support integrations |
| **Extra recipients** | Additional email addresses that can receive expense-related notifications |
| **Counters** | Per-organization badge counts for expenses, approvals, notifications, and rejections |

## Get current user

Retrieve the authenticated user's profile. This is typically the first call after obtaining a user-scoped access token.

```
GET /v1/expense/me
```

### Query parameters

| Parameter | Type | Description |
| - | - | - |
| `include` | string | Optional properties: `preferences`, `extraRecipients`, `meta.capabilities`, `temporaryApprovers` |

### Example request

```bash bash theme={"system"}
curl -X GET "https://stage-api.findity.com/api/v1/expense/me?include=preferences,meta.capabilities" \
  -H "Authorization: Bearer {access_token}"
```

### Example response

```json theme={"system"}
{
  "id": "370e10197a2740dea1f39d21bc9e4a8c",
  "email": "emma.expense@example.com",
  "firstName": "Emma",
  "lastName": "Expense",
  "mobilePhone": "+4670123456789",
  "defaultCurrency": "USD",
  "language": "en_US",
  "IBAN": "US2138890474392778363181++",
  "BIC": "ABCDEF123",
  "formattingRegion": "sv_SE",
  "address": {
    "id": "2ee7643987df40b38537b32ccda585d9",
    "street1": "132, My Street",
    "postalCode": "12401",
    "city": "New York",
    "countryCode": "US"
  },
  "meta": {
    "capabilities": {
      "canImpersonate": false,
      "canViewAdmin": false
    }
  }
}
```

## Update current user

Update the authenticated user's profile information.

```
PUT /v1/expense/me
```

### Example request

```bash bash theme={"system"}
curl -X PUT "https://stage-api.findity.com/api/v1/expense/me" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Emma",
    "lastName": "Expense",
    "defaultCurrency": "SEK",
    "language": "sv_SE",
    "IBAN": "SE3550000000054910000003",
    "BIC": "ESSESESS",
    "address": {
      "street1": "Sveavägen 10",
      "postalCode": "111 57",
      "city": "Stockholm",
      "countryCode": "SE"
    },
    "formattingRegion": "sv_SE"
  }'
```

### Updatable fields

| Field | Type | Description |
| - | - | - |
| `firstName` | string | User's first name |
| `lastName` | string | User's last name |
| `defaultCurrency` | string | Preferred currency code (e.g., `SEK`, `USD`, `EUR`) |
| `language` | string | Language/locale code (e.g., `en_US`, `sv_SE`) |
| `bankAccountNumber` | string | Bank account number |
| `IBAN` | string | International bank account number |
| `BIC` | string | Bank identifier code |
| `formattingRegion` | string | Region used for formatting dates and numbers |
| `address` | object | Address with `street1`, `postalCode`, `city`, `countryCode` |

### Action parameter

The `PUT /me` endpoint also supports an `action` query parameter instead of a request body:

| Action | Description |
| - | - |
| `enableSupportAccess` | Grants Findity support temporary access to the user's account |
| `disableSupportAccess` | Revokes support access |

```
PUT /v1/expense/me?action=enableSupportAccess
```

## User settings (fields format)

User settings can also be fetched and updated using the **fields format** — the same dynamic form system used for expenses. This is useful when building a full settings UI.

### Fetch settings

```
GET /v1/expense/me/settings/fields
```

Returns a fields structure with grouped settings:

| Field group | Contents |
| - | - |
| `settings.personalGroup` | Name, email, phone, address, password, receipt email |
| `settings.bankGroup` | Bank account, IBAN, BIC |
| `settings.experienceGroup` | Currency, language, formatting region |
| `settings.appGroup` | App theme, camera defaults, prompt preferences |
| `settings.notificationGroup` | Card transaction notifications, email preferences |
| `temporaryApprovalGroup` | Temporary approver assignments |

### Update settings

```
PUT /v1/expense/me?format=fields
```

Send the full fields structure back with updated values. Fields with `requiresUpdate: true` trigger server-side recalculation — follow the same update loop pattern described in the [Fields system guide](/docs/getting-started-with-expense-api#step-3-the-fields-system).

## Counters

Retrieve badge counts across all organizations the user belongs to. Use this to show notification badges in your UI.

```
GET /v1/expense/me/counters
```

### Example response

```json theme={"system"}
{
  "meta": { "count": 2, "total": 2 },
  "data": [
    {
      "organizationId": "8ab28c4e6f858814016f8a9bd6b20054",
      "expenses": 19,
      "approvals": 0,
      "rejections": 0,
      "notifications": 3,
      "transactions": 0,
      "inbox": 0
    },
    {
      "organizationId": "8ab28c4f6fd37d39016fd6fee0470003",
      "expenses": 10,
      "approvals": 11,
      "rejections": 0,
      "notifications": 0,
      "transactions": 0,
      "inbox": 0
    }
  ]
}
```

### Counter fields

| Field | Description |
| - | - |
| `expenses` | Number of new/draft expenses |
| `approvals` | Number of items pending approval (if user is an approver) |
| `rejections` | Number of recently rejected expenses |
| `notifications` | Number of unread notifications |
| `transactions` | Number of card transactions |
| `inbox` | Number of expenses created via email or card auto-creation |

## Profile image

Manage the user's profile picture.

| Operation | Method | Endpoint |
| - | - | - |
| Get profile image | `GET` | `/v1/expense/me/profileimage` |
| Upload profile image | `PUT` | `/v1/expense/me/profileimage` |
| Delete profile image | `DELETE` | `/v1/expense/me/profileimage` |

When uploading, set the `Content-Type` header to the image format (e.g., `image/jpeg`, `image/png`). The request body should be the binary image data.

## Email and phone management

Email and phone number changes require a two-step verification process to prevent unauthorized changes.

### Change email

1. **Request a verification code:**

```bash bash theme={"system"}
curl -X POST "https://stage-api.findity.com/api/v1/expense/me/email?action=change" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{ "email": "new.email@example.com" }'
```

2. **Verify with the code:**

```bash bash theme={"system"}
curl -X POST "https://stage-api.findity.com/api/v1/expense/me/email?action=verify" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{ "verificationCode": "123456" }'
```

### Change phone number

Follows the same two-step pattern using `/v1/expense/me/phone`:

1. `POST /me/phone?action=change` with `{ "phoneNumber": "+46701234567" }`
2. `POST /me/phone?action=verify` with `{ "verificationCode": "123456" }`

To remove a phone number entirely: `DELETE /v1/expense/me/phone`

> [!NOTE]
>
> Phone numbers must be in international format with a plus-code prefix (e.g., `+46701234567`).

## Change password

```
POST /v1/expense/me/password
```

### Example request

```json theme={"system"}
{
  "oldPassword": "currentPassword123!",
  "newPassword": "newSecurePassword456!"
}
```

A successful response returns new access and refresh tokens, since the password change invalidates the previous session:

```json theme={"system"}
{
  "expiresIn": 3600,
  "accessToken": "eae3a3a225368f247b419e...",
  "refreshToken": "59fec24de9f49918f50a31..."
}
```

> [!WARNING]
>
> Passwords must be at least eight characters long with at least one number, one uppercase letter, and one lowercase letter.

> [!WARNING]
>
> After a successful password change, immediately replace your stored access and refresh tokens with the new ones returned in the response. The old tokens are invalidated and will no longer work.

## Extra recipients

Extra recipients are additional email addresses that receive expense-related notifications alongside the user's primary email.

| Operation | Method | Endpoint |
| - | - | - |
| List extra recipients | `GET` | `/v1/expense/me/extrarecipients` |
| Add extra recipient | `POST` | `/v1/expense/me/extrarecipients?action=add` |
| Verify extra recipient | `POST` | `/v1/expense/me/extrarecipients?action=verify` |
| Delete extra recipient | `DELETE` | `/v1/expense/me/extrarecipients/{extraRecipientId}` |

Each extra recipient has a status of `CONFIRMED` or `NOT_CONFIRMED` — they must complete email verification before receiving notifications.

## Impersonation

Users with impersonation privileges can search for and act as other users. This is used for admin tools, support scenarios, and delegated expense management.

### Search impersonation targets

```
GET /v1/expense/impersonation/users/search
```

| Parameter | Type | Description |
| - | - | - |
| `searchText` | string | Free text search across users |
| `max` | integer | Max records to return |
| `offset` | integer | Pagination offset |
| `sort` | string | Sort with order, e.g., `firstName:desc,lastName:asc` |

### Example response

```json theme={"system"}
{
  "meta": { "total": 2, "count": 2 },
  "data": [
    {
      "personId": "a61fa8a24d9d49f7b75d1874257e044a",
      "firstName": "Emma",
      "lastName": "Expense",
      "email": "emma.expense@example.com",
      "impersonatorType": "PERSON",
      "profilePictureUrl": "https://expense.findity.com/api/v1/expense/profileimage/a61fa..."
    }
  ]
}
```

The `impersonatorType` indicates the scope of impersonation:

| Type | Description |
| - | - |
| `PERSON` | Can act as the person across all their organizations |
| `EMPLOYEE` | Can act as the person within a specific organization only |

> [!WARNING]
>
> Impersonation requires special permissions. Check `meta.capabilities.canImpersonate` on the `/me` response before showing impersonation features in your UI.

## Integration best practices

> **Fetch user context immediately after authentication**
>
> Call `GET /me?include=meta.capabilities` as the first request after obtaining a user-scoped token. This confirms the token is valid, identifies the user, and reveals system-level capabilities like impersonation access.

> **Use counters for badge notifications**
>
> Poll `GET /me/counters` periodically to update badge counts in your UI. This single endpoint returns counts across all organizations, so you don't need to query each organization separately.

> **Cache user profile data**
>
> User profile data (name, currency, language) changes rarely. Fetch it once at login and store it in your session. Only refresh when the user explicitly edits their profile.

> **Respect the fields format for settings**
>
> When building a settings screen, use the fields format (`GET /me/settings/fields`) rather than constructing the UI manually. This ensures your settings UI stays in sync with any new settings Findity adds, and handles the `requiresUpdate` loop for interdependent settings.

> **Handle verification flows gracefully**
>
> Email and phone changes are asynchronous — the user receives a verification code externally. Design your UI to handle the two-step flow: show an input for the verification code after requesting the change, and handle error codes like `EXPIRED_VERIFICATION_CODE` by prompting the user to request a new code.

## Error handling

Common errors when working with user endpoints:

| Status code | Error code | Description |
| - | - | - |
| `400` | `NO_KNOWN_JSON_PROPERTIES` | Failed to parse the JSON body. Check your request payload. |
| `400` | `INVALID_JSON_BODY` | No known or only read-only properties received. Verify you're sending updatable fields. |
| `400` | `EMAIL_IS_BLANK` | Email address is required but was empty. |
| `400` | `INVALID_EMAIL` | The provided email address is not valid. |
| `400` | `EMAIL_ALREADY_IN_USE` | An account already exists with this email address. |
| `400` | `INVALID_PHONE_NUMBER` | The phone number format is invalid. Use international format with plus-code. |
| `400` | `PHONE_NUMBER_ALREADY_USED` | The phone number is already associated with another account. |
| `400` | `INVALID_VERIFICATION_CODE` | The verification code is incorrect. |
| `400` | `EXPIRED_VERIFICATION_CODE` | The verification code has expired. Request a new one. |
| `400` | `PASSWORD_VALIDATION_ERROR` | Password doesn't meet requirements (8+ chars, 1 number, 1 uppercase, 1 lowercase). |
| `400` | `INCORRECT_CURRENT_PASSWORD` | The current password provided doesn't match. |
| `404` | `ORGANIZATION_NOT_FOUND` | Person does not belong to any organization (for counters endpoint). |

## Next steps

> **[Organizations](/docs/organizations)**
>
> Retrieve the user's organizations, permissions, and configuration

> **[Fields system](/docs/getting-started-with-expense-api#step-3-the-fields-system)**
>
> Learn how to render dynamic expense forms using the Fields system

> **[Authentication](/docs/accessing)**
>
> Set up OAuth tokens and user impersonation

> **[Notifications](/reference/list-notifications)**
>
> Manage user notifications via the API
