> ## 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.

# Authorization

> Control access to LLM resources with role-based authorization. Assign owner, writer, or reader roles to users and verify permissions before every action.

The MKA1 API provides resource-level role-based access control (RBAC) through the authorization endpoints.
Use these endpoints to grant, check, and revoke permissions on LLM resources for specific users.

## How resource authorization works

Every LLM resource (completion, file, vector store, conversation, response, or skill) can have per-user roles assigned to it.

Three roles form a strict hierarchy:

| Role       | Can do                                                |
| ---------- | ----------------------------------------------------- |
| **owner**  | Grant and revoke roles, plus everything writer can do |
| **writer** | Modify the resource, plus everything reader can do    |
| **reader** | Read the resource                                     |

Resource IDs are created when you make LLM API calls.
For example, creating a conversation returns a `conv_` ID, creating a response returns a `resp_` ID, and uploading a file returns a `file_` ID.
The authenticated caller (or the end user specified via `X-On-Behalf-Of`) automatically becomes the **owner** of that resource.

Use the returned resource ID with the authorization endpoints below to manage access for other users.
Only owners can grant or revoke roles.
If a non-owner attempts to grant or revoke, the API returns `403 Forbidden`.

These authorizations are enforced by the MKA1 backend on every request.
Any attempt to read, modify, or delete a resource that the caller does not have access to is rejected with an appropriate error response.

You can also grant public access by using `"*"` as the user ID, but only for `writer` or `reader` roles.

## Grant a role to a user

Use [`POST /api/v1/authorization/llm/grant`](/api-reference/resource-authorization/grant-permission-to-a-user-or-make-public) to assign a role to a user on a resource.
The caller must be the resource owner.

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

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

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

  // Grant reader access to user_bob on a conversation resource
  await mka1.permissions.llm.grant({
    resourceType: 'conversation',
    resourceId: 'conv-abc-123',
    userId: 'user_bob',
    role: 'reader',
  })
  ```

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

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

  // Grant reader access to user_bob on a conversation resource
  await sdk.Permissions.Llm.GrantAsync(new GrantPermissionRequest()
  {
      ResourceType = LlmResourceType.Conversation,
      ResourceId = "conv-abc-123",
      UserId = "user_bob",
      Role = ResourceRole.Reader,
  });
  ```

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

  sdk = SDK(bearer_auth="Bearer YOUR_API_KEY")

  # Grant reader access to user_bob on a conversation resource
  sdk.permissions.llm.grant(
      resource_type="conversation",
      resource_id="conv-abc-123",
      user_id="user_bob",
      role="reader",
  )
  ```

  ```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": "conv-abc-123",
      "userId": "user_bob",
      "role": "reader"
    }'
  ```
</CodeGroup>

A successful grant returns `204 No Content`.

The `resourceType` must be one of: `completion`, `file`, `vector_store`, `conversation`, `response`, or `skill`.
The `role` must be one of: `owner`, `writer`, or `reader`.

## Check a user's permission

Use [`GET /api/v1/authorization/llm/check`](/api-reference/resource-authorization/check-user-permission) to verify whether the authenticated caller has a specific role on a resource.
The response contains an `allowed` boolean.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 permissions llm check \
    --resource-type conversation \
    --resource-id conv-abc-123 \
    --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' })

  // Check if the caller has reader access
  const result = await mka1.permissions.llm.check({
    resourceType: 'conversation',
    resourceId: 'conv-abc-123',
    role: 'reader',
  })

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

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

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

  // Check if the caller has reader access
  var result = await sdk.Permissions.Llm.CheckAsync(
      resourceType: LlmResourceType.Conversation,
      resourceId: "conv-abc-123",
      role: ResourceRole.Reader
  );

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

  ```python Python SDK theme={null}
  # Check if the caller has reader access
  result = sdk.permissions.llm.check(
      resource_type="conversation",
      resource_id="conv-abc-123",
      role="reader",
  )

  print(result.allowed)  # True
  ```

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

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

Because roles are hierarchical, an owner also passes a `reader` or `writer` check.

## Revoke a role

Use [`POST /api/v1/authorization/llm/revoke`](/api-reference/resource-authorization/revoke-permission-from-a-user-or-remove-public-access) to remove a role from a user.
Only the resource owner can revoke.

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

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

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

  // Revoke reader access from user_bob
  await mka1.permissions.llm.revoke({
    resourceType: 'conversation',
    resourceId: 'conv-abc-123',
    userId: 'user_bob',
    role: 'reader',
  })
  ```

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

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

  // Revoke reader access from user_bob
  await sdk.Permissions.Llm.RevokeAsync(new RevokePermissionRequest()
  {
      ResourceType = LlmResourceType.Conversation,
      ResourceId = "conv-abc-123",
      UserId = "user_bob",
      Role = ResourceRole.Reader,
  });
  ```

  ```python Python SDK theme={null}
  # Revoke reader access from user_bob
  sdk.permissions.llm.revoke(
      resource_type="conversation",
      resource_id="conv-abc-123",
      user_id="user_bob",
      role="reader",
  )
  ```

  ```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": "conv-abc-123",
      "userId": "user_bob",
      "role": "reader"
    }'
  ```
</CodeGroup>

A successful revoke returns `204 No Content`.

## Grant public access

Grant a role to all authenticated users by setting `userId` to `"*"`.
Public access is restricted to `writer` and `reader` roles — you cannot make someone a public owner.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 permissions llm grant \
    --resource-type conversation \
    --resource-id conv-abc-123 \
    --user-id '*' \
    --role reader
  ```

  ```ts MKA1 SDK theme={null}
  await mka1.permissions.llm.grant({
    resourceType: 'conversation',
    resourceId: 'conv-abc-123',
    userId: '*',
    role: 'reader',
  })
  ```

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

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

  await sdk.Permissions.Llm.GrantAsync(new GrantPermissionRequest()
  {
      ResourceType = LlmResourceType.Conversation,
      ResourceId = "conv-abc-123",
      UserId = "*",
      Role = ResourceRole.Reader,
  });
  ```

  ```python Python SDK theme={null}
  sdk.permissions.llm.grant(
      resource_type="conversation",
      resource_id="conv-abc-123",
      user_id="*",
      role="reader",
  )
  ```

  ```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>' \
    --data '{
      "resourceType": "conversation",
      "resourceId": "conv-abc-123",
      "userId": "*",
      "role": "reader"
    }'
  ```
</CodeGroup>

## Handle authorization errors

When a non-owner attempts to grant or revoke, the API returns `403 Forbidden`:

```json theme={null}
{
  "error": "Forbidden",
  "message": "Only resource owners can grant or revoke permissions"
}
```

Always check the caller's role before attempting permission changes, or handle the 403 response in your application.

## End-to-end example: two users with different roles

This walkthrough demonstrates distinct user profiles with differentiated permissions.
Alice creates a conversation (becoming its owner), then grants read access to Bob.
We verify that Bob can read but cannot write, and that Bob cannot grant permissions to others.

<CodeGroup>
  ```bash CLI theme={null}
  # Step 1 — Alice creates a conversation (she becomes the owner)
  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')

  # Step 2 — Alice grants reader access to 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'

  # Step 3 — Check Bob has reader access (allowed: true)
  mka1 permissions llm check \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --role reader \
    -H 'X-On-Behalf-Of: user_bob'

  # Step 3b — Check Bob does NOT have writer access (allowed: false)
  mka1 permissions llm check \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --role writer \
    -H 'X-On-Behalf-Of: user_bob'

  # Step 4 — Bob tries to grant (returns 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'

  # Step 5 — Alice revokes Bob's access
  mka1 permissions llm revoke \
    --resource-type conversation \
    --resource-id "$RESOURCE_ID" \
    --user-id user_bob \
    --role reader \
    -H 'X-On-Behalf-Of: user_alice'

  # Step 6 — Verify Bob is now denied (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' })

  // Step 1 — Alice creates a conversation (she becomes the owner)
  const conv = await mka1.llm.conversations.create(
    { metadata: { project: 'acme' } },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )
  const resourceId = conv.id

  // Step 2 — Alice grants reader access to Bob
  await mka1.permissions.llm.grant(
    {
      resourceType: 'conversation',
      resourceId,
      userId: 'user_bob',
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )

  // Step 3 — Check Bob's permissions
  // Bob has reader access → allowed
  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 does NOT have writer access → denied
  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

  // Step 4 — Bob tries to grant (fails with 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
  }

  // Step 5 — Alice revokes Bob's access
  await mka1.permissions.llm.revoke(
    {
      resourceType: 'conversation',
      resourceId,
      userId: 'user_bob',
      role: 'reader',
    },
    { headers: { 'X-On-Behalf-Of': 'user_alice' } }
  )

  // Step 6 — Verify Bob is now denied
  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;

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

  // Step 1 — Create a conversation (caller becomes the owner)
  var conv = await sdk.Llm.Conversations.CreateAsync(new CreateConversationRequest()
  {
      Metadata = new Dictionary<string, string> { { "project", "acme" } },
  });
  var resourceId = conv.ConversationObject!.Id;

  // Step 2 — Grant reader access to Bob
  await sdk.Permissions.Llm.GrantAsync(new GrantPermissionRequest()
  {
      ResourceType = LlmResourceType.Conversation,
      ResourceId = resourceId,
      UserId = "user_bob",
      Role = ResourceRole.Reader,
  });

  // Step 3 — Check Bob's permission
  var bobReader = await sdk.Permissions.Llm.CheckAsync(
      LlmResourceType.Conversation, resourceId, ResourceRole.Reader);

  // Step 4 — Revoke Bob's access
  await sdk.Permissions.Llm.RevokeAsync(new RevokePermissionRequest()
  {
      ResourceType = LlmResourceType.Conversation,
      ResourceId = resourceId,
      UserId = "user_bob",
      Role = ResourceRole.Reader,
  });
  ```

  ```python Python SDK theme={null}
  # Step 1 — Alice creates a conversation (she becomes the owner)
  conv = sdk.llm.conversations.create(
      metadata={"project": "acme"},
      http_headers={"X-On-Behalf-Of": "user_alice"},
  )
  resource_id = conv.id

  # Step 2 — Alice grants reader access to Bob
  sdk.permissions.llm.grant(
      resource_type="conversation",
      resource_id=resource_id,
      user_id="user_bob",
      role="reader",
      http_headers={"X-On-Behalf-Of": "user_alice"},
  )

  # Step 3 — Check Bob's permissions
  # Bob has reader access -> allowed
  bob_reader = sdk.permissions.llm.check(
      resource_type="conversation",
      resource_id=resource_id,
      role="reader",
      http_headers={"X-On-Behalf-Of": "user_bob"},
  )
  print("Bob reader:", bob_reader.allowed)  # True

  # Bob does NOT have writer access -> denied
  bob_writer = sdk.permissions.llm.check(
      resource_type="conversation",
      resource_id=resource_id,
      role="writer",
      http_headers={"X-On-Behalf-Of": "user_bob"},
  )
  print("Bob writer:", bob_writer.allowed)  # False

  # Step 4 — Bob tries to grant (fails with 403)
  try:
      sdk.permissions.llm.grant(
          resource_type="conversation",
          resource_id=resource_id,
          user_id="user_charlie",
          role="reader",
          http_headers={"X-On-Behalf-Of": "user_bob"},
      )
  except Exception as err:
      print("Bob grant rejected:", err)  # 403

  # Step 5 — Alice revokes Bob's access
  sdk.permissions.llm.revoke(
      resource_type="conversation",
      resource_id=resource_id,
      user_id="user_bob",
      role="reader",
      http_headers={"X-On-Behalf-Of": "user_alice"},
  )

  # Step 6 — Verify Bob is now denied
  bob_after_revoke = sdk.permissions.llm.check(
      resource_type="conversation",
      resource_id=resource_id,
      role="reader",
      http_headers={"X-On-Behalf-Of": "user_bob"},
  )
  print("Bob after revoke:", bob_after_revoke.allowed)  # False
  ```

  ```bash bash theme={null}
  # Step 1 — Alice creates a conversation (she becomes the owner)
  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')

  # Step 2 — Alice grants reader access to 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\"
    }"

  # Step 3 — Check Bob has reader access (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'

  # Step 3b — Check Bob does NOT have writer access (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'

  # Step 4 — Bob tries to grant (returns 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\"
    }"
  # Output: 403

  # Step 5 — Alice revokes Bob's access
  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\"
    }"

  # Step 6 — Verify Bob is now denied (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>

## Next steps

* Review the [Authentication](/docs/authentication) guide for API key and JWT usage.
* API Reference for the authorization endpoints:
  * [`POST /api/v1/authorization/llm/grant`](/api-reference/resource-authorization/grant-permission-to-a-user-or-make-public) — Grant a role
  * [`POST /api/v1/authorization/llm/revoke`](/api-reference/resource-authorization/revoke-permission-from-a-user-or-remove-public-access) — Revoke a role
  * [`GET /api/v1/authorization/llm/check`](/api-reference/resource-authorization/check-user-permission) — Check permission
