# Sandbox Environment

Test the FairePlace API without affecting production data or incurring charges.

## Sandbox vs Production

| Feature | Sandbox | Production |
|---------|---------|------------|
| Base URL | `https://sandbox.faireplace.com/api` | `https://api.faireplace.com/api` |
| API keys | Separate sandbox keys (`fp_sandbox_*`) | Production keys (`fp_*`) |
| Data | Isolated test data, reset weekly | Real data |
| Signatures | OTP bypass with code `000000` | Real SMS OTP |
| Payments | Stripe test mode (no real charges) | Real Stripe charges |
| PDFs | Watermarked "SANDBOX - AUCUNE VALEUR JURIDIQUE" | Production PDFs |
| Notifications | Logged but not sent | Real email/SMS |
| Rate limits | Same as production plan | Per plan |

## Getting started with sandbox

### 1. Get a sandbox API key

Sandbox API keys are managed separately from production:

1. Log in to [FairePlace](https://app.faireplace.com)
2. Go to **Paramètres** > **Développeur** > **Clés API**
3. Toggle the **Sandbox** switch
4. Click **+ Créer une clé API**
5. Your sandbox key will have the prefix `fp_sandbox_`

### 2. Point your client to sandbox

```bash
export FAIREPLACE_API_KEY="fp_sandbox_a1b2c3d4e5f6..."
export FAIREPLACE_BASE_URL="https://sandbox.faireplace.com/api"

curl $FAIREPLACE_BASE_URL/health \
  -H "Authorization: Bearer $FAIREPLACE_API_KEY"
```

### 3. Create test data

All standard API endpoints work identically in sandbox:

```bash
# Create a test owner
curl -X POST $FAIREPLACE_BASE_URL/owners \
  -H "Authorization: Bearer $FAIREPLACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Mr.",
    "first_name": "Test",
    "last_name": "Owner",
    "owner_category": "individual",
    "email": "test@example.com",
    "mobile_phone": "+33600000000"
  }'
```

## Test scenarios

### Scenario 1: Happy path — signed lease in 5 minutes

```bash
export B="https://sandbox.faireplace.com/api"
export H="Authorization: Bearer $FAIREPLACE_API_KEY"

# 1. Create owner
OWNER=$(curl -s -X POST $B/owners \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"first_name":"Test","last_name":"Owner","owner_category":"individual","email":"owner@test.com","mobile_phone":"+33600000001"}' | jq -r '.id')

# 2. Create place + estate
PLACE=$(curl -s -X POST $B/places \
  -H "$H" -H "Content-Type: application/json" \
  -d "{\"name\":\"Test Building\",\"owner_id\":\"$OWNER\",\"postal_code\":\"75012\",\"city\":\"Paris\"}" | jq -r '.id')

ESTATE=$(curl -s -X POST $B/places/$PLACE/estates \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"area":45.0,"number_of_room":2}' | jq -r '.id')

# 3. Create lease
LEASE=$(curl -s -X POST $B/leases \
  -H "$H" -H "Content-Type: application/json" \
  -d "{\"estate_id\":\"$ESTATE\",\"rent_amount\":950,\"deposit_amount\":950,\"start_date\":\"2026-04-01\",\"end_date\":\"2029-03-31\"}" | jq -r '.id')

# 4. Add tenant
TENANT=$(curl -s -X POST $B/lessees \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"category":"individual","first_name":"Test","last_name":"Tenant","email":"tenant@test.com","mobile_phone":"+33600000002"}' | jq -r '.id')

curl -s -X POST $B/leases/$LEASE/lessees \
  -H "$H" -H "Content-Type: application/json" \
  -d "{\"lessee_id\":\"$TENANT\",\"role\":\"PrimaryTenant\",\"start_date\":\"2026-04-01\"}"

# 5. Generate PDF
PDF=$(curl -s -X POST $B/leases/$LEASE/pdf \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"template_type":"StandardLease"}' | jq -r '.pdf_id')

# 6. Sign (OTP bypass with 000000)
curl -s -X POST $B/leases/$LEASE/signature/initiate \
  -H "$H" -H "Content-Type: application/json" \
  -d "{\"pdf_id\":\"$PDF\",\"signers\":[{\"signer_type\":\"PROPRIETAIRE\",\"first_name\":\"Test\",\"last_name\":\"Owner\",\"person_id\":\"$OWNER\",\"email\":\"owner@test.com\",\"phone\":\"+33600000001\"}]}"

echo "Lease $LEASE created and signature initiated!"
```

### Scenario 2: Compliance rejection

Test what happens when rent exceeds the legal cap:

```bash
# This will fail with 422 COMPLIANCE_ERROR — rent too high for Paris 75012
curl -s -X POST $B/leases \
  -H "$H" -H "Content-Type: application/json" \
  -d "{\"estate_id\":\"$ESTATE\",\"rent_amount\":5000,\"deposit_amount\":5000,\"start_date\":\"2026-04-01\",\"end_date\":\"2029-03-31\"}"
```

Expected response:
```json
{
  "error": {
    "code": 422,
    "type": "COMPLIANCE_ERROR",
    "message": "Rent exceeds legal maximum for this area",
    "details": {
      "rule": "RentCap",
      "current_value": 111.11,
      "maximum_allowed": 30.60,
      "unit": "EUR/m2"
    }
  }
}
```

### Scenario 3: Dependency conflict

Test what happens when you try to delete a place that still has estates:

```bash
# This will fail with 409 CONFLICT
curl -s -X DELETE $B/places/$PLACE \
  -H "$H"
```

Expected response:
```json
{
  "error": {
    "code": 409,
    "type": "CONFLICT",
    "message": "Cannot delete place with associated estates"
  }
}
```

### Scenario 4: Check rent regulation

```bash
# Check if 75012 is in a zone tendue
curl -s -X POST $B/legislative-zones/rent-regulation/check \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"postal_code":"75012"}'

# Check rent control compliance
curl -s -X POST $B/legislative-zones/rent-control/check \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"address":"24 rue du Faubourg Saint-Antoine","postal_code":"75012","room_count":2,"surface_area":45.0,"construction_period":"1946_1970","is_furnished":false,"proposed_rent":950}'
```

## Signature testing

In sandbox, the OTP verification step is bypassed with a fixed code:

```bash
# Initiate signature (same as production)
curl -X POST $FAIREPLACE_BASE_URL/leases/{lease_id}/signature/initiate \
  -H "Authorization: Bearer $FAIREPLACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "pdf_id": "<pdf_uuid>", "signers": [...] }'

# Validate OTP with bypass code
curl -X POST $FAIREPLACE_BASE_URL/leases/{lease_id}/signature/validate-otp \
  -H "Authorization: Bearer $FAIREPLACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signature_request_id": "<sig_request_uuid>",
    "otp_code": "000000"
  }'
```

> **Note:** Sandbox-signed PDFs include a visible watermark: **"SANDBOX - AUCUNE VALEUR JURIDIQUE"**. These documents have no legal value.

## Payment testing

Sandbox uses Stripe test mode. Use [Stripe test card numbers](https://docs.stripe.com/testing#cards):

| Card number | Scenario |
|-------------|----------|
| `4242 4242 4242 4242` | Successful payment |
| `4000 0000 0000 0002` | Card declined |
| `4000 0000 0000 9995` | Insufficient funds |

```bash
# Purchase test credits
curl -X POST $FAIREPLACE_BASE_URL/credits/checkout \
  -H "Authorization: Bearer $FAIREPLACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "package_id": "<package_uuid>" }'
```

No real charges are made in sandbox mode.

## Sandbox limitations

- **Data resets:** Sandbox data is reset every Sunday at 02:00 UTC
- **No real notifications:** Emails and SMS are logged but not delivered
- **No real signatures:** Signed PDFs have no legal value (eIDAS proof is simulated)
- **Credit limits:** Sandbox accounts start with 100 free test credits

## Switching to production

When you're ready to go live:

1. Replace your sandbox API key with a production key
2. Update the base URL from `sandbox.faireplace.com` to `api.faireplace.com`
3. Purchase real signature credits
4. Test the signature flow with a real phone number to verify OTP delivery

```bash
# Production
export FAIREPLACE_API_KEY="fp_a1b2c3d4e5f6..."
export FAIREPLACE_BASE_URL="https://api.faireplace.com/api"
```

---

## Related

- **[Getting Started](/quickstart/getting-started)** — First API call tutorial
- **[Authentication](/quickstart/authentication)** — API key management
- **[Electronic Signatures](/signatures/overview)** — Full signature workflow
- **[Payments & Credits](/payments/credits)** — Credit purchase flow
