All jobs

Add OpenAPI Docs to a Gin API

Yes. An agent annotates your Gin handlers with swag comments, generates the OpenAPI spec, serves Swagger UI, checks it against the real routes and opens a PR.

Run this on RushIn a Project that has your repository.

What the agent does

The agent starts by building the list of routes from the code, not from a README, because a generated spec is only as true as the handlers it documents. In a Gin API like gothinkster/golang-gin-realworld-example-app, hello.go mounts everything under /api: users.UsersRegister on /users, then the optional AuthMiddleware(false) for the anonymous article, tag and profile routes, then AuthMiddleware(true) for /user, /profiles/:username/follow and the authenticated article routes, plus /api/ping. users/routers.go and articles/routers.go register each handler, often twice, with and without a trailing slash because RedirectTrailingSlash is off. go.mod says go 1.21, gin v1.10.0, validator/v10 and golang-jwt/jwt/v5, and users/middlewares.go reads the token from an Authorization header that begins with Token .

Then it reads the shapes. Request bodies are the validator structs in users/validators.go and articles/validators.go, such as UserModelValidator with a nested User object whose fields carry binding tags like required,min=4,max=255 and email. Responses are the serializer structs in users/serializers.go and articles/serializers.go, UserResponse and ProfileResponse among them, but the handlers wrap them in gin.H{"user": ...} or gin.H{"article": ...}, and errors come back as common.CommonError with an errors map from common/utils.go. Status codes are explicit in each handler: 201 on create, 404 for an unknown slug or username, 422 for validation and database errors, 401 on a failed login and 403 when the caller is not the author.

From that it adds swag annotations. hello.go gets the general block: title, version, @BasePath /api and an apikey security definition named for the Authorization header with a description that the value is Token <jwt>. Each handler gets @Summary, @Tags, @Param, @Success and @Failure lines with the right codes, and because gin.H has no schema, the agent defines small envelope types, UserEnvelope, ArticleEnvelope, ArticlesEnvelope with articlesCount, CommentsEnvelope, TagsEnvelope and an ErrorEnvelope for CommonError, so the generated schemas match the JSON the handlers emit. Only the canonical path of each duplicate route is annotated. It adds github.com/swaggo/swag, github.com/swaggo/gin-swagger and github.com/swaggo/files to go.mod, runs swag init -g hello.go to generate docs/docs.go, docs/swagger.json and docs/swagger.yaml, and mounts GET /swagger/*any with ginSwagger.WrapHandler(swaggerFiles.Handler) in hello.go.

Then it checks the spec against the real server and the repository's own gates. It starts the API, loads /swagger/doc.json, and compares every request in api/Conduit.postman_collection.json with the paths and methods in the spec, listing any route the collection calls that the spec lacks. It runs scripts/run-api-tests.sh with Newman as .github/workflows/ci.yml does to confirm the server still passes, scripts/gofmt.sh, golangci-lint with the staticcheck, unused and misspell linters from .golangci.yml, and go test ./..., and it excludes the generated docs/ package from the linter rather than editing generated code. A CI step runs swag init and fails on git diff --exit-code docs/, so the committed spec cannot drift from the annotations.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Add OpenAPI documentation to this Gin API. Read hello.go, go.mod, users/routers.go, users/validators.go, users/serializers.go, users/middlewares.go, articles/routers.go, articles/validators.go, articles/serializers.go, common/utils.go, api/Conduit.postman_collection.json, scripts/run-api-tests.sh, .golangci.yml and .github/workflows/ci.yml first, and list every route with its method, auth requirement, request struct, response wrapper and status codes. Use swaggo/swag annotations on each handler, with a general block in hello.go that sets the base path and an API key security definition for the Authorization header whose value is Token followed by the JWT. Define named envelope types for the gin.H wrappers and the errors map so the generated schemas match the real JSON, and annotate only the canonical path of routes registered with and without a trailing slash. Add the swag, gin-swagger and files modules, run swag init, commit the generated docs package and serve Swagger UI at /swagger. Start the server, compare the spec's paths with the requests in the Postman collection and list any gaps, run the Newman API tests, gofmt, golangci-lint with the generated package excluded, and go test. Add a CI step that regenerates the docs and fails if they differ. Fix anything until everything passes, and open a pull request that lists the documented routes and where to find the UI.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is a pull request on its own branch with the annotated handler files, the envelope types, the generated docs/ package, the /swagger/*any route in hello.go, the module additions in go.mod and go.sum, the linter exclusion and the new CI step. The description lists every documented route grouped by tag with its auth requirement, names the handlers whose responses needed an envelope type, and reports the comparison with the Postman collection, including any route the collection calls that is still undocumented.

The transcript on the session keeps every command with its output: swag init, the server start, the fetch of /swagger/doc.json, the Newman run, gofmt, golangci-lint, go test and any linter complaint fixed on the way. The repository's own CI workflow runs on the pull request too, so the Go version matrix, the coverage run, golangci-lint, gosec and the API integration job report beside the new doc-drift step.

Before merging, a reviewer opens the Swagger UI from the branch, tries a request that needs the Token header through the authorize dialog, checks that the 422 error example matches what common.NewValidatorError really returns, and confirms the generated files are committed so the API serves the spec without swag installed. A follow-up message to the same session adds examples to specific fields or splits the spec by tag.

Run it on every Linear issue labelled api-docs

A team that tracks documentation work in Linear starts one run per issue from an automation with a linear trigger on the api-docs label. The automation adds the issue after this prompt, so the agent reads which routes or fields the issue asks for and starts in the repository the automation names.

Each run opens its own pull request from its own sandbox, and an automation run has its own max_runs_per_hour cap. The pull request links back to the issue, and the transcript holds the swag init output and the collection comparison for that change.

The use case this job follows, with its automation and trigger: Turn a Linear label into a PR.

Why run it on Rush?

The tedious part of this job is reading every handler, every validator struct and every serializer to write annotations that match, then discovering that gin.H wrappers produce empty schemas and going back to add envelope types. Doing it for two dozen routes is an afternoon on a laptop, and checking the result means starting the server and clicking through the UI. In a Rush sandbox the agent runs the server, Newman and the linters itself, and the sandbox parks when the turn ends.

The pull request carries its own proof. The comparison between the spec and the Postman collection, the Newman run and the linter output are in the transcript beside the commands that produced them, so a reviewer can see that the documented routes are the ones the API serves, not the ones a README remembers.

Frequently asked questions

Why are there new envelope types?

Because the handlers return gin.H{"user": serializer.Response()} and similar maps, which carry no type information for swag. Small structs such as UserEnvelope with a User UserResponse field give the generator a schema that matches the JSON exactly. They are used only in annotations and optionally in the handlers, and the pull request says which.

Do I need swag installed to run the API?

No. swag init output is committed as the docs/ package, and hello.go imports it so the spec is compiled into the binary and served at /swagger/doc.json. swag is needed only to regenerate after an annotation changes, and the new CI step catches a forgotten regeneration.

How is authentication described in the spec?

As an API key security scheme on the Authorization header, because users/middlewares.go expects Token followed by the JWT rather than the Bearer scheme. The authorize dialog in Swagger UI then accepts the full Token <jwt> value, and each route that runs behind AuthMiddleware(true) is marked as requiring it.

Will golangci-lint complain about the generated code?

It would, so the agent excludes the docs/ directory in .golangci.yml instead of editing generated files. gofmt.sh and go test ./... run over the rest as before, and the transcript shows them passing.