Skip to main content
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:
RoleCan do
ownerGrant and revoke roles, plus everything writer can do
writerModify the resource, plus everything reader can do
readerRead 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 to assign a role to a user on a resource. The caller must be the resource owner.
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',
})
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 to verify whether the authenticated caller has a specific role on a resource. The response contains an allowed boolean.
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
{ "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 to remove a role from a user. Only the resource owner can revoke.
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',
})
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.
await mka1.permissions.llm.grant({
  resourceType: 'conversation',
  resourceId: 'conv-abc-123',
  userId: '*',
  role: 'reader',
})

Handle authorization errors

When a non-owner attempts to grant or revoke, the API returns 403 Forbidden:
{
  "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.
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

Next steps