API

Pushpen has one real API endpoint, and it exists to power the official GitHub Action (published on the GitHub Marketplace). This page documents it accurately, verified against the current code — not a general-purpose developer API, and not a roadmap promise.

If you want automatic documentation without touching your own CI, connect a repository from the dashboard instead — that path needs no API key and no workflow file.

Endpoint

POST https://pushpen.dev/api/action/generate-docs

Authentication — two credentials, both required

Sent as two separate request headers, not a single Bearer token:

x-pushpen-api-key — an API key generated from the dashboard under Settings → API Keys. Account-wide, not repo-scoped — it identifies which Pushpen account and plan to bill against. Shown once at creation; only its prefix (pk_live_...) is ever visible again.

x-github-token — a GitHub token with write access (contents + pull requests) to the target repository.

The supported way to get the GitHub token is the official Action, where it's the workflow's own automatic GITHUB_TOKEN — passed explicitly as github-token: ${{ secrets.GITHUB_TOKEN }}, no separate setup. This is the only integration path Pushpen documents or supports.

For completeness: the endpoint itself doesn't verify that the token actually came from a GitHub Actions run — any valid token with equivalent write access would be accepted the same way. That isn't a documented or supported integration path; calling this endpoint outside the Action is unverified territory with no guarantees about behavior across future changes.

Request body

{
  "repoOwner": "octocat",       // required
  "repoName": "hello-world",    // required
  "eventName": "push",          // optional, defaults to "push"
  "headSha": "a1b2c3d...",      // optional — used to ignore duplicate deliveries of the same commit
  "commits": [                  // optional — present for push events; drives what gets generated
    {
      "id": "a1b2c3d...",
      "message": "fix: handle empty responses",
      "author": { "name": "Jane Doe", "email": "jane@example.com" },
      "added": [], "modified": ["src/api.ts"], "removed": []
    }
  ],
  "docTypes": ["readme", "changelog"]   // optional — narrows your plan's defaults, never expands past them
}

repoOwner and repoName must match ^[a-zA-Z0-9_.-]+$. If a repository named by repoOwner/repoNameisn't already connected to your account, this request connects it automatically (subject to your plan's repository limit, below) and generates an initial documentation baseline from the full repository instead of a diff, since there's nothing to diff against yet.

Responses

200 — generated

{
  "success": true,
  "generated": ["readme", "changelog"],
  "prUrl": "https://github.com/octocat/hello-world/pull/47",  // present only if a PR was opened
  "unchanged": ["api-docs"],        // present only if some doc types had nothing to change
  "failed": ["onboarding"],         // present only if generation itself failed for a doc type
  "connected": false,               // true only on this repo's first-ever request
  "kickstart": false                // true only when this run generated the initial baseline
}

deliveryFailed and skipped_no_match can also appear, each only when relevant — none of the array fields are guaranteed present. No PR is opened (no prUrl) if the repository has auto-commit enabled and the delivery committed directly instead.

200 — not a generation result

Two edge cases return 200 without the shape above:

{ "message": "duplicate delivery, ignored" }

Returned when headSha matches a request already handled recently — redelivery protection, not an error.

{ "skipped": true, "reason": "kickstart_in_progress" }

Returned if this repository's first-run baseline is already being generated by a concurrent request.

Errors

StatuserrorWhen
401missing_api_keyx-pushpen-api-key header not sent
400missing_github_tokenx-github-token header not sent
401invalid_api_keyKey doesn't match any active key, or it's been revoked
429rate_limitedOver 50 requests/hour for this specific API key
400invalid_requestRequest body fails validation (bad repoOwner/repoName format, malformed commits, etc.)
403repo_limit_reachedFirst-ever request for this repo, and your plan's connected-repo limit is already used
500connect_failedAuto-connecting the repository failed at the database level
403limit_reachedYour plan's generation quota (doc-generation count) is used up
403plan_requireddocTypes explicitly requested only paid-plan doc types (api-docs/onboarding) on the free plan

Every error body is { error, message }, with upgrade_url: "/upgrade" added for plan/limit errors. The limit_reached and plan_required cases also carry connected/kickstart, since they're returned after the repo-connect step already ran.

Rate limit

50 requests per hour, per API key — independent of any other Pushpen rate limit (the dashboard and webhook paths are limited separately).

Plan and doc-type restrictions

Free-plan keys can generate readme and changelog only — requesting api-docs or onboarding is silently dropped (or rejected with plan_required if that leaves nothing else requested), never expanded past what the plan actually allows. Starter, Pro, Teams plans can generate all 4 doc types. Repository-count and generation-quota limits are identical to the dashboard and webhook paths — this endpoint enforces the same limits, not a separate copy of them. Current plan details: pushpen.dev/pricing.

Setup

See the pushpen-action README for the full workflow YAML example and required permissions: block. A machine-readable summary of Pushpen as a whole, including this endpoint's role, is at /llms-full.txt.