Référence

SDKs officiels

Surface API miroir sur les 3 langages : PHP (stable), Node.js (alpha), Python (alpha). Tous incluent OAuth2 auto-refresh, idempotency intégrée, retry exponentiel sur 5xx, et un hook onRetry pour l'observabilité.

Distribution : via le dépôt pour l'instant (publication Packagist / npm / PyPI planifiée). L'API REST reste intégrable sans SDK — cf. Démarrage rapide.

Surface commune

MéthodeEndpoint
obtainAccessTokenPOST /oauth/token
openSmsSession / openTotpSessionPOST /v1/sessions
validateOtpPOST /v1/sessions/{id}/validate
recordActionPOST /v1/sessions/{id}/actions
attachArtifactPOST /v1/sessions/{id}/artifacts
closeSessionPOST /v1/sessions/{id}/close
getProof / downloadProofBundlePOST /v1/proofs
getTimelineGET /v1/timelines
setOnRetry(hook observabilité)

PHP — composer require session-proof/php-sdk

use SessionProof\Client;

$client = new Client(
    baseUrl: 'https://proof.example',
    organisationId: '01900000-...',
);
$client->obtainAccessToken('sp_secret');                       // auto-refresh à 60s avant expiry

$session = $client->openSession('sms', 'user-1', [
    'phone_number'        => '+33600000001',
    'beneficiary_email'   => 'alice@example.com',
    'planned_operations'  => [
        ['op_id' => 'ctr-001', 'type' => 'INTENT_DECLARED', 'payload_hash' => '...'],
    ],
]);

$client->validate($session['session_id'], '000000');
$client->recordAction($session['session_id'], 'INTENT_DECLARED', '...', ['op_id' => 'ctr-001']);
$client->close($session['session_id']);

$bundle = $client->getProof($session['session_id']);            // ou attendre bundle.generated (anchor_coverage: complete)
file_put_contents('bundle.asice', $bundle);

Node.js — bun add @session-proof/sdk

import { Client } from '@session-proof/sdk';

const client = new Client({
  baseUrl: 'https://proof.example',
  organisationId: '01900000-...',
});
await client.obtainAccessToken('sp_secret');

const session = await client.openSmsSession({
  actorUserId: 'user-1',
  phoneNumber: '+33600000001',
  idempotencyKey: 'open-42',
});

await client.validateOtp(session.session_id, '000000');
await client.recordAction(session.session_id, 'VALIDATION_ACTION', '...', { op_id: 'ctr-001' });
await client.closeSession(session.session_id);

const bundle = await client.downloadProofBundle(session.session_id);
await Bun.write('bundle.asice', bundle);

// Observabilité retry
client.setOnRetry((attempt, status, delayMs) => {
  console.warn(`retry attempt=${attempt} status=${status} delay=${delayMs}ms`);
});

Python — pip install session-proof-sdk

from session_proof import Client

client = Client(
    base_url="https://proof.example",
    organisation_id="01900000-...",
)
client.obtain_access_token("sp_secret")

session = client.open_sms_session(
    actor_user_id="user-1",
    phone_number="+33600000001",
    beneficiary_email="alice@example.com",
    idempotency_key="open-42",
)

client.validate_otp(session["session_id"], "000000")
client.record_action(session["session_id"], "VALIDATION_ACTION", "...", metadata={"op_id": "ctr-001"})
client.close_session(session["session_id"])

bundle: bytes = client.download_proof_bundle(session["session_id"])
open("bundle.asice", "wb").write(bundle)

# Observabilité retry
client.set_on_retry(lambda attempt, status, delay_ms:
    print(f"retry attempt={attempt} status={status} delay={delay_ms}ms"))

Exceptions typées

Les 3 SDK exposent 12-13 exceptions typées qui mappent les codes d'erreur métier (OTP_EXPIRED, SESSION_CLOSED, ORGANISATION_SUSPENDED, etc.). Catch précis plutôt que des string-comparisons.

try {
  await client.validateOtp(sessionId, otp);
} catch (e) {
  if (e instanceof OtpInvalidException) console.log(`attempts left: ${e.attemptsLeft}`);
  if (e instanceof OtpExpiredException) refreshOtp();
  if (e instanceof SessionNotFoundException) ...;
}
Roadmap V0.2 :
  • Timeline paginator (générateur asynchrone qui suit next_cursor)
  • SDK Swift + Kotlin pour mobile
  • Webhook subscriptions client-side helper (vérification HMAC)