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

# Autorización

> Controla el acceso a los recursos LLM con autorización basada en roles. Asigna roles de propietario, escritor o lector a los usuarios y verifica los permisos antes de cada acción.

La API de MKA1 proporciona control de acceso basado en roles (RBAC) a nivel de recurso a través de los endpoints de autorización.
Utiliza estos endpoints para otorgar, comprobar y revocar permisos sobre recursos LLM para usuarios específicos.

<Warning>
  Cada endpoint de esta guía está protegido por ámbitos de API key dedicados: `write:fine-grained-authorization` (otorgar, revocar) y `read:fine-grained-authorization` (comprobar).
  Las solicitudes realizadas con una key que carece de ellos fallan con `403 Forbidden` independientemente de la propiedad del recurso.
  Estos son **ámbitos de administrador**: solo los administradores de la organización pueden añadirlos a una API key, por lo que, si no eres administrador, necesitarás una key emitida por uno para completar esta guía.
  Consulta [Ámbitos requeridos de la API key](#ámbitos-requeridos-de-la-api-key) para saber cómo habilitarlos.
</Warning>

## Ámbitos requeridos de la API key

Las API keys llevan un conjunto fijo de ámbitos, y los ámbitos de autorización granular no forman parte de cada key de forma predeterminada:

| Ámbito                             | Requerido para                                                                                                                       |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `write:fine-grained-authorization` | [Otorgar un rol](#otorgar-un-rol-a-un-usuario), [revocar un rol](#revocar-un-rol), [otorgar acceso público](#otorgar-acceso-público) |
| `read:fine-grained-authorization`  | [Comprobar el permiso de un usuario](#comprobar-el-permiso-de-un-usuario)                                                            |

Ambos son **ámbitos de administrador** — solo los administradores de la organización pueden habilitarlos en una API key.
Si no eres administrador, las casillas no están disponibles para ti: pide a un administrador que te emita una key con estos ámbitos, o que los añada a tu key existente.

Si a tu key le falta un ámbito, la API rechaza la solicitud antes de que se realice cualquier comprobación de propiedad:

```json theme={null}
{
  "code": "FORBIDDEN",
  "status": 403,
  "message": "API key is missing required scope(s): write:fine-grained-authorization",
  "data": {
    "missingScopes": ["write:fine-grained-authorization"],
    "requiredScopes": ["write:fine-grained-authorization"]
  }
}
```

### Habilitar los ámbitos en la consola de la plataforma

Como administrador de la organización, puedes crear una key con estos ámbitos, o añadirlos a una key existente, en [platform.mka1.com/admin/api-keys](https://platform.mka1.com/admin/api-keys):

1. En la consola, ve a **Access → API Keys**.
2. Crea una nueva key, o abre una key existente para editarla.
3. En la lista de ámbitos, busca la fila **Fine-grained authorization** y habilita ambas casillas (lectura y escritura).
4. Haz clic en **Save**.

<img src="https://mintcdn.com/meetkaiinc/QhBs-l8jT0P0xIwA/images/authorization/fine-grained-authorization-scopes.png?fit=max&auto=format&n=QhBs-l8jT0P0xIwA&q=85&s=907cacb2e01f4521875286c418445556" alt="El editor de ámbitos de API key con ambas casillas de Fine-grained authorization habilitadas y resaltadas" width="713" height="915" data-path="images/authorization/fine-grained-authorization-scopes.png" />

Para más información sobre la creación de API keys y cómo funcionan los ámbitos en general, consulta [la guía de introducción a la plataforma](/es/docs/platform-getting-started).

## Cómo funciona la autorización de recursos

Cada recurso LLM (completado, archivo, almacén vectorial, conversación, respuesta o habilidad) puede tener roles asignados por usuario.

Tres roles forman una jerarquía estricta:

| Rol        | Puede hacer                                                            |
| ---------- | ---------------------------------------------------------------------- |
| **owner**  | Otorgar y revocar roles, además de todo lo que puede hacer un escritor |
| **writer** | Modificar el recurso, además de todo lo que puede hacer un lector      |
| **reader** | Leer el recurso                                                        |

Los IDs de recursos se crean cuando realizas llamadas a la API de LLM.
Por ejemplo, crear una conversación devuelve un ID `conv_`, crear una respuesta devuelve un ID `resp_`, y subir un archivo devuelve un ID `file_`.
El llamador autenticado (o el usuario final especificado mediante `X-On-Behalf-Of`) se convierte automáticamente en el **propietario** de ese recurso.

Utiliza el ID de recurso devuelto con los endpoints de autorización a continuación para gestionar el acceso de otros usuarios.
Solo los propietarios pueden otorgar o revocar roles.
Si un no propietario intenta otorgar o revocar, la API devuelve `403 Forbidden`.

Estas autorizaciones son aplicadas por el backend de MKA1 en cada solicitud.
Cualquier intento de leer, modificar o eliminar un recurso al que el llamador no tenga acceso será rechazado con una respuesta de error adecuada.

También puedes otorgar acceso público usando `"*"` como el ID de usuario, pero solo para los roles de `writer` o `reader`.

## Configuración: crear un recurso como Alice

Los ejemplos de esta guía se basan unos en otros y utilizan dos usuarios finales:

* **Alice** (`user_alice`) crea una conversación, lo que la convierte en su propietaria.
* A **Bob** (`user_bob`) se le otorga acceso, lo ejerce y luego se le revoca.

El encabezado `X-On-Behalf-Of` controla como qué usuario final actúa una llamada.
Empieza creando una conversación como Alice y capturando su ID — cada fragmento a continuación lo utiliza:

<CodeGroup>
  ```bash CLI theme={null}
  RESOURCE_ID=$(mka1 llm conversations create \
    --body '{ "metadata": { "project": "acme" } }' \
    -H 'X-On-Behalf-Of: user_alice' \
    -o json | jq -r '.id')
  ```

  ```ts MKA1 SDK theme={null}
  import { SDK } from '@meetkai/mka1'

  const mka1 = new SDK({ bearerAuth: 'Bearer YOUR_API_KEY' })

  // Alice crea una conversación y se convierte en su propietaria
  const conv = await mka1.llm.conversations.create(
    { metadata: { project: 'acme' } },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )
  const resourceId = conv.id // p. ej. conv_abc123
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");

  // Alice crea una conversación y se convierte en su propietaria
  var conv = await sdk.Llm.Conversations.CreateAsync(
      body: new CreateConversationRequest()
      {
          Metadata = new Dictionary<string, string> { { "project", "acme" } },
      },
      xOnBehalfOf: "user_alice"
  );
  var resourceId = conv.ConversationObject!.Id;
  ```

  ```python Python SDK theme={null}
  from mka1 import SDK

  sdk = SDK(bearer_auth="Bearer YOUR_API_KEY")

  # Alice crea una conversación y se convierte en su propietaria
  conv = sdk.llm.conversations.create(
      metadata={"project": "acme"},
      x_on_behalf_of="user_alice",
  )
  resource_id = conv.id
  ```

  ```bash bash theme={null}
  RESOURCE_ID=$(curl -s https://apigw.mka1.com/api/v1/llm/conversations \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_alice' \
    --data '{ "metadata": { "project": "acme" } }' | jq -r '.id')
  ```
</CodeGroup>

<Note>
  Otorgar, comprobar y revocar solo funcionan contra un ID de recurso real que el usuario actuante posee.
  Dos errores comunes producen un error `403 Forbidden` "is not an owner": pasar un ID inventado (como `conv-abc-123`), u omitir `X-On-Behalf-Of` — sin él, la llamada actúa como el usuario de servicio propio de la API key, no como Alice.
</Note>

## Otorgar un rol a un usuario

Utiliza [`POST /api/v1/authorization/llm/grant`](/es/api-reference/resource-authorization/grant-permission-to-a-user-or-make-public) para asignar un rol a un usuario sobre un recurso.
El llamador debe ser el propietario del recurso, por lo que Alice es quien otorga:

<CodeGroup>
  ```bash CLI theme={null}
  mka1 permissions llm grant \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --user-id user_bob \
    --role reader \
    -H 'X-On-Behalf-Of: user_alice'
  ```

  ```ts MKA1 SDK theme={null}
  // Alice otorga acceso de lectura a user_bob en su conversación
  await mka1.permissions.llm.grant(
    {
      resourceType: 'conversation',
      resourceId,
      userId: 'user_bob',
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )
  ```

  ```csharp C# SDK theme={null}
  // Alice otorga acceso de lectura a user_bob en su conversación
  await sdk.Permissions.Llm.GrantAsync(
      body: new GrantPermissionRequest()
      {
          ResourceType = LlmResourceType.Conversation,
          ResourceId = resourceId,
          UserId = "user_bob",
          Role = ResourceRole.Reader,
      },
      xOnBehalfOf: "user_alice"
  );
  ```

  ```python Python SDK theme={null}
  # Alice otorga acceso de lectura a user_bob en su conversación
  sdk.permissions.llm.grant(
      resource_type="conversation",
      resource_id=resource_id,
      user_id="user_bob",
      role="reader",
      x_on_behalf_of="user_alice",
  )
  ```

  ```bash bash theme={null}
  curl https://apigw.mka1.com/api/v1/authorization/llm/grant \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_alice' \
    --data "{
      \"resourceType\": \"conversation\",
      \"resourceId\": \"$RESOURCE_ID\",
      \"userId\": \"user_bob\",
      \"role\": \"reader\"
    }"
  ```
</CodeGroup>

Un otorgamiento exitoso devuelve `204 No Content`.

El `resourceType` debe ser uno de: `completion`, `file`, `vector_store`, `conversation`, `response` o `skill`.
El `role` debe ser uno de: `owner`, `writer` o `reader`.

## Comprobar el permiso de un usuario

Utiliza [`GET /api/v1/authorization/llm/check`](/es/api-reference/resource-authorization/check-user-permission) para verificar si el llamador autenticado tiene un rol específico sobre un recurso.
La respuesta contiene un booleano `allowed`.
Aquí Bob — a quien se le acaba de otorgar `reader` — comprueba su propio acceso:

<CodeGroup>
  ```bash CLI theme={null}
  mka1 permissions llm check \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --role reader \
    -H 'X-On-Behalf-Of: user_bob'
  ```

  ```ts MKA1 SDK theme={null}
  // Bob comprueba si tiene acceso de lectura
  const result = await mka1.permissions.llm.check(
    {
      resourceType: 'conversation',
      resourceId,
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_bob' } }
  )

  console.log(result.allowed) // true
  ```

  ```csharp C# SDK theme={null}
  // Bob comprueba si tiene acceso de lectura
  var result = await sdk.Permissions.Llm.CheckAsync(
      resourceType: LlmResourceType.Conversation,
      resourceId: resourceId,
      role: ResourceRole.Reader,
      xOnBehalfOf: "user_bob"
  );

  Console.WriteLine(result.CheckPermissionResponse!.Allowed); // true
  ```

  ```python Python SDK theme={null}
  # Bob comprueba si tiene acceso de lectura
  result = sdk.permissions.llm.check(
      resource_type="conversation",
      resource_id=resource_id,
      role="reader",
      x_on_behalf_of="user_bob",
  )

  print(result.allowed)  # True
  ```

  ```bash bash theme={null}
  curl "https://apigw.mka1.com/api/v1/authorization/llm/check?resourceType=conversation&resourceId=$RESOURCE_ID&role=reader" \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_bob'
  ```
</CodeGroup>

```json theme={null}
{ "allowed": true }
```

Debido a que los roles son jerárquicos, un propietario también pasa una comprobación de `reader` o `writer`.

## Revocar un rol

Utiliza [`POST /api/v1/authorization/llm/revoke`](/es/api-reference/resource-authorization/revoke-permission-from-a-user-or-remove-public-access) para eliminar un rol de un usuario.
Solo el propietario del recurso puede revocar, por lo que Alice elimina el acceso de Bob:

<CodeGroup>
  ```bash CLI theme={null}
  mka1 permissions llm revoke \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --user-id user_bob \
    --role reader \
    -H 'X-On-Behalf-Of: user_alice'
  ```

  ```ts MKA1 SDK theme={null}
  // Alice revoca el acceso de lectura de Bob
  await mka1.permissions.llm.revoke(
    {
      resourceType: 'conversation',
      resourceId,
      userId: 'user_bob',
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )
  ```

  ```csharp C# SDK theme={null}
  // Alice revoca el acceso de lectura de Bob
  await sdk.Permissions.Llm.RevokeAsync(
      body: new RevokePermissionRequest()
      {
          ResourceType = LlmResourceType.Conversation,
          ResourceId = resourceId,
          UserId = "user_bob",
          Role = ResourceRole.Reader,
      },
      xOnBehalfOf: "user_alice"
  );
  ```

  ```python Python SDK theme={null}
  # Alice revoca el acceso de lectura de Bob
  sdk.permissions.llm.revoke(
      resource_type="conversation",
      resource_id=resource_id,
      user_id="user_bob",
      role="reader",
      x_on_behalf_of="user_alice",
  )
  ```

  ```bash bash theme={null}
  curl https://apigw.mka1.com/api/v1/authorization/llm/revoke \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_alice' \
    --data "{
      \"resourceType\": \"conversation\",
      \"resourceId\": \"$RESOURCE_ID\",
      \"userId\": \"user_bob\",
      \"role\": \"reader\"
    }"
  ```
</CodeGroup>

Una revocación exitosa devuelve `204 No Content`.

## Otorgar acceso público

Otorga un rol a todos los usuarios autenticados estableciendo `userId` en `"*"`.
El acceso público está restringido a los roles de `writer` y `reader` — no puedes hacer que alguien sea propietario público.
Como en cualquier otorgamiento, el llamador debe ser el propietario del recurso (Alice):

<CodeGroup>
  ```bash CLI theme={null}
  mka1 permissions llm grant \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --user-id '*' \
    --role reader \
    -H 'X-On-Behalf-Of: user_alice'
  ```

  ```ts MKA1 SDK theme={null}
  // Alice hace que su conversación sea legible públicamente
  await mka1.permissions.llm.grant(
    {
      resourceType: 'conversation',
      resourceId,
      userId: '*',
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )
  ```

  ```csharp C# SDK theme={null}
  // Alice hace que su conversación sea legible públicamente
  await sdk.Permissions.Llm.GrantAsync(
      body: new GrantPermissionRequest()
      {
          ResourceType = LlmResourceType.Conversation,
          ResourceId = resourceId,
          UserId = "*",
          Role = ResourceRole.Reader,
      },
      xOnBehalfOf: "user_alice"
  );
  ```

  ```python Python SDK theme={null}
  # Alice hace que su conversación sea legible públicamente
  sdk.permissions.llm.grant(
      resource_type="conversation",
      resource_id=resource_id,
      user_id="*",
      role="reader",
      x_on_behalf_of="user_alice",
  )
  ```

  ```bash bash theme={null}
  curl https://apigw.mka1.com/api/v1/authorization/llm/grant \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_alice' \
    --data "{
      \"resourceType\": \"conversation\",
      \"resourceId\": \"$RESOURCE_ID\",
      \"userId\": \"*\",
      \"role\": \"reader\"
    }"
  ```
</CodeGroup>

## Manejar errores de autorización

Los endpoints de autorización devuelven `403 Forbidden` por dos razones distintas — comprueba el `message` para diferenciarlas.

**A la API key le falta un ámbito requerido.** La solicitud se rechaza antes de cualquier comprobación de propiedad.
Soluciónalo habilitando los ámbitos de autorización granular en tu key — consulta [Ámbitos requeridos de la API key](#ámbitos-requeridos-de-la-api-key).

```json theme={null}
{
  "code": "FORBIDDEN",
  "status": 403,
  "message": "API key is missing required scope(s): write:fine-grained-authorization",
  "data": {
    "missingScopes": ["write:fine-grained-authorization"],
    "requiredScopes": ["write:fine-grained-authorization"]
  }
}
```

**El llamador no es el propietario del recurso.** Solo los propietarios pueden otorgar o revocar:

```json theme={null}
{
  "code": "FORBIDDEN",
  "status": 403,
  "message": "User vzEeDJQ39D631rCPWvekWENlJP8WEAgF is not an owner of conversation:conv-abc-123"
}
```

Además de un problema genuino de permisos, este error tiene dos causas comunes:

* **El ID de recurso no existe** — debes [crear el recurso primero](#configuración-crear-un-recurso-como-alice) y usar su ID real; los IDs de marcador de posición se rechazan como no poseídos.
* **Falta `X-On-Behalf-Of`** — la llamada actúa entonces como el usuario de servicio propio de la API key (el ID de usuario opaco en el mensaje anterior), no como el usuario final que posee el recurso.

Comprueba siempre el rol del llamador antes de intentar cambios de permisos, o maneja la respuesta 403 en tu aplicación.

## Ejemplo de extremo a extremo: dos usuarios con diferentes roles

Esta guía muestra perfiles de usuario distintos con permisos diferenciados.
Alice crea una conversación (convirtiéndose en su propietaria), luego otorga acceso de lectura a Bob.
Verificamos que Bob puede leer pero no puede escribir, y que Bob no puede otorgar permisos a otros.

<CodeGroup>
  ```bash CLI theme={null}
  # Paso 1 — Alice crea una conversación (ella se convierte en la propietaria)
  CONV_JSON=$(mka1 llm conversations create \
    --body '{ "metadata": { "project": "acme" } }' \
    -H 'X-On-Behalf-Of: user_alice' \
    -o json)
  RESOURCE_ID=$(echo "$CONV_JSON" | jq -r '.id')

  # Paso 2 — Alice otorga acceso de lectura a Bob
  mka1 permissions llm grant \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --user-id user_bob \
    --role reader \
    -H 'X-On-Behalf-Of: user_alice'

  # Paso 3 — Comprobar que Bob tiene acceso de lectura (allowed: true)
  mka1 permissions llm check \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --role reader \
    -H 'X-On-Behalf-Of: user_bob'

  # Paso 3b — Comprobar que Bob NO tiene acceso de escritura (allowed: false)
  mka1 permissions llm check \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --role writer \
    -H 'X-On-Behalf-Of: user_bob'

  # Paso 4 — Bob intenta otorgar (devuelve 403 Forbidden)
  mka1 permissions llm grant \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --user-id user_charlie \
    --role reader \
    -H 'X-On-Behalf-Of: user_bob'

  # Paso 5 — Alice revoca el acceso de Bob
  mka1 permissions llm revoke \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --user-id user_bob \
    --role reader \
    -H 'X-On-Behalf-Of: user_alice'

  # Paso 6 — Verificar que Bob ahora está denegado (allowed: false)
  mka1 permissions llm check \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --role reader \
    -H 'X-On-Behalf-Of: user_bob'
  ```

  ```ts MKA1 SDK theme={null}
  import { SDK } from '@meetkai/mka1'

  const mka1 = new SDK({ bearerAuth: 'Bearer YOUR_API_KEY' })

  // Paso 1 — Alice crea una conversación (ella se convierte en la propietaria)
  const conv = await mka1.llm.conversations.create(
    { metadata: { project: 'acme' } },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )
  const resourceId = conv.id

  // Paso 2 — Alice otorga acceso de lectura a Bob
  await mka1.permissions.llm.grant(
    {
      resourceType: 'conversation',
      resourceId,
      userId: 'user_bob',
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )

  // Paso 3 — Comprobar los permisos de Bob
  // Bob tiene acceso de lectura → permitido
  const bobReader = await mka1.permissions.llm.check(
    {
      resourceType: 'conversation',
      resourceId,
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_bob' } }
  )
  console.log('Bob reader:', bobReader.allowed) // true

  // Bob NO tiene acceso de escritura → denegado
  const bobWriter = await mka1.permissions.llm.check(
    {
      resourceType: 'conversation',
      resourceId,
      role: 'writer',
    },
    { headers: { 'X-On-Behalf-Of': 'user_bob' } }
  )
  console.log('Bob writer:', bobWriter.allowed) // false

  // Paso 4 — Bob intenta otorgar (falla con 403)
  try {
    await mka1.permissions.llm.grant(
      {
        resourceType: 'conversation',
        resourceId,
        userId: 'user_charlie',
        role: 'reader',
      },
      { headers: { 'X-On-Behalf-Of': 'user_bob' } }
    )
  } catch (err) {
    console.log('Bob grant rejected:', err.statusCode) // 403
  }

  // Paso 5 — Alice revoca el acceso de Bob
  await mka1.permissions.llm.revoke(
    {
      resourceType: 'conversation',
      resourceId,
      userId: 'user_bob',
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )

  // Paso 6 — Verificar que Bob ahora está denegado
  const bobAfterRevoke = await mka1.permissions.llm.check(
    {
      resourceType: 'conversation',
      resourceId,
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_bob' } }
  )
  console.log('Bob after revoke:', bobAfterRevoke.allowed) // false
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;
  using MeetKai.MKA1.Types.Errors;

  var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");

  // Paso 1 — Alice crea una conversación (ella se convierte en la propietaria)
  var conv = await sdk.Llm.Conversations.CreateAsync(
      body: new CreateConversationRequest()
      {
          Metadata = new Dictionary<string, string> { { "project", "acme" } },
      },
      xOnBehalfOf: "user_alice"
  );
  var resourceId = conv.ConversationObject!.Id;

  // Paso 2 — Alice otorga acceso de lectura a Bob
  await sdk.Permissions.Llm.GrantAsync(
      body: new GrantPermissionRequest()
      {
          ResourceType = LlmResourceType.Conversation,
          ResourceId = resourceId,
          UserId = "user_bob",
          Role = ResourceRole.Reader,
      },
      xOnBehalfOf: "user_alice"
  );

  // Paso 3 — Comprobar los permisos de Bob
  // Bob tiene acceso de lectura → permitido
  var bobReader = await sdk.Permissions.Llm.CheckAsync(
      resourceType: LlmResourceType.Conversation,
      resourceId: resourceId,
      role: ResourceRole.Reader,
      xOnBehalfOf: "user_bob"
  );
  Console.WriteLine($"Bob reader: {bobReader.CheckPermissionResponse!.Allowed}"); // true

  // Bob NO tiene acceso de escritura → denegado
  var bobWriter = await sdk.Permissions.Llm.CheckAsync(
      resourceType: LlmResourceType.Conversation,
      resourceId: resourceId,
      role: ResourceRole.Writer,
      xOnBehalfOf: "user_bob"
  );
  Console.WriteLine($"Bob writer: {bobWriter.CheckPermissionResponse!.Allowed}"); // false

  // Paso 4 — Bob intenta otorgar (falla con 403)
  try
  {
      await sdk.Permissions.Llm.GrantAsync(
          body: new GrantPermissionRequest()
          {
              ResourceType = LlmResourceType.Conversation,
              ResourceId = resourceId,
              UserId = "user_charlie",
              Role = ResourceRole.Reader,
          },
          xOnBehalfOf: "user_bob"
      );
  }
  catch (APIException ex)
  {
      Console.WriteLine($"Bob grant rejected: {(int)ex.Response.StatusCode}"); // 403
  }

  // Paso 5 — Alice revoca el acceso de Bob
  await sdk.Permissions.Llm.RevokeAsync(
      body: new RevokePermissionRequest()
      {
          ResourceType = LlmResourceType.Conversation,
          ResourceId = resourceId,
          UserId = "user_bob",
          Role = ResourceRole.Reader,
      },
      xOnBehalfOf: "user_alice"
  );

  // Paso 6 — Verificar que Bob ahora está denegado
  var bobAfterRevoke = await sdk.Permissions.Llm.CheckAsync(
      resourceType: LlmResourceType.Conversation,
      resourceId: resourceId,
      role: ResourceRole.Reader,
      xOnBehalfOf: "user_bob"
  );
  Console.WriteLine($"Bob after revoke: {bobAfterRevoke.CheckPermissionResponse!.Allowed}"); // false
  ```

  ```python Python SDK theme={null}
  # Paso 1 — Alice crea una conversación (ella se convierte en la propietaria)
  conv = sdk.llm.conversations.create(
      metadata={"project": "acme"},
      x_on_behalf_of="user_alice",
  )
  resource_id = conv.id

  # Paso 2 — Alice otorga acceso de lectura a Bob
  sdk.permissions.llm.grant(
      resource_type="conversation",
      resource_id=resource_id,
      user_id="user_bob",
      role="reader",
      x_on_behalf_of="user_alice",
  )

  # Paso 3 — Comprobar los permisos de Bob
  # Bob tiene acceso de lectura -> permitido
  bob_reader = sdk.permissions.llm.check(
      resource_type="conversation",
      resource_id=resource_id,
      role="reader",
      x_on_behalf_of="user_bob",
  )
  print("Bob reader:", bob_reader.allowed)  # True

  # Bob NO tiene acceso de escritura -> denegado
  bob_writer = sdk.permissions.llm.check(
      resource_type="conversation",
      resource_id=resource_id,
      role="writer",
      x_on_behalf_of="user_bob",
  )
  print("Bob writer:", bob_writer.allowed)  # False

  # Paso 4 — Bob intenta otorgar (falla con 403)
  try:
      sdk.permissions.llm.grant(
          resource_type="conversation",
          resource_id=resource_id,
          user_id="user_charlie",
          role="reader",
          x_on_behalf_of="user_bob",
      )
  except Exception as err:
      print("Bob grant rejected:", err)  # 403

  # Paso 5 — Alice revoca el acceso de Bob
  sdk.permissions.llm.revoke(
      resource_type="conversation",
      resource_id=resource_id,
      user_id="user_bob",
      role="reader",
      x_on_behalf_of="user_alice",
  )

  # Paso 6 — Verificar que Bob ahora está denegado
  bob_after_revoke = sdk.permissions.llm.check(
      resource_type="conversation",
      resource_id=resource_id,
      role="reader",
      x_on_behalf_of="user_bob",
  )
  print("Bob after revoke:", bob_after_revoke.allowed)  # False
  ```

  ```bash bash theme={null}
  # Paso 1 — Alice crea una conversación (ella se convierte en la propietaria)
  CONV=$(curl -s https://apigw.mka1.com/api/v1/llm/conversations \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_alice' \
    --data '{ "metadata": { "project": "acme" } }')
  RESOURCE_ID=$(echo "$CONV" | jq -r '.id')

  # Paso 2 — Alice otorga acceso de lectura a Bob
  curl https://apigw.mka1.com/api/v1/authorization/llm/grant \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_alice' \
    --data "{
      \"resourceType\": \"conversation\",
      \"resourceId\": \"$RESOURCE_ID\",
      \"userId\": \"user_bob\",
      \"role\": \"reader\"
    }"

  # Paso 3 — Comprobar que Bob tiene acceso de lectura (allowed: true)
  curl "https://apigw.mka1.com/api/v1/authorization/llm/check?resourceType=conversation&resourceId=$RESOURCE_ID&role=reader" \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_bob'

  # Paso 3b — Comprobar que Bob NO tiene acceso de escritura (allowed: false)
  curl "https://apigw.mka1.com/api/v1/authorization/llm/check?resourceType=conversation&resourceId=$RESOURCE_ID&role=writer" \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_bob'

  # Paso 4 — Bob intenta otorgar (devuelve 403 Forbidden)
  curl -s -o /dev/null -w "%{http_code}" \
    https://apigw.mka1.com/api/v1/authorization/llm/grant \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_bob' \
    --data "{
      \"resourceType\": \"conversation\",
      \"resourceId\": \"$RESOURCE_ID\",
      \"userId\": \"user_charlie\",
      \"role\": \"reader\"
    }"
  # Salida: 403

  # Paso 5 — Alice revoca el acceso de Bob
  curl https://apigw.mka1.com/api/v1/authorization/llm/revoke \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_alice' \
    --data "{
      \"resourceType\": \"conversation\",
      \"resourceId\": \"$RESOURCE_ID\",
      \"userId\": \"user_bob\",
      \"role\": \"reader\"
    }"

  # Paso 6 — Verificar que Bob ahora está denegado (allowed: false)
  curl "https://apigw.mka1.com/api/v1/authorization/llm/check?resourceType=conversation&resourceId=$RESOURCE_ID&role=reader" \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: user_bob'
  ```
</CodeGroup>

## Próximos pasos

* Revisa la guía de [Autenticación](/es/docs/authentication) para el uso de API key y JWT.
* Referencia de API para los endpoints de autorización:
  * [`POST /api/v1/authorization/llm/grant`](/es/api-reference/resource-authorization/grant-permission-to-a-user-or-make-public) — Otorgar un rol
  * [`POST /api/v1/authorization/llm/revoke`](/es/api-reference/resource-authorization/revoke-permission-from-a-user-or-remove-public-access) — Revocar un rol
  * [`GET /api/v1/authorization/llm/check`](/es/api-reference/resource-authorization/check-user-permission) — Comprobar permiso
