Mantys Eligibility API

Create a task

POST /create-task: every request field, the two server-side defaults, and the per-payer special cases.

POST /create-task

Queues 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_nameWhat it does
DHPOAsks the DHPO / eClaim aggregator, then fans out.
RIYATIAsks the Riyati aggregator, then fans out.
BOTHAsks 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 with INPATIENT_ 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.

ExtraShapePayersOmitted
maternity_treatment
Answers 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 | falseTPA001, TPA004the portal answers as though you had said no
is_member_present_at_the_facility
Sets 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 | falseTPA001, TPA004the portal is told the member is present
exception_case
Ticks 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 | falseTPA001, TPA004the portal’s bypass checkbox is ticked and no referral is resolved
referral_document_url
A fetchable URL to the referral letter. The scraper downloads it and attaches it on the portal’s referral upload field.
"referral_document_url": TPA001, TPA004no referral document is uploaded
referring_physician
The 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, D004no referring clinician is recorded on the check
referring_department
The 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": TPA002a gatekeeper plan completes with a warning and no referral selected
service_type
The 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, TPA037the claim form is filled with Consultation GP
use_dental
Runs 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"INS010the ordinary non-dental flow runs, whatever visit_type says
referral_code
The payer’s own referral code, typed into the Aafiya portal’s referral field. Only useful when the patient arrives with one.
"referral_code": TPA026the portal’s referral code field is left blank
visit_category
ADNIC’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"INS017the consultation request is submitted as FIRST_VISIT
use_metlife
Switches the MedNet portal to MetLife before the search. Send "YES" as a string; anything else falls back to MedNet.
"use_metlife": "YES" | "NO"TPA036the member is looked up in MedNet’s own book
use_dubaicare
Switches the MedNet portal to DubaiCare before the search. Ignored when use_metlife is "YES", which is checked first.
"use_dubaicare": "YES" | "NO"TPA036the member is looked up in MedNet’s own book
is_emergency
Ticks MedNet’s emergency registration. Independent of visit_type, which at MedNet carries the consultation type and benefit instead.
"is_emergency": true | falseTPA036the encounter is registered as an ordinary visit
is_pod
Marks the encounter as a POD dispense. Send pod_id with it; the product refuses to submit a POD check without one.
"is_pod": true | falseTPA023, INS026, INS078, D002, D004isPOD is left off the payload entirely, which is not the same as false
pod_id
The 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, D004podId is left off the payload
is_maternity
Flags 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 | falseTPA023, INS026, INS078, D002, D004isMaternity is left off the payload
free_followup_or_extended_followup
Pre-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 | falseTPA023, INS026, INS078, D002, D004a business-rule violation ends the check instead of being answered
not_related_to_chief_complaint
Pre-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 | falseTPA023, INS026, INS078, D002, D004a 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_metlife and use_dubaicare are compared against the literal string "YES", so a JSON true reads as no. is_emergency, exception_case and the Daman flags do take a real boolean.
  • visit_category is the one extra with an enum behind it, and the only one where a wrong value fails the check rather than being ignored.
  • is_maternity needs doctorId. 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 username and password; license_number is the only other key accepted
  • keys must be TPA codes: BOTH, DHPO and RIYATI name a fan-out, not a portal, so they are not accepted as keys
  • the single-login shorthand is only accepted when tpa_name is 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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Request body
{
  "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.

Errors

{
  "success": false,
  "message": "TPA or Insurance name not provided",
  "data": null
}

See Errors for the full list.

On this page