# Recomienda UTILIA OS

Consulta desde tu aplicación el enlace y el código para recomendar UTILIA OS, las empresas recomendadas, el descuento por recomendación y quién recomendó a tu empresa, con /external/v1/referrals y el permiso org:referrals:read.

Con `GET /external/v1/referrals` tu aplicación lee lo mismo que el bloque
**Recomienda UTILIA OS** de **Ajustes › Mi plan**: el enlace
y el código con los que tu empresa recomienda UTILIA OS, las empresas que se
dieron de alta con ellos, el descuento por recomendación que habéis ganado y
quién recomendó a tu empresa. Sirve, por ejemplo, para enseñar el enlace en
tu propia web o para llevar la cuenta del descuento en tu panel. Solo lee:
nada de la cuenta cambia, salvo que la primera llamada crea el código si aún
no existía, igual que la pantalla.

Nunca trae la invitación al Programa de colaboradores: esa la abre en
Mi plan la persona a la que se le ofreció.

## Autenticación, plan y permisos

Todas las peticiones llevan la clave de API de la aplicación:

```
X-Api-Key: <api_key_de_la_app>
```

Hacen falta las mismas puertas que en la pantalla, más las de la API:

- El plan de tu organización tiene que incluir el aplicativo **Integraciones**, como el resto de la API externa.
- La aplicación tiene que ser de una **empresa cliente** de UTILIA OS. Con la clave de una aplicación de una cuenta de desarrollador, de colaborador o de una empresa de prueba, la respuesta es `403 COMPANY_ACCOUNT_REQUIRED`.
- La aplicación necesita el permiso de la API `org:referrals:read`, que solo se ofrece a las empresas cliente. Sin él, `403 INSUFFICIENT_SCOPE` con el permiso que falta en `requiredScopes`; lo concede un administrador de la organización. Se exige desde la primera llamada, sin periodo de gracia.
- La **persona responsable** de la aplicación tiene que poder ver Mi plan, como cualquiera que abre ese bloque. Si no puede, `403 APP_RESPONSIBLE_PERMISSION_DENIED`; sin responsable activo, `403 APP_RESPONSIBLE_INACTIVE`.

| Endpoint | Permiso de la API (scope) |
| - | - |
| `GET /external/v1/referrals` | `org:referrals:read` |

El catálogo completo está en [Permisos de la API](https://os.utilia.ai/docs/developers/api/scopes).

## Endpoint

**`GET /api/external/v1/referrals`**

Recomienda UTILIA OS de tu empresa: enlace y código, empresas recomendadas, descuento por recomendación y quién os recomendó. Permiso: org:referrals:read.

No admite parámetros. Cada aplicación puede llamarlo hasta **30 veces por
minuto**; por encima, `429 RATE_LIMIT_EXCEEDED` con la cabecera
`Retry-After`.

## Respuesta

| Nombre | Tipo | Obligatorio | Descripción |
| - | - | - | - |
| `programAvailable` | `boolean` | Sí | false mientras los Términos de uso que regulan la recomendación no estén publicados. Entonces no hay código ni enlace y no se concede ningún descuento. |
| `available` | `boolean` | Sí | Si tu empresa puede recomendar ahora. También es false en cortesía, en una cuenta interna o con la baja hecha. |
| `code` | `string \| null` | Sí | El código de recomendación de tu empresa, o null si no está disponible. |
| `url` | `string \| null` | Sí | El enlace para compartir, que ya lleva el código, o null si no está disponible. |
| `credits` | `object` | Sí | El descuento por recomendación: grantedCents (concedido en total), usedCents (ya descontado en las cuotas), balanceCents (lo que queda por descontar) e items (cada descuento). Céntimos de euro, sin impuestos. |
| `credits.items[]` | `array` | Sí | companyName (la empresa que dio el descuento, o «Descuento de cortesía de UTILIA OS»), status (CREDIT_GRANTED, PENDING o NOT_ELIGIBLE), amountCents, remainingCents y grantedAt (ISO 8601 o null). |
| `referred[]` | `array` | Sí | Empresas que se dieron de alta con tu código, de la más reciente a la más antigua (hasta 200): companyName, status, statusLabel (el rótulo de Mi plan) y signedUpAt (ISO 8601). |
| `referredBy` | `object \| null` | Sí | Quién recomendó a tu empresa: kind (PARTNER, un colaborador del Programa de colaboradores, o CUSTOMER, otra empresa cliente) y displayName. null si nadie. |

El estado de cada empresa recomendada es el de la pantalla y nunca dice el
motivo de una que no genera descuento:

| `status` | `statusLabel` | Qué significa |
| - | - | - |
| `TRIAL` | En prueba | Está probando UTILIA OS. |
| `PAID` | Ha empezado a pagar | Ya paga su plan; el descuento llega cuando cumpla las condiciones de los Términos de uso. |
| `CREDIT_GRANTED` | Descuento concedido | Ya te ha dado el descuento por recomendación. |
| `PENDING` | Prueba terminada | Terminó la prueba sin llegar a pagar. |
| `NOT_ELIGIBLE` | No genera descuento | No da descuento, sin el motivo. |

El nombre de cada campo conserva la palabra _credit_ por compatibilidad;
lo que contiene es el **descuento por recomendación** que se aplica en tus
próximas cuotas de UTILIA OS, antes de impuestos.

## Ejemplo de respuesta

```json
{
  "programAvailable": true,
  "available": true,
  "code": "TALLERES-7K2Q",
  "url": "https://os.utilia.ai/r/TALLERES-7K2Q",
  "credits": {
    "grantedCents": 4900,
    "usedCents": 0,
    "balanceCents": 4900,
    "items": [
      {
        "companyName": "Asesoría Ruiz",
        "status": "CREDIT_GRANTED",
        "amountCents": 4900,
        "remainingCents": 4900,
        "grantedAt": "2026-10-01T08:00:00.000Z"
      }
    ]
  },
  "referred": [
    {
      "companyName": "Asesoría Ruiz",
      "status": "CREDIT_GRANTED",
      "statusLabel": "Descuento concedido",
      "signedUpAt": "2026-07-01T10:00:00.000Z"
    },
    {
      "companyName": "Panadería Sol",
      "status": "TRIAL",
      "statusLabel": "En prueba",
      "signedUpAt": "2026-10-20T10:00:00.000Z"
    }
  ],
  "referredBy": null
}
```

## Ejemplos

### curl

```bash
curl -X GET 'https://os.utilia.ai/api/external/v1/referrals' \
  -H 'X-Api-Key: utl_xxx' \
  -H 'x-request-id: req-demo-001'
```

### SDK JavaScript

```typescript
const recomienda = await sdk.referrals.get();
if (recomienda.available) {
  console.log(recomienda.url, recomienda.credits.balanceCents);
}
```

### SDK Python

```python
recomienda = await sdk.referrals.get()
if recomienda.available:
    print(recomienda.url, recomienda.credits.balance_cents)
```

## Errores frecuentes

- `401`: falta la clave de API, no es válida o no es de una aplicación externa.
- `403 INSUFFICIENT_SCOPE`: falta el permiso `org:referrals:read`.
- `403 COMPANY_ACCOUNT_REQUIRED`: la clave no es de una empresa cliente de UTILIA OS. Ver [el apartado del error](https://os.utilia.ai/docs/developers/getting-started/errors-and-retries#company_account_required).
- `403 APP_RESPONSIBLE_PERMISSION_DENIED`: la persona responsable de la aplicación no puede ver Mi plan.
- `403 APP_RESPONSIBLE_INACTIVE`: la aplicación no tiene una persona responsable activa.
- `403 MODULE_NOT_ENABLED`: el plan no incluye Integraciones.
- `429 RATE_LIMIT_EXCEEDED`: más de 30 llamadas por minuto de la aplicación, o el límite por hora de la clave. La respuesta trae la cabecera `Retry-After`.

Envía `x-request-id` en cada petición para poder seguirla en los registros.
Con los Términos de la recomendación sin publicar no hay error: la respuesta
llega con `programAvailable: false`.
