Overview

The Garden.gg API supports three authentication methods:

  1. JWT Tokens — Short-lived access tokens obtained via email/password login, ideal for user-facing applications.
  2. API Keys — Long-lived keys generated in your account settings, ideal for server-to-server integrations and scripts.
  3. Device Tokens — Short opaque strings baked into the URL path, for firmware-constrained IoT devices (Ecowitt gateways etc.) that can’t configure headers or query parameters.

JWT + API Keys use the Authorization header with a Bearer token:

Authorization: Bearer <token>

API Keys can alternatively be passed via the X-API-Key header or ?api_key= query parameter.

JWT Authentication

JWT authentication uses an access token (15-minute expiry) and a refresh token (7-day expiry). The access token is used for API requests, and the refresh token is used to obtain a new access token when it expires.

Register

Create a new account.

POST /auth/register

Request Body

FieldTypeRequiredDescription
emailstringYesValid email address
passwordstringYesMinimum 8 characters
usernamestringYes3-30 chars, alphanumeric + underscore
display_namestringNoDisplay name for profile

Request

{
  "email": "[email protected]",
  "password": "s3cur3p4ssw0rd",
  "username": "green_thumb",
  "display_name": "Green Thumb"
}

Response 201 Created

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 900,
  "user": {
    "id": "usr_a1b2c3d4",
    "email": "[email protected]",
    "username": "green_thumb",
    "display_name": "Green Thumb"
  }
}

Login

Authenticate with email and password to receive tokens.

POST /auth/login

Request Body

FieldTypeRequiredDescription
emailstringYesAccount email
passwordstringYesAccount password

Request

{
  "email": "[email protected]",
  "password": "s3cur3p4ssw0rd"
}

Response 200 OK

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 900
}

Login Examples

cURL

curl -X POST "https://garden.gg/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "s3cur3p4ssw0rd"
  }'

Python

import requests

response = requests.post(
    "https://garden.gg/api/v1/auth/login",
    json={
        "email": "[email protected]",
        "password": "s3cur3p4ssw0rd",
    },
)

tokens = response.json()
access_token = tokens["access_token"]
refresh_token = tokens["refresh_token"]

# Use the access token for subsequent requests
headers = {"Authorization": f"Bearer {access_token}"}
gardens = requests.get("https://garden.gg/api/v1/gardens", headers=headers)
print(gardens.json())

Node.js

// Login and get tokens
const loginResponse = await fetch("https://garden.gg/api/v1/auth/login", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    email: "[email protected]",
    password: "s3cur3p4ssw0rd",
  }),
});

const { access_token, refresh_token } = await loginResponse.json();

// Use the access token for subsequent requests
const gardensResponse = await fetch("https://garden.gg/api/v1/gardens", {
  headers: { Authorization: `Bearer ${access_token}` },
});

const gardens = await gardensResponse.json();
console.log(gardens);

Refresh Token

Exchange a valid refresh token for a new access token. Use this when your access token expires (after 15 minutes) without requiring the user to log in again.

POST /auth/refresh

Request Body

FieldTypeRequiredDescription
refresh_tokenstringYesValid refresh token

Request

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}

Response 200 OK

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 900
}

Both the access token and refresh token are rotated on each refresh. The old refresh token is invalidated immediately.

Token Expiry Summary

TokenLifetimeRefresh Strategy
Access Token15 minutesUse refresh token to get a new one
Refresh Token7 daysRe-authenticate with email/password

API Key Authentication

API keys provide a simpler authentication method for scripts, CI/CD pipelines, and server-side integrations. They do not expire but can be revoked at any time.

Creating API Keys

Generate API keys from your account settings or via the API:

POST /account/api-keys

Request Body

FieldTypeRequiredDescription
namestringYesA label for this key (e.g., “CI”)

Response 201 Created

{
  "id": "key_x9y8z7",
  "name": "CI",
  "key": "gg_live_abc123def456ghi789jkl012mno345",
  "created_at": "2026-03-15T10:00:00Z"
}

The full key value is only returned once at creation time. Store it securely.

Using API Keys

Pass the API key as a Bearer token, the same way you would use a JWT access token:

curl -X GET "https://garden.gg/api/v1/gardens" \
  -H "Authorization: Bearer gg_live_abc123def456ghi789jkl012mno345"

All API keys are prefixed with gg_live_ so they can be easily identified in logs and secret scanners.

Managing API Keys

GET /account/api-keys         — List all API keys
DELETE /account/api-keys/:id  — Revoke an API key

See the Users reference for full details.

Best Practices

  • Never expose tokens in client-side code. Store JWT tokens in secure, HTTP-only cookies or secure storage on mobile devices. Never include them in URLs or local storage on web.
  • Use the refresh flow. Do not store passwords to re-authenticate. Implement proper token refresh logic in your application.
  • Rotate API keys regularly. Create a new key, update your integrations, then revoke the old key.
  • Use API keys for automation. For CI/CD, scripts, and server-to-server calls, API keys are simpler and avoid the overhead of the JWT refresh flow.
  • Monitor key usage. Review your active API keys periodically and revoke any that are no longer in use.
  • Scope keys to purpose. Create separate API keys for different integrations so you can revoke one without affecting others.

Error Responses

ScenarioHTTP StatusError Code
Missing Authorization header401UNAUTHORIZED
Invalid or expired token401UNAUTHORIZED
Invalid credentials (login)401UNAUTHORIZED
Expired refresh token401UNAUTHORIZED
Account suspended403FORBIDDEN

Device Tokens

Device tokens are short opaque strings (ggd_ + 12 base62 chars, 16 chars total) that authenticate a single IoT device. They’re generated when you connect a device from Profile → Sensor Devices in the web app.

Unlike JWT tokens or API keys, device tokens live in the URL path, not in a header or query string:

POST https://garden.gg/i/e/ggd_AbCdEf123456
Content-Type: application/x-www-form-urlencoded

tempf=68.1&humidity=55&tempf1=72.2&humidity1=62&soilmoisture1=37

This shape exists because Ecowitt gateways (and similar firmware) can only configure a single URL field — no custom headers, no extra query params. A device token keeps the full URL under 40 characters so it fits every gateway.

Scope. A device token can only drive the one device it’s bound to. It can’t read data, can’t list plots, and can’t be used with any endpoint other than the ingest path (POST /i/e/:token). The device’s channel map controls which plots the ingested data lands on.

Rotation. Revoke the device (DELETE /api/v1/devices/:id) and create a new one. There’s no in-place rotation.

See IoT & Devices for the full ingest reference.