Skip to content

Fix draft listing on custom-domain / Cloudflare publications#8

Open
mattmillerai wants to merge 1 commit into
conorbronsdon:mainfrom
mattmillerai:fix/custom-domain-drafts-endpoint
Open

Fix draft listing on custom-domain / Cloudflare publications#8
mattmillerai wants to merge 1 commit into
conorbronsdon:mainfrom
mattmillerai:fix/custom-domain-drafts-endpoint

Conversation

@mattmillerai

Copy link
Copy Markdown

Problem

On Substack publications served via a custom domain (Cloudflare-fronted), the server can't list drafts even with a valid session token:

  • list_drafts / validateAuth call GET /api/v1/drafts, which returns 403 "Not authorized" on these publications.
  • Cloudflare rejects the default Node/undici request signature with 403 error code: 1010 ("browser signature banned").
  • Against the canonical *.substack.com host, a missing Referer causes a 301 → custom domain → 401 redirect chase (the client uses redirect: "follow").

Net effect: auth appears to "fail" when the token is actually fine.

Fix

  • getDraftsGET /api/v1/post_management/drafts (the endpoint the Substack editor uses) with the required order_by/order_direction params; unwraps the { posts } response shape.
  • validateAuth validates by listing via the same endpoint instead of the dead /api/v1/drafts?limit=1.
  • request() defaults to a browser User-Agent (overridable via SUBSTACK_USER_AGENT) and sends Accept + Referer headers, keeping canonical-host API calls first-party and served directly.

Verification

Tested live against a custom-domain publication (custom domain → canonical *.substack.com): validateAuth, list_drafts, and get_draft all succeed where they previously 401'd.

  • npm run build clean
  • npm test → 96 passing (6 new: endpoint choice, posts[] unwrap, default + override headers)

Notes

  • For custom-domain publications, recommend setting SUBSTACK_PUBLICATION_URL to the canonical *.substack.com host (documented in the README).
  • No changes to the create/update/notes endpoints.

🤖 Generated with Claude Code

- getDrafts: use /api/v1/post_management/drafts (the endpoint the Substack
  editor itself uses) with order_by/order_direction, and unwrap the
  { posts } response. The bare /api/v1/drafts collection returns
  403 "Not authorized" on many publications.
- validateAuth: validate by listing via the same endpoint instead of the
  dead /api/v1/drafts?limit=1.
- request(): default to a browser User-Agent (overridable via
  SUBSTACK_USER_AGENT) and send Accept + Referer headers. Without a browser
  UA, Cloudflare-fronted custom domains reject requests with 403 error 1010;
  without a Referer, canonical *.substack.com API calls 301-redirect to the
  custom domain and then 401.
- Add tests covering the endpoint, the posts[] unwrap, and the new headers.
- Document custom-domain / Cloudflare behavior and SUBSTACK_USER_AGENT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant