
# Crea una transazione di accesso

`POST /fapi/v1/transactions` · chiamato dall'host di accesso dell'ambiente

Avvia una transazione di accesso per un'applicazione. La pagina di accesso ospitata, i componenti UI e i client headless chiamano questo
endpoint sull'host di accesso dell'ambiente (un sottodominio `keenauth.app` o un dominio personalizzato verificato). La risposta imposta il
cookie di transazione `__Host-idp_ltx` (Secure, HttpOnly, SameSite=Lax, valido per 15 minuti); i passaggi successivi usano quel cookie.

## Richiesta

Intestazioni:

| Intestazione | Obbligatoria | Descrizione |
| --- | --- | --- |
| `Idp-Client` | sì | Identificatore dell'SDK, `<package>/<version> (<runtime>/<version>)`. |
| `Origin` | sì | Deve essere esattamente `https://<sign-in host>`. |

Corpo:

| Campo | Tipo | Obbligatorio | Descrizione |
| --- | --- | --- | --- |
| `client_id` | string | sì | L'ID client dell'applicazione. |
| `redirect_uri` | string (URI) | sì | Uno dei redirect URI registrati dell'applicazione. |

## Risposta

`201 Created` con l'oggetto transazione:

```json
{
  "schema_version": 1,
  "state": "identifier_pending",
  "next_steps": [{ "step": "identifier", "fields": ["identifier"] }],
  "csrf_token": "<token>",
  "expires_at": "2026-10-05T10:15:00Z"
}
```

Invia `csrf_token` di nuovo nell'intestazione `Idp-Csrf-Token` in ogni richiesta di questa transazione che modifica lo stato.

Quando la richiesta di autorizzazione indica un'organizzazione che richiede SSO, la risposta può essere già
`federated_redirect` con `redirect_url` (una sola connessione: naviga la finestra di primo livello verso quell'URL) oppure contenere
`sso_connections` (più connessioni: invia la scelta con il passaggio `sso_start`).

## Transazioni headless vincolate a DPoP

Una SPA o un'app nativa di prima parte sulla propria origine avvia un accesso headless con un'intestazione `DPoP` (RFC 9449): una prova
firmata con una chiave che il client conserva (chiave WebCrypto non estraibile in un browser, key store della piattaforma su un dispositivo),
con `htm` `POST` e `htu` `https://<sign-in host>/fapi/v1/transactions`. La transazione è quindi vincolata a quella chiave
anziché a un cookie: non viene impostato alcun cookie e l'oggetto non ha `csrf_token`. Ogni richiesta successiva della transazione invia
una nuova prova dalla stessa chiave.

Campi aggiuntivi nel corpo, verificati con le regole dell'endpoint di autorizzazione:

| Campo | Tipo | Obbligatorio | Descrizione |
| --- | --- | --- | --- |
| `code_challenge` | string | sì | Challenge PKCE S256 (RFC 7636). |
| `code_challenge_method` | string | sì | `S256`. |
| `scope` | string | no | Scope separati da spazi, per esempio `openid profile email offline_access`. |
| `state` | string | no | Restituito insieme al codice di autorizzazione. |
| `nonce` | string | no | Il `nonce` dell'ID token. |
| `organization` | string | no | ID o nome dell'organizzazione. |
| `ui_locales` | string | no | Lingue preferite. |
| `resource` | string (URI) | no | Indicatore di risorsa (RFC 8707). |

`Origin` può essere l'host di accesso o una delle origini web consentite dell'applicazione; un'app nativa non invia alcuna origine. Solo
le applicazioni di prima parte possono avviarne una. Un campo che l'endpoint di autorizzazione rifiuta restituisce `422 validation_failed` con
il codice di errore OAuth in `errors[].code`. Quando il server risponde `401 use_dpop_nonce`, firma una nuova prova con il valore
dell'intestazione di risposta `DPoP-Nonce` e riprova. Inviare questi campi senza un'intestazione `DPoP` restituisce
`422 validation_failed` (`dpop_required`).

## Cambio di organizzazione

Un'applicazione che possiede un access token della front API per l'utente connesso (vedi [Access token della front API](/docs/api/fapi/front-api-token))
può spostare l'utente su un'altra organizzazione senza mostrare schermate. Invia il token in `Authorization: DPoP <token>` con una prova
della chiave del token (con `ath`), e nel corpo il `flow` `organization_switch`, i campi headless vincolati a DPoP descritti sopra
(`client_id` dell'applicazione del token, `redirect_uri`, `code_challenge`, `state`, `nonce`, `scope`) e
`organization`, cioè l'ID `org_…` di un'organizzazione da `GET /fapi/v1/me/organizations`.

La richiesta viene decisa subito con la sessione del token, come avverrebbe per un accesso che riutilizza la sessione: si applicano la
policy dell'organizzazione (MFA, limite di inattività, SSO, lista IP), il consenso e i documenti legali in attesa. La risposta è `201` con una
transazione `completed` che contiene `authorization_response`; scambia il suo `code` all'endpoint dei token con il tuo
`code_verifier` e una prova della stessa chiave. I nuovi token riportano l'organizzazione in `org_id`. Nessuna transazione resta aperta
e non viene impostato alcun cookie.

```json
{
  "schema_version": 1,
  "state": "completed",
  "next_steps": [],
  "authorization_response": { "code": "<code>", "state": "<state>", "iss": "https://example.keenauth.app" },
  "expires_at": "2026-10-05T10:15:00Z"
}
```

| Stato | Codice | Quando |
| --- | --- | --- |
| 401 | `unauthenticated` | Il token non è valido, è scaduto, è stato revocato o non è vincolato a una chiave, la sua applicazione non è un'applicazione di prima parte attiva, oppure la sua sessione è terminata. |
| 422 | `validation_failed` | `flow` non è `organization_switch`, `client_id` non è l'applicazione del token, `organization` non è un ID `org_…`, oppure l'utente non può passare a quell'organizzazione in questa applicazione (`not_eligible`). |
| 403 | `authorization_failed` | L'organizzazione richiede una schermata che il cambio non può mostrare; `error` è l'errore OAuth (`login_required`, `interaction_required`, `consent_required`). Avvia un nuovo accesso con `organization` impostato. |

`flow` `organization_switch` senza il token restituisce `422 validation_failed` (`access_token_required`).

## Errori

| Stato | Codice | Quando |
| --- | --- | --- |
| 400 | `malformed_request` | `Idp-Client` manca oppure il corpo non è JSON valido. |
| 403 | `origin_not_allowed` | `Origin` non è l'host di accesso. |
| 403 | `headless_not_allowed` | Un'applicazione di terze parti ha provato ad avviare un flusso headless. |
| 404 | `not_found` | L'host non è un host di accesso di alcun ambiente. |
| 422 | `validation_failed` | `client_id` non è un'applicazione di questo ambiente, `redirect_uri` non è registrato per essa, oppure un campo non è valido. |
| 401 | `invalid_dpop_proof` | La prova `DPoP` manca, non è valida o è stata riutilizzata, oppure l'host non gestisce transazioni vincolate a DPoP. |
| 401 | `use_dpop_nonce` | Firma una nuova prova con il valore dell'intestazione di risposta `DPoP-Nonce`. |
| 503 | `temporarily_unavailable` | Una dipendenza non è disponibile; riprova più tardi. |

## Esempio

```bash
curl -X POST https://example.keenauth.app/fapi/v1/transactions \
  -H "Idp-Client: my-app/1.0.0 (node/22.0.0)" \
  -H "Origin: https://example.keenauth.app" \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"client_id":"<client_id>","redirect_uri":"https://app.example.com/callback"}'
```

## Dati personali

Questo endpoint tratta dati personali: il profilo dell'utente finale. [Dati personali nelle API di KeenAuth](/docs/policies/personal-data) descrive cosa viene memorizzato, per quanto tempo viene conservato e come viene cancellato. Non inviare in alcun campo categorie particolari di dati personali (salute, dati biometrici, religione, opinioni politiche, orientamento sessuale).
