
# Créer une transaction de connexion

`POST /fapi/v1/transactions` · appelé depuis l'hôte de connexion de l'environnement

Démarre une transaction de connexion pour une application. La page de connexion hébergée, les composants d'interface et les clients
headless appellent cet point de terminaison sur l'hôte de connexion de l'environnement (un sous-domaine `keenauth.app` ou un domaine
personnalisé vérifié). La réponse définit le cookie de transaction `__Host-idp_ltx` (Secure, HttpOnly, SameSite=Lax, valable 15 minutes) ;
les étapes suivantes utilisent ce cookie.

## Requête

En-têtes :

| En-tête | Obligatoire | Description |
| --- | --- | --- |
| `Idp-Client` | oui | Identifiant du SDK, `<package>/<version> (<runtime>/<version>)`. |
| `Origin` | oui | Doit être exactement `https://<hôte de connexion>`. |

Corps :

| Champ | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `client_id` | string | oui | L'identifiant client de l'application. |
| `redirect_uri` | string (URI) | oui | L'une des URI de redirection enregistrées de l'application. |

## Réponse

`201 Created` avec l'objet de transaction :

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

Renvoyez `csrf_token` dans l'en-tête `Idp-Csrf-Token` à chaque requête de cette transaction qui modifie un état.

Lorsque la demande d'autorisation désigne une organisation qui exige une authentification unique, la réponse peut déjà être
`federated_redirect` avec `redirect_url` (une seule connexion : naviguez alors vers cette URL dans la fenêtre de premier niveau) ou contenir
`sso_connections` (plusieurs : envoyez le choix avec l'étape `sso_start`).

## Transactions sans interface liées à DPoP

Une application SPA ou native de première partie, sur sa propre origine, démarre une connexion sans interface avec un en-tête `DPoP` (RFC 9449) : une preuve
signée avec une clé que le client conserve (clé WebCrypto non extractible dans un navigateur, magasin de clés de la plateforme sur un appareil),
avec `htm` `POST` et `htu` `https://<hôte de connexion>/fapi/v1/transactions`. La transaction est alors liée à cette clé
au lieu d'un cookie : aucun cookie n'est défini et l'objet ne contient pas de `csrf_token`. Chaque requête suivante de la transaction envoie
une nouvelle preuve issue de la même clé.

Champs supplémentaires du corps, vérifiés selon les règles du point de terminaison d'autorisation :

| Champ | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `code_challenge` | string | oui | Challenge PKCE S256 (RFC 7636). |
| `code_challenge_method` | string | oui | `S256`. |
| `scope` | string | non | Scopes séparés par des espaces, par exemple `openid profile email offline_access`. |
| `state` | string | non | Renvoyé avec le code d'autorisation. |
| `nonce` | string | non | Le `nonce` du jeton ID. |
| `organization` | string | non | Identifiant ou nom de l'organisation. |
| `ui_locales` | string | non | Langues préférées. |
| `resource` | string (URI) | non | Indicateur de ressource (RFC 8707). |

`Origin` peut être l'hôte de connexion ou l'une des origines web autorisées de l'application ; une application native n'en envoie aucune. Seules les
applications de première partie peuvent en démarrer une. Un champ que le point de terminaison d'autorisation refuse renvoie `422 validation_failed` avec
le code d'erreur OAuth dans `errors[].code`. Lorsque le serveur répond `401 use_dpop_nonce`, signez une nouvelle preuve avec la valeur
de l'en-tête de réponse `DPoP-Nonce` et réessayez. Envoyer ces champs sans en-tête `DPoP` renvoie
`422 validation_failed` (`dpop_required`).

## Changer d'organisation

Une application qui détient un jeton d'accès Front API pour l'utilisateur connecté (voir [Jeton d'accès Front API](/docs/api/fapi/front-api-token))
fait passer l'utilisateur à une autre organisation sans écran. Envoyez le jeton dans `Authorization: DPoP <token>` avec une preuve
de la clé du jeton (avec `ath`), et dans le corps le `flow` `organization_switch`, les champs sans interface liés à DPoP ci-dessus
(`client_id` de l'application du jeton, `redirect_uri`, `code_challenge`, `state`, `nonce`, `scope`) et
`organization`, l'identifiant `org_…` d'une organisation issu de `GET /fapi/v1/me/organizations`.

La demande est traitée immédiatement avec la session du jeton, comme le serait une connexion qui réutilise la session : la
politique de l'organisation (MFA, limite d'inactivité, authentification unique, liste d'IP), le consentement et les documents juridiques en attente. La réponse est `201` avec une
transaction `completed` qui contient `authorization_response` ; échangez son `code` au point de terminaison de jeton avec votre
`code_verifier` et une preuve de la même clé. Les nouveaux jetons portent l'organisation dans `org_id`. Aucune transaction n'est laissée ouverte
et aucun cookie n'est défini.

```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"
}
```

| Statut | Code | Quand |
| --- | --- | --- |
| 401 | `unauthenticated` | Le jeton est invalide, expiré, révoqué ou non lié à une clé, son application n'est pas une application de première partie active, ou sa session a pris fin. |
| 422 | `validation_failed` | `flow` n'est pas `organization_switch`, `client_id` n'est pas l'application du jeton, `organization` n'est pas un identifiant `org_…`, ou l'utilisateur ne peut pas basculer vers elle dans cette application (`not_eligible`). |
| 403 | `authorization_failed` | L'organisation exige un écran que le changement ne peut pas afficher ; `error` est l'erreur OAuth (`login_required`, `interaction_required`, `consent_required`). Démarrez une nouvelle connexion avec `organization` renseigné. |

Un `flow` `organization_switch` sans le jeton renvoie `422 validation_failed` (`access_token_required`).

## Erreurs

| Statut | Code | Quand |
| --- | --- | --- |
| 400 | `malformed_request` | `Idp-Client` est absent ou le corps n'est pas un JSON valide. |
| 403 | `origin_not_allowed` | `Origin` n'est pas l'hôte de connexion. |
| 403 | `headless_not_allowed` | Une application tierce a tenté de démarrer un flux sans interface. |
| 404 | `not_found` | L'hôte n'est l'hôte de connexion d'aucun environnement. |
| 422 | `validation_failed` | `client_id` n'est pas une application de cet environnement, `redirect_uri` n'est pas enregistrée pour elle, ou un champ est invalide. |
| 401 | `invalid_dpop_proof` | La preuve `DPoP` est absente, invalide ou réutilisée, ou l'hôte ne sert pas de transactions liées à DPoP. |
| 401 | `use_dpop_nonce` | Signez une nouvelle preuve avec la valeur de l'en-tête de réponse `DPoP-Nonce`. |
| 503 | `temporarily_unavailable` | Une dépendance est indisponible ; réessayez plus tard. |

## Exemple

```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"}'
```

## Données personnelles

Ce point de terminaison traite des données personnelles : le profil de l'utilisateur final. [Données personnelles dans les API KeenAuth](/docs/policies/personal-data) décrit ce qui est stocké, pendant combien de temps il est conservé et comment il est supprimé. N'envoyez aucune catégorie particulière de données personnelles (santé, données biométriques, religion, opinions politiques, orientation sexuelle) dans aucun champ.
