---
title: "Upsert your user profile (call on first login)"
method: POST
path: "/users/me"
tags: ["Users"]
---

# Upsert your user profile (call on first login)

`POST /users/me`

## Request

- Base URL: `https://api.atako.ai`
- URL: `https://api.atako.ai/users/me`
- Auth: HTTP bearer

May create a company if `company`/`teamSize` are provided. Phone is normalized to E.164 (national trunk 0 dropped) and silently dropped if unparsable — never blocks signup; an existing phone is never overwritten (PATCH changes it).

## Request body

- object
  - `name` string, required
  - `company` string
  - `phone` string
  - `teamSize` 'solo' | 'under_10' | 'under_50' | 'under_150' | 'under_300' | 'over_300'
  - `heardFrom` 'search' | 'ai_assistant' | 'social' | 'word_of_mouth' | 'event' | 'press' | 'ads' | 'other': « How did you hear about us? » — stored on the company created by this call; ignored otherwise.
  - `timezone` string: IANA timezone.
  - `locale` 'fr' | 'en' | 'de' | 'es' | 'it' | 'pt-BR' | 'nl' | 'pl' | 'sv' | 'da' | 'tr' | 'ja' | 'ko': UI locale at signup. Applied on insert only — never overwrites an existing value.
  - `acceptTerms` true: Terms checkbox ticked on /signup. Server stamps `termsAcceptedAt` and the currently published terms version (`termsVersionId`) — first acceptance only, never overwritten.

## Response `200`

- User
  - `id` string, uuid, required
  - `name` string, required
  - `email` string, email, required
  - `phone` string, nullable: E.164.
  - `companyId` string, uuid, nullable
  - `photo` string, nullable
  - `timezone` string, required: IANA timezone.
  - `locale` 'fr' | 'en' | 'de' | 'es' | 'it' | 'pt-BR' | 'nl' | 'pl' | 'sv' | 'da' | 'tr' | 'ja' | 'ko', nullable: Preferred UI/email locale. NULL = no preference recorded yet.
  - `banned` boolean: Not returned by GET /users/me (restricted profile).
  - `onboardingEmailsSent` string[]
  - `lastActiveAt` string, date-time, nullable
  - `npsTriggeredAt` string, date-time, nullable
  - `termsAcceptedAt` string, date-time, nullable: First terms acceptance (signup checkbox).
  - `termsVersionId` string, uuid, nullable: Published terms version in force at acceptance (legal_document_versions).
  - `tourSeenAt` string, date-time, nullable: Guided tour finished or skipped. NULL = the web app opens it at next load (first login).
  - `storeyToursSeen` object: Storey (`dashboard` | `projects` | `floor` | `context`) → when its guided tour was finished or skipped. A missing storey = its tour opens the first time the user reaches it.
  - `createdAt` string, date-time
  - `updatedAt` string, date-time

## Other responses

- `400`: Validation error.
- `401`: Unauthorized, or ACCOUNT_NOT_FOUND — no `users` row and the Supabase Auth identity no longer exists (deleted account): nothing is recreated.
- `409`: PHONE_EXISTS — account/company still created, just without the phone.
- `503`: AUTH_UNAVAILABLE — no `users` row and Supabase Auth could not confirm the identity.

---

[API](https://skmtc.dev/atako/apis/atako-api.md) · [All operations](https://skmtc.dev/atako/apis/atako-api/llms.txt) · [OpenAPI document](https://skmtc.dev/atako/apis/atako-api/revisions/aa555c56fbd7?raw)
