---
title: "Account profile — API v1 and MCP | Nodea Documentation"
description: "Full reference for the account profile: REST API v1 endpoints (GET/PATCH /profile — details, address, VAT with VIES re-validation), account:read/write scope, MCP tools profile-get/profile-update and curl / JSON-RPC examples."
url: "https://nodea.io/en/api-docs/profile"
lang: "en"
updated: "2026-07-16"
---

# Account profile — API v1 and MCP | Nodea Documentation

> Account profile reference — API v1 endpoints (GET/PATCH /profile: details, company, address, VAT with VIES re-validation, timezone), account:read/write scopes, MCP tools profile-get/profile-update and examples.

The **Account profile** module covers reading and partially updating the token user's profile: identity, company and billing address, the VAT profile (country, company status, VAT number with VIES validation), tax number and timezone. This document describes the REST API v1 layer (/api/v1/profile) and the MCP tools profile-get and profile-update.

## Authentication and scopes

API v1 authenticates with a Passport personal access token via the Authorization: Bearer \<token\> header. The profile operates **exclusively on the token user** — it accepts no ids, so there is no cross-tenant surface.

- account:read — read the profile (GET /profile).
- account:write — partial update (PATCH /profile).

The profile is **not gated by any Pennant flag** — account access is never feature-flagged (same as the web UI). Rate limit: throttle:api-v1 = 240/min per token user.

## Deliberately omitted (web-only)

Sensitive, confirmation-bound operations stay in the web UI and are **not** exposed over API v1:

- **email** change,
- **password** change,
- **2FA** (two-factor) management,
- **OAuth** provider disconnect,
- **avatar** upload,
- **billing currency** change (locked in the web by the zero-wallet-balance guard).

The email field is returned on read, but PATCH /profile has no rule for it — submitting it is ignored (the field is not part of validated()). By contrast the MCP profile-update tool **explicitly rejects** email and password with a validation error (see the MCP section) and — unlike the API — can change the currency.

## Profile and the personal account (billing details)

The profile stays the **user's profile** — its endpoints do not depend on the account context (the X-Account-Id header does not change them) and need no account permission. The invoice buyer, however, is the **customer account** (see [Customer accounts](https://nodea.io/en/api-docs/accounts)), so after every PATCH /profile save the billing fields that changed (name, company, address, country, company status, VAT number / tax number and the VIES validation result) are copied onto the user's **personal account** — the account they own that is billed by Nodea (the default account first, otherwise the oldest one). Only fields changed in that save are copied, so e.g. changing the timezone never overwrites data edited on the account profile.

The sync **never** writes an account imported from WHMCS (WHMCS is its source of truth) or an account the user is only a member of. The target place to edit invoice details is the account profile in the panel.

## API v1 — endpoints

MethodPathScopeDescriptionGET/api/v1/profileaccount:readThe token user's profile (details, company, address, VAT, timezone, locale language, currency). PATCH/api/v1/profileaccount:writePartial update of any subset of general and VAT fields. Returns the updated profile.

### GET /profile — response

Returns a ProfileResource — fields: id, name, email, company\_name, address, country, is\_company (bool), vat\_number, vat\_validated (bool), vat\_validated\_at (ISO 8601 or null), tax\_number, timezone, locale (pl|en or null — the user's preferred language: web UI and every e-mail / notification sent to them, including ones triggered by another person or through the API), currency (PLN|EUR), created\_at. It **never** returns secrets, 2FA state, password/remember hashes, OAuth provider rows or admin flags.

### PATCH /profile — body parameters

Every rule carries sometimes — a client may send any subset of fields; omitted fields are left untouched (partial update).

- name (string, sometimes|required, max 255) — a submitted name may not be blank.
- company\_name (string|null, max 255).
- address (string|null, max 1000).
- tax\_number (string|null, max 64).
- timezone (string|null, timezone rule — IANA identifier).
- locale (string|null, in:pl,en — preferred language of the web UI and of e-mails / notifications; null clears the preference).
- country (string|null, size:2 — ISO 3166-1 alpha-2).
- is\_company (boolean).
- vat\_number (string|null, max 32).

**Currency is not accepted by the API** — changing currency is web-only (and available in MCP). General fields (name, company\_name, address, tax\_number, timezone, locale) are persisted only when present in the payload.

### VAT profile and VIES re-validation

Submitting **any** of country / is\_company / vat\_number invalidates the previous VIES validation: vat\_validated is set to false and vat\_validated\_at to null. Then, when the effective profile (submitted values merged over stored ones) is a company (is\_company = true) with both a country and a VAT number set, a round-trip to **VIES** (ViesValidator) is performed. The country and VAT number are normalised (upper-cased, trimmed). Like the web flow, a failed VIES lookup **still persists** the submitted values — vat\_validated simply stays false (no reverse-charge branch). VIES uses SOAP (not the Http facade) and is sandboxed by default outside production (billing.vies.sandbox).

### Error codes

- 401 — missing/invalid token.
- 403 — missing required scope (account:read for GET, account:write for PATCH).
- 422 — validation (e.g. blank name, country not size:2, invalid timezone, locale other than pl/en).

## Examples — API

### Read the profile

curl -s https://app.nodea.io/api/v1/profile \\ -H "Authorization: Bearer $NODEA\_TOKEN" \\ -H "Accept: application/json"

### Update general details and timezone

curl -s -X PATCH https://app.nodea.io/api/v1/profile \\ -H "Authorization: Bearer $NODEA\_TOKEN" \\ -H "Content-Type: application/json" \\ -d '{ "name": "John Smith", "company\_name": "Smith Ltd.", "address": "1 Example St, London", "timezone": "Europe/London" }'

### Set the VAT profile (triggers VIES re-validation)

curl -s -X PATCH https://app.nodea.io/api/v1/profile \\ -H "Authorization: Bearer $NODEA\_TOKEN" \\ -H "Content-Type: application/json" \\ -d '{ "is\_company": true, "country": "PL", "vat\_number": "PL1234567890" }'

## MCP — tools

MCP tools share the auth and scope model with API v1 (Bearer + scope checked in ensureScope). The profile is ungated, so both tools are always present in tools/list. They operate on the token user only.

ToolScopeTypeDescriptionprofile-getaccount:readread-onlyReturns the profile: name, email, company\_name, address, country, company/VAT status, tax\_number, timezone, preferred language (locale: pl|en or null) and currency. No credentials or 2FA state. profile-updateaccount:writeidempotentPartial update; only provided fields change. Additionally supports currency. Rejects email/password. Copies changed billing fields onto the personal account (as the API does).

### profile-get

No parameters. The response is a superset of ProfileResource — the same fields plus effective\_timezone (the user's resolved timezone when timezone is null).

### profile-update — parameters

All optional; only provided fields change. Empty strings / null clear the field (blank → null), country and VAT number are upper-cased.

- name (string, max 255).
- company\_name (string, max 255; null/empty clears).
- address (string, max 1000; null/empty clears).
- tax\_number (string, max 64; null/empty clears).
- timezone (string — IANA identifier; null clears).
- locale (enum pl|en — preferred language of the web UI and of e-mails / notifications; null clears).
- country (string — ISO 3166-1 alpha-2; null clears).
- is\_company (boolean).
- vat\_number (string, max 32; null/empty clears).
- currency (enum PLN|EUR) — **locked** while any wallet holds a non-zero balance (returns a tool error); the change is written to the activity log.

**Differences from the API:** changing a VAT field (country/is\_company/vat\_number) to a value different from the stored one resets vat\_validated (and vat\_validated\_at), but this tool **does not** perform the VIES round-trip — re-validation is done in the web UI. Submitting email or password fails validation (the prohibited rule: "The email address / password can only be changed in the web UI.") rather than being silently ignored as in the API.

### Example — MCP (JSON-RPC)

{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "profile-get", "arguments": {} } } { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "profile-update", "arguments": { "is\_company": true, "country": "PL", "vat\_number": "PL1234567890", "timezone": "Europe/Warsaw" } } }

## Related

- [Support tickets — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/tickets)
- [Billing — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/billing)
- [Customer accounts — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/accounts)

---

Languages: [pl](https://nodea.pl/pl/dokumentacja-api/profile.md)  
Site index: [llms.txt](https://nodea.io/en/llms.txt)
