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

# Facts Management

> CRUD operations for managing facts.

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>;
};

## Insert Facts

Add new facts to your authorization data.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Insert a single fact
    await oso.insert([
      "has_role", 
      { type: "User", id: "bob" }, 
      "owner", 
      { type: "Organization", id: "acme" }
    ]);

    // Insert with variables for reuse
    const user = { type: "User", id: "alice" };
    const repo = { type: "Repository", id: "anvils" };
    await oso.insert(["has_role", user, "maintainer", repo]);
    ```

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

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    from oso_cloud import Value

    # Insert a single fact
    oso.insert((
      "has_role", 
      Value("User", "bob"), 
      "owner", 
      Value("Organization", "acme")
    ))

    # Insert with variables for reuse
    user = Value("User", "alice")
    repo = Value("Repository", "anvils")
    oso.insert(("has_role", user, "maintainer", repo))
    ```

    **Signature:** `oso.insert((name, *args))`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    import oso "github.com/osohq/go-oso-cloud/v2"

    // Insert a single fact
    user := oso.NewValue("User", "bob")
    role := oso.String("owner")
    resource := oso.NewValue("Organization", "acme")
    err := osoClient.Insert(oso.NewFact("has_role", user, role, resource))
    if err != nil {
        log.Fatal(err)
    }

    // Insert with reusable variables
    alice := oso.NewValue("User", "alice")
    repo := oso.NewValue("Repository", "anvils")
    err = osoClient.Insert(oso.NewFact("has_role", alice, oso.String("maintainer"), repo))
    ```

    **Signature:** `osoClient.Insert(fact)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Insert a single fact
    oso.insert(new Fact(
      "has_role", 
      new Value("User", "bob"), 
      new Value("owner"), 
      new Value("Organization", "acme")
    ));

    // Insert with variables for reuse
    Value user = new Value("User", "alice");
    Value repo = new Value("Repository", "anvils");
    oso.insert(new Fact("has_role", user, new Value("maintainer"), repo));
    ```

    **Signature:** `oso.insert(Fact)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Insert a single fact
    oso.tell(
      "has_role", 
      OsoCloud::Value.new(type: "User", id: "bob"), 
      "owner", 
      OsoCloud::Value.new(type: "Organization", id: "acme")
    )

    # Insert with variables for reuse
    user = OsoCloud::Value.new(type: "User", id: "alice")
    repo = OsoCloud::Value.new(type: "Repository", id: "anvils")
    oso.tell("has_role", user, "maintainer", repo)

    # Bulk insert multiple facts
    oso.bulk_tell([
      ["has_role", user, "owner", org],
      ["has_role", user, "maintainer", repo]
    ])
    ```

    **Signature:** `oso.tell(name, *args)` | `oso.bulk_tell([*[name, *args]])`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Insert a single fact
    await oso.Tell("has_role", new List<Value> { 
      new Value("User", "bob"), 
      new Value("String", "owner"), 
      new Value("Organization", "acme") 
    });

    // Insert with variables for reuse
    var user = new Value("User", "alice");
    var repo = new Value("Repository", "anvils");
    await oso.Tell("has_role", new List<Value> { user, new Value("String", "maintainer"), repo });

    // Bulk insert multiple facts
    await oso.BulkTell(new List<Fact> { 
      new Fact("has_role", new List<Value> { user, new Value("String", "owner"), org }),
      new Fact("has_role", new List<Value> { user, new Value("String", "maintainer"), repo })
    });
    ```

    **Signature:** `oso.Tell(name, args)` | `oso.BulkTell(facts)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Insert a single fact
    oso-cloud tell has_role User:bob "owner" Organization:acme

    # Insert with different resource types
    oso-cloud tell has_role User:alice "maintainer" Repository:anvils
    oso-cloud tell has_permission User:charlie "read" Issue:123
    ```

    **Signature:** `oso-cloud tell <predicate> ([<type>:]<id>)*`
  </CodeTab>
</CodeTabs>

## Delete Facts

Remove facts from your authorization data using exact matches or patterns.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Delete specific fact
    await oso.delete([
      "has_role", 
      { type: "User", id: "bob" }, 
      "maintainer", 
      { type: "Repository", id: "anvils" }
    ]);

    // Delete using patterns (null = wildcard)
    await oso.delete(["has_role", { type: "User", id: "bob" }, null, null]); // All roles for bob
    await oso.delete(["has_role", null, "admin", null]); // All admin roles
    ```

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

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    from oso_cloud import Value, ValueOfType

    # Delete specific fact
    oso.delete((
      "has_role", 
      Value("User", "bob"), 
      "maintainer", 
      Value("Repository", "anvils")
    ))

    # Delete using patterns
    oso.delete(("has_role", Value("User", "bob"), None, None))  # All roles for bob
    oso.delete(("has_role", Value("User", "bob"), None, ValueOfType("Repository")))  # All repo roles for bob
    ```

    **Signature:** `oso.delete((name, *args))`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Delete specific fact
    err := osoClient.Delete(oso.NewFact(
      "has_role", 
      oso.NewValue("User", "bob"), 
      oso.String("maintainer"), 
      oso.NewValue("Repository", "anvil")
    ))

    // Delete using patterns
    err = osoClient.Delete(oso.NewFactPattern(
      "has_role", 
      oso.NewValue("User", "alice"), 
      nil,  // Any role
      oso.NewValueOfType("Repo")  // Any repo
    ))
    ```

    **Signature:** `osoClient.Delete(pattern)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Delete specific fact
    oso.delete(new Fact(
      "has_role", 
      user, 
      new Value("maintainer"), 
      repo
    ));

    // Delete using patterns
    oso.delete(new FactPattern(
      "has_role", 
      user, 
      ValuePattern.ANY,  // Any role
      ValuePattern.ANY   // Any resource
    ));

    oso.delete(new FactPattern(
      "has_role", 
      user, 
      new Value("member"), 
      new ValuePattern.ValueOfType("Organization")  // Any organization
    ));
    ```

    **Signature:** `oso.delete(Fact/FactPattern)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Delete specific fact
    oso.delete("has_role", user, "maintainer", repo)

    # Delete using patterns (nil = wildcard)
    oso.delete("has_role", user, nil, repo)  # All roles for user on repo

    # Bulk delete multiple facts
    oso.bulk_delete([
      ["has_role", user, "owner", org],
      ["has_role", user, "maintainer", repo]
    ])
    ```

    **Signature:** `oso.delete(name, *args)` | `oso.bulk_delete([*[name, *args]])`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Delete specific fact
    await oso.Delete("has_role", new List<Value> { user, new Value("String", "maintainer"), repo });

    // Delete using patterns (null = wildcard)
    await oso.Delete("has_role", new List<Value> { 
      new Value("User", null),     // Any user
      new Value("String", null),   // Any role
      repo 
    });

    // Bulk delete multiple facts
    await oso.BulkDelete(new List<Fact> { fact1, fact2 });
    ```

    **Signature:** `oso.Delete(name, args)` | `oso.BulkDelete(facts)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Delete specific fact
    oso-cloud delete has_role User:bob "maintainer" Repository:anvil

    # Delete using wildcards (_)
    oso-cloud delete has_role User:_ _ Repository:anvil  # All user roles on anvil repo
    oso-cloud delete has_role User:bob _ _              # All roles for bob
    ```

    **Signature:** `oso-cloud delete <predicate> ({_, <type>}:{_, <id>})*`
  </CodeTab>
</CodeTabs>

## Query Facts

Retrieve facts from your authorization data using exact matches or patterns.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Get specific fact
    const facts = await oso.get([
      "has_role", 
      { type: "User", id: "bob" }, 
      "admin", 
      { type: "Repository", id: "anvils" }
    ]);

    // Query with patterns
    const allRepoRoles = await oso.get([
      "has_role", 
      null,  // Any user
      null,  // Any role
      { type: "Repository", id: "anvils" }
    ]);

    console.log("Facts found:", facts.length);
    ```

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

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Get specific fact
    facts = oso.get((
      "has_role", 
      Value("User", "bob"), 
      "admin", 
      Value("Repository", "anvils")
    ))

    # Query with patterns
    all_repo_roles = oso.get((
      "has_role", 
      None,  # Any user
      None,  # Any role
      Value("Repository", "anvils")
    ))

    # Query by type
    repo_roles = oso.get((
      "has_role", 
      None, 
      None, 
      ValueOfType("Repository")  # Any repository
    ))

    print(f"Facts found: {len(facts)}")
    ```

    **Signature:** `oso.get((name, *args))`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Get specific fact
    facts, err := osoClient.Get(oso.NewFact(
      "has_role", 
      user, 
      oso.String("admin"), 
      repo
    ))

    // Query with patterns
    allRepoRoles, err := osoClient.Get(oso.NewFactPattern(
      "has_role", 
      nil,  // Any user
      nil,  // Any role
      repo
    ))

    // Query by type
    repoRoles, err := osoClient.Get(oso.NewFactPattern(
      "has_role", 
      nil, 
      nil, 
      oso.NewValueOfType("Repository")
    ))

    fmt.Printf("Facts found: %d\n", len(facts))
    ```

    **Signature:** `osoClient.Get(pattern)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Get specific fact
    Fact[] specificFacts = oso.get(new FactPattern(
      "has_role", 
      user, 
      new Value("admin"), 
      repo
    ));

    // Query with patterns
    Fact[] userRoles = oso.get(new FactPattern(
      "has_role", 
      user, 
      ValuePattern.ANY,  // Any role
      ValuePattern.ANY   // Any resource
    ));

    Fact[] repoRoles = oso.get(new FactPattern(
      "has_role", 
      new ValuePattern.ValueOfType("User"),  // Any user
      ValuePattern.ANY, 
      repo
    ));

    System.out.println("Facts found: " + specificFacts.length);
    ```

    **Signature:** `oso.get(FactPattern)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Get specific fact
    facts = oso.get("has_role", user, "admin", repo)

    # Query with patterns (nil = wildcard)
    all_user_roles = oso.get("has_role", user, nil, nil)  # All roles for user
    repo_roles = oso.get("has_role", nil, nil, repo)      # All roles on repo

    puts "Facts found: #{facts.length}"
    ```

    **Signature:** `oso.get(name, *args)`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Get specific fact
    var facts = await oso.Get("has_role", new List<Value> { user, new Value("String", "admin"), repo });

    // Query with patterns
    var allUserRoles = await oso.Get("has_role", new List<Value> { 
      user, 
      new Value("String", null),  // Any role
      new Value(null, null)       // Any resource
    });

    Console.WriteLine($"Facts found: {facts.Count}");
    ```

    **Signature:** `oso.Get(name, args)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Get specific facts
    oso-cloud get has_role _ "maintainer" Repository:_

    # Query all facts for a user
    oso-cloud get has_role User:bob _ _

    # Inspect all facts for an object
    oso-cloud inspect User:bob
    ```

    **Signature:** `oso-cloud get {_, <predicate>} ({_, <type>}:{_, <id>})*`
  </CodeTab>
</CodeTabs>

## Batch Operations

Perform multiple fact operations atomically for better performance and consistency.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Batch multiple operations
    await oso.batch((tx) => {
      // Insert new facts
      tx.insert(["has_role", user, "owner", org]);
      tx.insert(["has_permission", user, "admin", repo]);
      
      // Delete old facts
      tx.delete(["has_role", user, "maintainer", repo]);
      tx.delete(["has_role", user, "member", org]);
    });
    ```

    **Signature:** `oso.batch((tx) => { })`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Batch multiple operations
    with oso.batch() as tx:
        # Insert new facts
        tx.insert(("has_role", user, "owner", org))
        tx.insert(("has_permission", user, "admin", repo))
        
        # Delete old facts
        tx.delete(("has_role", user, "maintainer", repo))
        tx.delete(("has_role", user, "member", org))
    ```

    **Signature:** `oso.batch()`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Batch multiple operations
    err := osoClient.Batch(func(tx oso.BatchTransaction) {
        // Insert new facts
        tx.Insert(oso.NewFact("has_role", user, oso.String("owner"), org))
        tx.Insert(oso.NewFact("has_permission", user, oso.String("admin"), repo))
        
        // Delete old facts
        tx.Delete(oso.NewFact("has_role", user, oso.String("maintainer"), repo))
        tx.Delete(oso.NewFactPattern("has_role", user, nil, org))
    })
    ```

    **Signature:** `osoClient.Batch(fn)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Batch multiple operations
    oso.batch(tx -> {
        // Insert facts from collections
        factsToInsert.forEach(tx::insert);
        
        // Delete specific facts
        factsToDelete.forEach(tx::delete);
        
        // Delete using patterns
        tx.delete(patternToDelete);
    });
    ```

    **Signature:** `oso.batch(Consumer<BatchTransaction>)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Batch operations using bulk methods
    oso.bulk(
      delete: [
        ["has_role", user, nil, repo],      # Delete all user roles on repo
        ["has_role", user, "owner", org]    # Delete specific role
      ],
      tell: [
        ["has_role", user, "member", repo], # Insert new role
        ["has_permission", user, "read", repo] # Insert permission
      ]
    )
    ```

    **Signature:** `oso.bulk(delete: [*facts], tell: [*facts])`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Batch operations
    await oso.Bulk(
      delete: new List<Fact> { deleteFactPattern },
      tell: new List<Fact> { 
        new Fact("has_role", new List<Value> { user, new Value("String", "member"), repo }),
        new Fact("has_permission", new List<Value> { user, new Value("String", "read"), repo })
      }
    );
    ```

    **Signature:** `oso.Bulk(delete, tell)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Sync data from configuration file
    oso-cloud reconcile config.yaml

    # Perform updates with timeout
    oso-cloud reconcile --perform-updates --timeout 1200 config.yaml

    # Get summarized output
    oso-cloud reconcile --summarized-output config.yaml
    ```

    **Signature:** `oso-cloud reconcile <config-file>`
  </CodeTab>
</CodeTabs>
