Place of Service (POS) Codes
A CMS-maintained two-digit code set placed on the service-line level of professional (CMS-1500 / 837P) claims to indicate the physical or virtual setting where a service was furnished, enabling site-of-care classification, telehealth identification, and setting-shift studies in real-world evidence research.
In plain language
A Place of Service (POS) code is a two-digit code that goes on the billing form when a doctor or other provider submits a claim for payment, and it tells the insurer where the service actually happened — for example, code 11 means the patient was seen in a physician's office, code 23 means an emergency room, and codes 02 and 10 indicate a telehealth visit. These codes only appear on the professional claim (the physician's bill), not on the facility's hospital bill, so they give analysts a direct way to classify the care setting for doctor visits but they cannot be used to classify hospital stays by themselves. One important catch: in 2022 Medicare split a single telehealth code into two, so any study comparing telehealth rates before and after 2022 needs to look for both codes or it will undercount telehealth in recent years.
Place of Service (POS) codes
are a standardized two-digit code set administered by the Centers for Medicare & Medicaid Services (CMS) and placed in field 24B of the CMS-1500 paper form (loop 2300/2400 of the 837P electronic transaction) on every professional claim service line. A professional claim covers physician services, outpatient evaluations, procedures, and other non-facility encounters billed by a rendering provider — as opposed to the UB-04 / 837I institutional claim filed by the facility itself. Because the institutional claim has no POS field (setting is instead conveyed via the Type of Bill and revenue center codes), POS codes apply exclusively to the professional claim stream. This asymmetry is the single most important structural fact for any analyst constructing a setting classifier from claims data.
What POS codes encode
Each two-digit code maps to a defined care setting. The most analytically consequential codes for RWE research are:
- 11 — Office: A physician's office or independent clinic not attached to a hospital outpatient department.
- 19 — Off-Campus Outpatient Hospital vs. 22 — On-Campus Outpatient Hospital: The 2016 Bipartisan Budget Act
- 21 — Inpatient Hospital: A physician billing for a service furnished to a patient admitted as an inpatient.
- 23 — Emergency Room – Hospital: An emergency department visit billed by the physician (emergency medicine
- 24 — Ambulatory Surgical Center (ASC): Services furnished in a free-standing surgery center. Relevant for
- 31 — Skilled Nursing Facility (SNF): Professional services furnished to SNF residents. Important for
- 12 — Home: Services furnished in the patient's private residence. Used to identify home health visits billed
- 02 — Telehealth Provided Other Than in Patient's Home vs. 10 — Telehealth in Patient's Home: The 2022
- 81 — Independent Laboratory: Professional claims for services furnished in a free-standing laboratory. Useful
Core conceptual distinction — setting vs. billing rule
POS reflects the setting as designated for billing purposes, not always the physical location of service delivery. Provider-based billing rules allow a physician practice acquired by or affiliated with a hospital to bill its professional claims at hospital POS codes (19 or 22) even if the physician continues to see patients in what looks externally like an independent office. The patient may receive identical care from the same physician in the same exam room before and after the hospital acquisition, yet the POS on the professional claim shifts from 11 to 22, producing a facility fee from the hospital and a higher Medicare payment rate. This is the central pitfall of POS-based site-of-care research: an apparent shift from office (POS 11) to hospital outpatient (POS 22) in a claims trend study may reflect a change in billing affiliation rather than any change in where or how care is actually delivered.
Pros, cons, and trade-offs — specific and comparative
- vs. UB-04 type of bill + revenue center codes for setting classification: The UB-04 / institutional claim carries no POS field; setting is instead encoded in the Type of Bill (three-digit code where the first digit is the facility type and the second is the care type, e.g., 011x = inpatient hospital, 013x = outpatient hospital) and in the revenue center code at the service-line level (e.g., 045x = emergency room, 0636 = pharmacy). For site-of-care research that spans both the professional and institutional streams — as almost all complete encounter-level studies do — the analyst must join POS from the professional claim to the institutional claim's type-of-bill and revenue codes to reconstruct the full setting picture. Failing to do this linkage means attributing the setting entirely from one stream and missing the cross-stream inconsistencies that require reconciliation rules. Prefer POS for the physician setting; use type of bill + revenue codes for the facility setting; link them for complete encounter cost and setting reconstruction. - vs. taxonomy of place codes in EHR or registry data: EHR records may carry a place-of-service concept but it is typically an internal facility designation that does not map directly to CMS POS codes without a deliberate crosswalk. Registry data usually lacks a granular setting field entirely. Claims POS codes are therefore the most systematic source of setting information in a US administrative data study, as long as the institutional / professional claim asymmetry is respected. Prefer POS for multi-provider setting classification in claims; rely on EHR internal codes only when the question is confined to a single health system whose internal codes are validated against the CMS set. - Granularity vs. coding-practice noise: POS 11 (office) is clear when the provider is independently operating. POS 19 and 22 (off-campus and on-campus outpatient hospital) are contaminated by the pre-2016 vs. post-2016 coding change, by cross-payer differences (commercial payers may not require the 19/22 split in the same way Medicare does), and by provider-based billing that makes "office" and "outpatient hospital" indistinguishable from true care-delivery changes. Sensitivity analyses by pre/post-2016 and by payer type are standard in any study using POS 19/22. - Telehealth POS completeness: Before the March 2020 COVID-19 emergency declaration, Medicare telehealth was tightly restricted (specific rural originating sites required, narrow list of allowed services). Commercial payers used POS 02 inconsistently. Post-emergency, the POS 02/10 universe expanded dramatically. Any claims database that spans the pre-2020 and post-2020 periods will have a structural break in POS 02/10 prevalence that reflects regulatory expansion, not underlying utilization change.
When to use
- Site-of-care classification on the professional claim side of any utilization study: identify which encounters were office-based, outpatient-hospital-based, ED-based, SNF-based, or telehealth. POS is the correct variable for this on professional claims; do not attempt it from diagnosis or CPT codes alone. - Telehealth adoption and trend studies using Medicare or commercial claims: POS 02 (pre-2022) or POS 02 + 10 (from 2022 forward) is the operationally correct telehealth indicator on the professional claim. Verify that the payer in question was using POS 02/10 systematically before assuming completeness. - Site-neutrality and payment-differential studies: Comparing costs and utilization of care billed at POS 11 (office) vs. POS 22 (on-campus outpatient hospital) is the standard approach to studying the hospital outpatient premium. Pre-specify how you will handle the 2016 code-set change and provider-based billing. - ED encounter identification on the professional claim side: POS 23 plus emergency E/M CPT codes is the standard professional-claim ED algorithm. Always link to the facility claim (Type of Bill 013x) for completeness. - Inpatient physician service identification: POS 21 on a professional claim identifies a physician billing for care delivered to an admitted patient. Link to the MedPAR or UB-04 to establish the admission date range.
When NOT to use — and when POS-based classification is actively misleading or dangerous
- On institutional claims. UB-04 / 837I records do not carry a POS field. Applying POS logic to institutional claims is undefined. Always route setting classification to the correct claim type before applying code logic. - As a sole indicator of physical care location in provider-based billing scenarios. A change in POS from 11 to 22 following a hospital acquisition may reflect only the billing relationship, not a change in the patient's physical location or the care delivered. For studies of actual care-setting migration (e.g., "did patients shift to hospital-based care?"), supplement POS with provider taxonomy codes, NPI-level affiliation data, or enrollment records to distinguish billing changes from true site shifts. - When POS disagrees with the facility claim setting. A professional claim with POS 21 (inpatient) may be filed for the same date of service as a facility claim showing an outpatient status — this is a real coding inconsistency that arises from observation status disputes, two-midnight rule decisions, and late billing. Use the facility claim's admission status as the authoritative record for inpatient/outpatient designation; treat POS 21 professional claims as requiring verification against the MedPAR or UB-04 before assuming the patient was a true inpatient. - Without date-conditional telehealth logic spanning 2022. Using POS 02 alone as a telehealth flag in data that includes 2022 onward will undercount telehealth by missing POS 10 (in-home telehealth). Write the flag as POS IN (02, 10) and apply the date awareness described above. - Across payers without verifying payer-specific coding conventions. Commercial payers do not always enforce the same POS code granularity as Medicare. POS 19 (off-campus outpatient) may be entirely absent in some commercial datasets. Validate POS distributions against expected frequencies before building a cross-payer site-of-care classifier. - When the code set version is not pinned. CMS adds, retires, and redefines POS codes over time. A study spanning many years should document which code set version was current for each calendar year in the study window, particularly for codes that were newly introduced or redefined (19/22 split in 2016; 02/10 split in 2022). Using a single static code list on longitudinal data introduces measurement error that can be mistaken for trend.
Data-source operational depth
- Medicare FFS carrier / physician claims: POS is consistently populated and is the most reliable source for POS-based classification. However, Medicare applies coverage rules that make POS 02/10 appear only when the service is a CMS-approved telehealth service. Off-label use of POS 02 for a non-covered telehealth service would be denied and would not appear in paid claims, so the Medicare carrier file captures telehealth that was actually reimbursed — a selection filter, not a complete census of all telehealth encounters. For MA beneficiaries, POS may be less consistently submitted in encounter data; restrict to FFS when POS completeness is required. - Commercial claims (e.g., IBM MarketScan, Optum Clinformatics): POS is present but payer-specific coding conventions mean that POS 19 (off-campus outpatient) may be underreported in some payer streams, and the telehealth POS usage will reflect the payer's own coverage policies during and after the PHE. Validate POS frequency distributions before building a cross-payer classifier. The date of the POS 19/22 split may also lag for commercial payers relative to the 2016 Medicare effective date. - Medicaid claims (T-MACS / MAX / TAF): POS is present in professional claims but coding quality varies by state and by managed care organization versus fee-for-service. States that route Medicaid services through capitated MCOs may have encounter data with variable POS completeness. Use POS for Medicaid professional claims only after verifying completeness for the specific state/year.
POS in the context of a complete encounter
A single visit to a hospital-based outpatient oncology clinic may generate two claims: an institutional (UB-04) claim from the hospital for the facility fee, coded with Type of Bill 0131 (hospital outpatient) and revenue codes 036x (medical / surgical supplies) and 0260 (IV therapy); and a professional (CMS-1500) claim from the oncologist for the physician fee, with POS 22 (on-campus outpatient hospital) and J-codes for the drug administered. The professional claim's POS 22 confirms the setting; the institutional claim's Type of Bill and revenue codes provide the facility cost components. Neither claim alone gives the full picture. This two-claim structure is why site-of-care cost analyses in oncology and infusion therapy must aggregate claims from both streams and why a POS-only analysis underestimates the true site differential by omitting the facility payment.
Worked example
Scenario
A health economist studying telehealth adoption and care-setting mix pulls one week of professional claims for a small hypothetical panel of eight patients seen on the same day at five different locations. She wants to classify each service line into one of four analytic buckets — Office, Outpatient Hospital, Emergency Department, or Telehealth — using the POS code, and then count how many service lines fall into each bucket. The table below shows the eight service lines exactly as they would appear in the raw claims data.
Dataset
Eight professional claim service lines for one study day: person_id, service date, pos_code, pos_description, and the CPT code billed.
| person_id | svc_date | pos_code | pos_description | cpt_code |
|---|---|---|---|---|
| 1001 | 2023-06-01 | 11 | Office | 99213 |
| 1002 | 2023-06-01 | 22 | On-Campus Outpatient Hospital | 99214 |
| 1003 | 2023-06-01 | 23 | Emergency Room – Hospital | 99283 |
| 1004 | 2023-06-01 | 2 | Telehealth Other Than Patient Home | 99213 |
| 1005 | 2023-06-01 | 10 | Telehealth in Patient Home | 99214 |
| 1006 | 2023-06-01 | 11 | Office | 99215 |
| 1007 | 2023-06-01 | 19 | Off-Campus Outpatient Hospital | 99213 |
| 1008 | 2023-06-01 | 21 | Inpatient Hospital | 99232 |
Steps
Classify each service line into an analytic bucket using the POS code. Office bucket: POS IN (11) -> persons 1001, 1006 = 2 lines. Outpatient Hospital bucket: POS IN (19, 22) -> persons 1002, 1007 = 2 lines. Emergency Department bucket: POS IN (23) -> person 1003 = 1 line. Telehealth bucket: POS IN (02, 10) -> persons 1004, 1005 = 2 lines. Inpatient physician visit (separate bucket, not outpatient): POS IN (21) -> person 1008 = 1 line.
Note that person 1008 (POS 21, inpatient hospital) is classified as a physician billing for an admitted patient, not an outpatient encounter. This line should be linked to the MedPAR / facility claim to confirm inpatient status before including in an outpatient utilization analysis.
Bucket counts: Office = 2, Outpatient Hospital = 2, ED = 1, Telehealth = 2, Inpatient physician = 1. Total lines = 2 + 2 + 1 + 2 + 1 = 8.
Setting share for the four outpatient categories (excluding the inpatient physician line): Office = 2/7, Outpatient Hospital = 2/7, ED = 1/7, Telehealth = 2/7. expr = 2 + 2 + 1 + 2 = 7 outpatient lines.
Telehealth share of all outpatient lines: 2/7 = 0.286. If the analyst had used only POS 02 (forgetting POS 10 introduced in 2022), she would have counted 1 telehealth line instead of 2, giving 1/7 = 0.143 — exactly half the correct rate. This illustrates the mandatory dual-code flag for post-2022 telehealth.
Result
Eight service lines classify into five buckets: Office=2, Outpatient Hospital=2, ED=1, Telehealth=2, Inpatient physician=1. Total = 2+2+1+2+1 = 8. Telehealth share of the 7 outpatient-eligible lines = 2/7 = 0.286 (28.6%). Using POS 02 alone would yield 1/7 = 0.143 (14.3%) — a 50% undercount of telehealth for post-2022 data.
Runnable example
python implementation
Builds a setting classifier for professional claims using POS codes, with date-conditional telehealth logic for the 2022 POS 02/10 split. Accepts a pandas DataFrame of professional claim service lines and returns a new column with the analytic setting...
import pandas as pd
from datetime import date
# CMS POS code -> canonical analytic setting bucket.
# For telehealth, the correct mapping depends on date (POS 10 introduced 2022-01-01).
# For hospital outpatient, POS 19 (off-campus) and 22 (on-campus) are both hospital outpatient;
# POS 19 was introduced in 2016 — prior to that all hospital outpatient was billed as POS 22.
SETTING_MAP = {
"11": "office",
"19": "outpatient_hospital", # off-campus, post-2016
"22": "outpatient_hospital", # on-campus (also used pre-2016 for all hosp outpatient)
"21": "inpatient_physician", # physician billing for admitted patient -- link to MedPAR
"23": "emergency_department",
"24": "ambulatory_surgical_center",
"31": "skilled_nursing_facility",
"12": "home",
"81": "independent_laboratory",
# telehealth: 02 always telehealth; 10 added 2022-01-01 for in-home
"02": "telehealth",
"10": "telehealth", # only valid from 2022-01-01; treated as telehealth regardless
}
TELEHEALTH_CODES = {"02", "10"}
POS_10_EFFECTIVE = date(2022, 1, 1)
def classify_pos(df: pd.DataFrame,
pos_col: str = "pos_code",
svc_date_col: str = "svc_date") -> pd.Series:
"""
Classify each professional claim service line into an analytic setting bucket.
Parameters
----------
df : pd.DataFrame
Must contain columns for pos_code (str or int) and svc_date (date or str ISO).
pos_col : str
Column name holding the two-digit POS code (will be zero-padded to 2 chars).
svc_date_col : str
Column name holding the service date (converted to datetime.date internally).
Returns
-------
pd.Series of str
Analytic setting bucket for each row. Unmapped codes return "other".
"""
pos = df[pos_col].astype(str).str.strip().str.zfill(2)
svc_dt = pd.to_datetime(df[svc_date_col]).dt.date
def _row_setting(p: str, svc: date) -> str:
if p == "10" and svc < POS_10_EFFECTIVE:
# POS 10 did not exist before 2022; treat as unmapped / data quality flag
return "pos10_before_effective_date"
return SETTING_MAP.get(p, "other")
return pd.Series(
[_row_setting(p, s) for p, s in zip(pos, svc_dt)],
index=df.index,
name="analytic_setting"
)
# --- worked example ---
if __name__ == "__main__":
lines = pd.DataFrame({
"person_id": [1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008],
"svc_date": ["2023-06-01"] * 8,
"pos_code": ["11", "22", "23", "02", "10", "11", "19", "21"],
"cpt_code": ["99213", "99214", "99283", "99213", "99214",
"99215", "99213", "99232"],
})
lines["analytic_setting"] = classify_pos(lines)
print(lines[["person_id", "pos_code", "analytic_setting"]])
# Telehealth flag: POS IN (02, 10) -- MUST include both for post-2022 data
telehealth_flag = lines["pos_code"].astype(str).str.zfill(2).isin(TELEHEALTH_CODES)
print(f"\nTelehealth lines (POS 02 or 10): {telehealth_flag.sum()}")
# If analyst mistakenly used POS 02 only:
pos02_only = lines["pos_code"].astype(str).str.zfill(2).isin({"02"})
print(f"POS 02 only (undercount): {pos02_only.sum()}")
# Distribution
print("\nSetting distribution:")
print(lines["analytic_setting"].value_counts())r implementation
R implementation of the POS setting classifier for professional claims. Uses a named vector lookup for the setting map and applies a date-conditional rule for the 2022 POS 10 split. Demonstrates the telehealth dual-code flag and the outpatient hospital...
library(dplyr)
# POS code -> analytic setting bucket
pos_setting_map <- c(
"11" = "office",
"19" = "outpatient_hospital", # off-campus outpatient, introduced 2016
"22" = "outpatient_hospital", # on-campus outpatient (also used pre-2016 for all hosp outpatient)
"21" = "inpatient_physician", # physician bill for admitted patient -- link to MedPAR
"23" = "emergency_department",
"24" = "ambulatory_surgical_center",
"31" = "skilled_nursing_facility",
"12" = "home",
"81" = "independent_laboratory",
"02" = "telehealth",
"10" = "telehealth" # valid from 2022-01-01 only
)
POS_10_EFFECTIVE <- as.Date("2022-01-01")
#' Classify professional claim service lines by POS setting.
#'
#' @param df A data frame with columns pos_code (character) and svc_date (Date or ISO string).
#' @param pos_col Column name for the two-digit POS code.
#' @param svc_date_col Column name for the service date.
#' @return The input data frame with an added column analytic_setting.
classify_pos <- function(df,
pos_col = "pos_code",
svc_date_col = "svc_date") {
df %>%
mutate(
# zero-pad to 2 chars to handle integer or character input
pos_padded = stringr::str_pad(as.character(.data[[pos_col]]),
width = 2, side = "left", pad = "0"),
svc_date_parsed = as.Date(.data[[svc_date_col]]),
analytic_setting = case_when(
# POS 10 was not valid before 2022-01-01
pos_padded == "10" & svc_date_parsed < POS_10_EFFECTIVE ~
"pos10_before_effective_date",
pos_padded %in% names(pos_setting_map) ~
unname(pos_setting_map[pos_padded]),
TRUE ~ "other"
)
) %>%
select(-pos_padded, -svc_date_parsed)
}
# --- worked example ---
lines <- data.frame(
person_id = 1001:1008,
svc_date = as.Date("2023-06-01"),
pos_code = c("11", "22", "23", "02", "10", "11", "19", "21"),
cpt_code = c("99213", "99214", "99283", "99213", "99214",
"99215", "99213", "99232"),
stringsAsFactors = FALSE
)
lines <- classify_pos(lines)
print(lines[, c("person_id", "pos_code", "analytic_setting")])
# Telehealth flag -- MUST include both POS 02 and POS 10 for post-2022 data
telehealth_flag <- lines$pos_code %in% c("02", "10")
cat(sprintf("\nTelehealth lines (POS 02 or 10): %d\n", sum(telehealth_flag)))
# Demonstrate undercount if analyst uses POS 02 only
pos02_only <- lines$pos_code == "02"
cat(sprintf("POS 02 only (undercount): %d\n", sum(pos02_only)))
# Setting distribution
cat("\nSetting distribution:\n")
print(table(lines$analytic_setting))