# Cuenta

> Endpoints GET /account y GET /account/credits/ledger de la API de Blogenia: plan, créditos, uso del mes, límites, blogs y movimientos de créditos.

Versión web: https://blogelia.com/docs/cuenta

Datos de tu organización: plan, créditos, uso del mes, límites y blogs. Útiles para pintar el resumen de tu propio panel.

> **Aviso:** Estas rutas exigen una clave con el permiso `account:read`, que da acceso a datos internos de tu cuenta. Úsalas solo desde tu servidor. Ver [Permisos](https://blogelia.com/docs/autenticacion#scopes).

## Obtener la cuenta

**`GET /account`**: Devuelve la organización de la clave, con créditos, uso, límites y blogs.

Permiso necesario: `account:read`. No tiene parámetros.

**Campos de la respuesta**

| Campo | Tipo | Descripción |
| --- | --- | --- |
| `organization` | object | `{ id, name, plan, createdAt }`. `plan` es `free`, `starter` o `pro`. |
| `credits` | object | `{ balance, buckets }`. `balance` son los créditos disponibles (lotes sin caducar menos las reservas en curso). `buckets` son los lotes: `[{ period, remaining, expiresAt }]`, con `period` en `YYYY-MM`. |
| `usage` | object | Uso del mes natural en Europe/Madrid: `{ period, articlesPublished, articlesGenerated, generationsFailed }`. |
| `limits` | object | `{ blogs, apiRatePerMin, autopilotPerWeek }`: blogs permitidos, peticiones por minuto de la API y artículos por semana del piloto automático. |
| `blogs` | array | Blogs de la organización (hasta 100), del más antiguo al más reciente: `[{ id, name, slug, publicUrl, status, createdAt }]`. `status` es `active` o `suspended`. |

**Petición**

*curl*

```bash
curl "https://api.blogelia.com/v1/account" \
  -H "Authorization: Bearer $BLOGENIA_API_KEY"
```

*Node.js*

```js
const res = await fetch('https://api.blogelia.com/v1/account', {
  headers: { Authorization: 'Bearer ' + process.env.BLOGENIA_API_KEY },
});
if (!res.ok) throw new Error('HTTP ' + res.status);
const body = await res.json();
console.log(body);
```

*Python*

```python
import os
import requests

res = requests.get(
    "https://api.blogelia.com/v1/account",
    headers={"Authorization": "Bearer " + os.environ["BLOGENIA_API_KEY"]},
    timeout=10,
)
res.raise_for_status()
print(res.json())
```

**Respuesta**

```json
{
  "organization": {
    "id": "66f1c0a2b3c4d5e6f7a8b900",
    "name": "Reformas Ejemplo S.L.",
    "plan": "starter",
    "createdAt": "2026-08-12T10:15:00.000Z"
  },
  "credits": {
    "balance": 940,
    "buckets": [
      { "period": "2026-10", "remaining": 120, "expiresAt": "2026-12-01T00:00:00.000Z" },
      { "period": "2026-11", "remaining": 820, "expiresAt": "2027-01-01T00:00:00.000Z" }
    ]
  },
  "usage": {
    "period": "2026-11",
    "articlesPublished": 4,
    "articlesGenerated": 6,
    "generationsFailed": 1
  },
  "limits": { "blogs": 1, "apiRatePerMin": 60, "autopilotPerWeek": 3 },
  "blogs": [
    {
      "id": "66f1c0a2b3c4d5e6f7a8b9c0",
      "name": "Reformas Ejemplo",
      "slug": "reformas-ejemplo",
      "publicUrl": "https://reformas-ejemplo.blogelia.com",
      "status": "active",
      "createdAt": "2026-08-12T10:20:00.000Z"
    }
  ]
}
```

**Respuestas**

| Código | Cuándo |
| --- | --- |
| `200` | Respuesta devuelta. |
| `401` | Clave ausente, no válida o revocada. |
| `403` | La clave no tiene el permiso `account:read` (`code: "insufficient_scope"`), tu plan es Free (`plan_required`) o la organización está suspendida (`ORGANIZATION_SUSPENDED`). |
| `429` | Límite del plan superado. Ver [Límites de uso](https://blogelia.com/docs/limites). |

## Movimientos de créditos

**`GET /account/credits/ledger`**: Lista los movimientos de créditos, del más reciente al más antiguo.

Permiso necesario: `account:read`. Es la misma información que el historial de créditos del panel.

**Parámetros de consulta**

| Nombre | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `page` | integer | No | Página, desde 1 (por defecto 1, máximo 10000). |
| `limit` | integer | No | Elementos por página, de 1 a 50 (por defecto 20). |
| `from` | string | No | Primer día, `YYYY-MM-DD` en Europe/Madrid, incluido. Sin `from` ni `to`, se devuelve todo el historial. |
| `to` | string | No | Último día, `YYYY-MM-DD` en Europe/Madrid, incluido. Los dos son opcionales e independientes: con solo `from`, hasta hoy; con solo `to`, desde el principio. Sin límite de días; `from` no puede ser posterior a `to`. |

**Campos de la respuesta**

| Campo | Tipo | Descripción |
| --- | --- | --- |
| `data` | array | Elementos de la página. |
| `page` | integer | Página devuelta. |
| `limit` | integer | Tamaño de página aplicado. |
| `total` | integer | Total de elementos que cumplen el filtro, en todas las páginas. |
| `data[].at` | string | Fecha del movimiento, ISO 8601 en UTC. |
| `data[].type` | string | `grant` (abono del plan), `reserve` (reserva al empezar una generación), `settle` (cobro real), `release` (devolución de una reserva), `expire` (caducidad), `refund` o `admin` (ajuste manual). |
| `data[].amount` | integer | Créditos con signo: negativo es gasto. |
| `data[].ref` | string | Referencia interna del movimiento. Puede faltar. |
| `data[].articleId` | string | Artículo asociado, si se conoce. Puede faltar. |
| `data[].generationId` | string | Generación asociada. Puede faltar. |

> **Nota:** `reserve` y `release` son movimientos internos de la reserva de una generación: para saber lo que has gastado, cuenta los `settle`.

**Petición**

*curl*

```bash
curl "https://api.blogelia.com/v1/account/credits/ledger?from=2026-11-01&limit=10" \
  -H "Authorization: Bearer $BLOGENIA_API_KEY"
```

*Node.js*

```js
const res = await fetch('https://api.blogelia.com/v1/account/credits/ledger?from=2026-11-01&limit=10', {
  headers: { Authorization: 'Bearer ' + process.env.BLOGENIA_API_KEY },
});
if (!res.ok) throw new Error('HTTP ' + res.status);
const body = await res.json();
console.log(body);
```

*Python*

```python
import os
import requests

res = requests.get(
    "https://api.blogelia.com/v1/account/credits/ledger",
    params={"from": "2026-11-01", "limit": 10},
    headers={"Authorization": "Bearer " + os.environ["BLOGENIA_API_KEY"]},
    timeout=10,
)
res.raise_for_status()
print(res.json())
```

**Respuesta**

```json
{
  "data": [
    {
      "at": "2026-11-03T09:00:41.000Z",
      "type": "settle",
      "amount": -8,
      "ref": "gen-66f1c0a2b3c4d5e6f7a8b9e0",
      "articleId": "66f1c0a2b3c4d5e6f7a8b9c1",
      "generationId": "66f1c0a2b3c4d5e6f7a8b9e0"
    },
    {
      "at": "2026-11-01T00:00:00.000Z",
      "type": "grant",
      "amount": 1099
    }
  ],
  "page": 1,
  "limit": 10,
  "total": 2
}
```

**Respuestas**

| Código | Cuándo |
| --- | --- |
| `200` | Respuesta devuelta. |
| `400` | Fechas o paginación no válidas (`invalid_date`, `invalid_range`, `range_in_future`, `range_too_long`) o parámetro desconocido. |
| `401` | Clave ausente, no válida o revocada. |
| `403` | La clave no tiene el permiso `account:read` (`code: "insufficient_scope"`), tu plan es Free (`plan_required`) o la organización está suspendida (`ORGANIZATION_SUSPENDED`). |
| `429` | Límite del plan superado. Ver [Límites de uso](https://blogelia.com/docs/limites). |
