thfg vs thfg api vs curl: when to use each
Use this guide to choose the right level of control for a task.
Start with the contract shape
Section titled “Start with the contract shape”The default HTTP contract for this ecosystem is:
- Forgejo Proxy is the canonical API surface
- API families live under
/api/v1/... /healthstays rooted- ActivityPub keeps rooted public federation URLs, while the runtime also accepts
/api/v1/activitypub/...aliases for API clients
That means:
- use
thfg <command>first - use
thfg api /...for anything under/api/v1, including canonical/activitypub/...aliases when you want raw payloads - use
thfg activitypub ...for ergonomic ActivityPub access - use
curlfor/health, streaming/multipart work, or when you need the rooted public/activitypub/**URL itself
If you are migrating older scripts, read Migrate to the normalized /api/v1 API surface.
Quick decision table
Section titled “Quick decision table”| Approach | Use it when | Example |
|---|---|---|
thfg <command> | A dedicated CLI command already exists | thfg repo view myorg/myrepo |
thfg api | You need an /api/v1 endpoint that has no dedicated command yet | thfg api /repos/myorg/myrepo/topics |
curl | You need streaming, multipart uploads, raw status handling, or a root-path exception | curl "$PROXY_URL/health" |
1. Use thfg <command> for normal work
Section titled “1. Use thfg <command> for normal work”Prefer a dedicated command whenever one exists. It handles host resolution, authentication, output formatting, and repo detection for you.
# Repositoriesthfg repo listthfg repo view myorg/myrepo
# Issues and pull requeststhfg issue list --repo myorg/myrepothfg pr merge 7 --repo myorg/myrepo
# Actionsthfg run list --repo myorg/myrepothfg run logs --repo myorg/myrepo --job failed
# Projectsthfg project list --org myorgthfg project cards --id 2 --column 7Use thfg --help or thfg <command> --help to discover the current command surface.
2. Use thfg api for normalized /api/v1 escape-hatch calls
Section titled “2. Use thfg api for normalized /api/v1 escape-hatch calls”thfg api is the generic client for the proxy’s normalized API surface.
- The positional path must start with
/ - The path is joined onto the active
/api/v1base URL - The output is raw JSON
- Canonical ActivityPub aliases such as
/activitypub/actorare available throughthfg api
# GET (default)thfg api /repos/myorg/myrepo/topics
# Paginate an array endpointthfg api /user/repos --paginate
# PATCH with JSON body fieldsthfg api /repos/myorg/myrepo \ --method PATCH \ --field description="Updated description"
# POST from a file or stdinthfg api /repos/myorg/myrepo/issues \ --method POST \ --input issue.json
# Add custom headersthfg api /repos/myorg/myrepo \ --header "X-Forgejo-Host: https://codeberg.org"
# View the merged proxy specthfg api specUse thfg api when
Section titled “Use thfg api when”- the endpoint is part of
/api/v1but not wrapped by a CLI command yet - you want to reuse the active host, token, and proxy configuration
- you want a simple JSON response that you can pipe to
jq
Do not use thfg api when
Section titled “Do not use thfg api when”- you need log streaming or another long-lived response
- you need multipart uploads
- you need raw response codes or custom retry behaviour
- you need
/health - you specifically need the rooted public
/activitypub/**URL instead of the canonical alias
For ActivityPub resources, use the dedicated commands instead:
thfg activitypub instance actorthfg activitypub user outbox @methfg activitypub repo actor myorg/myrepo3. Use curl when you need raw control
Section titled “3. Use curl when you need raw control”Use curl directly when the CLI abstraction gets in the way.
TOKEN="$FORGEJO_TOKEN"PROXY_URL="https://forgejo-proxy.hochguertel.work"REPO="myorg/myrepo"
# Raw GET against the canonical proxy API surfacecurl -s -H "Authorization: token $TOKEN" \ "$PROXY_URL/api/v1/repos/$REPO/topics" | jq .
# Streaming logs still belong in curlcurl -N -H "Authorization: token $TOKEN" \ "$PROXY_URL/api/v1/repos/$REPO/actions/runs/latest/logs?job=failed"
# Root exception: healthcurl -s "$PROXY_URL/health"When to compare with direct Forgejo
Section titled “When to compare with direct Forgejo”Direct Forgejo REST is now mainly a debugging comparison tool. Reach for it when you need to answer questions like:
- “Is this failure in the proxy layer or upstream Forgejo?”
- “Does Forgejo return the same payload the proxy is forwarding?”
- “Is this endpoint truly proxy-only, or is it native upstream?”
BASE_URL="https://pastoral-oyster.pikapod.net"
# Compare upstream Forgejo directlycurl -s -H "Authorization: token $TOKEN" \ "$BASE_URL/api/v1/repos/$REPO/topics" | jq .Common patterns
Section titled “Common patterns”I need a project write endpoint
Section titled “I need a project write endpoint”Use the proxy contract under /api/v1/projects/..., not the old /api/v1/proxy/... paths.
curl -s -X POST \ -H "Authorization: token $TOKEN" \ -H "X-Webui-Username: $FORGEJO_WEBUI_USERNAME" \ -H "X-Webui-Password: $FORGEJO_WEBUI_PASSWORD" \ -H "Content-Type: application/json" \ -d '{"issue_id":7,"owner":"myorg","repo":"myrepo"}' \ "$PROXY_URL/api/v1/projects/2/columns/7/cards"I need a root ActivityPub resource
Section titled “I need a root ActivityPub resource”Use the dedicated CLI commands for normal work. Drop to the rooted path only when you need the published federation URL itself.
thfg activitypub instance actor# raw canonical alias through thfg apithfg api /activitypub/actor# orcurl -s -H "Authorization: token $TOKEN" \ "$PROXY_URL/activitypub/actor"Quick reference
Section titled “Quick reference”| Need | Recommended tool |
|---|---|
| Standard repo/issue/PR/project workflow | thfg <command> |
Missing /api/v1 endpoint | thfg api |
/health | curl |
| Canonical ActivityPub API alias | thfg activitypub or thfg api /activitypub/... |
Rooted public /activitypub/** URL | curl (or thfg activitypub for equivalent data access) |
| Streaming or multipart | curl |
| Compare proxy vs upstream Forgejo | curl against both hosts |