Vibecode Hypefury
track this build4 phases, 12 steps, beginner friendly0%The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Hypefury, draft and schedule text-led social posts with reusable queues. The hard boundary is platform integrations, auto-reposting, cross-posting, analytics, and creator templates, plus api access, connector upkeep, and collaboration.
You are building a lean indie version of Hypefury. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Hypefury · indie build A scheduling queue whose design is dictated by current platform economics: posts cost money per item, links cost far more, several capabilities are simply unavailable below Enterprise, and a queue that does not know this will produce an unexpected bill or a silent failure. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Publishing | Official APIs only, one adapter per platform | driving the web interface is against the terms and risks your account | | Cost | Per-post cost estimated before scheduling and tracked after | writes are billed per request and links cost substantially more | | Capability | A declared capability map per platform and tier | an endpoint your tier does not have should fail at scheduling, not at send | | Queue | Slots with per-platform rate awareness and a budget check | a queue that ignores either produces a bill or a gap | | Runtime | TypeScript with PostgreSQL and a worker | scheduling, retries and history are the system | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the server and the publishing worker. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **PostgreSQL 16 or newer** · free locally - Why: Queue state, spend tracking and idempotency records. - Get it: Docker or your package manager. - Verify: psql connects and a unique constraint enforces idempotency - [ ] **Developer access on one platform, with its current terms read** · pay per use; a link post costs many times a plain one - Why: Pricing and capabilities changed substantially in 2026, and building against a tutorial's description of the old tiers produces a tool that fails at send time. - Get it: Apply for access, read the current pricing and endpoint availability for your tier, and record both with the date you checked. - Verify: A test post publishes and the recorded capability map matches what your tier actually allows - [ ] **Your monthly posting budget** · free to decide - Why: Writes cost money per item and a queue that ignores that produces a bill rather than a surprise you can undo. - Get it: Decide a monthly figure, and work out how many posts that is at the URL and non-URL prices. - Verify: A figure is written down and the post count it implies is calculated - [ ] **Two weeks of real content to queue** · free - Why: Slot behaviour, empty queue handling and the cost difference between link and non-link posts only become visible with a real queue running for a while. - Get it: Write or gather enough posts to fill your slots for two weeks, including several with links. - Verify: A queue exists with at least a third of posts containing links ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Engagement automation. - Browser automation of the web interface. - Analytics beyond what the API returns. - Broad platform coverage. - platform integrations, auto-reposting, cross-posting, analytics, and creator templates - many maintained network APIs - inbox and moderation - deep analytics - team approvals and asset libraries If one of those is essential to you, that is the reason to keep paying for Hypefury, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Hypefury The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a posting queue for text-led social, built around what the APIs now actually cost and allow to replace Hypefury. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Build this against the current API terms rather than the ones a tutorial describes, because they changed substantially and in a direction that shapes the whole project. On X, pay-per-use became the default for new developers on 6 February 2026 and there is no meaningful free tier. Writes bill per request, at roughly $0.015 for a plain post and $0.20 for one containing a URL, which makes a link over ten times the price of a post without one. The April 2026 update removed follow, like and quote-post write endpoints from every self-serve tier, leaving them to Enterprise with no grandfathering, and the Pro tier was deprecated in August 2026. Two consequences. Each scheduled post has a real per-item cost, so the queue needs a budget the way a paid API client does, and features built on engagement endpoints cannot be built at all on a self-serve tier rather than being merely discouraged. The workaround people reach for is driving the web interface with a browser, which is outside the API and against the terms, and it puts your account at risk rather than theirs. This build does not include it. ### Stack (fixed, do not substitute) - Publishing: Official APIs only, one adapter per platform · driving the web interface is against the terms and risks your account - Cost: Per-post cost estimated before scheduling and tracked after · writes are billed per request and links cost substantially more - Capability: A declared capability map per platform and tier · an endpoint your tier does not have should fail at scheduling, not at send - Queue: Slots with per-platform rate awareness and a budget check · a queue that ignores either produces a bill or a gap - Runtime: TypeScript with PostgreSQL and a worker · scheduling, retries and history are the system ### Data model (create this before Phase 1) - accounts(id, platform, handle, tier, connected_at, capabilities_json) - posts(id, account_id, body, media_ids_json, contains_url, thread_parent_id, state, scheduled_for, posted_at, external_id) - cost_estimates(post_id, currency, amount_minor, basis) · basis records which price rule applied - spend(account_id, period, estimated_minor, actual_minor) - capabilities(platform, tier, endpoint, available, checked_on, source_url) · dated, because this is what changed - slots(id, account_id, day_of_week, time_local, timezone) · the recurring queue - failures(post_id, at, kind, detail, retryable) · kind is rate_limit, capability, auth or content - A post records whether it contains a URL, because that single fact changes its cost by more than an order of magnitude. ### Phase 1 · Knowing what your tier can actually do Build: A capability map per platform and tier, dated with a source. Check it when a post is created and refuse to schedule something your tier cannot perform. Surface capability records that have not been rechecked recently. Trap: Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. Done when: Scheduling an action your tier cannot perform is refused at creation with the reason. The capability map records a checked date and a source per entry. Stale capability records are surfaced for rechecking. A capability removed after a post was scheduled is caught before sending rather than failing silently. Do not build yet: No costing, queue or publishing. ### Phase 2 · Costing a post before you queue it Build: Estimate cost per post from a dated pricing table, accounting for whether the post contains a URL. Show the projected cost of the queue. Track actual spend against estimate. Trap: A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. Done when: Cost is estimated per post with the URL surcharge applied, verified against the current pricing table. The queue's projected cost for a period is shown before it runs. Actual spend is tracked against estimate and divergence is surfaced. Pricing entries record when they were checked and are flagged when stale. Do not build yet: No queue mechanics or publishing yet. ### Phase 3 · A queue that fills slots sensibly Build: Recurring slots in a stated timezone, filled from a queue in order. Handle an empty queue by leaving the slot empty rather than reposting. Handle a slot falling on a daylight transition. Trap: Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. Done when: Slots fill from the queue in order and an empty queue leaves the slot empty with a notification. Slots compute correctly across both daylight transitions. Reordering the queue does not lose scheduled items. No automatic recycling of previously posted content exists, verified by looking for it. Do not build yet: No publishing or failure handling yet. ### Phase 4 · Publishing, and failing usefully Build: Publish through official APIs with idempotency so a retry cannot double post. Classify failures into rate limit, capability, authentication and content, and handle each differently. Never retry a content rejection. Trap: Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. Done when: A retry after a timeout does not create a duplicate, tested by simulating one. Failures are classified and only retryable classes are retried. A content rejection is surfaced with the platform's reason rather than retried. An authentication failure pauses the account rather than burning through the queue. Do not build yet: No threads or reporting yet. ### Phase 5 · Threads, media and knowing what happened Build: Publish threads in order with each part linked to the previous, handling a mid-thread failure. Upload media through the platform's own flow. Report what posted, what failed and what it cost. Trap: A thread that fails on its third part leaves two orphaned posts in public that read as an incomplete thought, which is worse than nothing having posted at all. Decide the rule in advance: either delete the published parts or post a continuation, and make the choice explicit rather than leaving orphans. Done when: A thread publishes in order with each part correctly linked. A mid-thread failure follows a stated rule rather than leaving orphans, tested by injecting a failure. Media uploads through the platform's own flow with size and format validated first. The report shows posted, failed and cost per period. Do not build yet: Nothing after this. ### Out of scope (and why) - Any engagement automation, which is unavailable below Enterprise on self-serve tiers and is not something to work around. - Browser automation of the web interface, which is against the terms and puts your own account at risk. - Analytics beyond what the API returns, which is most of what the hosted products sell and which requires read volume you would be paying for. - Broad platform coverage, which is an adapter per platform and a maintenance burden that never ends. ### README must contain - That writes are billed per request and a post containing a link costs many times one without - That several endpoints are unavailable below Enterprise, so some features cannot be built rather than being discouraged - That capability and pricing are dated data, because both changed substantially during 2026 - That there is no browser automation, deliberately - That an empty queue leaves an empty slot rather than recycling old posts ===== AGENTS.md ===== # Agent instructions · Hypefury indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Official APIs only, one adapter per platform, Per-post cost estimated before scheduling and tracked after, A declared capability map per platform and tier, Slots with per-platform rate awareness and a budget check, TypeScript with PostgreSQL and a worker. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. ===== BUILD_PLAN.md ===== # Build plan · Hypefury A scheduling queue whose design is dictated by current platform economics: posts cost money per item, links cost far more, several capabilities are simply unavailable below Enterprise, and a queue that does not know this will produce an unexpected bill or a silent failure. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Knowing what your tier can actually do An unavailable capability fails when you schedule, not when it sends. ### Steps 1. Keep capabilities as dated data They changed in 2026 without grandfathering. Constants go quietly wrong. Files: `config/capabilities.json` 2. Check at scheduling time A failure at send is a failure you find out about afterwards. Files: `src/capability.ts` 3. Recheck before sending too Between scheduling and sending, a tier can change. Files: `src/publish.ts` ### Done when - [ ] An unavailable action is refused at scheduling - [ ] Capabilities are dated with a source - [ ] Stale records are surfaced - [ ] A post-scheduling removal is caught before sending ### Watch out - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. ## Phase 2 · Costing a post before you queue it You know what a week's queue will cost before it runs. ### Steps 1. Detect URLs and price accordingly A link costs over ten times a plain post. That belongs in the interface. Files: `src/cost.ts`, `src/cost.test.ts` 2. Project the queue's cost Before the week runs, not on the invoice. Files: `src/projection.ts` 3. Track actual against estimate Divergence means the pricing table is stale. Files: `src/spend.ts` ### Done when - [ ] URL posts are priced with the surcharge - [ ] Queue cost is projected before running - [ ] Actual spend is tracked against estimate - [ ] Stale pricing is flagged ### Watch out - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. ## Phase 3 · A queue that fills slots sensibly Content flows into recurring slots without you scheduling each item. ### Steps 1. Define slots in a stated timezone Test both daylight transitions. A post an hour early is visible. Files: `src/slots.ts`, `src/slots.test.ts` 2. Leave empty slots empty An empty queue is information. A recycled post is noise. Files: `src/queue.ts` 3. Keep reordering safe Moving an item should not lose its scheduled position. Files: `src/queue.ts` ### Done when - [ ] Slots fill in order from the queue - [ ] An empty queue leaves the slot empty - [ ] Both daylight transitions are handled - [ ] No automatic recycling exists ### Watch out - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. ## Phase 4 · Publishing, and failing usefully Posts go out through the official API and failures are explicable. ### Steps 1. Use an idempotency key per post A timeout that actually succeeded is the common duplicate. Files: `src/publish.ts` 2. Classify failures Rate limit, capability, auth, content. Four different responses. Files: `src/failures.ts` 3. Pause the account on auth failure Rather than failing every queued post in sequence. Files: `src/accounts.ts` ### Done when - [ ] A retry after timeout does not duplicate - [ ] Failures are classified and handled by class - [ ] Content rejections are surfaced, not retried - [ ] An auth failure pauses the account ### Watch out - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. ## Not in this build - Engagement automation. - Browser automation of the web interface. - Analytics beyond what the API returns. - Broad platform coverage. ## After v1, if you want it - Per-slot content types, so the queue mixes rather than posting five similar things - A cost comparison between posting with and without a link, which is now a real editorial decision - Draft review before an item enters the queue, once the queue itself is trustworthy ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Queue state, spend and platform tokens by reference. DATABASE_URL=postgres://localhost:5432/queue # Required · secret. Official API only. Browser automation is not part of this build. PLATFORM_API_KEY=a platform-issued key # Required. Dated per-write pricing including the URL surcharge. PRICING_PATH=./config/pricing.json # Required. Dated endpoint availability per tier. This is what changed in 2026. CAPABILITIES_PATH=./config/capabilities.json # Required. Checked before scheduling. Writes cost money per item. MONTHLY_BUDGET_MINOR=3000 # Required. Present as a constant. An empty slot is information; a recycled post is noise. RECYCLE_OLD_POSTS=never
You are building a lean indie version of Hypefury. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Hypefury · indie build A scheduling queue whose design is dictated by current platform economics: posts cost money per item, links cost far more, several capabilities are simply unavailable below Enterprise, and a queue that does not know this will produce an unexpected bill or a silent failure. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Publishing | Official APIs only, one adapter per platform | driving the web interface is against the terms and risks your account | | Cost | Per-post cost estimated before scheduling and tracked after | writes are billed per request and links cost substantially more | | Capability | A declared capability map per platform and tier | an endpoint your tier does not have should fail at scheduling, not at send | | Queue | Slots with per-platform rate awareness and a budget check | a queue that ignores either produces a bill or a gap | | Runtime | TypeScript with PostgreSQL and a worker | scheduling, retries and history are the system | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the server and the publishing worker. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **PostgreSQL 16 or newer** · free locally - Why: Queue state, spend tracking and idempotency records. - Get it: Docker or your package manager. - Verify: psql connects and a unique constraint enforces idempotency - [ ] **Developer access on one platform, with its current terms read** · pay per use; a link post costs many times a plain one - Why: Pricing and capabilities changed substantially in 2026, and building against a tutorial's description of the old tiers produces a tool that fails at send time. - Get it: Apply for access, read the current pricing and endpoint availability for your tier, and record both with the date you checked. - Verify: A test post publishes and the recorded capability map matches what your tier actually allows - [ ] **Your monthly posting budget** · free to decide - Why: Writes cost money per item and a queue that ignores that produces a bill rather than a surprise you can undo. - Get it: Decide a monthly figure, and work out how many posts that is at the URL and non-URL prices. - Verify: A figure is written down and the post count it implies is calculated - [ ] **Two weeks of real content to queue** · free - Why: Slot behaviour, empty queue handling and the cost difference between link and non-link posts only become visible with a real queue running for a while. - Get it: Write or gather enough posts to fill your slots for two weeks, including several with links. - Verify: A queue exists with at least a third of posts containing links ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Engagement automation. - Browser automation of the web interface. - Analytics beyond what the API returns. - Broad platform coverage. - platform integrations, auto-reposting, cross-posting, analytics, and creator templates - many maintained network APIs - inbox and moderation - deep analytics - team approvals and asset libraries If one of those is essential to you, that is the reason to keep paying for Hypefury, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Hypefury The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a posting queue for text-led social, built around what the APIs now actually cost and allow to replace Hypefury. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Build this against the current API terms rather than the ones a tutorial describes, because they changed substantially and in a direction that shapes the whole project. On X, pay-per-use became the default for new developers on 6 February 2026 and there is no meaningful free tier. Writes bill per request, at roughly $0.015 for a plain post and $0.20 for one containing a URL, which makes a link over ten times the price of a post without one. The April 2026 update removed follow, like and quote-post write endpoints from every self-serve tier, leaving them to Enterprise with no grandfathering, and the Pro tier was deprecated in August 2026. Two consequences. Each scheduled post has a real per-item cost, so the queue needs a budget the way a paid API client does, and features built on engagement endpoints cannot be built at all on a self-serve tier rather than being merely discouraged. The workaround people reach for is driving the web interface with a browser, which is outside the API and against the terms, and it puts your account at risk rather than theirs. This build does not include it. ### Stack (fixed, do not substitute) - Publishing: Official APIs only, one adapter per platform · driving the web interface is against the terms and risks your account - Cost: Per-post cost estimated before scheduling and tracked after · writes are billed per request and links cost substantially more - Capability: A declared capability map per platform and tier · an endpoint your tier does not have should fail at scheduling, not at send - Queue: Slots with per-platform rate awareness and a budget check · a queue that ignores either produces a bill or a gap - Runtime: TypeScript with PostgreSQL and a worker · scheduling, retries and history are the system ### Data model (create this before Phase 1) - accounts(id, platform, handle, tier, connected_at, capabilities_json) - posts(id, account_id, body, media_ids_json, contains_url, thread_parent_id, state, scheduled_for, posted_at, external_id) - cost_estimates(post_id, currency, amount_minor, basis) · basis records which price rule applied - spend(account_id, period, estimated_minor, actual_minor) - capabilities(platform, tier, endpoint, available, checked_on, source_url) · dated, because this is what changed - slots(id, account_id, day_of_week, time_local, timezone) · the recurring queue - failures(post_id, at, kind, detail, retryable) · kind is rate_limit, capability, auth or content - A post records whether it contains a URL, because that single fact changes its cost by more than an order of magnitude. ### Phase 1 · Knowing what your tier can actually do Build: A capability map per platform and tier, dated with a source. Check it when a post is created and refuse to schedule something your tier cannot perform. Surface capability records that have not been rechecked recently. Trap: Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. Done when: Scheduling an action your tier cannot perform is refused at creation with the reason. The capability map records a checked date and a source per entry. Stale capability records are surfaced for rechecking. A capability removed after a post was scheduled is caught before sending rather than failing silently. Do not build yet: No costing, queue or publishing. ### Phase 2 · Costing a post before you queue it Build: Estimate cost per post from a dated pricing table, accounting for whether the post contains a URL. Show the projected cost of the queue. Track actual spend against estimate. Trap: A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. Done when: Cost is estimated per post with the URL surcharge applied, verified against the current pricing table. The queue's projected cost for a period is shown before it runs. Actual spend is tracked against estimate and divergence is surfaced. Pricing entries record when they were checked and are flagged when stale. Do not build yet: No queue mechanics or publishing yet. ### Phase 3 · A queue that fills slots sensibly Build: Recurring slots in a stated timezone, filled from a queue in order. Handle an empty queue by leaving the slot empty rather than reposting. Handle a slot falling on a daylight transition. Trap: Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. Done when: Slots fill from the queue in order and an empty queue leaves the slot empty with a notification. Slots compute correctly across both daylight transitions. Reordering the queue does not lose scheduled items. No automatic recycling of previously posted content exists, verified by looking for it. Do not build yet: No publishing or failure handling yet. ### Phase 4 · Publishing, and failing usefully Build: Publish through official APIs with idempotency so a retry cannot double post. Classify failures into rate limit, capability, authentication and content, and handle each differently. Never retry a content rejection. Trap: Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. Done when: A retry after a timeout does not create a duplicate, tested by simulating one. Failures are classified and only retryable classes are retried. A content rejection is surfaced with the platform's reason rather than retried. An authentication failure pauses the account rather than burning through the queue. Do not build yet: No threads or reporting yet. ### Phase 5 · Threads, media and knowing what happened Build: Publish threads in order with each part linked to the previous, handling a mid-thread failure. Upload media through the platform's own flow. Report what posted, what failed and what it cost. Trap: A thread that fails on its third part leaves two orphaned posts in public that read as an incomplete thought, which is worse than nothing having posted at all. Decide the rule in advance: either delete the published parts or post a continuation, and make the choice explicit rather than leaving orphans. Done when: A thread publishes in order with each part correctly linked. A mid-thread failure follows a stated rule rather than leaving orphans, tested by injecting a failure. Media uploads through the platform's own flow with size and format validated first. The report shows posted, failed and cost per period. Do not build yet: Nothing after this. ### Out of scope (and why) - Any engagement automation, which is unavailable below Enterprise on self-serve tiers and is not something to work around. - Browser automation of the web interface, which is against the terms and puts your own account at risk. - Analytics beyond what the API returns, which is most of what the hosted products sell and which requires read volume you would be paying for. - Broad platform coverage, which is an adapter per platform and a maintenance burden that never ends. ### README must contain - That writes are billed per request and a post containing a link costs many times one without - That several endpoints are unavailable below Enterprise, so some features cannot be built rather than being discouraged - That capability and pricing are dated data, because both changed substantially during 2026 - That there is no browser automation, deliberately - That an empty queue leaves an empty slot rather than recycling old posts ===== AGENTS.md ===== # Agent instructions · Hypefury indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Official APIs only, one adapter per platform, Per-post cost estimated before scheduling and tracked after, A declared capability map per platform and tier, Slots with per-platform rate awareness and a budget check, TypeScript with PostgreSQL and a worker. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. ===== BUILD_PLAN.md ===== # Build plan · Hypefury A scheduling queue whose design is dictated by current platform economics: posts cost money per item, links cost far more, several capabilities are simply unavailable below Enterprise, and a queue that does not know this will produce an unexpected bill or a silent failure. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Knowing what your tier can actually do An unavailable capability fails when you schedule, not when it sends. ### Steps 1. Keep capabilities as dated data They changed in 2026 without grandfathering. Constants go quietly wrong. Files: `config/capabilities.json` 2. Check at scheduling time A failure at send is a failure you find out about afterwards. Files: `src/capability.ts` 3. Recheck before sending too Between scheduling and sending, a tier can change. Files: `src/publish.ts` ### Done when - [ ] An unavailable action is refused at scheduling - [ ] Capabilities are dated with a source - [ ] Stale records are surfaced - [ ] A post-scheduling removal is caught before sending ### Watch out - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. ## Phase 2 · Costing a post before you queue it You know what a week's queue will cost before it runs. ### Steps 1. Detect URLs and price accordingly A link costs over ten times a plain post. That belongs in the interface. Files: `src/cost.ts`, `src/cost.test.ts` 2. Project the queue's cost Before the week runs, not on the invoice. Files: `src/projection.ts` 3. Track actual against estimate Divergence means the pricing table is stale. Files: `src/spend.ts` ### Done when - [ ] URL posts are priced with the surcharge - [ ] Queue cost is projected before running - [ ] Actual spend is tracked against estimate - [ ] Stale pricing is flagged ### Watch out - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. ## Phase 3 · A queue that fills slots sensibly Content flows into recurring slots without you scheduling each item. ### Steps 1. Define slots in a stated timezone Test both daylight transitions. A post an hour early is visible. Files: `src/slots.ts`, `src/slots.test.ts` 2. Leave empty slots empty An empty queue is information. A recycled post is noise. Files: `src/queue.ts` 3. Keep reordering safe Moving an item should not lose its scheduled position. Files: `src/queue.ts` ### Done when - [ ] Slots fill in order from the queue - [ ] An empty queue leaves the slot empty - [ ] Both daylight transitions are handled - [ ] No automatic recycling exists ### Watch out - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. ## Phase 4 · Publishing, and failing usefully Posts go out through the official API and failures are explicable. ### Steps 1. Use an idempotency key per post A timeout that actually succeeded is the common duplicate. Files: `src/publish.ts` 2. Classify failures Rate limit, capability, auth, content. Four different responses. Files: `src/failures.ts` 3. Pause the account on auth failure Rather than failing every queued post in sequence. Files: `src/accounts.ts` ### Done when - [ ] A retry after timeout does not duplicate - [ ] Failures are classified and handled by class - [ ] Content rejections are surfaced, not retried - [ ] An auth failure pauses the account ### Watch out - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. ## Not in this build - Engagement automation. - Browser automation of the web interface. - Analytics beyond what the API returns. - Broad platform coverage. ## After v1, if you want it - Per-slot content types, so the queue mixes rather than posting five similar things - A cost comparison between posting with and without a link, which is now a real editorial decision - Draft review before an item enters the queue, once the queue itself is trustworthy ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Queue state, spend and platform tokens by reference. DATABASE_URL=postgres://localhost:5432/queue # Required · secret. Official API only. Browser automation is not part of this build. PLATFORM_API_KEY=a platform-issued key # Required. Dated per-write pricing including the URL surcharge. PRICING_PATH=./config/pricing.json # Required. Dated endpoint availability per tier. This is what changed in 2026. CAPABILITIES_PATH=./config/capabilities.json # Required. Checked before scheduling. Writes cost money per item. MONTHLY_BUDGET_MINOR=3000 # Required. Present as a constant. An empty slot is information; a recycled post is noise. RECYCLE_OLD_POSTS=never
You are building a production product version of Hypefury. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Hypefury · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Hypefury, draft and schedule text-led social posts with reusable queues. The hard boundary is platform integrations, auto-reposting, cross-posting, analytics, and creator templates, plus api access, connector upkeep, and collaboration. ## Product outcome A queue that knows what each post costs before it sends, refuses to schedule something your tier cannot do, and never quietly reposts old content to fill a gap. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - developer application for one social network - OAuth credentials - PostgreSQL - public HTTPS endpoint - background worker ## Explicit non-goals for v1 - Engagement automation. - Browser automation of the web interface. - Analytics beyond what the API returns. - Broad platform coverage. - platform integrations, auto-reposting, cross-posting, analytics, and creator templates - many maintained network APIs - inbox and moderation - deep analytics - team approvals and asset libraries ## Success criteria - An unavailable action is confirmed refused at scheduling - URL posts are confirmed priced with the surcharge - An empty queue is confirmed to leave the slot empty - A retry after timeout is confirmed not to duplicate - A mid-thread failure is confirmed to follow a stated rule ===== BRIEF.md ===== # Build brief · Hypefury The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a posting queue for text-led social, built around what the APIs now actually cost and allow to replace Hypefury. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Build this against the current API terms rather than the ones a tutorial describes, because they changed substantially and in a direction that shapes the whole project. On X, pay-per-use became the default for new developers on 6 February 2026 and there is no meaningful free tier. Writes bill per request, at roughly $0.015 for a plain post and $0.20 for one containing a URL, which makes a link over ten times the price of a post without one. The April 2026 update removed follow, like and quote-post write endpoints from every self-serve tier, leaving them to Enterprise with no grandfathering, and the Pro tier was deprecated in August 2026. Two consequences. Each scheduled post has a real per-item cost, so the queue needs a budget the way a paid API client does, and features built on engagement endpoints cannot be built at all on a self-serve tier rather than being merely discouraged. The workaround people reach for is driving the web interface with a browser, which is outside the API and against the terms, and it puts your account at risk rather than theirs. This build does not include it. ### Stack (fixed, do not substitute) - Publishing: Official APIs only, one adapter per platform · driving the web interface is against the terms and risks your account - Cost: Per-post cost estimated before scheduling and tracked after · writes are billed per request and links cost substantially more - Capability: A declared capability map per platform and tier · an endpoint your tier does not have should fail at scheduling, not at send - Queue: Slots with per-platform rate awareness and a budget check · a queue that ignores either produces a bill or a gap - Runtime: TypeScript with PostgreSQL and a worker · scheduling, retries and history are the system ### Data model (create this before Phase 1) - accounts(id, platform, handle, tier, connected_at, capabilities_json) - posts(id, account_id, body, media_ids_json, contains_url, thread_parent_id, state, scheduled_for, posted_at, external_id) - cost_estimates(post_id, currency, amount_minor, basis) · basis records which price rule applied - spend(account_id, period, estimated_minor, actual_minor) - capabilities(platform, tier, endpoint, available, checked_on, source_url) · dated, because this is what changed - slots(id, account_id, day_of_week, time_local, timezone) · the recurring queue - failures(post_id, at, kind, detail, retryable) · kind is rate_limit, capability, auth or content - A post records whether it contains a URL, because that single fact changes its cost by more than an order of magnitude. ### Phase 1 · Knowing what your tier can actually do Build: A capability map per platform and tier, dated with a source. Check it when a post is created and refuse to schedule something your tier cannot perform. Surface capability records that have not been rechecked recently. Trap: Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. Done when: Scheduling an action your tier cannot perform is refused at creation with the reason. The capability map records a checked date and a source per entry. Stale capability records are surfaced for rechecking. A capability removed after a post was scheduled is caught before sending rather than failing silently. Do not build yet: No costing, queue or publishing. ### Phase 2 · Costing a post before you queue it Build: Estimate cost per post from a dated pricing table, accounting for whether the post contains a URL. Show the projected cost of the queue. Track actual spend against estimate. Trap: A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. Done when: Cost is estimated per post with the URL surcharge applied, verified against the current pricing table. The queue's projected cost for a period is shown before it runs. Actual spend is tracked against estimate and divergence is surfaced. Pricing entries record when they were checked and are flagged when stale. Do not build yet: No queue mechanics or publishing yet. ### Phase 3 · A queue that fills slots sensibly Build: Recurring slots in a stated timezone, filled from a queue in order. Handle an empty queue by leaving the slot empty rather than reposting. Handle a slot falling on a daylight transition. Trap: Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. Done when: Slots fill from the queue in order and an empty queue leaves the slot empty with a notification. Slots compute correctly across both daylight transitions. Reordering the queue does not lose scheduled items. No automatic recycling of previously posted content exists, verified by looking for it. Do not build yet: No publishing or failure handling yet. ### Phase 4 · Publishing, and failing usefully Build: Publish through official APIs with idempotency so a retry cannot double post. Classify failures into rate limit, capability, authentication and content, and handle each differently. Never retry a content rejection. Trap: Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. Done when: A retry after a timeout does not create a duplicate, tested by simulating one. Failures are classified and only retryable classes are retried. A content rejection is surfaced with the platform's reason rather than retried. An authentication failure pauses the account rather than burning through the queue. Do not build yet: No threads or reporting yet. ### Phase 5 · Threads, media and knowing what happened Build: Publish threads in order with each part linked to the previous, handling a mid-thread failure. Upload media through the platform's own flow. Report what posted, what failed and what it cost. Trap: A thread that fails on its third part leaves two orphaned posts in public that read as an incomplete thought, which is worse than nothing having posted at all. Decide the rule in advance: either delete the published parts or post a continuation, and make the choice explicit rather than leaving orphans. Done when: A thread publishes in order with each part correctly linked. A mid-thread failure follows a stated rule rather than leaving orphans, tested by injecting a failure. Media uploads through the platform's own flow with size and format validated first. The report shows posted, failed and cost per period. Do not build yet: Nothing after this. ### Out of scope (and why) - Any engagement automation, which is unavailable below Enterprise on self-serve tiers and is not something to work around. - Browser automation of the web interface, which is against the terms and puts your own account at risk. - Analytics beyond what the API returns, which is most of what the hosted products sell and which requires read volume you would be paying for. - Broad platform coverage, which is an adapter per platform and a maintenance burden that never ends. ### README must contain - That writes are billed per request and a post containing a link costs many times one without - That several endpoints are unavailable below Enterprise, so some features cannot be built rather than being discouraged - That capability and pricing are dated data, because both changed substantially during 2026 - That there is no browser automation, deliberately - That an empty queue leaves an empty slot rather than recycling old posts ===== ARCHITECTURE.md ===== # Architecture · Hypefury ## Stack | Part | Choice | Why | | --- | --- | --- | | Publishing | Official APIs only, one adapter per platform | driving the web interface is against the terms and risks your account | | Cost | Per-post cost estimated before scheduling and tracked after | writes are billed per request and links cost substantially more | | Capability | A declared capability map per platform and tier | an endpoint your tier does not have should fail at scheduling, not at send | | Queue | Slots with per-platform rate awareness and a budget check | a queue that ignores either produces a bill or a gap | | Runtime | TypeScript with PostgreSQL and a worker | scheduling, retries and history are the system | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Capability | Dated endpoint availability per tier. | Any source, provided it is data with a checked date rather than constants. | | Cost | Per-post estimation including the URL surcharge. | Any pricing source, provided the surcharge is visible where links are added. | | Queue | Slots, ordering and empty-slot behaviour. | Any scheduler, provided it does not recycle old content. | | Publishing | Official API calls with idempotency. | Any client, provided browser automation is not one of them. | | Failures | Classification and selective retry. | Any implementation, provided content rejections are never retried. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DATABASE_URL` · required, secret · Queue state, spend and platform tokens by reference. - `PLATFORM_API_KEY` · required, secret · Official API only. Browser automation is not part of this build. - `PRICING_PATH` · required · Dated per-write pricing including the URL surcharge. - `CAPABILITIES_PATH` · required · Dated endpoint availability per tier. This is what changed in 2026. - `MONTHLY_BUDGET_MINOR` · required · Checked before scheduling. Writes cost money per item. - `RECYCLE_OLD_POSTS` · required · Present as a constant. An empty slot is information; a recycled post is noise. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Hypefury product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Official APIs only, one adapter per platform, Per-post cost estimated before scheduling and tracked after, A declared capability map per platform and tier, Slots with per-platform rate awareness and a budget check, TypeScript with PostgreSQL and a worker. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. - A thread that fails on its third part leaves two orphaned posts in public that read as an incomplete thought, which is worse than nothing having posted at all. Decide the rule in advance: either delete the published parts or post a continuation, and make the choice explicit rather than leaving orphans. ===== MILESTONES.md ===== # Delivery milestones · Hypefury Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Knowing what your tier can actually do An unavailable capability fails when you schedule, not when it sends. ### Steps 1. Keep capabilities as dated data They changed in 2026 without grandfathering. Constants go quietly wrong. Files: `config/capabilities.json` 2. Check at scheduling time A failure at send is a failure you find out about afterwards. Files: `src/capability.ts` 3. Recheck before sending too Between scheduling and sending, a tier can change. Files: `src/publish.ts` ### Done when - [ ] An unavailable action is refused at scheduling - [ ] Capabilities are dated with a source - [ ] Stale records are surfaced - [ ] A post-scheduling removal is caught before sending ### Watch out - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. ## M2 · Costing a post before you queue it You know what a week's queue will cost before it runs. ### Steps 1. Detect URLs and price accordingly A link costs over ten times a plain post. That belongs in the interface. Files: `src/cost.ts`, `src/cost.test.ts` 2. Project the queue's cost Before the week runs, not on the invoice. Files: `src/projection.ts` 3. Track actual against estimate Divergence means the pricing table is stale. Files: `src/spend.ts` ### Done when - [ ] URL posts are priced with the surcharge - [ ] Queue cost is projected before running - [ ] Actual spend is tracked against estimate - [ ] Stale pricing is flagged ### Watch out - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. ## M3 · A queue that fills slots sensibly Content flows into recurring slots without you scheduling each item. ### Steps 1. Define slots in a stated timezone Test both daylight transitions. A post an hour early is visible. Files: `src/slots.ts`, `src/slots.test.ts` 2. Leave empty slots empty An empty queue is information. A recycled post is noise. Files: `src/queue.ts` 3. Keep reordering safe Moving an item should not lose its scheduled position. Files: `src/queue.ts` ### Done when - [ ] Slots fill in order from the queue - [ ] An empty queue leaves the slot empty - [ ] Both daylight transitions are handled - [ ] No automatic recycling exists ### Watch out - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. ## M4 · Publishing, and failing usefully Posts go out through the official API and failures are explicable. ### Steps 1. Use an idempotency key per post A timeout that actually succeeded is the common duplicate. Files: `src/publish.ts` 2. Classify failures Rate limit, capability, auth, content. Four different responses. Files: `src/failures.ts` 3. Pause the account on auth failure Rather than failing every queued post in sequence. Files: `src/accounts.ts` ### Done when - [ ] A retry after timeout does not duplicate - [ ] Failures are classified and handled by class - [ ] Content rejections are surfaced, not retried - [ ] An auth failure pauses the account ### Watch out - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. ## M5 · Threads, media and knowing what happened (production only) Multi-part posts publish correctly and you can see what the queue did. ### Steps 1. Link thread parts correctly Each to the previous, in order, with the platform's own mechanism. Files: `src/threads.ts` 2. Decide the mid-thread failure rule Delete or continue. Orphaned parts are the worst outcome. Files: `src/threads.ts` 3. Validate media before uploading Size and format. A rejection after upload wastes the request and the money. Files: `src/media.ts` ### Done when - [ ] Threads publish in order and linked - [ ] A mid-thread failure follows a stated rule - [ ] Media is validated before upload - [ ] The report shows posted, failed and cost ### Watch out - A thread that fails on its third part leaves two orphaned posts in public that read as an incomplete thought, which is worse than nothing having posted at all. Decide the rule in advance: either delete the published parts or post a continuation, and make the choice explicit rather than leaving orphans. ===== OPERATIONS.md ===== # Operations · Hypefury ## Backup The queue and the spend record are the operational data. Tokens should be recoverable by reconnecting rather than restored, since a restored token may have been revoked. ## Restore Restore and confirm idempotency records survived, since losing them means a resumed queue can republish posts that already went out. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Spend against estimate and capability staleness. Both go wrong quietly, and the second is what turns into a silent send failure when a platform changes its tiers again. ## Incident checklist A duplicate post is the most visible failure and means an idempotency key was missing or lost. A silent gap in the queue usually means a capability check failed at send rather than at scheduling. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] An unavailable action is confirmed refused at scheduling - [ ] URL posts are confirmed priced with the surcharge - [ ] An empty queue is confirmed to leave the slot empty - [ ] A retry after timeout is confirmed not to duplicate - [ ] A mid-thread failure is confirmed to follow a stated rule ## Launch constraint Do not market omitted Hypefury capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Queue state, spend and platform tokens by reference. DATABASE_URL=postgres://localhost:5432/queue # Required · secret. Official API only. Browser automation is not part of this build. PLATFORM_API_KEY=a platform-issued key # Required. Dated per-write pricing including the URL surcharge. PRICING_PATH=./config/pricing.json # Required. Dated endpoint availability per tier. This is what changed in 2026. CAPABILITIES_PATH=./config/capabilities.json # Required. Checked before scheduling. Writes cost money per item. MONTHLY_BUDGET_MINOR=3000 # Required. Present as a constant. An empty slot is information; a recycled post is noise. RECYCLE_OLD_POSTS=never
# Hypefury · indie build A scheduling queue whose design is dictated by current platform economics: posts cost money per item, links cost far more, several capabilities are simply unavailable below Enterprise, and a queue that does not know this will produce an unexpected bill or a silent failure. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Publishing | Official APIs only, one adapter per platform | driving the web interface is against the terms and risks your account | | Cost | Per-post cost estimated before scheduling and tracked after | writes are billed per request and links cost substantially more | | Capability | A declared capability map per platform and tier | an endpoint your tier does not have should fail at scheduling, not at send | | Queue | Slots with per-platform rate awareness and a budget check | a queue that ignores either produces a bill or a gap | | Runtime | TypeScript with PostgreSQL and a worker | scheduling, retries and history are the system | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the server and the publishing worker. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **PostgreSQL 16 or newer** · free locally - Why: Queue state, spend tracking and idempotency records. - Get it: Docker or your package manager. - Verify: psql connects and a unique constraint enforces idempotency - [ ] **Developer access on one platform, with its current terms read** · pay per use; a link post costs many times a plain one - Why: Pricing and capabilities changed substantially in 2026, and building against a tutorial's description of the old tiers produces a tool that fails at send time. - Get it: Apply for access, read the current pricing and endpoint availability for your tier, and record both with the date you checked. - Verify: A test post publishes and the recorded capability map matches what your tier actually allows - [ ] **Your monthly posting budget** · free to decide - Why: Writes cost money per item and a queue that ignores that produces a bill rather than a surprise you can undo. - Get it: Decide a monthly figure, and work out how many posts that is at the URL and non-URL prices. - Verify: A figure is written down and the post count it implies is calculated - [ ] **Two weeks of real content to queue** · free - Why: Slot behaviour, empty queue handling and the cost difference between link and non-link posts only become visible with a real queue running for a while. - Get it: Write or gather enough posts to fill your slots for two weeks, including several with links. - Verify: A queue exists with at least a third of posts containing links ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Engagement automation. - Browser automation of the web interface. - Analytics beyond what the API returns. - Broad platform coverage. - platform integrations, auto-reposting, cross-posting, analytics, and creator templates - many maintained network APIs - inbox and moderation - deep analytics - team approvals and asset libraries If one of those is essential to you, that is the reason to keep paying for Hypefury, and the README should say so rather than pretend.
# Build brief · Hypefury The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a posting queue for text-led social, built around what the APIs now actually cost and allow to replace Hypefury. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Build this against the current API terms rather than the ones a tutorial describes, because they changed substantially and in a direction that shapes the whole project. On X, pay-per-use became the default for new developers on 6 February 2026 and there is no meaningful free tier. Writes bill per request, at roughly $0.015 for a plain post and $0.20 for one containing a URL, which makes a link over ten times the price of a post without one. The April 2026 update removed follow, like and quote-post write endpoints from every self-serve tier, leaving them to Enterprise with no grandfathering, and the Pro tier was deprecated in August 2026. Two consequences. Each scheduled post has a real per-item cost, so the queue needs a budget the way a paid API client does, and features built on engagement endpoints cannot be built at all on a self-serve tier rather than being merely discouraged. The workaround people reach for is driving the web interface with a browser, which is outside the API and against the terms, and it puts your account at risk rather than theirs. This build does not include it. ### Stack (fixed, do not substitute) - Publishing: Official APIs only, one adapter per platform · driving the web interface is against the terms and risks your account - Cost: Per-post cost estimated before scheduling and tracked after · writes are billed per request and links cost substantially more - Capability: A declared capability map per platform and tier · an endpoint your tier does not have should fail at scheduling, not at send - Queue: Slots with per-platform rate awareness and a budget check · a queue that ignores either produces a bill or a gap - Runtime: TypeScript with PostgreSQL and a worker · scheduling, retries and history are the system ### Data model (create this before Phase 1) - accounts(id, platform, handle, tier, connected_at, capabilities_json) - posts(id, account_id, body, media_ids_json, contains_url, thread_parent_id, state, scheduled_for, posted_at, external_id) - cost_estimates(post_id, currency, amount_minor, basis) · basis records which price rule applied - spend(account_id, period, estimated_minor, actual_minor) - capabilities(platform, tier, endpoint, available, checked_on, source_url) · dated, because this is what changed - slots(id, account_id, day_of_week, time_local, timezone) · the recurring queue - failures(post_id, at, kind, detail, retryable) · kind is rate_limit, capability, auth or content - A post records whether it contains a URL, because that single fact changes its cost by more than an order of magnitude. ### Phase 1 · Knowing what your tier can actually do Build: A capability map per platform and tier, dated with a source. Check it when a post is created and refuse to schedule something your tier cannot perform. Surface capability records that have not been rechecked recently. Trap: Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. Done when: Scheduling an action your tier cannot perform is refused at creation with the reason. The capability map records a checked date and a source per entry. Stale capability records are surfaced for rechecking. A capability removed after a post was scheduled is caught before sending rather than failing silently. Do not build yet: No costing, queue or publishing. ### Phase 2 · Costing a post before you queue it Build: Estimate cost per post from a dated pricing table, accounting for whether the post contains a URL. Show the projected cost of the queue. Track actual spend against estimate. Trap: A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. Done when: Cost is estimated per post with the URL surcharge applied, verified against the current pricing table. The queue's projected cost for a period is shown before it runs. Actual spend is tracked against estimate and divergence is surfaced. Pricing entries record when they were checked and are flagged when stale. Do not build yet: No queue mechanics or publishing yet. ### Phase 3 · A queue that fills slots sensibly Build: Recurring slots in a stated timezone, filled from a queue in order. Handle an empty queue by leaving the slot empty rather than reposting. Handle a slot falling on a daylight transition. Trap: Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. Done when: Slots fill from the queue in order and an empty queue leaves the slot empty with a notification. Slots compute correctly across both daylight transitions. Reordering the queue does not lose scheduled items. No automatic recycling of previously posted content exists, verified by looking for it. Do not build yet: No publishing or failure handling yet. ### Phase 4 · Publishing, and failing usefully Build: Publish through official APIs with idempotency so a retry cannot double post. Classify failures into rate limit, capability, authentication and content, and handle each differently. Never retry a content rejection. Trap: Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. Done when: A retry after a timeout does not create a duplicate, tested by simulating one. Failures are classified and only retryable classes are retried. A content rejection is surfaced with the platform's reason rather than retried. An authentication failure pauses the account rather than burning through the queue. Do not build yet: No threads or reporting yet. ### Phase 5 · Threads, media and knowing what happened Build: Publish threads in order with each part linked to the previous, handling a mid-thread failure. Upload media through the platform's own flow. Report what posted, what failed and what it cost. Trap: A thread that fails on its third part leaves two orphaned posts in public that read as an incomplete thought, which is worse than nothing having posted at all. Decide the rule in advance: either delete the published parts or post a continuation, and make the choice explicit rather than leaving orphans. Done when: A thread publishes in order with each part correctly linked. A mid-thread failure follows a stated rule rather than leaving orphans, tested by injecting a failure. Media uploads through the platform's own flow with size and format validated first. The report shows posted, failed and cost per period. Do not build yet: Nothing after this. ### Out of scope (and why) - Any engagement automation, which is unavailable below Enterprise on self-serve tiers and is not something to work around. - Browser automation of the web interface, which is against the terms and puts your own account at risk. - Analytics beyond what the API returns, which is most of what the hosted products sell and which requires read volume you would be paying for. - Broad platform coverage, which is an adapter per platform and a maintenance burden that never ends. ### README must contain - That writes are billed per request and a post containing a link costs many times one without - That several endpoints are unavailable below Enterprise, so some features cannot be built rather than being discouraged - That capability and pricing are dated data, because both changed substantially during 2026 - That there is no browser automation, deliberately - That an empty queue leaves an empty slot rather than recycling old posts
# Agent instructions · Hypefury indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Official APIs only, one adapter per platform, Per-post cost estimated before scheduling and tracked after, A declared capability map per platform and tier, Slots with per-platform rate awareness and a budget check, TypeScript with PostgreSQL and a worker. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help.
# Build plan · Hypefury A scheduling queue whose design is dictated by current platform economics: posts cost money per item, links cost far more, several capabilities are simply unavailable below Enterprise, and a queue that does not know this will produce an unexpected bill or a silent failure. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Knowing what your tier can actually do An unavailable capability fails when you schedule, not when it sends. ### Steps 1. Keep capabilities as dated data They changed in 2026 without grandfathering. Constants go quietly wrong. Files: `config/capabilities.json` 2. Check at scheduling time A failure at send is a failure you find out about afterwards. Files: `src/capability.ts` 3. Recheck before sending too Between scheduling and sending, a tier can change. Files: `src/publish.ts` ### Done when - [ ] An unavailable action is refused at scheduling - [ ] Capabilities are dated with a source - [ ] Stale records are surfaced - [ ] A post-scheduling removal is caught before sending ### Watch out - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. ## Phase 2 · Costing a post before you queue it You know what a week's queue will cost before it runs. ### Steps 1. Detect URLs and price accordingly A link costs over ten times a plain post. That belongs in the interface. Files: `src/cost.ts`, `src/cost.test.ts` 2. Project the queue's cost Before the week runs, not on the invoice. Files: `src/projection.ts` 3. Track actual against estimate Divergence means the pricing table is stale. Files: `src/spend.ts` ### Done when - [ ] URL posts are priced with the surcharge - [ ] Queue cost is projected before running - [ ] Actual spend is tracked against estimate - [ ] Stale pricing is flagged ### Watch out - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. ## Phase 3 · A queue that fills slots sensibly Content flows into recurring slots without you scheduling each item. ### Steps 1. Define slots in a stated timezone Test both daylight transitions. A post an hour early is visible. Files: `src/slots.ts`, `src/slots.test.ts` 2. Leave empty slots empty An empty queue is information. A recycled post is noise. Files: `src/queue.ts` 3. Keep reordering safe Moving an item should not lose its scheduled position. Files: `src/queue.ts` ### Done when - [ ] Slots fill in order from the queue - [ ] An empty queue leaves the slot empty - [ ] Both daylight transitions are handled - [ ] No automatic recycling exists ### Watch out - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. ## Phase 4 · Publishing, and failing usefully Posts go out through the official API and failures are explicable. ### Steps 1. Use an idempotency key per post A timeout that actually succeeded is the common duplicate. Files: `src/publish.ts` 2. Classify failures Rate limit, capability, auth, content. Four different responses. Files: `src/failures.ts` 3. Pause the account on auth failure Rather than failing every queued post in sequence. Files: `src/accounts.ts` ### Done when - [ ] A retry after timeout does not duplicate - [ ] Failures are classified and handled by class - [ ] Content rejections are surfaced, not retried - [ ] An auth failure pauses the account ### Watch out - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. ## Not in this build - Engagement automation. - Browser automation of the web interface. - Analytics beyond what the API returns. - Broad platform coverage. ## After v1, if you want it - Per-slot content types, so the queue mixes rather than posting five similar things - A cost comparison between posting with and without a link, which is now a real editorial decision - Draft review before an item enters the queue, once the queue itself is trustworthy
# Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Queue state, spend and platform tokens by reference. DATABASE_URL=postgres://localhost:5432/queue # Required · secret. Official API only. Browser automation is not part of this build. PLATFORM_API_KEY=a platform-issued key # Required. Dated per-write pricing including the URL surcharge. PRICING_PATH=./config/pricing.json # Required. Dated endpoint availability per tier. This is what changed in 2026. CAPABILITIES_PATH=./config/capabilities.json # Required. Checked before scheduling. Writes cost money per item. MONTHLY_BUDGET_MINOR=3000 # Required. Present as a constant. An empty slot is information; a recycled post is noise. RECYCLE_OLD_POSTS=never
# Hypefury · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Hypefury, draft and schedule text-led social posts with reusable queues. The hard boundary is platform integrations, auto-reposting, cross-posting, analytics, and creator templates, plus api access, connector upkeep, and collaboration. ## Product outcome A queue that knows what each post costs before it sends, refuses to schedule something your tier cannot do, and never quietly reposts old content to fill a gap. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - developer application for one social network - OAuth credentials - PostgreSQL - public HTTPS endpoint - background worker ## Explicit non-goals for v1 - Engagement automation. - Browser automation of the web interface. - Analytics beyond what the API returns. - Broad platform coverage. - platform integrations, auto-reposting, cross-posting, analytics, and creator templates - many maintained network APIs - inbox and moderation - deep analytics - team approvals and asset libraries ## Success criteria - An unavailable action is confirmed refused at scheduling - URL posts are confirmed priced with the surcharge - An empty queue is confirmed to leave the slot empty - A retry after timeout is confirmed not to duplicate - A mid-thread failure is confirmed to follow a stated rule
# Build brief · Hypefury The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a posting queue for text-led social, built around what the APIs now actually cost and allow to replace Hypefury. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Build this against the current API terms rather than the ones a tutorial describes, because they changed substantially and in a direction that shapes the whole project. On X, pay-per-use became the default for new developers on 6 February 2026 and there is no meaningful free tier. Writes bill per request, at roughly $0.015 for a plain post and $0.20 for one containing a URL, which makes a link over ten times the price of a post without one. The April 2026 update removed follow, like and quote-post write endpoints from every self-serve tier, leaving them to Enterprise with no grandfathering, and the Pro tier was deprecated in August 2026. Two consequences. Each scheduled post has a real per-item cost, so the queue needs a budget the way a paid API client does, and features built on engagement endpoints cannot be built at all on a self-serve tier rather than being merely discouraged. The workaround people reach for is driving the web interface with a browser, which is outside the API and against the terms, and it puts your account at risk rather than theirs. This build does not include it. ### Stack (fixed, do not substitute) - Publishing: Official APIs only, one adapter per platform · driving the web interface is against the terms and risks your account - Cost: Per-post cost estimated before scheduling and tracked after · writes are billed per request and links cost substantially more - Capability: A declared capability map per platform and tier · an endpoint your tier does not have should fail at scheduling, not at send - Queue: Slots with per-platform rate awareness and a budget check · a queue that ignores either produces a bill or a gap - Runtime: TypeScript with PostgreSQL and a worker · scheduling, retries and history are the system ### Data model (create this before Phase 1) - accounts(id, platform, handle, tier, connected_at, capabilities_json) - posts(id, account_id, body, media_ids_json, contains_url, thread_parent_id, state, scheduled_for, posted_at, external_id) - cost_estimates(post_id, currency, amount_minor, basis) · basis records which price rule applied - spend(account_id, period, estimated_minor, actual_minor) - capabilities(platform, tier, endpoint, available, checked_on, source_url) · dated, because this is what changed - slots(id, account_id, day_of_week, time_local, timezone) · the recurring queue - failures(post_id, at, kind, detail, retryable) · kind is rate_limit, capability, auth or content - A post records whether it contains a URL, because that single fact changes its cost by more than an order of magnitude. ### Phase 1 · Knowing what your tier can actually do Build: A capability map per platform and tier, dated with a source. Check it when a post is created and refuse to schedule something your tier cannot perform. Surface capability records that have not been rechecked recently. Trap: Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. Done when: Scheduling an action your tier cannot perform is refused at creation with the reason. The capability map records a checked date and a source per entry. Stale capability records are surfaced for rechecking. A capability removed after a post was scheduled is caught before sending rather than failing silently. Do not build yet: No costing, queue or publishing. ### Phase 2 · Costing a post before you queue it Build: Estimate cost per post from a dated pricing table, accounting for whether the post contains a URL. Show the projected cost of the queue. Track actual spend against estimate. Trap: A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. Done when: Cost is estimated per post with the URL surcharge applied, verified against the current pricing table. The queue's projected cost for a period is shown before it runs. Actual spend is tracked against estimate and divergence is surfaced. Pricing entries record when they were checked and are flagged when stale. Do not build yet: No queue mechanics or publishing yet. ### Phase 3 · A queue that fills slots sensibly Build: Recurring slots in a stated timezone, filled from a queue in order. Handle an empty queue by leaving the slot empty rather than reposting. Handle a slot falling on a daylight transition. Trap: Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. Done when: Slots fill from the queue in order and an empty queue leaves the slot empty with a notification. Slots compute correctly across both daylight transitions. Reordering the queue does not lose scheduled items. No automatic recycling of previously posted content exists, verified by looking for it. Do not build yet: No publishing or failure handling yet. ### Phase 4 · Publishing, and failing usefully Build: Publish through official APIs with idempotency so a retry cannot double post. Classify failures into rate limit, capability, authentication and content, and handle each differently. Never retry a content rejection. Trap: Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. Done when: A retry after a timeout does not create a duplicate, tested by simulating one. Failures are classified and only retryable classes are retried. A content rejection is surfaced with the platform's reason rather than retried. An authentication failure pauses the account rather than burning through the queue. Do not build yet: No threads or reporting yet. ### Phase 5 · Threads, media and knowing what happened Build: Publish threads in order with each part linked to the previous, handling a mid-thread failure. Upload media through the platform's own flow. Report what posted, what failed and what it cost. Trap: A thread that fails on its third part leaves two orphaned posts in public that read as an incomplete thought, which is worse than nothing having posted at all. Decide the rule in advance: either delete the published parts or post a continuation, and make the choice explicit rather than leaving orphans. Done when: A thread publishes in order with each part correctly linked. A mid-thread failure follows a stated rule rather than leaving orphans, tested by injecting a failure. Media uploads through the platform's own flow with size and format validated first. The report shows posted, failed and cost per period. Do not build yet: Nothing after this. ### Out of scope (and why) - Any engagement automation, which is unavailable below Enterprise on self-serve tiers and is not something to work around. - Browser automation of the web interface, which is against the terms and puts your own account at risk. - Analytics beyond what the API returns, which is most of what the hosted products sell and which requires read volume you would be paying for. - Broad platform coverage, which is an adapter per platform and a maintenance burden that never ends. ### README must contain - That writes are billed per request and a post containing a link costs many times one without - That several endpoints are unavailable below Enterprise, so some features cannot be built rather than being discouraged - That capability and pricing are dated data, because both changed substantially during 2026 - That there is no browser automation, deliberately - That an empty queue leaves an empty slot rather than recycling old posts
# Architecture · Hypefury ## Stack | Part | Choice | Why | | --- | --- | --- | | Publishing | Official APIs only, one adapter per platform | driving the web interface is against the terms and risks your account | | Cost | Per-post cost estimated before scheduling and tracked after | writes are billed per request and links cost substantially more | | Capability | A declared capability map per platform and tier | an endpoint your tier does not have should fail at scheduling, not at send | | Queue | Slots with per-platform rate awareness and a budget check | a queue that ignores either produces a bill or a gap | | Runtime | TypeScript with PostgreSQL and a worker | scheduling, retries and history are the system | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Capability | Dated endpoint availability per tier. | Any source, provided it is data with a checked date rather than constants. | | Cost | Per-post estimation including the URL surcharge. | Any pricing source, provided the surcharge is visible where links are added. | | Queue | Slots, ordering and empty-slot behaviour. | Any scheduler, provided it does not recycle old content. | | Publishing | Official API calls with idempotency. | Any client, provided browser automation is not one of them. | | Failures | Classification and selective retry. | Any implementation, provided content rejections are never retried. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DATABASE_URL` · required, secret · Queue state, spend and platform tokens by reference. - `PLATFORM_API_KEY` · required, secret · Official API only. Browser automation is not part of this build. - `PRICING_PATH` · required · Dated per-write pricing including the URL surcharge. - `CAPABILITIES_PATH` · required · Dated endpoint availability per tier. This is what changed in 2026. - `MONTHLY_BUDGET_MINOR` · required · Checked before scheduling. Writes cost money per item. - `RECYCLE_OLD_POSTS` · required · Present as a constant. An empty slot is information; a recycled post is noise. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Hypefury product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Official APIs only, one adapter per platform, Per-post cost estimated before scheduling and tracked after, A declared capability map per platform and tier, Slots with per-platform rate awareness and a budget check, TypeScript with PostgreSQL and a worker. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. - A thread that fails on its third part leaves two orphaned posts in public that read as an incomplete thought, which is worse than nothing having posted at all. Decide the rule in advance: either delete the published parts or post a continuation, and make the choice explicit rather than leaving orphans.
# Delivery milestones · Hypefury Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Knowing what your tier can actually do An unavailable capability fails when you schedule, not when it sends. ### Steps 1. Keep capabilities as dated data They changed in 2026 without grandfathering. Constants go quietly wrong. Files: `config/capabilities.json` 2. Check at scheduling time A failure at send is a failure you find out about afterwards. Files: `src/capability.ts` 3. Recheck before sending too Between scheduling and sending, a tier can change. Files: `src/publish.ts` ### Done when - [ ] An unavailable action is refused at scheduling - [ ] Capabilities are dated with a source - [ ] Stale records are surfaced - [ ] A post-scheduling removal is caught before sending ### Watch out - Discovering that your tier no longer has an endpoint at the moment a scheduled post tries to send means a silent failure at the worst possible time, and the removals in 2026 came without grandfathering. Check capability at scheduling time, and treat the map as dated data that goes stale rather than as constants. ## M2 · Costing a post before you queue it You know what a week's queue will cost before it runs. ### Steps 1. Detect URLs and price accordingly A link costs over ten times a plain post. That belongs in the interface. Files: `src/cost.ts`, `src/cost.test.ts` 2. Project the queue's cost Before the week runs, not on the invoice. Files: `src/projection.ts` 3. Track actual against estimate Divergence means the pricing table is stale. Files: `src/spend.ts` ### Done when - [ ] URL posts are priced with the surcharge - [ ] Queue cost is projected before running - [ ] Actual spend is tracked against estimate - [ ] Stale pricing is flagged ### Watch out - A post containing a link costs many times one without, which is invisible in an interface that treats all posts the same, and a queue full of link posts produces a bill nobody predicted. Detect the URL, price accordingly, and show the difference at the point where someone is deciding whether to include a link. ## M3 · A queue that fills slots sensibly Content flows into recurring slots without you scheduling each item. ### Steps 1. Define slots in a stated timezone Test both daylight transitions. A post an hour early is visible. Files: `src/slots.ts`, `src/slots.test.ts` 2. Leave empty slots empty An empty queue is information. A recycled post is noise. Files: `src/queue.ts` 3. Keep reordering safe Moving an item should not lose its scheduled position. Files: `src/queue.ts` ### Done when - [ ] Slots fill in order from the queue - [ ] An empty queue leaves the slot empty - [ ] Both daylight transitions are handled - [ ] No automatic recycling exists ### Watch out - Automatically recycling old posts when the queue runs dry is the feature that turns a scheduling tool into a repetition machine, and followers notice long before the operator does. Leave the slot empty and say the queue is empty, since an empty slot is information and a recycled post is noise. ## M4 · Publishing, and failing usefully Posts go out through the official API and failures are explicable. ### Steps 1. Use an idempotency key per post A timeout that actually succeeded is the common duplicate. Files: `src/publish.ts` 2. Classify failures Rate limit, capability, auth, content. Four different responses. Files: `src/failures.ts` 3. Pause the account on auth failure Rather than failing every queued post in sequence. Files: `src/accounts.ts` ### Done when - [ ] A retry after timeout does not duplicate - [ ] Failures are classified and handled by class - [ ] Content rejections are surfaced, not retried - [ ] An auth failure pauses the account ### Watch out - Retrying every failure identically will repost after a timeout that actually succeeded, producing a duplicate, and will retry a content rejection forever. Use an idempotency key, classify the failure, and retry only the classes where retrying can help. ## M5 · Threads, media and knowing what happened (production only) Multi-part posts publish correctly and you can see what the queue did. ### Steps 1. Link thread parts correctly Each to the previous, in order, with the platform's own mechanism. Files: `src/threads.ts` 2. Decide the mid-thread failure rule Delete or continue. Orphaned parts are the worst outcome. Files: `src/threads.ts` 3. Validate media before uploading Size and format. A rejection after upload wastes the request and the money. Files: `src/media.ts` ### Done when - [ ] Threads publish in order and linked - [ ] A mid-thread failure follows a stated rule - [ ] Media is validated before upload - [ ] The report shows posted, failed and cost ### Watch out - A thread that fails on its third part leaves two orphaned posts in public that read as an incomplete thought, which is worse than nothing having posted at all. Decide the rule in advance: either delete the published parts or post a continuation, and make the choice explicit rather than leaving orphans.
# Operations · Hypefury ## Backup The queue and the spend record are the operational data. Tokens should be recoverable by reconnecting rather than restored, since a restored token may have been revoked. ## Restore Restore and confirm idempotency records survived, since losing them means a resumed queue can republish posts that already went out. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Spend against estimate and capability staleness. Both go wrong quietly, and the second is what turns into a silent send failure when a platform changes its tiers again. ## Incident checklist A duplicate post is the most visible failure and means an idempotency key was missing or lost. A silent gap in the queue usually means a capability check failed at send rather than at scheduling. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] An unavailable action is confirmed refused at scheduling - [ ] URL posts are confirmed priced with the surcharge - [ ] An empty queue is confirmed to leave the slot empty - [ ] A retry after timeout is confirmed not to duplicate - [ ] A mid-thread failure is confirmed to follow a stated rule ## Launch constraint Do not market omitted Hypefury capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Queue state, spend and platform tokens by reference. DATABASE_URL=postgres://localhost:5432/queue # Required · secret. Official API only. Browser automation is not part of this build. PLATFORM_API_KEY=a platform-issued key # Required. Dated per-write pricing including the URL surcharge. PRICING_PATH=./config/pricing.json # Required. Dated endpoint availability per tier. This is what changed in 2026. CAPABILITIES_PATH=./config/capabilities.json # Required. Checked before scheduling. Writes cost money per item. MONTHLY_BUDGET_MINOR=3000 # Required. Present as a constant. An empty slot is information; a recycled post is noise. RECYCLE_OLD_POSTS=never
$ choose a build depth, inspect the files, then open the complete pack in your agent
People still pay for Hypefury because the scheduler is easy; staying approved and correct across constantly changing social APIs is the subscription. The recurring cost buys OAuth review, API policy changes, media limits, queues, retries, rate limits, webhooks, tokens, and moderation workflows, not just the visible interface.
xplatform integrations, auto-reposting, cross-posting, analytics, and creator templates
xmany maintained network APIs
xinbox and moderation
xdeep analytics
xteam approvals and asset libraries
Don't feel like building it? These folks already made it free.
all 5 free alternatives to Hypefury →· no votes, no pay-to-list · just what's real
Vibecode Hypefury
Kinda. The core of Hypefury is buildable in a weekend with the prompt on this page, but there are real gaps: platform integrations, auto-reposting, cross-posting, analytics, and creator templates, many maintained network APIs. Read the honest list above before committing.
How much does Hypefury cost?
Hypefury costs about $29/month (Standard, checked 2026-07-31), which is $348 per year.
What do I lose by replacing Hypefury?
Honestly: platform integrations, auto-reposting, cross-posting, analytics, and creator templates; many maintained network APIs; inbox and moderation; deep analytics; team approvals and asset libraries. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Hypefury?
Yes: Postiz (Threads, scheduling and recurring posts across many networks; the server stack is the recurring part you keep.) Mixpost Lite (Evergreen queues and scheduled posts without the X-growth theatre; Docker is compulsory.) BrightBean Studio (Drafts, schedules and repeatable publishing across networks; the growth-hack buttons are absent.) All 5 curated free alternatives are at vibecodeit.com/hypefury/alternatives. The prompt is for when you want it exactly your way.