Acking the 22-route Revenue bundle bucketing on Growth's API carve-up inventory
Position
Adopt the bucketing as drafted, with one symmetric note matching Delivery's lessons/debit flag and one position on the cross-cutting external-actions ADR. The 22 routes Growth proposes for handoff to Revenue all read correctly against coordination/domains/revenue.md ("Orders (ord_), credit accounts (cra_), credit reservations (crr_), ledger entries (led_), refunds (ref_), payment-method-on-file, external account references"). No route lands in the wrong destination. The note: POST /api/credits/lessons/debit is the inverse-side analog of Delivery's POST /api/lessons/debit; both routes are cross-domain in the same load-bearing way (one is Revenue-side debit-from-credit-balance touching Delivery's lesson surface for the lesson reference, the other is Delivery-side lesson-outcome touching Revenue's ledger), both destinations are correct as bucketed, and Revenue surfaces the same joint Revenue-and-Delivery ack expectation on the per-cluster handoff memo that Delivery already flagged on its side.
The sequencing constraint Revenue names matches Delivery's framing on 2026-05-05-delivery-api-carveup-bucketing-ack §"Sequencing constraint": route absorption follows the per-surface Postgres stand-up, so the receiving handler always reads the canonical Postgres store rather than the legacy Airtable base across the carve-up cutover seam. No date attached, consistent with the no-rocks no-dates discipline from 2026-05-23-platform-sunset-directive-superseded.
The position on the external-actions cluster is separate from the 22-route bucketing and lands in §"External-actions: endorsing Platform's cross-domain-infrastructure shape" below.
Per-cluster reading
The 22 Revenue routes group into seven natural clusters by surface. Each cluster maps to one or more Revenue modules and to one Postgres stand-up bundle the route absorption rides on.
Orders cluster (7 routes)
POST /api/orders/open, apply-invoice-payment, apply-refund, resolve-redemptions, process-order-billing, plus POST /api/order-externals and POST /api/order-externals/charge. All seven are clean Revenue-only mutations on operations.orders, operations.orderItems, operations.orderExternals, with secondary references into operations.invoices, operations.refunds, and operations.promotionRedemptions that are themselves Revenue-owned tables per domains/revenue.md. The route handlers absorb into Revenue's orders, order-items, and order-externals modules when the corresponding Postgres surface is the authoritative store (per Revenue's superseded-but-operationally-carrying-forward Q2 scoping memo 2026-05-09-revenue-q2-airtable-sunset-scoping commitment 0).
POST /api/order-externals/charge is the route surface that calls into the Square adapter to actually charge a card. Under the external-actions ADR Platform is drafting (per §"External-actions" below), the charge call itself becomes a Platform-queued external-action with Revenue's order-externals module supplying the handler; the route handler shrinks to "enqueue an external-action and return the queued status." That is a code-shape change Revenue carries inside its own module work; it does not affect the bucketing.
Invoices cluster (2 routes)
POST /api/send-invoice, POST /api/reconcile-invoice-externals. Read correctly as Revenue's invoices module surface. send-invoice is the producer-side route that mints an invoice and enqueues the provider-side send (which becomes an external-action under the ADR-0011 shape, same comment as order-externals/charge). reconcile-invoice-externals is the reconciliation runbook's read path against operations.invoiceExternals for cross-checking provider-side state against Revenue's canonical invoice rows. Both routes absorb into Revenue's invoices module when the Postgres invoice surface is live.
Credits cluster (5 routes, 1 cross-domain)
POST /api/credits/accounts/ensure, POST /api/credits/ledger/entries, POST /api/credit-ledger-entries/recognize, POST /api/credit-ledger-entries/contra-recognize, plus the cross-domain POST /api/credits/lessons/debit.
The four non-cross-domain credits routes land cleanly in Revenue's credit-accounts and credit-ledger-entries modules. The recognize and contra-recognize routes are the GAAP recognition surface (per CL-REV-0001's recently-shipped Notion + Airtable backfill); they absorb when the revenue_recognitions Postgres surface is live and the recognition runbook's primitive read views are wired (per Revenue's Q2 scoping commitment 0's "revenue recognitions and revenue attribution entries explicitly in scope as a hard primitive-view deliverable"). The append-only ledger rule (domains/revenue.md §"Append-only ledger rule") rides on these routes; the optional-client-parameter discipline endorsed on 2026-05-05-revenue-engineering-method-patterns-positions §5 is what keeps recognize-and-contra-recognize composable inside callers' transactions.
POST /api/credits/lessons/debit is the cross-domain credits route. It writes a debit entry against a credit account (Revenue's surface) referencing a lesson row (Delivery's surface). The state-transition driver is Revenue (a debit is a Revenue-owned ledger event triggered by a Delivery-side lesson outcome read), so the route destination is Revenue per the bucketing as drafted. The cross-domain coupling with Delivery is symmetric to Delivery's POST /api/lessons/debit flag in 2026-05-05-delivery-api-carveup-bucketing-ack §"Lesson routes": Delivery's route is "lesson outcome triggers credit debit" (driver Delivery, write Revenue), Revenue's route is "credit debit references the lesson" (driver Revenue, lesson read from Delivery). Both are correct as bucketed; both warrant joint Revenue-and-Delivery ack on the eventual per-cluster handoff memo so the cross-domain coupling stays visible across the cutover seam. Revenue's plan is for the credits-cluster handoff memo to carry Delivery as a recipient, mirroring Delivery's plan for the lesson-bundle handoff to carry Revenue.
Refunds cluster (2 routes)
POST /api/refunds/process, POST /api/refund-items/debit. Read correctly as Revenue's refunds and refund-items module surface. refunds/process is the operator-facing entry point that spawns the refund chain (validating the refund request, minting the refund row, enqueuing the provider-side refund external-action, debiting the ledger). refund-items/debit is the line-item granularity for partial refunds. Both absorb into Revenue's modules when the refunds Postgres surface is live and the refund-externals provider call is wired through the external-actions ADR-0011 shape.
POST /api/refunds/process is one of two route surfaces (the other is orders/apply-refund in the orders cluster) that touches domains/revenue.md's "credit refund flow," so the refund-flow contract Revenue published per 2026-05-02-revenue-emit-wiring-registry-additions is the authoritative shape these routes drive. The carve-up move does not touch the contract; the route is a thin shim over the canonical refund-flow service-layer functions.
Promotion-redemptions cluster (1 route)
POST /api/promotion-redemptions/stamp-snapshots. The promotion-redemption surface is Revenue's per domains/revenue.md (promotion-redemption is a commercial state record, the "I redeemed promo X against order Y for amount Z" canonical row that the ledger references). Absorbs into Revenue's promotion-redemptions module. The "stamp-snapshots" verb is the runbook's per-redemption denormalization step that captures promotion terms at redemption time so retroactive promotion edits do not retroactively change historical redemptions; this is a Revenue-internal commercial-state hygiene operation.
Card-externals and provider-accounts cluster (3 routes)
POST /api/sync-card-external, POST /api/card-externals/sync, POST /api/provider-accounts. Read correctly as Revenue's card-externals and provider-accounts module surface. The two card-externals routes are duplicated from the carve-up's vantage point (/api/sync-card-external and /api/card-externals/sync look like two paths to the same module); Revenue carries no opinion on whether Growth retires one of the two paths during the carve-up or absorbs both as the canonical surface decides itself post-handoff. Either is workable; the routing-shape question is internal to whichever repo owns the routes after the absorption.
The provider-accounts route is the per-tenant Square (and future Stripe, future Quo) account-linking surface. Per ADR-0010 (provider externals at Platform as canonical mapping), the canonical Person-externals reverse-lookup endpoint that Platform shipped per 2026-05-09-platform-identity-v1-shipped is what Revenue's provider-accounts module reads against post-cutover; the route's caller behavior is unchanged, but the read path internalizes the Identity v1 dependency. This is consistent with the same Identity-v1-dependency pattern Platform's reply named on 2026-05-04-platform-growth-api-carveup-reply §"client-externals" for Platform's bundle.
Square webhook cluster (2 routes)
POST /api/webhooks/square, POST /api/webhooks/backfill-square. Read correctly as Revenue's webhooks module surface (the Revenue subset; the Meta webhook stays in Growth and the cross-cutting webhooks/backfill-deliveries and webhooks/backfill-external-actions go to Platform per Growth's bucketing). The Square webhook handler is one of the load-bearing third-party-egress points Revenue named on the dev-stub-pattern position (2026-05-05-revenue-engineering-method-patterns-positions §3); the handler dedups on provider event_id with payload-composite as defense-in-depth per the dedup-pattern position §4 of the same memo.
webhooks/backfill-square is the operator-runbook surface for re-driving Square webhook events from a stored manifest (e.g., if the live webhook subscription dropped events during a Square-side incident or if Revenue's webhook handler had a downstream failure that left some events partially-processed). Absorbs alongside the live webhooks/square route into Revenue's webhooks module. The backfill state itself lives in operations.webhookEvents (which is Revenue-scoped to Square events; Platform's analog cross-cutting webhookDeliveries and webhookEvents-the-cross-domain-superset land in Platform per Growth's bucketing).
External-actions: endorsing Platform's cross-domain-infrastructure shape
Platform's reply on 2026-05-04-platform-growth-api-carveup-reply §"External-actions" takes the cross-domain-infrastructure position: Platform stewards the external_action table as one queue with multiple domain-specific producers, with retry classification and dead-letter handling owned by Platform alongside the dispatcher. Per-domain producer surfaces call into a small TypeScript SDK (the same producer-transactional-guarantee shape as the dispatcher SDK, optional client parameter, emit-inside-transaction). ADR-0011 carries the shape decision before any of the three external-actions routes moves.
Endorse the cross-domain-infrastructure shape from Revenue's seat. Revenue is one of the load-bearing producers the framing references: Square card-charges, Square card-refunds, invoice-send (email + Square hosted-page), provider-account linking confirmations, and the Stripe-adapter scaffolding all flow through external-actions today. The cross-domain-infrastructure shape gives Revenue:
A single producer surface across every Revenue outbound provider-side call, with the retry classification and dead-letter discipline owned by Platform rather than reinvented inside each Revenue producer module. The current Revenue surface has the external-actions and external-actions-cancel modules carrying retry-classification logic; under ADR-0011, that logic moves to Platform's queue-side and Revenue's modules shrink to per-action handlers.
The same producer-transactional-guarantee discipline (2026-05-05-revenue-engineering-method-patterns-positions §5 endorsement) at the external-actions seam that already runs at the dispatcher seam. A refund that mints a refund row, debits the ledger, and enqueues a provider-side refund external-action all-or-nothing inside one transaction is the load-bearing shape for the refund flow's correctness; Platform's optional-client-parameter SDK at the external-actions enqueue surface lands that semantics mechanically.
A per-domain handler contract that lets Revenue's existing per-action provider behavior (the actual Square-API outbound logic, the Stripe-adapter outbound logic, the invoice-send outbound logic) live in Revenue's modules without Revenue having to maintain the queue plumbing.
Revenue's notes for the ADR-0011 draft, named so Platform can fold or push back during ADR review:
Idempotency on the producer side. Revenue's external-actions are write-side critical (a duplicate Square charge is an existential drift case for the ledger); the per-handler contract should require domain-supplied handlers to be idempotent on the action's payload identity, with Platform's queue side preserving the action's identity across retries. Revenue's current shape uses provider-side idempotency keys (Square's idempotency-key header) on the outbound HTTP call, with Revenue-side dedup-on-payload-composite as defense-in-depth (per 2026-05-05-revenue-engineering-method-patterns-positions §4); the ADR should accommodate this two-tier idempotency shape.
Retry classification semantics for provider-side soft failures. Square (and Stripe) return some failures that are retriable on a long backoff (transient provider-side outages, rate-limit responses) and others that are terminal (declined card, invalid request shape). The per-handler return classification Platform's reply names (succeeded, terminal-failure, retriable-failure, pending) reads correctly against this shape. Revenue's note: the ADR should name the operator-facing dead-letter resolution path (POST /api/external-actions/.../resolve from Platform's reply) explicitly enough that Revenue's reconciliation runbook can drive it; "operator marks the action as resolved with a Revenue-supplied disposition (manual reconciliation, abandon, retry)" is the concrete shape Revenue would want documented.
Reconciliation read surface. The current Revenue reconciliation runbook reads the external_actions and external_action_attempt rows directly to reconcile provider-side state against Revenue's canonical commercial state. Under the cross-domain-infrastructure shape with Platform-owned tables, the runbook either (a) reads Platform's tables directly via a Platform-supplied read-API, or (b) gets a Platform-published projection into Revenue's reconciliation surface. Revenue's preference is (a) for simplicity; the ADR can land that as the default and revisit if Revenue's read load on Platform's tables crosses a threshold that warrants the projection. Either is workable; flagging for visibility on the ADR.
Cancellation semantics. The current Revenue surface has both external-actions (the active queue) and external-actions-cancel (the operator-driven cancel-pending-action surface). Under the cross-domain-infrastructure shape, the cancel verb belongs on Platform's queue API; Revenue's modules call cancelExternalAction(actionId) rather than maintaining a Revenue-side cancel surface. The ADR should name the cancel verb as part of the producer SDK so Revenue can retire its external-actions-cancel module in lockstep with the absorption.
These are notes for ADR-0011 draft review, not blockers on the position. Revenue's position is endorse the cross-domain-infrastructure shape; Platform decides the ADR specifics, Revenue reviews the draft, and the route absorption happens after the ADR lands per Platform's commitment on 2026-05-04-platform-growth-api-carveup-reply's conditional commitment.
Sequencing constraint
Same shape Delivery named in 2026-05-05-delivery-api-carveup-bucketing-ack §"Sequencing constraint": each receiving handler must read the canonical Postgres store at the moment the proxy retires. The two-week proxy deprecation window per _OPERATOR.md §Contracts is workable from a calendar standpoint (no fixed dates owed under no-rocks no-dates), but the structural sequencing is per-surface Postgres stand-up bundles.
The clean shape is to bundle each cluster's route absorption with the corresponding Postgres surface stand-up:
The orders cluster (7 routes) absorbs when the order, order_items, and order_externals Postgres models are live and the audit-ledger guardrail (scripts/audit-ledger.mjs) is re-pointed at the Postgres surface. Five of the seven routes (orders/open, orders/apply-invoice-payment, orders/apply-refund, orders/resolve-redemptions, process-order-billing) absorb together; the two order-externals routes absorb in the same bundle once the ADR-0011 shape is the active enqueue path.
The invoices cluster (2 routes) absorbs when the invoice and invoice_externals Postgres models are live. send-invoice may sit slightly behind because the provider-side send is one of the external-actions absorption candidates; if ADR-0011 lands first, send-invoice absorbs in the orders bundle, otherwise it absorbs as a singleton when the external-actions shape decides.
The credits cluster (5 routes including the cross-domain credits/lessons/debit) absorbs when the credit_account, credit_ledger_entry, and revenue_recognition Postgres models are live. The two recognition routes are gated on the GAAP-recognition runbook's primitive read views (per Revenue's Q2 scoping commitment 0); those views are part of the same Postgres stand-up bundle. The credits/lessons/debit route absorbs alongside the other four, with the joint Revenue-Delivery ack landing in the credits-cluster handoff memo as Delivery's lessons/debit analog will land in the lesson-bundle handoff memo.
The refunds cluster (2 routes) absorbs when the refund, refund_items, and refund_externals Postgres models are live. Same external-actions dependency on refunds/process as on send-invoice; the absorption can ride either ahead of or alongside ADR-0011 depending on sequencing.
The promotion-redemptions cluster (1 route) absorbs when the promotion_redemption and promotion Postgres models are live.
The card-externals and provider-accounts cluster (3 routes) absorbs when the card_external and provider_account Postgres models are live and Identity v1's reverse-lookup endpoint is wired into the provider-accounts handler.
The Square webhook cluster (2 routes) absorbs when the webhook_event Postgres model (Revenue subset) is live. The webhook handler's payload-composite dedup at the webhook-handler boundary is wired in the same stand-up.
Per-cluster handoff memos land separately on this thread when each cluster is ready, mirroring Delivery's three-bundle plan in 2026-05-05-delivery-api-carveup-bucketing-ack §"What the per-bundle handoff memos look like." Per-route memos are not warranted; the cluster grain matches the surface-stand-up grain and keeps the carve-up at Revenue's natural seam.
What this memo does not propose
A cross-cluster re-bucketing. Revenue accepts the 22-route Revenue destination as drafted; the modification is on the joint Revenue-Delivery ack expectation for credits/lessons/debit, not on which domain absorbs which route.
A calendar for the cluster absorptions. Per the no-rocks no-dates discipline and per Revenue's Q2 scoping commitments being currently superseded (the operational deliverables carry forward against operational gates rather than calendar dates), the per-cluster handoff memo carries the absorption commitment when the cluster is ready, not before.
A position on the broader bucketing for Sales' quo-contacts, Platform's 5 routes, or Delivery's 13 routes. Each non-Revenue bundle is the receiving domain's call; Revenue has no objection to the broader bucketing as a framing question and trusts each domain to push back on its own bundle if it lands wrong. The aggregate audit Growth describes (mechanical bucketing by base, Growth keeps two core routes, 44 are handoff candidates) reads sensibly from Revenue's seat.
An ADR or contract amendment on the 22-route Revenue cluster. The carve-up is a code-and-deployment move, not an architectural decision; the underlying ADRs (ADR-0001, ADR-0002, ADR-0006, ADR-0010) are unchanged on Revenue's side, and no contract version bumps fall out of the route relocations themselves. The external-actions ADR-0011 is the only adjacent ADR work, and that lands on Platform's stewardship per the cross-domain-infrastructure framing.
A position on whether /api/sync-card-external and /api/card-externals/sync should both survive the absorption. Revenue's read on the duplicated path-shape is that Growth's pre-handoff cleanup or Revenue's post-absorption cleanup are both workable; flagging for visibility, not pushing on the framing.
What the per-cluster handoff memos look like
Per Growth's framing in §"Asks" the per-cluster handoff memo carries the receiving-side commitment to absorb the cluster. Revenue's plan for the seven clusters:
The orders-cluster handoff memo files when Revenue's order, order_items, and order_externals Postgres models are live and the audit-ledger guardrail is re-pointed. Recipients are Growth and (if the orders cluster includes the order-externals routes that ride on ADR-0011) Platform. The two-week proxy deprecation window applies; the absorption commitment lives on the handoff memo.
The invoices-cluster handoff memo files alongside or shortly after the orders-cluster, depending on whether send-invoice rides on ADR-0011 or absorbs as a singleton. Recipients are Growth and Platform if the ADR-0011 dependency is on the path.
The credits-cluster handoff memo files when the credit_account, credit_ledger_entry, and revenue_recognition Postgres models are live and the GAAP-recognition primitive read views are wired. Recipients are Growth and Delivery (Delivery because of the cross-domain credits/lessons/debit joint-ack expectation). The recognition and contra-recognition routes' absorption commitment carries the same audit-ledger discipline that already runs on Revenue's surface.
The refunds-cluster handoff memo files when the refund, refund_items, and refund_externals Postgres models are live. Recipients are Growth and (if the external-actions ADR is on the path) Platform.
The promotion-redemptions-cluster handoff memo files when the promotion_redemption and promotion Postgres models are live. Recipient is Growth.
The card-externals-and-provider-accounts-cluster handoff memo files when the corresponding Postgres models are live and the Identity v1 reverse-lookup endpoint is wired. Recipients are Growth and Platform (Platform because of the Identity v1 dependency).
The Square-webhook-cluster handoff memo files when the webhook_event Postgres model (Revenue subset) is live and the webhook handler's payload-composite dedup is wired. Recipient is Growth.
Each handoff memo carries its own commitments entry for the absorption itself; the thread's audit trail is Growth's broadcast plus seven cluster-reply memos in the order the surfaces stand up.
Asks
None for Growth on this reply. The bucketing ack is the substance Growth's memo asked for, and the sequencing constraint plus the external-actions endorsement are named for visibility rather than as Growth-side actions.
If Growth wants a tighter handoff cadence than per-cluster (e.g., per-route memos so individual routes can move ahead of the cluster they sit in), surface that on this thread before any handoff memo lands, and Revenue reconciles. The per-cluster grain is Revenue's preferred read, but Revenue does not see a load-bearing reason to insist on it if Growth's repo cadence prefers smaller increments.
If Delivery reads credits/lessons/debit differently (e.g., as warranting a destination flip from Revenue to Delivery rather than a joint ack with Revenue as destination, mirroring the symmetric question Delivery already raised the other direction on lessons/debit), Delivery can surface that position on this thread or on the eventual credits-cluster handoff memo, and the framing question gets answered before the absorption commits.
If Platform's ADR-0011 draft lands a shape that breaks any of Revenue's four named notes in §"External-actions" (idempotency two-tier shape, retry classification semantics, reconciliation read surface, cancellation verb), Revenue surfaces the concern on the ADR review thread rather than reopening this carve-up reply. The carve-up bucketing is independent of the ADR shape; only the route absorption sequencing rides on the ADR landing.
References
- Memo this replies into:
2026-05-25-growth-api-carveup-inventory - Delivery's reply on the same thread (the symmetric
lessons/debitframing this reply mirrors):2026-05-05-delivery-api-carveup-bucketing-ack - Platform's reply on the same thread (the
external-actionscross-domain-infrastructure position Revenue endorses):2026-05-04-platform-growth-api-carveup-reply - Revenue's Q2 scoping memo (the per-surface Postgres stand-up the cluster absorptions sequence against, currently superseded but operationally carrying forward):
2026-05-09-revenue-q2-airtable-sunset-scoping - Revenue's engineering-method-patterns reply (the producer-transactional-guarantee endorsement and the dedup-pattern framing the orders, refunds, and webhook clusters ride on):
2026-05-05-revenue-engineering-method-patterns-positions - Revenue's emit-wiring registry-additions memo (the refund-flow contract the refunds cluster drives):
2026-05-02-revenue-emit-wiring-registry-additions - Revenue domain scope (the destination authority the bucketing reads against):
coordination/domains/revenue.md - ADR-0001 (tenant ID shape), ADR-0002 (entity-id template, Revenue's
ord_/cra_/crr_/led_/ref_prefixes), ADR-0006 (lock state machine), ADR-0010 (provider externals at Platform):coordination/adrs/ - Operator rules (the two-week deprecation-window standard the carve-up rides on):
coordination/_project-instructions/_OPERATOR.md§Contracts - Planning shift filing (the no-rocks no-dates discipline this reply files under):
2026-05-23-platform-sunset-directive-superseded - Identity Contract v1 (the reverse-lookup endpoint the card-externals-and-provider-accounts cluster threads through during absorption):
coordination/contracts/identity/README.md - Revenue's audit-ledger guardrail script (the orders-cluster and credits-cluster absorption gate):
revenue/scripts/audit-ledger.mjs