# Sandbox simulator

> API reference · Wathiqaty Insurer API v1 (draft)

Sandbox-only routes that play the customer, so you can drive every flow from your side without the Wathiqaty app. They fire the same webhooks as real activity. They return 404 in production.

Base URL: `https://sandbox-api.wathiqaty.com/api/v1` (sandbox), `https://api.wathiqaty.com/api/v1` (production).

- Simulate a sale: `POST /partner/sandbox/simulate/policy-sale`
- Simulate a cancellation request: `POST /partner/sandbox/simulate/cancellation-request`
- Simulate a claim: `POST /partner/sandbox/simulate/claim`
- Simulate a document resubmission: `POST /partner/sandbox/simulate/claim-resubmission`
- Move the sandbox clock: `POST /partner/sandbox/clock`

## Simulate a sale

`POST /partner/sandbox/simulate/policy-sale`

- Auth: Bearer token, scope `any`
- Sandbox only

Creates a test customer (eKYC passed), a captured payment and an issued policy. Fires `policy.issued`.

### Body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `packageId` | uuid | yes | An `active` package of yours. |
| `customer` | object | no | Override `{ name, birthDate, gender }`; random otherwise. |
| `vehicle` | object | no | Car packages: `{ brand, model, year, value }`; random otherwise. |

### Request examples

```bash
curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/policy-sale" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"packageId":"5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13","customer":{"name":"Test Customer","birthDate":"1990-01-01","gender":"female"}}'
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/policy-sale", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "packageId": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13",
    "customer": {
      "name": "Test Customer",
      "birthDate": "1990-01-01",
      "gender": "female"
    }
  }),
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests

res = requests.post(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/policy-sale",
    headers={
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    },
    json={
      "packageId": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13",
      "customer": {
        "name": "Test Customer",
        "birthDate": "1990-01-01",
        "gender": "female"
      }
    },
    timeout=30,
)
payload = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {payload['error']['code']}")
data = payload["data"]
```

```csharp
using var req = new HttpRequestMessage(HttpMethod.Post,
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/policy-sale");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
req.Content = new StringContent(
    """
    {
      "packageId": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13",
      "customer": {
        "name": "Test Customer",
        "birthDate": "1990-01-01",
        "gender": "female"
      }
    }
    """,
    Encoding.UTF8, "application/json");
using var res = await http.SendAsync(req);
var json = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
```

### Response 201

The issued policy.

```json
{
  "status": "success",
  "data": {
    "id": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e",
    "number": "POL-00000042",
    "externalReference": "MI/2026/MED/004213",
    "packageId": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13",
    "packageExternalReference": "PRD-GOLD-2026",
    "packageName": {
      "en": "Gold Care",
      "ar": "جولد كير"
    },
    "insuranceType": "individual_medical",
    "status": "active",
    "coverageStartsAt": "2026-11-05T09:15:00.000Z",
    "coverageEndsAt": "2027-11-05T09:15:00.000Z",
    "cancelledAt": null,
    "sumInsured": "250000.00",
    "premium": {
      "net": "8333.33",
      "taxes": [
        {
          "name": "VAT",
          "rate": "14.00",
          "amount": "1166.67"
        }
      ],
      "total": "9500.00",
      "currency": "EGP"
    },
    "commission": {
      "rate": "10.00",
      "amount": "833.33"
    },
    "customer": {
      "name": "Mona Adel",
      "email": "mona@example.com",
      "phone": "01012345678",
      "nationalId": "29001011234567",
      "birthDate": "1990-01-01",
      "gender": "female"
    },
    "kyc": {
      "provider": "lumin",
      "verifiedId": "lm_7c1d9e2f",
      "verifiedAt": "2026-11-05T09:02:11.000Z"
    },
    "vehicle": null,
    "cardNumber": "MC-2026-42",
    "cancellationScale": {
      "effectiveFrom": "2026-10-01",
      "tiers": [
        {
          "fromDay": 0,
          "toDay": 14,
          "refundPercent": "90.00",
          "basis": "flat"
        },
        {
          "fromDay": 15,
          "toDay": 365,
          "refundPercent": "80.00",
          "basis": "pro_rata"
        }
      ]
    },
    "createdAt": "2026-11-05T09:15:05.000Z",
    "updatedAt": "2026-11-05T09:16:40.000Z"
  },
  "error": null,
  "metadata": {
    "timestamp": "2026-11-05T09:15:02.481Z",
    "path": "/api/v1/partner/…",
    "method": "POST",
    "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77"
  }
}
```

### Errors

| Status | Code | When |
| --- | --- | --- |
| 422 | `package_not_active` | The package isn't on sale. |

Plus the generic errors any route can return: [Errors](/guides/errors.md).

## Simulate a cancellation request

`POST /partner/sandbox/simulate/cancellation-request`

- Auth: Bearer token, scope `any`
- Sandbox only

Fires `cancellation.requested`.

### Body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `policyId` | uuid | yes | An active sandbox policy. |
| `reason` | string | no | Default "Sandbox test". |

### Request examples

```bash
curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/cancellation-request" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"policyId":"6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e"}'
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/cancellation-request", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e"
  }),
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests

res = requests.post(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/cancellation-request",
    headers={
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    },
    json={
      "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e"
    },
    timeout=30,
)
payload = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {payload['error']['code']}")
data = payload["data"]
```

```csharp
using var req = new HttpRequestMessage(HttpMethod.Post,
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/cancellation-request");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
req.Content = new StringContent(
    """
    {
      "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e"
    }
    """,
    Encoding.UTF8, "application/json");
using var res = await http.SendAsync(req);
var json = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
```

### Response 201

The cancellation.

```json
{
  "status": "success",
  "data": {
    "id": "b7d4e1a9-2c3f-4a58-9e6b-0d1c2b3a4f5e",
    "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e",
    "status": "requested",
    "initiatedBy": "customer",
    "reason": "Moved to my employer's group cover",
    "requestedAt": "2026-11-20T11:00:00.000Z",
    "refundQuote": {
      "daysElapsed": 15,
      "tier": {
        "fromDay": 15,
        "toDay": 365,
        "refundPercent": "80.00",
        "basis": "pro_rata"
      },
      "refundAmount": "6202.74",
      "taxesRetained": "1166.67",
      "commissionReversal": "620.27"
    },
    "decidedAt": null,
    "completedAt": null,
    "updatedAt": "2026-11-20T11:00:00.000Z"
  },
  "error": null,
  "metadata": {
    "timestamp": "2026-11-05T09:15:02.481Z",
    "path": "/api/v1/partner/…",
    "method": "POST",
    "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77"
  }
}
```

## Simulate a claim

`POST /partner/sandbox/simulate/claim`

- Auth: Bearer token, scope `any`
- Sandbox only

A submitted claim with placeholder documents. Fires `claim.submitted`.

### Body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `policyId` | uuid | yes | An active sandbox policy. |
| `claimedAmount` | decimal | yes | Amount claimed. |
| `categoryId` | uuid | no | Default: the first category of the line. |

### Request examples

```bash
curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/claim" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"policyId":"6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e","claimedAmount":"4200.00"}'
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/claim", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e",
    "claimedAmount": "4200.00"
  }),
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests

res = requests.post(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/claim",
    headers={
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    },
    json={
      "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e",
      "claimedAmount": "4200.00"
    },
    timeout=30,
)
payload = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {payload['error']['code']}")
data = payload["data"]
```

```csharp
using var req = new HttpRequestMessage(HttpMethod.Post,
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/claim");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
req.Content = new StringContent(
    """
    {
      "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e",
      "claimedAmount": "4200.00"
    }
    """,
    Encoding.UTF8, "application/json");
using var res = await http.SendAsync(req);
var json = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
```

### Response 201

The claim.

```json
{
  "status": "success",
  "data": {
    "id": "c9e8d7f6-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
    "number": "CLM-00000107",
    "externalReference": null,
    "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e",
    "categoryId": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
    "status": "submitted",
    "incidentDate": "2026-12-01",
    "claimedAmount": "4200.00",
    "approvedAmount": null,
    "items": [
      {
        "lineId": "l1",
        "description": "Outpatient consultation",
        "claimedAmount": "700.00"
      },
      {
        "lineId": "l2",
        "description": "MRI scan",
        "claimedAmount": "3500.00"
      }
    ],
    "documents": [
      {
        "id": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a",
        "requirementKey": "medical_report",
        "status": "pending",
        "fileName": "report.pdf",
        "url": "https://files.wathiqaty.com/claims/report.pdf"
      }
    ],
    "documentRequests": [],
    "policyCoverage": {
      "sumInsured": "250000.00",
      "consumed": "0.00",
      "remaining": "250000.00"
    },
    "submittedAt": "2026-12-02T08:30:00.000Z",
    "updatedAt": "2026-12-02T08:30:00.000Z"
  },
  "error": null,
  "metadata": {
    "timestamp": "2026-11-05T09:15:02.481Z",
    "path": "/api/v1/partner/…",
    "method": "POST",
    "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77"
  }
}
```

## Simulate a document resubmission

`POST /partner/sandbox/simulate/claim-resubmission`

- Auth: Bearer token, scope `any`
- Sandbox only

The customer answers your open document requests. Fires `claim.resubmitted`.

### Body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `claimId` | uuid | yes | A `documents_requested` claim. |

### Request examples

```bash
curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/claim-resubmission" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"claimId":"c9e8d7f6-1a2b-4c3d-8e4f-5a6b7c8d9e0f"}'
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/claim-resubmission", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "claimId": "c9e8d7f6-1a2b-4c3d-8e4f-5a6b7c8d9e0f"
  }),
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests

res = requests.post(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/claim-resubmission",
    headers={
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    },
    json={
      "claimId": "c9e8d7f6-1a2b-4c3d-8e4f-5a6b7c8d9e0f"
    },
    timeout=30,
)
payload = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {payload['error']['code']}")
data = payload["data"]
```

```csharp
using var req = new HttpRequestMessage(HttpMethod.Post,
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/simulate/claim-resubmission");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
req.Content = new StringContent(
    """
    {
      "claimId": "c9e8d7f6-1a2b-4c3d-8e4f-5a6b7c8d9e0f"
    }
    """,
    Encoding.UTF8, "application/json");
using var res = await http.SendAsync(req);
var json = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
```

### Response 200

The claim, back `under_review`.

```json
{
  "status": "success",
  "data": {
    "id": "c9e8d7f6-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
    "number": "CLM-00000107",
    "externalReference": null,
    "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e",
    "categoryId": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
    "status": "under_review",
    "incidentDate": "2026-12-01",
    "claimedAmount": "4200.00",
    "approvedAmount": null,
    "items": [
      {
        "lineId": "l1",
        "description": "Outpatient consultation",
        "claimedAmount": "700.00"
      },
      {
        "lineId": "l2",
        "description": "MRI scan",
        "claimedAmount": "3500.00"
      }
    ],
    "documents": [
      {
        "id": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a",
        "requirementKey": "medical_report",
        "status": "pending",
        "fileName": "report.pdf",
        "url": "https://files.wathiqaty.com/claims/report.pdf"
      }
    ],
    "documentRequests": [],
    "policyCoverage": {
      "sumInsured": "250000.00",
      "consumed": "0.00",
      "remaining": "250000.00"
    },
    "submittedAt": "2026-12-02T08:30:00.000Z",
    "updatedAt": "2026-12-02T08:30:00.000Z"
  },
  "error": null,
  "metadata": {
    "timestamp": "2026-11-05T09:15:02.481Z",
    "path": "/api/v1/partner/…",
    "method": "POST",
    "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77"
  }
}
```

## Move the sandbox clock

`POST /partner/sandbox/clock`

- Auth: Bearer token, scope `any`
- Sandbox only

Moves your sandbox's clock forward, to test expiry and the cancellation scale's tiers. `{ "reset": true }` goes back to real time.

### Body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `advanceDays` | integer | no | 1–400 days forward. |
| `reset` | boolean | no | Back to real time. |

### Request examples

```bash
curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/clock" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"advanceDays":30}'
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/clock", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "advanceDays": 30
  }),
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests

res = requests.post(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/clock",
    headers={
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    },
    json={
      "advanceDays": 30
    },
    timeout=30,
)
payload = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {payload['error']['code']}")
data = payload["data"]
```

```csharp
using var req = new HttpRequestMessage(HttpMethod.Post,
    "https://sandbox-api.wathiqaty.com/api/v1/partner/sandbox/clock");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
req.Content = new StringContent(
    """
    {
      "advanceDays": 30
    }
    """,
    Encoding.UTF8, "application/json");
using var res = await http.SendAsync(req);
var json = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
```

### Response 200

The sandbox's current time.

```json
{
  "status": "success",
  "data": {
    "now": "2026-12-05T09:15:02.000Z"
  },
  "error": null,
  "metadata": {
    "timestamp": "2026-11-05T09:15:02.481Z",
    "path": "/api/v1/partner/…",
    "method": "POST",
    "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77"
  }
}
```
