Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/producthunt

README.md

222 lines · 8.6 KB · Markdown

@kody/producthunt

Intent

Give any Kody account reusable Product Hunt helpers for today’s posts, lookups (posts, hunters, topics, collections), and comments as allowed by the Product Hunt API. The public GraphQL schema can read comments; it does not expose a comment-create mutation. Writes that are in the schema (goals and follow/unfollow) go through ./request with dryRun: true / confirm: true.

This listing is meant to be forked. After you fork, connect your Product Hunt app or developer token and run ./smoke-test on your copy. Do not treat the live @kody/producthunt integration as yours.

Share this package as https://kody.codes/@kody/producthunt (never a /community/{listing_id} URL).

When To Use

  • List today’s featured hunts without writing GraphQL by hand
  • Look up a post, hunter, topic, or collection by id or slug
  • Read comments on a post (./list-comments)
  • Call an unwrapped Product Hunt GraphQL operation through ./request
  • Verify credentials with ./smoke-test before building workflows

Do not use this package as a scraper, a place to paste access tokens, or a comment poster — Product Hunt’s public API does not expose comment writes.

Agent setup

Product Hunt has no built-in Kody OAuth app. Tokens come from a bring-your-own confidential OAuth app or a developer token saved as a user secret. Never paste a client secret or access token into chat.

Required API host: api.producthunt.com. Approve it in the account secrets UI. Saving a secret does not approve hosts by itself.

LaneWhen to useCredential
OAuth (recommended)Connected hunter (viewer), private fields, refreshable tokensSaved integration producthunt or producthunt-<purpose>
Developer tokenPublic reads when you only have a dashboard tokenUser secret producthuntAccessToken (or producthuntAccessToken-<purpose>)
Lane A — BYO OAuth
  1. Create a Confidential OAuth app at Product Hunt API applications. Public clients cannot store the secret Kody needs.
  2. Register the redirect URI exactly as https://kody.codes/connect/oauth.
  3. Request scopes public and private (space-separated). Add write only when Product Hunt has approved your app for mutations.
  4. Open this prefilled connect URL while signed in to Kody:

https://kody.codes/connect/oauth?provider=producthunt&authorizeUrl=https%3A%2F%2Fapi.producthunt.com%2Fv2%2Foauth%2Fauthorize&tokenUrl=https%3A%2F%2Fapi.producthunt.com%2Fv2%2Foauth%2Ftoken&apiBaseUrl=https%3A%2F%2Fapi.producthunt.com%2Fv2%2Fapi%2Fgraphql&flow=confidential&allowedHosts=api.producthunt.com&dashboardUrl=https%3A%2F%2Fapi.producthunt.com%2Fv2%2Foauth%2Fapplications&scopes=public%20private

  1. Paste the Client ID and Client Secret into the Kody form (not into chat). If you need to save the client secret yourself first:

https://kody.codes/account/secrets/new?name=producthuntClientSecret&description=Product%20Hunt%20OAuth%20client%20secret%20for%20the%20confidential%20app&allowedHosts=api.producthunt.com&scope=user

  1. Approve host api.producthunt.com.
  2. Reconnect later with https://kody.codes/connect/oauth?provider=producthunt

Decoded connect fields:

  • Redirect / callback: https://kody.codes/connect/oauth
  • Authorize: https://api.producthunt.com/v2/oauth/authorize
  • Token: https://api.producthunt.com/v2/oauth/token
  • API: https://api.producthunt.com/v2/api/graphql
  • Flow: confidential
  • Host: api.producthunt.com
  • Scopes: public, private

To connect a second account, change provider (for example provider=producthunt-work) and pass account: 'work' or integrationName: 'producthunt-work' on every call.

Lane B — Developer / access token
  1. Create or copy a developer token from the same API applications page.
  2. Save it (do not paste the value in chat):

https://kody.codes/account/secrets/new?name=producthuntAccessToken&description=Product%20Hunt%20developer%20or%20OAuth%20access%20token%20for%20GraphQL%20reads&allowedHosts=api.producthunt.com&scope=user

  1. Approve host api.producthunt.com.
  2. Run ./smoke-test. For a second token use producthuntAccessToken-work and pass secretName: 'producthuntAccessToken-work' (or account: 'work').

Scopes

ScopeNeeded for
publicFeatured posts, lookups, comments, topics, collections
private./get-viewer (viewer.user)
writeGoal and follow mutations via ./request (Product Hunt must approve the app)

./request uses whatever scopes the saved token already has.

Auth / transport

There is no Product Hunt OpenAPI surface. This package posts GraphQL to https://api.producthunt.com/v2/api/graphql with either createAuthenticatedFetch('producthunt') or Authorization: Bearer from producthuntAccessToken.

Mutation safety

The public schema’s mutations are goals and user follow/unfollow — not comments. ./request treats query documents as read-only. Mutation documents need confirm: true. Pass dryRun: true to inspect the payload without calling Product Hunt.

import request from 'kody:@kody/producthunt/request'

export default async function main() {
	return await request({
		query: `mutation FollowUser($userId: ID!) { userFollow(input: { userId: $userId }) { user { id } } }`,
		variables: { userId: 'user-id-from-caller' },
		dryRun: true,
	})
}

Multiple accounts

Every export accepts:

  • accountwork → integration producthunt-work, secret producthuntAccessToken-work
  • integrationName / integration — saved OAuth name (producthunt or producthunt-*)
  • secretName — developer-token secret override (default producthuntAccessToken)
  • auth'oauth' or 'token' when both lanes exist

Do not hard-code a hunter username or post slug.

Exports

ExportPurpose
.Package overview and setup URLs. Pass { action: 'smoke-test' } to smoke.
./accountsResolve integration/secret names and report what is connected
./smoke-testSetup URLs always; live featured/today sample when credentials exist
./guideConnect URL and setup notes ({ origin?: string })
./list-today-postsFeatured hunts for the current Product Hunt day (Pacific Time)
./list-postsPaginated posts (featured, date window, topic, order)
./get-postPost by id or slug
./list-commentsComments on a post (read-only; API has no comment create)
./get-userHunter by id or username
./get-viewerConnected hunter (private scope)
./get-topic / ./list-topicsTopic lookup and search
./get-collection / ./list-collectionsCollection lookup and lists
./requestAuthenticated GraphQL escape hatch (dryRun / confirm on mutations)
./typesShared TypeScript type catalog

Page sizes are clamped to 1–20 to match the Product Hunt API.

Smoke test

After connect or reconnect, invoke ./smoke-test in the forked package runtime (not a static import):

import { packages } from 'kody:runtime'

export default async function main() {
	return await packages.invoke({
		kodyId: 'producthunt',
		exportName: './smoke-test',
	})
}

Or through the root export:

import producthunt from 'kody:@kody/producthunt'

export default async function main() {
	return await producthunt({ action: 'smoke-test' })
}

Without credentials this returns { ok: true, live: false } plus the connect and token setup URLs. After OAuth or a developer token is saved it reads today’s featured hunts and returns { live: true, hasViewer, todayPostCount } without hunter PII.

Examples

import listTodayPosts from 'kody:@kody/producthunt/list-today-posts'

export default async function main() {
	return await listTodayPosts({ first: 5 })
}
import getPost from 'kody:@kody/producthunt/get-post'
import listComments from 'kody:@kody/producthunt/list-comments'

export default async function main() {
	const post = await getPost({ slug: 'some-product-slug' })
	const comments = await listComments({ slug: post.slug, first: 10 })
	return { post, comments }
}

Troubleshooting

  • Connect page asks for authorize/token URLs: use the complete first-time URL in Lane A.
  • HTTP 401 / expired token: reconnect at https://kody.codes/connect/oauth?provider=producthunt
  • viewer.user empty: reconnect with the private scope.
  • Mutation FORBIDDEN: Product Hunt must approve write on your app, then reconnect with that scope.
  • redirect_uri mismatch: the Product Hunt app redirect must be exactly https://kody.codes/connect/oauth.