# Usage

Part of AppEngine API Documentation. Full index: https://appengine.appmint.io/documentation.md
## GET /usage/shared

**Get shared services used by this org**

`operationId: UsageController_getSharedUsage`

Every shared platform service: whether this org runs it on the shared account or its own (`account`), whether its agreement is accepted, and what it was charged in the window (default: this month). Our cost and margin are not included.

#### Signature

```http
GET /usage/shared (from?: string, to?: string) -> Shared service usage
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /usage/statement`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `from` | query | string | — | Start (ISO date). Default: start of this month. |
| `to` | query | string | — | End (ISO date). Default: now. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Shared service usage |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /usage/endpoints

**List tokenized endpoints**

`operationId: UsageController_getTokenizedEndpoints`

Every endpoint that consumes tokens, with its cost. The table behind what a call charges an org.

#### Signature

```http
GET /usage/endpoints () -> Endpoints and their costs
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /usage/endpoints`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Endpoints and their costs |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /usage/endpoints

**Set an endpoint cost**

`operationId: UsageController_setEndpointCost`

Sets the token cost for an endpoint. Affects pricing for every organization, not just the caller. A cost set too high starts refusing calls for orgs with small balances; too low, and usage stops being paid for.

#### Signature

```http
POST /usage/endpoints (body) -> Confirmation
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Affects pricing for every organization, not just the caller.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `DELETE /usage/endpoints/{endpoint}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The endpoint and its cost.

```json
{
  "endpoint": "POST /ai/chat",
  "cost": 10
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | Confirmation |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

Example response:

```json
{
  "success": true,
  "message": "Cost for POST /ai/chat set to 10 tokens"
}
```

## DELETE /usage/endpoints/{endpoint}

**Remove an endpoint from tokenization**

`operationId: UsageController_removeEndpoint`

Stops charging for an endpoint — it becomes free for every organization. Affects pricing for every organization, not just the caller.

#### Signature

```http
DELETE /usage/endpoints/{endpoint} (endpoint: string) -> The result
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Affects pricing for every organization, not just the caller.
- Makes the endpoint free platform-wide.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /usage/endpoints`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `endpoint` | path | string | yes | The endpoint identifier. |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The result |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /usage/stats

**Get usage records**

`operationId: UsageController_getUsageStats`

The org's usage records (`usage`), optionally within a date range on `data.timestamp`, in the repository's paged list shape. `all=true` drops the org scope.

#### Signature

```http
GET /usage/stats (startDate?: string, endDate?: string, all?: boolean) -> Paged usage records
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- `all=true` is not restricted to operators: any signed-in caller can pass it.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /usage/statement`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `startDate` | query | string | — | ISO date. |
| `endDate` | query | string | — |  |
| `all` | query | boolean | — | Read without the org scope. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Paged usage records |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /usage/statement

**Get the organization statement**

`operationId: UsageController_getStatement`

Every change to the balance in the window (default: the last 30 days), newest first and labelled with what each charge was for. Back-to-back charges for the same service are one row. `breakdown` totals spending by service. Amounts are full precision.

#### Signature

```http
GET /usage/statement (from?: string, to?: string) -> The statement
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | — | from/to must be ISO dates | `from` or `to` does not parse. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /usage/balance`
- `GET /usage/shared`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `from` | query | string | — | Start (ISO date). |
| `to` | query | string | — | End (ISO date). Default: now. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The statement |
| `400` | from/to must be ISO dates — `from` or `to` does not parse. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /usage/balance

**Get the organization balance**

`operationId: UsageController_getBalance`

The org's credit balance (dollars), its spend rate (the multiplier applied to every charge), plan, whether it is active, and any promotion it qualifies for. Check it before a bulk AI job — a run that exhausts the balance part-way leaves the work half done.

#### Signature

```http
GET /usage/balance () -> The balance
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Without the `orgid` header it answers `{ success: false, error: 'Missing orgid header' }` with status 200.
- The response includes the full company record.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /usage/statement`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The balance |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

Example response:

```json
{
  "balance": 42.18,
  "spendRate": 1,
  "plan": "pro",
  "active": true,
  "orgId": "org_4821",
  "promotions": []
}
```

## GET /usage/ai/provider-key

**Get an AI provider key and balance**

`operationId: UsageController_getAIProviderKey`

Returns the API key for an AI provider along with the org's balance, so a client can call the provider directly.

**This hands out a provider credential.** Anything holding it can spend against that provider outside this platform's accounting — treat the response as a secret and prefer the server-side `/ai/*` endpoints where possible.

#### Signature

```http
GET /usage/ai/provider-key (provider?: string) -> { success, orgId, provider, apiKey, balance, spendRate, plan, active }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Returns a live provider credential.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /usage/ai/charge`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `provider` | query | string | yes | openai, anthropic, gemini, deepseek… |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { success, orgId, provider, apiKey, balance, spendRate, plan, active } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /usage/ai/models

**List AI models and pricing**

`operationId: UsageController_getSupportedModels`

Supported models and what each costs — read this to work out what a job will cost before running it.

#### Signature

```http
GET /usage/ai/models () -> Models and pricing
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /usage/ai/charge`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Models and pricing |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /usage/ai/charge

**Charge AI usage**

`operationId: UsageController_chargeAiUsage`

Records AI consumption and **deducts it from the org's balance**. Token models are charged on `promptTokens` and `completionTokens`; image models on `imageCount` and `imageQuality` tiers. The org's spend rate is applied on top.

Call it **after** a successful provider call, not before — charging for a call that failed bills the customer for nothing. It is also not idempotent: calling it twice for one completion charges twice.

#### Signature

```http
POST /usage/ai/charge (body) -> The charge result
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Deducts real balance. Not idempotent.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /usage/balance`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

What was consumed.

```json
{
  "provider": "openai",
  "model": "gpt-5",
  "promptTokens": 1200,
  "completionTokens": 800
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The charge result |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /usage/gift

**Gift credit to another organization**

`operationId: UsageController_giftCredit`

Moves `amount` of the calling org's balance to `toOrg`: the caller is debited, the recipient credited, and a `gift_deduction` transaction is recorded on the caller. Nothing is created from nothing — the caller must hold the amount.

#### Signature

```http
POST /usage/gift (body) -> { success, message }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- A refused gift (amount ≤ 0, same org, unknown org, insufficient balance) is not an error: it answers `success: false`.
- `notes` is recorded as who gifted it (`giftedBy`), and the transaction's own notes stay empty.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /usage/balance`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The gift.

```json
{
  "toOrg": "org_4821",
  "amount": 20,
  "notes": "Trial extension"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | { success, message } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /usage/{orgId}/current/{type}

**Get an organization's usage counts**

`operationId: UsageController_getCurrentUsage`

Usage counts for a named org over the last six months, optionally for one usage type — the same data as usage history, despite the name. The path org is explicit, so a caller with content read permission can read any org.

#### Signature

```http
GET /usage/{orgId}/current/{type} (orgId: string, type: string) -> { orgId, type, usage: [{ year, month, count }] }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /usage/history/{type}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgId` | path | string | yes | Org to read. |
| `type` | path | string | yes | Usage type. Optional. |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { orgId, type, usage: [{ year, month, count }] } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /usage/history/{type}

**Get usage history**

`operationId: UsageController_getUsageHistory`

Usage counts for each of the last `months` months, optionally for one usage type. Requires content read permission.

#### Signature

```http
GET /usage/history/{type} (type: string, months?: integer) -> { orgId, type, history: [{ year, month, count }] }
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- The route reads the org from a path parameter it does not declare, so `orgId` is empty and the counts are not scoped as intended; every month also reports the same all-time count.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /usage/statement`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgId` | path | string | yes |  |
| `type` | path | string | yes | Usage type. Optional. |
| `months` | query | integer | — |  |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | { orgId, type, history: [{ year, month, count }] } |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

