# Policies

> API reference · Wathiqaty Insurer API v1 (draft)

Wathiqaty issues a policy the moment the customer's payment is captured, and sends `policy.issued`. Book it in your core system, then store your policy number on it. Everything sold (cover, price, taxes, cancellation scale) is frozen on the policy and never changes afterwards.

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

- List policies: `GET /partner/policies`
- Get a policy: `GET /partner/policies/{id}`
- List policy documents: `GET /partner/policies/{id}/documents`
- Set your policy number: `PUT /partner/policies/{id}/external-reference`
- Cancel a policy: `POST /partner/policies/{id}/cancel`

## List policies

`GET /partner/policies`

- Auth: Bearer token, scope `policies:read`

Also the change feed: `sortBy=updatedAt&sortOrder=asc&updatedSince=<last updatedAt>`. Use `hasExternalReference=false` to find policies you haven't booked yet.

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | string | no | Policy status. One of: `active`, `expired`, `cancelled`. |
| `insuranceType` | string | no | Line of business. One of: `individual_medical`, `new_cars`. |
| `packageId` | uuid | no | Sold from this package. |
| `updatedSince` | date-time | no | Only rows changed at or after this instant. Pass the `updatedAt` of the last row you processed (change feed). |
| `issuedFrom` | date | no | Issued on or after (Cairo date). |
| `issuedTo` | date | no | Issued on or before (Cairo date). |
| `externalReference` | string | no | Exact match on your number. |
| `hasExternalReference` | boolean | no | `false`: policies you haven't booked yet. |
| `sortBy` | string | no | Default `createdAt`. One of: `createdAt`, `updatedAt`, `coverageEndsAt`. |
| `page` | integer | no | Page number, from 1. Default 1. |
| `size` | integer | no | Items per page, 1–100. Default 20. |
| `sortOrder` | string | no | Default `desc`. Use `asc` with `sortBy=updatedAt` for a change feed. One of: `asc`, `desc`. |

### Request examples

```bash
curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/policies" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/policies", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
  },
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests

res = requests.get(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/policies",
    headers={
        "Authorization": f"Bearer {access_token}",
    },
    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.Get,
    "https://sandbox-api.wathiqaty.com/api/v1/partner/policies");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
using var res = await http.SendAsync(req);
var json = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
```

### Response 200

A page of policies.

```json
{
  "status": "success",
  "data": {
    "items": [
      {
        "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"
      }
    ],
    "meta": {
      "page": 1,
      "size": 20,
      "totalItems": 1,
      "totalPages": 1,
      "hasNextPage": false,
      "hasPreviousPage": false
    }
  },
  "error": null,
  "metadata": {
    "timestamp": "2026-11-05T09:15:02.481Z",
    "path": "/api/v1/partner/policies",
    "method": "GET",
    "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77"
  }
}
```

## Get a policy

`GET /partner/policies/{id}`

- Auth: Bearer token, scope `policies:read`

Includes the policyholder (`customer`, with the national id verified by eKYC), the eKYC evidence (`kyc`), the vehicle for car policies, and the frozen premium breakdown. Every read is written to the audit log.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | yes | The policy's Wathiqaty id. |

### Request examples

```bash
curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
  },
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests

res = requests.get(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}",
    headers={
        "Authorization": f"Bearer {access_token}",
    },
    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.Get,
    "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
using var res = await http.SendAsync(req);
var json = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
```

### Response 200

The 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/policies/6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e",
    "method": "GET",
    "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77"
  }
}
```

### Errors

| Status | Code | When |
| --- | --- | --- |
| 404 | `not_found` | Policy doesn't exist or isn't your company's. |

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

## List policy documents

`GET /partner/policies/{id}/documents`

- Auth: Bearer token, scope `policies:read`

Generated PDFs (`policy_schedule`, `insurance_card`, `signed_contract`). Download from `url`; files are never streamed through the API.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | yes | The policy's Wathiqaty id. |

### Request examples

```bash
curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/documents" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/documents", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
  },
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests

res = requests.get(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/documents",
    headers={
        "Authorization": f"Bearer {access_token}",
    },
    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.Get,
    "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/documents");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
using var res = await http.SendAsync(req);
var json = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
```

### Response 200

The documents.

```json
{
  "status": "success",
  "data": [
    {
      "id": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a",
      "documentType": "policy_schedule",
      "fileName": "POL-00000042.pdf",
      "contentType": "application/pdf",
      "url": "https://files.wathiqaty.com/policies/POL-00000042.pdf",
      "createdAt": "2026-11-05T09:15:40.000Z"
    }
  ],
  "error": null,
  "metadata": {
    "timestamp": "2026-11-05T09:15:02.481Z",
    "path": "/api/v1/partner/…",
    "method": "GET",
    "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77"
  }
}
```

### Errors

| Status | Code | When |
| --- | --- | --- |
| 404 | `not_found` | Policy doesn't exist or isn't your company's. |

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

## Set your policy number

`PUT /partner/policies/{id}/external-reference`

- Auth: Bearer token, scope `policies:write`
- `Idempotency-Key`: optional
- `If-Match`: supported

Store your core system's policy number. Send `If-Match` to change one that is already set. Fires `policy.updated`.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | yes | The policy's Wathiqaty id. |

### Body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `externalReference` | string | yes | Your own reference, 1–100 characters, unique within your company. |
| `showToCustomer` | boolean | no | Show it in the customer's app next to the Wathiqaty number. Default `true`. |

### Request examples

```bash
curl -X PUT "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/external-reference" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "If-Match: W/"7"" \
  -d '{"externalReference":"MI/2026/MED/004213","showToCustomer":true}'
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/external-reference", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
    "If-Match": "W/\"7\"",
  },
  body: JSON.stringify({
    "externalReference": "MI/2026/MED/004213",
    "showToCustomer": true
  }),
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests, uuid

res = requests.put(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/external-reference",
    headers={
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
        "If-Match": "W/\"7\"",
    },
    json={
      "externalReference": "MI/2026/MED/004213",
      "showToCustomer": True
    },
    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.Put,
    "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/external-reference");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString());
req.Headers.TryAddWithoutValidation("If-Match", "W/\"7\"");
req.Content = new StringContent(
    """
    {
      "externalReference": "MI/2026/MED/004213",
      "showToCustomer": true
    }
    """,
    Encoding.UTF8, "application/json");
using var res = await http.SendAsync(req);
var json = await res.Content.ReadAsStringAsync();
res.EnsureSuccessStatusCode();
```

### Response 200

The 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": "PUT",
    "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77"
  }
}
```

### Errors

| Status | Code | When |
| --- | --- | --- |
| 404 | `not_found` | Policy doesn't exist or isn't your company's. |
| 412 | `version_conflict` | Changed since you read it (`If-Match` doesn't match). Reload and retry. |
| 409 | `conflict` | Another policy of yours already has this reference. |

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

## Cancel a policy

`POST /partner/policies/{id}/cancel`

- Auth: Bearer token, scope `policies:write`
- `Idempotency-Key`: required

Insurer-initiated cancellation. The refund comes from your cancellation scale in force today; taxes are never refunded. Creates a cancellation (`refund_pending`, or `completed` when nothing is due).

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | yes | The policy's Wathiqaty id. |

### Body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | string | yes | 3–2000 characters, shown to the customer. |
| `effectiveDate` | date | no | Default today. Not in the past, not after the cover ends. |

### Request examples

```bash
curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/cancel" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"reason":"Non-disclosure of a pre-existing condition"}'
```

```js
const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/cancel", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${accessToken}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    "reason": "Non-disclosure of a pre-existing condition"
  }),
})
const { data, error } = await res.json()
if (!res.ok) throw new Error(`${res.status} ${error.code}`)
```

```python
import requests, uuid

res = requests.post(
    "https://sandbox-api.wathiqaty.com/api/v1/partner/policies/{id}/cancel",
    headers={
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
      "reason": "Non-disclosure of a pre-existing condition"
    },
    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/policies/{id}/cancel");
req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString());
req.Content = new StringContent(
    """
    {
      "reason": "Non-disclosure of a pre-existing condition"
    }
    """,
    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": "refund_pending",
    "initiatedBy": "insurer",
    "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"
  }
}
```

### Errors

| Status | Code | When |
| --- | --- | --- |
| 404 | `not_found` | Policy doesn't exist or isn't your company's. |
| 409 | `cancellation_already_open` | A cancellation is already open on this policy. |
| 422 | `policy_not_active` | The policy is expired or cancelled. |
| 422 | `claim_approved` | An approved claim on the policy blocks a refunding cancellation. |

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