docs / api

{ API Reference

REST API documentation for building integrations and custom clients.

_base_url

https://gh-env.sh/api/v1

All endpoints are prefixed with this base URL. For example, the login endpoint is: https://gh-env.sh/api/v1/auth/login

_authentication

Most endpoints require authentication via a JWT Bearer token. Include it in the Authorization header:

Authorization: Bearer <access_token>

Token Lifecycle

Token TypeExpiryUsage
accessToken15 minutesAPI requests (Authorization header)
refreshToken7 daysGet new access tokens via /auth/refresh

_request_format

All requests should include these headers:

Content-Type: application/json
Accept: application/json
# For authenticated endpoints:
Authorization: Bearer <access_token>

Request bodies should be JSON. Example:

POST /api/v1/auth/login
{
  "email": "user@example.com",
  "password": "securepass123"
}

_endpoints/auth

POST /api/v1/auth/register public

Create a new user account

Request Body
{ "email": "user@example.com", "username": "hunter42", "password": "securepass123" }
Response
{ "user": { "id": "...", "email": "...", "username": "..." }, "accessToken": "...", "refreshToken": "..." }
POST /api/v1/auth/login public

Authenticate and get tokens

Request Body
{ "email": "user@example.com", "password": "securepass123" }
Response
{ "accessToken": "...", "refreshToken": "..." }
// or if 2FA enabled:
{ "requires2fa": true, "pendingId": "..." }
POST /api/v1/auth/login/2fa public

Complete 2FA verification

Request Body
{ "pendingId": "...", "code": "123456" }
Response
{ "accessToken": "...", "refreshToken": "..." }
POST /api/v1/auth/refresh public

Refresh access token

Request Body
{ "refreshToken": "..." }
Response
{ "accessToken": "...", "refreshToken": "..." }
POST /api/v1/auth/device public

Start device code flow

Request Body
{ "hostname": "my-laptop" }
Response
{ "deviceCode": "...", "userCode": "XKCD-1337", "verificationUrl": "...", "expiresIn": 600, "interval": 5 }
POST /api/v1/auth/device/poll public

Poll for device authorization

Request Body
{ "deviceCode": "..." }
Response
{ "status": "pending" }
// or when authorized:
{ "status": "authorized", "accessToken": "...", "refreshToken": "..." }

_endpoints/profiles

GET /api/v1/profiles requires auth

List all profiles for authenticated user

Response
{ "profiles": [{ "id": "...", "name": "default", "description": "...", "isDefault": true, "createdAt": "..." }] }
POST /api/v1/profiles requires auth

Create a new profile

Request Body
{ "name": "work", "description": "Work environment", "isDefault": false }
Response
{ "profile": { "id": "...", "name": "work", ... } }
GET /api/v1/profiles/:id requires auth

Get profile details

Response
{ "profile": { "id": "...", "name": "...", "scripts": [...] } }
PUT /api/v1/profiles/:id requires auth

Update a profile

Request Body
{ "name": "...", "description": "...", "isDefault": false }
Response
{ "profile": { ... } }
DELETE /api/v1/profiles/:id requires auth

Delete a profile

Response
{ "success": true }

_endpoints/scripts

GET /api/v1/profiles/:id/scripts requires auth

Get all scripts for a profile

Response
{ "scripts": [{ "id": "...", "type": "aliases", "content": "...", "signature": "..." }] }
PUT /api/v1/profiles/:id/scripts/:type requires auth

Update a script (auto-signed)

Request Body
{ "content": "alias ll=\"ls -la\"" }
Response
{ "script": { "id": "...", "type": "aliases", "content": "...", "signature": "..." } }
GET /api/v1/scripts/activate/:profileId requires auth

Get signed scripts for activation (CLI endpoint)

Response
{ "scripts": [...], "timestamp": "...", "bundleSignature": "..." }

Script Types

Valid values for the :type parameter:

env_variablesaliasesfunctionspromptcompletioninitcleanup

_endpoints/devices

GET /api/v1/devices requires auth

List all registered devices

Response
{ "devices": [{ "id": "...", "hostname": "...", "lastSeen": "...", "mode": "normal" }] }
DELETE /api/v1/devices/:id requires auth

Revoke device access

Response
{ "success": true }

_error_codes

Error responses follow this format:

{
  "error": "Error message here",
  "code": "ERROR_CODE"
}
StatusCodeDescription
400INVALID_REQUESTMissing or invalid parameters
401UNAUTHORIZEDMissing or invalid token
401TOKEN_EXPIREDAccess token has expired
403FORBIDDENNot authorized for this resource
404NOT_FOUNDResource doesn't exist
409CONFLICTResource already exists (duplicate)
429RATE_LIMITEDToo many requests
500INTERNAL_ERRORServer error

_rate_limits

Endpoint GroupLimitWindow
Global (all endpoints)100 requestsper minute per IP
Auth endpoints (/auth/*)10 requestsper minute per IP

Rate limit headers are included in responses:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067200