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.
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
| Parameter | Required / default | Accepted values and use |
|---|---|---|
| type | Required | health, motor, property, life, travel, business |
| coverage | Required | Positive sum-insured amount in the selected currency |
| model | wakalah_mudarabah | wakalah or wakalah_mudarabah |
| currency / country | AED / AE | Currency: AED, SAR, QAR, BHD, OMR, KWD, USD, EUR, GBP. Country: AE, SA, QA, BH, OM, KW. |
| age / dependents | 30 / 0 | Age applies to health and life; dependents apply to health. |
| vehicle_age | 0 | Vehicle age in years for motor coverage. |
| property_type | residential | residential or commercial. |
| trip_days / destination | 7 / gcc | Destination: gcc, asia, europe, americas, africa. Travel coverage. |
| employees / industry | 1 / low_risk | Industry: 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:
| Model | Annual contribution | Wakalah fee | Risk pool | Mudarabah share |
|---|---|---|---|---|
| wakalah | AED 4,000 | AED 1,000 (25%) | AED 3,000 | Not applicable |
| wakalah_mudarabah | AED 4,000 | AED 800 (20%) | AED 3,200 | 40% 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.
{ "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.
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.
// app/api/takaful/route.tsimport { 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.
"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.messagefields. - 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, andX-RateLimit-Resetheaders. - Present the bilingual estimate disclaimer and let a licensed operator provide any binding quote.