Bowmark AIdocs

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

FunctionWhat it does
listHaircareProductsLists 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…
getHaircareProductPriceLooks 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…
getHairPrescriptionRuns 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") };