# How to Use the AxonHub GraphQL API for Management Operations

> Manage channels, API keys, users, and more with the AxonHub GraphQL API. Discover type-safe, RBAC-protected operations implemented via gqlgen in this guide.

- Repository: [Loop/axonhub](https://github.com/looplj/axonhub)
- Tags: how-to-guide
- Published: 2026-03-06

---

**AxonHub exposes a single GraphQL endpoint at `/openapi/v1/graphql` that provides type-safe, RBAC-protected management operations for channels, API keys, users, roles, and projects, implemented via gqlgen and wired into the Gin HTTP server.**

The `looplj/axonhub` repository provides a comprehensive management interface through its **GraphQL API**. This endpoint allows administrators to programmatically control every aspect of the platform, from provisioning AI model channels to managing user permissions, all through a strongly-typed schema defined in the source code.

## Architecture of the GraphQL Management API

### GraphQL Schema Definition

All public mutations and queries live in `internal/server/gql/axonhub.graphql`. The `type Mutation` definition lists every management operation, including `createChannel`, `bulkCreateChannels`, `deleteChannel`, `disableChannelAPIKey`, `createUser`, and `updateProject`. These definitions enforce input validation and type safety at the API boundary.

Source: [`internal/server/gql/axonhub.graphql`](https://github.com/looplj/axonhub/blob/unstable/internal/server/gql/axonhub.graphql)

### Resolver Implementation

Each schema field is backed by a resolver in [`internal/server/gql/axonhub.resolvers.go`](https://github.com/looplj/axonhub/blob/main/internal/server/gql/axonhub.resolvers.go). These resolvers act as the bridge between the GraphQL layer and the business logic. For example, the `CreateChannel` resolver calls `r.channelService.CreateChannel` to persist the new channel and returns the result to the client.

Source: [[`internal/server/gql/axonhub.resolvers.go`](https://github.com/looplj/axonhub/blob/main/internal/server/gql/axonhub.resolvers.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/gql/axonhub.resolvers.go)

### Business Logic Layer

The core operations are implemented in the `internal/server/biz/` directory. Services such as `ChannelService`, `APIKeyService`, `UserService`, and `ProjectService` handle validation, database persistence via Ent, and side effects.

Sources:
- Channel: [[`internal/server/biz/channel.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/channel.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/biz/channel.go)
- API key: [[`internal/server/biz/apikey.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/apikey.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/biz/apikey.go)
- User/role/project services: [`internal/server/biz/user.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/user.go), [`internal/server/biz/role.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/role.go), [`internal/server/biz/project.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/project.go)

### Authentication and Authorization

Management calls require a valid **JWT**. Users obtain a token via the `POST /auth/signin` HTTP route, handled by [`internal/server/api/auth.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/auth.go). The token must be sent as an `Authorization: Bearer <token>` header on every GraphQL request. The Gin router applies middleware to enforce this protection.

Sources:
- Route definition: [[`internal/server/routes.go`](https://github.com/looplj/axonhub/blob/main/internal/server/routes.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/routes.go)
- Handler: [[`internal/server/api/auth.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/auth.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/api/auth.go)

## GraphQL Endpoint and Authentication

The management **GraphQL API** is exposed at `/openapi/v1/graphql`. All requests must include a Bearer token in the `Authorization` header.

First, obtain a **JWT** token:

```bash
curl -X POST http://localhost:8090/auth/signin \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@example.com","password":"your‑password"}'

```

The response contains:

```json
{
  "user": { "id": 1, "email": "admin@example.com" },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

```

## Management Operations Examples

### Creating a Channel

Use the `createChannel` mutation to provision a new AI model channel. This delegates to the channel service in [`internal/server/biz/channel.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/channel.go).

```bash
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI…"

curl -X POST http://localhost:8090/openapi/v1/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "query": "mutation CreateChannel($input: CreateChannelInput!) { createChannel(input: $input) { id name type status } }",
    "variables": {
      "input": {
        "type": "OPENAI",
        "name": "my-openai-channel",
        "tags": ["production"],
        "baseURL": "https://api.openai.com/v1",
        "apiKeys": ["sk-xxxx"],
        "supportedModels": ["gpt-4o"],
        "defaultTestModel": "gpt-4o"
      }
    }
  }'

```

### Disabling a Channel API Key

To revoke a specific key without deleting the channel, use the `disableChannelAPIKey` mutation implemented in the resolvers.

```bash
curl -X POST http://localhost:8090/openapi/v1/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "query": "mutation($channelID: ID!, $key: String!){disableChannelAPIKey(channelID: $channelID, key: $key)}",
    "variables": {
      "channelID": "1",
      "key": "sk-xxxx"
    }
  }'

```

### Managing Users and Projects

Similar patterns apply to user and project management. The `createUser`, `updateProject`, and related mutations follow the same resolver → service → database flow. Refer to [`internal/server/biz/user.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/user.go) and [`internal/server/biz/project.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/project.go) for the underlying business logic.

## Type-Safe Go Client with GenQLient

For production Go applications, use **genqlient** to generate a type-safe client from the schema.

First, install the tool and generate the client:

```bash
go install github.com/Khan/genqlient@latest
genqlient generate ./internal/server/gql/...

```

Then use the generated code:

```go
package main

import (
	"context"
	"log"
	"os"

	"github.com/Khan/genqlient/graphql"
	"your/module/internal/server/gql/genqlient"
)

func main() {
	token := os.Getenv("AXONHUB_TOKEN")
	httpClient := graphql.NewHTTPClient(
		"http://localhost:8090/openapi/v1/graphql",
		graphql.WithHeader("Authorization", "Bearer "+token),
	)

	ctx := context.Background()
	resp, err := genqlient.CreateChannel(ctx, httpClient, genqlient.CreateChannelInput{
		Type:             genqlient.ChannelTypeOpenAI,
		Name:             "go-client-channel",
		Tags:             []string{"automated"},
		BaseURL:          "https://api.openai.com/v1",
		APIKeys:          []string{"sk-xxxx"},
		SupportedModels:  []string{"gpt-4o-mini"},
		DefaultTestModel: "gpt-4o-mini",
	})
	if err != nil {
		log.Fatalf("createChannel failed: %v", err)
	}
	log.Printf("Created channel ID: %s", resp.ID)
}

```

## Key Source Files

| Purpose | File |
|--------|------|
| GraphQL schema (all management mutations/queries) | [`internal/server/gql/axonhub.graphql`](https://github.com/looplj/axonhub/blob/unstable/internal/server/gql/axonhub.graphql) |
| Resolver implementations (wire GraphQL to services) | [[`internal/server/gql/axonhub.resolvers.go`](https://github.com/looplj/axonhub/blob/main/internal/server/gql/axonhub.resolvers.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/gql/axonhub.resolvers.go) |
| Channel business logic | [[`internal/server/biz/channel.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/channel.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/biz/channel.go) |
| API‑key business logic | [[`internal/server/biz/apikey.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/apikey.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/biz/apikey.go) |
| User/role/project services (similar pattern) | [`internal/server/biz/user.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/user.go), [`internal/server/biz/role.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/role.go), [`internal/server/biz/project.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/project.go) |
| Authentication endpoint (sign‑in) | [[`internal/server/api/auth.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/auth.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/api/auth.go) |
| Gin route registration (auth + GraphQL) | [[`internal/server/routes.go`](https://github.com/looplj/axonhub/blob/main/internal/server/routes.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/routes.go) |
| Server startup & GraphQL handler registration | [[`internal/server/server.go`](https://github.com/looplj/axonhub/blob/main/internal/server/server.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/server.go) |
| Generated Go client (GenQLient) – after running `genqlient generate` | `internal/server/gql/genqlient/...` (auto‑generated) |

## Summary

- AxonHub exposes a single **GraphQL API** endpoint at `/openapi/v1/graphql` for all management operations.
- The schema is defined in `axonhub.graphql` and implemented by resolvers in [`axonhub.resolvers.go`](https://github.com/looplj/axonhub/blob/main/axonhub.resolvers.go), which delegate to business services in `internal/server/biz/`.
- **Authentication** requires a JWT obtained from `POST /auth/signin` and passed as a Bearer token.
- You can interact with the API using raw **cURL** commands or generate a type-safe **Go client** using **genqlient**.
- Key operations include creating channels, disabling API keys, managing users, and configuring projects.

## Frequently Asked Questions

### What is the AxonHub GraphQL endpoint URL for management operations?

The management **GraphQL API** is available at `/openapi/v1/graphql` relative to your AxonHub base URL (e.g., `http://localhost:8090/openapi/v1/graphql`). This single endpoint handles all administrative queries and mutations, including channel provisioning, user management, and API key rotation, as defined in the schema file [`internal/server/gql/axonhub.graphql`](https://github.com/looplj/axonhub/blob/unstable/internal/server/gql/axonhub.graphql).

### How do I authenticate requests to the GraphQL API?

All management calls require a valid **JWT** token. First, authenticate via the REST endpoint `POST /auth/signin` (implemented in [[`internal/server/api/auth.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/auth.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/api/auth.go)) with your email and password. The response returns a `token` string that you must include in the `Authorization: Bearer <token>` header on every GraphQL request. The Gin router enforces this protection in [[`internal/server/routes.go`](https://github.com/looplj/axonhub/blob/main/internal/server/routes.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/routes.go).

### Can I use a type-safe client instead of raw HTTP requests?

Yes. AxonHub's **GraphQL schema** is compatible with **genqlient**, a Go code generator for GraphQL. By running `genqlient generate` against `internal/server/gql/axonhub.graphql`, you produce a type-safe client that validates queries at compile time. This eliminates manual JSON construction and reduces runtime errors when calling management operations like `createChannel` or `disableChannelAPIKey`.

### Where are the GraphQL resolvers for management operations implemented?

The resolver functions that bridge the **GraphQL API** to the business layer are located in [[`internal/server/gql/axonhub.resolvers.go`](https://github.com/looplj/axonhub/blob/main/internal/server/gql/axonhub.resolvers.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/gql/axonhub.resolvers.go). Each mutation (such as `createChannel`, `disableChannelAPIKey`, or `createUser`) has a corresponding resolver method that validates inputs, invokes the appropriate service in `internal/server/biz/`, and returns the result to the GraphQL layer.