Tracking issue for the platform gaps surfaced while onboarding the Seeker Music org on 2026-07-03 (api#746): org membership and org-domain management have no API endpoints (both required direct SQL), enterprise-domain accounts still land on the 333 free credit tier, and the pro tier itself is being raised. All 11 PRs are merged and live (implementation: docs#261, api#748/#749/#747, chat#1842, docs#263; process: skills#58/#68, api#750, chat#1843, mono#42). Remaining: the two Open — process items.
Decision (sweetmantech, 2026-07-03): all PRs target main; the api/chat test-branch PR flow is retired. api#747/#748/#749 and chat#1842 were opened against test per the then-current CLAUDE.md instructions and retargeted to main the same day (all clean — branches were cut from origin/main). Root-cause docs fixed by api#750 / chat#1843 / mono#42; the ship-issue skill fix was already in flight as skills#68. Supersedes the "base test" notes elsewhere in this issue.
Decision (sweetmantech, 2026-07-03): chat#1842 rescoped from mirror to consolidation. Chat stops computing credits in its duplicated stack; the sidebar consumes the api's GET /api/accounts/{id}/credits (bearer auth) and chat's /api/credits/get route, checkAndResetCredits, dead initializeAccountCredits, and credit constants are deleted. Rationale: tracing showed chat's stack had one live consumer (useCredits → /api/credits/get) plus dead code; mirroring would have committed every future credits change to two repos back-to-back. Credits logic now lives only in api.
Goal
Four deliverables, all in the existing api/chat/docs stack (no new tables — account_organization_ids and organization_domains already exist):
- Org member endpoints —
POST + DELETE /api/organizations/members so members can be added/removed via the API instead of SQL inserts into account_organization_ids.
- Org domain endpoints —
POST + DELETE (+ GET) /api/organizations/domains to manage organization_domains rows (email-domain auto-join, consumed by assignAccountToOrg).
- Enterprise domains grant pro credits — accounts whose email domain is in
ENTERPRISE_DOMAINS resolve is_pro: true in the credits system, in both api and chat.
- Raise
PRO_CREDITS from 1,000 to 9,999 per month, in both repos plus the docs example.
PRs (updated 2026-07-03)
| PR |
Item |
Base |
State |
| docs#261 |
OpenAPI contract: org members add/remove + org domains add/remove/list; credits example → 9,999 |
main |
✅ merged 2026-07-03 — see Done. 401-envelope reconciliation still pending post-preview-verify |
| api#748 |
POST + DELETE /api/organizations/members |
main |
✅ merged 2026-07-03 — see Done |
| api#749 |
GET + POST + DELETE /api/organizations/domains |
main |
✅ merged 2026-07-03 — see Done |
| docs#263 |
Error envelope reconcile: member/domain errors are {status, error} (ErrorEnvelope schema; closes the 401 drift) |
main |
✅ merged 2026-07-03 |
| api#747 |
Enterprise-domain accounts resolve pro in credits + PRO_CREDITS → 9,999 |
main |
✅ merged 2026-07-03 — see Done |
| chat#1842 |
Consolidation: sidebar consumes the api credits endpoint; chat's duplicated credits stack + stripe dependency deleted |
main |
✅ merged 2026-07-03 — see Done |
| skills#58 |
PRs-matrix section + #TBD/PR-open upkeep rules in recoup-internal-dev-issue-tracker; KISS pass (Base column dropped, stale test-branch guidance + gh quick-ref deleted) |
main |
✅ merged 2026-07-03 |
| skills#68 |
ship-issue skill: targets main, PR-open matrix rule, KISS pass (Principles section deleted, JSON + preview-auth lessons corrected) |
main |
✅ merged 2026-07-03 |
| api#750 |
api CLAUDE.md: PRs target main; retire test-branch flow |
main |
✅ merged 2026-07-03 |
| chat#1843 |
chat CLAUDE.md: PRs target main; retire test-branch flow |
main |
✅ merged 2026-07-03 |
| mono#42 |
monorepo AGENTS.md: all submodule PRs target main |
main |
✅ merged 2026-07-03 |
Error-envelope convergence (2026-07-03, via api#749 review): the members+domains surface now emits {status: "error", error} everywhere via the shared errorResponse helper — matching the 401s validateAuthContext always emitted. docs#263 reconciles the contract (supersedes the earlier plan to patch only the 401 blocks). The artists endpoint keeps {status, message}.
Merge sequencing (docs-first): docs → api → chat. The docs OpenAPI change is the contract the api PRs fulfill. The two credits PRs (api + chat) should land back-to-back — chat duplicates the entire credits stack (chat/lib/credits/checkAndResetCredits.ts, chat/lib/consts.ts L55-56), so a gap between them means the sidebar shows a different tier than the API grants. The two endpoint PRs are independent of the credits PRs and of each other.
What we noticed (context)
All findings from the 2026-07-03 Seeker Music onboarding, verified against prod:
- Org Seeker Music
5d2be75b-b49b-4c2a-ac0a-63d812430dda (creator: sidney@recoupable.com). Adding its 4 members required a direct SQL insert into account_organization_ids — the only API paths that write membership are org creation (createOrganizationHandler, creator only) and email-domain auto-join.
- The
seekermusic.com → org mapping likewise required a direct SQL insert into organization_domains. Auto-join then verified end-to-end: POST /api/accounts with a @seekermusic.com email joins the org with no duplicate membership rows.
- api#746 (merged + deployed 2026-07-03) added
seekermusic.com to ENTERPRISE_DOMAINS — but that list feeds exactly one consumer: getProArtists → the nightly pro-artist-social-profiles-scrape cron. It has zero references in lib/credits, lib/stripe, or anywhere in chat. Verified live post-deploy: GET /api/accounts/8bd4cb8e-30fe-43a6-8584-3803ebbca2fe/credits (evan@seekermusic.com) still returns is_pro: false, total_credits: 333.
- Rostrum's 1,000/mo tier comes from an active Stripe subscription resolved through Rostrum Pacific org membership (
getAccountSubscriptionState → getOrgSubscription), not from the domain list — live-verified is_pro: true, total_credits: 1000 for benjy@rostrum.com and jonathan@rostrum.com, while stale rostrum-domain accounts that predate their last access sit at 333 until their next lazy refill.
Open — process
Architecture decisions
- Enterprise status stays a code-level domain list for now.
ENTERPRISE_DOMAINS in api/lib/enterprise/consts.ts currently gates only the nightly social scrape; this issue extends it to the credits tier. Moving it to a DB table is out of scope (adding a domain today = one-line PR, e.g. api#746).
- Credits pro-status precedence: active Stripe subscription (account or org) OR enterprise email domain. Stripe stays the only path that carries
current_period_start (early-refill trigger) and payment rails.
- Credits logic lives only in
api (decision 2026-07-03, supersedes the earlier accept-the-duplication stance): chat consumes GET /api/accounts/{id}/credits and its own credits computation is deleted (chat#1842).
Source references
- Reference issues for house style: chat#1747, chat#1767
- api#746 —
seekermusic.com → ENTERPRISE_DOMAINS (merged 2026-07-03, commit 97735c53)
- Docs surface for the new endpoints:
docs/api-reference/openapi/accounts.json (/api/organizations* paths) + docs/api-reference/organizations/*.mdx
Tracking issue for the platform gaps surfaced while onboarding the Seeker Music org on 2026-07-03 (api#746): org membership and org-domain management have no API endpoints (both required direct SQL), enterprise-domain accounts still land on the 333 free credit tier, and the pro tier itself is being raised. All 11 PRs are merged and live (implementation: docs#261, api#748/#749/#747, chat#1842, docs#263; process: skills#58/#68, api#750, chat#1843, mono#42). Remaining: the two Open — process items.
Goal
Four deliverables, all in the existing
api/chat/docsstack (no new tables —account_organization_idsandorganization_domainsalready exist):POST+DELETE /api/organizations/membersso members can be added/removed via the API instead of SQL inserts intoaccount_organization_ids.POST+DELETE(+GET)/api/organizations/domainsto manageorganization_domainsrows (email-domain auto-join, consumed byassignAccountToOrg).ENTERPRISE_DOMAINSresolveis_pro: truein the credits system, in bothapiandchat.PRO_CREDITSfrom 1,000 to 9,999 per month, in both repos plus the docs example.PRs (updated 2026-07-03)
mainPOST+DELETE /api/organizations/membersmainGET+POST+DELETE /api/organizations/domainsmain{status, error}(ErrorEnvelopeschema; closes the 401 drift)mainPRO_CREDITS→ 9,999mainstripedependency deletedmain#TBD/PR-open upkeep rules inrecoup-internal-dev-issue-tracker; KISS pass (Base column dropped, stale test-branch guidance + gh quick-ref deleted)mainmain, PR-open matrix rule, KISS pass (Principles section deleted, JSON + preview-auth lessons corrected)mainmain; retire test-branch flowmainmain; retire test-branch flowmainmainmainWhat we noticed (context)
All findings from the 2026-07-03 Seeker Music onboarding, verified against prod:
5d2be75b-b49b-4c2a-ac0a-63d812430dda(creator: sidney@recoupable.com). Adding its 4 members required a direct SQL insert intoaccount_organization_ids— the only API paths that write membership are org creation (createOrganizationHandler, creator only) and email-domain auto-join.seekermusic.com→ org mapping likewise required a direct SQL insert intoorganization_domains. Auto-join then verified end-to-end:POST /api/accountswith a@seekermusic.comemail joins the org with no duplicate membership rows.seekermusic.comtoENTERPRISE_DOMAINS— but that list feeds exactly one consumer:getProArtists→ the nightlypro-artist-social-profiles-scrapecron. It has zero references inlib/credits,lib/stripe, or anywhere in chat. Verified live post-deploy:GET /api/accounts/8bd4cb8e-30fe-43a6-8584-3803ebbca2fe/credits(evan@seekermusic.com) still returnsis_pro: false, total_credits: 333.getAccountSubscriptionState→getOrgSubscription), not from the domain list — live-verifiedis_pro: true, total_credits: 1000for benjy@rostrum.com and jonathan@rostrum.com, while stale rostrum-domain accounts that predate their last access sit at 333 until their next lazy refill.Open — process
test-branch deployment (test-recoup-api.vercel.app) — repoint or auto-sync.main,testonly moves when someone syncs it; it drifted 3 commits stale on 2026-07-03 and chat previews showedtotal_credits: 1000after api#747 was already on prod (caught during chat#1842 browser verification; manually synced0550e9fd).test←mainon merge (workflow); or per-PR api preview URLs.mainmerge is visible from a chat preview without a manualtestsync.test-branch deployment (test-recoup-api.vercel.app) — repoint or auto-sync.main,testonly moves when someone syncs it; it drifted 3 commits stale on 2026-07-03 and chat previews showedtotal_credits: 1000after api#747 was already on prod (caught during chat#1842 browser verification; manually synced0550e9fd).test←mainon merge (workflow); or per-PR api preview URLs.mainmerge is visible from a chat preview without a manualtestsync.recoup-internal-dev-issue-trackeronly covers keeping State current for existing PR refs, andrecoup-internal-dev-ship-issueonly updates the tracker at merge (step 8), not at PR-open.recoupable/skillstouching both SKILL.md files. Tracker skill: planned rows may userepo#TBD | item | base | ⏳ not startedso the fleet is visible before implementation. Both skills: replace the#TBDplaceholder with the live PR ref + verification status in the same session the PR opens — opening a PR without updating its matrix row is drift, same class as a merged PR without a closure note. Per the prune-as-you-add rule, cut any now-redundant wording while adding.npx skills add recoupable/skillsreflects it; no duplicated guidance between the two skills.Architecture decisions
ENTERPRISE_DOMAINSinapi/lib/enterprise/consts.tscurrently gates only the nightly social scrape; this issue extends it to the credits tier. Moving it to a DB table is out of scope (adding a domain today = one-line PR, e.g. api#746).current_period_start(early-refill trigger) and payment rails.api(decision 2026-07-03, supersedes the earlier accept-the-duplication stance): chat consumesGET /api/accounts/{id}/creditsand its own credits computation is deleted (chat#1842).Source references
seekermusic.com→ENTERPRISE_DOMAINS(merged 2026-07-03, commit97735c53)docs/api-reference/openapi/accounts.json(/api/organizations*paths) +docs/api-reference/organizations/*.mdx