# Sessions

> Référence de l’API sessions nouko : créer une session, lire son statut, lister avec pagination, annuler et obtenir la décision motivée.

Source : https://nouko.openclouds.fr/docs/sessions

Une **session** représente une vérification d’identité pour un client. Elle possède un lien de collecte à usage unique et un cycle de vie.

## Créer une session

```
POST /v1/sessions
```

En-tête `Idempotency-Key` **obligatoire** (120 caractères max.). Renvoyer la même clé renvoie la même session (`200`, `replayed: true`) avec un nouveau lien si l’ancien n’a pas été utilisé.

| Champ | Type | Requis | Description |
| --- | --- | --- | --- |
| `reference` | string | Oui | Votre identifiant, 1 à 120 caractères |
| `country` | string | Oui | `BJ` ou `CI` |
| `tier` | integer | Non | Palier visé, 1 à 3 (défaut 2) |
| `redirect_url` | string | Non | URL HTTPS de retour, 500 caractères max. |
| `metadata` | object | Non | 20 clés max., renvoyées dans chaque événement. Aucune donnée personnelle. |
| `expires_in` | integer | Non | Durée de validité en secondes, 900 à 2 592 000 (défaut 604 800, soit 7 jours) |

**curl**

```bash
curl -X POST https://nouko.openclouds.fr/api/public/v1/sessions \
  -H "Authorization: Bearer $NOUKO_API_KEY" \
  -H "Idempotency-Key: commande-001" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "client-42",
    "country": "BJ",
    "tier": 2,
    "redirect_url": "https://votre-site.com/kyc/retour",
    "metadata": { "user_id": "42" }
  }'
```

**Node.js**

```javascript
// Node.js 18+ (fetch intégré)
const res = await fetch('https://nouko.openclouds.fr/api/public/v1/sessions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.NOUKO_API_KEY}`,
    'Idempotency-Key': 'commande-001',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    reference: 'client-42',
    country: 'BJ',
    tier: 2,
    redirect_url: 'https://votre-site.com/kyc/retour',
    metadata: { user_id: '42' },
  }),
});
const session = await res.json();
if (!res.ok) throw new Error(`${session.error.code}: ${session.error.message}`);
// Redirigez votre client vers session.verification_url
console.log(session.id, session.verification_url);
```

**Python**

```python
# pip install requests
import os, requests

res = requests.post(
    "https://nouko.openclouds.fr/api/public/v1/sessions",
    headers={
        "Authorization": f"Bearer {os.environ['NOUKO_API_KEY']}",
        "Idempotency-Key": "commande-001",
    },
    json={
        "reference": "client-42",
        "country": "BJ",
        "tier": 2,
        "redirect_url": "https://votre-site.com/kyc/retour",
        "metadata": {"user_id": "42"},
    },
    timeout=15,
)
session = res.json()
if not res.ok:
    raise RuntimeError(f"{session['error']['code']}: {session['error']['message']}")
print(session["id"], session["verification_url"])
```

**PHP**

```php
<?php
$ch = curl_init('https://nouko.openclouds.fr/api/public/v1/sessions');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('NOUKO_API_KEY'),
        'Idempotency-Key: commande-001',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'reference' => 'client-42',
        'country' => 'BJ',
        'tier' => 2,
        'redirect_url' => 'https://votre-site.com/kyc/retour',
        'metadata' => ['user_id' => '42'],
    ]),
]);
$session = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) throw new RuntimeException($session['error']['code']);
echo $session['id'], ' ', $session['verification_url'];
```

**Go**

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"reference":    "client-42",
		"country":      "BJ",
		"tier":         2,
		"redirect_url": "https://votre-site.com/kyc/retour",
		"metadata":     map[string]string{"user_id": "42"},
	})
	req, _ := http.NewRequest("POST", "https://nouko.openclouds.fr/api/public/v1/sessions", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+os.Getenv("NOUKO_API_KEY"))
	req.Header.Set("Idempotency-Key", "commande-001")
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var s struct {
		ID              string `json:"id"`
		VerificationURL string `json:"verification_url"`
	}
	json.NewDecoder(res.Body).Decode(&s)
	fmt.Println(res.StatusCode, s.ID, s.VerificationURL)
}
```

**Java**

```java
// Java 15+
import java.net.URI;
import java.net.http.*;

public class CreateSession {
  public static void main(String[] args) throws Exception {
    String body = """
      {"reference":"client-42","country":"BJ","tier":2,
       "redirect_url":"https://votre-site.com/kyc/retour",
       "metadata":{"user_id":"42"}}""";
    HttpRequest req = HttpRequest.newBuilder(URI.create("https://nouko.openclouds.fr/api/public/v1/sessions"))
        .header("Authorization", "Bearer " + System.getenv("NOUKO_API_KEY"))
        .header("Idempotency-Key", "commande-001")
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(body))
        .build();
    HttpResponse<String> res = HttpClient.newHttpClient()
        .send(req, HttpResponse.BodyHandlers.ofString());
    System.out.println(res.statusCode() + " " + res.body());
  }
}
```

**Ruby**

```ruby
require "net/http"
require "json"

uri = URI("https://nouko.openclouds.fr/api/public/v1/sessions")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("NOUKO_API_KEY")}"
req["Idempotency-Key"] = "commande-001"
req["Content-Type"] = "application/json"
req.body = {
  reference: "client-42", country: "BJ", tier: 2,
  redirect_url: "https://votre-site.com/kyc/retour",
  metadata: { user_id: "42" }
}.to_json

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
session = JSON.parse(res.body)
puts session["id"], session["verification_url"]
```

**Réponse 201**

```
HTTP/1.1 201 Created
X-Request-Id: 3b1e…

{
  "id": "6f1c2a9e-…",
  "reference": "client-42",
  "status": "draft",
  "country": "BJ",
  "tier": 2,
  "environment": "sandbox",
  "redirect_url": "https://votre-site.com/kyc/retour",
  "metadata": { "user_id": "42" },
  "expires_at": "2026-10-24T18:00:00.000Z",
  "verification_url": "https://nouko.openclouds.fr/verify/…",
  "replayed": false,
  "sandbox_scenario": null
}
```

## Lire une session

```
GET /v1/sessions/{id}
```

**curl**

```bash
curl https://nouko.openclouds.fr/api/public/v1/sessions/6f1c2a9e-… \
  -H "Authorization: Bearer $NOUKO_API_KEY"
```

**Node.js**

```javascript
const res = await fetch(`https://nouko.openclouds.fr/api/public/v1/sessions/${id}`, {
  headers: { Authorization: `Bearer ${process.env.NOUKO_API_KEY}` },
});
const session = await res.json(); // session.status
```

**Python**

```python
res = requests.get(f"https://nouko.openclouds.fr/api/public/v1/sessions/{session_id}",
    headers={"Authorization": f"Bearer {os.environ['NOUKO_API_KEY']}"}, timeout=15)
print(res.json()["status"])
```

**PHP**

```php
$ch = curl_init("https://nouko.openclouds.fr/api/public/v1/sessions/" . urlencode($id));
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NOUKO_API_KEY')]]);
$session = json_decode(curl_exec($ch), true);
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://nouko.openclouds.fr/api/public/v1/sessions/"+id, nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("NOUKO_API_KEY"))
res, err := http.DefaultClient.Do(req)
```

**Java**

```java
HttpRequest req = HttpRequest.newBuilder(URI.create("https://nouko.openclouds.fr/api/public/v1/sessions/" + id))
    .header("Authorization", "Bearer " + System.getenv("NOUKO_API_KEY")).GET().build();
```

**Ruby**

```ruby
uri = URI("https://nouko.openclouds.fr/api/public/v1/sessions/#{id}")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("NOUKO_API_KEY")}"
```

## Lister les sessions

```
GET /v1/sessions?status=&reference=&limit=&cursor=
```

| Paramètre | Description |
| --- | --- |
| `status` | Filtre sur un statut |
| `reference` | Filtre sur votre référence |
| `limit` | 1 à 100 (défaut 20) |
| `cursor` | Valeur `next_cursor` de la page précédente |

La réponse contient `data` (les sessions, les plus récentes d’abord), `has_more` et `next_cursor`.

**curl**

```bash
# Nécessite jq
cursor=""
while :; do
  page=$(curl -s "https://nouko.openclouds.fr/api/public/v1/sessions?limit=100&status=pending${cursor:+&cursor=$cursor}" \
    -H "Authorization: Bearer $NOUKO_API_KEY")
  echo "$page" | jq -r '.data[] | "\(.id) \(.reference)"'
  [ "$(echo "$page" | jq -r '.has_more')" = "true" ] || break
  cursor=$(echo "$page" | jq -r '.next_cursor')
done
```

**Node.js**

```javascript
async function* allSessions(params = {}) {
  let cursor;
  do {
    const qs = new URLSearchParams({ limit: '100', ...params, ...(cursor ? { cursor } : {}) });
    const res = await fetch(`https://nouko.openclouds.fr/api/public/v1/sessions?${qs}`, {
      headers: { Authorization: `Bearer ${process.env.NOUKO_API_KEY}` },
    });
    const page = await res.json();
    yield* page.data;
    cursor = page.has_more ? page.next_cursor : null;
  } while (cursor);
}

for await (const s of allSessions({ status: 'pending' })) console.log(s.id, s.reference);
```

**Python**

```python
def all_sessions(**params):
    cursor = None
    while True:
        q = {"limit": 100, **params, **({"cursor": cursor} if cursor else {})}
        page = requests.get("https://nouko.openclouds.fr/api/public/v1/sessions", params=q,
            headers={"Authorization": f"Bearer {os.environ['NOUKO_API_KEY']}"}, timeout=15).json()
        yield from page["data"]
        if not page["has_more"]:
            break
        cursor = page["next_cursor"]

for s in all_sessions(status="pending"):
    print(s["id"], s["reference"])
```

**PHP**

```php
$cursor = null;
do {
    $q = http_build_query(array_filter(['limit' => 100, 'status' => 'pending', 'cursor' => $cursor]));
    $ch = curl_init("https://nouko.openclouds.fr/api/public/v1/sessions?$q");
    curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NOUKO_API_KEY')]]);
    $page = json_decode(curl_exec($ch), true);
    foreach ($page['data'] as $s) echo $s['id'], PHP_EOL;
    $cursor = $page['has_more'] ? $page['next_cursor'] : null;
} while ($cursor);
```

## Obtenir la décision

```
GET /v1/sessions/{id}/decision
```

Renvoie la décision, son motif et les contrôles effectués, sans aucune image. `final` vaut `true` quand la décision est définitive ; `decision` vaut `approved`, `declined` ou `null`.

**curl**

```bash
curl https://nouko.openclouds.fr/api/public/v1/sessions/6f1c2a9e-…/decision \
  -H "Authorization: Bearer $NOUKO_API_KEY"
```

**Node.js**

```javascript
const res = await fetch(`https://nouko.openclouds.fr/api/public/v1/sessions/${id}/decision`, {
  headers: { Authorization: `Bearer ${process.env.NOUKO_API_KEY}` },
});
const { final, decision, reason } = await res.json();
if (final) console.log(decision, reason); // "approved" | "declined"
```

**Python**

```python
d = requests.get(f"https://nouko.openclouds.fr/api/public/v1/sessions/{session_id}/decision",
    headers={"Authorization": f"Bearer {os.environ['NOUKO_API_KEY']}"}, timeout=15).json()
if d["final"]:
    print(d["decision"], d["reason"])
```

**Réponse 200**

```
{
  "id": "6f1c2a9e-…",
  "reference": "client-42",
  "status": "approved",
  "final": true,
  "decision": "approved",
  "tier": 2,
  "achieved_tier": 2,
  "reason": "Pièce lisible, visage concordant",
  "decided_at": "2026-10-17T18:12:00.000Z",
  "checks": [
    { "check_type": "document", "provider": "agent", "outcome": "pass", "risk": "low", "created_at": "…" }
  ]
}
```

## Annuler une session

```
POST /v1/sessions/{id}/cancel
```

Possible uniquement pour une session `draft` ou `complement` ; le lien est désactivé. Une session déjà annulée renvoie `200`. Sinon : `409 not_cancellable`.

**curl**

```bash
curl -X POST https://nouko.openclouds.fr/api/public/v1/sessions/6f1c2a9e-…/cancel \
  -H "Authorization: Bearer $NOUKO_API_KEY"
```

**Node.js**

```javascript
await fetch(`https://nouko.openclouds.fr/api/public/v1/sessions/${id}/cancel`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.NOUKO_API_KEY}` },
});
```

**Python**

```python
requests.post(f"https://nouko.openclouds.fr/api/public/v1/sessions/{session_id}/cancel",
    headers={"Authorization": f"Bearer {os.environ['NOUKO_API_KEY']}"}, timeout=15)
```

## Statuts

| Statut | Signification | Définitif |
| --- | --- | --- |
| `draft` | Créée, en attente du client | Non |
| `pending` | Pièces soumises, examen en cours (automatique puis humain si besoin) | Non |
| `complement` | Pièces à refaire : le client peut reprendre son lien | Non |
| `approved` | Identité vérifiée | Oui |
| `rejected` | Vérification refusée (événement `session.declined`) | Oui |
| `expired` | Lien expiré avant soumission | Oui |
| `cancelled` | Annulée par vous | Oui |
