MENA Insurtech Integration Guide

Integrate the Takaful Calculator API

Request indicative contribution estimates for six coverage types, choose a supported Wakalah model, and present the bilingual results in a Node.js or React / Next.js application.

Endpoint and authentication

Send GET /api/v1/takaful/calculate with an API key that has the finance:takaful scope. Every response is wrapped in data and meta; API errors use an error object. Keep the key on your server, and do not expose it in browser code or a NEXT_PUBLIC_* variable.

bash
curl --get "https://khaleejiapi.dev/api/v1/takaful/calculate" \
--data-urlencode "type=health" \
--data-urlencode "coverage=100000" \
--data-urlencode "currency=AED" \
--data-urlencode "country=AE" \
--data-urlencode "model=wakalah_mudarabah" \
--data-urlencode "age=30" \
-H "Authorization: Bearer $KHALEEJI_API_KEY"

Parameters and coverage types

ParameterRequired / defaultAccepted values and use
typeRequiredhealth, motor, property, life, travel, business
coverageRequiredPositive sum-insured amount in the selected currency
modelwakalah_mudarabahwakalah or wakalah_mudarabah
currency / countryAED / AECurrency: AED, SAR, QAR, BHD, OMR, KWD, USD, EUR, GBP. Country: AE, SA, QA, BH, OM, KW.
age / dependents30 / 0Age applies to health and life; dependents apply to health.
vehicle_age0Vehicle age in years for motor coverage.
property_typeresidentialresidential or commercial.
trip_days / destination7 / gccDestination: gcc, asia, europe, americas, africa. Travel coverage.
employees / industry1 / low_riskIndustry: low_risk, medium_risk, high_risk. Business coverage.

To discover the currently returned models, types, and country regulators, call the same endpoint with ?models=true. A standalone mudaraba model is not available; Mudaraba profit sharing is represented only by the hybrid model.

Wakalah models and estimate fields

wakalah applies a 25% operator fee to the estimated annual contribution. wakalah_mudarabah applies a 20% Wakalah fee and reports a 40% Mudaraba share of investment profits. The API does not forecast investment returns or calculate the operator's realized profit share; mudarabahSharePercent is model metadata. For either model, the endpoint calculates the estimate from coverage and applicable type, country, and risk factors.

For a health estimate with coverage=100000, country=AE, and age 30, both models produce a 4% effective rate and an annual contribution of AED 4,000. The model changes the fee allocation, not the estimated total:

ModelAnnual contributionWakalah feeRisk poolMudarabah share
wakalahAED 4,000AED 1,000 (25%)AED 3,000Not applicable
wakalah_mudarabahAED 4,000AED 800 (20%)AED 3,20040% of investment profits

The calculation uses an annual base rate for each type, applies the relevant adjustment factors, and multiplies by coverage. Monthly contribution is the annual amount divided by 12. The returned effectiveRate is a percentage (for example, 4 means 4%), while estimatedSurplus is a simplified estimate of 12% of the risk-pool contribution—not an actual distribution or guarantee.

Base annual rates before adjustments: health 4%, motor 2.5%, property 0.2%, life 1.5%, travel 0.8%, and business 3.5%. Country and coverage-specific factors can change the effective rate.

json
{
"data": {
"estimate": {
"type": "health",
"coverageAmount": 100000,
"currency": "AED",
"country": "AE",
"annualContribution": 4000,
"monthlyContribution": 333.33,
"wakalahFee": 800,
"riskPoolContribution": 3200,
"estimatedSurplus": 384,
"effectiveRate": 4
},
"model": {
"id": "wakalah_mudarabah",
"name": "Wakalah-Mudarabah Hybrid",
"nameAr": "نموذج الوكالة-المضاربة الهجين",
"description": "Combines agency fee for underwriting with profit-sharing on investments.",
"descriptionAr": "يجمع بين رسوم الوكالة للاكتتاب ومشاركة الأرباح في الاستثمارات.",
"wakalahFeePercent": 20,
"mudarabahSharePercent": 40
},
"adjustments": [{ "factor": "Age loading", "value": 1 }],
"regulator": {
"name": "Central Bank of the UAE (CBUAE)",
"nameAr": "مصرف الإمارات المركزي"
},
"disclaimer": {
"en": "This is an indicative estimate only. Actual takaful contributions depend on individual risk assessment, medical underwriting, and operator-specific pricing. Please consult a licensed takaful operator.",
"ar": "هذا تقدير إرشادي فقط. تعتمد مساهمات التكافل الفعلية على تقييم المخاطر الفردي والاكتتاب الطبي وتسعير المشغل. يرجى استشارة مشغل تكافل مرخص."
}
},
"meta": { "timestamp": "2026-10-22T11:17:00.000Z" }
}

The model, regulator, and disclaimer contain Arabic and English fields. Query parameter names and enum values are language-neutral: send canonical values such as type=health and model=wakalah, not translated labels. Localize your own form labels and choose the appropriate name / nameAr and disclaimer.en / disclaimer.ar for display.

Node.js / TypeScript service

This server-side helper accepts coverage inputs, checks for an API error, and returns the data object. Install no additional packages; it uses the Node.js fetch API.

typescript
const apiKey = process.env.KHALEEJI_API_KEY
if (!apiKey) throw new Error("KHALEEJI_API_KEY is not configured")
type TakafulEstimate = {
estimate: {
annualContribution: number
monthlyContribution: number
wakalahFee: number
riskPoolContribution: number
estimatedSurplus: number
effectiveRate: number
currency: string
}
model: {
id: "wakalah" | "wakalah_mudarabah"
name: string
nameAr: string
wakalahFeePercent: number
mudarabahSharePercent: number | null
}
disclaimer: { en: string; ar: string }
}
export async function calculateTakaful(
input: { type: string; coverage: number; country?: string; currency?: string; model?: string },
): Promise<TakafulEstimate> {
const url = new URL("https://khaleejiapi.dev/api/v1/takaful/calculate")
for (const [key, value] of Object.entries(input)) {
if (value !== undefined) url.searchParams.set(key, String(value))
}
const response = await fetch(url, {
headers: { Authorization: "Bearer " + apiKey, Accept: "application/json" },
cache: "no-store",
signal: AbortSignal.timeout(10_000),
})
const payload = (await response.json()) as {
data?: TakafulEstimate
error?: { message?: string }
}
if (!response.ok || !payload.data) {
throw new Error(payload.error?.message ?? `Takaful API returned HTTP ${response.status}`)
}
return payload.data
}
const quote = await calculateTakaful({
type: "health",
coverage: 100000,
country: "AE",
currency: "AED",
model: "wakalah_mudarabah",
})
console.log(quote.estimate.annualContribution, quote.model.nameAr)

Secure Next.js and bilingual React UI

Keep the credential in a server-only environment variable. Authenticate and rate-limit users of your own route as appropriate for your application; then use that route from the browser. The proxy below forwards only documented query parameters and never sends the API key to the client.

typescript
// app/api/takaful/route.ts
import { NextResponse } from "next/server"
const parameterNames = [
"type", "coverage", "currency", "country", "model", "age", "dependents",
"vehicle_age", "property_type", "trip_days", "destination", "employees", "industry",
]
export async function GET(request: Request) {
const apiKey = process.env.KHALEEJI_API_KEY
if (!apiKey) {
return NextResponse.json(
{ error: { code: "SERVER_CONFIG", message: "Takaful estimates are unavailable" } },
{ status: 500 },
)
}
const incoming = new URL(request.url)
const upstreamUrl = new URL("https://khaleejiapi.dev/api/v1/takaful/calculate")
for (const name of parameterNames) {
const value = incoming.searchParams.get(name)
if (value !== null) upstreamUrl.searchParams.set(name, value)
}
try {
const upstream = await fetch(upstreamUrl, {
headers: { Authorization: "Bearer " + apiKey, Accept: "application/json" },
cache: "no-store",
})
const body: unknown = await upstream.json()
return NextResponse.json(body, { status: upstream.status })
} catch {
return NextResponse.json(
{ error: { code: "UPSTREAM_ERROR", message: "Could not reach the Takaful service" } },
{ status: 502 },
)
}
}

React form with Arabic / English labels

Pass the user's selected locale to this client component. It sets right-to-left direction for Arabic and displays the matching model name and disclaimer; the request itself still uses canonical API values.

tsx
"use client"
import { useState, type FormEvent } from "react"
type Locale = "en" | "ar"
type Model = "wakalah" | "wakalah_mudarabah"
type Quote = {
data?: {
estimate: { annualContribution: number; currency: string }
model: { name: string; nameAr: string; wakalahFeePercent: number; mudarabahSharePercent: number | null }
disclaimer: { en: string; ar: string }
}
error?: { message?: string }
}
export function TakafulQuoteForm({ locale = "en" }: { locale?: Locale }) {
const [model, setModel] = useState<Model>("wakalah_mudarabah")
const [coverage, setCoverage] = useState("100000")
const [quote, setQuote] = useState<Quote["data"]>()
const [message, setMessage] = useState("")
const [loading, setLoading] = useState(false)
const isArabic = locale === "ar"
async function submit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
setLoading(true)
setQuote(undefined)
setMessage("")
try {
const query = new URLSearchParams({ type: "health", coverage, country: "AE", currency: "AED", model })
const response = await fetch(`/api/takaful?${query}`)
const result = (await response.json()) as Quote
if (!response.ok || !result.data) {
throw new Error(result.error?.message ?? "Request failed")
}
setQuote(result.data)
} catch {
setMessage(isArabic ? "تعذر الحصول على التقدير. حاول مرة أخرى." : "Could not get an estimate. Please try again.")
} finally {
setLoading(false)
}
}
return (
<form onSubmit={submit} dir={isArabic ? "rtl" : "ltr"} className="space-y-4">
<label className="block">
<span>{isArabic ? "مبلغ التغطية (درهم)" : "Coverage amount (AED)"}</span>
<input
type="number"
min="0.01"
step="0.01"
required
value={coverage}
onChange={(event) => setCoverage(event.target.value)}
/>
</label>
<label className="block">
<span>{isArabic ? "نموذج التكافل" : "Takaful model"}</span>
<select value={model} onChange={(event) => setModel(event.target.value as Model)}>
<option value="wakalah">{isArabic ? "الوكالة" : "Wakalah"}</option>
<option value="wakalah_mudarabah">{isArabic ? "الوكالة والمضاربة" : "Wakalah-Mudarabah"}</option>
</select>
</label>
<button type="submit" disabled={loading}>
{loading ? (isArabic ? "جارٍ الحساب…" : "Calculating…") : (isArabic ? "احسب التقدير" : "Get estimate")}
</button>
{message && <p role="alert">{message}</p>}
{quote && (
<section aria-live="polite">
<h2>{isArabic ? quote.model.nameAr : quote.model.name}</h2>
<p>
{isArabic ? "المساهمة السنوية التقديرية: " : "Estimated annual contribution: "}
{new Intl.NumberFormat(isArabic ? "ar-AE" : "en-AE", {
style: "currency",
currency: quote.estimate.currency,
}).format(quote.estimate.annualContribution)}
</p>
<p>{isArabic ? quote.disclaimer.ar : quote.disclaimer.en}</p>
</section>
)}
</form>
)
}

Errors and operational handling

  • Handle non-2xx responses and the API's error.code / error.message fields.
  • Common validation errors include missing coverage, unsupported currency or country, and an unknown model or type.
  • Respect the returned X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
  • Present the bilingual estimate disclaimer and let a licensed operator provide any binding quote.