
Complete guide to modeling providers in Orita β eligibility attributes, onboarding, bulk import, and how providers flow into the scheduling resolution engine.
The provider graph is your network of professionals inside Orita. Every provider is a self-contained node with everything Orita needs to route appointments to them.
Identity
Name, username, email, and bio.
Eligibility attributes
Hard constraints used during resolution: specialty, language, modality, license region, insurance plan, service region, age group, panel capacity.
Services
Event types with duration, price, and scheduling rules.
Availability
Weekly schedules and date-specific overrides.
When you call POST /api/v2/resolutions, Orita runs each provider in your graph through the eligibility rules before checking real-time availability.
A fully populated provider object looks like this:
{
"id": "pro_01K...",
"organizationId": "org_01K...",
"externalId": "customer-provider-2841",
"displayName": "Dr. Ana GarcΓa",
"email": "ana@example.com",
"status": "active",
"searchable": true,
"professionCode": "clinical_psychologist",
"specialtyCodes": ["anxiety", "cbt"],
"languageCodes": ["es", "en"],
"modalityCodes": ["virtual"],
"serviceRegionCodes": ["US-NJ"],
"insurancePlanCodes": ["aetna", "cigna"],
"ageGroupCodes": ["adult"],
"licenseRecords": [{
"licenseTypeCode": "psychologist",
"regionCode": "US-NJ",
"status": "active",
"expiresAt": "2027-06-30",
"verifiedAt": "2026-07-20T14:00:00Z"
}],
"acceptsNewClients": true,
"timezone": "America/New_York",
"profileVersion": 12
}Orita uses these fields as hard constraints during resolution. If a constraint is present in the resolution request and the provider does not satisfy it, the provider is excluded.
Hard vs. soft constraints
Hard constraints (the fields below) must match β provider is excluded on failure. Soft preferences (e.g. preference: "afternoon") are used for ranking only, not exclusion.
| Field | Type | Description | Example |
|---|---|---|---|
specialtyCodes | string[] | Clinical or professional specialties. Hard constraint in resolution. | ["anxiety", "cbt", "trauma"] |
languageCodes | string[] | ISO 639-1 language codes. Hard constraint in resolution. | ["es", "en", "fr"] |
modalityCodes | string[] | "virtual" or "in_person". Hard constraint in resolution. | ["virtual"] |
insurancePlanCodes | string[] | Accepted insurance plans. Hard constraint. Aliases normalized (e.g. "Blue Cross" β "bcbs"). | ["aetna", "cigna"] |
licenseRecords | LicenseRecord[] | License records with regionCode, status, expiresAt, verifiedAt. Hard constraint in resolution β expired or missing licenses exclude the provider. | [{licenseTypeCode: "psychologist", regionCode: "US-NJ", status: "active", expiresAt: "2027-06-30"}] |
serviceRegionCodes | string[] | Regions the provider serves. Hard constraint in resolution. | ["US-NJ", "US-NY"] |
ageGroupCodes | string[] | Supported patient age groups: adult, adolescent, child, senior. Hard constraint in resolution. | ["adult"] |
location | string | City, state, or country | "Madrid, Spain" |
profession | string | Professional category | "therapist" |
Three steps to get a provider ready for resolution: create the provider, create a service, then configure availability.
// Step 1 β Create the provider
const resp = await fetch('https://orita.online/api/v2/providers', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.ORITA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
externalId: 'your-provider-id',
displayName: 'Dr. Ana GarcΓa',
email: 'ana@example.com',
professionCode: 'clinical_psychologist',
specialtyCodes: ['anxiety', 'cbt'],
languageCodes: ['es', 'en'],
modalityCodes: ['virtual'],
serviceRegionCodes: ['US-NJ'],
insurancePlanCodes: ['aetna'],
licenseRecords: [{ licenseTypeCode: 'psychologist', regionCode: 'US-NJ', status: 'active', expiresAt: '2027-06-30' }],
acceptsNewClients: true,
timezone: 'America/New_York',
}),
});
const provider = await resp.json();
// Step 2 β Create a service for them
const service = await orita.eventTypes.create({
providerId: provider.id,
title: "Initial Consultation",
duration: 50,
price: 80,
currency: "EUR",
});
// Step 3 β Create availability
const availability = await orita.availability.create({
providerId: provider.id,
title: "Default Schedule",
timezone: "Europe/Madrid",
days: {
monday: { enabled: true, slots: [{ start: "09:00", end: "17:00" }] },
tuesday: { enabled: true, slots: [{ start: "09:00", end: "17:00" }] },
wednesday: { enabled: true, slots: [{ start: "09:00", end: "17:00" }] },
thursday: { enabled: true, slots: [{ start: "09:00", end: "17:00" }] },
friday: { enabled: true, slots: [{ start: "09:00", end: "17:00" }] },
saturday: { enabled: false, slots: [] },
sunday: { enabled: false, slots: [] },
},
});Use bulk import to upsert up to 500 providers in one call. Use your own stable externalId for each provider. Run a dry import first to validate, then commit.
const response = await fetch(
'https://orita.online/api/v2/provider-imports',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.ORITA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
mode: 'upsert',
dryRun: true,
providers, // your provider data with externalId fields
}),
}
);
const importJob = await response.json();
// { importId, status: 'validating', dryRun: true }Need a sample dataset? See the example providers.json in the AI Receptionist Provider Routing repo.
Use GET /api/v2/providers to list your provider graph β useful for admin dashboards or pre-flight checks.
const resp = await fetch(
'https://orita.online/api/v2/providers?status=active&limit=50',
{ headers: { Authorization: `Bearer ${process.env.ORITA_API_KEY}` } }
);
const { data: providers } = await resp.json();
console.log(`Found ${providers.length} active providers`);When you call POST /api/v2/resolutions, here is what happens under the hood:
Fetch providers
Orita fetches all providers linked to your platform account.
Apply hard constraints
Hard constraints exclude providers: license region, insurance plan, language, modality, specialty, age group, panel capacity. All constraints use anyOf semantics. Missing required data returns unknown state.
Check availability
For remaining eligible providers, Orita checks real-time calendar availability within the requested dateRange.
Rank candidates
Candidates are ranked by: preference match β availability density β provider score.
Return options
Top 10 options are returned with a reason field and reasonCodes explaining why each provider was selected.
Resolution pipeline (simplified)
All providers in your graph
β
βΌ
βββββββββββββββββββββββββββββββ
β Hard constraint filters β β specialty, language, modality, insurance
ββββββββββββββββ¬βββββββββββββββ
β eligible providers
βΌ
βββββββββββββββββββββββββββββββ
β Real-time availability β β checks calendar within dateRange
ββββββββββββββββ¬βββββββββββββββ
β available slots
βΌ
βββββββββββββββββββββββββββββββ
β Ranking β β preference match β density β score
ββββββββββββββββ¬βββββββββββββββ
β
βΌ
Top 10 options returned