Prose
Personalized haircare and skincare formulas, sold direct via an online consultation.
Domain: prose.com
Also known as: Prose, prose.com, Prose Hair, Prose Skin
Call it directly
bowmark.providers.prose.listHaircareProducts(): Promise<ProseHaircareProduct[]>
bowmark.providers.prose.getHaircareProductPrice(type: string): Promise<ProseHaircareProduct>
bowmark.providers.prose.getHairPrescription(answers: HairConsultationAnswers): Promise<HairPrescription>Functions
| Function | What it does |
|---|---|
listHaircareProducts | Lists every haircare formula/tool type Prose sells with its real one-time and subscription price in USD, read off the site's own account-scoped catalog endpoint (a throwaway US-locale… |
getHaircareProductPrice | Looks up ONE Prose product type's real USD price (e.g. "shampoo", "hair_mask", "supplement_core"). THROWS naming listHaircareProducts() as the way to find current valid types, rather than… |
getHairPrescription | Runs Prose's real online hair consultation — the same 38 questions its own /consultation/haircare quiz asks — and returns the actual personalized formula the site computes: which product… |
Types
interface ProseHaircareProduct {
type: string; // e.g. "shampoo", "conditioner", "hair_mask"
category: string;
subCategory: string;
isCustom: boolean; // true for a personalized formula, false for a fixed item
isSubscribable: boolean;
currency: string; // "USD" once the account's profile_country is set
price: number;
subscribedPrice: number | null;
}
// The 38 questions Prose's own /consultation/haircare quiz asks a US customer.
// EVERY field is required: a caller answering fewer than the real quiz asks is
// exactly the manufactured-fit this function exists to avoid. Choice values are
// Prose's own, read off its API, not reworded.
interface HairConsultationAnswers {
profile_age_range: "20" | "30" | "40" | "50" | "60" | "70";
profile_zipcode: string;
profile_hydration: "less_than_3_glasses" | "4_6_glasses" | "7_8_glasses" | "more_than_8_glasses";
profile_smoke_level: "rarely" | "little" | "often";
profile_stress_level: "never" | "little" | "moderately" | "lot";
profile_stress_cause: string[]; // life events in the last 3 months, if any
profile_sports: string[];
profile_diet_composition: string[];
profile_hormones: string[];
diag_hair_texture: string; // the site's own texture codes, e.g. "type_2b"
diag_hair_length: "short" | "shoulder" | "long" | "very_long";
diag_hair_type: "dry" | "normal" | "oily" | "oily_dry";
diag_split_ends: "yes" | "no" | "not_sure";
diag_hair_porosity: "low" | "medium" | "high";
diag_hair_thickness: "thin" | "average" | "thick";
diag_density_level: number; // 0 (sparse) to 4 (dense)
diag_hairloss_level: "normal" | "bit_than_normal" | "excessive";
diag_hairloss_genetic: "yes" | "no" | "not_sure";
diag_shampoo_frequency: "once_a_week" | "every_2_3_days" | "every_day" | "less_once_a_week";
diag_regrease_delay: "never" | "second_day" | "three_more_days" | "dont_know";
diag_scalp_type: "balanced" | "dry" | "oily" | "not_sure";
diag_dandruff_level: number; // 0 (never flaky) to 4 (very flaky)
diag_sensitivity_level: number; // 0 (not sensitive) to 4 (very sensitive)
diag_gray_hair: "none" | "few" | "half" | "full";
diag_color_treatments: boolean;
diag_color_treatments_location: "full_head" | "partial" | "tips" | "not_applicable";
diag_color_treatments_type: string[];
diag_color_treatments_frequency:
| "every_month" | "two_three_months" | "few_a_year" | "just_started" | null;
diag_other_treatments: string[];
diag_other_treatments_frequency:
| "every_month" | "two_three_months" | "few_a_year" | "just_started" | null;
diag_styling: string[]; // what the caller uses today, in the site's own catalog
diag_styling_frequency: "every_day" | "once_week" | "time_to_time";
diag_extensions: string[]; // protective styles / extensions; "none" is valid
diag_hold_level: "none" | "light" | "medium" | "not_sure";
diag_routine_effort: "low" | "medium" | "high";
pref_vegan: boolean;
pref_silicone: "yes" | "no" | "low";
pref_fragrance: string;
pref_fragrance_free: boolean; // Prose's own twin field — true asks for no scent at all
// Which of Prose's 8 haircare goals the caller cares about. Each becomes a 0/5
// intensity on the account; there is no finer setting because the real quiz
// does not ask for one.
goals: Array<
| "anti_brass" | "curl" | "hair_growth" | "hair_shedding"
| "moisture" | "shine" | "straight" | "volume"
>;
}
// One line of Prose's personalized-formula output — one product TYPE and where
// it landed in the caller's routine.
interface HairPrescriptionItem {
category: string;
type: string; // e.g. "shampoo", "curl_cream"
// "main" is what the consultation tells the customer to buy today;
// "inadvisable" is a product Prose's own engine advises AGAINST for this profile.
section: "main" | "upsale" | "optional" | "featured" | "inadvisable" | string;
quantity: number;
currency: string | null;
price: number | null; // null when the type has no matching catalog entry
subscribedPrice: number | null;
}
interface HairPrescription {
items: HairPrescriptionItem[];
coreRoutineTotal: number; // one-time USD for every "main" item — today's basket
coreRoutineSubscribedTotal: number;
currency: string;
}Examples
// What does Prose charge for a shampoo, one-time vs. subscribed?
const shampoo = await bowmark.providers.prose.getHaircareProductPrice("shampoo");
return { price: shampoo.price, subscribedPrice: shampoo.subscribedPrice };// What's MY personalized Prose routine and what does it cost?
const rx = await bowmark.providers.prose.getHairPrescription({
profile_age_range: "30", profile_zipcode: "10001", profile_hydration: "4_6_glasses",
profile_smoke_level: "rarely", profile_stress_level: "moderately", profile_stress_cause: [],
profile_sports: ["indoors"], profile_diet_composition: ["meat"], profile_hormones: ["none"],
diag_hair_texture: "type_2b", diag_hair_length: "shoulder", diag_hair_type: "normal",
diag_split_ends: "no", diag_hair_porosity: "medium", diag_hair_thickness: "average",
diag_density_level: 2, diag_hairloss_level: "normal", diag_hairloss_genetic: "no",
diag_shampoo_frequency: "every_2_3_days", diag_regrease_delay: "second_day",
diag_scalp_type: "balanced", diag_dandruff_level: 0, diag_sensitivity_level: 0,
diag_gray_hair: "none", diag_color_treatments: false, diag_color_treatments_location: "not_applicable",
diag_color_treatments_type: [], diag_color_treatments_frequency: null,
diag_other_treatments: ["none"], diag_other_treatments_frequency: null,
diag_styling: ["shampoo", "conditioner"], diag_styling_frequency: "time_to_time",
diag_extensions: ["none"], diag_hold_level: "medium", diag_routine_effort: "medium",
pref_vegan: false, pref_silicone: "no", pref_fragrance: "SIGNATURE", pref_fragrance_free: false,
goals: ["curl", "moisture"],
});
return { coreRoutineTotal: rx.coreRoutineTotal, mains: rx.items.filter(i => i.section === "main") };