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

# Authorization Checks

> Enforce permissions in your app.

export const Languages = {
  JAVASCRIPT: "js",
  PYTHON: "py",
  RUBY: "rb",
  JAVA: "java",
  DOTNET: "dotnet",
  GO: "go",
  SHELL: "shell",
  MACOS: "macos",
  WINDOWS: "windows",
  BASH: "bash",
  CMD: "cmd",
  TYPESCRIPT: "ts",
  HTML: "html",
  CSS: "css",
  JSON: "json",
  YAML: "yaml",
  XML: "xml"
};

export const CodeTab = ({title, language, children, disabled = false}) => {
  const getLanguageDisplayName = language => {
    const displayNames = {
      [Languages.JAVASCRIPT]: "JavaScript",
      [Languages.PYTHON]: "Python",
      [Languages.RUBY]: "Ruby",
      [Languages.JAVA]: "Java",
      [Languages.DOTNET]: ".NET",
      [Languages.GO]: "Go",
      [Languages.SHELL]: "Shell",
      [Languages.MACOS]: "MacOS / Linux",
      [Languages.WINDOWS]: "Windows",
      [Languages.CMD]: "Command Prompt",
      [Languages.TYPESCRIPT]: "TypeScript",
      [Languages.HTML]: "HTML",
      [Languages.CSS]: "CSS",
      [Languages.JSON]: "JSON",
      [Languages.YAML]: "YAML",
      [Languages.XML]: "XML"
    };
    return displayNames[language] || language || "Code";
  };
  const displayTitle = title || getLanguageDisplayName(language);
  if (!displayTitle) {
    console.warn('CodeTab component requires either a title prop or language prop');
    return null;
  }
  if (language && typeof language !== 'string') {
    console.warn('CodeTab language prop must be a string');
  }
  if (title && typeof title !== 'string') {
    console.warn('CodeTab title prop must be a string');
  }
  if (disabled && typeof disabled !== 'boolean') {
    console.warn('CodeTab disabled prop must be a boolean');
  }
  return React.createElement(React.Fragment, null, children);
};

export const CodeTabs = ({children, defaultTab = 0, className = '', syncLanguages = true, onTabChange, 'aria-label': ariaLabel = 'Code examples'}) => {
  const {useState, useMemo, useEffect, useCallback, useId, Children, isValidElement} = React;
  const baseId = useId();
  const getLanguageDisplayName = useCallback(language => {
    const displayNames = {
      [Languages.JAVASCRIPT]: "JavaScript",
      [Languages.PYTHON]: "Python",
      [Languages.RUBY]: "Ruby",
      [Languages.JAVA]: "Java",
      [Languages.DOTNET]: ".NET",
      [Languages.GO]: "Go",
      [Languages.SHELL]: "Shell",
      [Languages.MACOS]: "MacOS / Linux",
      [Languages.WINDOWS]: "Windows",
      [Languages.CMD]: "Command Prompt",
      [Languages.TYPESCRIPT]: "TypeScript",
      [Languages.HTML]: "HTML",
      [Languages.CSS]: "CSS",
      [Languages.JSON]: "JSON",
      [Languages.YAML]: "YAML",
      [Languages.XML]: "XML"
    };
    return displayNames[language] || language || "Code";
  }, []);
  const LANGUAGE_PREF_KEY = useMemo(() => 'mintlify-tabs-language-preference', []);
  const LANGUAGE_CHANGE_EVENT = useMemo(() => 'mintlify-tabs-language-change', []);
  const findMatchingLanguageTab = useCallback((tabs, targetLanguage) => {
    if (!targetLanguage?.trim() || !Array.isArray(tabs) || tabs.length === 0) return -1;
    const normalizedTarget = targetLanguage.toLowerCase().trim();
    return tabs.findIndex(tab => tab?.language?.toLowerCase().trim() === normalizedTarget);
  }, []);
  const getLanguagePreference = useCallback(() => {
    if (typeof window === 'undefined' || !window.localStorage) return null;
    try {
      const stored = localStorage.getItem(LANGUAGE_PREF_KEY);
      if (!stored) return null;
      const parsed = JSON.parse(stored);
      if (typeof parsed === 'object' && parsed.language && typeof parsed.language === 'string') {
        return parsed;
      }
      return null;
    } catch (error) {
      console.warn('Failed to parse language preference:', error);
      return null;
    }
  }, [LANGUAGE_PREF_KEY]);
  const setLanguagePreference = useCallback(language => {
    if (typeof window === 'undefined' || !window.localStorage || !language?.trim()) return;
    try {
      const preference = {
        language: language.trim(),
        timestamp: Date.now()
      };
      localStorage.setItem(LANGUAGE_PREF_KEY, JSON.stringify(preference));
      if (window.CustomEvent && window.dispatchEvent) {
        const event = new CustomEvent(LANGUAGE_CHANGE_EVENT, {
          detail: preference,
          bubbles: false,
          cancelable: false
        });
        window.dispatchEvent(event);
      }
    } catch (error) {
      console.warn('Failed to save language preference:', error);
    }
  }, [LANGUAGE_PREF_KEY, LANGUAGE_CHANGE_EVENT]);
  const generateTabId = useCallback((title, index) => {
    const cleanTitle = title?.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '') || 'tab';
    return `${baseId}-${cleanTitle}-${index}`;
  }, [baseId]);
  const [announcement, setAnnouncement] = useState('');
  const announceToScreenReader = useCallback((message, duration = 1000) => {
    setAnnouncement(message);
    const timeoutId = setTimeout(() => setAnnouncement(''), duration);
    return () => clearTimeout(timeoutId);
  }, []);
  const processedTabs = useMemo(() => {
    if (!children) return [];
    const tabData = [];
    Children.forEach(children, (child, index) => {
      if (!isValidElement(child) || child.type !== CodeTab) {
        console.warn(`Invalid child at index ${index}. Expected CodeTab component.`);
        return;
      }
      const {title, language, disabled = false} = child.props || ({});
      const displayTitle = title || getLanguageDisplayName(language);
      if (!displayTitle || typeof displayTitle !== 'string') {
        console.warn(`CodeTab at index ${index} missing title and language props`);
        return;
      }
      tabData.push({
        title: displayTitle.trim(),
        language: language?.trim() || null,
        disabled: Boolean(disabled),
        content: child.props.children,
        id: generateTabId(displayTitle, index),
        index
      });
    });
    return tabData;
  }, [children, generateTabId]);
  const [activeIndex, setActiveIndex] = useState(0);
  useEffect(() => {
    if (processedTabs.length === 0) return;
    if (typeof defaultTab === 'number') {
      const clampedIndex = Math.max(0, Math.min(defaultTab, processedTabs.length - 1));
      setActiveIndex(clampedIndex);
      return;
    }
    if (typeof defaultTab === 'string' && defaultTab.trim()) {
      const matchIndex = processedTabs.findIndex(tab => tab.title?.toLowerCase() === defaultTab.toLowerCase().trim());
      if (matchIndex >= 0 && !processedTabs[matchIndex].disabled) {
        setActiveIndex(matchIndex);
        return;
      }
    }
    const firstEnabledIndex = processedTabs.findIndex(tab => !tab.disabled);
    setActiveIndex(firstEnabledIndex >= 0 ? firstEnabledIndex : 0);
  }, [processedTabs, defaultTab]);
  useEffect(() => {
    if (!syncLanguages || processedTabs.length === 0) return;
    const preference = getLanguagePreference();
    if (preference) {
      const matchIndex = findMatchingLanguageTab(processedTabs, preference.language);
      if (matchIndex >= 0) {
        setActiveIndex(matchIndex);
      }
    }
  }, [syncLanguages, processedTabs, getLanguagePreference, findMatchingLanguageTab]);
  const handleTabClick = useCallback(index => {
    const targetTab = processedTabs[index];
    if (!targetTab || targetTab.disabled || index === activeIndex) return;
    const previousIndex = activeIndex;
    setActiveIndex(index);
    const announcement = `${targetTab.title} tab selected. Tab ${index + 1} of ${processedTabs.length}.`;
    const cleanup = announceToScreenReader(announcement);
    if (syncLanguages && targetTab.language) {
      setLanguagePreference(targetTab.language);
    }
    onTabChange?.(index, targetTab, previousIndex);
    return cleanup;
  }, [processedTabs, activeIndex, syncLanguages, setLanguagePreference, onTabChange, announceToScreenReader]);
  useEffect(() => {
    if (!syncLanguages || typeof window === 'undefined') return;
    const handleLanguageChange = event => {
      try {
        const {language} = event.detail || ({});
        if (!language || typeof language !== 'string') return;
        const matchIndex = findMatchingLanguageTab(processedTabs, language);
        if (matchIndex >= 0 && matchIndex !== activeIndex && !processedTabs[matchIndex]?.disabled) {
          setActiveIndex(matchIndex);
          const currentTab = processedTabs[matchIndex];
          const announcement = `Synchronized to ${currentTab.title} tab. Tab ${matchIndex + 1} of ${processedTabs.length}.`;
          announceToScreenReader(announcement, 600);
          onTabChange?.(matchIndex, currentTab, activeIndex);
        }
      } catch (error) {
        console.warn('Error handling language change event:', error);
      }
    };
    window.addEventListener(LANGUAGE_CHANGE_EVENT, handleLanguageChange);
    return () => {
      window.removeEventListener(LANGUAGE_CHANGE_EVENT, handleLanguageChange);
    };
  }, [syncLanguages, processedTabs, activeIndex, findMatchingLanguageTab, LANGUAGE_CHANGE_EVENT, onTabChange, announceToScreenReader]);
  const handleKeyDown = useCallback((event, index) => {
    let targetIndex = -1;
    switch (event.key) {
      case 'ArrowLeft':
        event.preventDefault();
        targetIndex = index > 0 ? index - 1 : processedTabs.length - 1;
        break;
      case 'ArrowRight':
        event.preventDefault();
        targetIndex = index < processedTabs.length - 1 ? index + 1 : 0;
        break;
      case 'Enter':
      case ' ':
        event.preventDefault();
        handleTabClick(index);
        return;
      case 'Home':
        event.preventDefault();
        targetIndex = 0;
        break;
      case 'End':
        event.preventDefault();
        targetIndex = processedTabs.length - 1;
        break;
      default:
        return;
    }
    if (targetIndex !== -1) {
      setActiveIndex(targetIndex);
    }
  }, [processedTabs.length, handleTabClick]);
  if (processedTabs.length === 0) {
    return <div className="p-4 text-gray-500 italic text-center border border-gray-200 rounded-lg">
        No valid tabs found. Please check your CodeTab components.
      </div>;
  }
  const safeActiveIndex = Math.max(0, Math.min(activeIndex, processedTabs.length - 1));
  const activeTab = processedTabs[safeActiveIndex];
  return <div className={`tabs tab-container ${className}`} id={activeTab?.language || 'code-tabs'}>
      {}
      <div aria-live="polite" aria-atomic="true" className="sr-only">
        {announcement}
      </div>

      {}
      <ul role="tablist" aria-label={ariaLabel} className="not-prose mb-6 pb-[1px] flex-none min-w-full overflow-auto border-b border-gray-200 gap-x-6 flex dark:border-gray-200/10" data-component-part="tabs-list">
        {processedTabs.map((tab, index) => {
    const isActive = index === safeActiveIndex;
    const isDisabled = tab.disabled;
    return <li key={tab.id} id={tab.id} className="cursor-pointer">
              <button role="tab" aria-selected={isActive} aria-controls={`${tab.id}-panel`} aria-disabled={isDisabled} aria-setsize={processedTabs.length} aria-posinset={index + 1} tabIndex={isActive ? 0 : -1} disabled={isDisabled} onClick={() => handleTabClick(index)} onKeyDown={e => handleKeyDown(e, index)} className={isDisabled ? 'flex text-sm items-center gap-1.5 leading-6 font-semibold whitespace-nowrap pt-3 pb-2.5 -mb-px max-w-max border-b text-gray-400 border-transparent cursor-not-allowed opacity-50' : isActive ? 'flex text-sm items-center gap-1.5 leading-6 font-semibold whitespace-nowrap pt-3 pb-2.5 -mb-px max-w-max border-b text-primary dark:text-primary-light border-current' : 'flex text-sm items-center gap-1.5 leading-6 font-semibold whitespace-nowrap pt-3 pb-2.5 -mb-px max-w-max border-b text-gray-900 border-transparent hover:border-gray-300 dark:text-gray-200 dark:hover:border-gray-700'} data-component-part="tab-button" data-active={isActive}>
                {tab.title}
              </button>
            </li>;
  })}
      </ul>

      {}
      <div role="tabpanel" id={`${activeTab?.id}-panel`} aria-labelledby={activeTab?.id} aria-label={`${activeTab?.title} content, tab panel ${safeActiveIndex + 1} of ${processedTabs.length}`} tabIndex={0} className="prose dark:prose-dark overflow-x-auto" data-component-part="tab-content">
        {activeTab?.content}
      </div>
    </div>;
};

## Authorize

Check if an actor is allowed to perform an action on a specific resource.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Basic authorization check
    const authorized = await oso.authorize(alice, "read", anvilsRepository);
    if (!authorized) {
      throw new Error("Access denied");
    }

    // With context facts for additional information
    const authorized = await oso.authorize(
      alice, 
      "read", 
      issue, 
      [["has_relation", issue, "parent", anvilsRepository]]  // Context facts
    );

    console.log(`Access ${authorized ? 'granted' : 'denied'}`);
    ```

    **Signature:** `oso.authorize(actor, action, resource, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Basic authorization check
    if not oso.authorize(alice, "read", anvils_repository):
        raise Exception("Action is not allowed")

    # With context facts
    authorized = oso.authorize(
        alice, 
        "read", 
        anvils_repository, 
        context_facts=[("has_relation", issue_on_anvils_repository, "parent", anvils_repository)]
    )

    print(f"Access {'granted' if authorized else 'denied'}")
    ```

    **Signature:** `oso.authorize(actor, action, resource, context_facts=None)`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Basic authorization check
    allowed, err := osoClient.Authorize(user, "read", anvilsRepository)
    if err != nil {
        log.Fatal(err)
    }
    if !allowed {
        return fmt.Errorf("access denied")
    }

    // With context facts
    contextFacts := []oso.Fact{
        oso.NewFact("has_relation", issueOnAnvilsRepository, oso.String("parent"), anvilsRepository),
    }
    allowed, err = osoClient.AuthorizeWithContext(user, "read", issueOnAnvilsRepository, contextFacts)
    ```

    **Signature:** `osoClient.Authorize(actor, action, resource)` | `osoClient.AuthorizeWithContext(actor, action, resource, contextFacts)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Basic authorization check
    boolean allowed = oso.authorize(user, "read", anvilsRepository);
    if (!allowed) {
        throw new SecurityException("Access denied");
    }

    // With context facts
    List<Fact> contextFacts = Arrays.asList(
        new Fact("has_relation", Arrays.asList(issueOnAnvilsRepository, new Value("parent"), anvilsRepository))
    );
    boolean allowed = oso.authorize(user, "read", issueOnAnvilsRepository, contextFacts);
    ```

    **Signature:** `oso.authorize(actor, action, resource, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Basic authorization check
    raise "Action is not allowed" unless oso.authorize(alice, "read", anvils_repository)

    # With context facts
    authorized = oso.authorize(
      alice, 
      "read", 
      anvils_repository, 
      context_facts: [["has_relation", issue_on_anvils_repository, "parent", anvils_repository]]
    )

    puts authorized ? "Access granted" : "Access denied"
    ```

    **Signature:** `oso.authorize(actor, action, resource, context_facts=nil)`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Basic authorization check
    var allowed = await oso.Authorize(user, "read", anvilsRepository);
    if (!allowed) {
        throw new UnauthorizedAccessException("Access denied");
    }

    // With context facts
    var contextFacts = new List<Fact> { 
        new Fact("has_relation", new List<Value> { 
            issueOnAnvilsRepository, 
            new Value("String", "parent"), 
            anvilsRepository 
        })
    };
    var allowed = await oso.Authorize(user, "read", anvilsRepository, contextFacts);
    ```

    **Signature:** `oso.Authorize(actor, action, resource, contextFacts?)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Basic authorization check
    oso-cloud authorize User:bob read Repository:anvils

    # With context facts
    oso-cloud authorize \
      -c "has_relation Issue:issueOnAnvilsRepository parent Repository:anvils" \
      User:bob read Issue:issueOnAnvilsRepository
    ```

    **Signature:** `oso-cloud authorize <actor> <action> <resource>`
  </CodeTab>
</CodeTabs>

## List Resources

Get all resource IDs that an actor can perform an action on.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // List all repositories the user can read
    const repositoryIds = await oso.list(alice, "read", "Repository");
    console.log("Readable repositories:", repositoryIds);

    // With context facts
    const issueIds = await oso.list(
      alice, 
      "read", 
      "Issue",
      [
        ["has_relation", issueOnAnvilsRepository, "parent", anvilsRepository],
        ["has_relation", issueOnAcmeRepository, "parent", acmeRepository]
      ]
    );
    ```

    **Signature:** `oso.list(actor, action, resourceType, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # List all repositories the user can read
    repository_ids = oso.list(alice, "read", "Repository")
    print(f"Readable repositories: {repository_ids}")

    # With context facts
    issue_ids = oso.list(
        alice, 
        "read", 
        "Issue",
        context_facts=[
            ("has_relation", issue_on_anvils_repository, "parent", anvils_repository),
            ("has_relation", issue_on_acme_repository, "parent", acme_repository)
        ]
    )
    ```

    **Signature:** `oso.list(actor, action, resource_type, context_facts=None)`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // List all repositories the user can read
    repositoryIds, err := osoClient.List(user, "read", "Repository", nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Readable repositories: %v\n", repositoryIds)

    // With context facts
    contextFacts := []oso.Fact{
        oso.NewFact("has_relation", issueOnAnvilsRepository, oso.String("parent"), anvilsRepository),
        oso.NewFact("has_relation", issueOnAcmeRepository, oso.String("parent"), acmeRepository),
    }
    issueIds, err := osoClient.ListWithContext(user, "read", "Issue", contextFacts)
    ```

    **Signature:** `osoClient.List(actor, action, resourceType, nil)` | `osoClient.ListWithContext(actor, action, resourceType, contextFacts)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // List all repositories the user can read
    String[] repositoryIds = oso.list(user, "read", "Repository");
    System.out.println("Readable repositories: " + Arrays.toString(repositoryIds));

    // With context facts
    List<Fact> contextFacts = Arrays.asList(
        new Fact("has_relation", Arrays.asList(issueOnAnvilsRepository, new Value("parent"), anvilsRepository)),
        new Fact("has_relation", Arrays.asList(issueOnAcmeRepository, new Value("parent"), acmeRepository))
    );
    String[] issueIds = oso.list(user, "read", "Issue", contextFacts);
    ```

    **Signature:** `oso.list(actor, action, resourceType, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # List all repositories the user can read
    repository_ids = oso.list(alice, "read", "Repository")
    puts "Readable repositories: #{repository_ids}"

    # With context facts
    issue_ids = oso.list(
      alice, 
      "read", 
      "Issue",
      context_facts: [
        ["has_relation", issue_on_anvils_repository, "parent", anvils_repository],
        ["has_relation", issue_on_acme_repository, "parent", acme_repository]
      ]
    )
    ```

    **Signature:** `oso.list(actor, action, resource_type, context_facts=nil)`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // List all repositories the user can read
    var repositoryIds = await oso.List(user, "read", "Repository");
    Console.WriteLine($"Readable repositories: {string.Join(", ", repositoryIds)}");

    // With context facts (contextFacts parameter not shown in docs.json for .NET list)
    ```

    **Signature:** `oso.List(actor, action, resourceType)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # List all repositories the user can read
    oso-cloud list User:bob read Repository

    # With context facts
    oso-cloud list \
      -c "has_relation Issue:issueOnAnvilsRepository parent Repository:anvils" \
      -c "has_relation Issue:issueOnAcmeRepository parent Repository:acme" \
      User:bob read Issue
    ```

    **Signature:** `oso-cloud list <actor> <action> <resource-type>`
  </CodeTab>
</CodeTabs>

## List Resources (Paginated)

Paginate through resource IDs that an actor can perform an action on. Use this when you need control over page size or want to handle large result sets incrementally. `page_size` is a required parameter.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Fetch the first page
    const firstPage = await oso.listPaginated(alice, "read", "Repository", 10000);
    console.log("First page:", firstPage.results);

    // Fetch subsequent pages using the nextPageToken
    if (firstPage.nextPageToken) {
      const secondPage = await oso.listPaginated(
        alice,
        "read",
        "Repository",
        10000,
        firstPage.nextPageToken
      );
      console.log("Second page:", secondPage.results);
    }
    ```

    **Signature:** `oso.listPaginated(actor, action, resourceType, pageSize, pageToken?, contextFacts?)`

    **Returns:** `{ results: string[], nextPageToken: string | null }`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Fetch the first page
    first_page = oso.list_paginated(alice, "read", "Repository", page_size=10000)
    print(f"First page: {first_page.results}")

    # Fetch subsequent pages using the next_page_token
    if first_page.next_page_token:
        second_page = oso.list_paginated(
            alice,
            "read",
            "Repository",
            page_size=10000,
            page_token=first_page.next_page_token
        )
        print(f"Second page: {second_page.results}")
    ```

    **Signature:** `oso.list_paginated(actor, action, resource_type, page_size, page_token=None, context_facts=None)`

    **Returns:** `ListResult` with `.results` and `.next_page_token`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Fetch the first page
    firstPage, err := osoClient.ListPaginated(user, "read", "Repository", 10000, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("First page: %v\n", firstPage.Results)

    // Fetch subsequent pages using the NextPageToken
    if firstPage.NextPageToken != nil {
        secondPage, err := osoClient.ListPaginated(
            user, "read", "Repository", 10000, firstPage.NextPageToken,
        )
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Second page: %v\n", secondPage.Results)
    }
    ```

    **Signature:** `osoClient.ListPaginated(actor, action, resourceType, pageSize, pageToken)` | `osoClient.ListPaginatedWithContext(actor, action, resourceType, pageSize, pageToken, contextFacts)`

    **Returns:** `*ListPage` with `.Results` and `.NextPageToken`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Fetch the first page
    ListPage firstPage = oso.listPaginated(user, "read", "Repository", 10000, null);
    System.out.println("First page: " + Arrays.toString(firstPage.results));

    // Fetch subsequent pages using the nextPageToken
    if (firstPage.nextPageToken != null) {
        ListPage secondPage = oso.listPaginated(
            user, "read", "Repository", 10000, firstPage.nextPageToken
        );
        System.out.println("Second page: " + Arrays.toString(secondPage.results));
    }
    ```

    **Signature:** `oso.listPaginated(actor, action, resourceType, pageSize, pageToken)` | `oso.listPaginated(actor, action, resourceType, pageSize, pageToken, contextFacts)`

    **Returns:** `ListPage` with `.results` and `.nextPageToken`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Fetch the first page
    first_page = oso.list_paginated(alice, "read", "Repository", 10000)
    puts "First page: #{first_page.results}"

    # Fetch subsequent pages using the next_page_token
    if first_page.next_page_token
      second_page = oso.list_paginated(
        alice,
        "read",
        "Repository",
        10000,
        page_token: first_page.next_page_token
      )
      puts "Second page: #{second_page.results}"
    end
    ```

    **Signature:** `oso.list_paginated(actor, action, resource_type, page_size, page_token: nil, context_facts: [])`

    **Returns:** `ListResult` with `.results` and `.next_page_token`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Fetch the first page
    var firstPage = await oso.ListPaginated(user, "read", "Repository", 10000, null);
    Console.WriteLine($"First page: {string.Join(", ", firstPage.Results)}");

    // Fetch subsequent pages using the NextPageToken
    if (firstPage.NextPageToken != null) {
        var secondPage = await oso.ListPaginated(
            user, "read", "Repository", 10000, firstPage.NextPageToken
        );
        Console.WriteLine($"Second page: {string.Join(", ", secondPage.Results)}");
    }
    ```

    **Signature:** `oso.ListPaginated(actor, action, resourceType, pageSize, pageToken)` | `oso.ListPaginated(actor, action, resourceType, pageSize, pageToken, contextFacts)`

    **Returns:** `ListResult` with `.Results` and `.NextPageToken`
  </CodeTab>
</CodeTabs>

## Get Actions

Get all actions an actor can perform on a specific resource.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Get all actions user can perform on repository
    const actions = await oso.actions(user, acmeRepository);
    console.log("Available actions:", actions);

    // With context facts
    const actions = await oso.actions(
      alice, 
      issueOnAcmeRepository, 
      [["has_relation", issueOnAcmeRepository, "parent", acmeRepository]]
    );
    ```

    **Signature:** `oso.actions(actor, resource, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Get all actions user can perform on repository
    actions = oso.actions(alice, acme_repository)
    print(f"Available actions: {actions}")

    # With context facts
    actions = oso.actions(
        alice, 
        issue_on_acme_repository, 
        context_facts=[("has_relation", issue_on_acme_repository, "parent", acme_repository)]
    )
    ```

    **Signature:** `oso.actions(actor, resource, context_facts=None)`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Get all actions user can perform on repository
    actions, err := osoClient.Actions(user, anvilsRepository)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Available actions: %v\n", actions)

    // With context facts
    contextFacts := []oso.Fact{
        oso.NewFact("has_relation", issueOnAnvilsRepository, oso.String("parent"), anvilsRepository),
    }
    actions, err = osoClient.ActionsWithContext(user, issueOnAnvilsRepository, contextFacts)
    ```

    **Signature:** `osoClient.Actions(actor, resource)` | `osoClient.ActionsWithContext(actor, resource, contextFacts)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Get all actions user can perform on repository
    String[] actions = oso.actions(user, anvilsRepository);
    System.out.println("Available actions: " + Arrays.toString(actions));

    // With context facts
    List<Fact> contextFacts = Arrays.asList(
        new Fact("has_relation", Arrays.asList(issueOnAnvilsRepository, new Value("parent"), anvilsRepository))
    );
    String[] actions = oso.actions(user, issueOnAnvilsRepository, contextFacts);
    ```

    **Signature:** `oso.actions(actor, resource, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Get all actions user can perform on repository
    actions = oso.actions(alice, acme_repository)
    puts "Available actions: #{actions}"

    # With context facts
    actions = oso.actions(
      alice, 
      issue_on_acme_repository, 
      context_facts: [["has_relation", issue_on_acme_repository, "parent", acme_repository]]
    )
    ```

    **Signature:** `oso.actions(actor, resource, context_facts=nil)`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Get all actions user can perform on repository
    var actions = await oso.Actions(user, anvilsRepository);
    Console.WriteLine($"Available actions: {string.Join(", ", actions)}");

    // Context facts not shown in docs.json for .NET actions
    ```

    **Signature:** `oso.Actions(actor, resource)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Get all actions user can perform on repository
    oso-cloud actions User:bob Repository:anvils

    # With context facts
    oso-cloud actions \
      -c "has_relation Issue:issueOnAnvilsRepository parent Repository:anvils" \
      User:bob Issue:issueOnAnvilsRepository
    ```

    **Signature:** `oso-cloud actions <actor> <resource>`
  </CodeTab>
</CodeTabs>

## Authorize Multiple Resources

Check authorization for multiple resources at once for better performance.

<CodeTabs>
  <CodeTab title="Ruby" language={Languages.RUBY}>
    ```ruby theme={null}
    # Check multiple resources at once
    resources = oso.authorize_resources(
      alice, 
      "read", 
      [anvils_repository, acme_repository]
    )
    puts "Authorized resources: #{resources}"

    # With context facts
    authorized_resources = oso.authorize_resources(
      alice, 
      "read", 
      [issue_on_anvils_repository, issue_on_acme_repository],
      context_facts: [
        ["has_relation", issue_on_anvils_repository, "parent", anvils_repository],
        ["has_relation", issue_on_acme_repository, "parent", acme_repository]
      ]
    )
    ```

    **Signature:** `oso.authorize_resources(actor, action, resources, context_facts=nil)`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Check multiple resources at once
    var results = await oso.AuthorizeResources(
      user, 
      "read", 
      new List<Value> { anvilsRepository, acmeRepository }
    );
    Console.WriteLine($"Authorized resources: {string.Join(", ", results)}");

    // With context facts
    var contextFacts = new List<Fact> { contextFact1, contextFact2 };
    var results = await oso.AuthorizeResources(
      user, 
      "read", 
      new List<Value> { anvilsRepository, acmeRepository }, 
      contextFacts
    );
    ```

    **Signature:** `oso.AuthorizeResources(actor, action, resources, contextFacts?)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Check multiple resources at once
    oso-cloud authorize-resources User:bob read Repository:anvils Repository:acme

    # With context facts
    oso-cloud authorize-resources \
      -c "has_relation Issue:issueOnAnvilsRepository parent Repository:anvils" \
      -c "has_relation Issue:issueOnAcmeRepository parent Repository:acme" \
      User:bob read Issue:issueOnAnvilsRepository Issue:issueOnAcmeRepository
    ```

    **Signature:** `oso-cloud authorize-resources <actor> <action> (<resource>)*`
  </CodeTab>
</CodeTabs>

## Query Builder

Build complex queries with additional constraints and evaluate them flexibly.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Basic query building
    const query = oso.buildQuery(["allow", actor, "read", repository]);
    const result = await query.evaluate(repository);

    // Add constraints with 'and'
    const constrainedQuery = oso.buildQuery(["allow", actor, "read", repository])
      .and(["has_relation", repository, "folder", folder]);

    // Filter with 'in' clause
    const filteredQuery = oso.buildQuery(["allow", actor, action, repository])
      .in(repository, repositories);

    // Add context facts
    const contextQuery = oso.buildQuery(["allow", actor, "read", repository])
      .withContextFacts([["has_role", actor, "owner", repo]]);

    // Different evaluation modes
    const exists = await query.evaluate();                           // Boolean
    const actions = await query.evaluate(action);                    // Single variable
    const pairs = await query.evaluate([action, repository]);        // Tuple variables
    const mapped = await query.evaluate(new Map([[repository, action]])); // Map
    ```

    **Signature:** `oso.buildQuery([predicate, ...args])`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Basic query building
    query = oso.build_query(("allow", actor, "read", repository))
    result = query.evaluate(repository)

    # Add constraints with 'and_'
    constrained_query = oso.build_query(("allow", actor, "read", repository)) \
        .and_(("has_relation", repository, "folder", folder))

    # Filter with 'in_' clause
    filtered_query = oso.build_query(("allow", actor, action, repository)) \
        .in_(repository, repositories)

    # Add context facts
    context_query = oso.build_query(("allow", actor, "read", repository)) \
        .with_context_facts([("has_role", actor, "owner", repo)])

    # Different evaluation modes
    exists = query.evaluate()                              # Boolean
    actions = query.evaluate(action)                       # Single variable
    pairs = query.evaluate((action, repository))           # Tuple variables
    mapped = query.evaluate({repository: action})          # Dictionary
    ```

    **Signature:** `oso.build_query((predicate, *args))`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Basic query building
    query := osoClient.BuildQuery(oso.NewQueryFact("allow", actor, oso.String("read"), repository))
    repos, err := query.EvaluateValues(repository)

    // Add constraints with 'And'
    constrainedQuery := osoClient.BuildQuery(oso.NewQueryFact("allow", actor, oso.String("read"), repository)).
        And(oso.NewQueryFact("has_relation", repository, oso.String("folder"), folder))

    // Filter with 'In' clause
    filteredQuery := osoClient.BuildQuery(oso.NewQueryFact("allow", actor, action, repository)).
        In(repository, repositories)

    // Add context facts
    contextQuery := osoClient.BuildQuery(oso.NewQueryFact("allow", actor, oso.String("read"), repository)).
        WithContextFacts([]oso.Fact{oso.NewFact("has_role", actor, oso.String("owner"), repo)})

    // Different evaluation modes
    allowed, err := query.EvaluateExists()                                    // Boolean
    actions, err := query.EvaluateValues(action)                             // Values for variable
    pairs, err := query.EvaluateCombinations([]oso.Variable{action, repo})   // Variable combinations
    err = query.Evaluate(&repoActions, map[oso.Variable]oso.Variable{repository: action}) // Custom mapping
    ```

    **Signature:** `osoClient.BuildQuery(queryFact)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Basic query building
    QueryBuilder query = oso.buildQuery("allow", actor, action, repository);
    List<String> results = query.evaluate(EvaluateArgs.values(repository));

    // Add constraints with 'and'
    QueryBuilder constrainedQuery = oso.buildQuery("allow", actor, action, repository)
        .and("has_relation", repository, new Value("folder"), folder);

    // Filter with 'in' clause
    QueryBuilder filteredQuery = oso.buildQuery("allow", actor, actionVar, repositoryVar)
        .in(repositoryVar, repositoryIds);

    // Add context facts
    QueryBuilder contextQuery = oso.buildQuery("allow", actor, action, repository)
        .withContextFacts(contextFacts);

    // Different evaluation modes
    Boolean allowed = query.evaluate(EvaluateArgs.exists());                           // Boolean
    List<String> actions = query.evaluate(EvaluateArgs.values(actionVar));            // Values
    Map<String, List<String>> mapped = query.evaluate(                                // Map
        EvaluateArgs.map(repositoryVar, EvaluateArgs.values(actionVar))
    );
    ```

    **Signature:** `oso.buildQuery(predicate, ...args)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Basic query
    oso-cloud query allow User:bob read Repository:_

    # Query with wildcards
    oso-cloud query allow User:admin read _

    # With context facts
    oso-cloud query \
      -c "has_permission User:bob write Repository:anvils" \
      User:bob write Repository:_
    ```

    **Signature:** `oso-cloud query <predicate> (<actor|resource>)*`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Ruby has a simpler query method
    results = oso.query("allow", user, "read", OsoCloud::Value.new(type: "Repository"))
    admin_results = oso.query("allow", admin_user, "read", nil)

    # With context facts
    results = oso.query(
      "allow", 
      user, 
      "write", 
      OsoCloud::Value.new(type: "Repository"),
      context_facts: [["has_permission", user, "write", repo]]
    )
    ```

    **Signature:** `oso.query(rule, context_facts: nil)`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // .NET has a simpler query method
    var results = await oso.Query("allow", new List<Value> { 
      user, 
      new Value("String", "read"), 
      new Value("Repository", null) 
    });

    var adminResults = await oso.Query("allow", new List<Value> { 
      admin_user, 
      new Value("String", "read"), 
      new Value(null, null) 
    });
    ```

    **Signature:** `oso.Query(rule)`
  </CodeTab>
</CodeTabs>
