# Wathiqaty Insurer API: full documentation > System-to-system REST API for insurance companies selling through Wathiqaty (وثيقتي), an Egyptian insurance marketplace: receive issued policies, decide cancellations, work claims, sync pricing, reconcile commission. OAuth 2.0 client credentials, JSON envelope, signed webhooks. Status: v1 draft, not yet live. --- # Introduction Wathiqaty (وثيقتي) is an insurance marketplace in Egypt: customers compare medical and car insurance packages, buy online, and manage their policies, cancellations and claims in the Wathiqaty app. Your staff already handle all of this in the Wathiqaty portal. The **Insurer API** lets your own systems do the same work automatically. | You want to… | Use | | --- | --- | | Book every policy sold on Wathiqaty in your core system | [`policy.issued` webhook](/guides/webhooks.md) + [Policies](/reference/policies.md) | | Store your policy number against ours | [Set your policy number](/reference/policies.md#set-policy-reference) | | Keep prices in sync with your pricing engine | [Replace pricing rules](/reference/packages.md#replace-rating-rules) | | Decide on cancellation requests and refunds | [Cancellations](/reference/cancellations.md) | | Process claims in your claims system | [Claims](/reference/claims.md) | | Reconcile premiums and commission | [Finance](/reference/finance.md) and [Bordereaux](/reference/bordereaux.md) | ## How it fits together 1. A customer buys one of your packages on Wathiqaty. Wathiqaty prices it from your rating rules, verifies the customer's identity (eKYC) and captures the payment. 2. Wathiqaty issues the policy and sends you `policy.issued` with the full policy: policyholder, cover, premium breakdown and eKYC evidence. 3. Your system books it and stores your policy number on it (`PUT /partner/policies/{id}/external-reference`). 4. Over the policy's life you receive cancellation requests and claims as webhooks, and answer them through the API. 5. Each month Wathiqaty requests settlement of its commission; you reconcile with the ledger or the bordereau and upload the transfer proof. ## Environments | Environment | Base URL | Data | | --- | --- | --- | | Sandbox | `https://sandbox-api.wathiqaty.com/api/v1` | Test customers and policies, no real money. Reset weekly. | | Production | `https://api.wathiqaty.com/api/v1` | Real customers and money. Enabled after the go-live review. | Credentials are per environment: a sandbox client never works in production. ## What you need - A signed integration agreement with Wathiqaty (including the data processing terms), and your company live on the Wathiqaty portal. - An HTTPS endpoint reachable from the internet for webhooks, or a scheduled job that polls the change feed. - Any HTTP client. Examples are given in cURL, Node.js, Python and C#. The whole documentation is also available as Markdown for AI assistants: [llms.txt](/llms.txt), or add `.md` to any page URL. > New here? Follow the [Quickstart](/guides/quickstart.md) to make your first call in about ten minutes. --- # Quickstart From sandbox credentials to a policy booked in your system, in five calls. ## 1. Get sandbox credentials Ask your Wathiqaty account manager for a sandbox API client. You receive a `client_id` and a `client_secret` (delivered once, through a secure channel) with the scopes you asked for. Keep the secret in a secret manager, never in source code. ## 2. Get an access token **Request** ``` curl -X POST https://sandbox-api.wathiqaty.com/api/v1/auth/partner/token \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=client_credentials ``` **Response** ``` { "status": "success", "data": { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…", "tokenType": "Bearer", "expiresIn": 900, "scope": "policies:read policies:write" }, "error": null, "metadata": { "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77", "…": "…" } } ``` The token is valid for 15 minutes. Cache it and reuse it for every call; get a new one shortly before it expires. Details: [Authentication](/guides/authentication.md). ## 3. Check who you are **Request** ``` curl https://sandbox-api.wathiqaty.com/api/v1/partner/company \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` You get your company profile. A `401` means the token is wrong or expired. ## 4. Simulate a sale In the sandbox you play the customer. Find an active package with [`GET /partner/packages?status=active`](/reference/packages.md#list-packages), then: **Request** ``` 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":""}' ``` You get the issued policy. If you registered a webhook endpoint, it also receives `policy.issued`. ## 5. Book it Create the policy in your core system, then store your number: **Request** ``` curl -X PUT https://sandbox-api.wathiqaty.com/api/v1/partner/policies/$POLICY_ID/external-reference \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"externalReference":"MI/2026/MED/004213"}' ``` ## Next steps - [Set up webhooks](/guides/webhooks.md) instead of polling, and verify their signatures. - Read [Conventions](/guides/conventions.md) (money, dates, pagination, idempotency) before writing production code. - Work through the [go-live checklist](/guides/go-live.md). --- # Authentication The API uses the OAuth 2.0 **client credentials** grant. Each of your systems gets its own **API client** (a `client_id` and `client_secret`) with only the scopes it needs. A client exchanges its credentials for a short-lived bearer token and sends that token on every call. ## API clients - A client belongs to your company and only ever sees your company's data. - Wathiqaty creates, rotates and revokes clients at the request of your company's administrator. Ask for one client per system (core policy system, claims system, finance) so one can be revoked without stopping the others. - Secrets are stored hashed and can't be shown again. If one is lost or leaked, ask for a rotation: the old secret keeps working for 24 hours while you deploy the new one. - Optional per-client **IP allow-list**, recommended in production. Calls from other addresses get `403 ip_not_allowed`. - **Mutual TLS** is available on request if your security policy requires it. ## Scopes | Scope | Grants | | --- | --- | | `packages:read` | List and read packages, pricing rules, readiness. | | `packages:write` | Replace pricing rules, activate and deactivate, set your product code. | | `policies:read` | List and read policies (including the policyholder's personal data) and their documents. | | `policies:write` | Set your policy number, cancel a policy. | | `cancellations:read` | List and read cancellation requests. | | `cancellations:write` | Approve, reject, record manual refunds. | | `claims:read` | List and read claims, documents and messages. | | `claims:write` | Work claims: review, request documents, approve, reject, settle, close, message. | | `finance:read` | Payments, commission ledger, balance, settlements, bordereaux. | | `finance:write` | Submit settlement payment proof. | | `webhooks:manage` | Manage webhook endpoints and deliveries. | A call without the scope it needs gets `403 missing_permission`. Every endpoint in the reference shows its scope. ## Getting a token `POST /auth/partner/token` with HTTP Basic authentication (`client_id:client_secret`) and a form-encoded body. Every field and error is in the [reference](/reference/authentication.md#create-token). | Field | Meaning | | --- | --- | | `accessToken` | The bearer token (a signed JWT). Treat it as opaque. | | `expiresIn` | Seconds until it expires: 900 (15 minutes). | | `scope` | The scopes the token carries. | ## Using the token **HTTP** ``` GET /api/v1/partner/policies HTTP/1.1 Host: sandbox-api.wathiqaty.com Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9… ``` ### Cache it The token route allows 10 requests a minute per client. Keep one token in memory and renew it shortly before it expires, or once after a `401 unauthorized`: **Node.js** ``` // Node.js: one token shared by all calls, renewed 60 s before expiry let cached = { token: "", expiresAt: 0 } export async function accessToken() { if (Date.now() < cached.expiresAt - 60_000) return cached.token const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/auth/partner/token", { method: "POST", headers: { Authorization: "Basic " + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64"), "Content-Type": "application/x-www-form-urlencoded", }, body: "grant_type=client_credentials", }) const { data, error } = await res.json() if (!res.ok) throw new Error(`token: ${res.status} ${error.code}`) cached = { token: data.accessToken, expiresAt: Date.now() + data.expiresIn * 1000 } return cached.token } ``` ## Security notes - HTTPS only, TLS 1.2 or later. - Never put credentials or tokens in URLs, logs or front-end code. The API is for server-to-server use; browsers are refused by CORS. - Every call is recorded in Wathiqaty's audit log against the API client, including reads of personal data. --- # Going live ## Onboarding steps 1. **Agreement.** Sign the integration agreement and its data processing addendum. 2. **Sandbox access.** Your account manager issues sandbox clients and registers your technical contact (email and phone) for incident and deprecation notices. 3. **Build and test** against the sandbox, using the [simulator](/guides/sandbox.md) to drive every flow you integrate. 4. **Review.** Send the checklist below with sample logs. Wathiqaty usually reviews it within 3 business days. 5. **Production.** Production clients are issued, ideally with an IP allow-list. Register your production webhook endpoints. ## Checklist - Tokens are cached and renewed, not requested per call. - Every write you might retry sends an `Idempotency-Key`, and retries reuse it. - Webhook signatures are verified on the raw body, timestamps older than 5 minutes are rejected, and events are deduplicated by `id`. - Your webhook endpoint answers `2xx` within 10 seconds and processes the event asynchronously. - A reconciliation job (change feed or bordereau) catches anything a missed webhook would leave behind. - Money is parsed as a decimal type, never a binary float. - You log the `X-Request-Id` you send (or `metadata.requestId`) with every call. - Your client ignores unknown fields and unknown enum values, and answers `2xx` to unknown webhook event types. - Secrets live in a secret manager, not in code or config files. - Personal data from the API is stored and kept only as long as your policy administration requires (Egypt's Personal Data Protection Law 151/2020). ## Support Write to your account manager or `api-support@wathiqaty.com` with the request id, the endpoint, the time and the environment. Production incidents affecting the API are announced to registered technical contacts. --- # Conventions Rules every endpoint follows. Read them once before you build. ## Requests - JSON in and out (`Content-Type: application/json`), UTF-8. The only exceptions are the token route (form-encoded) and bordereaux (CSV out). - Unknown body fields and query parameters are rejected (`400 bad_request`), so typos fail loudly instead of being ignored. - Bodies are limited to 100 KB. | Header | Direction | Use | | --- | --- | --- | | `Authorization` | request | `Bearer ` on every route except the token route. | | `Idempotency-Key` | request | Safe retries of writes. See below. | | `If-Match` | request | The version you read, to avoid overwriting a newer change. See below. | | `X-Request-Id` | both | Your id for the call (a fresh UUID), echoed in the response and our logs. | | `ETag` | response | Version of an editable resource: `W/"7"`. | | `RateLimit`, `RateLimit-Policy`, `Retry-After` | response | Rate limiter state. See Rate limits. | ## Response envelope Every JSON response, success or error, has the same four keys: **200 OK** ``` { "status": "success", "data": { "id": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "status": "active" }, "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" } } ``` - `status` is `success` (2xx), `fail` (4xx: fix the request) or `error` (5xx: our side, retry later). - Read the HTTP status code and `error.code`. Never parse `error.message`: it's English text for developers and can change. See [Errors](/guides/errors.md). - `metadata.requestId` identifies the call. Log it and quote it to support. ## Data formats | Kind | Format | Example | | --- | --- | --- | | Ids | UUID strings. Path ids that aren't UUIDs get `400`. | `6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e` | | Money | EGP, decimal **strings** with 2 decimals. Prices are tax-inclusive. | `"9500.00"`, `"-620.27"` | | Instants | ISO 8601 in UTC. | `2026-11-05T09:15:02.481Z` | | Calendar dates | `YYYY-MM-DD`, on the Cairo (`Africa/Cairo`) calendar. | `2026-11-05` | | Phone numbers | Egyptian mobiles, returned as `01XXXXXXXXX`. | `01012345678` | | Translatable text | Both languages, always. | `{ "en": "Gold Care", "ar": "جولد كير" }` | | Enums | Lowercase snake_case, case-sensitive. | `documents_requested` | > Parse money with a decimal type (`decimal` in C#, `Decimal` in Python, `BigDecimal` in Java, a decimal library in JavaScript). Never with a binary float: rounding errors break reconciliation. ## Pagination Lists take `page` (from 1) and `size` (1–100, default 20) and return: **data** ``` { "items": [ { "id": "…" } ], "meta": { "page": 2, "size": 20, "totalItems": 47, "totalPages": 3, "hasNextPage": true, "hasPreviousPage": true } } ``` ## Change feed Policies, cancellations, claims and packages accept `updatedSince`. Sorted by `updatedAt` ascending, a list is a change feed: a reliable way to sync, and the safety net behind webhooks. **Pseudocode** ``` # Every few minutes, per resource: since = load_checkpoint("policies") # e.g. 2026-11-04T00:00:00.000Z loop: page = GET /partner/policies?updatedSince={since}&sortBy=updatedAt&sortOrder=asc&size=100 for row in page.items: upsert(row) unless already_seen(row.id, row.updatedAt) if page.items: since = page.items[-1].updatedAt; save_checkpoint("policies", since) if not page.meta.hasNextPage: break ``` Rows changed in the same millisecond can appear on two pages, so make the upsert idempotent on `id` + `updatedAt`. ## Idempotency A network failure can hide whether a write happened. Send `Idempotency-Key: ` on writes; if you retry with the same key, the action runs once and every retry gets the first result. - One key per **action** (one cancellation, one approval), reused for every retry of it. A new action gets a new key. - Successful results are kept for 24 hours. - A failed request (validation or business error) releases the key, so you can fix the input and retry with the same key. | Status | Code | Meaning | Do | | --- | --- | --- | --- | | 409 | `idempotency_request_is_still_processing` | The first request with this key is still running. | Wait `Retry-After` (1 s), retry with the same key. | | 422 | `idempotency_key_already_used` | Same key, different URL or body. | Bug: use a new key for a new action. | | 400 | `bad_request` | Key missing where required, or not 8–128 of `A-Z a-z 0-9 _ -`. | Send a UUID v4. | Each endpoint shows whether it **requires** or **supports** the header. Required on money-moving actions (cancel a policy, record a refund). ## Concurrent edits Your staff may edit the same resource in the Wathiqaty portal while your system does through the API. Editable resources return a version in `ETag` (and a `version` field). Send it back as `If-Match`: if someone changed the resource since you read it, you get `412 version_conflict` with `details.currentVersion` instead of silently overwriting their change. Reload, re-apply, resend. ## Files Files are never streamed through the API. Responses carry a `url` to download from directly. To upload (settlement proof), ask for a presigned upload URL, send the file straight to storage, then reference its `fileKey`. ## Personal data Policies carry the policyholder's personal data, including the national id verified by eKYC. Request `policies:read` only for systems that need it, store only what your policy administration requires, and handle it under Egypt's Personal Data Protection Law (151/2020) and the integration agreement. --- # Errors Errors use HTTP status codes and a stable, snake_case `error.code`. Branch on the code. `error.details` is optional and code-specific. **422 Unprocessable Entity** ``` { "status": "fail", "data": null, "error": { "code": "validation_error", "message": "Validation failed", "details": { "fields": { "rules.3.price": ["price must be a decimal string with at most 2 decimals"], "reason": ["reason must be at least 3 characters"] } } }, "metadata": { "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77", "…": "…" } } ``` ## Generic codes Any endpoint can return these. Business codes (`package_incomplete`, `claim_not_payable`, …) are listed on each endpoint in the reference. | Status | Code | Meaning | Do | | --- | --- | --- | --- | | 400 | `bad_request` | Malformed JSON, missing or unknown field, or an action not allowed in the resource's current status. | Fix the request. Don't retry as is. | | 400 | `invalid_scope` | Token request for a scope the client doesn't hold. | Request only granted scopes. | | 401 | `invalid_client` | Wrong client credentials, or client revoked. | Check credentials; ask your account manager. | | 401 | `unauthorized` | Token missing, invalid or expired. | Get a new token and retry **once**. | | 403 | `missing_permission` | The token lacks the endpoint's scope. | Ask for the scope on the client. | | 403 | `forbidden` | The client can never do this. | Don't retry. | | 403 | `ip_not_allowed` | IP not on the client's allow-list. | Fix the allow-list or call from an allowed IP. | | 403 | `tenant_not_active` | Your company is suspended on Wathiqaty. | Contact your account manager. | | 404 | `not_found` | Doesn't exist, or isn't your company's. `details.resource` names it. | Treat as missing. | | 408 | `request_timeout` | The request took too long. | Retry with the same `Idempotency-Key`. | | 409 | `conflict` | Conflicts with current state, e.g. a duplicate reference. | Reload, then decide. | | 409 | `idempotency_request_is_still_processing` | Same key still running. | Wait 1 s, retry with the same key. | | 412 | `version_conflict` | Changed since you read it. | Reload, re-apply, resend with the new `If-Match`. | | 413 | `payload_too_large` | Body over 100 KB. | Send less. | | 422 | `validation_error` | Field values invalid. `details.fields` maps each path to messages. | Fix the values. | | 422 | `idempotency_key_already_used` | Same key with a different request. | New key for a new action. | | 429 | `rate_limited` | Too many requests. | Wait `Retry-After` seconds. | | 500 | `internal_server_error` | Unexpected error on our side. | Retry with backoff; report the request id if it persists. | | 503 | `service_unavailable` | A dependency is down or we are deploying. | Retry with backoff, honour `Retry-After`. | ## Retrying - **Retry:** network errors, `408`, `429`, `5xx`, and `409 idempotency_request_is_still_processing` — always with the same `Idempotency-Key`. - **Refresh and retry once:** `401 unauthorized`. - **Never retry unchanged:** any other `4xx`. - Back off exponentially with jitter (1 s, 2 s, 4 s … up to 60 s), and honour `Retry-After` when present. **Pseudocode** ``` async function call(request, attempt = 0) { const res = await send(request) // same Idempotency-Key on every attempt if (res.ok) return res const retryable = [408, 429, 500, 502, 503, 504].includes(res.status) || (res.status === 409 && res.code === "idempotency_request_is_still_processing") if (res.status === 401 && attempt === 0) { await refreshToken() return call(request, attempt + 1) } if (!retryable || attempt >= 6) throw res const wait = res.retryAfter ?? Math.min(60, 2 ** attempt) * (0.5 + Math.random()) await sleep(wait * 1000) return call(request, attempt + 1) } ``` ### Status not allowed Moving a resource to a status its current status doesn't allow (e.g. approving a claim that's still `submitted`) is `400 bad_request` with `details.from` and `details.to`. Reload the resource: someone (your staff, the customer, or the system) moved it first. --- # Rate limits | Limit | Default | Notes | | --- | --- | --- | | Token route | 10 / minute per client | Cache tokens. | | Per API client | 600 / minute, bursts up to 100 / second | Raised on request for backfills. | | Per company | 1,200 / minute across all clients | | | Replace pricing rules | Counts as 10 requests | Bulk write. | Every response tells you where you stand: **Headers** ``` RateLimit-Policy: "client";q=600;w=60 RateLimit: "client";r=412;t=23 ``` `r` is the number of requests left and `t` the seconds until the window resets. Slow down before `r` reaches 0. Over the limit you get `429 rate_limited` with `Retry-After` (seconds) and `error.details.retryAfter`. Don't retry sooner: repeated early retries extend the block. A write refused with 429 never ran, so retry it with the same `Idempotency-Key`. > Backfilling history? Use the bordereaux or the change feed with `size=100`, and ask your account manager to raise your limit for the backfill window. --- # Webhooks When something happens to your company's data, Wathiqaty sends a signed HTTPS `POST` to your endpoint. Register endpoints with [Webhook endpoints](/reference/webhook-endpoints.md) and choose the event types each one receives. > Webhooks tell you **that** something changed. Delivery is at-least-once and not ordered. Deduplicate by event `id`, and keep a reconciliation job on the [change feed](/guides/conventions.md#change-feed) as a safety net. ## Event types **action needed** marks events that wait for your decision. Click an event to see its body. ### `ping` Test from `POST /partner/webhook-endpoints/{id}/test`. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "ping", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": {} } ``` ### `policy.issued` A payment was captured and the policy issued. Book it. (action needed) ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "policy.issued", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "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" } } ``` ### `policy.updated` Your reference was set, or the customer signed the e-contract. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "policy.updated", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "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" } } ``` ### `policy.signature_pending` The customer hasn't signed the e-contract yet (reminder sent). ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "policy.signature_pending", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "remindedAt": "2026-11-06T09:00:00.000Z" } } ``` ### `policy.expired` The cover ended. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "policy.expired", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "coverageEndsAt": "2027-11-05T09:15:00.000Z" } } ``` ### `policy.cancelled` The policy was cancelled, for any reason. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "policy.cancelled", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "cancelledAt": "2026-11-22T10:00:00.000Z", "reason": "cancellation_completed", "cancellationId": "b7d4e1a9-2c3f-4a58-9e6b-0d1c2b3a4f5e" } } ``` ### `cancellation.requested` The customer asked to cancel. Decide. (action needed) ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "cancellation.requested", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "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" } } ``` ### `cancellation.withdrawn` The customer withdrew the request. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "cancellation.withdrawn", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "id": "b7d4e1a9-2c3f-4a58-9e6b-0d1c2b3a4f5e", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "status": "withdrawn", "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" } } ``` ### `cancellation.approved` Approved (by you, your staff, or automatically). ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "cancellation.approved", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "id": "b7d4e1a9-2c3f-4a58-9e6b-0d1c2b3a4f5e", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "status": "refund_pending", "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" } } ``` ### `cancellation.rejected` Rejected. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "cancellation.rejected", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "id": "b7d4e1a9-2c3f-4a58-9e6b-0d1c2b3a4f5e", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "status": "rejected", "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" } } ``` ### `cancellation.completed` The refund is done; the policy is cancelled. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "cancellation.completed", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "id": "b7d4e1a9-2c3f-4a58-9e6b-0d1c2b3a4f5e", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "status": "completed", "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" } } ``` ### `payment.refunded` A refund reached the customer. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "payment.refunded", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "paymentId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "amount": "6202.74", "refundedAt": "2026-11-22T10:00:00.000Z" } } ``` ### `payment.reversed` A captured payment was voided or reversed at the gateway; its policy is cancelled. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "payment.reversed", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "paymentId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e" } } ``` ### `claim.submitted` A customer submitted a claim. Review it. (action needed) ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "claim.submitted", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "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" } } ``` ### `claim.resubmitted` The customer answered your document request. (action needed) ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "claim.resubmitted", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "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" } } ``` ### `claim.status_changed` Any other claim move. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "claim.status_changed", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "claimId": "c9e8d7f6-1a2b-4c3d-8e4f-5a6b7c8d9e0f", "from": "under_review", "to": "approved", "actor": "insurer_staff" } } ``` ### `claim.message_received` The customer wrote on a claim. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "claim.message_received", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "claimId": "c9e8d7f6-1a2b-4c3d-8e4f-5a6b7c8d9e0f", "messageId": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e" } } ``` ### `settlement.requested` Wathiqaty requested commission payment. Pay and upload proof. (action needed) ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "settlement.requested", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "id": "e4f5a6b7-c8d9-4e0f-9a1b-2c3d4e5f6a7b", "status": "requested", "periodFrom": "2026-11-01", "periodTo": "2026-11-30", "amount": "184500.00", "platformBankAccount": { "bank": "National Bank of Egypt", "iban": "EG380019000500000000263180002" }, "proof": null, "requestedAt": "2026-12-01T07:00:00.000Z", "updatedAt": "2026-12-01T07:00:00.000Z" } } ``` ### `settlement.confirmed` Wathiqaty confirmed your payment. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "settlement.confirmed", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "id": "e4f5a6b7-c8d9-4e0f-9a1b-2c3d4e5f6a7b", "status": "confirmed", "periodFrom": "2026-11-01", "periodTo": "2026-11-30", "amount": "184500.00", "platformBankAccount": { "bank": "National Bank of Egypt", "iban": "EG380019000500000000263180002" }, "proof": null, "requestedAt": "2026-12-01T07:00:00.000Z", "updatedAt": "2026-12-01T07:00:00.000Z" } } ``` ### `settlement.rejected` Your proof was rejected (`rejectionReason`). Submit again. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "settlement.rejected", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "id": "e4f5a6b7-c8d9-4e0f-9a1b-2c3d4e5f6a7b", "status": "rejected", "periodFrom": "2026-11-01", "periodTo": "2026-11-30", "amount": "184500.00", "platformBankAccount": { "bank": "National Bank of Egypt", "iban": "EG380019000500000000263180002" }, "proof": null, "requestedAt": "2026-12-01T07:00:00.000Z", "updatedAt": "2026-12-01T07:00:00.000Z", "rejectionReason": "Amount doesn't match" } } ``` ### `settlement.cancelled` Wathiqaty cancelled the request. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "settlement.cancelled", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "id": "e4f5a6b7-c8d9-4e0f-9a1b-2c3d4e5f6a7b", "status": "cancelled", "periodFrom": "2026-11-01", "periodTo": "2026-11-30", "amount": "184500.00", "platformBankAccount": { "bank": "National Bank of Egypt", "iban": "EG380019000500000000263180002" }, "proof": null, "requestedAt": "2026-12-01T07:00:00.000Z", "updatedAt": "2026-12-01T07:00:00.000Z" } } ``` ### `package.updated` A package changed, in the portal or through the API. ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "package.updated", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "packageId": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13", "status": "active", "version": 8 } } ``` ### `bordereau.ready` An async bordereau export is ready (link valid 24 h). ```json { "id": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "type": "bordereau.ready", "createdAt": "2026-11-05T09:15:02.481Z", "environment": "sandbox", "apiVersion": "v1", "data": { "exportId": "9c0d1e2f-3a4b-4c5d-8e6f-7a8b9c0d1e2f", "url": "https://files.wathiqaty.com/exports/premium-2026-q3.csv", "expiresAt": "2026-11-06T09:15:00.000Z" } } ``` ## The request **Headers** ``` POST /wathiqaty/webhooks HTTP/1.1 Host: core.insurer.example Content-Type: application/json User-Agent: Wathiqaty-Webhooks/1 Wathiqaty-Event: policy.issued Wathiqaty-Delivery: 0d0a7c9b-8e6f-4a5b-9c3d-2e1f0a9b8c7d Wathiqaty-Signature: t=1793876102,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd ``` | Body field | Meaning | | --- | --- | | `id` | Event id. The same on every retry and redelivery: deduplicate on it. | | `type` | Event type, e.g. `policy.issued`. | | `createdAt` | When the event happened. | | `environment` | `sandbox` or `production`. | | `apiVersion` | `v1`. The shape of `data` follows this version. | | `data` | The resource as the API returns it, or a small object for lifecycle events. | ## Verifying signatures `Wathiqaty-Signature` holds a Unix timestamp `t` and one or two `v1` signatures (two while a secret is being rotated). Each `v1` is the hex HMAC-SHA256 of `{t}.{raw body}` with your endpoint's signing secret. 1. Parse `t` and every `v1` from the header. 2. Reject the request if `t` is more than 5 minutes from now (replay protection). Keep your clock in sync (NTP). 3. Compute the HMAC over the **raw bytes** of the body, as received. Parsing and re-serialising the JSON changes the bytes and breaks the signature. 4. Compare in constant time. Accept if any `v1` matches. **Node.js** ```js import { createHmac, timingSafeEqual } from "node:crypto" // rawBody: the request body as received (Buffer), before JSON parsing export function verify(rawBody, header, secret, now = Date.now() / 1000) { const parts = header.split(",").map((p) => p.split("=")) const t = Number(parts.find(([k]) => k === "t")?.[1]) if (!Number.isFinite(t) || Math.abs(now - t) > 300) return false const expected = createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest() return parts .filter(([k]) => k === "v1") .some(([, sig]) => { const got = Buffer.from(sig, "hex") return got.length === expected.length && timingSafeEqual(got, expected) }) } // Express: app.post("/wathiqaty/webhooks", express.raw({ type: "application/json" }), handler) ``` **Python** ```python import hashlib, hmac, time def verify(raw_body: bytes, header: str, secret: str) -> bool: parts = [p.split("=", 1) for p in header.split(",")] t = int(next(v for k, v in parts if k == "t")) if abs(time.time() - t) > 300: return False expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest() return any(hmac.compare_digest(v, expected) for k, v in parts if k == "v1") # Flask: verify(request.get_data(), request.headers["Wathiqaty-Signature"], SECRET) ``` **C#** ```csharp using System.Security.Cryptography; using System.Text; static bool Verify(byte[] rawBody, string header, string secret) { var parts = header.Split(',').Select(p => p.Split('=', 2)).ToList(); var t = long.Parse(parts.First(p => p[0] == "t")[1]); if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - t) > 300) return false; using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var expected = hmac.ComputeHash(Encoding.UTF8.GetBytes($"{t}.").Concat(rawBody).ToArray()); return parts.Where(p => p[0] == "v1").Any(p => CryptographicOperations.FixedTimeEquals(Convert.FromHexString(p[1]), expected)); } // ASP.NET Core: read Request.Body into a byte[] before model binding ``` ## Responding and retries - Answer `2xx` within **10 seconds**, then process the event asynchronously (queue it). The response body is ignored. - Anything else (non-2xx, timeout, TLS or DNS error) is retried with exponential backoff and jitter: about 1 min, 5 min, 30 min, 2 h, 6 h, then every 12 h, for up to **3 days**. - Redirects are not followed: register the final URL. - After 3 days of failures the endpoint is **disabled** and your technical contact is emailed. Re-enable it and redeliver the missed events from its delivery list. - Answer `2xx` to event types you don't handle; new types can be added within v1. ## Endpoint requirements - HTTPS with a valid certificate from a public CA, on a public hostname. Private, loopback and link-local addresses are refused. - Deliveries come from fixed IP addresses, published by your account manager, so you can allow-list them. - Secrets: each endpoint has its own. Rotate with [Rotate the signing secret](/reference/webhook-endpoints.md#rotate-webhook-secret); both secrets sign for 24 hours. --- # Sandbox The sandbox runs the same code as production with test data and no real money. Use it to build and to rehearse every flow before going live. - Your sandbox company mirrors your production setup (packages, rating factors, cancellation scales) as it was when the sandbox was created. Edit it freely. - Data is **reset every Sunday at 02:00 Cairo time**: policies, claims and cancellations you created are removed. Your clients and webhook endpoints are kept. - No emails or SMS reach real people. Payments are test payments. ## Playing the customer The [simulator routes](/reference/sandbox.md) act as the customer and fire the same webhooks as real activity: | To test | Call | | --- | --- | | Booking new policies | `POST /partner/sandbox/simulate/policy-sale` | | Cancellation decisions and refunds | `POST /partner/sandbox/simulate/cancellation-request` | | Claim handling | `POST /partner/sandbox/simulate/claim`, then `…/claim-resubmission` after you request documents | | Expiry and cancellation-scale tiers | `POST /partner/sandbox/clock` with `advanceDays` | ## Rehearsal scenarios 1. Sale → `policy.issued` → book → set your policy number. 2. Cancellation request → approve → `cancellation.completed` and `policy.cancelled`; check the refund matches your scale. 3. Claim → start review → request a document → resubmission → approve → settle → close. 4. Approve a claim above the remaining cover: expect `400 claim_not_payable`. 5. Make your webhook endpoint return `500` for an hour, then fix it: events are retried and nothing is lost. 6. Reconcile a week with the premium bordereau. --- # Versioning & changelog ## Versioning policy The major version is in the path: `/api/v1/…`. Within v1 we only make **additive** changes: - new endpoints and new optional request fields; - new response fields; - new enum values; - new webhook event types. So your client must ignore unknown response fields, handle unknown enum values gracefully, and answer `2xx` to unknown event types. A breaking change ships as `/v2`. v1 then stays available for at least **12 months**. The retirement date is announced in this changelog, emailed to every technical contact, and sent on v1 responses as `Deprecation` and `Sunset` headers. ## Changelog | Date | Change | | --- | --- | | 2026-11-05 | v1 draft published for review: authentication, company, packages, policies, cancellations, claims, finance, bordereaux, webhooks, sandbox simulator. | --- # Authentication > API reference · Wathiqaty Insurer API v1 (draft) Exchange your API client's credentials for a 15-minute bearer token (OAuth 2.0 client credentials, RFC 6749 §4.4). Cache it and renew it shortly before it expires. Base URL: `https://sandbox-api.wathiqaty.com/api/v1` (sandbox), `https://api.wathiqaty.com/api/v1` (production). - Get an access token: `POST /auth/partner/token` ## Get an access token `POST /auth/partner/token` - Auth: HTTP Basic (`client_id:client_secret`) Send the client id and secret with HTTP Basic auth. There is no refresh token: ask again with the same credentials. This route is limited to 10 calls a minute per client. ### Body (form-encoded) | Name | Type | Required | Description | | --- | --- | --- | --- | | `grant_type` | string | yes | Always `client_credentials`. One of: `client_credentials`. | | `scope` | string | no | Space-separated subset of the client's scopes. Omit it to get them all. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/auth/partner/token" \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials&scope=policies%3Aread+claims%3Aread+claims%3Awrite" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/auth/partner/token", { method: "POST", headers: { Authorization: "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64"), "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ "grant_type": "client_credentials", "scope": "policies:read claims:read claims:write" }), }) 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/auth/partner/token", auth=(client_id, client_secret), headers={ "Content-Type": "application/x-www-form-urlencoded", }, data={ "grant_type": "client_credentials", "scope": "policies:read claims:read claims:write" }, 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/auth/partner/token"); req.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"))); req.Content = new FormUrlEncodedContent(new Dictionary { ["grant_type"] = "client_credentials", ["scope"] = "policies:read claims:read claims:write", }); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 A token. ```json { "status": "success", "data": { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…", "tokenType": "Bearer", "expiresIn": 900, "scope": "policies:read claims:read claims:write" }, "error": null, "metadata": { "timestamp": "2026-11-05T09:15:02.481Z", "path": "/api/v1/auth/partner/token", "method": "POST", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 400 | `invalid_scope` | A requested scope isn't granted to this client. | | 401 | `invalid_client` | Unknown client, wrong secret, or the client is revoked. | | 403 | `ip_not_allowed` | The client has an IP allow-list and this IP isn't on it. | | 403 | `tenant_not_active` | Your company is suspended on Wathiqaty. | | 429 | `rate_limited` | More than 10 token requests a minute. Cache tokens. | Plus the generic errors any route can return: [Errors](/guides/errors.md). --- # Company > API reference · Wathiqaty Insurer API v1 (draft) Your company as Wathiqaty sees it. A cheap call to check a new token works. Base URL: `https://sandbox-api.wathiqaty.com/api/v1` (sandbox), `https://api.wathiqaty.com/api/v1` (production). - Get your company: `GET /partner/company` ## Get your company `GET /partner/company` - Auth: Bearer token, scope `any` ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/company" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/company", { 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/company", 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/company"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 Your company profile. ```json { "status": "success", "data": { "id": "a3c1e0f2-5b7d-4e19-8c2a-0f6d9b1e4a77", "name": { "en": "Demo Insurance", "ar": "ديمو للتأمين" }, "status": "active", "insuranceTypes": [ "individual_medical", "new_cars" ], "numbering": { "policyCardPrefix": "MC" }, "supportSettings": { "phone": "19000", "email": "care@insurer.example" } }, "error": null, "metadata": { "timestamp": "2026-11-05T09:15:02.481Z", "path": "/api/v1/partner/company", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` --- # Packages > API reference · Wathiqaty Insurer API v1 (draft) Packages (your products) are authored by your staff in the Wathiqaty portal, because benefits, wordings and documents need a human. Through the API your core system keeps the pricing and the on-sale state in sync. Price changes apply to quotes from the next request. Policies already sold keep the price they were sold at. Base URL: `https://sandbox-api.wathiqaty.com/api/v1` (sandbox), `https://api.wathiqaty.com/api/v1` (production). - List packages: `GET /partner/packages` - Get a package: `GET /partner/packages/{id}` - Get pricing rules: `GET /partner/packages/{id}/rating-rules` - Replace pricing rules: `PUT /partner/packages/{id}/rating-rules` - Put a package on sale: `POST /partner/packages/{id}/activate` - Take a package off sale: `POST /partner/packages/{id}/deactivate` - Set your product code: `PUT /partner/packages/{id}/external-reference` ## List packages `GET /partner/packages` - Auth: Bearer token, scope `packages:read` ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `insuranceType` | string | no | Line of business. One of: `individual_medical`, `new_cars`. | | `status` | string | no | Only `active` packages are sold. One of: `draft`, `active`, `inactive`. | | `updatedSince` | date-time | no | Only rows changed at or after this instant. Pass the `updatedAt` of the last row you processed (change feed). | | `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/packages" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/packages", { 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/packages", 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/packages"); 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 packages. ```json { "status": "success", "data": { "items": [ { "id": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13", "insuranceType": "individual_medical", "status": "active", "name": { "en": "Gold Care", "ar": "جولد كير" }, "externalReference": "PRD-GOLD-2026", "sumInsured": "250000.00", "readiness": { "sellable": true, "missing": [] }, "version": 7, "createdAt": "2026-09-01T08:00:00.000Z", "updatedAt": "2026-11-02T10:20:00.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/packages", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ## Get a package `GET /partner/packages/{id}` - Auth: Bearer token, scope `packages:read` `readiness.missing` lists what blocks activation. The `ETag` header (and `version`) is what to send as `If-Match` on writes. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The package's Wathiqaty id. | ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{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/packages/{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/packages/{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 package. ```json { "status": "success", "data": { "id": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13", "insuranceType": "individual_medical", "status": "active", "name": { "en": "Gold Care", "ar": "جولد كير" }, "externalReference": "PRD-GOLD-2026", "sumInsured": "250000.00", "readiness": { "sellable": true, "missing": [] }, "version": 7, "createdAt": "2026-09-01T08:00:00.000Z", "updatedAt": "2026-11-02T10:20:00.000Z" }, "error": null, "metadata": { "timestamp": "2026-11-05T09:15:02.481Z", "path": "/api/v1/partner/packages/5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Package doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Get pricing rules `GET /partner/packages/{id}/rating-rules` - Auth: Bearer token, scope `packages:read` Each rule matches a customer profile (`criteria`) and sets a tax-inclusive `price`; `loadings` add a percentage for specific answers. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The package's Wathiqaty id. | ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/rating-rules" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/rating-rules", { 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/packages/{id}/rating-rules", 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/packages/{id}/rating-rules"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The package's rules. ```json { "status": "success", "data": { "version": 7, "rules": [ { "id": "8a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d", "price": "9500.00", "criteria": [ { "factor": "age", "min": 18, "max": 35 }, { "factor": "gender", "values": [ "male", "female" ] } ], "loadings": [ { "factor": "pre_existing_condition", "value": "diabetes", "percent": "15.00" } ] }, { "id": "9b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d5e", "price": "12800.00", "criteria": [ { "factor": "age", "min": 36, "max": 50 } ], "loadings": [] } ] }, "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` | Package doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Replace pricing rules `PUT /partner/packages/{id}/rating-rules` - Auth: Bearer token, scope `packages:write` - `Idempotency-Key`: optional - `If-Match`: supported Replaces **all** rules of the package in one transaction. Rules not in the body are removed. Counts as 10 requests against your rate limit. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The package's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `rules` | object[] | yes | 1–500 rules: `{ price (decimal string), criteria[{ factor, min?, max?, values? }], loadings[{ factor, value, percent }] }`. | ### Request examples ```bash curl -X PUT "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/rating-rules" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -H "If-Match: W/"7"" \ -d '{"rules":[{"price":"9500.00","criteria":[{"factor":"age","min":18,"max":35},{"factor":"gender","values":["male","female"]}],"loadings":[{"factor":"pre_existing_condition","value":"diabetes","percent":"15.00"}]},{"price":"12800.00","criteria":[{"factor":"age","min":36,"max":50}],"loadings":[]}]}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/rating-rules", { method: "PUT", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), "If-Match": "W/\"7\"", }, body: JSON.stringify({ "rules": [ { "price": "9500.00", "criteria": [ { "factor": "age", "min": 18, "max": 35 }, { "factor": "gender", "values": [ "male", "female" ] } ], "loadings": [ { "factor": "pre_existing_condition", "value": "diabetes", "percent": "15.00" } ] }, { "price": "12800.00", "criteria": [ { "factor": "age", "min": 36, "max": 50 } ], "loadings": [] } ] }), }) 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/packages/{id}/rating-rules", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), "If-Match": "W/\"7\"", }, json={ "rules": [ { "price": "9500.00", "criteria": [ { "factor": "age", "min": 18, "max": 35 }, { "factor": "gender", "values": [ "male", "female" ] } ], "loadings": [ { "factor": "pre_existing_condition", "value": "diabetes", "percent": "15.00" } ] }, { "price": "12800.00", "criteria": [ { "factor": "age", "min": 36, "max": 50 } ], "loadings": [] } ] }, 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/packages/{id}/rating-rules"); 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( """ { "rules": [ { "price": "9500.00", "criteria": [ { "factor": "age", "min": 18, "max": 35 }, { "factor": "gender", "values": [ "male", "female" ] } ], "loadings": [ { "factor": "pre_existing_condition", "value": "diabetes", "percent": "15.00" } ] }, { "price": "12800.00", "criteria": [ { "factor": "age", "min": 36, "max": 50 } ], "loadings": [] } ] } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The new rules and the package's new version. ```json { "status": "success", "data": { "version": 8, "rules": [ { "id": "8a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d", "price": "9500.00", "criteria": [ { "factor": "age", "min": 18, "max": 35 }, { "factor": "gender", "values": [ "male", "female" ] } ], "loadings": [ { "factor": "pre_existing_condition", "value": "diabetes", "percent": "15.00" } ] }, { "id": "9b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d5e", "price": "12800.00", "criteria": [ { "factor": "age", "min": 36, "max": 50 } ], "loadings": [] } ] }, "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` | Package 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. | | 422 | `validation_error` | A rule is invalid (`details.fields`, e.g. `rules.3.price`). | | 422 | `unknown_rating_factor` | A factor or value isn't one your company has selected. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Put a package on sale `POST /partner/packages/{id}/activate` - Auth: Bearer token, scope `packages:write` `draft` or `inactive` → `active`, only when the package is complete. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The package's Wathiqaty id. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/activate" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/activate", { method: "POST", 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.post( "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/activate", 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.Post, "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/activate"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The package, now `active`. ```json { "status": "success", "data": { "id": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13", "insuranceType": "individual_medical", "status": "active", "name": { "en": "Gold Care", "ar": "جولد كير" }, "externalReference": "PRD-GOLD-2026", "sumInsured": "250000.00", "readiness": { "sellable": true, "missing": [] }, "version": 7, "createdAt": "2026-09-01T08:00:00.000Z", "updatedAt": "2026-11-02T10:20: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` | Package doesn't exist or isn't your company's. | | 422 | `package_incomplete` | Something is missing (`details.missing`, e.g. `rating_rules`, `terms_and_conditions`). | | 409 | `package_duplicate` | Another active package has the same policy and name. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Take a package off sale `POST /partner/packages/{id}/deactivate` - Auth: Bearer token, scope `packages:write` → `inactive`. Sold policies are not affected. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The package's Wathiqaty id. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/deactivate" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/deactivate", { method: "POST", 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.post( "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/deactivate", 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.Post, "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/deactivate"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The package, now `inactive`. ```json { "status": "success", "data": { "id": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13", "insuranceType": "individual_medical", "status": "inactive", "name": { "en": "Gold Care", "ar": "جولد كير" }, "externalReference": "PRD-GOLD-2026", "sumInsured": "250000.00", "readiness": { "sellable": true, "missing": [] }, "version": 7, "createdAt": "2026-09-01T08:00:00.000Z", "updatedAt": "2026-11-02T10:20: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` | Package doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Set your product code `PUT /partner/packages/{id}/external-reference` - Auth: Bearer token, scope `packages:write` Echoed on every policy sold from the package as `packageExternalReference`. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The package's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `externalReference` | string | yes | Your own reference, 1–100 characters, unique within your company. | ### Request examples ```bash curl -X PUT "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/external-reference" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"externalReference":"PRD-GOLD-2026"}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/external-reference", { method: "PUT", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ "externalReference": "PRD-GOLD-2026" }), }) const { data, error } = await res.json() if (!res.ok) throw new Error(`${res.status} ${error.code}`) ``` ```python import requests res = requests.put( "https://sandbox-api.wathiqaty.com/api/v1/partner/packages/{id}/external-reference", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", }, json={ "externalReference": "PRD-GOLD-2026" }, 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/packages/{id}/external-reference"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Content = new StringContent( """ { "externalReference": "PRD-GOLD-2026" } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The package. ```json { "status": "success", "data": { "id": "5e2b8c41-9d3a-4f67-b1c0-7a8e2d4f6b13", "insuranceType": "individual_medical", "status": "active", "name": { "en": "Gold Care", "ar": "جولد كير" }, "externalReference": "PRD-GOLD-2026", "sumInsured": "250000.00", "readiness": { "sellable": true, "missing": [] }, "version": 7, "createdAt": "2026-09-01T08:00:00.000Z", "updatedAt": "2026-11-02T10:20:00.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` | Package doesn't exist or isn't your company's. | | 409 | `conflict` | Another package of yours already has this reference. | Plus the generic errors any route can return: [Errors](/guides/errors.md). --- # 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=`. 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). --- # Cancellations > API reference · Wathiqaty Insurer API v1 (draft) A customer's cancellation request waits for your decision (`cancellation.requested`). The refund is quoted when the request is made, from the scale in force that day, and frozen on the request. Base URL: `https://sandbox-api.wathiqaty.com/api/v1` (sandbox), `https://api.wathiqaty.com/api/v1` (production). - List cancellations: `GET /partner/cancellations` - Get a cancellation: `GET /partner/cancellations/{id}` - Approve a cancellation: `POST /partner/cancellations/{id}/approve` - Reject a cancellation: `POST /partner/cancellations/{id}/reject` - Record a manual refund: `POST /partner/cancellations/{id}/manual-refund` ## List cancellations `GET /partner/cancellations` - Auth: Bearer token, scope `cancellations:read` ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `status` | string | no | Cancellation status. One of: `requested`, `refund_pending`, `completed`, `rejected`, `withdrawn`. | | `policyId` | uuid | no | On this policy. | | `updatedSince` | date-time | no | Only rows changed at or after this instant. Pass the `updatedAt` of the last row you processed (change feed). | | `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/cancellations" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/cancellations", { 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/cancellations", 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/cancellations"); 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 cancellations. ```json { "status": "success", "data": { "items": [ { "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" } ], "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/…", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ## Get a cancellation `GET /partner/cancellations/{id}` - Auth: Bearer token, scope `cancellations:read` ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The cancellation's Wathiqaty id. | ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/cancellations/{id}" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/cancellations/{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/cancellations/{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/cancellations/{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 cancellation with its frozen refund quote. ```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": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Cancellation doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Approve a cancellation `POST /partner/cancellations/{id}/approve` - Auth: Bearer token, scope `cancellations:write` - `Idempotency-Key`: optional `requested` → `refund_pending` (the refund is then sent through the gateway or recorded manually) or `completed` when nothing is refunded. The policy is cancelled when the cancellation completes. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The cancellation's Wathiqaty id. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/cancellations/{id}/approve" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/cancellations/{id}/approve", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Idempotency-Key": crypto.randomUUID(), }, }) 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/cancellations/{id}/approve", headers={ "Authorization": f"Bearer {access_token}", "Idempotency-Key": str(uuid.uuid4()), }, 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/cancellations/{id}/approve"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString()); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The cancellation. ```json { "status": "success", "data": { "id": "b7d4e1a9-2c3f-4a58-9e6b-0d1c2b3a4f5e", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "status": "refund_pending", "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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Cancellation doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Reject a cancellation `POST /partner/cancellations/{id}/reject` - Auth: Bearer token, scope `cancellations:write` - `Idempotency-Key`: optional ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The cancellation's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `reason` | string | yes | 3–2000 characters, shown to the customer. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/cancellations/{id}/reject" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"reason":"The policy is past the cancellation window in the wording."}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/cancellations/{id}/reject", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ "reason": "The policy is past the cancellation window in the wording." }), }) 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/cancellations/{id}/reject", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), }, json={ "reason": "The policy is past the cancellation window in the wording." }, 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/cancellations/{id}/reject"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString()); req.Content = new StringContent( """ { "reason": "The policy is past the cancellation window in the wording." } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The cancellation. ```json { "status": "success", "data": { "id": "b7d4e1a9-2c3f-4a58-9e6b-0d1c2b3a4f5e", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "status": "rejected", "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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Cancellation doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Record a manual refund `POST /partner/cancellations/{id}/manual-refund` - Auth: Bearer token, scope `cancellations:write` - `Idempotency-Key`: required You refunded the customer outside the payment gateway: `refund_pending` → `completed`. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The cancellation's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `reference` | string | yes | Your transfer reference, 1–255 characters. | | `paidAt` | date-time | yes | When the refund was paid. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/cancellations/{id}/manual-refund" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"reference":"TRX-88213","paidAt":"2026-11-22T10:00:00Z"}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/cancellations/{id}/manual-refund", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ "reference": "TRX-88213", "paidAt": "2026-11-22T10:00:00Z" }), }) 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/cancellations/{id}/manual-refund", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), }, json={ "reference": "TRX-88213", "paidAt": "2026-11-22T10:00:00Z" }, 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/cancellations/{id}/manual-refund"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString()); req.Content = new StringContent( """ { "reference": "TRX-88213", "paidAt": "2026-11-22T10:00:00Z" } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The cancellation, now `completed`. ```json { "status": "success", "data": { "id": "b7d4e1a9-2c3f-4a58-9e6b-0d1c2b3a4f5e", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "status": "completed", "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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Cancellation doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | Plus the generic errors any route can return: [Errors](/guides/errors.md). --- # Claims > API reference · Wathiqaty Insurer API v1 (draft) The full claim workflow: `submitted` → `under_review` ⇄ `documents_requested` → `approved` → `settled` → `closed`, or `rejected` → `closed`. The customer can withdraw before a decision. Your staff can work the same claims in the Wathiqaty portal at the same time; both see the same state. Base URL: `https://sandbox-api.wathiqaty.com/api/v1` (sandbox), `https://api.wathiqaty.com/api/v1` (production). - List claims: `GET /partner/claims` - Get a claim: `GET /partner/claims/{id}` - Set your claim number: `PUT /partner/claims/{id}/external-reference` - Start reviewing: `POST /partner/claims/{id}/start-review` - Request documents: `POST /partner/claims/{id}/document-requests` - Accept or reject a document: `POST /partner/claims/{id}/documents/{documentId}/review` - Preview an approval: `GET /partner/claims/{id}/approval-preview` - Approve a claim: `POST /partner/claims/{id}/approve` - Reject a claim: `POST /partner/claims/{id}/reject` - Mark a claim paid: `POST /partner/claims/{id}/settle` - Close a claim: `POST /partner/claims/{id}/close` - List claim messages: `GET /partner/claims/{id}/messages` - Message the customer: `POST /partner/claims/{id}/messages` ## List claims `GET /partner/claims` - Auth: Bearer token, scope `claims:read` Drafts are never visible to you. ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `status` | string | no | Claim status. One of: `submitted`, `under_review`, `documents_requested`, `approved`, `rejected`, `settled`, `closed`, `withdrawn`. | | `policyId` | uuid | no | On this policy. | | `categoryId` | uuid | no | Claim category. | | `insuranceType` | string | no | Line of business. One of: `individual_medical`, `new_cars`. | | `updatedSince` | date-time | no | Only rows changed at or after this instant. Pass the `updatedAt` of the last row you processed (change feed). | | `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/claims" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims", { 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/claims", 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/claims"); 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 claims. ```json { "status": "success", "data": { "items": [ { "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" } ], "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/…", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ## Get a claim `GET /partner/claims/{id}` - Auth: Bearer token, scope `claims:read` With items, documents (download from `url`), open document requests and `policyCoverage` (what is left of the sum insured). ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{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/claims/{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/claims/{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 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": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Claim doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Set your claim number `PUT /partner/claims/{id}/external-reference` - Auth: Bearer token, scope `claims:write` - `If-Match`: supported ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `externalReference` | string | yes | Your own reference, 1–100 characters, unique within your company. | ### Request examples ```bash curl -X PUT "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/external-reference" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "If-Match: W/"7"" \ -d '{"externalReference":"CL/2026/000981"}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/external-reference", { method: "PUT", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "If-Match": "W/\"7\"", }, body: JSON.stringify({ "externalReference": "CL/2026/000981" }), }) const { data, error } = await res.json() if (!res.ok) throw new Error(`${res.status} ${error.code}`) ``` ```python import requests res = requests.put( "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/external-reference", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "If-Match": "W/\"7\"", }, json={ "externalReference": "CL/2026/000981" }, 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/claims/{id}/external-reference"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("If-Match", "W/\"7\""); req.Content = new StringContent( """ { "externalReference": "CL/2026/000981" } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The claim. ```json { "status": "success", "data": { "id": "c9e8d7f6-1a2b-4c3d-8e4f-5a6b7c8d9e0f", "number": "CLM-00000107", "externalReference": "CL/2026/000981", "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": "PUT", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Claim 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. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Start reviewing `POST /partner/claims/{id}/start-review` - Auth: Bearer token, scope `claims:write` `submitted` → `under_review`. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/start-review" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/start-review", { method: "POST", 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.post( "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/start-review", 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.Post, "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/start-review"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 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": "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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Claim doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Request documents `POST /partner/claims/{id}/document-requests` - Auth: Bearer token, scope `claims:write` - `Idempotency-Key`: optional → `documents_requested` (or adds to the open round). The customer is notified and resubmits (`claim.resubmitted`). ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `requests` | object[] | yes | 1–20 of `{ requirementKey \| name (1–255), note?, dueDate? }`. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/document-requests" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"requests":[{"name":"Itemised hospital invoice","note":"Stamped by the hospital","dueDate":"2026-12-15"}]}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/document-requests", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ "requests": [ { "name": "Itemised hospital invoice", "note": "Stamped by the hospital", "dueDate": "2026-12-15" } ] }), }) 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/claims/{id}/document-requests", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), }, json={ "requests": [ { "name": "Itemised hospital invoice", "note": "Stamped by the hospital", "dueDate": "2026-12-15" } ] }, 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/claims/{id}/document-requests"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString()); req.Content = new StringContent( """ { "requests": [ { "name": "Itemised hospital invoice", "note": "Stamped by the hospital", "dueDate": "2026-12-15" } ] } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 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": "documents_requested", "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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Claim doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Accept or reject a document `POST /partner/claims/{id}/documents/{documentId}/review` - Auth: Bearer token, scope `claims:write` Rejecting reopens a request for it and moves `under_review` → `documents_requested`. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | | `documentId` | uuid | yes | The document's id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `decision` | string | yes | Your decision. One of: `accepted`, `rejected`. | | `reason` | string | no | 3–2000 characters. Required to reject. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/documents/{documentId}/review" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"decision":"rejected","reason":"The report is not signed."}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/documents/{documentId}/review", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ "decision": "rejected", "reason": "The report is not signed." }), }) 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/claims/{id}/documents/{documentId}/review", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", }, json={ "decision": "rejected", "reason": "The report is not signed." }, 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/claims/{id}/documents/{documentId}/review"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Content = new StringContent( """ { "decision": "rejected", "reason": "The report is not signed." } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Claim or document doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Preview an approval `GET /partner/claims/{id}/approval-preview` - Auth: Bearer token, scope `claims:read` What the customer would be paid after the deductible, and what would remain of the cover. Changes nothing. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `approvedAmount` | decimal | yes | Amount you intend to approve, > 0. | ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/approval-preview?approvedAmount=" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/approval-preview?approvedAmount=", { 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/claims/{id}/approval-preview?approvedAmount=", 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/claims/{id}/approval-preview?approvedAmount="); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The preview. ```json { "status": "success", "data": { "deductibleApplied": "0.00", "payableAmount": "4200.00", "remainingAfter": "245800.00" }, "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` | Claim doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Approve a claim `POST /partner/claims/{id}/approve` - Auth: Bearer token, scope `claims:write` - `Idempotency-Key`: optional `under_review` → `approved`. Itemised claims list **every** item once (an item below its claimed amount needs a `reason`); other claims send `approvedAmount`. Every document must be reviewed first, and the total can't exceed what is left of the sum insured. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `items` | object[] | no | Itemised claims: `{ lineId, approvedAmount, reason? }` for every item. | | `approvedAmount` | decimal | no | Other claims: > 0 and ≤ the claimed amount. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/approve" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"items":[{"lineId":"l1","approvedAmount":"700.00"},{"lineId":"l2","approvedAmount":"3000.00","reason":"Network price for MRI is 3000"}]}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/approve", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ "items": [ { "lineId": "l1", "approvedAmount": "700.00" }, { "lineId": "l2", "approvedAmount": "3000.00", "reason": "Network price for MRI is 3000" } ] }), }) 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/claims/{id}/approve", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), }, json={ "items": [ { "lineId": "l1", "approvedAmount": "700.00" }, { "lineId": "l2", "approvedAmount": "3000.00", "reason": "Network price for MRI is 3000" } ] }, 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/claims/{id}/approve"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString()); req.Content = new StringContent( """ { "items": [ { "lineId": "l1", "approvedAmount": "700.00" }, { "lineId": "l2", "approvedAmount": "3000.00", "reason": "Network price for MRI is 3000" } ] } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The claim, now `approved`. ```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": "approved", "incidentDate": "2026-12-01", "claimedAmount": "4200.00", "approvedAmount": "3700.00", "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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Claim doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | | 400 | `documents_pending` | A document is still pending (`details.pendingDocumentIds`). | | 400 | `claim_not_payable` | Over the remaining cover (`details.remaining`, `details.sumInsured`, `details.consumed`). | | 422 | `validation_error` | Wrong form for the claim, or items missing or repeated. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Reject a claim `POST /partner/claims/{id}/reject` - Auth: Bearer token, scope `claims:write` - `Idempotency-Key`: optional ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `reason` | string | yes | 3–2000 characters, shown to the customer. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/reject" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"reason":"The treatment is excluded under section 4.2 of the wording."}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/reject", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ "reason": "The treatment is excluded under section 4.2 of the wording." }), }) 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/claims/{id}/reject", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), }, json={ "reason": "The treatment is excluded under section 4.2 of the wording." }, 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/claims/{id}/reject"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString()); req.Content = new StringContent( """ { "reason": "The treatment is excluded under section 4.2 of the wording." } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The claim, now `rejected`. ```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": "rejected", "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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Claim doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Mark a claim paid `POST /partner/claims/{id}/settle` - Auth: Bearer token, scope `claims:write` - `Idempotency-Key`: optional `approved` → `settled`, with your payment reference. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `settlementReference` | string | yes | 1–255 characters. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/settle" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"settlementReference":"PAY-2026-55120"}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/settle", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ "settlementReference": "PAY-2026-55120" }), }) 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/claims/{id}/settle", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), }, json={ "settlementReference": "PAY-2026-55120" }, 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/claims/{id}/settle"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString()); req.Content = new StringContent( """ { "settlementReference": "PAY-2026-55120" } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The claim, now `settled`. ```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": "settled", "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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Claim doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Close a claim `POST /partner/claims/{id}/close` - Auth: Bearer token, scope `claims:write` `settled` or `rejected` → `closed`. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/close" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/close", { method: "POST", 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.post( "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/close", 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.Post, "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/close"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The claim, now `closed`. ```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": "closed", "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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Claim doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## List claim messages `GET /partner/claims/{id}/messages` - Auth: Bearer token, scope `claims:read` ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `after` | uuid | no | Messages after this message id. | | `limit` | integer | no | 1–100. Default 50. | ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/messages" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/messages", { 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/claims/{id}/messages", 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/claims/{id}/messages"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 Messages, oldest first. ```json { "status": "success", "data": [ { "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e", "author": "customer", "body": "I uploaded the stamped invoice.", "attachments": [], "createdAt": "2026-12-03T09:00:00.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` | Claim doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Message the customer `POST /partner/claims/{id}/messages` - Auth: Bearer token, scope `claims:write` A public reply on the claim. Retries with the same `clientMessageId` don't post twice. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The claim's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `body` | string | yes | 1–5000 characters. | | `clientMessageId` | uuid | no | Your id for the message, for safe retries. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/messages" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"body":"Thank you, we are reviewing the invoice.","clientMessageId":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/claims/{id}/messages", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ "body": "Thank you, we are reviewing the invoice.", "clientMessageId": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f" }), }) 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/claims/{id}/messages", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", }, json={ "body": "Thank you, we are reviewing the invoice.", "clientMessageId": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f" }, 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/claims/{id}/messages"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Content = new StringContent( """ { "body": "Thank you, we are reviewing the invoice.", "clientMessageId": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f" } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 201 The message. ```json { "status": "success", "data": { "id": "4d5e6f7a-8b9c-4d0e-9f1a-2b3c4d5e6f7a", "author": "insurer", "body": "Thank you, we are reviewing the invoice.", "attachments": [], "createdAt": "2026-12-03T09:05: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` | Claim doesn't exist or isn't your company's. | | 400 | `bad_request` | The claim is closed or withdrawn. | Plus the generic errors any route can return: [Errors](/guides/errors.md). --- # Finance & settlements > API reference · Wathiqaty Insurer API v1 (draft) Every captured payment freezes its breakdown: gross (tax-inclusive) = net + taxes, and Wathiqaty's commission is a share of the net. Commission accrues on the ledger at capture and is reversed in proportion on refunds. Wathiqaty requests settlement of the balance periodically; you pay and upload the proof. Base URL: `https://sandbox-api.wathiqaty.com/api/v1` (sandbox), `https://api.wathiqaty.com/api/v1` (production). - List captured payments: `GET /partner/finance/payments` - List commission ledger entries: `GET /partner/finance/ledger` - Get the commission balance: `GET /partner/finance/balance` - List settlements: `GET /partner/commission-settlements` - Get a settlement: `GET /partner/commission-settlements/{id}` - Get an upload URL for the proof: `POST /partner/commission-settlements/{id}/proof-upload-url` - Submit payment proof: `POST /partner/commission-settlements/{id}/proof` ## List captured payments `GET /partner/finance/payments` - Auth: Bearer token, scope `finance:read` ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `paidFrom` | date | no | Captured on or after (Cairo date). | | `paidTo` | date | no | Captured on or before (Cairo date). | | `policyId` | uuid | no | For this policy. | | `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/finance/payments" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/finance/payments", { 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/finance/payments", 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/finance/payments"); 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 payments. ```json { "status": "success", "data": { "items": [ { "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "policyId": "6f1c2d3e-4b5a-4c69-8d7e-1f2a3b4c5d6e", "paidAt": "2026-11-05T09:15:00.000Z", "gross": "9500.00", "taxes": [ { "name": "VAT", "rate": "14.00", "amount": "1166.67" } ], "net": "8333.33", "commission": "833.33", "insurerNet": "7500.00", "refunded": "0.00" } ], "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/…", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ## List commission ledger entries `GET /partner/finance/ledger` - Auth: Bearer token, scope `finance:read` Append-only: `accrual` (+), `reversal` (−), `settlement_paid` (−). ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `from` | date | no | On or after. | | `to` | date | no | On or before. | | `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/finance/ledger" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/finance/ledger", { 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/finance/ledger", 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/finance/ledger"); 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 entries. ```json { "status": "success", "data": { "items": [ { "id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b", "type": "accrual", "amount": "833.33", "paymentId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "createdAt": "2026-11-05T09:15:01.000Z" }, { "id": "6f7a8b9c-0d1e-4f2a-9b3c-4d5e6f7a8b9c", "type": "reversal", "amount": "-620.27", "paymentId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "createdAt": "2026-11-22T10:00:01.000Z" } ], "meta": { "page": 1, "size": 20, "totalItems": 2, "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false } }, "error": null, "metadata": { "timestamp": "2026-11-05T09:15:02.481Z", "path": "/api/v1/partner/…", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ## Get the commission balance `GET /partner/finance/balance` - Auth: Bearer token, scope `finance:read` ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/finance/balance" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/finance/balance", { 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/finance/balance", 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/finance/balance"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 What you owe Wathiqaty now. ```json { "status": "success", "data": { "commissionDue": "184500.00", "currency": "EGP", "lastSettledAt": "2026-11-03T12:00:00.000Z" }, "error": null, "metadata": { "timestamp": "2026-11-05T09:15:02.481Z", "path": "/api/v1/partner/…", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ## List settlements `GET /partner/commission-settlements` - Auth: Bearer token, scope `finance:read` ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `status` | string | no | Settlement status. One of: `requested`, `proof_submitted`, `confirmed`, `rejected`, `cancelled`. | | `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/commission-settlements" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/commission-settlements", { 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/commission-settlements", 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/commission-settlements"); 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 settlements. ```json { "status": "success", "data": { "items": [ { "id": "e4f5a6b7-c8d9-4e0f-9a1b-2c3d4e5f6a7b", "status": "requested", "periodFrom": "2026-11-01", "periodTo": "2026-11-30", "amount": "184500.00", "platformBankAccount": { "bank": "National Bank of Egypt", "iban": "EG380019000500000000263180002" }, "proof": null, "requestedAt": "2026-12-01T07:00:00.000Z", "updatedAt": "2026-12-01T07:00:00.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/…", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ## Get a settlement `GET /partner/commission-settlements/{id}` - Auth: Bearer token, scope `finance:read` ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The settlement's Wathiqaty id. | ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/commission-settlements/{id}" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/commission-settlements/{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/commission-settlements/{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/commission-settlements/{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 settlement. ```json { "status": "success", "data": { "id": "e4f5a6b7-c8d9-4e0f-9a1b-2c3d4e5f6a7b", "status": "requested", "periodFrom": "2026-11-01", "periodTo": "2026-11-30", "amount": "184500.00", "platformBankAccount": { "bank": "National Bank of Egypt", "iban": "EG380019000500000000263180002" }, "proof": null, "requestedAt": "2026-12-01T07:00:00.000Z", "updatedAt": "2026-12-01T07:00:00.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` | Settlement doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Get an upload URL for the proof `POST /partner/commission-settlements/{id}/proof-upload-url` - Auth: Bearer token, scope `finance:write` Returns a presigned POST. Send the file straight to `url` as `multipart/form-data` with every entry of `fields` first, then `file`. PDF, PNG or JPEG, up to 10 MB, within 15 minutes. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The settlement's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `fileName` | string | yes | Original file name. | | `contentType` | string | yes | MIME type. One of: `application/pdf`, `image/png`, `image/jpeg`. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/commission-settlements/{id}/proof-upload-url" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"fileName":"transfer.pdf","contentType":"application/pdf"}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/commission-settlements/{id}/proof-upload-url", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ "fileName": "transfer.pdf", "contentType": "application/pdf" }), }) 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/commission-settlements/{id}/proof-upload-url", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", }, json={ "fileName": "transfer.pdf", "contentType": "application/pdf" }, 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/commission-settlements/{id}/proof-upload-url"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Content = new StringContent( """ { "fileName": "transfer.pdf", "contentType": "application/pdf" } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 201 Where to upload. ```json { "status": "success", "data": { "url": "https://uploads.wathiqaty.com/", "fields": { "key": "settlements/e4f5…/transfer.pdf", "Policy": "eyJ…", "X-Amz-Signature": "…" }, "fileKey": "settlements/e4f5…/transfer.pdf", "expiresAt": "2026-12-02T09:15: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` | Settlement doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Submit payment proof `POST /partner/commission-settlements/{id}/proof` - Auth: Bearer token, scope `finance:write` - `Idempotency-Key`: optional `requested` (or `rejected`) → `proof_submitted`. Wathiqaty confirms or rejects it (`settlement.confirmed` / `settlement.rejected`). ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The settlement's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `fileKey` | string | yes | `fileKey` from the upload URL, after uploading. | | `transferReference` | string | yes | Bank transfer reference. | | `transferredAt` | date-time | yes | When you paid. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/commission-settlements/{id}/proof" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"fileKey":"settlements/e4f5…/transfer.pdf","transferReference":"NBE-0098812","transferredAt":"2026-12-02T08:00:00Z"}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/commission-settlements/{id}/proof", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ "fileKey": "settlements/e4f5…/transfer.pdf", "transferReference": "NBE-0098812", "transferredAt": "2026-12-02T08:00:00Z" }), }) 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/commission-settlements/{id}/proof", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), }, json={ "fileKey": "settlements/e4f5…/transfer.pdf", "transferReference": "NBE-0098812", "transferredAt": "2026-12-02T08:00:00Z" }, 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/commission-settlements/{id}/proof"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString()); req.Content = new StringContent( """ { "fileKey": "settlements/e4f5…/transfer.pdf", "transferReference": "NBE-0098812", "transferredAt": "2026-12-02T08:00:00Z" } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The settlement. ```json { "status": "success", "data": { "id": "e4f5a6b7-c8d9-4e0f-9a1b-2c3d4e5f6a7b", "status": "proof_submitted", "periodFrom": "2026-11-01", "periodTo": "2026-11-30", "amount": "184500.00", "platformBankAccount": { "bank": "National Bank of Egypt", "iban": "EG380019000500000000263180002" }, "proof": null, "requestedAt": "2026-12-01T07:00:00.000Z", "updatedAt": "2026-12-01T07: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` | Settlement doesn't exist or isn't your company's. | | 400 | `bad_request` | Not allowed in the current status (`details.from`, `details.to`). | | 422 | `file_not_uploaded` | Nothing was uploaded under `fileKey`. | Plus the generic errors any route can return: [Errors](/guides/errors.md). --- # Bordereaux > API reference · Wathiqaty Insurer API v1 (draft) CSV files for reconciliation, or for companies that integrate by file first. UTF-8 with a BOM so Excel keeps Arabic names intact. A nightly SFTP drop of the same files is available on request. Base URL: `https://sandbox-api.wathiqaty.com/api/v1` (sandbox), `https://api.wathiqaty.com/api/v1` (production). - Premium bordereau: `GET /partner/bordereaux/premium` - Claims bordereau: `GET /partner/bordereaux/claims` ## Premium bordereau `GET /partner/bordereaux/premium` - Auth: Bearer token, scope `finance:read` One row per policy issued or cancelled in the range. Columns: `policy_id, policy_number, external_reference, package_external_reference, insurance_type, event, event_at, coverage_starts_at, coverage_ends_at, holder_name, holder_national_id, sum_insured, gross_premium, taxes, net_premium, commission, insurer_net, refund_amount, commission_reversed`. Ranges over 93 days answer `202` with an export id; the file follows in `bordereau.ready`. ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `from` | date | yes | First Cairo date, inclusive. | | `to` | date | yes | Last Cairo date, inclusive. | ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/bordereaux/premium?from=&to=" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/bordereaux/premium?from=&to=", { 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/bordereaux/premium?from=&to=", 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/bordereaux/premium?from=&to="); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The CSV file. ```csv policy_id,policy_number,external_reference,event,event_at,gross_premium,net_premium,commission,insurer_net 6f1c2d3e-…,POL-00000042,MI/2026/MED/004213,issued,2026-11-05T09:15:00Z,9500.00,8333.33,833.33,7500.00 ``` ### Errors | Status | Code | When | | --- | --- | --- | | 422 | `validation_error` | `to` before `from`, or a bad date. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Claims bordereau `GET /partner/bordereaux/claims` - Auth: Bearer token, scope `finance:read` One row per claim that changed status in the range: `claim_id, claim_number, external_reference, policy_number, status, status_at, claimed_amount, approved_amount, payable_amount, settlement_reference`. ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `from` | date | yes | First Cairo date, inclusive. | | `to` | date | yes | Last Cairo date, inclusive. | ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/bordereaux/claims?from=&to=" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/bordereaux/claims?from=&to=", { 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/bordereaux/claims?from=&to=", 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/bordereaux/claims?from=&to="); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The CSV file. ```csv claim_id,claim_number,policy_number,status,status_at,claimed_amount,approved_amount c9e8d7f6-…,CLM-00000107,POL-00000042,approved,2026-12-04T13:00:00Z,4200.00,3700.00 ``` ### Errors | Status | Code | When | | --- | --- | --- | | 422 | `validation_error` | `to` before `from`, or a bad date. | Plus the generic errors any route can return: [Errors](/guides/errors.md). --- # Webhook endpoints > API reference · Wathiqaty Insurer API v1 (draft) Where Wathiqaty sends events (see the Webhooks guide for payloads and signatures). Up to 5 endpoints per environment. Base URL: `https://sandbox-api.wathiqaty.com/api/v1` (sandbox), `https://api.wathiqaty.com/api/v1` (production). - List endpoints: `GET /partner/webhook-endpoints` - Create an endpoint: `POST /partner/webhook-endpoints` - Update an endpoint: `PATCH /partner/webhook-endpoints/{id}` - Delete an endpoint: `DELETE /partner/webhook-endpoints/{id}` - Rotate the signing secret: `POST /partner/webhook-endpoints/{id}/rotate-secret` - Send a test event: `POST /partner/webhook-endpoints/{id}/test` - List deliveries: `GET /partner/webhook-endpoints/{id}/deliveries` - Redeliver an event: `POST /partner/webhook-deliveries/{id}/redeliver` ## List endpoints `GET /partner/webhook-endpoints` - Auth: Bearer token, scope `webhooks:manage` ### Request examples ```bash curl -X GET "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints", { 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/webhook-endpoints", 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/webhook-endpoints"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 Your endpoints. ```json { "status": "success", "data": [ { "id": "f0e1d2c3-b4a5-4968-8776-655443322110", "url": "https://core.insurer.example/wathiqaty/webhooks", "events": [ "policy.issued", "policy.cancelled", "cancellation.requested" ], "enabled": true, "description": "Core policy system", "createdAt": "2026-11-01T10:00:00.000Z" } ], "error": null, "metadata": { "timestamp": "2026-11-05T09:15:02.481Z", "path": "/api/v1/partner/…", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ## Create an endpoint `POST /partner/webhook-endpoints` - Auth: Bearer token, scope `webhooks:manage` - `Idempotency-Key`: optional The response carries `signingSecret` **once**. Store it in your secret manager. ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Public `https://` URL. Private and loopback addresses are refused. | | `events` | string[] | yes | Event types, or `["*"]` for all, current and future. | | `description` | string | no | Up to 255 characters. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"url":"https://core.insurer.example/wathiqaty/webhooks","events":["policy.issued","policy.cancelled","cancellation.requested"],"description":"Core policy system"}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ "url": "https://core.insurer.example/wathiqaty/webhooks", "events": [ "policy.issued", "policy.cancelled", "cancellation.requested" ], "description": "Core policy system" }), }) 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/webhook-endpoints", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), }, json={ "url": "https://core.insurer.example/wathiqaty/webhooks", "events": [ "policy.issued", "policy.cancelled", "cancellation.requested" ], "description": "Core policy system" }, 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/webhook-endpoints"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString()); req.Content = new StringContent( """ { "url": "https://core.insurer.example/wathiqaty/webhooks", "events": [ "policy.issued", "policy.cancelled", "cancellation.requested" ], "description": "Core policy system" } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 201 The endpoint and its signing secret. ```json { "status": "success", "data": { "id": "f0e1d2c3-b4a5-4968-8776-655443322110", "url": "https://core.insurer.example/wathiqaty/webhooks", "events": [ "policy.issued", "policy.cancelled", "cancellation.requested" ], "enabled": true, "description": "Core policy system", "createdAt": "2026-11-01T10:00:00.000Z", "signingSecret": "whsec_5f3c9a…" }, "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 | `validation_error` | Not https, a private address, or an unknown event type. | | 409 | `webhook_endpoint_limit` | You already have 5 endpoints in this environment. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Update an endpoint `PATCH /partner/webhook-endpoints/{id}` - Auth: Bearer token, scope `webhooks:manage` Re-enabling an endpoint that was disabled after failures does not resend old events; redeliver them from the delivery list. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The endpoint's Wathiqaty id. | ### Body (JSON) | Name | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | New URL. | | `events` | string[] | no | Replaces the list. | | `enabled` | boolean | no | Pause or resume deliveries. | | `description` | string | no | Up to 255 characters. | ### Request examples ```bash curl -X PATCH "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"enabled":false}' ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}", { method: "PATCH", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ "enabled": false }), }) const { data, error } = await res.json() if (!res.ok) throw new Error(`${res.status} ${error.code}`) ``` ```python import requests res = requests.patch( "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}", headers={ "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", }, json={ "enabled": False }, 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.Patch, "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); req.Content = new StringContent( """ { "enabled": false } """, Encoding.UTF8, "application/json"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The endpoint. ```json { "status": "success", "data": { "id": "f0e1d2c3-b4a5-4968-8776-655443322110", "url": "https://core.insurer.example/wathiqaty/webhooks", "events": [ "policy.issued", "policy.cancelled", "cancellation.requested" ], "enabled": false, "description": "Core policy system", "createdAt": "2026-11-01T10:00:00.000Z" }, "error": null, "metadata": { "timestamp": "2026-11-05T09:15:02.481Z", "path": "/api/v1/partner/…", "method": "PATCH", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Endpoint doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Delete an endpoint `DELETE /partner/webhook-endpoints/{id}` - Auth: Bearer token, scope `webhooks:manage` ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The endpoint's Wathiqaty id. | ### Request examples ```bash curl -X DELETE "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}", { method: "DELETE", 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.delete( "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{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.Delete, "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 204 Deleted. No body. ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Endpoint doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Rotate the signing secret `POST /partner/webhook-endpoints/{id}/rotate-secret` - Auth: Bearer token, scope `webhooks:manage` For 24 hours every delivery carries two signatures (old and new), so you can deploy the new secret without dropping events. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The endpoint's Wathiqaty id. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}/rotate-secret" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}/rotate-secret", { method: "POST", 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.post( "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}/rotate-secret", 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.Post, "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}/rotate-secret"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 The new secret, shown once. ```json { "status": "success", "data": { "signingSecret": "whsec_a91e0b…", "previousSecretExpiresAt": "2026-11-06T09: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" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Endpoint doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Send a test event `POST /partner/webhook-endpoints/{id}/test` - Auth: Bearer token, scope `webhooks:manage` Sends a signed `ping` event now and reports what your endpoint answered. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The endpoint's Wathiqaty id. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}/test" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}/test", { method: "POST", 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.post( "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}/test", 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.Post, "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}/test"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 200 Your endpoint's answer. ```json { "status": "success", "data": { "deliveryId": "0d0a7c9b-8e6f-4a5b-9c3d-2e1f0a9b8c7d", "status": 200, "latencyMs": 182 }, "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` | Endpoint doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## List deliveries `GET /partner/webhook-endpoints/{id}/deliveries` - Auth: Bearer token, scope `webhooks:manage` The last 30 days of attempts, newest first. ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The endpoint's Wathiqaty id. | ### Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `eventType` | string | no | Only this event type. | | `failed` | boolean | no | Only attempts that didn't get a 2xx. | | `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/webhook-endpoints/{id}/deliveries" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-endpoints/{id}/deliveries", { 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/webhook-endpoints/{id}/deliveries", 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/webhook-endpoints/{id}/deliveries"); 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 delivery attempts. ```json { "status": "success", "data": { "items": [ { "id": "0d0a7c9b-8e6f-4a5b-9c3d-2e1f0a9b8c7d", "eventId": "evt_7f6c1b0e-3c2d-4c84-9a0e-2f3b1c5d7e90", "eventType": "policy.issued", "attempt": 2, "status": 503, "latencyMs": 10000, "responseSnippet": "Service Unavailable", "nextAttemptAt": "2026-11-05T09:51:00.000Z", "createdAt": "2026-11-05T09:21:00.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/…", "method": "GET", "requestId": "8d7e2c1a-41f0-4a4e-9a55-6f0b3c2d1e77" } } ``` ### Errors | Status | Code | When | | --- | --- | --- | | 404 | `not_found` | Endpoint doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). ## Redeliver an event `POST /partner/webhook-deliveries/{id}/redeliver` - Auth: Bearer token, scope `webhooks:manage` Sends the event again now, with the same event `id` (so your dedupe still works). ### Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | uuid | yes | The delivery's Wathiqaty id. | ### Request examples ```bash curl -X POST "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-deliveries/{id}/redeliver" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ```js const res = await fetch("https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-deliveries/{id}/redeliver", { method: "POST", 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.post( "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-deliveries/{id}/redeliver", 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.Post, "https://sandbox-api.wathiqaty.com/api/v1/partner/webhook-deliveries/{id}/redeliver"); req.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}"); using var res = await http.SendAsync(req); var json = await res.Content.ReadAsStringAsync(); res.EnsureSuccessStatusCode(); ``` ### Response 202 Queued. ```json { "status": "success", "data": { "deliveryId": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d" }, "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` | Delivery doesn't exist or isn't your company's. | Plus the generic errors any route can return: [Errors](/guides/errors.md). --- # 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" } } ```