Add Formulary Checking to Your Prescribing Workflow
Your telehealth app needs to check drug coverage before the provider writes the prescription. This guide walks through looking up formulary data, checking step therapy requirements against a patient's medication history, and parsing the result into a clear prescribing decision.
Estimated integration time: 20 minutes. The formulary lookup endpoint is free and unauthenticated. The readiness check requires an API key -- use a sandbox key (tln_test_) while developing.
Look up formulary data
The GET /api/v1/formulary endpoint is free and does not require authentication. It returns tier, prior auth requirements, step therapy drugs, quantity limits, and clinical criteria for a payer + drug combination.
curl "https://talonapi.dev/api/v1/formulary?payer=aetna&drug=humira"{
"formulary": [
{
"payer_name": "Aetna",
"drug_name": "Humira",
"generic_name": "adalimumab",
"formulary_tier": "specialty",
"prior_auth_required": true,
"step_therapy_required": true,
"step_therapy_drugs": [
{ "drug": "methotrexate", "status": "must_have_tried" },
{ "drug": "sulfasalazine", "status": "must_have_tried" }
],
"quantity_limit": "2 pens per 28 days",
"clinical_criteria": "Diagnosis of RA, PsA, or Crohn disease with documented failure of conventional DMARDs"
}
]
}If step_therapy_required is true, the patient must have tried (and failed) the listed drugs before the payer will approve this one. This is the key piece of information for the prescribing workflow.
Check step therapy requirements
Before submitting a readiness check, review the step therapy drugs from the formulary response. Each entry in step_therapy_drugs has a status field:
must_have_tried -- The patient must have a documented trial of this drug before the target drug can be approved.
preferred_alternative -- The payer prefers this drug but it is not a hard requirement. Documenting why it was not used strengthens the case.
Map each step therapy drug against the patient's medication history from your EHR. Build an array of medication history objects:
// From your EHR's medication records
const medicationHistory = [
{
drug: "methotrexate",
status: "failed",
reason: "Inadequate response after 12 weeks at max tolerated dose"
},
{
drug: "sulfasalazine",
status: "failed",
reason: "Discontinued due to GI intolerance after 4 weeks"
}
];status values are "failed", "intolerant", "contraindicated", and "current". Always include a reason -- the check uses it to evaluate whether the trial was adequate.Submit the formulary readiness check
Post the payer, drug, and medication history to POST /api/v1/formulary/check. This endpoint requires an API key.
curl -X POST "https://talonapi.dev/api/v1/formulary/check" \
-H "X-API-Key: tln_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"payer": "Aetna",
"drug_name": "Humira",
"patient_medication_history": [
{
"drug": "methotrexate",
"status": "failed",
"reason": "Inadequate response after 12 weeks"
},
{
"drug": "sulfasalazine",
"status": "failed",
"reason": "GI intolerance after 4 weeks"
}
]
}'Parse the prescribing verdict
The response gives you a clear answer and the details to act on:
{
"covered": true,
"prior_auth_required": true,
"step_therapy_required": true,
"step_therapy_status": {
"steps_required": [
{ "drug": "methotrexate", "status": "must_have_tried" },
{ "drug": "sulfasalazine", "status": "must_have_tried" }
],
"steps_completed": ["methotrexate", "sulfasalazine"],
"steps_remaining": []
},
"quantity_limits": "2 pens per 28 days",
"ready_to_prescribe": true,
"recommendation": "All step therapy requirements satisfied. Prior authorization required -- submit with documentation of failed methotrexate and sulfasalazine trials.",
"metadata": {
"source": "Aetna Formulary",
"last_verified": "2026-02-20T00:00:00.000Z"
}
}When steps remain, the response looks different:
{
"covered": true,
"prior_auth_required": true,
"step_therapy_required": true,
"step_therapy_status": {
"steps_required": [
{ "drug": "methotrexate", "status": "must_have_tried" },
{ "drug": "sulfasalazine", "status": "must_have_tried" }
],
"steps_completed": ["methotrexate"],
"steps_remaining": [
{ "drug": "sulfasalazine", "action": "Trial of sulfasalazine required before Humira" }
]
},
"ready_to_prescribe": false,
"recommendation": "Step therapy required: complete trial of sulfasalazine before requesting Humira."
}ready_to_prescribe -- Boolean. If false, the provider should not prescribe yet.
steps_remaining -- Array of drugs the patient still needs to try, each with a human-readable action string you can show directly in the UI.
recommendation -- A plain-English summary suitable for displaying to the prescribing provider.
Complete TypeScript integration
A full prescribing workflow function that combines the lookup and check:
const TALON_BASE = "https://talonapi.dev";
interface MedHistory {
drug: string;
status: "failed" | "intolerant" | "contraindicated" | "current";
reason: string;
}
interface FormularyResult {
covered: boolean;
ready_to_prescribe: boolean;
prior_auth_required: boolean;
steps_remaining: { drug: string; action: string }[];
recommendation: string;
quantity_limits: string | null;
}
export async function checkFormulary(
apiKey: string,
payer: string,
drugName: string,
medHistory: MedHistory[]
): Promise<FormularyResult> {
// 1. Quick lookup (free, no auth needed)
const lookupRes = await fetch(
`${TALON_BASE}/api/v1/formulary?payer=${encodeURIComponent(payer)}&drug=${encodeURIComponent(drugName)}`
);
if (!lookupRes.ok) {
throw new Error(`Formulary lookup failed: ${lookupRes.statusText}`);
}
const lookupData = await lookupRes.json();
if (lookupData.count === 0) {
return {
covered: false,
ready_to_prescribe: false,
prior_auth_required: false,
steps_remaining: [],
recommendation: `${drugName} not found in ${payer}'s formulary. Consider an alternative or submit an exception request.`,
quantity_limits: null,
};
}
// 2. If no step therapy, shortcut
const entry = lookupData.formulary[0];
if (!entry.step_therapy_required && !entry.prior_auth_required) {
return {
covered: true,
ready_to_prescribe: true,
prior_auth_required: false,
steps_remaining: [],
recommendation: `${drugName} is covered with no restrictions. Proceed with prescribing.`,
quantity_limits: entry.quantity_limit,
};
}
// 3. Full readiness check
const checkRes = await fetch(`${TALON_BASE}/api/v1/formulary/check`, {
method: "POST",
headers: {
"X-API-Key": apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
payer,
drug_name: drugName,
patient_medication_history: medHistory,
}),
});
if (!checkRes.ok) {
const err = await checkRes.json();
throw new Error(`Formulary check failed: ${err.error?.message ?? checkRes.statusText}`);
}
const data = await checkRes.json();
return {
covered: data.covered,
ready_to_prescribe: data.ready_to_prescribe,
prior_auth_required: data.prior_auth_required,
steps_remaining: data.step_therapy_status?.steps_remaining ?? [],
recommendation: data.recommendation,
quantity_limits: data.quantity_limits,
};
}
// Usage
const result = await checkFormulary(
"tln_test_abc123",
"Aetna",
"Humira",
[
{ drug: "methotrexate", status: "failed", reason: "Inadequate response after 12 weeks" },
{ drug: "sulfasalazine", status: "intolerant", reason: "GI side effects" },
]
);
if (result.ready_to_prescribe) {
console.log("Ready to prescribe.", result.recommendation);
} else {
console.log("Not ready:", result.steps_remaining);
}Python equivalent
import requests
from dataclasses import dataclass, field
TALON_BASE = "https://talonapi.dev"
@dataclass
class FormularyResult:
covered: bool
ready_to_prescribe: bool
prior_auth_required: bool
steps_remaining: list[dict] = field(default_factory=list)
recommendation: str = ""
quantity_limits: str | None = None
def check_formulary(
api_key: str,
payer: str,
drug_name: str,
med_history: list[dict],
) -> FormularyResult:
# 1. Free formulary lookup
lookup_res = requests.get(
f"{TALON_BASE}/api/v1/formulary",
params={"payer": payer, "drug": drug_name},
)
lookup_res.raise_for_status()
lookup_data = lookup_res.json()
if lookup_data["count"] == 0:
return FormularyResult(
covered=False,
ready_to_prescribe=False,
prior_auth_required=False,
recommendation=f"{drug_name} not found in {payer}'s formulary.",
)
entry = lookup_data["formulary"][0]
if not entry["step_therapy_required"] and not entry["prior_auth_required"]:
return FormularyResult(
covered=True,
ready_to_prescribe=True,
prior_auth_required=False,
recommendation=f"{drug_name} is covered with no restrictions.",
quantity_limits=entry.get("quantity_limit"),
)
# 2. Full readiness check
check_res = requests.post(
f"{TALON_BASE}/api/v1/formulary/check",
headers={"X-API-Key": api_key},
json={
"payer": payer,
"drug_name": drug_name,
"patient_medication_history": med_history,
},
)
check_res.raise_for_status()
data = check_res.json()
return FormularyResult(
covered=data["covered"],
ready_to_prescribe=data["ready_to_prescribe"],
prior_auth_required=data["prior_auth_required"],
steps_remaining=data.get("step_therapy_status", {}).get("steps_remaining", []),
recommendation=data["recommendation"],
quantity_limits=data.get("quantity_limits"),
)
# Usage
result = check_formulary(
api_key="tln_test_abc123",
payer="Aetna",
drug_name="Humira",
med_history=[
{"drug": "methotrexate", "status": "failed", "reason": "Inadequate response after 12 weeks"},
{"drug": "sulfasalazine", "status": "intolerant", "reason": "GI side effects"},
],
)
if result.ready_to_prescribe:
print("Ready to prescribe.", result.recommendation)
else:
for step in result.steps_remaining:
print(f"Remaining: {step['drug']} -- {step['action']}")Tips for production
count: 0, the drug is not in Talon's database for that payer. Show the provider a "coverage unknown" state and suggest they verify with the payer directly or submit a formulary exception request. steps_completed and steps_remaining to render a progress indicator. Providers appreciate seeing exactly where the patient is in the step therapy sequence at a glance. GET /api/v1/formulary endpoint is free. Cache the result for 24 hours to speed up repeat lookups for the same drug. quantity_limits field alongside the prescribing form so the provider writes a compliant quantity from the start, avoiding pharmacy rejections.ndc parameter instead of drug for exact matches. Drug name matching is fuzzy and may return multiple results.