---
title: "Write Profile"
method: PUT
path: "/api/autopilot/profile"
tags: ["autopilot"]
---

# Write Profile

`PUT /api/autopilot/profile`

## Request

- Base URL: `https://api.taxcompass.it` (the document declares no server; this is the origin it was published from)
- URL: `https://api.taxcompass.it/api/autopilot/profile`
- Auth: HTTP bearer

Apply a partial update; ``None`` fields are left alone (see the store).

The onboarding event (AP-084b) fires on the *transition* into a complete
profile, which is why the stored row is read first: the wizard sends one step
at a time and keeps sending them after the last required field lands, so
"``onboarding_complete`` is true on the way out" would re-fire on every later
edit and turn one onboarding into five.

## Request body

- FiscalProfileUpdate: ``PUT /api/autopilot/profile`` body — a partial update. Every field is optional and ``None`` means "leave it alone", so the client can send one step of the onboarding wizard at a time. ``onboarding_complete`` is deliberately absent: the store derives it from whether the fields the first invoice needs are present, rather than trusting the client to say so. ``forfettario_rate`` and ``coefficient`` are absent for the same reason and a sharper one. The coefficient is statutory — fixed by the ATECO division under L. 190/2014 Allegato 4 — and the rate has conditions no field here records, so both are computed by the store on every write (``derived_regime_params``). They used to be accepted here and were the *only* way to set them, which is how every real profile ended up with neither and the year's payment calendar refused to compute. A body that still sends them is ignored rather than refused: they were never anyone's to send, so a 422 would punish a client for a field this API should not have offered.
  - `regime` 'forfettario' | 'ordinario', nullable
  - `business_type` 'professionista' | 'artigiano' | 'commerciante', nullable
  - `inps_fund` 'gestione_separata' | 'artigiani' | 'commercianti', nullable
  - `primary_ateco` string, nullable
  - `activity_start_date` string, date, nullable
  - `startup_rate_eligible` boolean, nullable
  - `invoice_prefix` string, nullable
  - `codice_fiscale` string, nullable
  - `partita_iva` string, nullable
  - `full_legal_name` string, nullable
  - `address` string, nullable
  - `pec` string, nullable
  - `iban` string, nullable

## Response `200`

Successful Response

- FiscalProfile: The stored profile as the API returns it (``GET /api/autopilot/profile``). Carries the two derived numbers the write model does not: they are part of the tax position the user is entitled to see — and to check against their commercialista — even though nothing they send can set them. The bounds are the engine's own: a rate or coefficient outside 0–1 is not a rate.
  - `regime` 'forfettario' | 'ordinario', nullable
  - `business_type` 'professionista' | 'artigiano' | 'commerciante', nullable
  - `inps_fund` 'gestione_separata' | 'artigiani' | 'commercianti', nullable
  - `primary_ateco` string, nullable
  - `activity_start_date` string, date, nullable
  - `startup_rate_eligible` boolean, nullable
  - `invoice_prefix` string, nullable
  - `codice_fiscale` string, nullable
  - `partita_iva` string, nullable
  - `full_legal_name` string, nullable
  - `address` string, nullable
  - `pec` string, nullable
  - `iban` string, nullable
  - `forfettario_rate` string, nullable
  - `coefficient` string, nullable
  - `onboarding_complete` boolean
  - `updated_at` string, date-time, nullable

## Other responses

- `422`: Validation Error

---

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