> ## Documentation Index
> Fetch the complete documentation index at: https://docs.garmint.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> API error codes and handling

# Error Handling

The Garmint API uses standard HTTP status codes and returns consistent JSON error responses.

## Error Response Format

All errors follow this structure:

```json theme={}
{
  "error": {
    "code": "error_code",
    "message": "Human-readable description",
    "details": { ... }
  }
}
```

| Field     | Type   | Description                   |
| --------- | ------ | ----------------------------- |
| `code`    | string | Machine-readable error code   |
| `message` | string | Human-readable description    |
| `details` | object | Additional context (optional) |

## Error Codes

### Authentication Errors

| Code           | Status | Description                          |
| -------------- | ------ | ------------------------------------ |
| `unauthorized` | 401    | Missing or invalid API key           |
| `forbidden`    | 403    | Key lacks permission for this action |

```json theme={}
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key. Provide a valid key via Authorization header."
  }
}
```

### Request Errors

| Code              | Status | Description                          |
| ----------------- | ------ | ------------------------------------ |
| `invalid_request` | 400    | Malformed request body or parameters |
| `not_found`       | 404    | Requested resource doesn't exist     |

```json theme={}
{
  "error": {
    "code": "invalid_request",
    "message": "The request body is invalid.",
    "details": {
      "reason": [
        { "path": ["designUrl"], "message": "Required" }
      ]
    }
  }
}
```

### Rate & Usage Errors

| Code                  | Status | Description                     |
| --------------------- | ------ | ------------------------------- |
| `rate_limited`        | 429    | Too many requests               |
| `insufficient_tokens` | 402    | Not enough tokens for operation |

```json theme={}
{
  "error": {
    "code": "insufficient_tokens",
    "message": "You don't have enough tokens for this operation.",
    "details": {
      "required": 1,
      "available": 0
    }
  }
}
```

### Server Errors

| Code             | Status | Description             |
| ---------------- | ------ | ----------------------- |
| `internal_error` | 500    | Unexpected server error |

```json theme={}
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred. Please try again."
  }
}
```

## HTTP Status Codes

| Status | Meaning                      |
| ------ | ---------------------------- |
| `200`  | Success                      |
| `201`  | Created (new resource)       |
| `400`  | Bad Request                  |
| `401`  | Unauthorized                 |
| `402`  | Payment Required (no tokens) |
| `403`  | Forbidden                    |
| `404`  | Not Found                    |
| `429`  | Rate Limited                 |
| `500`  | Internal Server Error        |

## Handling Errors in Code

### TypeScript

```typescript theme={}
interface APIError {
  error: {
    code: string;
    message: string;
    details?: Record<string, unknown>;
  };
}

async function generateMockup(params: GenerateParams) {
  const response = await fetch('https://garmint.app/api/v1/generate', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(params),
  });

  if (!response.ok) {
    const error: APIError = await response.json();
    
    switch (error.error.code) {
      case 'unauthorized':
        throw new Error('Invalid API key');
      case 'insufficient_tokens':
        throw new Error('Buy more tokens at garmint.app');
      case 'rate_limited':
        const retryAfter = response.headers.get('Retry-After');
        throw new Error(`Rate limited. Retry in ${retryAfter}s`);
      default:
        throw new Error(error.error.message);
    }
  }

  return response.json();
}
```

### Python

```python theme={}
import requests

def generate_mockup(params):
    response = requests.post(
        'https://garmint.app/api/v1/generate',
        headers={'Authorization': f'Bearer {API_KEY}'},
        json=params
    )
    
    if not response.ok:
        error = response.json()['error']
        
        if error['code'] == 'unauthorized':
            raise Exception('Invalid API key')
        elif error['code'] == 'insufficient_tokens':
            raise Exception('Buy more tokens at garmint.app')
        elif error['code'] == 'rate_limited':
            retry_after = response.headers.get('Retry-After', 60)
            raise Exception(f'Rate limited. Retry in {retry_after}s')
        else:
            raise Exception(error['message'])
    
    return response.json()
```

## Debugging Tips

<AccordionGroup>
  <Accordion title="Check request headers">
    Ensure you're sending:

    * `Authorization: Bearer gm_live_xxx`
    * `Content-Type: application/json` for POST requests
  </Accordion>

  <Accordion title="Validate URLs">
    Design and garment URLs must be:

    * Publicly accessible (no auth required)
    * HTTPS preferred
    * Valid image formats (PNG, JPEG, WebP)
  </Accordion>

  <Accordion title="Check your token balance">
    The `X-Tokens-Remaining` header shows your current balance in every response.
  </Accordion>

  <Accordion title="Review rate limit headers">
    Monitor `X-RateLimit-Remaining` to avoid hitting limits.
  </Accordion>
</AccordionGroup>

## Getting Help

If you encounter persistent errors:

1. Check our [status page](https://status.garmint.app)
2. Search [Discord](https://discord.com/invite/twbP9uxm) for similar issues
3. Email [api@garmint.app](mailto:api@garmint.app) with:
   * Your request (redact API key)
   * The error response
   * Timestamp of the request
