logicsrc/prd/0002-hourly-hire-us-rate.md
Anthony Ettinger eaf0a6162b
feat(web): move Hire Us pricing to $400/hour metered billing (PRD 0002) (#102)
* feat(web): move Hire Us pricing to $400/hour metered billing (PRD 0002)

Replaces the $250/week retainer with a $400/hour rate billed against actual
hours, invoiced through CoinPay after the client approves them. A 10-hour
minimum engagement replaces the week as the unit of commitment.

The weekly price lived in 12 places, not the 3 the PRD listed: the front-page
Hire Us section, the Top-Level Pages list, /hire-us metadata, /pricing
(metadata, two FAQ answers, rate bullet), /about, llms.txt, skill.md, and the
Hire Us form success message.

Metered billing rather than a committed weekly block, because the old
"recurring CoinPay invoice" copy documented a mechanic that never existed:
/api/payments/create makes a single one-shot payment, not a subscription.

- coinpay-checkout derives amount_usd from hours x 400 instead of a hardcoded
  250, validates hours as quarter-hour increments at or above the minimum, and
  returns 422 before calling CoinPay on bad input. Payment metadata carries
  billing/hours/rate_usd_per_hour in place of interval.
- project-request returns a rate, billing mode, and minimum; no amount exists
  until hours are approved.
- CoinPay config block documents COINPAY_RATE_USD_PER_HOUR / COINPAY_BILLING /
  COINPAY_MINIMUM_HOURS instead of a weekly amount and interval.
- New real /terms route replacing the SPA stub: what is billable, the
  approve-then-invoice flow, the minimum, cancellation on one week's notice,
  and an explicit clause that existing engagements keep their terms until both
  sides agree in writing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(mcp): advance prd_next_id expectation to 0003 for PRD 0002

The standards test asserts prd_next_id against the live prd/ directory, so
adding prd/0002-hourly-hire-us-rate.md moves the next free id to 0003. This
assertion advances with every PRD added to the repo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 10:34:49 -07:00

146 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
openprd: "0.2"
id: "0002"
title: Move Hire Us pricing from a weekly retainer to an hourly rate
status: Accepted
authors:
- anthony@profullstack.com
created: 2026-07-28
updated: 2026-07-28
repo: https://github.com/profullstack/logicsrc
implementation: apps/logicsrc-web
tags: [pricing, site, billing]
---
## Problem
The Hire Us surface on logicsrc.com prices standards work at $250/week, paid through a
recurring CoinPay invoice after project acceptance. That number reads as a token retainer
rather than a rate for senior open-spec implementation work — schemas, CLIs, SDKs, MCP
resources, and provider-neutral plugin surfaces. It anchors every inbound conversation at a
price that cannot cover the work, and it selects for clients who are shopping on price rather
than on the standard.
The replacement is $400/hour. This is not a bump to an existing hourly number; it is a change
of pricing *model*, which touches page copy, the payment configuration, and the invoicing
mechanics that currently assume a fixed weekly amount.
## Goals
- Every public price on logicsrc.com states one rate, in one unit, with no stale $250/week
copy left behind on any surface.
- Inbound Hire Us inquiries arrive already anchored to a senior rate, so the pricing
conversation is about scope rather than about the number.
- Billing can actually execute the new model: an accepted project produces a correct invoice
without manual repair.
- The change is reversible and auditable — the reasoning survives in the repo, not in a Slack
thread.
## Non-Goals
- Repricing the standard itself. LogicSRC schemas, specs, and reference implementations stay
open and free; this covers implementation services only.
- Building time tracking. Hourly billing needs hours captured, but that is an operational
process for now, not a product to build.
- Replacing CoinPay or adding a second payment provider.
- Publishing a rate card with tiers, discounts, or role-based pricing. One rate, one line.
- Migrating anyone currently engaged at $250/week. Handled case by case, not by this PRD.
## Users
- **Prospective clients** evaluating whether to hire Profullstack for LogicSRC work — mostly
founders and engineering leads arriving from the spec pages, who read the price before they
read anything else.
- **Profullstack**, as the party quoting, invoicing, and collecting.
- **Existing clients** on the weekly plan, who must not be silently repriced.
## Decisions
Two questions blocked this PRD at drafting time. Both were resolved before implementation:
- **The rate is $400/hour, confirmed deliberately.** Against $250/week this is roughly a 64x
change at a 40-hour week. The magnitude is the point: the weekly figure was a token retainer,
not a rate, and the new number is intended to filter out engagements too small to scope.
- **Billing is metered against actual hours, not a committed weekly block.** This matches how
the code already works — `/api/payments/create` creates a single one-shot payment, never a
recurring subscription, so the previous "recurring invoice" copy documented a mechanic that
did not exist. Metered billing also avoids the committed-block failure mode, which is a
weekly rate wearing an hourly label. A **10-hour minimum engagement** replaces the week as
the unit of commitment.
## Requirements
- R1 [P0] The Hire Us section heading states the new rate and unit, replacing "$250/week for
accepted LogicSRC work".
- R2 [P0] The price display block shows $400 with the unit "per hour".
- R3 [P0] The `/hire-us` entry in the Top-Level Pages list is updated; it repeated the weekly
figure independently of the section above it.
- R4 [P0] The `/hire-us` page body and its meta description are updated.
- R5 [P0] The CoinPay configuration block reflects the new model. `COINPAY_AMOUNT_USD=250` and
`COINPAY_INTERVAL=week` are both wrong under hourly pricing and are replaced by a rate, a
billing mode, and a minimum rather than renumbered.
- R6 [P0] A repo-wide search for `250`, `per week`, and `/week` returns no remaining pricing
references before the change is considered done.
- R7 [P1] The site states what an hour is billed against — metered actual hours, invoiced after
the client approves them — so the invoice mechanic is legible before a client asks.
- R8 [P1] Minimum engagement and cancellation terms are stated, since removing the weekly
cadence also removes the implicit unit of commitment.
- R9 [P2] Terms of engagement are documented at `/terms` rather than only in marketing copy.
Surfaces the original draft did not list, but which carried the weekly price and were therefore
in scope for R6: `/pricing` (metadata, two FAQ answers, and the rate bullet), `/about`,
`llms.txt`, `skill.md`, the Hire Us form's success message, and both the contract and e2e tests.
## UX Notes
The price appears in several places across the front page, `/pricing`, `/about`, and the
machine-readable surfaces; they are separate strings and will drift if edited one at a time.
Treat the set as one change.
The CoinPay block is rendered as example configuration, so it reads as documentation of how
billing actually works. A weekly interval next to an hourly rate is worse than a stale price —
it looks like the system does not do what the copy says.
The stated rate sits next to what it buys. The existing four capability cards (workflow specs,
reference implementations, integration hardening, open infrastructure) already do that work and
are unchanged.
## Implementation Notes
- `src/lib/page-markup.ts` — section heading, price row, a new `.price-terms` line carrying the
minimum and cancellation summary, the CoinPay config block, and the Top-Level Pages entries
for both Hire Us and Terms.
- `src/app/api/hire-us/coinpay-checkout/route.ts` — the amount is now derived as
`hours × $400` rather than hardcoded. Hours are validated as quarter-hour increments at or
above the 10-hour minimum, defaulting to the minimum when omitted; invalid hours return 422
before any call to CoinPay. Payment metadata carries `billing`, `hours`, and
`rate_usd_per_hour` in place of `interval`.
- `src/app/api/hire-us/project-request/route.ts` — returns a rate, billing mode, and minimum
instead of a fixed `amount_usd`/`interval` pair, since no amount exists until hours are
approved.
- `src/app/terms/page.tsx` — new real route, replacing the `/terms` stub that rendered the
homepage SPA. The stub's entry was removed from the catch-all's `ROUTE_META`.
## Success Metrics
- Zero occurrences of the weekly price across the site and repo after the change, verified by
grep rather than by reading.
- First accepted project under the new model invoices correctly on the first attempt, with no
manual adjustment to the CoinPay invoice.
- Inbound inquiries that reach a scoping conversation do not open by disputing the rate.
- No existing engagement is repriced without explicit agreement.
## Risks & Open Questions
- **Hours are not currently tracked.** Metered billing won, and there is no capture mechanism.
`/terms` now defines what is billable (project work, meetings, and written communication in
quarter-hour increments; not scoping calls, invoicing, or warranty fixes), so the definition
is settled even though the tooling is not. Capturing hours is an operational process until it
is worth building.
- **Client mix will change.** An hourly rate at this level filters out the small experimental
engagements the weekly price attracted. This is the intent, recorded here as a decision
rather than left to be discovered.
- **Existing weekly clients** keep their terms until both sides agree in writing to move, per
`/terms`. Whether any active weekly engagements exist is still unknown.
- **Author attribution** is assumed from the CoinPay org configuration and should be corrected
if wrong.