odapm/v1 · your AI
Build a rate sheet
One prompt. Your AI fetches this page's files: walk.md, model.json (hardwired SKUs), and tax.json (starter). It fills them in. Then you test both on the rate sheet and attach them in OpenData. This is not an odapm.ai account.
One rule. Never paste a licensed proprietary price list. Your own costs are welcome. A competitor's book is not.
Paste this once
Copy the block below into your AI. You do not copy the later steps one by one. ChatGPT should open the walk and the SKU table. If it cannot, paste those pages into the chat.
Paste this
Use your browser. Open these HTML pages (not the .md — ChatGPT often cannot fetch markdown): https://odapm.org/build/walk/ https://odapm.org/build/skus/ JSON twins if you can open them: https://odapm.org/build/model.json and https://odapm.org/build/tax.json If you cannot open the HTML, say so — do not invent the catalog. Follow walk.md in order. You may rebuild the JSON. The catalog must be exact: every kept item's id, name, unit, group, and pick option ids copied character-for-character from model.json. Do not add, rename, clean up, or invent SKUs. Skip = omit that object, not a new id. Fill meta and prices on those SKUs. Fill tax.json jurisdictions; keep schema odapm-tax/v1 and tax_applies_to material. I never upload a file to you. When a file is ready, emit the full document as a fenced block I can copy. Do not ask "what file?" or "should I generate it?" Ask me the questions from that walk, one topic at a time. Do not invent my region, markup, or loss types. ECEC burden_pct is 100 × (benefits share of total compensation) / (wages share of total compensation) — cite both. If I give my own crew wage it replaces OEWS as the unburdened rate. Never use a licensed proprietary price list.
Start
Bring your own model. Mileage varies — some follow the spec, some invent fields, some skip basis. You are the check. When a step finishes, keep the file and move to the next prompt in the same session.
Labor burden is not a question. It is derived from BLS Employer Costs for Employee Compensation (ECEC), construction, benefits as a percent of wages. The wage floor is the federal local wage survey for construction occupations (BLS OEWS) for their metro. Markup is still yours, and it is disclosed.
01 — Region
What you sell, where you work, which metro sets the wage floor, your markup, your units. Not your burden percentage. Crew wage is optional; otherwise it is derived from OEWS + ECEC.
Paste this
I'm building an ODAPM pricing model (schema odapm/v1). Help me set the foundation. Ask me, one topic at a time: - The loss types I handle (e.g. water/Cat 1–3, fire & smoke, mold, storm, contents). - My service region (metro / counties) and my home-base city. - Which metro to price labor in. Use the federal local wage survey for construction occupations — BLS Occupational Employment and Wage Statistics (OEWS) — for that metro as the wage floor. Name the metro you picked and why it matches my region. - My markup target. This is mine, as a fraction: 0.25 means cost × 1.25. Do not ask me for a labor-burden percentage. - The unit system I think in (SF, LF, EA, HR, day). Crew wage: if I give you my own number, use it and note that. Otherwise derive the hourly rate from OEWS for that metro (construction occupations) plus BLS Employer Costs for Employee Compensation (ECEC) for construction — benefits as a percent of wages. Labor burden is derived from ECEC and disclosed in basis. It is not a number I type. Don't pull in any licensed proprietary price list. When we're done, write a model.json with meta.schema "odapm/v1" covering region, base location, labor_basis (rate, source, burden_pct from ECEC), markup_target, and loss types. Leave items empty — we build those in step 02.
02 — Scope
Fetch model.json — the hardwired OpenData SKU list. Copy those items. Keep or skip. Do not add SKU ids. Missing work is a note for Support, not a new id.
Paste this
Fetch https://odapm.org/build/model.json. You may rebuild the JSON. Catalog fields must be exact: id, name, unit, group, and pick option ids character-for-character. Walk group by group. For each item ask whether I (a) keep it or (b) skip it. Do not add, rename, clean up, or invent SKU ids. Skip = omit that object. If you cannot fetch, stop and say so. If I perform work that is not on the catalog, record a short note for Support — not a new id. Leave every price null for now. When a group is done, summarize what we kept and skipped, plus any Support notes.
03 — Prices
Fixed recipe: fully-burdened wage from OEWS + ECEC, named material and rental sources, production rates, then markup on cost. A baseline table first. Every basis is one sentence of math, not “market rate.”
Paste this
Help me derive prices for the line items in my model.json. Do NOT use any licensed proprietary price list. Do not guess a market rate.
Recipe: fb_hourly = labor_basis.rate × (1 + burden_pct/100). cost = labor + material + equipment. labor = hours × fb_hourly. material = qty × unit_cost. equipment = day_rate × days / units (only if this SKU is the machine). markup = cost × markup_target. unit = cost + markup. 0.25 markup means cost × 1.25, not a 25% margin.
Split: rem = tear-out labor only. rep = install/reset labor (+ equipment share if this SKU is the machine). mat = taxable materials only. Labor is never taxed.
Before any SKU, publish a baseline table and wait for my yes: (1) fb_hourly with OEWS metro + vintage and ECEC B/W math, (2) consumable unit costs each with a public source and date, (3) equipment day-rates with named rental source and date, (4) production rates (SF/hr, LF/hr, min/EA) — ask me once; if I have none, state a conservative assumed rate labeled "assumed — shop may adjust." Hours = work / production_rate.
Then group by group. Show the math. Let me adjust one assumption at a time.
Every non-zero basis is one line: rem: {h} hr × ${fb}/hr FB (OEWS {metro}, {vintage}, {occupation} + ECEC {vintage} {burden_pct}% on wages) = ${n}; rep: …; mat: {q} × ${u} ({source}, {date}) = ${n}; eq: {d} day × ${r}/day ({rental source}, {date}) / {units} = ${n}; markup {m} on cost.
Omit $0 clauses. Never write market, industry standard, around, or a proprietary list. If you can't fill a clause, leave the price null. After each group, list anything still unpriced.
04 — Tax
Tax lives in tax.json, separate from prices. Destination-based: the rooftop of the loss picks the rate later. You do not type a shop-wide tax percent. It hits material only. Fill jurisdictions from the states and cities you cover, from each state's Department of Revenue (or equivalent free rate file). A ZIP can contain more than one rate — list zip_candidates.
Paste this
Help me build tax.json (schema odapm-tax/v1). Destination-based: the rooftop of the loss picks the rate later; I do not type a shop-wide tax percent. Tax applies to the material portion only (meta.tax_applies_to must be "material"). Labor is never taxed. I cover this service area: __________ (states / cities / counties — fill jurisdictions from that area). If I already put a region in model.json meta, use that. For each state I cover, find the free authoritative rate source (the state's Department of Revenue combined rate file or address lookup). Tell me what it is and link it. If I can download that file, use it as the source of truth. Otherwise populate each jurisdiction's combined rate from that source and mark it approximate. A single ZIP often spans multiple tax jurisdictions, so build a zip_candidates map: keys are 5-digit ZIPs, values are arrays of jurisdiction ids that can occur in that ZIP (don't guess one). Keep a lookup_url to the state's by-address tool. Write tax.json.
05 — Validate
Validate model.json and tax.json, then drop both on the rate sheet. Nothing is uploaded. This walk is not an account on odapm.ai.
Paste this
Validate my ODAPM model.json (meta.schema odapm/v1) and tax.json (meta.schema odapm-tax/v1). Report: - Any missing required fields. - tax_applies_to must be material; sourcing destination. - Every line item still priced at 0 / null (so I can decide if that's intentional). - Any priced item missing a basis note (un-auditable numbers). - A quick sanity scan: prices that look implausible high/low vs. their derivation. - Jurisdiction count and zip_candidates (5-digit ZIP keys). Then build me one test estimate that touches several line types and show the math — line item total = (qty × remove) + (qty × replace) + tax, where tax = qty × material × rate. Confirm the totals are internally consistent. Fix any schema issues you find, but never invent a price to fill a gap — flag it for me instead. When this passes, I will drop both files on https://odapm.org/rate-sheet/. This walk is not an account on odapm.ai.
05 — Update prompt
Copy the prompt below into your AI. It must write a text block with six markdown headers as their own lines: ## Labor metro, ## Markup, ## Service area, ## Labor method, ## Catalog, ## Tax. Each section needs a non-empty body. Paste that block into OpenData's "Update prompt" field (signup, or Admin → Rate pack) — not a random chat. OpenData's checker looks for these headers. A copied step-03 prompt will fail.
Paste this
Write one markdown document I will paste into OpenData's "Update prompt" field (signup, or Admin → Rate pack). Not a chat. Not JSON. Not the step-03 price prompt. OpenData's checker looks for these six headers as their own lines (case-insensitive). Each section needs a non-empty body. A copied step-03 prompt will fail. Output only that document, headers exactly as written: ## Labor metro The BLS OEWS area I price labor in (from this session / model.json meta). Name the metro. ## Markup My disclosed markup (meta.markup_target). Do not invent one. ## Service area The states / cities / counties I cover. ## Labor method BLS ECEC construction, benefits as a percent of wages. Do not invent a burden percentage. Wage floor is BLS OEWS for that metro (federal local wage survey, construction occupations). ## Catalog Do not invent SKUs. Overlay existing OpenData ids only. ## Tax Destination / rooftop of the loss. Material portion only. Labor is never taxed. Fill every section from what you already know about this shop. If a fact is missing, ask me once, then write the six-header document.
06 — Keep current
Re-index on a schedule instead of rebuilding. Labor follows the wage index, materials the construction PPI, equipment the rental PPI — not CPI, not the wage index for machines.
Paste this
Re-index my ODAPM model.json. Pull the latest published index values: labor from BLS Employment Cost Index (ECI), wages and salaries, construction; materials from BLS PPI inputs to construction; equipment from BLS PPI for commercial and industrial machinery and equipment rental and leasing — not the wage index. Compute the change since my model's last escalated date, and apply the per-item weighting (labor portion × labor index change, material portion × material index change, equipment portion × equipment index change). Show me the before/after on a few representative items and the overall percentage move before writing. Update each item and stamp a new escalated date in meta. Cite series and vintage. Then re-check tax.json against my state's current rate file (rates change too, usually twice a year) and flag any jurisdiction that moved.