When you ask Orita to find an available provider, you're not just querying a calendar. You're running a resolution β a structured search that applies rules about who qualifies and how results should be ranked.
That resolution has two distinct input categories: hard constraints and soft preferences. Mixing them up is one of the most common sources of bugs in scheduling integrations. This guide explains the difference, when each applies, and how Orita handles missing or unknown data.
The Core Distinction
Hard constraints define eligibility. A provider either passes or fails. There is no middle ground.
Soft preferences express desirability. Every provider still qualifies; preferences change the order in which options are presented.
The clearest way to see the distinction:
{
"constraints": {
"languageCodes": { "anyOf": ["es"] },
"modalityCodes": { "anyOf": ["video"] },
"acceptsNewClients": true,
"specialties": { "anyOf": ["anxiety", "depression"] }
},
"preferences": {
"dayParts": ["afternoon"],
"earliestAvailable": true,
"continuityProviderId": "prov_9a21b"
}
}
In this resolution request:
- A provider who speaks only English is excluded (fails
languageCodes). - A provider who speaks Spanish but only offers in-person sessions is excluded (fails
modalityCodes). - A provider who meets all constraints but only has morning slots is included β just ranked lower because of the
dayParts: ["afternoon"]preference.
The practical implication: soft preferences never cause a zero-result response. If no afternoon slots exist, Orita returns whatever is available. If a constraint has no matches, the response is empty β intentionally.
Full Reference: Constraints vs. Preferences
| Field | Type | Category | Behavior |
|---|---|---|---|
| languageCodes | { anyOf: string[] } | Constraint | Excludes providers who don't offer any listed language |
| modalityCodes | { anyOf: string[] } | Constraint | Excludes providers who don't support any listed modality (e.g., video, in_person, phone) |
| acceptsNewClients | boolean | Constraint | Excludes providers not accepting new clients when true |
| specialties | { anyOf: string[] } | Constraint | Excludes providers without at least one matching specialty |
| profession | string | Constraint | Excludes providers of a different professional category |
| dayParts | string[] | Preference | Boosts options in matching time windows (morning, afternoon, evening) |
| earliestAvailable | boolean | Preference | Boosts the chronologically earliest valid option |
| continuityProviderId | string | Preference | Boosts options from a provider the client has seen before |
Preferences contribute points to the ranking score but never cause exclusion. A provider who doesn't match any preference is still eligible β they just rank lower.
Fail Closed: What Happens with Missing or Unknown Data
This is the behavior that catches developers off guard: when a constraint's data is missing for a provider, that provider is excluded.
This is intentional. Orita fails closed rather than open.
Why fail closed?
Consider a languageCodes constraint. If a provider's language offerings haven't been configured, Orita doesn't assume they speak the requested language. The safer assumption is that we don't know, and therefore the provider doesn't qualify.
Failing open β assuming the provider qualifies when data is absent β leads to mismatches that only surface after a booking is confirmed. Failing closed means zero-result responses can be debugged before a client is affected.
What it looks like in the API response
When providers are excluded, the resolution response includes an exclusions summary:
{
"options": [],
"exclusions": {
"LANGUAGE_NOT_SUPPORTED": 14,
"MODALITY_NOT_SUPPORTED": 6,
"DATA_MISSING_LANGUAGE": 3,
"NO_AVAILABLE_SLOTS": 11
},
"resolvedAt": "2026-07-29T14:23:01Z"
}
The DATA_MISSING_LANGUAGE: 3 count tells you that 3 providers were excluded because their languageCodes field was empty β not because they don't speak Spanish, but because no data was available.
This is the correct behavior. It also tells you exactly what to fix: configure language offerings for those 3 providers and they become eligible.
Constraint: languageCodes
The anyOf operator means the provider must support at least one of the listed languages.
{
"constraints": {
"languageCodes": { "anyOf": ["es", "pt"] }
}
}
A provider offering ["es", "en"] qualifies. A provider offering only ["en"] does not. A provider with no configured languages does not.
If you want to match providers who speak any language (no language filter), omit languageCodes from the constraints entirely. An empty anyOf array is treated as a misconfigured constraint and will return an error.
Constraint: modalityCodes
Modality refers to the format of the session. Common values are video, in_person, and phone.
{
"constraints": {
"modalityCodes": { "anyOf": ["video", "phone"] }
}
}
A provider who offers only in-person appointments is excluded. A provider who offers video and in-person qualifies (because they offer at least one matching modality).
Constraint: acceptsNewClients
A boolean constraint. When set to true, providers who have marked themselves as not accepting new clients are excluded.
{
"constraints": {
"acceptsNewClients": true
}
}
When this constraint is absent, new-client status is not checked β both accepting and non-accepting providers appear in results.
Constraint: specialties
{
"constraints": {
"specialties": { "anyOf": ["trauma", "ptsd", "anxiety"] }
}
}
A provider who lists ["anxiety", "depression", "ocd"] qualifies. A provider who lists only ["depression"] does not (assuming depression isn't in the constraint list). A provider with no specialties configured is excluded.
Constraint: profession
profession is an exact-match constraint. It filters by professional category rather than specialty.
{
"constraints": {
"profession": "psychologist"
}
}
A provider registered as a psychiatrist would be excluded even if their specialties overlap.
Preference: dayParts
The dayParts preference doesn't filter results β it shifts the ranking of options that fall in preferred time windows.
{
"preferences": {
"dayParts": ["afternoon"]
}
}
An option at 3:00 PM gets a PREFERRED_DAYPART bonus in its ranking score. An option at 9:00 AM is still returned if it's the only valid slot β it's just ranked lower.
Day part definitions:
morning: 6:00β12:00afternoon: 12:00β18:00evening: 18:00β22:00
(All times are evaluated in the provider's configured time zone.)
Preference: earliestAvailable
When true, the chronologically first valid option receives a score boost. This is useful for urgent scheduling where minimizing wait time matters more than session preferences.
{
"preferences": {
"earliestAvailable": true
}
}
This doesn't guarantee the earliest slot is always first β a slot with multiple strong preference matches may still outrank it. But for a tie in constraint satisfaction, the earlier option wins.
Preference: continuityProviderId
Care continuity is a meaningful signal in professional services. If a client has seen a specific provider before, continued sessions with the same provider often produce better outcomes.
{
"preferences": {
"continuityProviderId": "prov_9a21b"
}
}
Options from prov_9a21b receive a score boost. If that provider has no availability in the requested window, options from other providers are returned β continuity is a preference, not a filter.
Putting It Together: A Full Resolution Request
import { OritaClient } from "orita-sdk";
const orita = new OritaClient({ apiKey: process.env.ORITA_API_KEY });
const resolution = await orita.resolutions.create({
serviceId: "svc_therapy_60min",
windowStart: "2026-08-04T00:00:00Z",
windowEnd: "2026-08-08T23:59:59Z",
constraints: {
languageCodes: { anyOf: ["es"] },
modalityCodes: { anyOf: ["video"] },
acceptsNewClients: true,
specialties: { anyOf: ["anxiety", "depression"] },
},
preferences: {
dayParts: ["afternoon"],
continuityProviderId: "prov_9a21b",
},
});
console.log(resolution.options[0]);
// {
// optionId: "opt_7x2c",
// providerId: "prov_9a21b",
// score: 94,
// matchedConstraints: ["language_matched", "modality_supported", "accepts_new_clients", "specialty_matched"],
// rankingFactors: [
// { code: "CONTINUITY_PROVIDER", points: 20 },
// { code: "PREFERRED_DAYPART", points: 15 },
// { code: "EARLIEST_VALID_OPTION", points: 30 }
// ],
// reason: "Matches all required conditions. Preferred provider with an afternoon slot.",
// slot: { start: "2026-08-06T15:00:00Z", end: "2026-08-06T16:00:00Z" }
// }
Python Equivalent
import os
from orita import OritaClient
orita = OritaClient(api_key=os.environ["ORITA_API_KEY"])
resolution = orita.resolutions.create(
service_id="svc_therapy_60min",
window_start="2026-08-04T00:00:00Z",
window_end="2026-08-08T23:59:59Z",
constraints={
"languageCodes": {"anyOf": ["es"]},
"modalityCodes": {"anyOf": ["video"]},
"acceptsNewClients": True,
"specialties": {"anyOf": ["anxiety", "depression"]},
},
preferences={
"dayParts": ["afternoon"],
"continuityProviderId": "prov_9a21b",
},
)
top_option = resolution.options[0]
print(top_option.reason)
# "Matches all required conditions. Preferred provider with an afternoon slot."
Debugging Zero Results
If your resolution returns options: [], the exclusions object tells you exactly why:
{
"options": [],
"exclusions": {
"LANGUAGE_NOT_SUPPORTED": 18,
"DATA_MISSING_LANGUAGE": 5,
"NO_AVAILABLE_SLOTS": 31
}
}
This response tells you:
- 18 providers were excluded because they don't support the requested language
- 5 providers were excluded because language data is missing (fixable by updating those provider profiles)
- 31 providers passed all constraints but had no open slots in the requested window
Zero results from missing data is a data quality signal. Zero results from NO_AVAILABLE_SLOTS is a supply signal. The distinction matters when deciding whether to fix the resolution request or expand the scheduling window.
Summary
The constraint/preference split is foundational to how Orita's resolution engine works. Getting it right leads to predictable, debuggable provider matching:
- Use constraints for eligibility requirements that must be met for a booking to be clinically, legally, or operationally valid.
- Use preferences for signals that improve the quality of a match without making any provider ineligible.
- Expect fail-closed behavior for missing data β and use the
exclusionssummary to diagnose and fix data quality issues.
Read the full API reference β Β· Try provider matching live β
