# OTP Verification & Proof

Deep dive into the OTP validation flow, proof certificate format, and eIDAS compliance details.

## OTP flow

Each signer goes through this sequence:

```
1. Initiate  →  System sends OTP via SMS to signer's phone
2. Validate  →  Signer enters 6-digit OTP code
3. Sign      →  Signer draws their signature on the document
4. Complete  →  Signature embedded in PDF with certificate
```

### OTP rules

| Parameter | Value |
|-----------|-------|
| OTP length | 6 digits |
| Validity | 5 minutes from sending |
| Max attempts | 3 per OTP |
| Resend cooldown | 60 seconds |
| Delivery | SMS to signer's registered mobile phone |

### Validate an OTP

The signer's application calls:

```bash
curl -X POST https://api.faireplace.com/api/leases/{lease_id}/signature/validate-otp \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "document_signature_id": "550e8400-e29b-41d4-a716-446655440000",
    "otp": "482916"
  }'
```

```json
{
  "success": true,
  "message": "Signature completed successfully",
  "document_signature_id": "550e8400-e29b-41d4-a716-446655440000",
  "signer_status": "COMPLETED",
  "document_status": "SIGNING",
  "remaining_signers": 1,
  "signed_pdf_url": null
}
```

### Resend an OTP

If the OTP expires or the signer didn't receive it:

```bash
curl -X POST https://api.faireplace.com/api/leases/{lease_id}/signature/resend/{signer_type} \
  -H "Authorization: Bearer $API_KEY"
```

> The previous OTP is invalidated when a new one is sent.

## OTP error handling

### Wrong OTP code

```json
{
  "error": {
    "code": 422,
    "type": "VALIDATION_ERROR",
    "message": "Invalid OTP code",
    "details": {
      "attempts_remaining": 2,
      "expires_at": "2026-02-20T14:35:00Z"
    }
  }
}
```

### OTP expired

```json
{
  "error": {
    "code": 422,
    "type": "OTP_EXPIRED",
    "message": "OTP has expired. Request a new one.",
    "details": {
      "expired_at": "2026-02-20T14:35:00Z"
    }
  }
}
```

### Max attempts exceeded

After 3 failed attempts, the signer's OTP is locked. A new OTP must be sent:

```json
{
  "error": {
    "code": 429,
    "type": "OTP_LOCKED",
    "message": "Maximum OTP attempts reached. Request a new OTP.",
    "details": {
      "locked_until": "2026-02-20T14:40:00Z"
    }
  }
}
```

## Proof certificate

After all signers complete, FairePlace generates a proof certificate — a JSON document that provides a legally admissible record of the signing process.

### Retrieve the proof

```bash
curl https://api.faireplace.com/api/leases/{lease_id}/signature/proof \
  -H "Authorization: Bearer $API_KEY"
```

### Proof format

```json
{
  "status": "AVAILABLE",
  "proof": {
    "version": "1.0",
    "proof_id": "proof-550e8400-e29b-41d4-a716-446655440000",
    "generated_at": "2026-02-19T16:45:01Z",
    "document": {
      "document_signature_id": "550e8400-e29b-41d4-a716-446655440000",
      "lease_id": "550e8400-e29b-41d4-a716-446655440001",
      "original_document_hash": "a1b2c3d4e5f6789012345678abcdef0123456789abcdef0123456789abcdef01",
      "signed_document_hash": "f0e1d2c3b4a5968778695a4b3c2d1e0f...",
      "page_count": 12,
      "created_at": "2026-02-19T10:08:00Z",
      "completed_at": "2026-02-19T16:45:00Z"
    },
    "signers": [
      {
        "signer_type": "PROPRIETAIRE",
        "identity": {
          "email": "jean.dupont@sci-rivoli.com",
          "phone": "+33612345678",
          "first_name": "Jean",
          "last_name": "Dupont"
        },
        "process": {
          "notification_sent_at": "2026-02-19T14:28:00Z",
          "otp_sent_at": "2026-02-19T14:29:00Z",
          "otp_validated_at": "2026-02-19T14:29:50Z",
          "signature_completed_at": "2026-02-19T14:30:00Z",
          "ip_address": "92.184.xxx.xxx",
          "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
          "country_code": "FR"
        },
        "cryptographic": {
          "signature_hash": "abc123def456...",
          "certificate_issuer": "Certinomis"
        }
      },
      {
        "signer_type": "LOCATAIRE",
        "identity": {
          "email": "marie.martin@email.com",
          "phone": "+33698765432",
          "first_name": "Marie",
          "last_name": "Martin"
        },
        "process": {
          "notification_sent_at": "2026-02-19T16:43:00Z",
          "otp_sent_at": "2026-02-19T16:44:00Z",
          "otp_validated_at": "2026-02-19T16:44:50Z",
          "signature_completed_at": "2026-02-19T16:45:00Z",
          "ip_address": "86.247.xxx.xxx",
          "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0)...",
          "country_code": "FR"
        },
        "cryptographic": {
          "signature_hash": "def789ghi012...",
          "certificate_issuer": "Certinomis"
        }
      }
    ],
    "audit_trail": [
      {
        "timestamp": "2026-02-19T14:28:00Z",
        "event_type": "SignatureRequested",
        "actor_type": "PROPRIETAIRE",
        "description": "Signature request sent to owner"
      },
      {
        "timestamp": "2026-02-19T14:30:00Z",
        "event_type": "OtpValidated",
        "actor_type": "PROPRIETAIRE",
        "description": "OTP validated and signature completed"
      },
      {
        "timestamp": "2026-02-19T16:45:00Z",
        "event_type": "DocumentFinalized",
        "actor_type": "SYSTEM",
        "description": "All signatures collected, document finalized"
      }
    ],
    "compliance": {
      "eidas_level": "Advanced Electronic Signature",
      "signature_format": "PAdES-B",
      "etsi_standard": "ETSI EN 319 142-1",
      "hash_algorithm": "SHA-256",
      "retention_years": 10
    }
  },
  "pdf_url": "https://api.faireplace.com/api/media/serve/proof-pdf-id",
  "json_url": "https://api.faireplace.com/api/media/serve/proof-json-id"
}
```

### Proof fields explained

| Section | Purpose |
|---------|---------|
| `document` | SHA-256 hash of the original document — proves the signed document matches |
| `signers` | Audit trail for each signer: when OTP was sent, validated, and when they signed |
| `certificate` | Certificate authority details and eIDAS compliance level |
| `integrity` | Hash chain linking the document, signatures, and proof together |

## eIDAS compliance

FairePlace signatures meet the **Advanced Electronic Signature (AdES)** level under EU Regulation 910/2014 (eIDAS):

| eIDAS Requirement | FairePlace Implementation |
|-------------------|--------------------------|
| Uniquely linked to signer | OTP sent to signer's personal mobile phone |
| Identifies the signer | Identity verified via email + phone number |
| Under sole control of signer | OTP valid 5 minutes, 3 attempts max, single-use |
| Detects subsequent changes | PAdES-B with SHA-256 hash — any modification breaks the seal |
| Qualified certificate | Certinomis (ANSSI-qualified trust service provider) |
| Qualified timestamp | Certinomis TSA (RFC 3161 compliant) |

### Legal validity in France

Under French law (Article 1367 of the Civil Code), an electronic signature has the same legal force as a handwritten signature when it meets:
1. A reliable identification process
2. A link between the signature and the document

FairePlace's Advanced Electronic Signature satisfies both requirements through OTP verification and PAdES-B certificate embedding.

## Troubleshooting

### Signer didn't receive the SMS

1. Verify the phone number is in international format (e.g., `+33612345678`)
2. Check that the number is a mobile phone (landlines cannot receive SMS)
3. Wait 60 seconds and resend the OTP
4. If the issue persists, verify the signer's phone is not in "Do Not Disturb" mode

### Signature expired

Signatures expire after 14 days by default. If expired:
1. Cancel the expired signature
2. Generate a new PDF (if lease data changed)
3. Initiate a new signature process

### Proof not available

The proof certificate is only generated after all signers complete. Check `GET /leases/{id}/signature/status` to verify the overall status is `Completed`.

---

## Related

- **[Electronic Signatures](/signatures/overview)** — Full signature workflow
- **[Lease Workflow](/signatures/lease-workflow)** — End-to-end tutorial
- **[Payments & Credits](/payments/credits)** — Purchase signature credits
