Base URL & environments
| Environment | Base URL | Notes |
|---|---|---|
| Sandbox | https://teduhkhairat.my/api/v1 | Client IDs prefixed tk_test_; demo data only. |
| Production | https://api.teduhkhairat.my/v1 | Issued after partner onboarding & security review. |
This is a demonstration environment. All member, claim and payment data is fictitious.
Authentication
Exchange your client credentials for a short-lived bearer token (1 hour). Send it as Authorization: Bearer <token> on every request.
curl -X POST https://teduhkhairat.my/api/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{"grant_type":"client_credentials","client_id":"tk_test_…","client_secret":"sk_test_…","scope":"members:read claims:read"}'
{ "access_token": "…", "token_type": "Bearer", "expires_in": 3600, "scope": "members:read claims:read" }
Scopes
| Scope | Grants |
|---|---|
members:read | List and read members within your organisation scope |
members:write | Register members (status PENDING_PAYMENT until first payment) |
certificates:read | Verify takaful certificate status |
claims:read / claims:write | Read claims; update claim status & settlement (takaful providers) |
payments:read / payments:write | Payment history; bulk import from legacy systems |
Partner clients bound to an organisation only ever see that organisation's data.
Rate limits
Each client has a per-minute limit (shown in the console). Responses include X-RateLimit-Limit. Exceeding it returns 429 TOO_MANY_REQUESTS; retry with exponential backoff.
Idempotency
For every POST, send a unique Idempotency-Key header. Repeating a request with the same key returns the original result (header Idempotent-Replay: true) and never creates duplicates.
Errors
HTTP/1.1 403
{ "error": "insufficient_scope", "message": "insufficient_scope" }
| Status | Meaning |
|---|---|
| 400 | Validation error (invalid_phone, invalid_ic, …) |
| 401 | invalid_client / invalid_token |
| 403 | insufficient_scope |
| 404 | Not found or outside your scope |
| 409 | Conflict, e.g. member_exists |
| 429 | Rate limited |
Versioning
The major version is in the path (/v1) and echoed in X-API-Version. Additive changes ship without notice; breaking changes get a new major version with at least 6 months' overlap.
Members
/v1/members?updated_since=2026-01-01members:read/v1/members/{member_no}members:read/v1/membersmembers:writecurl -X POST https://teduhkhairat.my/api/v1/members \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-H "Idempotency-Key: 8f1c2d6e-…" \
-d '{"name":"Ahmad bin Ali","ic":"800101-10-1234","phone":"0123344556","state":"Selangor","scheme":"SKM-KWP"}'
{ "data": { "member_no": "TDH-2026-000215", "status": "PENDING_PAYMENT" } }
Certificates
/v1/certificates/{certificate_no}certificates:read{ "data": { "certificate_no": "TKF-88231045", "member_no": "TDH-2025-000128", "scheme": "Pelan Teduh Keluarga",
"benefit_amount": 10000, "provider": "Amanah Takaful Berhad", "status": "IN_FORCE", "valid_until": "2027-08-02T…" } }
Claims
/v1/claims?status=SUBMITTEDclaims:read/v1/claims/{ref}/statusclaims:write{ "status": "SETTLED", "settled_amount": 8500, "reason": "Approved by claims committee" }
// status: UNDER_REVIEW | PENDING_INFO | SETTLED | REJECTED
Webhooks & signatures
Register an HTTPS endpoint per client. Events: member.activated, payment.succeeded, case.created, case.verified, claim.created, claim.updated. Failed deliveries retry with backoff for 24 hours.
Each delivery carries X-Teduh-Signature: t=<unix>,v1=<hex>, an HMAC-SHA256 of "<t>.<raw body>" using your webhook secret. Reject requests older than 5 minutes.
// Node.js verification
const [t, v1] = header.split(',').map(p => p.split('=')[1]);
const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(`${t}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected)) && Date.now() / 1000 - Number(t) < 300;
Live example: /api/v1/webhooks/signature-example
Powered by Khairat MyKasih · DKSB · Sandbox credentials are issued from the Teduh console (API & Integrations).