Create a task
POST /create-task: every request field, the two server-side defaults, and the per-payer special cases.
POST /create-taskQueues one eligibility check and returns immediately with a task id. The portal work happens afterwards; nothing about the patient's coverage is known when this call returns.
Request
Prop
Type
Only tpa_name is really required
The handler reads the body key by key rather than validating it as a whole, so a missing
id_value is not rejected: the check simply runs with nothing to search for and fails later. Send
tpa_name, id_type, id_value and visit_type on every request and you will not meet this.
{
"success": true,
"message": "Task created successfully",
"data": {
"task_id": "00000000-0000-4000-a000-000000000001",
"status": "pending",
"organization_api_task_db": {
"task_id_in_db": "00000000-0000-4000-a000-000000000001",
"status": "IN_QUEUE"
}
}
}Response
Prop
Type
organization_api_task_db is internal bookkeeping that predates this API. It
carries the same id as task_id; you can ignore the whole object.
Choosing an identifier
Send exactly one identifier, and tell the API which kind it is with id_type.
Support varies by payer; the TPA matrix has the per-payer list.
Emirates ID
The default, and the only identifier every payer accepts.
{ "id_type": "EMIRATESID", "id_value": "784-0000-0000000-0" }The value must start with 784. This is checked before the task is created, so
a bad prefix costs you nothing:
{
"success": false,
"message": "Emirates ID does not start with 784-",
"data": null
}Hyphens are accepted. Nothing else about the format is validated at create
time: a well-formed but non-existent Emirates ID will be accepted, run, and
come back as member_not_found.
Member ID or card number
{ "id_type": "CARDNUMBER", "id_value": "MBR-0000000" }Use this when the patient has their insurance card but their Emirates ID is not
linked at the payer. AXA (INS010) expects the full seven-segment card number
here; most other payers accept the member id as printed.
Two rules govern it, and neither is enforced at create time:
- The payer has to support it. Only some payers can search on a card number; the rest treat whatever you send as an Emirates ID and find nobody. The TPA matrix marks which is which.
- A search-all accepts it. Prefer the Emirates ID for a sweep when you have one. See Search-all, member ID below.
DHA member ID
{ "id_type": "DHAMEMBERID", "id_value": "0000000000" }The regulator-issued member id. Supported by NAS, Neuron, Nextcare, Al Madallah, MedNet, AXA, NGI and Lifeline.
Policy number
{ "id_type": "POLICYNUMBER", "id_value": "POL-0000000" }Supported by Nextcare and Lifeline only. Least reliable of the four: a policy number can match several members, and the portal will pick one.
Some payers overrule you
Al Madallah inspects the shape of id_value and may reclassify it, so a value sent as
CARDNUMBER can be looked up as a DHA member id. The result tells you what was actually used.
Single TPA versus search all
If you know the payer, name it. If you do not, send one of three sentinels and
the platform will ask the aggregators first and then fan out across every payer
your clinic has credentials for. A search-all that carries
credentials asks DHPO only, and checks only the payers it
sent logins for.
tpa_name | What it does |
|---|---|
DHPO | Asks the DHPO / eClaim aggregator, then fans out. |
RIYATI | Asks the Riyati aggregator, then fans out. |
BOTH | Asks both aggregators, then fans out. The usual choice. |
Daman is never part of a search-all. If the patient may be on Daman you have to
ask for INS026, TPA023, D004, D002 or INS078 by name.
Prefer an Emirates ID for a search-all
A sentinel accepts any id_type and the sweep runs either way. The Emirates ID is the better
identifier here whenever you have one, because it is the only identifier every payer accepts, so
the search has the most to match on. If the patient has only their card, send that: you will still
get an answer.
A search-all create response carries two extra keys:
{
"success": true,
"message": "Task created successfully",
"data": {
"task_id": "00000000-0000-4000-a000-000000000001",
"status": "pending",
"organization_api_task_db": {
"task_id_in_db": "00000000-0000-4000-a000-000000000001",
"status": "SEARCH_ALL_PROCESSING"
},
"is_search_all": true,
"search_all_status": "SEARCH_ALL_PROCESSING"
}
}From there the flow is the same, except that you watch
search_all_status rather than status, and the
result carries aggregated_results instead of eligibility_result.
Visit type
visit_type defaults to OUTPATIENT. Each payer accepts its own list and
nothing is validated at create time, so an unsupported value produces a task
that runs and then fails at the portal. The TPA matrix has every
accepted value per payer.
Two payers deserve a note:
- MedNet (
TPA036) encodes the consultation type and the benefit in this one field. A bare benefit token means outpatient plus that benefit (DENTAL= outpatient dental); prefix it withINPATIENT_for the inpatient variant. - Daman uses a much longer service taxonomy (consultation reasons, ultrasound trimesters, PCSP screening packages) rather than the usual inpatient / outpatient split.
Optional patient fields
name, dob and phone are optional and are used only by payers whose portal
search needs more than an identifier, or which print them onto a generated
claim form. Sending them costs nothing and occasionally rescues a lookup that
would otherwise fail.
doctorId matters only where the check generates a claim form. FMC (TPA010)
is the clearest case. Sending it on every check is fine: payers that do not need
it ignore it. Note the camelCase: it is the one field on this endpoint that is
not snake_case.
It is the clinician's professional licence ID, the one issued by DHA, Riayati or Shafafiya. You hold the mapping from your clinicians to their licence IDs on your side; there is no endpoint here that returns it. A name you compose yourself will not match the portal's physician list, and at an Abu Dhabi practice a failed match fails the check rather than falling back.
doctorName is the same field, still accepted
This field was originally called doctorName, which is why you will meet that spelling in older
integrations and in anything the Mozart extension sends. It always carried the id, never a name.
Both keys are accepted and mean the same thing; if you send both, doctorId wins. New
integrations should send doctorId.
extra_args
A free-form object for per-payer extras. Nothing in it is validated, so a
misspelled key is accepted, the check runs, and the payer answers a question you
did not mean to ask. The server also adds its own request_start_time key to
whatever you send, so do not rely on getting the object back unchanged.
Every extra is an ordinary key on the object:
{
"extra_args": {
"maternity_treatment": true,
"is_member_present_at_the_facility": true
}
}The older title / value pair still works
maternity_treatment used to be readable only as a title naming the extra plus a value
answering it, and the key was ignored. The key is now read first, and the pair is still accepted
unchanged, so existing integrations do not have to move. Send one or the other; if you send both,
the key wins. Build new integrations against the key.
The documented extras today. Every one of them is optional, and none is validated, so the column that matters is the last one: what the payer does when the key is absent is what you get by default.
| Extra | Shape | Payers | Omitted |
|---|---|---|---|
maternity_treatmentAnswers the portal’s "Is IP Maternity" question. Send it whenever visit_type is MATERNITY: omitting it is not an error, it silently checks outpatient maternity cover. | "maternity_treatment": true | false | TPA001, TPA004 | the portal answers as though you had said no |
is_member_present_at_the_facilitySets the portal’s member-presence radio. Optional; the check runs either way, but some plans price a not-present encounter differently. | "is_member_present_at_the_facility": true | false | TPA001, TPA004 | the portal is told the member is present |
exception_caseTicks the portal’s exception checkbox, which submits the check without a referral. Absent means true, so send false when you want the portal’s own referral list searched and a real referral attached. | "exception_case": true | false | TPA001, TPA004 | the portal’s bypass checkbox is ticked and no referral is resolved |
referral_document_urlA fetchable URL to the referral letter. The scraper downloads it and attaches it on the portal’s referral upload field. | "referral_document_url": | TPA001, TPA004 | no referral document is uploaded |
referring_physicianThe clinician who referred the patient. NAS and Neuron match it against the portal’s referring-physician list; Daman sends it as referralClinicianLicense, and only on a referral consultation. | "referring_physician": | TPA001, TPA004, TPA023, INS026, INS078, D002, D004 | no referring clinician is recorded on the check |
referring_departmentThe department that referred the patient, matched against the portal’s referral list on gatekeeper plans. Ignored on every other plan, so sending it always is safe. | "referring_department": | TPA002 | a gatekeeper plan completes with a warning and no referral selected |
service_typeThe service the claim form is raised for. The value is typed into the portal’s service search, so it has to read as the portal spells it. | "service_type": "Consultation GP" | "Consultation Specialist" | TPA029, TPA037 | the claim form is filled with Consultation GP |
use_dentalRuns AXA’s dental flow. Compared against the exact string "YES", so send that and not a boolean. visit_type: "DENTAL" on its own does nothing here. | "use_dental": "YES" | "NO" | INS010 | the ordinary non-dental flow runs, whatever visit_type says |
referral_codeThe payer’s own referral code, typed into the Aafiya portal’s referral field. Only useful when the patient arrives with one. | "referral_code": | TPA026 | the portal’s referral code field is left blank |
visit_categoryADNIC’s consultation-request category. Unlike every other extra, a value outside this list fails the check rather than being ignored. | "visit_category": "FIRST_VISIT" | "VISIT_WITHOUT_REFERRAL" | "VISIT_WITH_REFERRAL" | INS017 | the consultation request is submitted as FIRST_VISIT |
use_metlifeSwitches the MedNet portal to MetLife before the search. Send "YES" as a string; anything else falls back to MedNet. | "use_metlife": "YES" | "NO" | TPA036 | the member is looked up in MedNet’s own book |
use_dubaicareSwitches the MedNet portal to DubaiCare before the search. Ignored when use_metlife is "YES", which is checked first. | "use_dubaicare": "YES" | "NO" | TPA036 | the member is looked up in MedNet’s own book |
is_emergencyTicks MedNet’s emergency registration. Independent of visit_type, which at MedNet carries the consultation type and benefit instead. | "is_emergency": true | false | TPA036 | the encounter is registered as an ordinary visit |
is_podMarks the encounter as a POD dispense. Send pod_id with it; the product refuses to submit a POD check without one. | "is_pod": true | false | TPA023, INS026, INS078, D002, D004 | isPOD is left off the payload entirely, which is not the same as false |
pod_idThe point-of-dispense identifier. Meaningless without is_pod: true, and a POD check without it is what the product blocks at the form. | "pod_id": | TPA023, INS026, INS078, D002, D004 | podId is left off the payload |
is_maternityFlags the encounter as maternity. Only applied when the doctor licence on the request resolves to a maternity specialty, so send doctorId alongside it or it is silently dropped. | "is_maternity": true | false | TPA023, INS026, INS078, D002, D004 | isMaternity is left off the payload |
free_followup_or_extended_followupPre-answers Daman’s follow-up business rules so a blocked check retries as a follow-up or extended follow-up visit. Checked before not_related_to_chief_complaint. | "free_followup_or_extended_followup": true | false | TPA023, INS026, INS078, D002, D004 | a business-rule violation ends the check instead of being answered |
not_related_to_chief_complaintPre-answers the same rules as a visit for a different chief complaint. Ignored when free_followup_or_extended_followup is also true. | "not_related_to_chief_complaint": true | false | TPA023, INS026, INS078, D002, D004 | a business-rule violation ends the check instead of being answered |
Three things about the shapes are worth reading twice, because none of them produces an error when you get it wrong:
- A boolean is not always a boolean.
use_dental,use_metlifeanduse_dubaicareare compared against the literal string"YES", so a JSONtruereads as no.is_emergency,exception_caseand the Daman flags do take a real boolean. visit_categoryis the one extra with an enum behind it, and the only one where a wrong value fails the check rather than being ignored.is_maternityneedsdoctorId. Daman applies it only when the clinician licence on the request resolves to a maternity specialty; without one the flag is read and dropped.
credentials
By default a check runs under the portal logins Mantys holds for the clinic
named by X-Clinic-ID. An integration that keeps those logins on its own side
can send them on the request instead. When credentials is present, the
portals it names use the supplied login for this check only. On a search-all
(BOTH, DHPO, RIYATI) it also sets the scope: only the portals it names
are checked. The DHPO lookup still runs on the stored DHPO login and narrows
them; Riayati is skipped, and no other stored login is used.
The Sending credentials page walks through the whole
contract with a full request.
Key the object by TPA code. A portal with one login takes an object; a portal
with several takes a list, and license_number is only read by payers that
need it:
{
"tpa_name": "BOTH",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "OUTPATIENT",
"credentials": {
"TPA004": { "username": "sample.user@sampleclinic.com", "password": "..." },
"INS012": [
{ "username": "sample.user.a@sampleclinic.com", "password": "...", "license_number": "MOH-F-0000000" },
{ "username": "sample.user.b@sampleclinic.com", "password": "...", "license_number": "DHA-F-0000000" }
]
}
}For a single-TPA request you can send one login without the key, and it
applies to tpa_name:
{
"tpa_name": "TPA004",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "OUTPATIENT",
"credentials": { "username": "sample.user@sampleclinic.com", "password": "..." }
}Unlike extra_args, this object is validated, and a bad shape is rejected with
a 400 before any task is created:
- every login needs a non-empty
usernameandpassword;license_numberis the only other key accepted - keys must be TPA codes:
BOTH,DHPOandRIYATIname a fan-out, not a portal, so they are not accepted as keys - the single-login shorthand is only accepted when
tpa_nameis one portal, because there is nothing for it to belong to on a search-all request
The logins are not stored
credentials is removed from the body before anything is persisted. It does not appear in
request_dump on /task-status or /eligibility-result, and it is not written to logs. A wrong
password still surfaces the way it does for stored logins: the check fails with an invalid
credentials error for that portal.
Worked combinations
Every combination below returns 200 and creates a task. Only the answer tells them apart, so they are worth reading before you meet one in production. The Try it walkthrough flags each of them as you build a request, and Common search scenarios collects them on one page you can send from the browser.
Named payer, Emirates IDSend this
The baseline every other example is a delta from. The 784 prefix is checked before the task is created, so a mistyped ID costs you nothing.
{
"tpa_name": "TPA004",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "OUTPATIENT"
}Search-all, Emirates IDSend this
When you do not know the payer. The create response carries is_search_all and search_all_status, and the result carries aggregated_results instead of eligibility_result. Daman is never in the fan-out.
{
"tpa_name": "BOTH",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "OUTPATIENT"
}Named payer, member ID or card numberSend this
The retry when the Emirates ID is not linked at the payer yet, which is common for a newly issued card or a dependant added mid-policy. Only the payers whose scraper reads id_type can do this; the TPA matrix lists them.
{
"tpa_name": "TPA004",
"id_type": "CARDNUMBER",
"id_value": "MBR-0000000",
"visit_type": "OUTPATIENT"
}Search-all, member ID or card numberWorth knowing
Accepted, and the sweep runs. Reach for it when you do not know the payer and the patient has their card but not their Emirates ID. Send the Emirates ID instead when you have one: it is the only identifier every payer accepts, so a sweep has the most to match on. Daman is never included in a search-all whichever identifier you send.
{
"tpa_name": "BOTH",
"id_type": "CARDNUMBER",
"id_value": "MBR-0000000",
"visit_type": "OUTPATIENT"
}Daman, where visit_type is not inpatient or outpatientNeeds a payer-specific value
Every other payer takes some variation of OUTPATIENT and INPATIENT. Daman takes a service taxonomy instead: consultation reasons, ultrasound trimesters, dental fee models, and two flows that are not visits at all (VALIDITY_CHECK and BABY_FLOW). Nothing is validated at create time, so OUTPATIENT is accepted here and then fails at the portal. Pick the value from the Daman row of the TPA matrix.
{
"tpa_name": "TPA023",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "CONSULTATION_ELECTIVE"
}Dental where the payer lists itSend this
Six payers take DENTAL as an ordinary visit_type, as do all three search-all sentinels. Everywhere else it is not on the list and a dental encounter goes in as OUTPATIENT, which costs you nothing: visit_type decides which benefit the payer prices and which portal flow runs, not what comes back, and the response carries the whole benefit schedule either way. Select the dental benefit from it yourself. AXA (INS010) is the exception in both directions: it lists DENTAL and ignores it, and its dental flow is reached through extra_args instead.
{
"tpa_name": "TPA002",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "DENTAL"
}Dental at Daman, where DENTAL is not a valueNeeds a payer-specific value
Daman calls it DENTAL_SERVICES, and Daman Thiqa splits that again into a capitation programme and two fee-for-service tiers, so the value decides which dental scheme the portal is asked about. MedNet is the other payer that splits it, there by setting: DENTAL is the outpatient one and INPATIENT_DENTAL the inpatient. Read the value off the payer matrix rather than mapping your own "dental" to one value everywhere.
{
"tpa_name": "TPA023",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "DENTAL_SERVICES_ROUTINE_FFS"
}Maternity at NAS or NeuronNeeds a payer-specific value
Both payers ask whether the maternity treatment is inpatient, and nothing makes you answer. Omitting maternity_treatment is not an error: the portal answers as though you had said no, and you get outpatient maternity cover back without being told.
{
"tpa_name": "TPA004",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "MATERNITY",
"extra_args": {
"maternity_treatment": true,
"is_member_present_at_the_facility": true
}
}Dental at AXA, where visit_type is not read at allNeeds a payer-specific value
AXA is the payer that makes the general rule about visit_type false. Its scraper never reads the field: the dental flow is switched on by extra_args.use_dental, compared against the literal string "YES", and it needs a card or DHA member ID because the dental search is not on the Emirates ID form. visit_type is left as DENTAL here because it costs nothing and keeps your own records honest.
{
"tpa_name": "INS010",
"id_type": "CARDNUMBER",
"id_value": "MBR-0000000",
"visit_type": "DENTAL",
"extra_args": {
"use_dental": "YES"
}
}NAS or Neuron with a real referralWorth knowing
The default at both JET payers is to tick the portal’s exception checkbox and submit with no referral, which is what you get by sending nothing. This is the other path: exception_case: false makes the scraper search the portal’s referral list, referral_document_url uploads the letter you already hold, and referring_physician names the clinician who wrote it.
{
"tpa_name": "TPA004",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "OUTPATIENT",
"extra_args": {
"exception_case": false,
"referral_document_url": "https://files.example.com/referral.pdf",
"referring_physician": "DHA-P-0000000"
}
}A Daman referral consultationNeeds a payer-specific value
CONSULTATION_REFERRAL is the one Daman visit type that carries a referring clinician, and referring_physician is where the licence goes: it becomes referralClinicianLicense on the eligibility payload. On any other Daman visit type the key is read and dropped, so pairing the two is the whole rule.
{
"tpa_name": "INS026",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "CONSULTATION_REFERRAL",
"extra_args": {
"referring_physician": "DHA-P-0000000"
}
}Maternity at Daman, where there is no MATERNITYNeeds a payer-specific value
Daman has no MATERNITY value. An antenatal scan goes in as the trimester it falls in, which your system knows from the gestational age and the appointment type does not; a delivery goes in as INPATIENT; a newborn not yet on the policy goes through BABY_FLOW. MATERNITY here is accepted at create time and fails at the portal, and maternity_treatment is a NAS and Neuron question that Daman never reads.
{
"tpa_name": "INS026",
"id_type": "EMIRATESID",
"id_value": "784-0000-0000000-0",
"visit_type": "ULTRASOUND_SECOND_TRIMESTER"
}mode and test_mode
The handler reads two further keys, mode (default "prod") and test_mode
(default false).
Do not send these yet
They are accepted, but what they do downstream is not settled enough to be documented as a contract. Leave them out; they will be specified here or removed once the behaviour is confirmed with the API owner.
When a check comes back empty
Three situations look like a failure but are really a hint to retry differently.
Send BOTH. It costs more wall-clock time than a direct check but it is the only way to discover
coverage you were not told about, and Daman aside it covers everything your clinic has credentials
for.
A newly issued card, or a dependant added mid-policy, often is not searchable by Emirates ID yet.
Retry with CARDNUMBER and the member id from the card, against a named payer: a card number sent
to BOTH is searched as an Emirates ID and finds nobody.
An expired policy returns member_not_found with is_eligible: false and a failure_reason, the
same status as a member the payer has never heard of. Tell them apart with data.patient_info,
which is present here and absent on a true miss. Re-running the same check will not change that;
you need a current card. See not eligible.
Errors
{
"success": false,
"message": "TPA or Insurance name not provided",
"data": null
}See Errors for the full list.