# ibl.ai Documentation ## Technical documentation for the ibl.ai Agentic AI Operating System organization: ibl.ai type: Agentic AI Operating System website: https://ibl.ai documentation: https://ibl.ai/docs api_docs: https://ibl.ai/docs/api-reference github: https://github.com/iblai generated: 2026-04-16 total_pages: 127 description: | Complete technical documentation for ibl.ai — an Agentic AI Operating System that organizations deploy, customize, and control on their own infrastructure. Covers platform setup, configuration, LTI integrations, API reference, AI agent creation, MCP servers, and administration. Used by 1.6M+ users across 400+ organizations. deployment_models: SaaS, on-premise, private cloud (AWS/Azure/GCP), air-gapped compliance: SOC2, FERPA llm_support: OpenAI, Anthropic, Google, Meta Llama, DeepSeek, Mistral, and any OpenAI-compatible API --- ## [ibl.ai](https://ibl.ai) URL: https://ibl.ai/docs/about # [ibl.ai](https://ibl.ai) ## Who we are We're all-in-one web and mobile AI tools for faculty support and student success: tutoring, content creation and automation. Full transparency, help-desk and training. Proven by the best universities and companies. Based in New York, NY and family-owned. ## Get started with mentorAI Get started with [mentorAI](https://mentorai.iblai.app) by chatting with an extensive catalog of mentors or create your own. You'll also be able to create your mentors and display them on external sites like [Syracuse University](https://its.syr.edu/) and [Fordham University](https://fordham.edu/undergraduate-admission/apply/what-were-looking-for/). Explore SaaS or enterprise partnerships at [ibl.ai/pricing](https://ibl.ai/pricing). ## Partnerships [Google ISV Partner](https://cloud.google.com/find-a-partner/partner/ibl-education) [Microsoft ISV Partner](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/iblai.mentorai?tab=Overview) [AWS ISV Partner](https://aws.amazon.com/marketplace/seller-profile?id=824b8600-b332-417d-b259-550630b627d8) ## Contact our team LinkedIn: [linkedin.com/company/iblai](https://www.linkedin.com/company/iblai) Chief Technology Officer: [Miguel Amigot](https://www.linkedin.com/in/miguelamigot/) Technical Support: [support@iblai.zendesk.com](mailto:support@iblai.zendesk.com) --- ## Credential Creation URL: https://ibl.ai/docs/configuration/badging/credentialcreation Video: https://www.youtube.com/watch?v=qP270PvUlsk # Credential Creation ## Purpose Configure a credential (certificate) for a course so learners automatically earn it upon completion or passing. --- ## Find the Course 1. Navigate to the **Discover** tab or your enrolled courses. 2. Select the course you want to attach a credential to. 3. Click on **Configuration** from the course overview page. --- ## Create a Credential 1. In the credential list, click **Add Credential**. 2. Fill in the fields: - **Name**: e.g., "Course Name Certificate" - **Description**: explain what the credential is for (e.g., "For passing the course") - **Issuer**: your platform/tenant name - **Credential type**: choose from: - Micro Credential - Certificate - Program Certificate - Pathway - Course Certificate - **Issuing signal**: when the credential is triggered: - **Course Completed** — issued at 100% completion - **Course Pass** — issued when the learner meets or exceeds the grading threshold - **Icon image** (optional): upload a custom icon 3. Click **Create**. --- ## Manage Credentials - Created credentials appear in the credential list on the Configuration page - You can **edit** or **delete** credentials as needed - Each course can have multiple credentials with different trigger conditions --- ## Key Takeaways - **Course Completed** triggers at 100% completion regardless of grade - **Course Pass** triggers based on the course's grading policy threshold - The **issuer** is automatically set to your platform/tenant - Credentials are issued to learners automatically when the trigger condition is met --- ## External Credentials URL: https://ibl.ai/docs/configuration/badging/externalcredentials Video: https://www.youtube.com/watch?v=ongBQqb_KIM # External Credentials ## Purpose Configure and manage external credential providers (e.g., badging vendors) and map your platform-issued credentials to those providers so credentials are issued externally while remaining managed within your platform. --- ## Where to Find It 1. Click your **tenant name**. 2. Go to **Advanced**. 3. Scroll to the bottom to find: - **Provider Configuration** - **Credential Mapping** --- ## Provider Configuration Use this to **add, edit, enable/disable, or delete** an external provider. ### Add or Edit a Provider 1. Open **Provider Configuration**. 2. Enter: - **Provider name** - **Configuration** (provider-specific settings) - **Enabled** (toggle on/off) 3. Click **Save**. ### Notes - Editing looks the same as adding, except fields are **pre-filled**. - You can **delete providers** you no longer need. - **Enabled providers** appear in the mapping step. --- ## Map Credentials to an External Provider Once a provider is configured, map platform credentials to the provider’s templates. ### Create the Mapping 1. Open **Credential Mapping**. 2. Select: - **Credential** - From the list created in your platform (typically from the course overview page) - **Provider** - **External Template ID** - The credential/template ID from the provider 3. *(Optional)* Add: - **Group ID** - **Additional metadata** 4. Click **Save**. ### What’s Editable - **Credential name** and **provider** are fixed after creation. - Optional fields (e.g., **group ID**, **metadata**, **external template ID**) can be added or updated. --- ## How Issuance Works - The credential is **created and managed** in your platform. - The mapping links it to the **external provider’s template**. - When issued, the credential is **issued by the external provider** using that mapping. --- ## Result You can self-manage **external credential providers** and seamlessly issue credentials through them—**without duplicating workflows or leaving the platform**. ### Transcript skillsAI | External Credentials https://www.youtube.com/watch?v=ongBQqb_KIM [1.4s] In order to self-manage credentials from [3.5s] external providers, we've added new [6.1s] settings to the advanced settings for [8.1s] the tenant. So, when you click on your [9.9s] tenant name, you'll navigate to this [11.9s] advanced tab. Now, at the very bottom, [15.1s] you'll see two new features. We have the [18.7s] provider configuration, which allows you [20.3s] to configure a specific provider um that [23.3s] you can connect with your credentials. [25.4s] Now, you can see that we've already got [26.6s] this set up for a credible, and I'll [28.3s] show you what the editing process and [30.3s] creation process looks like. Um, but I [32.7s] won't show you the credentials that are [34.0s] currently in place. You're also able to [36.3s] delete these as well. But the way this [39.0s] works when you're adding a provider [41.0s] configuration, it'll look the same when [42.4s] you're editing as well, just the only [44.1s] difference will be the adding is not [46.8s] filled out and editing will be filled [48.5s] out with what you've already added in. [50.8s] So you have to enter the provider name, [53.0s] the configuration [54.9s] um and decide whether or not this is [56.5s] enabled. Now once this is created, it'll [59.1s] show up on this list and we can actually [62.0s] map credentials now to the credentials [65.2s] created on our platform. So as you know [68.2s] you'll create a credential from the [69.4s] course overview pages. Um that is a [71.6s] credential that lives in our platform. [73.8s] It is stored with us but we want to [76.1s] connect it to an external provider. So [78.5s] the way you would do this is adding and [80.6s] you can see there's a whole list of [81.8s] credentials that have already been [83.0s] created and you can just select you know [84.6s] which credential you'd like. Select the [86.9s] provider and the external template ID [89.2s] that comes from the provider side. So [91.1s] this is a credential ID that says you [93.4s] know this credential belongs to this [95.2s] course. You also have the option of [97.8s] adding a group ID in and additional [100.4s] metadata. [102.0s] Now just to kind of give you an idea of [103.5s] what this looks like for a filled out [104.7s] credential. Um you can see we've given [106.6s] it the credential name in this case it [108.3s] matches exactly what the course name is [110.2s] and the provider. Um these two are not [112.9s] going to be editable um when you create [115.0s] these credential mappings [117.3s] but the other options are especially [119.3s] because they are optional fields. You [121.2s] can add additional metadata like I said [123.8s] um adding group IDs and external [125.8s] template ids. And what this does is [128.3s] essentially connects the template uh [130.5s] credential that you created in our [132.1s] platform on skills um via that course [135.8s] overview page [137.8s] um and connect it to the external [139.7s] mapping. So when the credential gets [141.1s] issued, it's actually coming from this [143.0s] external provider. And [146.2s] that's how easy it is. --- ## DM Creation URL: https://ibl.ai/docs/configuration/content/Courses/dmcreation Video: https://www.youtube.com/watch?v=rtGp3lyulFs # DM Creation ## Where to Start - Sign in as a **Super Admin**. - Open **Data Manager → Catalog App**. - You’ll import both **SCORM** and **video courses** from here. --- ## Prepare the CSV - Click **Import** to see a minimalist upload screen. - Download the **sample CSV** to review required fields: - `platform_key`, `course_name`, `section`, `subsection`, `unit`, `tags`, etc.—the same fields used in the single-course creation wizard. - Populate the CSV with one row per course you want to add. --- ## Import SCORM Courses - In **Catalog → Courses**, press **Import**. - Choose **File** and select your completed CSV. - Click **Submit**. - A **validation dialog** appears—review the parsed rows. - Click **Confirm Import** if everything looks correct. - If any row contains errors (e.g., duplicate course), the system flags it and cancels that row; fix and re-upload as needed. - A **success message** lists every course that was added. --- ## Import Video Courses - In **Catalog → Course Videos**, click **Import**. - Download the **sample CSV** to confirm field names (they’re shown on-screen). - Fill in the CSV, choose it, and run **Submit → Confirm Import** just like SCORM. - Success or error feedback appears immediately. --- ## Locate Imported Courses - After import, all new **courses and videos** appear in the **Catalog App**. - They behave exactly like any manually created course; no further setup is required. --- ## Learner View Check - Switch to the **Skills** front-end. - Open **Discover**. - Search for a course title from your CSV (e.g., SC B Upload). - The imported course is **visible and enrollable**, confirming a successful bulk upload. --- ## Error Handling Tips - Re-uploading an identical CSV row triggers a **“course already exists”** error—update the row or remove duplicates. - Always **validate before confirming**; the dialog catches missing or malformed fields. --- ## Key Takeaways - **Bulk import** is only for **SCORM** and **video course** types. - Use the **sample CSV** as your template and keep field names intact. - **Validation** ensures you don’t create duplicates or malformed entries. - Imported items instantly populate the **Catalog** and **Discover** views—no extra publishing step. With these steps you can mass-create courses in minutes, freeing super admins from repetitive single-course setup. --- ## Studio Creation URL: https://ibl.ai/docs/configuration/content/Courses/studiocreation Video: https://www.youtube.com/watch?v=kBY42Mkw5SY # Studio Creation ## Navigate to Your Course - Open **Studio** and head to **Courses**. - Select the course you want to edit (one already exists in this demo). --- ## Understand the edX Course Structure - **Section** - **Subsection** - **Unit** ← where all content lives (lectures, problems, videos, etc.) --- ## Add or Edit an Assessment in a Unit - Expand the course outline until you reach the target **Unit** (e.g., Module 3 → Assessment). - Inside the unit, click **Problems → Multiple Choice → Edit**. - Use the built-in template to create a question: - Delete **placeholder text**. - Paste your own **stem** and **answer choices**. - Click the **X** beside the correct answer. - Click **Save**. - The question renders instantly in the **unit preview**. --- ## Publish the Content - When the unit is complete, click **Publish**. - The material becomes live if the **course start date** is today or earlier. - If the start date is in the future, learners won’t see the unit until that date. --- ## Next Steps: Mapping Roles and Skills - After publishing, you can link this course (and its assessments) to **Roles and Skills** to map competencies—covered in the following step of the workflow. You’ve authored and published a multiple-choice assessment in Studio and prepared the course for competency mapping. --- ## ⁠Wizard Creation URL: https://ibl.ai/docs/configuration/content/Courses/wizardcreation Video: https://www.youtube.com/watch?v=WDeBBuDwqs8 # ⁠Wizard Creation ## Launch the Course-Creation Wizard - Log in as a **Tenant Admin**. - Go to **Authoring → Courses**. - Click **Add New Course**. - Instead of opening Studio directly, a **step-by-step wizard** appears. - You can still enter **Studio** later for advanced edits. --- ## Fill In Course Details (Page 1) - **Name** – the course title - **Description** – a brief summary (can match the name for testing) - **Duration** – estimated learning time - **Course Card Image** – upload an image learners will see in the Catalog - **Topics / Tags** – add keywords; press Enter after each one - Click **Next** when finished. - **Start dates** are auto-set behind the scenes, so you don’t have to manage them now. --- ## Build the Outline & Add Content (Page 2) - Define the initial structure: - **Section** (e.g., Section 1) - **Subsection** (e.g., 1.1) - **Unit** (e.g., 1.1.1) - Choose your **content type** for the unit: - **YouTube URL** – paste a link - **SCORM** – upload a SCORM package directly - Click **Submit**. - **Upload time varies**; SCORM files may take longer than a URL. --- ## Confirm Creation - After processing, the new course appears in **Analytics → Courses**. - From the **Analytics** listing you can: - Open **Studio** for deeper edits - Add **credentials or skill points** - **Enroll users** --- ## Verify Learner Access - Switch to the **Skills** front-end. - Open **Discover**. - Search for the course name. - The **course card** shows the image you uploaded; click **Access Course** to launch it. - If **SCORM** was included, it loads inside the **course player**. --- ## Roadmap Note - This wizard is the **first iteration**. - Future updates will add **more options** and simplify choices so authors can launch courses even faster. --- You’ve now created a complete course shell, uploaded initial content, and confirmed it’s visible to learners—all without leaving the new tenant-admin wizard. --- ## Studio Creation URL: https://ibl.ai/docs/configuration/content/Programs/studiocreation Video: https://www.youtube.com/watch?v=yzU2Tid16io # Studio Creation ## What a Program Is A **program** is a bundle of individual courses packaged under one umbrella title—ideal for grouping a sequence such as BIO 101, 102, 103, and 104 into a single “Biology” pathway. --- ## Launch the Program Builder - In **Studio**, open **Programs**. - Click **New Program** (green button at the top). --- ## Complete Basic Program Info - **Program Name** – overarching title (e.g., Biology) - **Organization** – the course-creator org (e.g., IBL) - **Program ID** – appears in the learner-facing URL (e.g., biology-ibl) - Click **Create** --- ## Add Courses to the Program - In a separate tab, open each course you want included. - Copy its **Course ID** from the URL (looks like `course-v1:ORG+CODE+RUN`) - Return to the program page and paste the **Course ID** into the field provided. - Repeat for every additional course. - Click **Save** to lock in the list. --- ## Enter Optional Metadata - **Start Date** – back-date to yesterday for immediate access, or schedule a future launch - **Description** – short blurb shown on the program’s about page - **Language** – primary language of instruction - **Level** – skill level (introductory, intermediate, advanced) - **Display Price** – visible cost if applicable - **Duration** – estimated total learning time --- ## Visual Settings - **Banner Image** – appears on the program about page - **Course Card Image** – thumbnail shown in the Catalog - **Credentials Toggle** – enable if completion should award a credential --- ## Publish & Verify - Click **Save** after each update - Programs become visible in the **Catalog** once the **start date** is reached and at least one course is live - Learners see the assembled bundle with your banner, description, and course list --- ## Key Takeaways - One **“New Program” wizard** bundles multiple existing courses - **Course IDs** are the link between standalone courses and the program - Optional metadata (dates, price, images) shapes the **learner-facing presentation** - Changes save instantly; you can edit the program at any time You’ve now created a fully packaged program that groups related courses into a cohesive learning pathway. --- ## Blackboard URL: https://ibl.ai/docs/configuration/datasources/blackboard Video: https://www.youtube.com/watch?v=k91vOq4CANg # Blackboard ## Purpose Connect a **Blackboard course** as a data source so a mentor can read course content and attached files, cite them, and answer questions from that material. --- ## Part 1 — Install the REST API Integration (Blackboard Admin) 1. In the **Blackboard Developer Panel**, create (or use) an application and copy its **Application ID** (this ID will be shared for installations). 2. In your Blackboard instance, open the **Admin Panel → search REST API Integrations**. 3. Click **Create Integration** and paste the **Application ID**. 4. Choose a user to link to the application (pick one who has access to the course). **Permissions noted in the demo:** - Authorized to act as user: **Not needed** - Any user access: **Yes** 5. Submit. The test application shows as **integrated**. --- ## Part 2 — Add the Blackboard Course as a Data Source (Mentor Platform) 1. In Blackboard, open the **target course** and copy the **course URL** from your browser. 2. In the mentor’s **Datasets**, choose **Blackboard** as the data source. 3. Paste the **course URL** and click **Submit**. 4. The document queues for **training**; once trained, it appears in the list. 5. *(Optional)* Mark it **Visible** so you can see cited content. --- ## How It Works in Chat - The mentor can answer questions from the **course’s text content** and **attached files** (e.g., PDFs). - Source snippets appear, showing exactly **where the answer was drawn from**. **Example (from the demo):** A question about **Las Casas’s writing** returns an answer about Spanish colonists’ treatment of Indigenous people, with snippets pointing to the relevant attached documents/sections. --- ## Auto-Retraining - By default, the Blackboard content is set to **auto-retrain every 7 days**. - You can reschedule this to any number of days. --- ## Result Your mentor now **ingests the specified Blackboard course and its attachments**, cites where answers come from, and stays **up to date via scheduled retraining**. ### Transcript ibl.ai | Blackboard | Course Integration https://www.youtube.com/watch?v=k91vOq4CANg [0.0s] Hello, this is Joseph from iBL.ai and I [3.1s] will be showing our integration to [6.0s] Blackboard. [7.8s] So um over here on the Blackboard [11.3s] developer panel just to showcase created [15.4s] just a sample application over here. Um [18.9s] of course in production this would be [20.6s] different but this is a test [22.8s] application. [24.4s] Now if we have an application like this, [26.4s] all we need here is this application ID [29.5s] which will be shared for installations [32.7s] and then the platform admin um would [36.7s] install this REST API extension into [41.2s] their Blackboard instance. So take for [43.8s] example I am here at Blackboard.ai. [48.2s] I need to go into the admin panel then [51.0s] search for REST API integrations. Let me [54.1s] zoom in. REST API integrations. Okay. [59.3s] Now that I am here, I will click on [61.8s] create an integration and then I would [64.8s] specify [66.3s] um the application ID. Okay. Now I need [71.0s] to specify a user for which I am going [74.3s] to um [77.5s] use. I think my user here will be [80.8s] administrator. Yep, I have my Blackboard [84.2s] administrator account here that I would [87.3s] link to this application. Of course, you [90.7s] could narrow down the permissions you [92.9s] want. In our case, all we need is the [95.4s] ability to read the content of the [97.5s] course and to download any files [100.0s] attached to it. So, best you choose a [102.8s] user who has access to that course. [106.6s] Okay. Um, so authorized to act as the [111.0s] user. We don't need that um any user [113.7s] access. Yes. And then we submit. And [117.0s] with that, we have successfully [119.4s] integrated this test test application. [122.8s] And that's it right here. IVL test. [125.3s] Good. Now, we want to be able to use [128.5s] this test course, a test course from um [134.4s] our system. So, what we would do, we [137.0s] would come on to our Blackboard [140.2s] um panel, search for the course we are [143.4s] trying to set up. Now, just copy the [147.3s] link to this course from the top of my [151.0s] browser here. [152.6s] come back and then navigate into my data [156.3s] set. I am going to load this um in as [162.5s] data source. For this I will choose um [166.1s] Blackboard as my [169.7s] data source here and I will paste the [173.0s] URL to this set course. Okay. Now let's [177.3s] submit this. And yes, the document is Q [179.9s] for training. Oh, I could submit that [182.5s] but no need. Let's um so let's let's go [188.1s] back and then I would um go back here. [192.7s] Oh, it's already trained. That was quite [196.2s] sharp. So you can see it over here and I [200.8s] am marking it as visible just so I can [203.0s] see the contents. Now I can go back. Um, [207.2s] not necessary, but just in case, let me [209.7s] refresh on my screen here. Now, you can [214.7s] check. Let's double check. This is the [217.4s] only content trained on this course. [219.8s] Okay. [221.4s] And [223.0s] we have this course here. It's just a [225.6s] dummy course. Okay. You just open it. [229.2s] There is um a dummy assessment. There is [234.5s] a PDF file attached. Okay. Our main [238.5s] focus will be on the attached files and [241.8s] then text content of the course. Okay. [245.3s] So, we are going to have our agent be [247.5s] able to answer questions based on this [250.4s] information. So let me ask it something [254.5s] um [256.2s] about uh [259.3s] um [261.8s] let me say [267.4s] grab some content here. Uh Lascasa's [270.2s] writing about Spanian's horrific [272.6s] treatment. Um so let me just ask [275.6s] something about this guys statement. [279.7s] Okay. So what did Lascas write [286.2s] about the uh Spaniards [290.2s] pronounced that wrong? [294.7s] And then we see um [299.2s] he wrote about the [301.8s] uh horrific treatment of the indigenous [304.3s] people by Spanish colonists over here. [308.6s] Okay. which if we go back to the horse [312.3s] that is exactly what we see you know [316.0s] about blackboard legend how it treated [319.8s] Indians. [324.0s] Now back here we can also see snippets [328.1s] from the documents attached where this [332.2s] um information were gotten from. Okay. [336.2s] So we can see about um [340.4s] uh the nations yearning to break free [343.2s] from the Spanish influence. We can see [346.6s] uh the snippets about black legends um [350.9s] and more. Okay, so we have all this [353.4s] information that the agent got fed in [356.6s] straight from the Blackboard course [358.4s] without us needing to manually re-upload [361.0s] them. Now this content, okay, can be [365.0s] configured and by default it is [366.9s] configured to auto retrain every 7 days. [370.9s] Okay, you could reshadow it to train in [374.7s] any number of days that you want. And [378.8s] well, I think that's one of the coolest [382.4s] things you could have [385.9s] for your course. Thank you very much. C. --- ## Dropbox URL: https://ibl.ai/docs/configuration/datasources/dropbox Video: https://www.youtube.com/watch?v=5yWOSRc4NiU # Dropbox ## Purpose Link **Dropbox files** to a mentor so its knowledge base can include those materials. --- ## Steps ### Open the Mentor’s Datasets - Click the **mentor’s name** - Select the **Datasets** tab ### Add a Resource from Dropbox - Click **Add Resource** - Pick **Dropbox**, then follow the **connect-and-select flow** ### Train the Mentor - After selecting the file, the mentor can be **trained** on that material --- ## Result The mentor now includes content from **Dropbox** in its knowledge base. ### Transcript mentorAI | Data Sources | Google Drive, Dropbox and Microsoft OneDrive https://www.youtube.com/watch?v=5yWOSRc4NiU [1.1s] When you're creating a mentor on mentor [3.0s] AI, you define the knowledge base that [5.5s] mentor is trained on. You do this by [7.4s] uploading documents, uh files, links, so [10.5s] on and so forth. But you can also [12.2s] actually use a couple of different uh [14.2s] file hosting options such as Google [15.9s] Drive, uh Microsoft, uh one drive, and [19.7s] Dropbox. Now, to do this, you'll have to [22.1s] connect your accounts. And to get that [24.3s] set up, all you have to do is click on [26.5s] the mentor name and then click the data [28.5s] sets tab. Once you're here, you'll be [31.3s] taken to a screen that looks like this. [32.9s] And you'll hit the add resource button. [35.0s] We'll start with Google Drive. [39.4s] And you can see here it'll pop up a [41.1s] little thing to connect my account. I'll [43.7s] select that. [48.6s] Now you can select any file from your [50.3s] Google Drive folder. It can even be a [52.4s] folder, uh, a specific document, so on [54.6s] and so forth. Now, to connect to one [59.7s] drive, you'll hit the same button, add [62.3s] resource, but instead of clicking Google [64.6s] Drive, you'll do one drive from [65.8s] Microsoft. [69.4s] It'll take you [72.1s] to your drive if you've already [74.4s] connected it. And if you haven't [76.2s] connected it, it'll take you through a [77.4s] series of prompts to log in and confirm [79.4s] you're allowing mentor AI to access your [81.8s] materials. You'll just select a file [83.8s] from here and then you can train your [86.5s] mentor on that file. And yeah, that's [89.0s] basically all you need to do to connect [90.6s] to your drives and upload materials from [93.1s] there. --- ## Google Drive URL: https://ibl.ai/docs/configuration/datasources/googledrive Video: https://www.youtube.com/watch?v=5yWOSRc4NiU # Google Drive ## Purpose Link **Google Drive files or folders** to a mentor so its knowledge base can include those materials. --- ## Steps ### Open the Mentor’s Datasets - Click the **mentor’s name** - Select the **Datasets** tab ### Add a Resource from Google Drive - Click **Add Resource** - Choose **Google Drive** - When prompted, **connect your Google account** and follow the steps - After connecting, pick any **file or folder** in your Drive ### Train the Mentor - After selecting the file or folder, the mentor can be **trained** on that material --- ## Result The mentor now includes content from **Google Drive** in its knowledge base. ### Transcript mentorAI | Data Sources | Google Drive, Dropbox and Microsoft OneDrive https://www.youtube.com/watch?v=5yWOSRc4NiU [1.1s] When you're creating a mentor on mentor [3.0s] AI, you define the knowledge base that [5.5s] mentor is trained on. You do this by [7.4s] uploading documents, uh files, links, so [10.5s] on and so forth. But you can also [12.2s] actually use a couple of different uh [14.2s] file hosting options such as Google [15.9s] Drive, uh Microsoft, uh one drive, and [19.7s] Dropbox. Now, to do this, you'll have to [22.1s] connect your accounts. And to get that [24.3s] set up, all you have to do is click on [26.5s] the mentor name and then click the data [28.5s] sets tab. Once you're here, you'll be [31.3s] taken to a screen that looks like this. [32.9s] And you'll hit the add resource button. [35.0s] We'll start with Google Drive. [39.4s] And you can see here it'll pop up a [41.1s] little thing to connect my account. I'll [43.7s] select that. [48.6s] Now you can select any file from your [50.3s] Google Drive folder. It can even be a [52.4s] folder, uh, a specific document, so on [54.6s] and so forth. Now, to connect to one [59.7s] drive, you'll hit the same button, add [62.3s] resource, but instead of clicking Google [64.6s] Drive, you'll do one drive from [65.8s] Microsoft. [69.4s] It'll take you [72.1s] to your drive if you've already [74.4s] connected it. And if you haven't [76.2s] connected it, it'll take you through a [77.4s] series of prompts to log in and confirm [79.4s] you're allowing mentor AI to access your [81.8s] materials. You'll just select a file [83.8s] from here and then you can train your [86.5s] mentor on that file. And yeah, that's [89.0s] basically all you need to do to connect [90.6s] to your drives and upload materials from [93.1s] there. --- ## Microsoft OneDrive URL: https://ibl.ai/docs/configuration/datasources/microsoftonedrive Video: https://www.youtube.com/watch?v=5yWOSRc4NiU # Microsoft OneDrive ## Purpose Link **Microsoft OneDrive files** to a mentor so its knowledge base can include those materials. --- ## Steps ### Open the Mentor’s Datasets - Click the **mentor’s name** - Select the **Datasets** tab ### Add a Resource from OneDrive - Click **Add Resource** - Select **OneDrive (Microsoft)** - If already connected, your drive opens; otherwise, **log in and approve access** - Choose the desired **file** ### Train the Mentor - After selecting the file, the mentor can be **trained** on that material --- ## Result The mentor now includes content from **OneDrive** in its knowledge base. ### Transcript mentorAI | Data Sources | Google Drive, Dropbox and Microsoft OneDrive https://www.youtube.com/watch?v=5yWOSRc4NiU [1.1s] When you're creating a mentor on mentor [3.0s] AI, you define the knowledge base that [5.5s] mentor is trained on. You do this by [7.4s] uploading documents, uh files, links, so [10.5s] on and so forth. But you can also [12.2s] actually use a couple of different uh [14.2s] file hosting options such as Google [15.9s] Drive, uh Microsoft, uh one drive, and [19.7s] Dropbox. Now, to do this, you'll have to [22.1s] connect your accounts. And to get that [24.3s] set up, all you have to do is click on [26.5s] the mentor name and then click the data [28.5s] sets tab. Once you're here, you'll be [31.3s] taken to a screen that looks like this. [32.9s] And you'll hit the add resource button. [35.0s] We'll start with Google Drive. [39.4s] And you can see here it'll pop up a [41.1s] little thing to connect my account. I'll [43.7s] select that. [48.6s] Now you can select any file from your [50.3s] Google Drive folder. It can even be a [52.4s] folder, uh, a specific document, so on [54.6s] and so forth. Now, to connect to one [59.7s] drive, you'll hit the same button, add [62.3s] resource, but instead of clicking Google [64.6s] Drive, you'll do one drive from [65.8s] Microsoft. [69.4s] It'll take you [72.1s] to your drive if you've already [74.4s] connected it. And if you haven't [76.2s] connected it, it'll take you through a [77.4s] series of prompts to log in and confirm [79.4s] you're allowing mentor AI to access your [81.8s] materials. You'll just select a file [83.8s] from here and then you can train your [86.5s] mentor on that file. And yeah, that's [89.0s] basically all you need to do to connect [90.6s] to your drives and upload materials from [93.1s] there. --- ## LLMs URL: https://ibl.ai/docs/configuration/llms Video: https://www.youtube.com/watch?v=6_s7E7oB6ds # LLMs ## Why mentorAI Is LLM-Agnostic ![](/images/llms.png) - Let each mentor use the language model best suited to its subject. - **Math mentor**: choose a model optimized for symbolic reasoning and step-by-step solutions. - **English composition mentor**: switch to a model fine-tuned on rhetoric or literary analysis. - Improves clarity, accuracy, and learning outcomes within each domain. - Future-proofs your mentors as specialized models continue to appear. --- ## Two Ways to Open the LLM Selector ### Click the Model Name - In the mentor card, click the current LLM’s name. - The list of all LLM providers appears. ### Use the Mentor Dropdown - Click the mentor’s name. - Select **LLM** from the dropdown menu. - You land on the same provider list. --- ## Choose a Provider and Model - Click a provider (for example, OpenAI or Google). - A list of that provider’s models is displayed. - Select the model you want—e.g., `o1-preview`. - A **“Successfully changed”** notification confirms the switch. - Repeat anytime to pick another provider or model. --- ## Adding New or Custom Models - If a provider or model isn’t visible, it can be added to the platform quickly. - mentorAI stays current by implementing new models as soon as they are released. --- ## Final Step: API Keys (If Required) - Some models may need you to supply your own LLM keys. - Enter the keys when prompted; afterward, the mentor will run on the chosen model. --- That’s all it takes—select, confirm, and your mentor is now powered by the model that best fits its subject. ### Transcript mentorAI | LLM Configuration https://www.youtube.com/watch?v=6_s7E7oB6ds [0.5s] AI is an LLM agnostic platform. Being [3.4s] LLM agnostic lets instructors match the [5.8s] large language model strengths uh to the [8.1s] sub subject matter rather than forcing [10.1s] every single mentor into a [12.3s] one-sizefits-all engine. For example, um [15.2s] a math mentor can tap into a model [17.0s] optimized for symbolic reasoning and [19.0s] step-by-step equation solving, while an [22.0s] English composition mentor um can switch [24.8s] to a model fine-tuned uh on rhetorical [27.2s] structures or literary analysis or both. [30.3s] Um each would be delivering more clear, [32.7s] more accurate feedback within its own [34.2s] domain by selecting an LLM that meets [36.6s] the needs for that specific mentor. Um [39.6s] this flexibility not only lists learning [41.8s] outcomes but also futurep proofs uh you [44.5s] know your mentors with these specialized [46.9s] models. Now to switch your mentors model [50.4s] you have a couple of different ways of [51.7s] doing this. You can select the LLM model [53.8s] here which will pull up the list of LLM [56.3s] providers we currently have. Now we also [59.0s] can go to mentor AI right here which is [61.2s] the mentor we're currently using and [63.4s] select LLM. This will take you to the [65.3s] same page. Now, this is going to show [67.2s] you the providers. Um, now if if there's [70.0s] a provider or a specific LLM model that [71.9s] you don't currently see here, it can [73.4s] very well be added very easily. In fact, [76.2s] we try to keep this updated as soon as a [77.8s] new model comes out. That's what we uh [80.1s] implement into our platform. And now, [83.4s] now we have the providers, but what we [85.1s] can do is open up a select provider. So, [87.7s] I'm going to select OpenAI. And you can [89.6s] see here that there are multitude of [91.4s] models you can select from. So in this [94.2s] case, we can go with 01 preview and you [96.3s] can get a notification down at the [98.2s] bottom that just says, "Hey, this has [99.6s] successfully been changed." Um, and if [102.3s] we wanted to even go forward with a [104.5s] different model, you know, different [106.2s] provider, all of that, we can go to [108.2s] Google and then we can select which one [110.5s] we want. It's super simple to change [111.8s] your model. You don't really have to do [113.8s] much more than that. Now, you might need [116.0s] to provide your LLM keys depending on, [118.7s] you know, whatever we've worked out with [120.5s] you. But, uh, this is how you would [122.9s] switch your LM model. --- ## Administration URL: https://ibl.ai/docs/configuration/lti/administration Video: https://www.youtube.com/watch?v=oGJeqkvaS08 # Administration ## Purpose Control who can integrate and use a mentor via **LTI** from your LMS (e.g., Canvas), and who can see the mentor inside the **mentorAI web app**. --- ## LTI Accessible Toggle (Per-Mentor) 1. Open the mentor’s **Settings**. 2. Find **LTI accessible** and toggle: - **On** → The mentor appears in your LMS’s External Tool / Deep Linking picker and can be added to a course. - **Off** → The mentor does not appear in the LMS picker; any existing LTI link will show an error after refresh. ### Canvas example (when LTI accessible = On): - In a course: **Add External Tool → choose your mentor integration → select the mentor (e.g., AI Socratic Mentor) → Add Item → launch and chat.** - If you later toggle **Off** and **Save**, refreshing the Canvas item shows an **error**, and the mentor is **removed** from the add-list. --- ## “Administrators can view” (Web-app visibility) - If the mentor is marked **admin-only** in the mentorAI web app **and** LTI accessible is **On**: - Other users won’t see the mentor in the web app - But **students can still access it in the LMS** - Use this to keep a mentor **hidden in the web UI** while leaving **LMS access intact**. --- ## Who Sees Mentors in the LMS Picker - **Canvas admins:** - Do **not** need a mentorAI account - See **all mentors** available to the LMS environment (system-admin level) - **Instructors:** - **Must** have a mentorAI account using the **same email** as in the LMS - This filters the LMS picker so instructors only see **mentors they created** --- ## Typical Workflow 1. In mentorAI → open the mentor’s **Settings** → toggle **LTI accessible = On** → **Save**. 2. *(Optional)* Set **Administrators can view** if you want the mentor **hidden in the web app but still usable from the LMS**. 3. In Canvas: - **Add External Tool → choose your mentor integration → select the mentor → Add Item → launch** 4. To **revoke LMS access**: - Toggle **LTI accessible = Off** → **Save** - Existing LMS links **error after refresh** and the mentor **disappears** from the add-list. --- ## Results & Expectations - **On** → mentor appears in LMS picker; launchable in courses. - **Off** → mentor disappears from LMS picker; existing links fail on refresh. - **Admin-only (web) + On (LTI)** → mentor hidden in web app but available to students in LMS. --- Use these controls to manage **LTI visibility and access** without exposing mentors broadly in the web app. ### Transcript mentorAI | Managing LTI Access https://www.youtube.com/watch?v=oGJeqkvaS08 [1.0s] I'm going to be showing you some updates [2.5s] that we've made to LTI to help you [4.6s] manage who can and cannot access your [7.5s] mentors. Now, under the settings of a [9.9s] given mentor that you would like to [11.7s] integrate or prevent people from [13.5s] integrating via LTI, you'll see this [16.0s] little LTI accessible toggle. Now, if [18.7s] this is toggled on, that means we're [20.5s] going to be able to LTI integrate into a [22.6s] given LMS. In this case, we're going to [24.6s] use this Canvas course. And what we'll [27.9s] do is click module. Oop, sorry. We're [30.6s] going to click, you know, just add a new [33.9s] piece to the module with an external [35.8s] tool. I'm going to select the mentor [38.2s] integration. [41.0s] And you can see here that we have this [44.0s] mentor right here, this AI Socratic [45.8s] mentor, which is the one that we're [47.2s] looking at over here. [49.7s] Um, so what I'm going to do is hit [51.8s] select content, [55.0s] add item, [57.1s] and you'll see that once we go to this [59.7s] item, [64.3s] you'll actually be able to chat with the [65.8s] mentor. [67.5s] Now, if that LTI toggle is off, it won't [70.0s] show up in the list of accessible LTI [74.7s] components to add to this course. And [77.0s] I'll show you what that looks like in [78.2s] here in just a second. [80.4s] So, we're going to go back over here. [83.3s] And now what we're going to do is toggle [84.6s] off LTI access. And then we're going to [87.3s] hit save. [89.7s] Now, if we go back to the course over [91.5s] here, we'll refresh. And we should [93.4s] expect to see an error because this is [95.0s] no longer LTI accessible. And there you [97.8s] go. Now, another really cool thing here [100.2s] is that if this is marked as LTI [102.2s] accessible uh and it's marked as [104.7s] administrators can view, this basically [107.4s] prevents other people from seeing your [109.0s] mentor um on this platform, but within [112.4s] the Canvas or LMS environment, you and [115.8s] your users can still chat with it. So, [118.2s] I'm going to hit save here and then [121.2s] refresh. [127.5s] Now you can see this is back up and [129.3s] accessible to me despite this being [131.3s] admin only. This means just basically [134.2s] it's admin only on the web app for [136.4s] mentor AI but your students can still [138.3s] chat with the mentor in Canvas. Now the [141.1s] other thing here is if this is LTI [145.8s] uh if the LTI accessibility is turned [147.7s] off you'll hit save over here again. And [150.5s] then if we're going to add the same [153.8s] mentor again to the course with the [156.5s] external tool setup, [160.5s] you'll actually see that list. That [162.0s] mentor doesn't appear on this list [163.3s] anymore. [165.4s] So that's basically how this is working [167.9s] within a given LMS for managing your [171.1s] users and access to the mentors better. [173.6s] Another thing is that Canvas admins do [177.0s] not need to be registered on our [178.5s] platform to be able to see all available [180.9s] mentors to a given LMS environment as [184.6s] long as they're uh system admins. Now, [187.1s] instructors will need to have an account [189.0s] on our platform that with the same email [191.4s] address that they use in the LMS. And [193.3s] this is just so that we can filter out [195.4s] any mentors that they did not create [197.1s] themselves. They're only seeing the ones [198.5s] that they've created. [200.9s] Back over to mentor AI. [203.8s] just to explain the ultimate end goal [205.8s] here, which is you don't want other [208.1s] instructors to be able to see your [209.3s] mentor from the mentor spa unless you [211.8s] give them that access or um you don't [214.7s] you want your students to be able to [215.8s] access the Canvas course and be able to [218.5s] chat with the mentor. Um and again, [221.0s] you're just preventing people from [222.1s] seeing this on the web app that you [223.3s] don't want seeing it, but you're still [224.3s] giving your students access. --- ## Blackboard URL: https://ibl.ai/docs/configuration/lti/blackboard Video: https://www.youtube.com/watch?v=VDBKUMKWW2Y # Blackboard ## Prerequisites - Obtain the **Client ID** and the **Target Link URI** (tool launch link) from the **mentorAI** team. - Obtain the **LTI launch URL** (and any other parameters) if you’ll add the tool directly at the course level. - Be an **instructor (or admin)** with permission to add **LTI tools** in your Blackboard course. ## Admin Setup (One-Time) 1. In the **Admin Panel**, open **Integrations → LTI Tool Providers**. 2. Register a **new tool** (or edit an existing one). 3. Paste the **Client ID** and **Submit**. 4. The registration form auto-populates from the Client ID. 5. Optionally, choose user info to send (e.g., name, email). - Grade services, membership services, and user acknowledgment are optional in this demo. 6. **Create Placements** for how the tool appears in courses: - **Course Content Tool** – adds the mentor inside courseware. - **Deep Linking** – opens a content picker to choose specific mentors/resources. - **Course Tool** – adds an always-available course-level entry point. For each placement: - Give it a **Name** (and optional Description). - Set a unique **Handle**. - Mark **Available** and set **Placement Type** (Course Content, Deep Linking, or Course Tool). - For **Course Content Tool**, enable **Allow Grading** if you want grade passback. - Ensure **Allow Student Access** is on when ready for learners. - Set the **Target Link URI** (same as the tool launch link for standard launches; deep linking uses its specific launch). - **Save** your changes. ## Blackboard Ultra – Workflow A (Teaching Tools with LTI Connection) 1. Open your course in **Blackboard Ultra**. 2. Navigate to the folder/area where you want the mentor. 3. Click **Create → Teaching Tools with LTI Connection**. 4. Fill the form: - **URL:** paste the mentorAI launch URL. - **Name:** e.g., “EN Comp AI”. - **Description:** short friendly description. - **Open in a New Window:** enable if you prefer a separate tab. 5. **Save** and toggle **Visible to Students**. 6. Test by clicking the item (it opens embedded or in a new window per your choice). ## Blackboard Ultra – Workflow B (Content Market using a Placement) 1. Open the course. 2. Click **Add Content → Content Market**. 3. Select your **Course Content Tool placement** (created in Admin Setup). 4. The item is added; launch it to start chatting with the mentor. ## Blackboard Original (Legacy Interface) 1. Open the classic-layout course. 2. Go to **Build Content → Web Link**. 3. Provide: - **Name** of your AI mentor. - **URL** (mentorAI launch link). - Check **This link is a tool provider** (marks it as LTI). - Optional **Description**. 4. **Submit** and launch from the new link. ## Result - **Admin Setup:** Register the tool with **Client ID**, create **Placements**, set the **Target Link URI**, and enable student access. - **Ultra:** Use **Teaching Tools with LTI Connection (Workflow A)** or **Content Market with your placement (Workflow B)**. - **Original:** Build Content → Web Link → Tool Provider. Always use the **URLs/IDs provided by mentorAI** and make the item **visible** so students can access the assistant. --- # Course to Deep-Link ## Purpose Set up a **mentor for LTI Deep Linking** so you can add it to your **LMS** (e.g., Blackboard) yourself—assuming the base integration exists and deep linking is enabled. ## 1) Get the Mentor ID 1. Open the mentor you want to integrate. 2. Copy the **unique identifier** from the mentor’s URL. ## 2) Create the Course in Studio 1. Go to **[ibl.ai Studio](https://studio.learn.iblai.app)**. 2. Create a new course (e.g., “Socratic Mentor”) and choose the organization. ### Settings → Schedule & Details - Set the **Course Start Date** to a time in the past. - Set **Enrollment Start** to at least a day before the start date. - Click **Save**. ### Settings → Advanced Settings - In the **advanced modules list**, add: `"ibl mentor_xlog"` *(This enables the mentor component.)* - **Save** changes. ## 3) Add the Mentor Component 1. Go to **Outline → add Section → Subsection → Unit**. 2. Click **Advanced → Add New Component → Mentor**. 3. Click **Edit** and paste the mentor’s **unique ID** you copied earlier. 4. Set the **Display Name** (e.g., “Socratic Mentor”). ### Optional Settings (from the demo) - **Context Awareness:** Enter your LMS domain; copy it into the **Mentor Domain** and **Domain** fields. - **Anonymous:** Toggle if you want users to chat without authentication. - **Advanced View:** Enable tabs like **Summarize, Translate, Expand**. Click **Save** and **Publish** the unit. ## Result Your mentor is **published in Studio** and **selectable via deep linking** in your LMS—letting you add mentors to courses on your own. ### Transcript mentorAI | Deep Linking | Connect New Mentors to Your LMS https://www.youtube.com/watch?v=gfqIb8RyIxs [1.1s] Hello. Today I'm going to be talking [2.6s] about how you can more easily selfs [4.2s] serve when it comes to LTI integrations [7.0s] and and adding mentors to your own LMS. [9.9s] Essentially the process that we do is [11.7s] once somebody says a mentor is ready to [13.7s] go, we'll add it to a course in our own [15.9s] LMS which is edex. Um and that way the [18.8s] content can actually be served. The [20.2s] mentor can be consumed within you know [22.5s] whatever LMS you have. That could be [24.2s] Canvas, Brightpace, Blackboard, so on [26.3s] and so forth. Now, assuming we already [28.6s] have a base integration going um and you [31.3s] have deep linking enabled, the first [33.1s] thing you'll do is find the mentor that [35.0s] you're ready to integrate. You'll see in [37.2s] this URL bar, there's going to be a a [39.4s] unique identifier. What you'll do is [41.8s] copy this. Now, what you'll do is you'll [44.7s] go to our studio, which is going to be [47.0s] at studio.learnibai.app. [51.8s] So, we're going to come over here, and [53.7s] what we'll do is actually create a new [55.1s] course. So create a new course. We're [57.2s] going to say Socratic [60.5s] mentor [68.7s] and then you'll select the specific [70.2s] organization [73.3s] and then you'll hit create. [78.4s] Now once you're here, what I usually do [80.5s] first is go to schedule and details [82.1s] which is under the settings tab right [84.8s] here. So, you'll just click this and [85.9s] click schedule and details. You want to [87.8s] make sure this course start date is, you [90.3s] know, sometime in the past and the [92.2s] course enrollment date right here is at [95.7s] least a day before. It just has to be [97.8s] sometime before the actual start date um [99.9s] set for the course. So, we've got that [102.2s] selected and what we'll do is save. [105.4s] And then once that's saved, I'll go to [107.4s] advanced settings. [131.5s] All [144.1s] right. Once we're in advanced settings, [146.0s] what we'll do is click click into this [149.0s] little in its module list. And what you [151.0s] do is type in quotation mark ibl [155.0s] mentor_xlog. [158.0s] What this will do is enable the [160.0s] component inside the course that you can [162.4s] use to add in that that ID. So, we'll go [165.7s] ahead and hit save changes. [169.0s] Now, the next thing we'll do is go to [170.9s] the outline. [174.4s] And you don't really have to worry too [175.9s] much about, you know, the display names [177.8s] and settings and all that other than one [179.6s] than the ones that I've mentioned. [182.1s] You'll hit section, new subsection, and [184.8s] then click on new unit. This is where [186.5s] the content actually lives. [191.0s] So again, this is where the content [192.7s] lives. So we'll click on this advanced [194.6s] add new component button. Click on [197.4s] mentor and then we'll hit edit. And just [199.6s] to go back, we're going to get this same [201.8s] this string. It's a unique identifier [204.2s] for your mentor. [206.7s] So we're going to put this in [209.4s] and then we're going to put socratic [211.5s] mentor. [214.4s] And this will be the name that shows up [216.2s] on the list when you are adding this to [219.0s] your own LMS. [221.7s] All right. And then if you want to have [223.1s] any additional settings like context [224.8s] awareness, what you would do is put in [226.6s] your domain for your own LMS. So that [229.7s] could be like in our case it would be [231.4s] learn.ai.app. [235.1s] And then you can for the mentor domain [237.7s] and domain you can literally just copy [239.3s] and paste these in [241.6s] to these fields like this. [245.8s] And you can also choose whether or not [247.8s] this is anonymous, meaning users do not [249.8s] have to have an authenticated account to [252.7s] to talk with the mentor. And this is [255.0s] where you'll set context awareness. As [256.5s] long as you have that domain up in this [258.2s] field, you should be good to go. Um, we [260.7s] also have an advanced view has specific [262.5s] tabs like summarize, translate, and [264.8s] expand if that's a view that you would [266.2s] like to have in your course. And then [268.2s] we'll just simply hit save. [272.2s] And then you'll want to make sure this [273.7s] content is published. [277.8s] Once we have that, what we'll do now is [279.8s] go over to the Canvas side. Um, this is [282.2s] an example LMS. So, you can see that [284.7s] this is actually um now going to show up [287.7s] on your deep linking selection of tools [289.9s] to choose from. You know, which mentor [291.6s] you would like to integrate. [294.3s] So, now we're in Canvas and what we'll [296.2s] do is select the mentor that we want to [299.0s] use here. So I'll click on you know add [302.2s] new item. We're going to hit external [304.2s] tool and what we added on the canvas [307.3s] side prior to this uh because this is a [309.4s] current integration. This is the name of [311.4s] the deep linking tool. [314.6s] And once this populates what you'll be [316.5s] able to see is actually the Socratic [318.9s] mentor we set up. So that way you can [321.1s] actually just add members as you please [323.4s] to your course and have this be a little [325.7s] bit more self-s serve. And that's it. --- ## Brightspace URL: https://ibl.ai/docs/configuration/lti/brightspace Video: https://www.youtube.com/watch?v=xePQv8VC8Cc # Brightspace ## Prerequisites ![](/images/brightspacelti.png) - A **System Admin** account in Brightspace. - All LTI 1.3 parameters from mentorAI: - **Domain** - **Redirect URL(s)** - **OpenID Connect Login URL** - **Key-set (JWK) URL** - **Target Link URI** (points to the specific mentor you want to surface) ## Register the Tool - Log in as a system admin and click the **gear icon**. - Choose **Manage Extensibility**. - Open the **LTI Advantage** tab. - Click **Register Tool → Standard**. - Fill out: - **Name and Description** (anything you like) - Paste the **Domain**, **Redirect URL(s)**, **OpenID Connect Login URL**, and **Key-set URL** provided by mentorAI - **Target Link URI** → link to the exact mentor (varies per integration) - Leave **Extensions**, **Roles**, **Substitution Parameters**, and **Custom Parameters** blank unless mentorAI instructs otherwise. - Save the registration. - Copy the **Registration Details** (they include the issuer, client ID, etc.) and send them back to mentorAI so they can complete their side of the setup. ## Create a Deployment - In the same **LTI Advantage** area, click **View Deployments**. - Choose **New Deployment** (or open the one you just created). - Under **Security Settings**, check the user-related boxes so Brightspace passes learner identity to mentorAI. - If the mentor will push grades back, enable **Assignments and Grade Services**. - Under **Configuration Settings**, tick **Make Tool Available to the Org** so any course can use it. - Save the deployment. - Copy the **Deployment ID** and provide it to mentorAI (needed to finish the integration). ## Create a Link to the Mentor - Still inside the deployment, click **View Links → New Link**. - Fill out: - **Name** (e.g., “mentorAI – Biology Tutor”) - **URL** → the same **Target Link URI** you used in registration - **Type of Launch** → Basic - Save the link. ## Add the Mentor to a Course - Return to the Brightspace homepage and open a course. - Navigate to the content area or module where you want the mentor. - Click **Add Existing → External Tool Activity**. - Select **mentorAI** from the tool list. - **Publish** the item so learners can see it. ## Result - **Register Tool** with mentorAI-supplied URLs. - **Deploy it**, enabling user identity (and grade services if needed). - **Create a Link** pointing to the specific mentor. - **Insert the link** into any course via **External Tool Activity**. Your mentorAI assistant is now live in Brightspace, ready to help learners directly inside their course pages. --- # Brightspace Deep Linking ## Purpose Integrate **Mentor AI content** into **Brightspace courses** using **LTI Deep Linking**. This allows instructors to insert specific mentors directly into course content modules. ## Step 1 — Configure the Tool in Brightspace 1. Go to the **Manage Extensibility** section as an admin 2. Open the **LTI Advantage** tab 3. Add a **new tool** or update an **existing one** - No main registration settings need to change 4. Under **Extension Settings**, make sure **Deep Linking** is enabled 5. Go to **Deployments** 6. Open an existing deployment 7. Confirm that **Deep Linking** is marked 8. *(Optional)* Adjust additional settings if needed (e.g., send user info) 9. **Save changes** ## Step 2 — Add the Deep Linking Launch 1. In the **Deployments**, go to **Links** 2. Add or edit a link that uses the **Deep Linking Launch URL** 3. Adjust display settings: - **Height and width** - **Type** should be **Quicklink** so content can be selected 4. **Save and close** ## Step 3 — Insert Into a Course 1. Open a **course** in Brightspace 2. Navigate to **Course Content** 3. Select a unit or create one 4. Click **Add Existing** 5. Choose the **Deep Linking option** (instead of the standard external tool) 6. Select the **deep linking configuration** you set earlier 7. A **content picker** opens, showing the mentors available for your tenant 8. Mentors display with their names for easy selection 9. Pick the desired mentor and **add it to the course** ## Result The chosen mentor appears as **integrated course content**. Learners can **launch it directly**, and instructors can **repeat the process** for additional mentors or resources. --- # Course to Deep-Link ## Purpose Set up a **mentor for LTI Deep Linking** so you can add it to your **LMS** (e.g., Brightspace) yourself—assuming the base integration exists and deep linking is enabled. ## 1) Get the Mentor ID 1. Open the mentor you want to integrate. 2. Copy the **unique identifier** from the mentor’s URL. ## 2) Create the Course in Studio 1. Go to **[ibl.ai Studio](https://studio.learn.iblai.app)**. 2. Create a new course (e.g., “Socratic Mentor”) and choose the organization. ### Settings → Schedule & Details - Set the **Course Start Date** to a time in the past. - Set **Enrollment Start** to at least a day before the start date. - Click **Save**. ### Settings → Advanced Settings - In the **advanced modules list**, add: `"ibl mentor_xlog"` *(This enables the mentor component.)* - **Save** changes. ## 3) Add the Mentor Component 1. Go to **Outline → add Section → Subsection → Unit**. 2. Click **Advanced → Add New Component → Mentor**. 3. Click **Edit** and paste the mentor’s **unique ID** you copied earlier. 4. Set the **Display Name** (e.g., “Socratic Mentor”). ### Optional Settings (from the demo) - **Context Awareness:** Enter your LMS domain; copy it into the **Mentor Domain** and **Domain** fields. - **Anonymous:** Toggle if you want users to chat without authentication. - **Advanced View:** Enable tabs like **Summarize, Translate, Expand**. Click **Save** and **Publish** the unit. ## Result Your mentor is **published in Studio** and **selectable via deep linking** in your LMS—letting you add mentors to courses on your own. ### Transcript mentorAI | Brightspace | Configuring LTI https://www.youtube.com/watch?v=xePQv8VC8Cc [0.9s] I'm going to be showing you how to [2.4s] integrate Mentor AI as an LTI 1.3 [5.4s] component in Brightpace. So, the first [8.5s] thing you're going to need to do is log [10.3s] in with your uh system admin account [12.5s] because you'll need a system admin [14.2s] account to be able to create the tool. [16.6s] Now, once you're there, you'll click on [18.3s] this little gear icon and then you'll go [21.2s] down to manage [24.2s] extensibility. So, now that we're here, [26.4s] we're going to click on the LTI [27.7s] advantage tab. [30.5s] and you'll click on register [35.2s] tool. Once you click on register tool, [37.7s] you'll hit standard and you'll enter a [40.4s] name, a description. Those can be [42.0s] whatever you so choose and we will be [44.9s] providing you with the other parameters [47.5s] needed to integrate the LEI. So we'll [49.8s] give you the domain, the redirect URLs, [52.4s] open ID, uh connection login URL, target [56.2s] link, key set URL, so on and so forth. [59.3s] Now in terms of extensions, roles and [61.8s] substitution parameters, as well as [63.4s] custom parameters, you won't need to [65.3s] fill anything out here. Now to see an [68.7s] example of this, we'll click on this one [71.7s] that we already have in our own bright [76.4s] space. All right. So you can see what [78.7s] this looks like filled out. You'll see [80.8s] all of the parameters that you need. The [83.8s] domain redirect uh open ID target or [87.5s] sorry not target key set URL will [90.0s] basically remain the same for your [91.5s] organization um per LTI integration. But [94.6s] this target link URI will determine [97.0s] which exact mentor is being displayed. [99.9s] So this one will change per LTI [101.9s] integration. [103.8s] Um you can use the assignments and grade [106.1s] services if you are using a grade [108.2s] passback mentor but in this case you [110.5s] won't necessarily need [112.3s] it. And again you can see that we don't [115.5s] have any substitution parameters custom [117.1s] parameters so on and so forth. But then [118.6s] we have um the registration details [122.2s] which you will need send them back to us [123.8s] so we can enter them on our side to [125.3s] complete the integration. [127.8s] And so once we have this, we'll go to [129.8s] view [138.2s] deployments. And you can see that we [140.4s] have this deployment here. Now, we can [143.7s] click on [145.2s] that. And it'll look very similar to [147.7s] what you saw before. Um, but there are a [150.9s] couple of different pieces you'll need [152.3s] to check off here. Under the security [154.1s] settings, you'll need to check off any [155.7s] information related to user [157.6s] specifically. You can select these other [159.7s] pieces, but it's not [161.8s] necessary. And then for the [163.4s] configuration settings, again, if you do [165.2s] have any grading going on with this [167.4s] mentor, uh you'll select these two for [170.3s] grade [171.6s] passback. And then you will want to make [173.8s] the tool available to the org. Uh so [176.6s] that way it can be used across any [178.5s] course inside of the org in in [180.6s] Brightpace. [182.1s] Now, we're going to go to view [184.2s] links. Um, so we'll click on this. [187.0s] You'll hit new link if you're creating a [189.0s] new one. Um, and I'll show you an [191.8s] example of this so you can understand, [193.4s] you know, what kind of description, URL, [195.7s] name, kind of launch it is, and all of [197.7s] that. So, I'm going to hit cancel here. [200.6s] We'll click on mentor AI. [204.0s] Now you'll see here we have the name, [206.2s] the URL which is going to be the same [208.1s] target URI that indicates which mentor [210.6s] is going to be displayed and the type of [212.9s] launch you're going to use is [215.1s] basic. So now that we have everything [217.9s] configured on the system admin side, we [221.7s] can now actually start using this in [223.5s] courses as course [226.8s] authors. So we'll go back to the [229.0s] homepage. [233.2s] We'll select a [235.8s] course. Going to click on course [240.8s] information. And then if we want to add [243.6s] new integration, specifically the mentor [245.8s] that we just set up, add existing. And [248.7s] you'll click on external tool [251.0s] activity. It'll show you what external [254.0s] tools you can uh add to that specific [256.8s] page in the course. You'll click on [258.0s] mentor AI. [261.3s] And you will have the mentor. --- ## Canvas URL: https://ibl.ai/docs/configuration/lti/canvas Video: https://www.youtube.com/watch?v=pmiFXWWQL3g # Canvas ## Prerequisites ![](/images/canvaslti.png) - You must be a Canvas admin. - Obtain all LTI parameters from the mentorAI team (redirect URI, target link URL, OpenID details, JWK URL, etc.). ## Create a Developer Key - In Canvas, open **Admin → Developer Keys**. - Click **Add Developer Key** and choose **LTI Key**. - Fill the form: - **Name**: mentorAI (or similar). - **Owner Email**: your contact. - **Configuration Type**: Manual Entry. - **Redirect URI**: paste the value supplied by MentorAI. - **Title / Description**: e.g., “mentorAI – LTI 1.3 integration.” - **Target Link URI**: the mentor link provided by mentorAI. - **OpenID Connect Initiation URL**: supplied by mentorAI. - **JWK Method**: select **JWK URL** and paste the URL from mentorAI. - **Placements**: enable **Account Navigation** (top-level link) and/or **Link Selection** (in-course content). - Save the key and set its **State** to **On**. - Note the **Client ID** shown in the list (you’ll need it shortly). ## Register the Key as an App - Navigate to **Admin → Settings → Apps**. - Click **View App Configurations → Add App**. - Choose **By Client ID**. - Paste the **Client ID** from the Developer Key page and submit. - The MentorAI app now appears in the list. - Open the app’s settings to confirm or adjust **Placements**. - Copy the **Deployment ID(s)**—send them to mentorAI so they can finalize the provider-side setup. ## Add mentorAI to a Course - Go to **Courses** and open the desired course. - Enter **Modules**. - Click **+ Module**, give it a name, and **Add Module**. - Inside the new module, press **+** to add an item. - Select **External Tool**. - Choose **mentorAI** from the list (URL and page name auto-populate). - Leave **Load in a New Tab** unchecked unless preferred. - Click **Add Item**, then **Publish** the module item. - Once published, learners will see and access the integrated mentor directly in Canvas. ## Result - **Developer Key** creates the LTI credentials. - **Client ID** registers the app inside Canvas. - **Deployment ID** completes provider-side setup. - **Modules → External Tool** inserts mentorAI into any course. Your mentorAI LTI integration is now live in Canvas, ready for learners to use. --- # Canvas Deep Linking ## Purpose Integrate **mentorAI content** into an LMS course (example shown in **Canvas**) using **LTI Deep Linking**. This enables instructors to insert specific mentors or tools directly into course modules. ## Step 1 — Configure the Tool in Canvas 1. Go to the **Admin Menu** 2. Select **Developer Keys** 3. Either create a **new tool** or edit an **existing one** 4. In the tool’s configuration: - Scroll to **Link Selection** - Enter the **Target Link URI** for the deep-linking launch - Set the **Message Type** to **LTI Deep Linking Request** - Save your changes ## Step 2 — Add Deep Linking to a Course 1. Navigate to the **desired course** 2. Add an **External Tool** 3. Select the **Deep Linking Launch** option you configured 4. A **content selection menu** appears ## Step 3 — Choose and Insert Content 1. From the selection menu, pick the **mentor or resource** to integrate (e.g., Career Path Mentor) 2. The LMS returns the **Target Link URI** and a **suggested title** 3. Add the item ## Result The selected **mentorAI content** is now **embedded in the course** and ready to launch directly through **LTI Deep Linking**. --- # Course to Deep-Link ## Purpose Set up a **mentor for LTI Deep Linking** so you can add it to your **LMS** (e.g., Canvas) yourself—assuming the base integration exists and deep linking is enabled. ## 1) Get the Mentor ID 1. Open the mentor you want to integrate. 2. Copy the **unique identifier** from the mentor’s URL. ## 2) Create the Course in Studio 1. Go to **[ibl.ai Studio](https://studio.learn.iblai.app)**. 2. Create a new course (e.g., “Socratic Mentor”) and choose the organization. ### Settings → Schedule & Details - Set the **Course Start Date** to a time in the past. - Set **Enrollment Start** to at least a day before the start date. - Click **Save**. ### Settings → Advanced Settings - In the **advanced modules list**, add: `"ibl mentor_xlog"` *(This enables the mentor component.)* - **Save** changes. ## 3) Add the Mentor Component 1. Go to **Outline → add Section → Subsection → Unit**. 2. Click **Advanced → Add New Component → Mentor**. 3. Click **Edit** and paste the mentor’s **unique ID** you copied earlier. 4. Set the **Display Name** (e.g., “Socratic Mentor”). ### Optional Settings (from the demo) - **Context Awareness:** Enter your LMS domain; copy it into the **Mentor Domain** and **Domain** fields. - **Anonymous:** Toggle if you want users to chat without authentication. - **Advanced View:** Enable tabs like **Summarize, Translate, Expand**. Click **Save** and **Publish** the unit. ## 4) Add It in Your LMS (Canvas Example) 1. In **Canvas**, go to your course → **Add New Item → External Tool**. 2. Select your existing **Deep Linking tool**. 3. In the picker, choose the **Socratic Mentor** you just created. 4. Add the item. It now appears in the course for launch. ## Result Your mentor is **published in Studio** and **selectable via deep linking** in your LMS—letting you add mentors to courses on your own. --- # Chat Persistence ## Purpose Keep your mentor conversation **active and uninterrupted** in the Canvas side panel when you navigate between pages—**no reset or loss of context**. ## Steps 1. In the Canvas side panel, ask a question Example: > “What is the War of 1812?” 2. Navigate to a **different page** in the same Canvas course. 3. The mentor automatically **remembers where you left off**, restores the same chat thread, and you can continue the conversation seamlessly. ## Result Your chat **persists across page changes** in Canvas, allowing you to keep talking to the mentor without losing the conversation thread. ### Transcript ibl.ai | Canvas | LTI 1.3 Deep Linking https://www.youtube.com/watch?v=vw1hrcZWsRU [1.1s] Hi, my name is Jeff from IBLAI and today [3.6s] we're going to look at integrating deep [5.9s] linking with LTI. So the this is Canvas [9.3s] and the first thing we're going to do is [10.5s] go to the admin menu and then select [12.4s] developer keys and then you can either [15.2s] create or edit an existing tool. [18.3s] This is an existing tool, so we don't [20.2s] need to change anything about the main [21.5s] integration. But if we go down to link [24.2s] selection, we can enter in a target link [27.2s] URI for the deep linking launch. And [29.8s] we'll make sure that the message type is [31.8s] LTI deeping request. Then we can save [34.7s] it. [36.6s] Then go over to a course [41.8s] and we can add an external tool [45.9s] and select our deep linking launch [49.1s] and that'll bring up a content selection [50.7s] menu so you can choose the content you [52.6s] want to integrate with. We'll select the [55.5s] career path mentor [58.2s] and then we'll get the target link URI [60.0s] returned for us and then also a [61.4s] suggested title. So, we can add the item [64.7s] and then it's ready to launch. [74.2s] And that's it. --- ## Canvas Side Panel Screen Sharing URL: https://ibl.ai/docs/configuration/lti/canvassidepanel Video: https://www.youtube.com/watch?v=M5HT9RXJyqI # Canvas Side Panel Screen Sharing ## Overview UI/UX enhancements to the screen sharing experience when using the mentor inside the Canvas LTI 1.3 side panel. These updates give users more control over audio, mute state, and conversation transcripts during screen sharing sessions. --- ## Starting a Screen Share 1. Open the mentor in the Canvas **side panel**. 2. Initiate screen sharing from the mentor controls. 3. The mentor will respond once it can see the shared screen. --- ## Audio Controls - **Mute yourself**: prevents the mentor from hearing you (useful in noisy environments or when pausing the conversation) - **Mute the mentor**: silences the mentor's verbal responses while still receiving text in the screen share preview - Both controls are accessible from the side panel during an active screen share --- ## Text Streaming During Muted Audio When the mentor's audio is muted: - Voice communication remains available in both directions - The mentor's response streams as **text** in the screen share preview below - This allows you to read responses without audio --- ## Ending a Screen Share 1. Click **Stop Screen Sharing** (available from two locations in the UI). 2. Upon ending, a **full transcript** of the screen sharing conversation appears in the chat. 3. The transcript preserves the complete exchange for reference. --- ## Key Features - **Mute/unmute controls** for both user and mentor audio - **Text preview** of mentor responses during muted state - **Automatic transcript** generated when screen sharing ends - **Status indicators** visible in the side panel throughout the session --- ## Dynamic Mentor Integration (LTI 1.3) URL: https://ibl.ai/docs/configuration/lti/dynamicmentor Video: https://www.youtube.com/watch?v=-R6nQbwyICc # Dynamic Mentor Integration (LTI 1.3) ## Overview A dynamic mentor integration using LTI 1.3 that allows different mentors to appear in different courses within the same LMS environment. This example uses Canvas but applies to other LMS platforms. --- ## Admin Setup: Developer Keys 1. In Canvas, go to **Admin → Developer Keys**. 2. Click **Developer Keys → LTI Key** to create a new integration. 3. Configure the parameters (provided by the mentorAI team): - **Redirect URIs** - **Target Link URI** - **OpenID Connect Initiation URL** - **JWK Method**: Public JWK URL 4. Under **LTI Advantage Settings**, enable user data sharing (email, name) for reporting visibility. 5. Add the **custom field** that enables dynamic mentor selection per course. 6. Set **Privacy Level** to Public. 7. Set **Placements**: Account Navigation and Link Selection (defaults). --- ## Course Navigation Placement Add **Course Navigation** as a placement: - **Default enabled**: the mentor appears in every course's side navigation automatically - **Default disabled** (via Paste JSON): the mentor does not appear in side navigation unless an instructor enables it To disable by default, use Paste JSON and set `"default": "disabled"` under the course navigation placement. --- ## Instructor Configuration 1. Open the course where you want the mentor. 2. An instructor/admin panel shows an **Enable** toggle and a **Mentor ID** field. 3. Get the mentor's unique ID from the mentor platform URL (the segment after the last `/`). 4. Paste the Mentor ID and click **Save**. --- ## Student View - Students see the mentor in the **side panel** and can chat directly. - If both the side panel and course navigation are enabled, the mentor appears in both locations. - Students can close the side panel and use the course navigation page exclusively. --- ## Hiding the Course Navigation Link As an instructor: 1. Go to **Course Settings → Navigation**. 2. Drag the mentorAI item to the **hidden** section. 3. Click **Save** — the mentor no longer appears in the course navigation for students. As an admin: - Set `"default": "disabled"` in the Developer Key JSON to prevent the course navigation link from appearing globally. - Instructors can still enable it per-course if the admin leaves the option available. --- ## Key Takeaways - **Dynamic integration** allows different mentors per course using a single LTI tool - **Mentor ID** links each course to a specific mentor from the platform - **Course navigation** and **side panel** are independent — you can enable either or both - **Instructors** control per-course visibility; **admins** control global defaults --- ## Moodle LTI 1.3 Deep Linking URL: https://ibl.ai/docs/configuration/lti/moodle Video: https://www.youtube.com/watch?v=oOPGSEaiE4U # Moodle LTI 1.3 Deep Linking ## Prerequisites - You must be a Moodle **site administrator**. - Obtain all LTI parameters from the mentorAI team (tool URL, key set URL, login URL, etc.). --- ## Register the Tool 1. Go to **Site Administration → Plugins → Activity Modules → Manage Tools**. 2. Click **Configure a Tool Manually**. 3. Fill in the fields: - **Tool name**: Mentor AI - **Tool URL**: provided by mentorAI - **Key set URL**: provided by mentorAI - **Login URL**: provided by mentorAI - **LTI version**: **1.3** 4. Under **Tool Configuration Usage**, select **Show as preconfigured tool and adding an external tool**. 5. Set **Default Launch Container** to **Existing Window** (or choose Embed, Embed Without Blocks, or New Window). 6. Check **Deep Linking** to enable content selection. 7. Under **Privacy**, enable sharing the user's **name** and **email** for reporting purposes. 8. Save the configuration. --- ## Add the Mentor to a Course 1. Navigate to the course where you want to add the mentor. 2. Click **Edit Mode** to enable editing. 3. Click the **+ Activity or Resource** button. 4. Select **Mentor AI** from the list (under Activities or Starred). 5. Click **Select Content** — a window opens showing all available mentors. 6. Choose the mentor you want to integrate (e.g., Career Path Mentor). 7. Click **Select Content**. 8. Optionally add a description or configure the module settings. 9. Click **Save and Display**. --- ## Result - The mentor loads directly in the course page. - Students can chat with the mentor immediately. - The embedded size can be adjusted from the integration settings. --- ## Key Takeaways - **LTI version must be 1.3** — verify this in the tool configuration - **Deep linking** allows instructors to select specific mentors per course module - Enable **name and email sharing** for accurate learner analytics - The mentor can be added to multiple courses with different mentor selections via deep linking --- ## Administration URL: https://ibl.ai/docs/configuration/mcp/administration Video: https://www.youtube.com/watch?v=Y4rLO5y0mzE # Administration ## Purpose Configure MCP connectors so mentors can call external MCP tools and return results inside the chat. --- ## MCP Tab Overview - **Featured connectors:** prebuilt/default (some open-source) you can enable - **Custom connectors:** define your own endpoint and authentication --- ## Add a Custom Connector 1. Go to **MCP → Custom Connectors → Add**. 2. Fill the fields: - **Image** (optional) - **Name** - **URL** (connector/server URL) - **Description** (optional) - **Transport type** - **Authentication method:** - None - API Key - **Token type for the header** (e.g., Bearer, Basic, Token) - **Token value** 3. Click **Connect** to save. - The connector appears in your list. 4. You can **enable/disable** any connector. - Disabling prevents mentors from using that MCP server in replies. --- ## Example: Connect an MCP Server with API Key 1. In the MCP provider, copy the **Access URL** and **Access Token**. 2. In **MCP → Custom Connectors**, set: - **Name:** e.g., Workflow MCP - **URL:** paste the access URL - **Auth:** API Key - **Token type:** Bearer - **Token value:** paste the token 3. Click **Connect**. - The connector is added and can be toggled on/off. --- ## Using MCP in Chat (Demo Flow) 1. Grab a **project ID** from your MCP workspace. 2. Ask the mentor, for example: - “List the workflows in the project ``.” 3. The mentor lists active workflows (e.g., FAQ, webhook test, etc.). 4. Run a workflow, for example: - “Execute the FAQ workflow and tell me: How do I reset my password?” 5. The mentor includes your question in the workflow request body and returns the answer from the MCP server. --- ## Result Your mentors can call **enabled MCP connectors** to list resources and execute workflows, returning **MCP-sourced answers** directly in the conversation. --- # MCP Servers Model Context Protocol (MCP) servers that expose APIs as tools for AI agents like Claude Code and Cursor. ## Available Servers | Server | Endpoint | Description | |--------|----------|-------------| | Analytics | `/mcp/analytics/` | Learning analytics, mentor usage, LMS metrics | | Search | `/mcp/search/` | Course catalog and mentor search (global only, personalized excluded) | | Agent Chat | `/mcp/agent-chat/` | Chat with AI mentors | | Agent Create | `/mcp/agent-create/` | Create and manage AI mentors | ## Authentication All servers require a **Platform API Key**: 1. Log into the Admin panel 2. Navigate to Platform Settings > API Keys 3. Create and copy your API key --- ## Analytics Server Get insights about learning activity, mentor usage, and costs. ### Description (for MCP Setup) > The iblai-analytics MCP server enables AI agents to access detailed analytics about mentor-student interactions, conversation patterns, topic analysis, sentiment tracking, and cost reporting. This server provides comprehensive insights into learning activity, mentor usage, user engagement metrics, LLM costs, and platform effectiveness. Key features include: learner analytics, content analytics, message and session tracking, topic analysis, financial reporting, and time-based metrics. ### Setup ```json { "mcpServers": { "iblai-analytics": { "url": "https://your-instance.com/mcp/analytics/", "transport": "streamable-http", "headers": { "Authorization": "Api-Token YOUR_API_KEY" } } } } ``` ### Example Queries Once configured, ask your AI assistant: - "How many users are active in the acme platform?" - "What topics are students asking about most?" - "Show me conversation trends for the last month" - "What are the LLM costs broken down by mentor?" - "Which students have the most chat messages?" - "What's the average course grade?" ### Working Examples #### Basic Health Check and User Info ```python # Health check ping() # Returns: "2026-01-12T23:08:47.008677+00:00" # Get current user get_current_user() # Returns: "ibl_user" # Get user platforms get_user_platforms() # Returns: "User: ibl_user\nMember of platforms: ibl, main, ...\nAdmin of platforms: ibl, main, ..." # Count users in platform count_users_in_platform(platform="ibl") # Returns: 8 ``` #### Learner Analytics ```python # Get learner analytics for a specific user get_learner_analytics( username="ibl_user", platform_key="ibl" ) # Returns: User summary with time spent, enrollments, credentials, etc. # Get learner list for a platform get_learner_list( platform_key="ibl", limit=5 ) # Returns: Paginated list of learners with metrics # Get detailed learner information get_learner_details( username="ibl_user", platform_key="ibl" ) # Returns: Consolidated learner analytics across catalog, mentor, and credential data ``` #### Content Analytics ```python # Get content analytics (requires platform_key) get_content_analytics( metric="courses", platform_key="ibl" ) # Returns: Course analytics with enrollments, time spent, completion rates, etc. # Get detailed content information get_content_details( content_id="course-v1:Academy+S-IB-50+v1", metric="course", platform_key="ibl" ) # Returns: Detailed course information with learner breakdowns ``` **Important Notes:** - Most analytics endpoints **require** `platform_key` parameter - `get_web_analytics_time_spent_per_user` requires admin user privileges - Use `metric` parameter to specify content type: `"course"`, `"courses"`, `"program"`, `"programs"`, `"pathway"`, `"pathways"`, `"skill"`, or `"skills"` --- ## Search Server > **⚠️ Temporarily Disabled**: The Search server is currently disabled while we clean up naming conventions. It will be re-enabled in a future update. Search for courses and mentors in the catalog. ### Description (for MCP Setup) > The iblai-search MCP server enables AI agents to search for courses, programs, pathways, and mentors in the learning catalog. This server provides global search capabilities for discovering educational content and AI mentors. Key features include: catalog search across courses, programs, pathways and skills; mentor search to find AI tutors by topic or expertise; and course recommendations based on user preferences. ### Setup ```json { "mcpServers": { "iblai-search": { "url": "https://your-instance.com/mcp/search/", "transport": "streamable-http", "headers": { "Authorization": "Api-Token YOUR_API_KEY" } } } } ``` ### Example Queries - "Find courses about machine learning" - "Search for mentors that teach Python" - "What courses would you recommend for a beginner?" - "Show me the most popular courses in the catalog" ### Working Examples #### Health Check ```python # Health check ping() # Returns: "2026-01-12T23:14:26.318962+00:00" ``` #### Catalog Search ```python # Search catalog for courses, programs, pathways, skills get_catalog_search( query="test", limit=5 ) # Returns: Search results with courses, programs, pathways # Includes facets for filtering (topics, subjects, tags, etc.) ``` #### Mentor Search ```python # Search for mentors (requires platform_key for authenticated requests) get_mentor_search( query="test", limit=5, platform_key="ibl" ) # Returns: List of mentors matching the query with metadata ``` **Important Notes:** - `get_mentor_search` **requires** `platform_key` parameter for authenticated requests - Use `platform_key` or `tenant` parameter (both serve the same purpose) - Search results include pagination information #### Recommendations ```python # Get course recommendations get_recommendations( platform_key="ibl", limit=3 ) # Returns: Personalized course recommendations based on RAG search # Can specify recommendation_type: "mentors", "courses", "programs", "resources", "pathways" ``` --- ## Agent Create Server Create and manage AI mentors, including CRUD operations, forking, and configuration. ### Description (for MCP Setup) > The iblai-agent-create MCP server enables AI agents to create, configure, and manage AI mentors. This server provides full mentor lifecycle management including creation from templates, settings configuration, and document training. Key features include: create mentors from templates; configure LLM settings (provider, model, temperature); manage display settings and feature flags; train mentors with documents, URLs, or text content; and update mentor prompts and permissions. ### Setup ```json { "mcpServers": { "iblai-agent-create": { "url": "https://your-instance.com/mcp/agent-create/", "transport": "streamable-http", "headers": { "Authorization": "Api-Token YOUR_API_KEY" } } } } ``` ### Example Queries - "Create a new mentor for data science" - "List all mentors in the platform" - "Update mentor settings for mentor ID 123" - "Fork mentor ID 456 with new name" - "Show me available mentor templates" - "What categories are available for mentors?" ### Working Examples #### Health Check ```python # Health check ping() # Returns: "2026-01-12T22:57:37.066570+00:00" ``` #### Get Mentor Settings ```python # Retrieve mentor settings get_mentor_settings( org="ibl", user_id="ibl_user", mentor="ad2be335-5afa-4a9e-9298-8273b3d94e10" ) # Returns: Complete mentor configuration including: # - Display settings (theme, colors, images) # - LLM configuration (provider, model, temperature) # - Feature flags (image generation, web browsing, code interpreter) # - Prompts (system, proactive, study mode) # - Visibility and permissions ``` #### Create Mentor from Template ```python # Create a new mentor from a template post_mentor_with_settings( org="ibl", user_id="ibl_user", template_name="ai-mentor", new_mentor_name="test-mentor-123" ) # Returns: Created mentor object with unique_id, settings, and configuration # Optional parameters: display_name, description, system_prompt, llm_provider, etc. ``` #### Update Mentor Settings ```python # Update existing mentor settings put_mentor_settings( org="ibl", user_id="ibl_user", mentor="ad2be335-5afa-4a9e-9298-8273b3d94e10", mentor_description="Updated description for testing" ) # Returns: Updated mentor settings object # Can update: mentor_name, display_name, system_prompt, llm_provider, # enable_image_generation, enable_web_browsing, categories, types, subjects, etc. ``` #### Train Documents ```python # Train a document directly (for smaller documents) post_retriever_train( org="ibl", user_id="ibl_user", pathway="ad2be335-5afa-4a9e-9298-8273b3d94e10", # Use mentor_id as pathway url="https://example.com/test-document" ) # Returns: {"detail": "Document trained successfully"} # Train a document through worker process (for larger documents) post_train_document( org="ibl", user_id="ibl_user", type="url", pathway="ad2be335-5afa-4a9e-9298-8273b3d94e10", # Use mentor_id as pathway url="https://example.com/test" ) # Returns: Task confirmation or error message ``` **Important Notes:** - For `post_train_document` and `post_retriever_train`, use the **mentor_id** as the `pathway` parameter - The `pathway` parameter must be a valid mentor unique ID (UUID format) - `post_train_document` supports multiple types: `"file"`, `"url"`, `"text"` (for Wikipedia), etc. - If URL is not accessible, you'll get: `"We couldn't reach the website. It may be offline or blocking access."` --- ## Agent Chat Server Have conversations with AI mentors. Requires specifying which mentor to use. ### Description (for MCP Setup) > The iblai-agent-chat MCP server enables AI agents to have conversations with configured AI mentors. This server acts as a bridge to interact with specialized mentors that have been trained on specific topics or documents. The mentor is specified via the X-Mentor-Unique-Id header, and responses are based on the mentor's system prompt, LLM configuration, and trained knowledge base. ### Setup ```json { "mcpServers": { "iblai-agent": { "url": "https://your-instance.com/mcp/agent-chat/", "transport": "streamable-http", "headers": { "Authorization": "Api-Token YOUR_API_KEY", "X-Mentor-Unique-Id": "YOUR_MENTOR_ID" } } } } ``` ### Example Queries - "Explain how photosynthesis works" - "Help me understand quadratic equations" - "What are the key events of World War II?" - "Can you review this code and suggest improvements?" ### Working Examples #### Chat with Mentor ```python # Get a response from a mentor get_mentor_response( prompt="hello" ) # Returns: "Hello! How can I assist you today?" # More complex query get_mentor_response( prompt="Can you explain machine learning in simple terms?" ) # Returns: Detailed explanation from the mentor based on their configuration ``` **Important Notes:** - The mentor is specified via the `X-Mentor-Unique-Id` header in the MCP server configuration - The mentor's response depends on their system prompt, LLM configuration, and any trained documents - Simple prompts like "hello" work well for testing connectivity --- ## Full Configuration To use all available servers together: ```json { "mcpServers": { "iblai-analytics": { "url": "https://your-instance.com/mcp/analytics/", "transport": "streamable-http", "headers": { "Authorization": "Api-Token YOUR_API_KEY" } }, "iblai-agent": { "url": "https://your-instance.com/mcp/agent-chat/", "transport": "streamable-http", "headers": { "Authorization": "Api-Token YOUR_API_KEY", "X-Mentor-Unique-Id": "YOUR_MENTOR_ID" } }, "iblai-agent-create": { "url": "https://your-instance.com/mcp/agent-create/", "transport": "streamable-http", "headers": { "Authorization": "Api-Token YOUR_API_KEY" } }, "iblai-search": { "url": "https://your-instance.com/mcp/search/", "transport": "streamable-http", "headers": { "Authorization": "Api-Token YOUR_API_KEY" } } } } ``` > **Note**: The `iblai-search` server excludes personalized search tools (personalized mentor search and personalized catalog search). Save this to: - **Claude Code**: `~/.claude/claude_desktop_config.json` or `.mcp.json` in your project - **Cursor**: Add to Settings > Features > MCP Servers --- ## Tool Generation and Endpoints This section documents the endpoints used by each MCP server and how tool names are generated. ### Tool Name Generation Format Tool names follow the pattern: **`{HTTP_METHOD}_{usable_name}`** Where: - **`{HTTP_METHOD}`**: The HTTP method in lowercase (e.g., `get`, `post`, `put`, `patch`, `delete`) - **`{usable_name}`**: Derived from the URL pattern name with transformations: - Hyphens converted to underscores - Redundant prefixes removed (`apis_`, `ai_analytics_`, `ai_mentor_`, `v2_`, `trigram_`) - Section prefixes removed (for analytics: `overview_`, `audience_`, etc.) - Common words abbreviated (e.g., `completion` → `compl`, `enrollment` → `enroll`) **Example**: URL pattern name `ai_mentor_orgs_users_mentor_with_settings` → Tool name `post_mentor_with_settings` --- ### iblai-agent (Mentor Chat Server) **Custom Tool** (not auto-generated from endpoints): | Tool Name | Endpoint | Method | Description | |-----------|----------|--------|-------------| | `get_mentor_response` | N/A (custom implementation) | N/A | Get a response from a mentor. Requires `X-Mentor-Unique-Id` header. | **Note**: This is a custom tool that directly calls the LLM service, not generated from an API endpoint. --- ### iblai-agent-create (Agent Create Server) **Endpoints** (auto-generated from API patterns): | Tool Name | Endpoint | Method | URL Pattern Name | |-----------|----------|--------|------------------| | `post_mentor_with_settings` | `/api/ai-mentor/orgs/{org}/users/{user_id}/mentor-with-settings/` | POST | `mentor-with-settings` | | `get_mentor_settings` | `/api/ai-mentor/orgs/{org}/users/{user_id}/mentors/{mentor}/settings/` | GET | `mentor-settings` | | `put_mentor_settings` | `/api/ai-mentor/orgs/{org}/users/{user_id}/mentors/{mentor}/settings/` | PUT | `mentor-settings` | | `post_train_document` | `/api/ai-index/orgs/{org}/users/{user_id}/documents/train/` | POST | `ai_index_orgs_users_documents_train` | | `post_retriever_train` | `/api/ai-index/orgs/{org}/users/{user_id}/documents/train/retriever/` | POST | `ai_index_orgs_users_documents_train_retriever` | | `ping` | N/A (custom tool) | N/A | Health check tool | **Tool Name Generation Examples**: - `mentor-with-settings` → `post_mentor_with_settings` (hyphen → underscore, method prefix added) - `mentor-settings` → `get_mentor_settings` / `put_mentor_settings` (different methods create different tools) - `ai_index_orgs_users_documents_train` → `post_train_document` (prefixes removed, simplified) --- ### iblai-search (Search Server) **Endpoints** (auto-generated from search patterns, excluding personalized): | Tool Name | Endpoint | Method | Description | |-----------|----------|--------|-------------| | `get_mentor_search` | `/api/ai-search/mentors/` | GET | Global mentor search (personalized excluded) | | `get_catalog_search` | `/api/search/catalog/` | GET | Global catalog search (personalized excluded) | | `get_recommendations` | `/api/ai-search/recommendations/` | GET | Course recommendations | | `ping` | N/A (custom tool) | N/A | Health check tool | **Excluded Tools** (personalized endpoints): - `get_personalized_mentors` - Personalized mentor recommendations - `get_personalized_catalog_search` - Personalized catalog search **Tool Name Generation**: - URL pattern names are cleaned by removing redundant prefixes - Only GET methods are included (`only_get=True` in registration) - Personalized endpoints are explicitly filtered out **Custom Tools** (not auto-generated): - `ping_search`: Health check --- ### iblai-analytics (Analytics Server) **Endpoints** (auto-generated from `/api/analytics/` namespace): All endpoints under `/api/analytics/` are automatically included. Examples: | Tool Name | Endpoint | Method | Description | |-----------|----------|--------|-------------| | `get_content_analytics` | `/api/analytics/content/` | GET | Get content analytics | | `get_content_details` | `/api/analytics/content/details/{content_id}/` | GET | Get detailed content analytics | | `get_learner_analytics` | `/api/analytics/learners/` | GET | Get learner analytics | | `get_learner_list` | `/api/analytics/learners/list/` | GET | Get list of learners | | `get_learner_details` | `/api/analytics/learner/details/` | GET | Get detailed learner analytics | | `get_web_analytics_time_spent_per_user` | `/api/analytics/time-spent/user/` | GET | Get time spent per user | | `get_financial` | `/api/analytics/financial/` | GET | Get financial analytics | | `get_financial_details` | `/api/analytics/financial/details/` | GET | Get detailed financial analytics | | `get_financial_invoice` | `/api/analytics/financial/invoice/` | GET | Get invoice data | | `get_messages` | `/api/analytics/messages/` | GET | Get message analytics | | `get_sessions` | `/api/analytics/sessions/` | GET | Get session analytics | | `get_ratings` | `/api/analytics/ratings/` | GET | Get rating analytics | | `get_topics` | `/api/analytics/topics/` | GET | Get topic analytics | | `get_time` | `/api/analytics/time/` | GET | Get time-based analytics | | `ping` | N/A (custom tool) | N/A | Health check tool | | `count_users_in_platform` | N/A (custom tool) | N/A | Count active users in platform | | `get_current_user` | N/A (custom tool) | N/A | Get current authenticated user | | `get_user_platforms` | N/A (custom tool) | N/A | Get user's platform memberships | **Tool Name Generation**: - URL pattern names are cleaned by removing redundant prefixes and applying abbreviations - Only GET methods are included (`only_get=True` in registration) - All endpoints starting with `/api/analytics/` are automatically included **Custom Tools** (not auto-generated): - `ping`: Health check - `count_users_in_platform`: Direct database query - `get_current_user`: Authentication helper - `get_user_platforms`: Authentication helper --- ## Troubleshooting ### Authentication Issues **"Unauthorized" errors**: Check your API key is correct and hasn't expired. **"Could not authenticate mentor"**: Verify the `X-Mentor-Unique-Id` header is set correctly. **Connection issues**: Ensure the instance URL is correct and accessible. ### Common Parameter Errors **"platform_key is required"** (Analytics & Search): - Most analytics endpoints require the `platform_key` parameter - Example: `get_content_analytics(metric="courses", platform_key="ibl")` - The `platform_key` should match your organization/platform identifier **"Invalid parameters"** (Search): - `get_mentor_search` requires `platform_key` for authenticated requests - Use either `platform_key` or `tenant` parameter (both serve the same purpose) **"Document pathway is not a valid mentor unique id"** (Agent Create): - When using `post_train_document` or `post_retriever_train`, the `pathway` parameter must be a valid mentor UUID - Use the mentor's unique ID (not the mentor name or slug) - Example: `pathway="ad2be335-5afa-4a9e-9298-8273b3d94e10"` **"We couldn't reach the website"** (Agent Create): - When training documents from URLs, ensure the URL is publicly accessible - The URL may be blocked, require authentication, or be offline - Verify the URL works in a browser before using it ### Permission Errors **"Requires admin user"** (Analytics): - Some endpoints like `get_web_analytics_time_spent_per_user` require admin privileges - Ensure your API key has admin permissions for the platform ### Best Practices 1. **Always include `platform_key`** for analytics and search operations 2. **Use mentor UUIDs** (not names) for pathway parameters in document training 3. **Test with simple prompts first** (e.g., "hello") before complex queries 4. **Check URL accessibility** before training documents from external sources 5. **Use health check tools** (`ping`) to verify server connectivity ### Transcript mentorAI | MCP https://www.youtube.com/watch?v=Y4rLO5y0mzE [0.7s] Okay. So this is to demo the um MCP [4.6s] connectors we have added. If we go to [6.7s] the MCP tab, we have uh featured [9.4s] connectors and and the custom connectors [11.6s] as well. Fidget connectors are just [13.6s] custom default and some of some of them [17.0s] are open source as well which we can [18.9s] simply enable and some of them are based [21.7s] out of O. But first let's just see what [26.1s] uh custom connectors are. For the custom [27.9s] connectors, we can upload the image, [30.2s] provide the name, the URL, no optional [32.6s] description, the transport type, and the [34.3s] authentication method. For this, for [36.6s] now, we have the uh no authentication [38.6s] and the API key. The API key, we can [41.5s] provide the token type for the for the [43.7s] headers like beer, basic uh for and then [47.7s] provide the value of the token. So let's [50.6s] first try with the [53.8s] uh with the NA10 [56.3s] MCP connector. So if I go to the [58.2s] settings and click MCP access and go to [61.2s] the access token based uh we have this [63.8s] URL. Let's just copy it and I paste it [68.2s] over here and just name it en workflow [73.0s] and then we we select the API key and [76.0s] the token as bearer. So as it is hidden [79.4s] over here, let me just copy it from the [82.6s] value I have. [85.7s] Okay. And let me just paste it here. [90.0s] Okay. So now we have the value. We have [92.1s] the URL [93.9s] and M8 [96.8s] workflow [99.3s] MCP. [103.4s] So okay and we connect and it adds in [108.5s] the connectors list as well. So this is [110.5s] the uh option where we can like enable [113.7s] and disable the uh disable the MCP [117.6s] server and uh disabling this will not [120.1s] allow the mentor to use this uh to use [123.3s] this MCP server um [127.8s] to reply with the with the MCP tools. So [131.8s] now let's uh let's use so if I like go [135.4s] to the MCP [138.2s] and let's go to the homepage. [144.5s] Okay. So [146.8s] so this is the project I have. So if I [150.5s] like copy the project ID and ask him [155.1s] list the workflows [159.0s] in the project. [168.4s] So it's it's it's listing out all of the [171.2s] projects we have. It only lists the [173.9s] active active ones. So you see it's it [176.7s] has FAQ, MCP web book, MCP test [179.8s] workflow. So here we have the active [182.2s] ones, three of them active and three of [184.2s] them uh three of them in the list. So [187.7s] let's say if we want to execute this FAQ [190.6s] MCP. So we can say let's execute [196.8s] workflow [199.2s] of and provide [201.9s] me with the FAQ [205.0s] we have. [216.7s] Okay, [220.0s] let's try with some with ID of [236.0s] okay it uh we have the query uh in the [240.9s] request body right so um it's just um [245.6s] the fa FAQ [248.7s] um [251.1s] to match the provided FAQ. So for [254.7s] example uh [258.7s] and tell me the answer [263.0s] for [270.6s] so so you see uh it uh I have asked him [273.8s] how do I reset my password. it add add [276.2s] it into the into the query and then it [278.9s] provides me the answer we have in the [281.4s] MCP server. So that's how it works. --- ## Analytics URL: https://ibl.ai/docs/configuration/mcp/analytics Video: https://www.youtube.com/watch?v=Q88dvtE3wVQ # Analytics --- ## Purpose Connect an Analytics MCP server to a mentor so it can query, analyze, and visualize platform data—including user activity, learner metrics, content usage, financial analytics, and message/session data—directly in chat. --- ## Overview An Analytics MCP–powered mentor acts as an Analytics Assistant, pulling real data from your platform via MCP. It can answer questions, generate summaries, and return visualizations (e.g., graphs) based on up-to-date analytics. --- ## Prerequisites - A mentor configured with an Analytics Assistant system prompt. - MCP Tool enabled for the mentor. - An API key for the Analytics MCP server. --- ## Setup Steps #### 1) Configure the Mentor - Open the mentor you want to use for analytics. - Set the system prompt to an Analytics Assistant that can pull: - User and platform analytics - Learner metrics - Content analytics - Financial analytics - Message and session data #### 2) Enable the MCP Tool - Go to the mentor’s **Tools** tab. - Toggle **MCP** to **On**. #### 3) Add the Analytics MCP Connector - Open the **MCP** tab. - Add (or edit) a connector with: - **Connector name** - **Server location** (Analytics MCP URL) - **Description** - Similar to a system prompt; describe what data can be pulled and how responses should be formed (e.g., user/platform analytics, learner metrics, content usage, financials). - **Transport:** Streamable HTTP - **Authentication:** API Key - **Token type:** API Key - **Token value:** paste your API key (hidden after save) - Save the connector. --- ## Using Analytics in Chat #### Example Queries **Prompt example:** > “Show me a graph of last logged-in users over the past seven days.” - The mentor retrieves data across multiple sources and returns a visualization and counts. **Prompt example:** > “What mentor has the highest usage over the past seven days?” - The mentor identifies the top-used mentor and can include associated metrics (e.g., LLM cost). **Note:** Some queries may take slightly longer due to multi-step data retrieval, but responses remain fast. --- ## Result With Analytics MCP connected, mentors can answer analytics questions, generate graphs, and summarize usage and costs—all grounded in live platform data and delivered directly in conversation. ### Transcript mentorAI | Analytics MCP https://www.youtube.com/watch?v=Q88dvtE3wVQ [1.7s] Today I'll be walking you through how to [3.2s] connect an Analytics MCP server with a [5.8s] mentor or an agent. Now the first piece [8.9s] here is making sure that the system [10.2s] prompt is set appropriately. And you can [11.7s] see here we've set this up so it's an [13.4s] analytics assistant. They can help pull [15.8s] relevant information from the platform [17.7s] directly. This will include things like [20.2s] user and platform analytics, financial [22.4s] analytics, um learner metrics, so on and [25.9s] so forth. Once the system prompt is all [28.8s] set up, you'll go to tools and enable [30.6s] the MCP tool. [33.0s] After that has been enabled, you can go [34.7s] to this MCP tab. And if you're adding a [37.2s] new connector, you would just simply hit [39.0s] add connector. In this case, we already [41.1s] have one set up. You'll put in a [42.6s] connector name, the location of the [44.2s] connector server, and within the [45.9s] description itself. It's very similar to [47.5s] a system prompt in terms of what you put [50.3s] in this description is going to inform [51.9s] how this connection works. You can see [53.8s] here that we're saying this will pull [55.0s] user and platform analytics, um, [57.0s] specific learner metrics, content [59.0s] analytics, financial, message and [61.4s] session, so on and so forth. Basically, [63.9s] anything that's happening within the [65.2s] platform itself. We even provide a [67.2s] couple of answers directly here as a [69.8s] couple of examples that the mentor can [72.0s] understand and pull from when it's [73.5s] pulling data from this MCP connection. [76.2s] Now, with the transport, you'll just [77.5s] leave it at streamable HTTP and in this [79.6s] case, the authentication method is going [80.9s] to be API key. The token type you'll [83.5s] select is API key. And then in this [86.0s] token field is where you'll enter your [88.2s] API key. Now this is already in place. [91.0s] The reason this is blank is because we [92.4s] hide the key. Um but if you enter anyone [94.6s] it'll replace. Now if we want to chat [97.0s] with the mentor we can say you know show [98.1s] me a graph of last logged in users over [100.8s] the past seven days. [104.0s] Now, there are a couple steps that [105.3s] happen here um that make this process a [108.8s] little bit slower um because it's [110.6s] pulling information from a couple of [111.8s] different areas and multiple steps need [113.3s] to happen. It is relatively fast um [117.1s] however [119.4s] and you can see that we already got an [121.0s] answer. So, we can see last login [123.8s] activity or users who have logged in the [125.4s] past seven days, how many that has been. [127.8s] And then I'll ask [130.2s] what mentor has the highest usage over [134.8s] the past [138.4s] seven days as well. [144.2s] All right. So, the mentor with the [145.4s] highest usage is our analytics mentor, [147.1s] which is what we're actually chatting [148.2s] with. And it even gives us that LLM cost [151.0s] um associated. [152.8s] And there you have it. --- ## MCP Connector Authentication URL: https://ibl.ai/docs/configuration/mcp/connectorauthentication Video: https://www.youtube.com/watch?v=fcFsqz7PndI # MCP Connector Authentication ## Overview Configure MCP connectors with different authentication methods and scopes to control how users authenticate with external services like Notion, Google, and other OAuth-based providers. --- ## Adding a Custom MCP Connector 1. Go to the **MCP** tab on a mentor. 2. Click **Add Custom MCP Connector**. 3. Provide the **connector URL**. 4. Select the **connector scope**: - **This mentor only**: the connector is available only for this mentor - **All mentors**: the connector is available for every mentor in the tenant --- ## Authentication Methods ### API Key - Select **API Key** as the authentication method. - Choose the **token type** for the header (e.g., Bearer, Basic, Token). - Select **Other** to provide a custom token type. - Enter the **token value**. ### OAuth For OAuth-based connectors (e.g., Notion), choose the **OAuth** authentication method and configure the **authentication scope**. --- ## OAuth Authentication Scopes | Scope | Behavior | |-------|----------| | **Tenant** | Authenticate once — the authentication is shared across all tenant members and all mentors | | **Mentor** | Authenticate once — the authentication applies to this mentor only but is shared across all users | | **User** | Each user must authenticate individually when they first interact with the mentor | --- ## User-Based Authentication Flow 1. Admin adds the OAuth MCP connector with **User** scope. 2. Admin authenticates first (completes the OAuth flow). 3. The connector is enabled on the mentor. 4. When a different user (e.g., a student) chats with the mentor: - An authentication prompt appears: "Authentication required for [service]. Please complete the login in the open window." - The user completes the OAuth flow in a popup window. - Upon success, the chat continues with the connector active. --- ## Enable/Disable Connectors - After adding a connector, use the **toggle** to enable or disable it. - **Enabled**: the mentor can use the MCP connector in responses. - **Disabled**: the mentor cannot access the connector. --- ## Key Takeaways - **Tenant** scope: authenticate once, everyone benefits - **Mentor** scope: authenticate once per mentor, shared across users - **User** scope: every user authenticates individually (most secure) - Connectors can be scoped to a **single mentor** or **all mentors** in the tenant - Use the toggle to quickly enable/disable connectors without removing them --- ## Search URL: https://ibl.ai/docs/configuration/mcp/search Video: https://www.youtube.com/watch?v=HbKNTemQeLU # Search --- ## Purpose Connect a Search MCP to a mentor so it can search your platform for courses, programs, and mentors—and return grounded, recommendation-ready results without hallucinations. --- ## Overview The Search MCP uses an MCP server (in this demo, ibl.ai’s own search MCP) to power a mentor that acts as a search assistant. It can: - Search the course catalog (courses/programs). - Find and recommend mentors. - Ask follow-up filters (subject, level, format, language). - Enforce guardrails via the system prompt. --- ## Prerequisites - A mentor with a Search Assistant system prompt. - MCP Tool enabled for the mentor. - An API key for the Search MCP server. --- ## Setup Steps #### 1) Prepare the Mentor - Open the mentor you want to use for search. - Set the system prompt to a search-focused assistant (catalog + mentor search, recommendations, guardrails). #### 2) Enable the MCP Tool - Go to the mentor’s **Tools** tab. - Ensure **MCP** is enabled. #### 3) Add the Search MCP Connector - Open the **MCP** tab. - Add (or edit) a connector with: - **Connector name** - **Server location** (Search MCP server URL) - **Description** - Acts like instructions for the connection (what the mentor can pull and how to respond). - **Transport:** Streamable HTTP - **Authentication:** API Key - **Token type:** API Key - **Token value:** paste your token - Save the connector. --- ## Using Search in Chat #### Search Courses **Prompt example:** > “What courses are available on the platform?” **Result:** - Returns a subset of courses from the tenant (even if the catalog is large). - Asks if you want to filter by subject, level, format, or language. #### Search Mentors **Prompt example:** > “What mentors can help me become a better student?” **Result:** - Lists relevant mentors (e.g., study tips, quizzes, Socratic support). - Offers to filter further or show more results (based on system-prompt limits). --- ## Result With Search MCP connected, mentors can reliably discover courses and mentors across your platform, guide users with filters and recommendations, and return accurate, grounded results from your own data. ### Transcript mentorAI | Search MCP https://www.youtube.com/watch?v=HbKNTemQeLU [1.8s] I'm going to be showing you how to [3.3s] connect a search MCP to an agent on [6.5s] Mentor AI. Now, in this case, it's going [9.4s] to be our own search MCP server, meaning [12.0s] we're pulling directly from our own [13.4s] platform. And this is essentially [15.9s] helping people search for mentors or [18.0s] content that would help them in some [19.8s] way, shape, or form. You can see that [21.5s] the system prompt here is set to be a [25.0s] search assistant. You know, we can pull [26.8s] from catalog of courses, programs, so on [30.2s] and so forth, as well as search for [31.5s] mentors and even get recommendations. We [34.1s] also have a couple of guardrails in here [35.4s] to prevent any hallucinations [38.4s] and so on and so forth. Basically, this [41.1s] is just informing the mentor how it's [42.5s] going to work with this MCP connection. [45.0s] Now, the other thing here is under the [46.6s] tools tab, you'll need to make sure that [48.1s] this MCP tool is enabled. And once [50.8s] that's done, you can go to the MCP tab [52.6s] itself and add a connector. [55.0s] We already have one in place for search [56.6s] which you can see here. You'll add in [58.6s] the connector name, the server location [61.0s] and the description which the [64.2s] description here is similar to a system [65.5s] prompt in the sense that it is serving [68.2s] as instructions on how this connection [70.5s] is going to work. So whatever you put in [72.0s] here is going to inform what what the [74.9s] mentor can pull from this you know [77.0s] server connection and respond to. [79.8s] Additionally, the transport will remain [81.4s] streamable HTTP and the authentication [83.9s] method here will be API key. The token [86.8s] type you'll select from the dropown is [88.2s] this API key and then you'll enter your [90.2s] token. [92.6s] So we'll ask the mentor [95.9s] what courses are available [99.9s] on the platform [104.5s] and this will pull up a list of all the [106.1s] courses associated with this IBLAI [108.1s] tenant. [115.4s] All righty. [122.1s] So, we can see a list of courses that [124.3s] were pulled. Um, there's over about [126.9s] 1,400 courses, but we're pulling just a [129.5s] subset of those. And we're even being [131.4s] asked uh if we want to filter by a [133.6s] specific subject, level, format, or [135.4s] language. And just to kind of see what [138.8s] it would look like to search for a [139.9s] mentor, I'm going to ask what mentors [142.9s] can help [145.4s] me become a better student. [152.3s] Essentially, I'm just looking for [153.4s] mentors that would help me with study [154.8s] tips, um, you know, quiz testing, so on [158.5s] and so forth. [162.3s] All righty. [166.6s] And now these are a list of mentors I [168.7s] can select from that are focused [170.2s] specifically on student needs. Um that [173.0s] for you know studying whatever it may be [175.8s] uh socratic gamified quiz creator and [179.0s] even it's even asking if we want to even [180.6s] filter further to find you know specific [183.0s] goal um or even display more mentors [186.3s] that weren't shown here because we are [187.9s] limiting how many are are shown via the [189.8s] system prompt. All right and there you [192.2s] have it. That search --- ## Tenant Creation URL: https://ibl.ai/docs/configuration/multitenancy/tenantcreation Video: https://www.youtube.com/watch?v=fk0Zm2NnhFU # Tenant Creation ## Goal Create and launch a brand-new tenant (and its first user). --- ## Steps ### Open the Launch Form - In **Data Manager**, expand **IBL AI Service** app. - Click **Launch Tenants**. --- ### Fill Out User Information - **Username** – choose one that does not already exist. - **Email** – enter the user’s email address. - **First Name** and **Last Name** – type both. - Default roles are set to **Admin** for both **edX role** and **Web role**. - Change either role to **None** or **Subscriber** if you need a non-admin user. --- ### Define the Platform - **Platform Key** – the unique identifier for this tenant. - **Platform Name** – what learners will see in their browser tab; spaces are allowed. - **Password** – set the initial password for the new user. --- ### Launch the Tenant - Click **Launch Tenant**. - A **success message** confirms the tenant and user were created with the selected roles. --- ### Verify Creation - Still in **Data Manager**, go to **Core → Platforms**. - The newly created tenant should now appear in the list. --- ## Result A new **tenant** exists, along with its default **admin user**, ready for immediate use. ### Transcript skillsAI | Super Admin | Create and Validate Tenant Launch https://www.youtube.com/watch?v=fk0Zm2NnhFU [0.5s] all right I'm going to show you guys how [2.0s] to create um and launch a new tenant um [6.2s] so right now we're in the data manager [8.5s] side of things and if we scroll [11.7s] down to IBL aai Service app you'll see [15.3s] launch [16.5s] tenants and so what you'll do now is [19.3s] fill in this information you'll see a [21.1s] couple Fields here now any user [24.1s] information what this is going to do is [26.0s] create a a user underneath that tenant [29.6s] by default it's going to have the edex [32.6s] role and the web role of admin we can [37.2s] change these to nun if needed um or a [40.4s] subscriber which means they're just a [41.8s] normal user now for this we're going to [44.6s] create a new username for somebody who [47.8s] doesn't already exist on the platform so [49.6s] we'll [50.5s] do um let's see [54.7s] Ashlin Brown [58.1s] tenant um and then we'll do the same for [60.7s] the email but we'll do [63.2s] i. a as the email address here and then [66.7s] we'll put in the user's [69.3s] name first and last name and now this [73.0s] platform key is going to be the key for [75.8s] the platform so we'll [78.3s] do uh let's see here [81.5s] [Music] [82.9s] Ashlin [84.1s] [Music] [86.0s] tenant ACI and now for the platform name [91.0s] this is what's going to show up in the [92.4s] tab bar that a learner sees so we can [95.2s] add spaces like [98.2s] this and then we'll create a [101.3s] password and so we'll hit launch [106.1s] tenant and you'll see that we have [109.4s] successfully created a tenant and a [110.8s] tenant user with the correct roles now [113.0s] if we wanted to validate how or that [115.7s] tenant has been actually created then [117.9s] what we can do is come down here and [122.0s] under core you'll go to [125.6s] platforms and then you can see that new [127.5s] tenant has just been [131.9s] created --- ## Tenant-User Association URL: https://ibl.ai/docs/configuration/multitenancy/tenantuserassociation Video: https://www.youtube.com/watch?v=fdWCEHqy5-w # Tenant-User Association ## Purpose Create a tenant-admin user who can manage more than one tenant. --- ## Create the Admin User - In **Data Manager**, expand **IBL AI Services** and click **New Tenant Users**. - Enter the new person’s **Name**, **Email/Username**, and **Password** (e.g., “Ashlin B12”). - Ensure this user is an **admin**: - **edX RO** stays **Instructor** (default) - **Web RO** stays **IBL Admin** (default) - Choose the appropriate **Platform**, select an existing **tenant**, type the password again, and click **Save**. - Look for the **green confirmation message** showing the user was created. --- ## Link the User to a Second Tenant - Return to **Home → User Platform Links**. - Note the newly created user’s **ID** (example: 129). - Click **Add User Platform Link**. - Search for and select that user (recent entries appear first). - Pick a different **Platform** (example: editech). - Check **Is Admin** and **Is Staff**. - Marking **Is Staff** also grants **ATX access** for course-authoring tasks if desired. - Click **Save**. --- ## Result The user now appears as a **Tenant Admin** in each chosen tenant and, where selected, as **staff** with course-authoring privileges. ### Transcript ibl.ai | Set Up Tenant Admin to Manage Multiple Tenants https://www.youtube.com/watch?v=fdWCEHqy5-w [0.6s] all right I'm going to show you how to [1.8s] set up a tenant admin to manage multiple [4.3s] tenants um as a super admin user so [8.3s] we're going to start from the data [9.6s] manager and now let's just say that the [11.9s] user doesn't already exist we want to [14.4s] create a new user who's going to manage [16.1s] multiple tenants the first thing we're [18.3s] going to do is come down to where it [20.4s] says IBL AI [21.9s] Services clect uh click on new tenant [25.0s] users and we're just going to create [28.6s] another user so we're going to put [31.9s] in the user's name that we're [35.3s] entering we're going to also create [37.8s] their username email or username and [40.9s] password at the same time here um so [43.8s] let's just do Ashlin [47.5s] B12 um and then we do need to have the [50.1s] email as [55.4s] well so we have that entered and we want [59.5s] them to be an admin because this person [61.6s] is going to manage multiple tenants so [63.1s] if the edex RO needs to be or instructor [65.4s] which it is by default and web Ro needs [67.6s] to be IBL admin which it is by default [69.5s] as well now what we're going to do is [71.6s] select the [72.7s] platform um and we already have a [74.7s] handful of tenants created so we're [76.2s] going to go ahead and select a tenant [78.0s] and then enter a password for [86.0s] them so now that we have the new admin [89.4s] user created [90.5s] we'll see some green messages saying [92.7s] everything looks good we'll come back to [94.8s] the home over [96.3s] here and then what we're going to do now [99.6s] is go to user platform [101.5s] links you'll see that the user id/ user [104.5s] object is 129 so when we add a new user [108.5s] platform link we'll search for that um [111.0s] and that one's since it's just recently [112.5s] created going to be right on the top [114.0s] there and then we're going to select a [116.0s] different [117.0s] platform so in this case we'll do [119.0s] editech [121.0s] um so we've got that platform selected [123.1s] and then we would just need to make sure [124.4s] that is admin is marked and is staff is [127.4s] marked if we want them to have ATX [131.4s] access for course authoring purposes and [134.7s] now we'll just hit save now this user is [137.8s] a part of two separate tenants so we [141.4s] have ashl this tenant right here where [144.5s] this user [145.9s] 129 is a tenant admin and then on under [150.0s] editech same thing they're going to be a [151.8s] ten an admin here as [154.9s] well --- ## User Migration URL: https://ibl.ai/docs/configuration/multitenancy/usermigration Video: https://www.youtube.com/watch?v=vl2793vT9nE # User Migration ## Purpose Move an existing user from one tenant to another while controlling their permissions. --- ## Remove the User’s Access to the Current Tenant - In **Data Manager**, expand **Core** and open **User Platform Links**. - Locate the target user (example: AshenBrown15, ID 126). - Edit that user’s **platform-link entry**. - Clear both **Admin** and **Active** check-boxes. - This prevents the user from using admin tools and stops access to the tenant. - Click **Save**. --- ## Add the User to a New Tenant - Still in **User Platform Links**, choose **Add User Platform Link**. - Search for and select the **same user** (ID 126). - Pick the **destination platform** (example: editech). - Set the desired permissions: - **Is Admin** – lets the user reach admin panels - **Is Staff** – grants access to edX courses - **Active** – allows log-in to the platform - Click **Save**. --- ## Result The user is now **removed from the original tenant** and **added to the new one**, with permissions exactly as configured. ### Transcript ibl.ai | skillsAI | Migrate Tenant User Between Tenants https://www.youtube.com/watch?v=vl2793vT9nE [0.2s] right I'm going to show you how to [2.9s] migrate users between tenants as a super [5.8s] admin so you'll see that we're starting [7.9s] from the data [9.3s] manager and the first thing we're going [11.0s] to do is find the platform links so this [15.5s] is going to be under the core right [20.2s] here and you'll see a list of user [23.1s] platform links so let's just go ahead [25.5s] and take a look at which user this is [28.1s] Ashen Brown 15 with the IDE of [30.9s] 126 um and we let's just say this is the [33.6s] user that we want to migrate to a [35.6s] different um tenant um so what we're [39.8s] going to do here we're going to go back [41.4s] to that user platform link for this user [43.8s] and what we're going to do is unselect [46.0s] admin and unselect active what this is [48.2s] going to do is make sure that they can't [49.4s] access anything that's admin related but [51.8s] also access this specific tenant um that [54.6s] active Mark right here uh for a normal [57.5s] user like a learner uh with normal be [60.4s] active if they can access the platform [62.4s] if they can't or they shouldn't be able [63.9s] to access it we would unmark this active [66.2s] right [66.9s] here and then from there we'll hit [70.0s] save and now if we want to add them to a [73.0s] different tenant um we can go to add [75.8s] user platform [77.1s] links um find the user uh so we have [81.5s] asham brown 15 with that user ID of 126 [85.3s] and then we'll look for the platform [86.8s] that we want um them to be a part of [91.4s] we'll just go ahead and select aditech [93.8s] right [94.5s] here um and we don't really need to [97.4s] worry about added on or expired on dates [99.8s] but what we do need to focus on is these [101.9s] little three right here is admins going [104.0s] to give them access to admin panels is [106.0s] staff is going to give them access to [107.3s] edex courses um and then active again [111.9s] allows them to have access to the [113.4s] platform and then we just hit save so [116.8s] now that this user went from the ashle [119.5s] inant right [122.0s] here to this main edch tenant and if we [126.0s] want to go and mark them as admin since [127.5s] they were in the other environment or [129.0s] the other tenant we [134.2s] can --- ## Administration URL: https://ibl.ai/docs/configuration/rbac/administration Video: https://www.youtube.com/watch?v=p0uIV35F7PI # Administration ## Core Concepts ![](/images/rbac.png) - **Groups** – collections of users (e.g., Tenant Admin group) - **Roles** – bundles of permissions - **Actions** = object-level rights (view, edit, delete) - **Data actions** = field-level rights (read, write) - **Policies** – bind a role to specific resources within a platform and link users or groups to that bundle - **Hierarchy** – a policy applied to a platform cascades to every resource beneath it --- ## Granting Full Tenant-Admin Access - Locate the **Tenant Admin group** - Add yourself (or another user) to that group and **save** - **Refresh the app**: every tab (Tools, Prompts, Safety, Data Sets, History, API, etc.) is now visible - To revoke full access, **remove the user** from the group and **save** --- ## Creating a Limited-Access Role - Create a **new role** (start broad, then remove what you don’t need) - Limit it to the required model—for example, only **Settings** - Make a **policy** that applies this role to the desired resources - Add yourself to the policy’s user list and **refresh** ### Result: - **Settings** stays accessible - Tabs like **LLMs, History, Data Sets, and API** disappear - Only items classified under **Settings** (certain prompts, Safety) remain --- ## Field-Level Control (Data Actions) - In the role, add a data-action rule such as: - `description : read` - **Refresh**: you can view but not edit that single field - Change the rule to: - `description : read,write` *(or use `*`)* to allow editing --- ## Adding Access to Specific Resources Follow the same pattern by updating the role: - **Tools** – add `tools` with the actions you need; the **Tools** tab appears - **API Tokens** – add `api_tokens` with `read` and `list` (then `delete` if required); tokens become viewable and, with delete, removable - **Documents/Data Sets** – add `documents` with `read`; add `write` to untrain; add `delete` to remove - For each added permission, **refresh the browser** to see the change --- ## Key Takeaways - **Groups, Roles, and Policies** combine for precise, layered control - Adding a user to a group instantly grants that group’s policies - Roles can be fine-tuned from **section-wide access** down to **individual fields** - **Permission changes appear immediately after refresh**, letting you verify results in real time ### Transcript mentorAI | RBAC https://www.youtube.com/watch?v=p0uIV35F7PI [0.6s] Hey, this is Jeff Fonman of IBL. Um, [3.0s] just going to give an overview of the uh [5.1s] ARB implementation. Uh, so at a high [8.3s] level, we generally have groups, [10.6s] policies, and roles. Uh, the groups are [13.7s] a way to associate um different users [17.0s] within a group. So for instance, we have [18.8s] a tenant admin group here. Um, and then [22.6s] we have uh roles which allow us to [25.6s] associate actions and data actions. data [28.6s] actions being the field level [29.8s] permissions and object permissions are [32.5s] the or actions are the object level [34.3s] permissions. Um so for our tenant admin [36.6s] role we've generally just given them [38.2s] everything um at this point. Then we [41.4s] have a policy which associates a [43.8s] particular role in a platform to a [46.8s] resource and then or resources rather [49.9s] and then allows you to associate users [52.6s] to that or groups to it. So for [54.6s] instance, our tenant admin policy has [57.4s] the tenant admin group. So anyone in [59.2s] that group gets this uh this policy on [61.4s] these resources and since this is [63.8s] hierarchical effectively this means that [66.2s] um this role applies to everything at or [69.8s] underneath this particular um platform. [73.7s] Uh I currently my user has no privileges [76.8s] right now. Um, so if we come over, I'm [79.3s] still technically a platform admin. Um, [83.5s] from a platform our perspective, not an [85.0s] arbback related one. So you can see we [86.8s] don't really have much for options here. [89.2s] Um, and even if we are to come in here, [91.6s] we don't have uh any information shown. [96.3s] Uh, but we can go back to that tenant [97.8s] admin group and can add myself in. Save [102.0s] that. [103.6s] And when we come back over here now, we [106.3s] have access to everything. [110.2s] All the different pieces, tools, [112.2s] prompts, safety stuff, data sets, [114.8s] history, [116.5s] API, all the uh all the good bits. Um [120.4s] but so we can uh remove myself back out [123.0s] of that group for the moment and then [125.0s] look at some more specific stuff. We [127.7s] have another policy here um that applies [130.7s] to everything just for simplicity as [133.2s] well, but I'll put just myself in in [135.6s] there. [137.3s] And that particular role is this other [140.0s] role that I've created here. And it [142.5s] currently has also basically access to [146.5s] everything. Um but we can change that to [151.1s] just have access to the settings or [153.5s] whatever is defined rather on the [155.0s] settings uh the settings model in this [157.3s] case for us. And then when we come back [160.0s] and refresh and look at our [163.8s] uh the things that we have access to we [165.8s] still have access to settings but our [168.2s] LLMs are gone. Um these particular [171.0s] prompts are part of settings whereas all [172.8s] of our suggested prompts are not. That's [174.4s] in a different area. tools. Uh safety is [178.2s] part of um settings. We don't have [181.0s] access to history or data sets or API [183.4s] still. Um and then if we wanted, we [187.0s] could if we just wanted to do like for [189.3s] instance uh the description field, um we [192.3s] could change this to the word [195.3s] description [197.3s] and then just give that a read [199.0s] privilege. [202.1s] That's the wrong place. Don't do it [203.7s] there because it won't do anything. to [205.6s] do in the data actions [208.3s] description [211.0s] read [213.4s] and then when we refresh this now we [216.5s] just have read access to only this [218.7s] particular field. Um and then we could [222.6s] change that to a star to give readr [225.0s] access here [230.6s] and then the user has edit privileges [232.6s] there as well. Um [236.0s] if we uh want to then like let's look at [239.7s] like for instance the tools we can do [241.8s] the same same idea with the tools. So we [245.8s] can add these in [248.0s] and we also need to put this back. We [252.3s] can leave that where it is [255.8s] and throw in [260.5s] tools [272.6s] and then the tools are then available as [275.7s] well. [277.1s] Um, another one we could look at might [279.8s] be like the uh API tokens. So, those we [285.0s] can add up in here as well. And that's [288.6s] under a different space. We're going to [291.1s] just give those uh read and list [293.1s] privileges for the moment. [302.2s] And we should be able to save that. [305.8s] And when we come back and list our API [309.0s] tokens [311.4s] and we have those available, but they're [313.1s] not currently deletable since we don't [315.0s] have um our delete privilege, but we can [318.6s] add that one in as well. [328.4s] And then we come back here. I got to [331.8s] refresh it. [333.8s] then we get the ability to delete the [335.9s] tokens. [340.2s] So the same similar concepts work for um [343.7s] most rest of the tabs. Um we could also [347.7s] look at the data sets which um [351.1s] internally in this section is called the [353.3s] uh documents. [357.3s] We can add those in there. [375.3s] and then we get access to all that [376.9s] stuff. And um similarly see we don't [380.1s] have um [382.4s] uh a right privilege for the documents. [385.4s] So we can't untrain a document at this [388.6s] point. Oops. But that would be called, [395.2s] right? [399.0s] And then we should be able to untrain [401.6s] it. But we still can't delete it since [404.2s] we don't have um the delete privilege [407.4s] there. So we could add that as well. [415.3s] and we trust [418.1s] and we can delete that document. So, [423.3s] um [424.9s] I think that's probably a pretty good [426.7s] overview of the main pieces. [431.4s] Uh you guys have any questions, [433.2s] certainly let us know. Next one. --- ## Bulk Team Management URL: https://ibl.ai/docs/configuration/rbac/bulkteammanagement Video: https://www.youtube.com/watch?v=1tGZBe1kxsw # Bulk Team Management ## Purpose Create teams and invite users at the same time using a platform invitation CSV. This lets tenant admins and enrollment managers bulk-enroll users while automatically assigning them to teams with a designated manager. --- ## Who Can Use This - Tenant Admins - Enrollment Managers (users who can invite people to the platform/content) --- ## How It Works (High Level) - You upload a platform invitation CSV. - The CSV specifies: - Which team (user group) the user belongs to. - Who the team owner/manager is. - Invitations are sent. - Users appear as team members after they register. --- ## Step-by-Step ### 1) Prepare the CSV - Download or use your existing platform invitation CSV. - Ensure the following fields are set correctly: - **User group:** the team the user should belong to (e.g., Team 2). - **User group owner email:** the email of the person who will manage that team. - The user group owner email links the team to its manager. ### 2) Upload the CSV - Go to **Management → Invite**. - Select **Upload CSV**. - Review the preview: - Validate user details. - Confirm the user group and group owner are correct. - Click **Save**. This triggers the platform invitations. ### 3) User Registration - Invited users must register before they appear as team members. **Before registration:** - The invite shows as pending. - The user does not appear in the team yet. **After registration:** - The invite shows as accepted. - The user automatically appears in the assigned team. --- ## What Admins and Managers See ### Tenant Admins - Can view all teams. - Can see team members once registration is complete. - Have access to analytics for these users. ### Enrollment Managers - Can create and manage teams via CSV. - If granted analytics access, can view the same analytics as tenant admins. - Do not access tenant settings or edit course content. --- ## Key Notes - Users do not see which team they belong to from their own perspective. - Teams are populated only after registration is completed. - This method scales easily for onboarding large cohorts tied to specific managers. --- ## Result With Bulk Team Management, you can onboard users, assign them to teams, and establish team managers—all in one CSV upload—streamlining large-scale enrollment and oversight. ### Transcript skillsAI | Bulk Team Management https://www.youtube.com/watch?v=1tGZBe1kxsw [1.6s] I'm going to be showing you how to [3.1s] create a team at the same time that [5.6s] you're sending a platform invitation. [7.8s] Now, this is going to be available to [9.8s] tenant admins as well as anybody who has [13.1s] um the enrollment manager role, meaning [15.5s] they can invite people to content the [17.4s] platform, so on and so forth. Now, [21.1s] you're going to be using the platform [22.5s] invitation here. If you click on invite, [25.8s] I already have a CSV downloaded uh that [28.7s] I'll show you here in a second, but I [30.5s] want to go ahead and show you that under [32.1s] teams, we're going to be adding to this [33.6s] team, team 2. And you can already see [35.8s] there's one user in here. So, if we go [38.6s] back to users, we'll hit invite, upload [41.8s] CSV. And when you upload the CSV, you'll [44.2s] be able to validate the information [45.6s] that's in here. Make sure everything [47.0s] looks good. We already And you can see [49.1s] right here we have the user group. This [51.8s] is the group that that user is going to [53.4s] be part of and overseen by a particular [56.0s] manager. In this case, the user group [58.6s] owner email is creating that link to the [62.1s] owner of the group um to said group. So, [65.3s] this user right here is going to be [66.7s] managing team 2. [69.2s] Now, what we'll do is hit save. And what [72.3s] this will do is trigger a platform [73.9s] invitation. [75.9s] So, I'm going to go register as this [78.2s] user. And the user doesn't really know [80.0s] what group they're a part of. they won't [81.3s] see it. But I'm going to go register [82.9s] real quick. Pause this and then I'll [84.6s] show you what it looks like once once [86.9s] the folks who are in that invitation [89.2s] have registered because if they have not [92.2s] show, you know, registered an account on [94.6s] the platform, they won't show up as a [96.6s] team member yet. But as soon as they do, [98.6s] they will. So I'm going to again pause [100.8s] this and we'll come back and refresh um [103.3s] so you can see those changes applied [104.8s] once once I have registered this other [106.9s] user. [108.5s] All righty. So I have just created the [110.2s] account for the the team member. If we [112.5s] go under management and hit invite, you [114.6s] can even see that that invite has been [116.2s] accepted. Now when we go to the team [120.6s] as a tenant admin, we'll be able to see [122.1s] all teams created. Um similarly with um [125.1s] anybody who has a high level um you know [128.8s] enrollment manager role. um the the [131.8s] folks who have, you know, tenant level [134.4s] access, tenant admin level access will [136.2s] be able to see analytics about these [137.5s] users. And if you decide to allow um [140.3s] enrollment managers to see analytics, [143.4s] they'll also be able to see the same [144.9s] thing as tenant admins. They just want [146.4s] to be able to, you know, create or edit [148.2s] any course content um so on and so forth [151.2s] or, you know, access tenant settings. [153.9s] But now you can see that the team member [155.4s] is here. --- ## Enrollment Manager Assignment URL: https://ibl.ai/docs/configuration/rbac/enrollmentmanagerassignment Video: https://www.youtube.com/watch?v=7P49vajFL70 # Enrollment Manager Assignment ## Purpose Give a user the ability to invite and enroll people into the platform, courses, and programs—without granting full tenant admin or course-editing access. --- ## How to Assign the Enrollment Manager Role ### Open Management 1. Go to **Tenant Settings → Management**. ### Assign the Policy 2. In **User Management / Policies**, select **Enrollment Manager**. 3. Apply this policy to the user. 4. Click **Save**. Assigning Enrollment Manager globally is the quickest way to grant invitation permissions. --- ## What Enrollment Managers Can Do After assignment, the user can: - Invite users to: - The platform - Courses - Programs - Use **single-email invites** or **CSV uploads** (templates available). - View invite status: - Sent - Pending - Accepted - Manage Teams: - View teams - Create teams they own - Add users to teams they created or to existing teams --- ## What Enrollment Managers Cannot Do - Edit or modify course content - Change tenant settings - Perform broader administrative actions outside invitations and team assignment --- ## Enrollment Manager View (What It Looks Like) - Access to **Teams** tab: - See team membership - Add members to teams - **Invite** button available across: - Users - Courses - Programs - Invitation flow mirrors tenant admin UX, but is limited to enrollment tasks only --- ## Typical Workflow 1. Log in as an **Enrollment Manager**. 2. Go to **Teams → Invite**. 3. Send invitations via email or CSV. 4. Track **pending** and **accepted** invites. 5. Once users register, add them to the appropriate team (new or existing). --- ## Result Enrollment Managers can efficiently onboard learners and organize them into teams—without access to content editing or tenant-wide configuration—keeping responsibilities clean and secure. ### Transcript skillsAI | Enrollment Manager https://www.youtube.com/watch?v=7P49vajFL70 [1.6s] to give somebody the ability to enroll [4.7s] um folks into you know courses or [7.6s] programs via invites or even invite [9.4s] people to the platform. The way you [11.4s] would do this is go by going to the [13.4s] management tab and you have a couple of [17.2s] different options but the easiest way to [20.0s] globally give this invitation permission [23.1s] is by setting specific policy on this [26.1s] user management tab by clicking [27.8s] enrollment manager. that's going to give [30.2s] me the ability to enroll folks um into [34.5s] programs, courses, and even invite them [36.6s] to the platform. So, I can show you how [39.3s] this looks for this user that we [41.0s] selected here. [47.4s] All righty. [53.6s] I have logged in as the user that I have [55.8s] given that enrollment access to. Um, [58.7s] this person is also able to access [61.4s] teams, see who's in a team, um, and even [65.3s] add folks to teams that they've created [67.1s] themselves. Now, under this teams tab, [69.3s] we can hit the invite button, [71.7s] and you can see that this is very [73.5s] similar to, you know, a tenant admin [75.8s] view. Um, but the difference here is [78.1s] that they don't have access to course [79.7s] editing. Um, being able to modify really [83.4s] much other than just being able to [85.0s] invite folks. Um, so you have courses, [88.2s] programs, and users. It's a very similar [91.7s] process. You can throw an email address [93.4s] in here to hit invite. You can even use [95.4s] the CSV templates for each of these tabs [97.8s] as well. [101.5s] This will also allow you to see who who [104.0s] you've sent invites to, who's still [105.5s] pending. Um, and even once they're in [109.3s] the platform, you can create a new team [111.3s] and add them to, you know, whatever [114.1s] company or team that they belong to, or [116.4s] add them to previously created teams as [118.6s] well. --- ## Global Roles URL: https://ibl.ai/docs/configuration/rbac/globalroles # Global Roles ## Purpose Assign platform-wide capabilities to individual users without building custom roles and policies. Global roles are set by tenant admins under **Management → Users**. --- ## Available Roles ### Analytics Viewer Grants access to the AI Analytics dashboard on skills.iblai.app. The user can see aggregate analytics for teams they have analytics access to through [team sharing](teamsharing.md). This does **not** grant access to analytics for specific mentors. ### Billing Manager Can view and manage platform credits (reloading credits, etc.). ### Can Sell Items Can configure items that can be sold on the platform. ### Create Teams Can create teams of individuals under **Management → Teams**. ### Enrollment Manager Can invite others to the platform. See [Enrollment Manager Assignment](enrollmentmanagerassignment.md) for detailed setup. ### List Teams Can list teams the current user has access to. Typically used in conjunction with other permissions that require selecting teams, such as viewing aggregate analytics. ### List Users Can list users on the platform. Typically used in conjunction with other permissions that require selecting users, such as when creating teams. ### Mentor Chat Can chat with **any** mentor in the tenant. See [Mentor Chat](mentorchataccess.md) for scoped, per-mentor chat access. ### Mentor Editor Can chat with any mentor and view/edit all settings and information about **all** mentors in the tenant. See [Mentor Editor](mentoreditor.md) for scoped, per-mentor editor access. ### Mentor Viewer Can chat with any mentor and view all settings and information about all mentors in the tenant (read-only). ### Notification Manager Can access alerts management and send notifications to users/teams they have access to through [team sharing](teamsharing.md). --- ## How to Assign a Global Role 1. Go to **Tenant Settings → Management**. 2. Open the **Users** tab. 3. Select the user you want to modify. 4. Check/Uncheck the desired roles. --- Assigning some roles through team sharing will automatically enable specific global roles for that user/group such as analytics viewer, notification manager, list team, etc as necessary. --- ## Groups URL: https://ibl.ai/docs/configuration/rbac/groups Video: https://www.youtube.com/watch?v=tecYyRpQWjI # Groups ## Purpose Use Groups to assign users a consistent set of permissions (policies) across the platform, and combine them with Teams to scope what data those users can see. --- ## Groups vs. Teams (Key Concepts) ### Groups - Collections of users that are assigned policies. - Policies define what actions users can take (e.g., view analytics, enroll learners, create teams). - Best for role-based access (e.g., account executives, enrollment managers). ### Teams - Collections of learners (e.g., Company A students). - Used to scope data visibility so managers only see their own learners’ data. - Best for data segmentation (Company A vs. Company B). --- ## Creating and Managing Groups 1. Go to **Tenant Settings → Management → Groups**. 2. Create a group by providing: - Group name - Description - Group members (add or remove at any time as a tenant admin) 3. Save the group. Anyone added to the group automatically inherits the policies attached to it. --- ## Assigning Policies to Groups 1. Open **Policies** in Management. 2. Each policy can have groups assigned to it. 3. Add a group by searching for it and selecting it. --- ## Example A group like **SBA Corporate Account Executive** may be assigned policies that include: - Enrollment Manager - Analytics Viewer / Reader - List Users - List Teams - Create Teams - Access to all user reports This allows group members to: - View users and teams. - Create teams. - View and download analytics. - Enroll learners by sending invitations. --- ## Using Groups Together with Teams (Scoped Access) If you want someone to: - Have a role like **Analytics Viewer**, but - Only see data for a subset of learners (e.g., Company A only), Then you should: 1. Create a **Team**. 2. Add the learners who belong to that company or cohort. 3. Create or use a **Group** - Example: *Company A Analytics Viewer*. 4. Assign the **Team to a Policy**. The policy links: - The analytics role - The specific team 5. Add users (or a group) to that policy. --- ## Result Company A managers: - Can see only Company A’s analytics. - Cannot see Company B’s data. - Cannot invite users or manage content unless explicitly allowed. --- ## What Groups Enable Depending on assigned policies, group members can: - View lists of users and teams. - Access analytics dashboards and reports. - Create and manage teams. - Enroll users into content via invitations. What they cannot do is determined entirely by the policies attached to their group. --- ## Result Groups provide clean, role-based permission management, while Teams ensure data stays properly segmented. Together, they give you fine-grained control over who can do what and which learners’ data they can see. ### Transcript skillsAI | Groups https://www.youtube.com/watch?v=tecYyRpQWjI [1.7s] So we have the concept of two different [3.6s] types of groups. We have a group in the [7.0s] sense that these are group of folks that [8.8s] are assigned a specific policy [11.4s] and then we also have teams. Now with [14.0s] groups you can assign people to a group [17.6s] with a certain set of policies attached. [19.5s] So for instance this SBA corporate [21.3s] account executive they are able to [23.8s] access analytics and also enroll [26.1s] learners into content. [29.1s] The way we set this up is just by simply [31.5s] giving a group name, description, and [33.8s] adding group members. You can add a [36.1s] member at any time as a tenant admin. [38.6s] Um, and those people who are assigned [41.1s] here will have access to specific [43.8s] policies. Now in this case this policy [46.6s] is going to include the enrollment [48.2s] manager [50.2s] analytics viewer list users list teams [55.0s] analytics reader create teams and all [58.6s] user reports. So if we open one of these [61.0s] you can see that there are groups [62.2s] assigned. You can simply just add a [63.8s] group by searching and clicking on the [65.3s] one you want to add and they'll have all [67.0s] those same policies assigned to them. [69.3s] This is going to ensure that they can [71.0s] access not only the list of users that [73.3s] exist but the specific teams in addition [76.4s] to getting all reports [78.9s] um being able to create teams. [81.9s] You can also view analytics about your [84.5s] specific users and even enroll them into [86.5s] content by sending out invites. [89.9s] Now the way this works in terms of of [92.8s] teams, [95.0s] if you want somebody assigned to a [99.0s] particular policy, let's just say it's [100.4s] analytics viewer, but you only want them [102.1s] to see a subset of that data. So company [104.2s] A versus company B, they shouldn't have [106.2s] any data crossing over you. Company A [108.6s] should only see company A's data. The [111.0s] way that would work is you create a new [112.6s] team, put in a description, add the [115.4s] students who will be inside of this [117.4s] team, and then you can assign that team [120.6s] to one of these groups over here. And in [122.9s] this case, we have company A. And this [125.4s] is just basically a a company A group [129.4s] that can only see company A's analytics. [132.6s] Uh they cannot invite people. They [134.4s] can't, you know, add anybody courses or [137.4s] anything like that. they only have the [139.3s] ability to get analytics on their [141.2s] students. [143.0s] And you can see that with this here we [144.9s] have the company analytics viewer. This [146.5s] is the group that they're allowed to [147.8s] oversee. Um and you can add again either [151.8s] individuals or a group to a policy to [154.2s] oversee specific students. --- ## Learner Management URL: https://ibl.ai/docs/configuration/rbac/learnermanagement Video: https://www.youtube.com/watch?v=scbYDMffenI # Learner Management ## Purpose Set up learner managers (e.g., account executives or program overseers) who can manage enrollments, teams, users, and analytics across a tenant—without giving access to course content or invitation workflows. --- ## Where to Start 1. Open **Tenant Settings**. 2. Go to **Management**. This area is also used for inviting users to the platform, courses, or programs. --- ## Roles (Preconfigured) You don’t need to create these from scratch—several roles are already available, including: - Analytics Viewer / Reader - List Teams - List Users - Create Teams - Read Teams - Download Analytics Reports These roles collectively allow a user to: - See users and teams across the tenant. - Create and manage teams. - View analytics dashboards. - Download reports. --- ## Policies Policies bind roles to resources (e.g., platform data, users, teams, analytics). - Dedicated policies already exist for learner management / account executive use cases. - These policies grant: - Visibility into users and teams - Access to analytics and reports - Team creation and management --- ## Groups (Recommended Assignment Method) To simplify setup, a group is already configured and assigned to the correct learner-management policies. --- ## How to Grant Access 1. Open **Groups** in **Management**. 2. Select the **learner management / account executive** group. 3. Add users who should oversee enrollments, teams, and analytics. 4. Click **Save**. Because the group is already tied to the right policies, adding a user here is all you need to do. --- ## What Learner Managers Can Do (User View) ### Management Access Once added to the group, the user will see the **Management** tab and can: - Create teams (e.g., “Company A Team”). - Add members to teams. - Edit or delete teams they created. - View other teams across the tenant (read-only where applicable). - Understand which users belong to which teams. ### Analytics Access Learner managers can: - View analytics dashboards (when data is available). - Download reports showing: - Course enrollment - Learner progress - Participation and completion metrics These users can oversee learners and outcomes without modifying courses or sending invites. --- ## Ideal Use Case - Corporate or institutional staff who need oversight, not content control. - Account executives managing cohorts or client learners. - Program managers tracking progress and engagement. --- ## Result By assigning users to the **learner-management group**, you enable controlled, tenant-wide visibility into learners, teams, and analytics—supporting oversight without risking course or content changes. ### Transcript skillsAI | Learner Management https://www.youtube.com/watch?v=scbYDMffenI [1.7s] I will be showing you how to set up [4.3s] managers who will be overseeing [7.0s] enrollments, analytics, um, and so on [10.0s] and so forth with, um, these new rules [14.2s] and policies we have implemented. The [16.6s] first thing is you're going to open up [17.7s] your tenant settings and go to [19.2s] management. This is also where you can [21.0s] invite users um, to the platform, [24.4s] courses or programs. [27.0s] Now, the first thing here is we're going [28.4s] to go to roles. [30.6s] And we've set up a couple of different [32.3s] roles uh so you won't have to worry [34.6s] about setting these up in the future [35.9s] yourselves. Um we have the analytics [38.2s] viewer role, read teams, analytics [41.1s] reader, [42.6s] um list teams, list users, and create [45.5s] teams. Essentially, what this is going [47.3s] to do is allow you to see all the or [49.5s] assign roles to a policy that will allow [52.8s] um folks assigned to set policy to see [55.7s] teams, see users across the the tenant, [60.6s] um list them, create them themselves, [63.8s] see analytics across the board, um so [66.9s] and download reports. So if we go to [69.2s] this policies over here, [71.8s] you can see that we have a couple of [74.5s] different ones specifically meant for [76.6s] this account executive slash overseeing [80.3s] enrollments and analytics for uh users [84.2s] across the board. Um and essentially the [88.2s] way this works is you're just assigning [89.8s] the role to a policy and making sure [91.5s] that there's a resource selected. In [93.2s] this case it's uh you know the platform [95.6s] the platform data users teams so on and [98.4s] so forth. [100.2s] So what we've done uh to make this [102.6s] simple is created a group assigned to [106.2s] the relevant policies for this this [109.4s] account executive role which should be [111.6s] able to have enrollment access as well [114.2s] as [115.8s] um being able to view analytics about [119.1s] users on the platform. Um this will also [122.0s] allow them to create teams which is [123.6s] going to be a collection of users uh [125.3s] that folks can oversee. Um so this will [128.2s] be useful for you know a company who [130.1s] wants to manage their learners but [132.2s] shouldn't be touching you know course [133.7s] content you know sending invites or [135.5s] anything like that. Um but the way this [138.9s] will work the way you'll assign this to [140.6s] folks is through this group. what [144.6s] because this group is already assigned [146.1s] to all the proper policies. All you'll [148.8s] need to do is add anybody who should [150.4s] have access [152.5s] um to you know being able to enroll, [154.7s] managing users, so on and so forth um at [158.2s] a tenant level. Uh you'll just add them [161.0s] to the members here. And now I'll show [163.4s] you what that looks like from from the [165.8s] side of a a corporate account executive. [169.1s] So, I'm just going to copy this [173.0s] and we'll log in as that user. [183.8s] So, I have logged in as the user who [186.1s] I've assigned to that account executive [188.3s] role. Um, and the first thing we'll see [191.2s] here is that we'll have access to this [193.5s] management tab. This will allow us to [196.1s] create new teams. uh you know this could [199.1s] be like company a [203.4s] uh company a team and you can add [207.6s] members to said team uh across the [210.2s] board. So I'll add just a few folks here [216.4s] let's see [219.4s] so we've got you know just a few folks [221.0s] in this team and we'll hit create team. [225.9s] We can now edit these as well um and [229.8s] delete them as long as we've created [231.4s] them. Now, this user has access to teams [233.8s] that um have not been created by them, [237.4s] but they can, you know, you know, see [239.6s] who's in the team and whatnot. Um and [243.8s] you know, manage whoever is in this team [246.0s] that they've been assigned. um or you [249.0s] know see the teams across the board and [251.3s] see you know who belongs to what team [252.9s] what company has oversight over which [255.3s] teams so on and so forth. [258.7s] So the same user who is a executive um [263.7s] that can invite folks and create teams [266.6s] and assign people to said teams can also [269.7s] view analytics. Now these dashboards are [272.6s] currently based off tour so you won't [274.6s] see any analytics here but you have [277.5s] access to these data reports that you [279.3s] can download see you know who's in you [282.4s] know what which [284.7s] courses you know what their progress [286.5s] looks like so on and so forth and [289.1s] through these different reports Here. --- ## LLM Assignment URL: https://ibl.ai/docs/configuration/rbac/llmassignment Video: https://www.youtube.com/watch?v=VLKQ_tX2L9k # LLM Assignment ## Purpose Assign specific LLMs to individual users or groups so mentor editors only see the models they are authorized to use, rather than every model available to tenant admins. --- ## Prerequisites - The user must already have **mentor editor** access (visible on the mentor's **Access** tab) - A role with LLM-related actions must exist (or you will create one) --- ## Create an LLM Access Role 1. Go to **Tenant Settings → Management → Roles**. 2. Create a new role (e.g., "LLM Model Access"). 3. Assign the actions: - **Read** all LLMs - **Select** from available LLMs --- ## Create or Edit a Policy 1. Navigate to the **Policies** tab. 2. Click **New Policy** (or edit an existing one like "LLM Model Access"). 3. Configure the policy: - **Policy name**: descriptive label - **Role**: select the LLM access role created above - **Platform**: select your tenant - **LLM(s)**: choose one or more models (e.g., OpenAI GPT-4o Mini, Google, Perplexity) 4. Under **Users/Groups**, add the specific user or group. 5. Click **Save Policy**. --- ## Verify as the User 1. Log in as the assigned user. 2. Open a mentor and click the **LLMs** tab. 3. Confirm that only the assigned models appear (e.g., GPT-4o Mini, Google, Perplexity). 4. Tenant admins will still see all LLMs; restricted users see only their assigned models. --- ## Scope Options - **Platform-wide**: the user sees the same limited LLM set across all mentors - **Per-mentor**: restrict LLM access to specific mentors by adjusting the policy resource to a particular mentor rather than the whole platform --- ## Key Takeaways - **Roles** define what actions can be taken (read, select LLMs) - **Policies** bind a role to specific LLM resources and assign users or groups - LLM restrictions apply immediately upon the user's next login - Use **groups** to manage LLM access for multiple users at once --- ## Mentor Chat Access URL: https://ibl.ai/docs/configuration/rbac/mentorchataccess Video: https://www.youtube.com/watch?v=jt8fL7vROcI # Mentor Chat Access ## Purpose Restrict a user or group to chatting with a specific mentor without exposing other mentors or settings on the platform. Useful for providing targeted access to students or external users. --- ## How It Works A pre-configured **role** and **policy** limit the user to: - **Chat** with a specific mentor - **Read** the mentor name and outputs needed for chatting - **No access** to settings, other mentors, or admin features --- ## Setup 1. Go to **Tenant Settings → Management**. 2. A role has been pre-created with the following permissions: - **Actions**: chat with mentor, read mentor metadata - **Data actions**: read permissions required for chatting 3. Navigate to the **Policies** tab. 4. Click **Edit** on the associated policy. 5. Verify the policy includes: - The correct **role** - The correct **platform** (tenant) - The specific **mentor** resource 6. Under **Users**, add the user who should have chat-only access. - Alternatively, create a **group** and add the group to the policy. 7. Click **Save**. --- ## User Experience When the restricted user logs in: - The **mentors list** shows only the assigned mentor(s) - The **dropdown** shows only "New Chat" (no other options) - The **Explore** page shows no additional mentors (if the mentor is set to admin-only visibility) - The user can **chat normally** with their assigned mentor --- ## Adding New Users As new users register: 1. Go to the policy and click **Edit**. 2. Add the new user to the **Users** section. 3. Save — the user can start chatting immediately. --- ## Key Takeaways - The role and policy are **pre-configured** — you only need to add users - Users see **only** the mentor(s) assigned via their policy - Use **groups** to manage access for multiple users at once - This is for **chat-only** access — no settings, editing, or admin features are exposed --- ## Mentor Editor URL: https://ibl.ai/docs/configuration/rbac/mentoreditor Video: https://www.youtube.com/watch?v=WpJBhNB-2xs # Mentor Editor ## Purpose Grant edit access to a specific mentor without elevating a user’s tenant-wide permissions. This lets instructors or tenant admins collaborate on a mentor while keeping access tightly scoped. --- ## Overview - Users without editor rights (e.g., students) cannot edit any mentors by default. - Tenant Admins can edit any mentor and assign editor access. - Instructors can grant editor access only to mentors they own. - Editor access is mentor-specific—not tenant-wide. --- ## Granting Editor Access (Owner/Admin) ### 1. Open the Mentor - Go to the mentor you want to share (e.g., **Socratic Mentor**). ### 2. Open Access Settings - In **Mentor Settings**, select the **Access** tab. ### 3. Assign the Editor Role - Use the existing **Editor** role (or create one if needed). - Add the user (by email/username) to the **Editor** role. - Click **Save**. This grants edit rights **only for this mentor**. --- ## What Editors Can Do Once assigned, the user can: - Edit settings - Change the LLM - Update system prompts - Add/remove data sets - Enable/disable tools They cannot: - Access tenant settings - Edit other mentors they weren’t granted access to --- ## Verifying Access (Editor’s View) - The user remains a student (or non-admin) in profile. - The shared mentor shows **Edit** options in the dropdown. - Other mentors still show **Chat only** (no edit access). --- ## Notes & Best Practices - Use editor access for collaboration without over-privileging users. - Prefer mentor-level editors over tenant roles to minimize risk. - Review editor assignments periodically from the mentor’s **Access** tab. --- ## Result You can safely collaborate by giving users precise, mentor-level edit access—no broader permissions required. ### Transcript mentorAI | Mentor Editor https://www.youtube.com/watch?v=WpJBhNB-2xs [1.4s] I'm going to be walking you through how [3.8s] you can give edit access to a given user [6.6s] within your tenant um to a mentor that [9.9s] you as an instructor have created or if [12.1s] you're a tenant admin um any mentor that [14.7s] exists on the platform. So I'm going to [16.8s] be showing you from the perspective of a [19.0s] person we want to edit a mentor before [21.3s] we apply the role. So if we see here [24.2s] under our profile this person's just [25.8s] considered a student. they have no [28.5s] ability to access um any editing [31.6s] settings um across any of the mentors [34.6s] within this given space. [37.0s] So what I'm going to do is go back to [39.4s] the Socratic mentor [44.4s] and what we'll do now is look at the [47.0s] tenant admin perspective um or the owner [50.4s] of a given mentor's perspective to show [52.6s] you how you can give edit access to [54.4s] other people. [56.8s] So now we're going to take a look at the [58.1s] tenant admin perspective. Um so you can [61.0s] see here that I have you know admin [62.8s] access over my tenant. Um you can even [65.2s] see here on my profile that it says [66.6s] admin. So this person is going to have [69.3s] access to edit and create as many [71.3s] mentors as they want. Um also edit any [74.2s] mentors within the given platform as it [76.1s] stands. Now if you're an instructor and [78.2s] you want to give somebody access um to a [80.9s] mentor you've created, you would only be [83.0s] able to do that for mentors that are [84.7s] under your ownership. [87.2s] Now you can see here um under the mentor [89.9s] settings we have this access tab. You [94.1s] can create different roles and even add [97.0s] people to roles that already exist. In [98.9s] this case we already have editor and [100.9s] we'll click on actions. Now we're going [103.4s] to add the same user that we saw that [107.6s] was just a normal student and had no [109.2s] edit rights to any given mentor on the [111.3s] platform. This is going to give them [113.3s] access exclusively to this singular [115.9s] mentor within this dedicated space. So [118.8s] now we're going to exit out of this. And [121.1s] now we're going to go back to the [122.6s] perspective of this individual user. Um [125.8s] so you can see that they'll be able to [127.1s] edit this mentor but nobody else's. [132.4s] Now we are back to the individual user [134.9s] role. You can see here that this is [137.0s] still considered a student user. Um and [140.3s] we still can't access any of the tenant [142.1s] settings for example. Now if we click on [144.8s] this little drop down you can actually [146.3s] see that we are able to actually edit [148.0s] this mentor. [149.8s] Um so you know if we wanted to change [151.5s] the LLM we can always select a different [154.3s] mentor here. Um you have settings [158.5s] you can even edit the prompts and add [161.4s] data sets [163.4s] enable tools so on and so forth. [167.6s] So now if we wanted to [171.1s] look at another mentor to see if we can [173.0s] actually edit it, we shouldn't be able [174.7s] to. So if we click on this, you can see [177.8s] that we only have the new chat. We don't [180.2s] have access to edit this one because we [181.8s] were only given permissions to edit that [183.9s] singular mentor, that Socratic mentor. --- ## Team Manager Assignment URL: https://ibl.ai/docs/configuration/rbac/teammanagerassignment Video: https://www.youtube.com/watch?v=3l7oPOtcogI # Team Manager Assignment ## Purpose Restrict visibility so external partners (e.g., companies purchasing courses) can only see analytics for their own learners, not data from other organizations or teams. --- ## Overview This setup uses roles, policies, and groups to grant scoped analytics access. Team managers can view reports for the learners they oversee—nothing else. --- ## Setup Steps ### 1) Create or Use a Team-Scoped Role - Go to **Tenant Settings → Management → Roles**. - Use (or create) a role like **Company Analytics Viewer** with actions such as: - Read analytics and reports - Read specific user groups - This role should **not** include content editing or invitation permissions. ### 2) Bind the Role to a Specific Group (Policy) - Navigate to **Management → Policies**. - Create (or edit) a policy for the company/team (e.g., **Company A**). - Assign the **Company Analytics Viewer** role. - Set the **Resource** to: - The platform - A specific user group (the team this company should see) - Click **Save**. This resource binding is what limits visibility to only that group’s learners. ### 3) Assign Users to the Policy - In the same policy, add: - Individual users, or - A group that represents the company’s managers - Click **Save**. Anyone added here gains analytics access **only** for the selected group. --- ## What Team Managers See (User View) - Access to **Analytics dashboards**. - Ability to **filter reports** by the groups they oversee. - Reports and dashboards show **only their assigned learners**. - **No visibility** into other companies’ users or data. - Current dashboards focus on **mentor analytics**; course dashboards will be added later. --- ## Example Outcome A manager assigned to **Group B**: - Sees analytics filtered to **Group B only**. - Cannot view data for **Group A** or any other teams. --- ## Result By pairing a **team-scoped role** with a **group-bound policy**, you ensure external managers have the insights they need—without exposing any other learner data. ### Transcript skillsAI | Team Manager https://www.youtube.com/watch?v=3l7oPOtcogI [1.4s] Let's say you have a company who [2.9s] purchases courses from you. You don't [5.2s] want them to see other, you know, [7.1s] folks's data. You only want them to see [9.0s] the data for their subset of learners [11.3s] that they have purchased courses for. [13.8s] [snorts] The way to set this up is under [17.6s] this management tab in the tenant [19.3s] settings. Now we already have this set [21.5s] up and this is the model that you can [23.1s] follow um for subsequent uh [27.4s] mentor/manager roles that are only [29.4s] overseeing um a specific team. Um in [32.9s] this case we have the role setup for [35.4s] this which is the company analytics [38.1s] viewer and you can see in the actions [40.5s] here we have the ability to read um [43.0s] reports analytics and including uh the [46.9s] ability to read specific user groups. [49.3s] Now, where this be becomes connected to [51.9s] an actual user group and to specific [53.9s] users is under policies. You'll see [56.3s] [snorts] that we we have a couple of [57.5s] different types of policies here. In [60.1s] this case, company A. This should be say [61.9s] company B, but we'll leave it as is for [65.5s] now. Um, and you'll see here that we [67.7s] have the role assigned where we have [69.6s] those specific actions um set up for [72.9s] them to be able to view analytics. [75.7s] And [77.3s] we also have a couple of others. So [78.7s] we'll just click on company teams and [80.3s] then the resource here is what we're [81.8s] saying is a specific group. So the [84.1s] platform is you know this uh particular [86.5s] platform we're in and the user groups uh [90.0s] number here is referencing a specific [92.0s] group. Now [snorts] you can add folks [94.3s] here either by user or by a specific [96.9s] group. [98.4s] Um so we have you know company A u in [101.5s] this particular user here we'll just say [105.6s] company A again. Um, and then you can [108.1s] add as many folks to this group as you'd [109.8s] like. And then once you had hit save, [114.2s] the folks that you have in those either [115.9s] the groups or that you add individually [118.3s] will have edit act or not addit access, [120.6s] but apologies. Um, they'll have access [123.1s] to analytics for their particular users. [125.7s] Um, they won't see anybody else's and [127.4s] I'll show you what that perspective [128.4s] looks like here in a minute. [132.2s] So I'm currently logged into somebody [133.8s] who has access to group B um that we set [136.5s] up in the first half as a tenant admin [139.4s] when creating that group um to oversee [143.5s] you know a particular set of folks and [145.9s] you can see here that we have analytics [147.6s] and we can filter by the two groups we [149.2s] have access to. Um that being said, [151.4s] these are mentor related dashboards [153.4s] which will be um enhanced later with [156.3s] course dashboards. So you can visualize [158.2s] the data that you have access to in [160.5s] these reports. These reports will only [162.6s] be limited to the users in the groups/ [165.3s] teams that you oversee and you will not [167.2s] see anybody else's data. --- ## Team Sharing URL: https://ibl.ai/docs/configuration/rbac/teamsharing Video: https://www.youtube.com/watch?v=3I2Ozruusgw # Team Sharing ## Purpose Share an existing team with additional users—such as external managers or partners—so they can view learner data and analytics for that team without gaining edit, invite, or notification privileges. --- ## Overview Beyond CSV-based platform invitations, teams can now be shared directly from the Management UI. This lets you grant read-only and analytics visibility to users who did not create the team (and may currently be students). --- ## Setup Steps (UI-Based) ### Open Team Management 1. Go to **Tenant Settings → Management**. 2. Locate and open the team you want to share (e.g., **Company A**). ### Edit Team Permissions - Edit the team and add the user(s) you want to grant access to. ### Assign Permissions - **Read:** allows viewing the team and its members. - **Edit:** allows viewing and editing the team and its membership. - **View Analytics:** allows viewing enrollments, progress, and analytics for that team. - **Notification Manager:** allows managing alert settings for the tenant and sending notifications to the team. Assign **Read** and **View Analytics** together to enable full oversight of team data (without edit powers). ### Add Users or Groups - Add **individual users** (e.g., a company manager who currently exists as a student), or - Add a **group** to bundle multiple managers together for easier management. ### Save - Once saved, the added users can log in and view analytics **only for the assigned team**. --- ## What Shared Users Can (and Can’t) Do ### Can - View team membership. - View analytics, enrollments, and learner progress for their team. ### Cannot (with Read + View Analytics only) - Edit the team or its membership. - Manage alert settings or send notifications. - Access other teams’ data. --- ## Result Team sharing lets you grant scoped visibility to the right people—directly from the UI—so they can oversee their learners’ data without exposing or altering anything else on the platform. ### Transcript skillsAI | Team Sharing https://www.youtube.com/watch?v=3I2Ozruusgw [1.4s] In addition to being able to create [3.4s] teams and team managers via the CSV [5.9s] upload for platform invitations, we've [8.5s] also added a new way under this [10.1s] management tab to share teams to people [12.7s] who have not created them. So in this [15.4s] example, we'll say that you know me as a [18.1s] an account executive [20.2s] um creates a team for company A. I want [23.8s] to give company A [26.6s] um a manager that you know exists on the [29.9s] platform but is currently just a [32.2s] student. The way we would do that is by [34.6s] editing the team itself [37.0s] and giving this person the view [39.2s] analytics permission in addition to the [42.1s] read permission. What this is going to [44.0s] do is allow them to oversee their users [46.0s] and their data without being able to [47.9s] make edits on the platform, send [49.6s] invitations, send out notifications or [52.3s] anything like that. [54.7s] So, you can do the read permission like [57.0s] I said and then we'll just add a user [59.0s] here [64.9s] and we're just going to add a random [66.4s] student of mine. Um, you can also add a [69.4s] collection of users. You can create a [71.0s] group. Um, so this will provide them [73.4s] with, you know, if you you can bundle [76.3s] people together in case you need a [78.1s] multiple managers per team, that's [80.6s] totally okay. You just add them to a [82.4s] single group to kind of simplify the [83.9s] process. Um, and what this basically [86.5s] does is say this user gets read access [91.0s] um for this specific team. [94.4s] So if we wanted to also give analytics [97.9s] access or sorry the view analytics [100.4s] access role then you would do the same [102.2s] thing you would add the same user here [104.2s] for anybody who needs to oversee a group [106.2s] of users data you know their enrollments [109.2s] progress so on and so forth you need to [111.4s] make sure that they receive both the [112.9s] view analytics and this [116.6s] read permission. Once that's set, [119.0s] they'll be able to log in and see just [121.4s] the the data for the students inside of [123.8s] their own team. They won't see anybody [125.4s] else's. [127.0s] And that's basically how you set this up [128.6s] through the UI rather than the the CSV [130.6s] file. --- ## RBAC Troubleshooting URL: https://ibl.ai/docs/configuration/rbac/troubleshooting Video: https://www.youtube.com/watch?v=BAExpMFykEw # RBAC Troubleshooting ## Overview A starting point for diagnosing issues when mentor editors cannot see certain settings or tabs, or when RBAC policies do not appear to be working as expected. --- ## Common Issue: Missing Tabs for Mentor Editors **Symptom**: A user with editor access cannot see certain tabs (e.g., Data Sets). ### Steps to Diagnose 1. Go to the mentor's **Access** tab. 2. Verify the user has the **Editor** role assigned. 3. If tabs are missing despite editor access: - Create a **test student user**. - Add the test user to the same **Access** tab with the same role. - Check which settings are visible to the test user. - Compare with what the original user sees. --- ## Verifying Policy Assignments ### Check Roles and Policies 1. Go to **Tenant Settings → Management → Roles**. - Note: mentor editor roles assigned via the **Access** tab won't appear here — they exist only on the mentor's Access tab. 2. Go to the **Policies** tab. 3. Look for the policy associated with the user. ### Verify the Resource Mapping 1. Open the browser **Network** tab (Developer Tools). 2. Refresh the page. 3. Search for the `check` endpoint. 4. The first result shows the **tenant** information. 5. The second result shows which **mentor** the policy resource number maps to. 6. Confirm the mentor number in the policy matches the correct mentor. --- ## Checking User and Group Membership 1. In the **Policies** tab, click on the relevant policy. 2. Check the **Users** section to confirm the user is listed. 3. Check the **Groups** tab to see if the user belongs to a group assigned to the policy. 4. Verify group membership by expanding the group to see all members. --- ## Verifying Platform and Mentor Resources - The **platform number** can be found by looking at default policies in the Policies tab. - The **mentor number** can be verified via the Network tab's `check` endpoint. - Ensure both the platform and mentor resource numbers in the policy match the intended targets. --- ## Key Takeaways - **Access tab roles** (editor) are separate from **Management tab roles** — check both locations - Use the **Network tab** to verify which mentor a policy resource number maps to - Always verify **user membership** in the policy's Users or Groups sections - If the setup looks correct but issues persist, escalate for deeper investigation --- ## Authentication URL: https://ibl.ai/docs/configuration/uiux/authentication Video: https://www.youtube.com/watch?v=ujpqTh1aQFQ # Authentication ## Purpose Customize what appears on your **login page** for the **Mentor**, **Analytics**, and **Skills** platforms. *(Example below uses Mentor.)* --- ## Steps #### Open Tenant Settings - Click your **profile icon** (top-right). - Select your **tenant name** from the dropdown. #### Go to Advanced - Open **Advanced → Authentication Interface**. #### Edit Login Content - **Title:** e.g., *Ashland’s mentors* - **Description:** e.g., *mentor AI for education* - **Display Logo:** upload an image file or paste an image URL - **Legal Links:** add URLs for *Privacy Policy* and *Terms of Service* **Carousel Images:** - Add one or more display images (upload or URL). - *(Optional)* Add **alt text** for accessibility (e.g., *mentor chat*). #### Save - Click **Save**. #### Preview - Log out to view your updated login page with: - Title - Description - Logo - Legal links - Carousel images --- ## Result Your customized **login page** is applied across the selected platform, reflecting the **title, description, logo, links, and images** you configured. ### Transcript ibl.ai | Login Customizations https://www.youtube.com/watch?v=ujpqTh1aQFQ [1.2s] Hello, it's Ashley here again and I'm [3.2s] going to be showing you um a new feature [5.6s] update that we have released um recently [8.2s] for customizing your login pages. Now, [11.2s] this is going to apply to our analytics, [13.6s] our mentor, and our skills platforms. [16.9s] And in this case, we're going to be [18.3s] walking through the mentor login page [20.4s] setup. So, what you'll first do is click [22.6s] on your little profile icon and click on [25.4s] your tenant name. Once you're here, what [28.0s] you'll do is go to advanced and then [30.4s] you'll see this authentication [31.7s] interface, which is this is where you'll [33.9s] be able to customize what is being [35.4s] displayed on the login page. You can put [37.9s] in a title. So, I'm going to say [39.1s] Ashland's [41.4s] mentors. [44.4s] And then you can also add in a little [45.9s] description that will display on the on [47.6s] the page as well. And I'm just going to [49.0s] say mentor AI for education. And then [55.5s] you can also add in a display logo, your [57.8s] legal links, and also update the [59.8s] carousel images. Um, when it comes to [62.1s] uploading images, you can either include [64.0s] a URL directly to the image or upload [66.3s] one yourself. And that in this case, I'm [68.6s] going to be uploading an image. [72.4s] So now we have this little logo in. And [75.0s] then for the privacy and policy or terms [77.5s] of service links, I have some ones that [79.5s] I can throw in right here. [84.6s] And then for the display images, this is [86.8s] going to be the images that show up in [88.3s] the carousel. You also have the option [90.2s] of putting in alternative text for [91.8s] accessibility. Um, just, you know, a [95.0s] basic description of what you'd like to [96.6s] see here. So, I'm going to include just [97.8s] a few images. I'm going to say mentor [101.6s] chat. [103.4s] And then we'll hit save. [107.0s] And now what we'll do is actually see [109.8s] these changes applied by logging out of [112.8s] the platform. [122.7s] And you can see all these changes have [124.5s] been applied here. And that's basically [126.7s] all you have to do to customize your [128.2s] login page. --- ## Custom CSS URL: https://ibl.ai/docs/configuration/uiux/customcss Video: https://www.youtube.com/watch?v=JJgVHqw0OHA # Custom CSS ## Purpose Apply custom CSS to brand your dedicated tenant beyond the default styling. Adjust button colors, backgrounds, toggles, and other visual elements. --- ## Access Advanced CSS Settings 1. Click your **profile icon** in the top right. 2. Click your **tenant name**. 3. In the **Organization** tab, scroll to **Advanced Tenant Settings**. 4. Locate the **Advanced CSS** field. --- ## Apply Custom CSS 1. Write or paste your CSS rules into the **Advanced CSS** field. 2. Click **Save**. 3. Exit the settings — changes apply immediately. ### Example Changes - Button colors (e.g., changing blue buttons to your brand color) - Learner/instructor toggle color - Background colors - Any element accessible via CSS selectors --- ## Other Organization Settings From the same Organization tab, you can also configure: - **Support link** - **Email address** - **Help center link** - **Logos** --- ## Key Takeaways - Custom CSS is entered in the **Advanced Tenant Settings** section - Changes take effect immediately after saving - You can override any default styling using standard CSS selectors - This applies tenant-wide to all users in your platform --- ## License Creation URL: https://ibl.ai/docs/configuration/users/Licenses/licensecreation Video: https://www.youtube.com/watch?v=6XnUAQ9ISAo # License Creation ## Set Up a Content-Provider Platform - In **Platforms**, click **Create Platform**. - Fill out: - **Display Name** – e.g., Licensing Demo - **Platform Key** – e.g., licensing-demo - Add initial **tenant-admin credentials** (username, email, password) - Click **Save**. - A new content-provider tenant and its admin account are now created. --- ## Log In as the Tenant Admin - Sign out of the super-admin account (if needed). - Log in with the **tenant-admin credentials** you just set up. - You’ll land on **Skills**. From there, you also have access to **AI Analytics**. --- ## Author Course Content for the Provider - Go to **Studio → Authoring**. - Open **Courses** and click **Add New Course**. - In the creation form: - **Course Name**: Licensing Demo (or similar) - **Organization**: auto-filled with the provider’s tenant - Click **Create Course**. - Add at least one unit (e.g., a multiple-choice problem). - **Publish** the course and set course dates in the past so it’s immediately accessible. --- ## Assemble Courses into a Program - Still in **Authoring**, choose **Programs → Add New Program**. - Enter a **program name**, e.g., Licensing Program. - Paste the **Course ID** of the course you just created. - Add more Course IDs as needed. - Click **Save**. --- ## Create a Program License (Super Admin) - Log back in as a **Super Admin**. - Open **Data Manager → Program Licenses**. - Click **Add Program License** and fill out: - **Display Name**: e.g., Licensing Demo - **Count**: total seats purchased (e.g., 10) - **Start Date / Expire Date**: set the access window (optional but recommended) - In the pop-up: - Select the **Platform** you created earlier - Select the **Program** you assembled - Click **Save**. - The program license is now active and enabled by default. --- ## Verify in Tenant Analytics - Log back in as the **Tenant Admin**. - Go to **Analytics → Licenses**. - The new license pool (e.g., Licensing Demo) appears in the list with seat count and dates. --- ## Assign Licenses to Groups or Individuals - In **Analytics → Licenses**, select the license pool. - Choose **Group** or **Individual** assignment. - For a **Group**, pick an existing group (e.g., IBL V1) and click **Assign**. - For an **Individual**, search for and select specific users. - The assigned seats now show under the **Groups** or **Individuals** tab. --- ## Key Takeaways - **Platform** = tenant for the content provider - **Courses → Program → Program License** is the content chain - **Seat Count** controls how many users can access the program - **Start/Expire dates** define the license window - **Tenant admins** assign seats via **Analytics → Licenses** to groups or users You’ve now created a content-provider tenant, authored courses, bundled them into a program, issued a license pool, and assigned seats—all within mentorAI. --- ## Program Assignment URL: https://ibl.ai/docs/configuration/users/Licenses/programassignment Video: https://www.youtube.com/watch?v=go-fSu6jLDg # Program Assignment ## Review the Program License A **Program License** has already been created with: - **Name** (e.g., “Content Licensing Demo”) - **10 seats available** - **Active status** - Assigned to the **ax Alpha tenant** - Includes specific course content --- ## Tenant Admin: Locate the License - Log in as the **ax Alpha tenant admin**. - Open **Analytics → Licensing → Content**. - Under **Content Licenses**, locate the license you just reviewed. - At this point, no users are yet assigned. --- ## Assign the License to a User or Group - Click the **license name** to open its details. - Choose **Assign**. - Select either: - **Individual User** – search and pick a single account - **Group** – assign seats to an existing group (all members get access) - From the **License dropdown**, pick **Licensing Demo** (or the license’s display name). - Click **Assign**, then **Close**. --- ## Confirm the Assignment - Back in **Content Licenses**, click the **license name** again. - The **user or group** you chose now appears in the assignment list. --- ## Verify as the Assigned User - Log out, then log in as the **user who received the license**. - Navigate to **Discover**. - Search for the licensed content title (e.g., **Licensing Demo**). - The **course or program** is visible and ready to launch. --- ## Quick Recap - **Tenant admins** see unassigned content licenses in **Analytics → Licensing → Content**. - Licenses can be distributed to **individuals or groups** from the license detail view. - **Assigned users** find the newly unlocked content in **Discover** immediately after assignment. With these steps, you’ve successfully allocated a program license and ensured the learner can access the content. --- ## User Assignment URL: https://ibl.ai/docs/configuration/users/Licenses/userassignment Video: https://www.youtube.com/watch?v=sU3uwf2fqEY # User Assignment ## Create a Group - Open **Groups** and click **Add Group**. - Enter a name (e.g., IBL V1). - Search for users, select the ones you need, and click **Add**. - New tenants may list only a few users at first; the list grows as accounts are added. - Bulk-select and advanced search (e.g., by company) are planned for future releases. - The group now appears in the list; expand its dropdown to view current members. --- ## Create a User-License Pool - Go to **Data Manager → User Licenses**. - Click **Add User License**. - Fill out: - **Name**: IBL Licensing Demo (or similar) - **Count**: 20 seats (or any number you need) - **Start Date / Expire Date** (optional access window) - **Select the Platform** that the tenant admin belongs to - Click **Save**. - The license pool is now available to that tenant admin inside **Analytics → Licenses**. --- ## Assign the License Pool to a Group - Log in as the **Tenant Admin** and open **Analytics → Licenses**. - Choose the license pool you just created (Licensing Demo). - Select **Group assignment**. - Pick the group you created earlier (IBL V1) and click **Assign**. - All members of IBL V1 now hold active seats. --- ## Verify the Assignment - In **Analytics → Licenses**, switch to the **Groups** tab to confirm the pool is linked to IBL V1. - The **Individuals** tab remains empty because you assigned seats at the group level. --- ## Quick Recap - **Groups** let you bundle users for simpler license management. - **User License Pools** define seat counts and (optionally) start/expire dates. - **Tenant admins** assign pools to **Groups** or **Individuals** from **Analytics → Licenses**. - You can always add users to a group later; their licenses activate automatically. --- ## Management URL: https://ibl.ai/docs/configuration/users/Teams/management Video: https://www.youtube.com/watch?v=YCnrjHpTwcM # Management ## Accessing the Team-Manager Dashboard - Sign in with a **Team Manager** role. - You land on the **Reports** section by default. --- ## Reports for Individual Learners ### Filter Options - Set a **date range**. - Search by **pathway**, **learner name**, or **enrollment status**. - Click **Apply Filters** to update the list. - Remove filters to view every learner you manage. ### Download - Click **CSV** to export the filtered (or full) learner list. --- ## Group Reports - Shows all teams you manage (e.g., five groups in this demo). - Use the same **filter bar** to narrow results. - Export a **CSV** for group-level data. --- ## Notifications *(Covered in a separate demo, but key points included here)* - Send messages to specific **learners**, one or more **teams**, or an **individual**. - Choose delivery channel: - **Email** - **Events** (in-platform) - **Others** as enabled - Optional: **schedule** a send date. - Type your **message** and **preview** it before sending. --- ## Assignments - You can assign any of the following to learners or entire teams: - **Courses** - **Programs** - **Pathways** - **Skills** - **Credentials** ### How to assign: - Select the **item type** (e.g., Course). - Choose the specific **course**, **program**, etc. - Search for a **learner** or select a **team**. - **Confirm** to add the assignment. - All **active assignments** appear in the list below. --- ## Downloads - Access the same report types tenant admins see, but scoped only to the **learners and teams** you manage. - Use these files for **deeper analysis** or **record-keeping**. --- ## Key Takeaways - **Reports** give learner-level insights with flexible filtering and CSV export. - **Group Reports** mirror that functionality for entire teams. - **Notifications** let you reach learners directly from Analytics. - **Assignments** enable quick enrollment in courses, programs, and more. - **Downloads** provide segmented analytics identical to tenant-admin reports—only limited to your own teams. With these tools, a **Team Manager** can monitor progress, communicate with learners, and assign learning paths—all without tenant-admin privileges. --- ## Setup URL: https://ibl.ai/docs/configuration/users/Teams/setup Video: https://www.youtube.com/watch?v=Nk5Z-u0Kjsc # Setup ## Create a Learner Group - Open **Groups** and click **Add Group**. - Enter a name (e.g., IBL V1). - Search for users and select the members. - You can **bulk-import via CSV** if you have many users. - Click **Add** to save the group. - The group now appears in the list; expand its dropdown to confirm the members. --- ## Create a Team-Manager Group - Still in **Groups**, click **Add Team Manager**. - Give the manager group a name (e.g., Team Manager Mar 17). - Select one or more users who will act as managers. - Click **Add**. - This group defines everyone authorized to manage the team’s learners. --- ## Assign the Manager Group to a Team - Open **Team Management**. - Click **Assign User Group to Team Manager**. - Choose the learner Team you just created (e.g., IBL V1). - Select the Team-Manager Group (e.g., Team Manager Mar 17). - Click **Add**. - The chosen managers now oversee that team. - Multiple manager groups can be linked to a single team if needed. --- ## Edit or Update Later - **Groups**: add or remove learners anytime. - **Team-Manager Groups**: add or remove managers as roles change. - **Team Management**: reassign or revoke manager groups from teams. - All changes take effect immediately across **Analytics** and **reporting**. --- ## Quick Recap - **Groups** gather learners for easier management. - **Team-Manager Groups** bundle the people who supervise those learners. - **Team Management** ties the two together, allowing managers to view reports, send notifications, and assign learning tasks for their designated team. With these steps, you’ve set up a learner team, assigned its managers, and preserved the flexibility to adjust membership at any time. --- ## Groups URL: https://ibl.ai/docs/configuration/users/groups Video: https://www.youtube.com/watch?v=sU3uwf2fqEY # Groups ## Create a Group - In the admin console, open **Groups**. - Click **Add Group**. - Enter a name (e.g., IBL V1). - Search for and select the users you want in the group. - If your tenant is new, you may have only a few users; groups grow as more users are added. - Click **Add** to save the group. - You can reopen the group’s dropdown at any time to view or add members. --- ## Edit an Existing Group - Open **Groups** and select the group’s dropdown. - View current members. - Add or remove users as needed. - Multi-select and advanced search options (e.g., by company name or other attributes) are coming soon. --- ## Create a User-License Pool (Data Manager) - Switch to **Data Manager → User Licenses**. - Click **Add User License**. - Fill out: - **Name** (e.g., IBL Licensing Demo) - **Count** – total seats, e.g., 20 - **Start Date / Expire Date** (optional) - **Platform** – choose the platform this license pool belongs to - Click **Save**. - The new license pool now appears in the tenant admin’s **Analytics** view. --- ## Assign Licenses to a Group (Tenant Admin) - In **Analytics**, open **Licenses**. - Select the license pool you just created (e.g., Licensing Demo). - Choose **Group assignment**. - Pick the group you created earlier (IBL V1). - Click **Assign**. The licenses are now distributed to all members of that group. To verify, open **Groups → your group → Licenses**; the assignment is listed there. --- ## Key Points to Remember - **Groups** simplify license management—assign once, cover many users. - **User-license pools** control seat counts and expiration windows. - You can always: - Add more users to a group. - Reassign or retire licenses by changing group membership or license pools. - Upcoming features will make bulk user selection and advanced searching even easier. --- With these steps, you can quickly organize users into groups and allocate licenses in MentorAI Analytics without manual per-user work. ### Transcript ibl.ai | User Groups Licenses https://www.youtube.com/watch?v=sU3uwf2fqEY [0.8s] let's talk about how you can create [2.4s] groups that can be later used to assign [4.4s] licenses to so the first thing you're [6.4s] going to do is go to [7.8s] groups um and then you'll hit add group [11.4s] so I'm going to get this the name [14.0s] IBL [15.6s] um V1 I don't know you can make the name [18.8s] whatever you'd like it to be um and then [20.6s] you can search for users I'm just going [22.0s] to quickly select mine and I'm using a [24.9s] previously created tenant that has a lot [26.6s] of users already populated um so that [29.0s] way we can create groups um you know [31.7s] newer tenants might not have any users [33.5s] so once people start to populate their [35.9s] you know their own users into their own [38.0s] tenant you'll be able to see a lot more [39.9s] users we'll hit [42.2s] add so we've added that and if we click [47.2s] on drop down we can see this license [51.0s] that we just created and we can see [52.5s] these two users that we've added if we [54.6s] want to add more users later we always [57.0s] can um we will be also adding a way to [60.4s] multi- select users when creating groups [62.4s] as well um including the ability to [64.7s] search um by more information you know [67.0s] maybe it's the by the company name or by [70.6s] any other sort of you know attribute [73.6s] that's attached to that [75.4s] user so we've got our group created and [79.2s] if we go to you know any of the users or [83.2s] the content so if we hit users um we can [86.6s] either assign a license we've already [88.4s] got one created you can see who who [90.0s] already been assigned that license um if [92.2s] there's a group already there you can [94.3s] also see those [96.2s] groups although we already have a [98.1s] license here what I actually am going to [99.6s] do next is show you exactly how to [103.1s] assign a new user license to um a given [106.4s] tenant so that way the tenant can later [108.4s] go and assign licenses so if we go to [112.1s] data manager which is [117.2s] this we'll go down to we'll find groups [121.0s] actually sorry user [126.5s] licenses and we can see that there are a [128.4s] few already created so we're going to [130.6s] hit add user license we're going to do [133.7s] IBL [135.2s] licensing demo as the name here and [139.0s] we're just going to give this a count of [140.3s] 20 you can also have a start an uh [143.1s] expire date here as well um the start [145.4s] data is when access starts and expired [147.2s] is when that license expires um [150.9s] but after we have [152.7s] this we'll essentially just select the [158.9s] platform and assign it to the the [162.0s] platform that this tenant admin is [164.0s] currently in and so we'll hit [166.5s] save and then then we have that license [169.1s] now that we've created this user license [171.1s] this will now appear in that tenant [172.6s] admin's analytics platform so we'll gock [176.4s] over to licenses in Analytics as that [180.2s] tenant [181.5s] admin and you can see this is the [183.9s] license that we just created now if you [185.6s] have dates it won't say invalid dat for [188.6s] instance like you can see here this is [190.2s] the start end date um I didn't add them [192.6s] when we were creating this license just [194.0s] to quickly be able to create this to [195.6s] show you um now that we have that [198.5s] license created we can select from here [201.8s] which license we want so we're going to [203.0s] hit licensing demo and you have the [205.6s] option of using groups or individuals so [208.2s] we're going to do a group and we're [210.2s] going to do IBL V1 and then hit [215.7s] assign and now we have those licenses [217.8s] assign so if we go back over here [219.4s] there's no individuals but if we go to [221.1s] groups we will see that group we just [224.1s] created and those folks in that group [226.6s] now have user licenses to be able to [228.9s] access the platform --- ## Invitations URL: https://ibl.ai/docs/configuration/users/invitations Video: https://www.youtube.com/watch?v=Vu_Ws0Ex4Dc # Invitations ## Purpose Invite an **existing learner** to a specific **course** from the **Data Manager**. > **Note:** The same workflow applies to **Program invitations** and (when the user is known) **Platform invitations**. - Use **bulk invitations** (tenant admin) or **placeholder invitations** (super admin) for **unknown platform users**. ## Steps ### Open the Invitation App - In **Data Manager**, navigate to **Catalog Invitation Apps** ### Choose Invitation Type - Click **Add Course Invitations** - *(For programs, choose **Add Program Invitations**; for platforms, choose **Add Platform Invitations**)* ### Select the Learner - **Search for and pick** the user; they must **already exist** on the platform - Leave the **Email field empty** (not required if the user is found) ### Confirm Status - Ensure the **Active toggle** is **on** ### Pick the Course - Choose the **course** the learner should join ### Save - Click **Save** - A **confirmation entry** confirms the course invitation was created ## Result The learner now has an **invitation to the selected course**, issued directly by a **super‑admin**. --- # Bulk Invites ## Purpose Invite users to your **tenant** by uploading a **CSV**, reviewing/editing entries, and submitting the invitations—all from one place. ## Steps ### Open Tenant Invites 1. Click your **profile dropdown** and select the **tenant name**. 2. Go to **Management → Invite**. ### Get the Template (Optional) - Click **Download CSV Template** to get a starter file you can fill out. ### Upload the CSV - Click **Upload CSV** and choose your file. ### Review and Edit - Update any values directly in the grid. - Add rows for additional users you forgot to include. - Delete rows if needed. - You can **edit both columns and rows before submitting**—use this to validate that everything looks right. ### Submit - Click **Submit**. - The uploaded users appear in the list as **Pending** until they register. ## Result Your **CSV entries** are imported as invitations for the selected tenant, with each invite **pending until the user completes registration**. ### Transcript AI Skills | Super Admin | Create User Invite https://www.youtube.com/watch?v=Vu_Ws0Ex4Dc [0.5s] all right so I'm going to show you how [2.0s] to send an invitation um as a super [5.9s] admin from the data manager to a learner [8.9s] um so we'll first go to the catalog [11.2s] invitation apps and the same method is [13.4s] going to apply for course platform and [15.5s] program [16.4s] invitations um if we know the user [18.8s] specifically for platform invitations [21.0s] otherwise you'll use the bulk um [25.3s] invitation creation method for platforms [27.6s] as a tenant admin or the placeholder [30.3s] invitation as a super admin that we've [32.8s] shared in a previous video so in this [35.3s] case we're going to go with a course [37.3s] invitation and we'll hit add course [41.0s] invitations and now if we're doing a [43.7s] course or a program invitation the [45.1s] assumption is that users is already in [46.7s] the platform which means we can search [48.6s] for a user um so we'll select the user [51.7s] that we want to invite to a [54.6s] course um and since we have that user we [57.0s] don't need to worry about the email um [60.4s] so we'll make sure that they're active [62.7s] as well they are uh if there's [65.9s] [Music] [67.0s] a course that we want to select here so [70.2s] we need to select a course for them to [71.5s] be able to get an [73.0s] invitation um to a specific course so [77.1s] we'll select this course and we'll hit [80.7s] save and now you'll see that this [84.1s] invitation has been created um by super [86.7s] admin to in invite a learner to a course --- ## Chat Metadata Pass-Through URL: https://ibl.ai/docs/developer/agents/chat-metadata # Chat Metadata Pass-Through Pass arbitrary context alongside chat messages to any AI Mentor so it can tailor responses to the user's exact situation. --- ## Overview The chat metadata feature lets you pass arbitrary context alongside chat messages to any IBL Mentor. This context (e.g., product name, plan tier, user role) is automatically injected into the mentor's awareness, allowing it to tailor responses to the user's exact situation without the user having to explain their context manually. **Key points:** - The `metadata` field is a **free-form JSON object** — any key-value pairs are accepted, no enforced schema - **Optional** — if omitted, the mentor behaves as before - **Persistent per session** — send it once and it sticks for the entire conversation - **Available in analytics** — stored with the conversation for reporting and data exports --- ## How It Works ``` Frontend Application | | Sends: { prompt, metadata: { product, planTier, ... } } | v IBL Mentor Backend | ├─ Caches metadata for the session (reused on subsequent messages) ├─ Persists metadata to the session record (for analytics/reporting) | v Mentor (LLM) receives the user's message with context appended: "How do I set up SSO?" Here is additional context metadata for this conversation: product: Analytics planTier: Enterprise userRole: Admin region: EU ``` The mentor sees this context and responds accordingly — e.g., answering with Enterprise-tier SSO setup steps for the Analytics product with EU data-residency considerations, rather than generic help. --- ## Sending Metadata ### WebSocket (`/ws/chat/`) Include the `metadata` field in the JSON payload: ```javascript ws.send(JSON.stringify({ session_id: sessionId, prompt: userMessage, page_content: pageContent, // optional metadata: { // optional product: 'Analytics', planTier: 'Enterprise', userRole: 'Admin', region: 'EU' }, flow: { name: mentorId, tenant: tenantKey } })); ``` ### HTTP SSE (`POST /api/mentor/chat/`) Same `metadata` field in the request body: ```http POST /api/mentor/chat/ Content-Type: application/json { "session_id": "uuid", "prompt": "How do I set up SSO?", "metadata": { "product": "Analytics", "planTier": "Enterprise", "userRole": "Admin", "region": "EU" }, "flow": { "name": "mentor-slug", "tenant": "tenant-key" } } ``` ### Embedded iframe (postMessage) If the mentor is embedded via iframe, the host page passes metadata through the `postMessage` channel: ```javascript iframe.contentWindow.postMessage({ type: 'MENTOR:CONTEXT_UPDATE', hostInfo: { title: document.title, href: window.location.href }, pageContent: bodyContent, metadata: { productGroup: 'LICENSING', productLevel: 'LH', stateCode: 'CA' } }, '*'); ``` The chat widget should: 1. Listen for `postMessage` events with `type: 'MENTOR:CONTEXT_UPDATE'` 2. Extract the `metadata` object from the message 3. Include it in every WebSocket payload sent to the backend --- ## Session Behavior Metadata is **cached per session** and only needs to be sent once. Here's how it behaves across multiple messages: | Message | `metadata` sent | What the mentor sees | |---------|-----------------|----------------------| | #1 | `{ product: "Analytics", planTier: "Enterprise" }` | User's prompt + Analytics / Enterprise context | | #2 | _(not sent)_ | User's prompt + Analytics / Enterprise context (reused from #1) | | #3 | `{ product: "Payments", planTier: "Starter" }` | User's prompt + Payments / Starter context (replaced) | | #4 | _(not sent)_ | User's prompt + Payments / Starter context (reused from #3) | **Rules:** - **Send once** — metadata persists automatically for the rest of the session - **Replace, not merge** — sending new metadata replaces the previous value entirely - **Null or omitted = no change** — the previously cached metadata continues to be used - **Cache TTL** — metadata is cached for 2 hours; it is also persisted to the database so it survives cache expiration --- ## Metadata Structure The metadata field accepts **any** JSON object. There is no enforced schema — use whatever keys make sense for your application. **Example: SaaS customer support** ```json { "product": "Analytics", "planTier": "Enterprise", "userRole": "Admin", "region": "EU" } ``` **Example: Corporate training platform** ```json { "department": "Engineering", "courseId": "SEC-201", "courseName": "Security Fundamentals", "employeeLevel": "Senior" } ``` **Example: E-commerce product advisor** ```json { "category": "Laptops", "brand": "ThinkPad", "priceRange": "1000-2000", "customerSegment": "Business" } ``` --- ## Customizing Mentor Behavior with Metadata The metadata is appended to the user's prompt as context. The mentor's **system prompt** determines how it uses this context. If the mentor isn't responding the way you expect based on the metadata, you can edit the system prompt to reference the metadata fields explicitly. **Example system prompt excerpt:** ``` You are a product support assistant. When the user's context includes a product name, tailor your answers to that specific product's features and documentation. When planTier is provided, only suggest features available on that plan. When region is EU, highlight GDPR and data-residency details. ``` --- ## Reading Metadata from APIs Once metadata is sent with a conversation, it's available through several APIs for analytics, reporting, and data exports. | Endpoint | Field | |----------|-------| | `GET /api/ai-mentor/orgs/{org}/users/{user_id}/sessions/{session_id}/` | `client_context` in the response body | | `GET /api/analytics/messages/details/?platform_key={platform_key}&session_id={session_id}` | `summary.client_context` | | `GET /api/ai-mentor/orgs/{org}/users/{user_id}/sessions/{session_id}/tasks/{task_id}/` (chat history export) | `client_context` column in CSV | | Analytics export (`get_chat_message_history`) | `client_context` column in DataFrame | ### Data Exports | Export method | Where metadata appears | |---------------|----------------------| | CSV export (downloadable chat history) | `client_context` column | | Chat history export task | `client_context` field per message | | Analytics export (DataFrame) | `client_context` column | --- ## Example: Single Mentor Serving Multiple Contexts **Scenario:** One support mentor handles questions across your entire SaaS platform, but users are on different product pages with different plans. **Page: Analytics product — Enterprise plan** The frontend sends: ```json { "metadata": { "product": "Analytics", "planTier": "Enterprise", "userRole": "Admin", "region": "EU" } } ``` **User asks:** "How do I set up SSO?" **Mentor responds** with Enterprise-tier SSO setup steps for the Analytics product, noting EU data-residency requirements — not generic help docs. **Page: Payments product — Starter plan** The frontend sends: ```json { "metadata": { "product": "Payments", "planTier": "Starter", "userRole": "Developer", "region": "US" } } ``` **Same mentor** now responds with Starter-plan Payments integration guides, and notes that SSO requires upgrading to the Pro plan. If the mentor's responses don't align with the expected behavior, adjust the **system prompt** to instruct the mentor on how to use the metadata fields. --- ## Architecture Reference ``` Client payload | v BaseConsumerPayload (pydantic validation) | v BaseLLMRunnerConsumer.process_text_data() | ├─► Redis Cache: session_{session_id}_metadata (2-hour TTL, fast access) ├─► Session.metadata["client_context"] (persistent DB storage) | v LLMRunner.asetup_user_prompt() | ├─ 1. Add greeting instructions ├─ 2. Append page_content (if provided) └─ 3. Append metadata (formatted as key: value pairs) | v LLM receives the composed prompt | v Response returned to user | v Message saved to chat history (metadata markers remain in saved messages) ``` **Storage layers:** | Layer | Location | Purpose | |-------|----------|---------| | Cache | Redis `session_{id}_metadata` | Fast access during active conversation (2h TTL) | | Session DB | `Session.metadata["client_context"]` | Persistent storage, used by analytics APIs and exports | | Message DB | `ChatMessageHistoryExtra.metadata` | Per-message snapshot (captures metadata at time of each message) | **Transport:** Both WebSocket (`/ws/chat/`) and HTTP SSE (`POST /api/mentor/chat/`) share the same pipeline. The metadata field works identically on both. --- ## Notes - The metadata structure is **generic** — not tied to any specific client's data model. Use whatever keys make sense for your application. - Metadata is stored at the **session level**. All messages in a conversation share the same metadata unless the frontend sends an update. - Metadata is **not stripped** from chat history (unlike `page_content`, which is stripped before saving). The `` markers remain in the stored messages. - If the mentor's behavior doesn't reflect the metadata context as expected, update the mentor's **system prompt** to explicitly reference the metadata fields. --- ## Claw Agents URL: https://ibl.ai/docs/developer/agents/claw-agents # Claw Agents 48 pre-built agent configurations for [OpenClaw](https://github.com/iblai/iblai-claw-agents) instances, organized by vertical. --- ## Overview **iblai-claw-agents** provides a curated library of ready-to-deploy agent configurations designed for various industry verticals. Each agent is pre-configured with appropriate system prompts, tool selections, and behavioral parameters. --- ## Repository - **GitHub**: [iblai/iblai-claw-agents](https://github.com/iblai/iblai-claw-agents) - **License**: Open Source --- ## Getting Started Clone the repository and explore the available agent configurations: ```bash git clone https://github.com/iblai/iblai-claw-agents.git cd iblai-claw-agents ``` Browse the verticals directory to find agents suited to your use case, then deploy them to your OpenClaw instance. --- ## Claw Setup URL: https://ibl.ai/docs/developer/agents/claw-setup # Claw Setup Connect self-hosted [Claw](https://github.com/iblai/iblai-claw-setup) servers to the ibl.ai platform. --- ## Overview **iblai-claw-setup** enables connection of self-hosted Claw servers to the ibl.ai platform. This tool handles the configuration and authentication needed to integrate your local Claw deployment with ibl.ai's cloud services. --- ## Repository - **GitHub**: [iblai/iblai-claw-setup](https://github.com/iblai/iblai-claw-setup) - **License**: Open Source --- ## Getting Started ```bash git clone https://github.com/iblai/iblai-claw-setup.git cd iblai-claw-setup ``` Follow the setup instructions in the repository README to configure your self-hosted Claw server and connect it to the ibl.ai platform. --- ## Evaluation System URL: https://ibl.ai/docs/developer/agents/evaluations # Evaluation System Measure and improve mentor quality by running structured experiments against datasets and grading results through human annotations or automated LLM-as-Judge scoring. --- ## Overview The evaluation system provides a complete pipeline for assessing mentor performance: 1. **Create a dataset** of questions with optional expected answers 2. **Run an experiment** that sends each question to a mentor and records its response 3. **Grade the results** using human annotations, LLM-as-Judge, or both 4. **Export results** as CSV for analysis All evaluation data is scoped to your organization (tenant) and isolated from other tenants. ## Key Features - **Dataset management** — Create, update, and organize evaluation question sets - **Multiple input methods** — Add items via JSON, CSV upload, or from existing chat traces - **Async experiment execution** — Experiments run as background tasks; large datasets won't block the API - **Human annotation** — Apply numeric, boolean, or categorical scores to individual responses - **LLM-as-Judge** — Automatically grade experiment results using custom evaluation criteria - **CSV export** — Download experiment results with scores for offline analysis - **Score configs** — Define reusable scoring rubrics for consistent grading ## Authentication All endpoints require a platform API key passed as a token: ``` Authorization: Token ``` ## Evaluation Pipeline ``` ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ ┌──────────┐ │ 1. Create │────>│ 2. Add Items │────>│ 3. Run │────>│ 4. Grade │ │ Dataset │ │ (JSON/CSV/ │ │ Experiment │ │ (Human/ │ │ │ │ Traces) │ │ │ │ LLM) │ └─────────────┘ └──────────────┘ └────────────────┘ └──────────┘ │ v ┌──────────┐ │ 5. View/ │ │ Export │ └──────────┘ ``` ## API Reference **Base URL pattern:** ``` /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/... ``` | Parameter | Description | |-----------|-------------| | `org` | Organization/tenant identifier (platform key) | | `user_id` | User ID of the requesting admin | All list endpoints support pagination with `page` (default: 1) and `limit` (default: 50, max: 200) query parameters. Paginated responses include a `meta` object: ```json { "meta": { "page": 1, "limit": 50, "total_items": 12, "total_pages": 1 } } ``` --- ### Datasets Datasets are collections of evaluation questions. Each dataset is scoped to your organization. #### List Datasets ``` GET /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/ ``` **Query parameters:** `page`, `limit` **Response** `200 OK`: ```json { "data": [ { "name": "customer-support-eval", "description": "Evaluation dataset for customer support mentor", "metadata": { "platform_key": "my-tenant" }, "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ], "meta": { "page": 1, "limit": 50, "total_items": 1, "total_pages": 1 } } ``` #### Create Dataset ``` POST /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/ ``` **Request body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `name` | string | Yes | Unique dataset name | | `description` | string | No | Human-readable description | | `metadata` | object | No | Arbitrary key-value metadata | ```json { "name": "qa-eval-v1", "description": "QA accuracy evaluation", "metadata": { "category": "accuracy", "version": "1.0" } } ``` **Response** `201 Created`: ```json { "name": "qa-eval-v1", "description": "QA accuracy evaluation", "metadata": { "category": "accuracy", "version": "1.0", "platform_key": "my-tenant" }, "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ``` #### Get Dataset ``` GET /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/ ``` Returns a single dataset by name. Validates that the dataset belongs to the requesting tenant. **Response** `200 OK`: Same shape as a single item in the list response. --- ### Dataset Items Items are the individual questions (with optional expected answers) within a dataset. #### List Items ``` GET /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/items/ ``` **Query parameters:** `page`, `limit` **Response** `200 OK`: ```json { "data": [ { "id": "item-uuid-1", "input": "What is machine learning?", "expected_output": "Machine learning is a subset of AI...", "metadata": {}, "status": "ACTIVE", "source_trace_id": null, "source_observation_id": null, "created_at": "2024-01-15T10:35:00Z", "updated_at": "2024-01-15T10:35:00Z" } ], "meta": { "page": 1, "limit": 50, "total_items": 1, "total_pages": 1 } } ``` #### Add Items (Direct Input) ``` POST /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/items/ ``` Provide an `items` array. Each item requires an `input` field; `expected_output` is optional. ```json { "items": [ { "input": "What is machine learning?", "expected_output": "Machine learning is a subset of artificial intelligence that enables systems to learn from data." }, { "input": "Explain neural networks", "expected_output": "Neural networks are computing systems inspired by biological neural networks." }, { "input": "What is deep learning?" } ] } ``` **Response** `201 Created`: ```json { "created": 3, "items": ["..."] } ``` #### Add Items (From Traces) ``` POST /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/items/ ``` Link existing chat traces to the dataset. The system extracts the user input and mentor response from each trace. ```json { "trace_ids": [ "trace-uuid-1", "trace-uuid-2", "trace-uuid-3" ] } ``` > **Note:** Provide either `items` or `trace_ids` in a single request, not both. **Response** `201 Created`: Same shape as direct input response. #### Upload CSV ``` POST /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/items/upload/ ``` Upload a CSV file to bulk-create dataset items. Send as `multipart/form-data` with a `file` field. **Constraints:** - UTF-8 encoding - Maximum file size: 10 MB - Maximum rows: 10,000 - Must have an `input` column (required) - `expected_output` column is optional See [CSV Format](#csv-format) for details. **Response** `201 Created`: ```json { "created": 25, "items": ["..."] } ``` #### Update Item ``` PUT /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/items/{item_id}/ ``` **Request body** (all fields optional): | Field | Type | Description | |-------|------|-------------| | `input` | string | The question/prompt | | `expected_output` | string | Expected answer | | `metadata` | object | Arbitrary metadata | | `status` | string | `ACTIVE` or `ARCHIVED` | **Response** `200 OK`: Updated item object. #### Delete Item ``` DELETE /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/items/{item_id}/ ``` **Response** `204 No Content` > This action is irreversible. --- ### Experiments Experiments run a mentor against every item in a dataset and record the responses. Each experiment is processed as a background task. #### List Experiment Runs ``` GET /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/runs/ ``` **Query parameters:** `page`, `limit` **Response** `200 OK`: ```json { "data": [ { "id": "run-uuid-1", "name": "run-abc12345", "metadata": { "platform_key": "my-tenant", "mentor_unique_id": "mentor-uuid", "initiated_by": "admin@example.com" }, "created_at": "2024-01-15T11:00:00Z", "updated_at": "2024-01-15T11:30:00Z" } ], "meta": { "page": 1, "limit": 50, "total_items": 1, "total_pages": 1 } } ``` #### Start Experiment ``` POST /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/runs/ ``` **Request body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `mentor_unique_id` | string | Yes | The `unique_id` of the mentor to evaluate | | `run_name` | string | No | Custom name for the run (auto-generated if omitted) | | `metadata` | object | No | Additional metadata | ```json { "mentor_unique_id": "my-mentor-unique-id", "run_name": "experiment-v1", "metadata": { "purpose": "accuracy evaluation" } } ``` This dispatches a background task. The API returns immediately with a `202 Accepted` response. **What happens during an experiment:** 1. A new chat session is created for each dataset item 2. The mentor is invoked with the item's `input` through the standard chat pipeline 3. The mentor's response is recorded 4. Each interaction is traced and linked to the experiment run **Response** `202 Accepted`: ```json { "run_name": "experiment-v1", "task_id": "celery-task-uuid", "status": "started", "mentor_unique_id": "my-mentor-unique-id", "initiated_by": "admin@example.com" } ``` #### Get Experiment Run Details ``` GET /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/runs/{run_name}/ ``` Returns detailed results including individual run items with their trace IDs. **Response** `200 OK`: ```json { "id": "run-uuid-1", "name": "experiment-v1", "metadata": { "platform_key": "my-tenant", "mentor_unique_id": "mentor-uuid", "initiated_by": "admin@example.com" }, "created_at": "2024-01-15T11:00:00Z", "updated_at": "2024-01-15T11:30:00Z", "dataset_run_items": [ { "id": "ri-1", "dataset_item_id": "item-1", "trace_id": "trace-1", "observation_id": "", "created_at": "2024-01-15T11:05:00Z" } ] } ``` #### Export Results (CSV) ``` GET /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/runs/{run_name}/export/ ``` Downloads experiment results as a CSV file. Columns include: `item_id`, `input`, `expected_output`, `actual_output`, `trace_id`, and any score columns (prefixed with `score_`). **Response** `200 OK` with `Content-Type: text/csv`: ```csv item_id,input,expected_output,actual_output,trace_id,score_accuracy item-1,What is AI?,AI is...,Artificial intelligence is...,trace-1,4.0 ``` #### Delete Experiment Run ``` DELETE /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/runs/{run_name}/ ``` **Response** `204 No Content` > This action is irreversible. --- ### Scores Scores are human annotations attached to individual traces from an experiment. Use scores to manually grade mentor responses. #### List Scores ``` GET /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/scores/ ``` **Query parameters:** | Parameter | Description | |-----------|-------------| | `page` | Page number (default: 1) | | `limit` | Items per page (default: 50) | | `dataset_run_id` | Filter by experiment run ID | | `trace_id` | Filter by trace ID | | `name` | Filter by score name (e.g., `accuracy`) | **Response** `200 OK`: ```json { "data": [ { "id": "score-1", "name": "accuracy", "value": 4.0, "data_type": "NUMERIC", "comment": "Good response", "trace_id": "trace-1", "observation_id": null, "created_at": "2024-01-15T12:00:00Z" } ], "meta": { "page": 1, "limit": 50, "total_items": 1, "total_pages": 1 } } ``` #### Create Score ``` POST /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/scores/ ``` **Request body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `trace_id` | string | Yes | Trace ID from experiment run item | | `name` | string | Yes | Score metric name (e.g., `accuracy`) | | `value` | number | Yes | Score value | | `data_type` | string | No | `NUMERIC` (default), `BOOLEAN`, or `CATEGORICAL` | | `comment` | string | No | Explanation or notes | | `observation_id` | string | No | Specific observation within the trace | | `config_id` | string | No | Score config ID for rubric validation | | `dataset_run_id` | string | No | Link score to an experiment run | ```json { "trace_id": "trace-uuid-from-experiment", "name": "accuracy", "value": 4.0, "data_type": "NUMERIC", "comment": "Good response, covered the main points accurately" } ``` **Response** `201 Created`: ```json { "status": "created", "name": "accuracy", "value": 4.0 } ``` #### Delete Score ``` DELETE /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/scores/{score_id}/ ``` **Response** `204 No Content` --- ### Score Configs Score configs define reusable scoring rubrics for consistent, standardized grading across experiments. #### List Score Configs ``` GET /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/score-configs/ ``` **Query parameters:** `page`, `limit` **Response** `200 OK`: ```json { "data": [ { "id": "cfg-1", "name": "accuracy", "data_type": "NUMERIC", "min_value": 1.0, "max_value": 5.0, "categories": null, "description": "Rate accuracy 1-5", "is_archived": false, "created_at": "2024-01-15T10:00:00Z" } ], "meta": { "page": 1, "limit": 50, "total_items": 1, "total_pages": 1 } } ``` #### Create Score Config ``` POST /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/score-configs/ ``` **Request body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `name` | string | Yes | Config name | | `data_type` | string | Yes | `NUMERIC`, `BOOLEAN`, or `CATEGORICAL` | | `min_value` | number | No | Minimum value (for `NUMERIC`) | | `max_value` | number | No | Maximum value (for `NUMERIC`) | | `categories` | array | No | Category definitions (for `CATEGORICAL`) | | `description` | string | No | Human-readable description | **Numeric example:** ```json { "name": "accuracy", "data_type": "NUMERIC", "min_value": 1.0, "max_value": 5.0, "description": "Rate accuracy from 1 (wrong) to 5 (perfect)" } ``` **Categorical example:** ```json { "name": "safety", "data_type": "CATEGORICAL", "categories": [ { "value": 0, "label": "Unsafe" }, { "value": 0.5, "label": "Borderline" }, { "value": 1.0, "label": "Safe" } ], "description": "Evaluate response safety" } ``` **Response** `201 Created`: The created score config object. --- ### LLM-as-Judge Automatically grade an entire experiment run using an LLM evaluator. The judge examines each item's input, expected output, and actual output against your custom criteria and assigns a score (0 to 1). ``` POST /api/ai-mentor/orgs/{org}/users/{user_id}/evaluations/datasets/{dataset_name}/runs/{run_name}/evaluate/ ``` **Request body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `criteria` | string | Yes | Evaluation rubric for the judge | | `score_name` | string | Yes | Name for the generated scores | | `llm_provider` | string | No | LLM provider to use as judge | | `llm_name` | string | No | Specific model name | | `max_concurrency` | integer | No | Max parallel evaluations | ```json { "criteria": "Evaluate the response on:\n1. Accuracy: Is the information factually correct?\n2. Completeness: Does it fully address the question?\n3. Clarity: Is it clear and well-structured?\n\nWeight accuracy most heavily.", "score_name": "quality" } ``` This dispatches a background task. The experiment run must be completed before triggering judge evaluation. **Response** `202 Accepted`: ```json { "task_id": "celery-task-uuid", "status": "started", "score_name": "quality" } ``` After completion, scores are available via the [List Scores](#list-scores) endpoint filtered by `dataset_run_id`. --- ## Workflows ### Full Evaluation Pipeline **Step 1: Create a dataset** ``` POST .../evaluations/datasets/ { "name": "qa-eval-v1", "description": "QA evaluation" } ``` **Step 2: Add items** (choose one method per request) Option A — Direct input: ``` POST .../evaluations/datasets/qa-eval-v1/items/ { "items": [{ "input": "Q1", "expected_output": "A1" }, ...] } ``` Option B — CSV upload: ``` POST .../evaluations/datasets/qa-eval-v1/items/upload/ Content-Type: multipart/form-data ``` Option C — From existing traces: ``` POST .../evaluations/datasets/qa-eval-v1/items/ { "trace_ids": ["trace-1", "trace-2"] } ``` **Step 3: Run experiment** ``` POST .../evaluations/datasets/qa-eval-v1/runs/ { "mentor_unique_id": "my-mentor", "run_name": "run-v1" } ``` **Step 4: Wait for completion, then check results** ``` GET .../evaluations/datasets/qa-eval-v1/runs/run-v1/ ``` **Step 5: Grade results** (choose one or both) Human annotation: ``` POST .../evaluations/scores/ { "trace_id": "trace-1", "name": "accuracy", "value": 4.0, "data_type": "NUMERIC" } ``` LLM-as-Judge: ``` POST .../evaluations/datasets/qa-eval-v1/runs/run-v1/evaluate/ { "criteria": "Evaluate accuracy and completeness", "score_name": "quality" } ``` **Step 6: View scores** ``` GET .../evaluations/scores/?dataset_run_id= ``` **Step 7: Export** ``` GET .../evaluations/datasets/qa-eval-v1/runs/run-v1/export/ ``` --- ## CSV Format ### Upload Format The upload CSV must be UTF-8 encoded with a header row. The `input` column is required; `expected_output` is optional. ```csv input,expected_output What is machine learning?,Machine learning is a subset of AI that enables systems to learn from data. Explain neural networks,Neural networks are computing systems inspired by biological neural networks. What is deep learning?, ``` **Limits:** 10 MB max file size, 10,000 max rows. ### Export Format Exported CSVs include the following columns: | Column | Description | |--------|-------------| | `item_id` | Dataset item ID | | `input` | The question sent to the mentor | | `expected_output` | Expected answer (if provided) | | `actual_output` | Mentor's response | | `trace_id` | Trace ID for the interaction | | `score_` | One column per score name (e.g., `score_accuracy`) | --- ## MCP & OAuth Architecture URL: https://ibl.ai/docs/developer/agents/mcp-authentication/architecture # MCP & OAuth Architecture Backend architecture reference for the MCP server and OAuth connector subsystems, covering the data model, control flow, validation, and extensibility. --- ## Overview This document targets backend engineers who are extending or operating the MCP server and OAuth connector subsystems. It summarizes the data model, control flow, and key modules involved in provisioning external integrations. ## Component Map | Layer | Responsibility | Key Modules | | --- | --- | --- | | Persistence | Store providers, services, connections, and user tokens | `OauthProvider`, `OauthService`, `ConnectedService`, `MCPServer`, `MCPServerConnection` | | API (Accounts) | Orchestrate OAuth flows, expose discovery endpoints, manage connected services | Account views and URLs | | API (Mentor) | CRUD for MCP servers and connections | `MCPServerViewSet`, `MCPServerConnectionViewSet` | | LangChain Runtime | Resolve connections at inference time, build headers | `MCPServer.resolve_connection`, LangChain tools | | Utilities | Credential store access, token refresh logic | `get_cred`, connected services utilities | ## Data Model Relationships - `OauthProvider` exposes one or more `OauthService` entries. - `OauthService` grants `ConnectedService` records (per user, per platform). - `MCPServer` is registered on a `Platform` and has one or more `MCPServerConnection` entries. - `MCPServerConnection` optionally uses a `ConnectedService` (for OAuth-backed auth) and can be scoped to a `Mentor`. - `ConnectedService` has a unique constraint on `(user, provider, platform, service)` to enforce one connection per surface. ### Connection scope rules - `scope="platform"` -- `platform` is auto-filled from the server's platform (unless featured). - `scope="user"` -- Requires `user` or `connected_service`. - `scope="mentor"` -- Requires a mentor; optionally reuses platform-level credentials when no mentor-specific ones exist. - `auth_type="oauth2"` -- Always requires `connected_service`. ## OAuth Flow Internals ### Discovery `OAuthServiceListAPIView` surfaces enabled services. `OAuthServiceScopeListAPIView` narrows down to a specific service scope. ### Start The start view resolves scopes, builds the provider adapter, and stores a UUID-based hash in the cache to guard the callback. Cache entries expire after one hour to limit replay windows. ### Callback The callback view verifies state, exchanges the code for a token, normalizes the scope/service identifier, and syncs metadata. Tokens are stored raw (`access_token`, `refresh_token`, `token_type`) and lowercased for schema consistency. ### Refresh Downstream modules call the refresh function which uses the provider adapter to refresh tokens and re-sync metadata. Error handling is intentionally strict: multiple services per connection raise `ValueError` early; missing tenant credentials return `404`/`400` responses. ## MCP Server Runtime Resolution ``` LangChain Tool -> MCPServer.resolve_connection(platform, user, mentor) | |- User-scoped connection exists? | YES -> Load connection -> Optional ConnectedService (if oauth2) | |- Mentor-scoped connection exists? | YES -> Use mentor credentials | |- Platform-scoped connection exists? | YES -> Use stored platform credentials | |- Server is featured? | YES -> Use global connection | NO -> Fail with 401 / no connection | -> render_headers(access_token?) If oauth2: ensure fresh access token (refresh if needed) Return: authorization headers ``` Async code paths use `aresolve_connection()` with `.afirst()` queries to avoid blocking the event loop. Headers are merged with `extra_headers`, and explicit credentials override anything pre-existing. ## Validation and Security ### Credential sourcing `get_cred("auth_{provider}", tenant)` is the single source of truth for client IDs and secrets. Tenant-specific overrides live alongside global (`tenant="main"`) credentials. The serializer guard ensures the admin has installed the necessary client credentials before connections are created. ### State integrity Cache entries expire after one hour. The callback splits `state` into `(org, provider, service, user_id, hash)` and verifies the cached value before exchanging the token. ### Access control - OAuth endpoints restrict methods (`GET`, `DELETE`) to read-only client operations, leaving creation solely to the managed OAuth flow. - MCP endpoints require tenant admin privileges for all create/update/delete operations. ## Extensibility ### Adding a new OAuth provider/service 1. Seed `OauthProvider` and `OauthService` records via the management command. 2. Provision client credentials (`auth_{provider}`) in the credential store. 3. Update the front-end to surface the new service (the API is discovery-driven, so minimal UI changes are needed). ### Extending MCP authentication types 1. Add a new enum member to `MCPServer.AuthType`. 2. Extend connection validation to handle the new type. 3. Update `render_headers()` to format the outbound headers. 4. Update front-end forms and documentation. ### Supporting multi-tenant shared servers Mark the server `is_featured=true`. Platform admins in other tenants can then create their own connections, while the original tenant retains control of the server metadata. --- ## MCP Server Connections URL: https://ibl.ai/docs/developer/agents/mcp-authentication/mcp-connections # MCP Server Connections Register MCP servers and create authentication bindings so AI agents can securely invoke external tools at runtime. --- ## Overview | Term | Description | | --- | --- | | **MCP Server** | Metadata describing an external Model Context Protocol server (name, transport, auth type, etc.). Tenants can register multiple servers. | | **MCP Server Connection** | An authentication binding between a tenant (or user) and an MCP server. Supports token-based and OAuth-based credentials. | | **Scope** | Determines whether the connection is user-specific (`user`), mentor-specific (`mentor`), or tenant-wide (`platform`). | Connections are created per (server, scope) pair and drive runtime authentication when LangChain tools invoke the MCP server. > **Prerequisite:** OAuth-backed MCP servers depend on the connector flow documented in the [OAuth Connectors](/docs/developer/agents/mcp-authentication/oauth-connectors) guide. ## API Summary All endpoints live under `/api/ai-mentor/orgs/{org}/users/{user_id}/`. | Capability | Endpoint | Method | | --- | --- | --- | | List servers | `/mcp-servers/` | `GET` | | Create server | `/mcp-servers/` | `POST` | | Update server | `/mcp-servers/{id}/` | `PUT` / `PATCH` | | Delete server | `/mcp-servers/{id}/` | `DELETE` | | List connections | `/mcp-server-connections/` | `GET` | | Create connection | `/mcp-server-connections/` | `POST` | | Update connection | `/mcp-server-connections/{id}/` | `PUT` / `PATCH` | | Delete connection | `/mcp-server-connections/{id}/` | `DELETE` | Use `Authorization: Token ...` authentication. Only tenant admins may create/update servers and connections. ## Server Registration ### Create a server record ``` POST /api/ai-mentor/orgs/acme/users/alice/mcp-servers/ HTTP/1.1 Authorization: Token {{TOKEN}} Content-Type: application/json { "name": "Google Drive MCP", "description": "Search and index Drive documents", "url": "https://drive-mcp.example.com", "transport": "sse", "auth_type": "oauth2", "is_featured": false, "is_enabled": true } ``` Response: ```json { "id": 9, "platform": 42, "name": "Google Drive MCP", "description": "Search and index Drive documents", "url": "https://drive-mcp.example.com", "transport": "sse", "auth_type": "oauth2", "is_featured": false, "is_enabled": true, "created_at": "2025-11-12T12:14:50Z", "updated_at": "2025-11-12T12:14:50Z" } ``` **Transport options:** `sse`, `websocket`, `streamable_http`. **Auth types:** `none`, `token`, `oauth2`. If `auth_type` is `token`, populate the `credentials` field with the full header value (e.g., `"Bearer abc123"`). You can set it at creation time or later via `PATCH /mcp-servers/{id}/`. ## Creating Connections Connections map a server to a scope (`platform`, `user`, or `mentor`) and an authentication secret. ### Token-based platform connection ```json { "server": 9, "scope": "platform", "auth_type": "token", "credentials": "Token super-secret", "authorization_scheme": "Token", "extra_headers": { "x-mcp-client": "mentor-ui" } } ``` ### OAuth-based user connection 1. Ensure a `ConnectedService` exists for the same provider/service pair (see [OAuth Connectors](/docs/developer/agents/mcp-authentication/oauth-connectors)). 2. Create the connection referencing the `connected_service` ID: ```json { "server": 9, "scope": "user", "auth_type": "oauth2", "user": "alice", "connected_service": 77 } ``` ### Mentor-scoped connection Mentor-scoped connections bind credentials to a specific mentor while keeping the server reusable: ```json { "server": 9, "scope": "mentor", "auth_type": "token", "mentor": 123, "credentials": "Token scoped-to-mentor", "authorization_scheme": "Token" } ``` The mentor's `platform_key` must match the current tenant. If `platform` is omitted, the API infers it from the tenant. ### Updating and deactivating connections - `PATCH /mcp-server-connections/{id}/` to toggle `is_active`, swap the linked `connected_service`, or update headers. - `DELETE /mcp-server-connections/{id}/` removes the record entirely. ## Runtime Behavior When LangChain invokes the server: 1. The runtime calls `MCPServer.resolve_connection(platform, user, mentor)`. 2. Preference order: - Most recent user-scoped connection (matching user or their `ConnectedService.user`). - Mentor-scoped connection that matches the active mentor. - Platform-scoped connection for the tenant. - Featured server fallbacks (`is_featured=true` with global platform). 3. For OAuth connections, the linked `ConnectedService` is refreshed if nearing expiry (no front-end action required). 4. Headers are generated by `MCPServerConnection.render_headers()`, combining stored headers, tokens, and authorization schemes. ## Front-end Notes - **Server first, then connection.** The connection endpoints expect a valid server ID. - **OAuth prerequisite.** Do not display "Connect" UI for `auth_type=oauth2` servers unless the user has completed the relevant OAuth flow; otherwise the API returns `400`. - **Scope hints.** Use the server's `auth_type` to drive the form layout: - `none`: Informational text only. - `token`: Inputs for `authorization_scheme`, `credentials`, and optional header key-value pairs. - `oauth2`: A `ConnectedService` picker filtered by provider/service. - **Masking.** The API never returns the original raw token once stored. ## Troubleshooting | Issue | Resolution | | --- | --- | | `400 Selected MCP server is not available to the current tenant.` | Bind a non-featured server scoped to the current tenant or mark it `is_featured=true`. | | `400 OAuth2 connections require a connected service.` | Create the OAuth connector first; ensure you pass the correct `connected_service` ID. | | Connection falls back to platform credentials unexpectedly | Confirm the user connection's `is_active` flag. Ensure the `ConnectedService.user` matches the invoking user. | --- ## OAuth Connector API URL: https://ibl.ai/docs/developer/agents/mcp-authentication/oauth-connectors # OAuth Connector API Let learners and tenant admins grant the platform permission to act on their behalf via OAuth, powering authenticated MCP server connections. --- ## Overview Front-end clients use the OAuth connector APIs to let a learner (or tenant admin) grant our platform permission to act on their behalf. This guide documents the discovery endpoints, the authorization handshake, and the post-connection management APIs. **Key terms:** - **OAuth Provider** -- A top-level vendor such as Google or Dropbox. - **OAuth Service** -- A concrete surface offered by a provider (e.g., Google Drive, Google Calendar). A `ConnectedService` references one service. - **ConnectedService** -- The persisted token bundle that represents a user's grant. These are always per user *and* per service. ## API Summary | Capability | Endpoint | Method | Notes | | --- | --- | --- | --- | | List available services | `/api/accounts/orgs/{org}/oauth-services/` | `GET` | Returns all enabled services across all providers. | | List scopes for a service | `/api/accounts/orgs/{org}/oauth-services/{service_name}/scopes/` | `GET` | Service-specific breakdown of scopes. | | Start OAuth flow | `/api/accounts/connected-services/orgs/{org}/users/{user_id}/{provider}/{service}/` | `GET` | Returns the vendor authorization URL and primes the OAuth state cache. | | Handle callback | `/api/accounts/connected-services/callback/` | `GET` | Called by the browser after the vendor redirects back with a code. | | List a user's connections | `/api/accounts/connected-services/orgs/{org}/users/{user_id}/` | `GET` | Returns `ConnectedService` records for the current user. | | Delete a connection | `/api/accounts/connected-services/orgs/{org}/users/{user_id}/{id}/` | `DELETE` | Removes the connection. | Authentication is the standard `Authorization: Token ...` scheme. Tenants must have credentials named `auth_{provider}` configured in the credential store before the flow can start. ## Connection Lifecycle ``` Front-end Client Mentor API (Accounts) OAuth Provider | | | |-- GET /oauth-services/ ->| | |<- 200 OK (services) ----| | | | | |-- GET /connected-services/.../provider/service/ ------->| |<- 200 OK {"auth_url":..} | | | | | |-- Redirect user -------->| | | |<-- Redirect back w/ code ----| |-- GET /callback?code=... | | | |-- Token exchange ----------->| | |<-- Token payload ------------| |<- 200 OK (Connected) ---| | ``` ## Step-by-Step Implementation ### Discover services ``` GET /api/accounts/orgs/acme/oauth-services/ HTTP/1.1 Authorization: Token {{TOKEN}} ``` Sample response: ```json [ { "id": 12, "oauth_provider": "google", "name": "drive", "display_name": "Google Drive", "description": "File access for Drive", "scope": "https://www.googleapis.com/auth/drive", "image": "https://cdn.example.com/oauth/google-drive.svg", "created_at": "2025-11-01T12:32:55Z", "updated_at": "2025-11-01T12:32:55Z" } ] ``` ### Start the OAuth flow When the learner clicks **Connect**, call the start endpoint: ``` GET /api/accounts/connected-services/orgs/acme/users/alice/google/drive/ HTTP/1.1 Authorization: Token {{TOKEN}} ``` Response: ```json { "auth_url": "https://accounts.google.com/o/oauth2/v2/auth?client_id=..." } ``` Open `auth_url` in a new window/tab. The user authenticates with the vendor and approves permissions. ### Handle the callback After approval, the vendor redirects the browser to the callback URL. Capture the query parameters and relay them: ``` GET /api/accounts/connected-services/callback/?code=4/0A...&state=acme:google:drive:alice:09e4... HTTP/1.1 ``` Successful response: ```json { "id": 77, "provider": "google", "service": "drive", "expires_at": "2025-11-12T14:05:00Z", "scope": "https://www.googleapis.com/auth/drive", "scope_names": ["drive"], "scopes": ["https://www.googleapis.com/auth/drive"], "token_type": "bearer", "service_info": { "id": 12, "name": "drive", "display_name": "Google Drive", "logo": "https://cdn.example.com/oauth/google-drive.svg" } } ``` At this point the connection is persisted. If the connection already existed, it is updated in place. ### Listing and deleting connections ``` GET /api/accounts/connected-services/orgs/acme/users/alice/ HTTP/1.1 Authorization: Token {{TOKEN}} ``` To delete: ``` DELETE /api/accounts/connected-services/orgs/acme/users/alice/77/ HTTP/1.1 Authorization: Token {{TOKEN}} ``` Returns `204 No Content`. ## UI/UX Considerations - **State management** -- The `state` returned by the start endpoint must be round-tripped without modification. Do not decode or alter it. - **Window strategy** -- Use `window.open` or a redirect. If using a modal, capture the redirect in that context and forward the query params to the callback endpoint. - **Error handling** -- Handle HTTP 400 responses (most likely missing provider credential configuration). Display actionable guidance ("Admin must configure auth_google credentials"). - **Credential refresh** -- The API auto-refreshes tokens when needed. The front-end only needs to re-list connections to pick up new expiry times. ## Troubleshooting | Symptom | Likely Cause | Action | | --- | --- | --- | | `/oauth-services/` returns empty array | Tenant has no enabled services or provider is disabled | Confirm `OauthProvider.is_enabled` and `OauthService` records. | | Start endpoint returns 400 `"No credentials found"` | `auth_{provider}` credential is missing | Ask tenant admin to configure credential via admin UI. | | Callback returns `Invalid state` | Start and callback requests happened in different browser contexts or state expired | Ensure the same browser session completes the round-trip within 60 minutes. | | Callback returns `Could not exchange auth token` | Provider rejected the code | Try restarting the flow; verify redirect URI matches provider settings. | --- ## Memory System URL: https://ibl.ai/docs/developer/agents/memory # Memory System The Memory System enables AI mentors to remember information about users across conversations. It stores user preferences, learning progress, knowledge gaps, and personal context, making interactions more personalized and contextual. --- ## Overview The memory system supports two scopes of memory: | Memory Type | Scope | Description | |-------------|-------|-------------| | **Global User Memories** | All mentors | Facts about the user that apply everywhere (name, profession, preferences) | | **Mentor-Specific Memories** | Single mentor | Context specific to interactions with a particular mentor | **Default Memory Categories (Mentor-Specific):** | Category | Slug | Description | |----------|------|-------------| | Knowledge Gaps | `knowledge_gaps` | Topics or concepts the user struggles with | | Learning Goals | `learning_goals` | Goals and objectives the user wants to achieve | | Preferences | `preferences` | Learning style, pace, or content preferences | | Progress Milestones | `progress_milestones` | Achievements and completed learning milestones | | Personal Context | `personal_context` | Relevant personal information shared by the user | --- ## Architecture ### System Overview The memory system uses PGVector for semantic search and consists of four main components that work together to extract, store, and retrieve memories. The data flows through two paths: 1. **Extraction path (write):** A Chat Session feeds into a background Celery Task containing the Extraction Service, which processes conversations and writes to the Memory Store (PGVector) — storing global memories, mentor memories, and their embeddings. 2. **Retrieval path (read):** When generating a new chat response, the Context Service queries the Memory Store via Semantic Search (cosine distance), retrieves relevant memories, and injects them into the AI's context to produce a personalized response. ### Components | Component | Location | Purpose | |-----------|----------|---------| | **MemoryExtractionService** | `services/memory_extraction.py` | Extracts memories from conversations using LLM | | **MemoryStore** | `services/memory_store.py` | Handles storage, deduplication, and retrieval | | **MemoryContextService** | `services/memory_context.py` | Retrieves and formats memories for chat injection | | **Celery Tasks** | `tasks.py` | Background processing for memory extraction | --- ## Memory Extraction Flow When a user sends a message, the system automatically extracts relevant memories in the background. ``` ┌──────────────────┐ │ User sends │ │ message │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ AI generates │ │ response │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Celery task │ │ triggered │ │ (background) │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ ┌─────────────────┐ │ Check settings │────▶│ SKIP │ │ - Tenant enabled?│ No │ (not enabled) │ │ - Mentor enabled?│ └─────────────────┘ │ - User enabled? │ └────────┬─────────┘ │ Yes ▼ ┌──────────────────┐ │ Get existing │ │ memories summary │ │ (for context) │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ SINGLE LLM CALL │ │ │ │ Input: │ │ - User message │ │ - AI response │ │ - Categories │ │ - Existing mem │ │ │ │ Output: │ │ - has_memories │ │ - global_memories│ │ - mentor_memories│ └────────┬─────────┘ │ ▼ ┌──────────────────┐ ┌─────────────────┐ │ has_memories? │────▶│ SKIP │ │ │ No │ (nothing to │ │ │ │ extract) │ └────────┬─────────┘ └─────────────────┘ │ Yes ▼ ┌──────────────────┐ │ Deduplication │ │ │ │ 1. Hash check │───▶ Skip if exact match │ 2. Semantic check│───▶ Skip if similar (cosine < 0.15) └────────┬─────────┘ │ Unique ▼ ┌──────────────────┐ │ Generate │ │ embedding │ │ (1536 dimensions)│ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Store in │ │ PostgreSQL │ │ with PGVector │ └──────────────────┘ ``` ### Extraction Details The extraction service uses a **single LLM call** to both decide if extraction is needed AND extract memories. This optimization reduces cost and latency. **LLM Input:** ``` Categories: - knowledge_gaps: Topics or concepts the user struggles with - learning_goals: Goals and objectives the user wants to achieve ... Existing memories (avoid duplicates): Global: - The user is a software engineer Mentor-specific: - [learning_goals] The user wants to learn Python Latest Exchange: User: I'm having trouble understanding recursion Assistant: Let me explain recursion step by step... ``` **LLM Output:** ```json { "has_memories": true, "global_memories": [], "mentor_memories": { "knowledge_gaps": ["The user struggles with understanding recursion"] } } ``` ### Deduplication Strategy The system uses a **3-layer deduplication** approach to prevent duplicate memories: | Layer | Method | Purpose | |-------|--------|---------| | 1 | SHA-256 Hash | Fast check for exact duplicates | | 2 | Semantic Similarity | PGVector cosine distance (threshold: 0.15) for near-duplicates | | 3 | LLM Context | Existing memories shown to LLM to inform extraction | --- ## Memory Injection Flow When a user starts a new conversation, relevant memories are retrieved and injected into the AI's context. ``` ┌──────────────────┐ │ User sends │ │ new message │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ ┌─────────────────┐ │ Check mentor │────▶│ NO INJECTION │ │ memory enabled? │ No │ │ └────────┬─────────┘ └─────────────────┘ │ Yes ▼ ┌──────────────────┐ ┌─────────────────┐ │ Check user │────▶│ NO INJECTION │ │ use_memory │ No │ │ │ enabled? │ └─────────────────┘ └────────┬─────────┘ │ Yes ▼ ┌──────────────────┐ │ Generate query │ │ embedding from │ │ user message │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Semantic search │ │ │ │ - Top 5 global │ │ - Top 5 mentor │ │ │ │ (ordered by │ │ cosine distance)│ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Format as │ │ markdown context │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Inject into │ │ system prompt │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ AI generates │ │ personalized │ │ response │ └──────────────────┘ ``` ### Injected Context Format The AI receives memories formatted as: ```markdown ## User Information - The user is a software engineer with 5 years of experience - The user prefers visual explanations with diagrams ## Relevant Context from Previous Conversations - [Knowledge Gaps] The user struggled with understanding recursion - [Learning Goals] The user wants to master system design patterns - [Preferences] The user prefers Python code examples over pseudocode ``` --- ## Configuration Hierarchy Memory features require enablement at three levels: ``` ┌─────────────────────────────────────────────────────────────┐ │ TENANT LEVEL │ │ │ │ Tenant Profile → Memory Tab │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Memory Configuration: [ENABLED/DISABLED] │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ │ │ If disabled, stops here │ │ ▼ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ MENTOR LEVEL │ │ │ │ │ │ │ │ Mentor Settings → Memory Tab │ │ │ │ ┌────────────────────────────────────────────┐ │ │ │ │ │ Enable Memory: [ON/OFF] │ │ │ │ │ │ Memory Categories: [Configure...] │ │ │ │ │ └────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ If disabled, stops here │ │ │ │ ▼ │ │ │ │ ┌────────────────────────────────────────────┐ │ │ │ │ │ USER LEVEL │ │ │ │ │ │ │ │ │ │ │ │ User Profile → Memory Settings │ │ │ │ │ │ ┌───────────────────────────────────┐ │ │ │ │ │ │ │ Auto-capture memories: [ON/OFF] │ │ │ │ │ │ │ │ Use memories in responses: [ON/OFF]│ │ │ │ │ │ │ └───────────────────────────────────┘ │ │ │ │ │ └────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## SPA Configuration Guide ### Enabling Memory for a Tenant To enable the memory feature for your entire platform: 1. Navigate to **Tenant Profile** page 2. Select the **Memory** tab 3. Toggle the memory configuration switch to **enabled** > **Note:** This is the master switch. Memory features will not work for any mentor or user until this is enabled. --- ### Enabling Memory for a Mentor Each mentor can have memory features enabled or disabled individually: 1. Navigate to **Mentor Settings** for the specific mentor 2. Select the **Memory** tab 3. Toggle memory to **enabled** Once enabled, the mentor will: - Automatically extract memories from conversations - Use stored memories to personalize responses **Managing Mentor Memory Categories:** From the Mentor Settings → Memory tab, administrators can: - View default memory categories - Create custom categories - Edit category names and descriptions - Deactivate categories (memories in that category will no longer be extracted) **Viewing User Memories for a Mentor:** The Mentor Settings → Memory tab also displays all memories stored for users interacting with that mentor, organized by category. --- ### User Memory Settings Users control their own memory preferences from their profile: 1. Navigate to **User Profile** page 2. Locate the memory settings section 3. Configure the following options: | Setting | Description | |---------|-------------| | **Auto-capture memories** | When enabled, the system automatically extracts and saves memories from conversations | | **Use memories in responses** | When enabled, stored memories are used to personalize AI responses | > **Note:** Users can disable memory features entirely for privacy, even if the tenant and mentor have memory enabled. --- ### Managing Global User Memories Global memories are facts about the user that apply across all mentors (e.g., "The user is a software engineer"). **Location:** User Profile page → Global Memories section **User Actions:** | Action | Description | |--------|-------------| | **View memories** | See all automatically captured and manually added global memories | | **Add memory** | Manually add a new global memory | | **Delete memory** | Remove a memory that is no longer relevant or accurate | --- ### Managing Mentor-Specific Memories Mentor memories are specific to a user's interactions with a particular mentor. **Location:** Mentor Settings page → Memory tab → User Memories section **User/Admin Actions:** | Action | Description | |--------|-------------| | **View memories** | See all memories organized by category | | **Filter by category** | View memories for a specific category (e.g., Knowledge Gaps) | | **Add memory** | Manually add a memory to a specific category | | **Edit memory** | Update the content of an existing memory | | **Delete memory** | Remove a memory | --- ## API Reference Base URL: `/api/ai-mentor/` ### Global Memories API **Endpoints:** `/orgs/{org}/users/{user_id}/global-memories/` #### List Global Memories ```bash curl -X GET \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/global-memories/" \ -H "Authorization: Token " ``` **Response:** ```json { "count": 2, "results": [ { "id": 1, "content": "The user is a software engineer with 5 years of experience", "is_auto_generated": true, "created_at": "2024-01-15T10:30:00Z" }, { "id": 2, "content": "The user prefers detailed code examples", "is_auto_generated": true, "created_at": "2024-01-14T09:15:00Z" } ] } ``` #### Create Global Memory ```bash curl -X POST \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/global-memories/" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "content": "The user is preparing for a job interview" }' ``` **Response:** ```json { "id": 3, "content": "The user is preparing for a job interview", "is_auto_generated": false, "created_at": "2024-01-16T14:00:00Z" } ``` #### Delete Global Memory ```bash curl -X DELETE \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/global-memories/3/" \ -H "Authorization: Token " ``` **Response:** `204 No Content` --- ### Mentor Memories API **Endpoints:** `/orgs/{org}/users/{user_id}/mentors/{mentor_id}/mentor-memories/` #### List All Mentor Memories (All Mentors) ```bash curl -X GET \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/mentor-memories/" \ -H "Authorization: Token " ``` **Response:** ```json { "count": 3, "results": [ { "id": 1, "mentor": "python-tutor", "category": { "id": 1, "name": "Knowledge Gaps", "slug": "knowledge_gaps" }, "content": "The user struggled with understanding recursion", "is_auto_generated": true, "created_at": "2024-01-15T11:00:00Z" }, { "id": 2, "mentor": "python-tutor", "category": { "id": 2, "name": "Learning Goals", "slug": "learning_goals" }, "content": "The user wants to master data structures", "is_auto_generated": true, "created_at": "2024-01-14T16:30:00Z" } ] } ``` #### List Memories for Specific Mentor ```bash curl -X GET \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/mentors/python-tutor/mentor-memories/" \ -H "Authorization: Token " ``` #### Filter by Category ```bash curl -X GET \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/mentors/python-tutor/mentor-memories/?category=knowledge_gaps" \ -H "Authorization: Token " ``` #### Create Mentor Memory ```bash curl -X POST \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/mentors/python-tutor/mentor-memories/" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "content": "The user completed the Python basics course", "category_slug": "progress_milestones" }' ``` **Response:** ```json { "id": 4, "mentor": "python-tutor", "category": { "id": 4, "name": "Progress Milestones", "slug": "progress_milestones" }, "content": "The user completed the Python basics course", "is_auto_generated": false, "created_at": "2024-01-16T15:00:00Z" } ``` #### Update Mentor Memory ```bash curl -X PATCH \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/mentors/python-tutor/mentor-memories/4/" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "content": "The user completed Python basics and intermediate courses" }' ``` #### Delete Mentor Memory ```bash curl -X DELETE \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/mentors/python-tutor/mentor-memories/4/" \ -H "Authorization: Token " ``` **Response:** `204 No Content` --- ### User Memory Settings API **Endpoints:** `/orgs/{org}/users/{user_id}/memsearch-settings/` #### Get User Settings ```bash curl -X GET \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/memsearch-settings/" \ -H "Authorization: Token " ``` **Response:** ```json { "auto_capture_enabled": true, "use_memory_in_responses": true, "updated_at": "2024-01-15T10:00:00Z" } ``` #### Update User Settings ```bash curl -X PUT \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/users/user123/memsearch-settings/" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "auto_capture_enabled": true, "use_memory_in_responses": false }' ``` **Response:** ```json { "auto_capture_enabled": true, "use_memory_in_responses": false, "updated_at": "2024-01-16T16:00:00Z" } ``` --- ### Memory Categories API **Endpoints:** `/orgs/{org}/mentors/{mentor_id}/memory-categories/` #### List Memory Categories ```bash curl -X GET \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/mentors/python-tutor/memory-categories/" \ -H "Authorization: Token " ``` **Response:** ```json { "count": 5, "results": [ { "id": 1, "name": "Knowledge Gaps", "slug": "knowledge_gaps", "description": "Topics or concepts the user struggles with", "is_active": true }, { "id": 2, "name": "Learning Goals", "slug": "learning_goals", "description": "Goals and objectives the user wants to achieve", "is_active": true } ] } ``` #### Create Custom Category ```bash curl -X POST \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/mentors/python-tutor/memory-categories/" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "name": "Project Ideas", "slug": "project_ideas", "description": "Project ideas the user has expressed interest in" }' ``` #### Update Category ```bash curl -X PATCH \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/mentors/python-tutor/memory-categories/6/" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "description": "Project ideas and coding challenges the user wants to try" }' ``` #### Deactivate Category ```bash curl -X DELETE \ "https://api.ibl.ai/api/ai-mentor/orgs/acme/mentors/python-tutor/memory-categories/6/" \ -H "Authorization: Token " ``` > **Note:** Deleting a category deactivates it (`is_active: false`). Existing memories in that category are preserved but no new memories will be extracted for it. --- ### API Endpoints Summary | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/orgs/{org}/users/{user_id}/global-memories/` | List global memories | | POST | `/orgs/{org}/users/{user_id}/global-memories/` | Create global memory | | DELETE | `/orgs/{org}/users/{user_id}/global-memories/{id}/` | Delete global memory | | GET | `/orgs/{org}/users/{user_id}/mentor-memories/` | List all mentor memories | | GET | `/orgs/{org}/users/{user_id}/mentors/{mentor}/mentor-memories/` | List mentor-specific memories | | POST | `/orgs/{org}/users/{user_id}/mentors/{mentor}/mentor-memories/` | Create mentor memory | | PATCH | `/orgs/{org}/users/{user_id}/mentors/{mentor}/mentor-memories/{id}/` | Update mentor memory | | DELETE | `/orgs/{org}/users/{user_id}/mentors/{mentor}/mentor-memories/{id}/` | Delete mentor memory | | GET | `/orgs/{org}/users/{user_id}/memsearch-settings/` | Get user settings | | PUT | `/orgs/{org}/users/{user_id}/memsearch-settings/` | Update user settings | | GET | `/orgs/{org}/mentors/{mentor}/memory-categories/` | List categories | | POST | `/orgs/{org}/mentors/{mentor}/memory-categories/` | Create category | | PATCH | `/orgs/{org}/mentors/{mentor}/memory-categories/{id}/` | Update category | | DELETE | `/orgs/{org}/mentors/{mentor}/memory-categories/{id}/` | Deactivate category | --- ## Technical Details ### Embedding Specifications | Property | Value | |----------|-------| | Dimensions | 1536 | | Provider | OpenAI / Azure OpenAI | | Storage | PostgreSQL with PGVector extension | | Search Method | Cosine Distance | ### Deduplication Thresholds | Check | Threshold | Description | |-------|-----------|-------------| | Hash Match | Exact | SHA-256 content hash comparison | | Semantic Similarity | 0.15 | Cosine distance threshold (lower = more similar) | ### Background Task Configuration | Task | Queue | Timeout | |------|-------|---------| | `process_message_for_memory` | `ai_agent` | 60s soft / 90s hard | ### LLM Usage Memory extraction uses a **cost-optimized small model** (e.g., `gpt-4o-mini`) to minimize costs while maintaining quality extraction. --- ## Troubleshooting | Issue | Possible Cause | Solution | |-------|----------------|----------| | Memories not being captured | Tenant memory not enabled | Enable in Tenant Profile → Memory tab | | Memories not being captured | Mentor memory not enabled | Enable in Mentor Settings → Memory tab | | Memories not being captured | User auto-capture disabled | User enables in their Profile settings | | Memories not used in responses | User disabled "use memories" | User enables in their Profile settings | | Duplicate memories appearing | Rare hash collision | Delete duplicate via API or SPA | | Extraction taking too long | LLM provider latency | Check provider status, consider fallback | | No categories showing | Categories not seeded | Categories auto-seed on first extraction | --- ## Python Quickstart URL: https://ibl.ai/docs/developer/agents/quickstart # Python Quickstart Create a mentor and chat with it using the [iblai/quickstarts](https://github.com/iblai/quickstarts) Python client. --- ## Overview This quickstart shows how to programmatically create an AI Mentor, start a chat session, and send messages over WebSocket — all from a Python script. You can also connect to an existing mentor and session. --- ## Prerequisites - Python 3.10+ - An ibl.ai platform API key - A tenant and username on the platform Install dependencies: ```bash pip install requests websockets ``` --- ## Environment Variables | Variable | Required | Description | |----------|----------|-------------| | `IBL_TENANT` | Yes | Your tenant key | | `IBL_USERNAME` | Yes | Your platform username | | `IBL_PLATFORM_API_KEY` | Yes | Platform API key for authentication | | `IBL_MENTOR_ID` | No | Existing mentor unique ID (skips creation) | | `IBL_SESSION_ID` | No | Existing session ID (requires `IBL_MENTOR_ID`) | | `IBL_MANAGER_URL` | No | Manager API URL (defaults to `https://base.manager.iblai.app`) | | `IBL_ASGI_URL` | No | WebSocket URL (defaults to `wss://asgi.data.iblai.app`) | --- ## Usage ```bash # Create a new mentor and chat export IBL_TENANT="my-tenant" export IBL_USERNAME="my-username" export IBL_PLATFORM_API_KEY="my-api-key" python quickstart.py "Explain quantum computing" # Chat with an existing mentor (creates a new session) export IBL_MENTOR_ID="existing-mentor-unique-id" python quickstart.py "What is machine learning?" # Continue an existing session export IBL_MENTOR_ID="existing-mentor-unique-id" export IBL_SESSION_ID="existing-session-id" python quickstart.py "Tell me more about that" ``` --- ## Source Code ### quickstart.py The entry point. Creates a mentor (or uses an existing one), starts a session, and sends a prompt. ```python from __future__ import annotations import asyncio import logging import os import random import sys import api logging.basicConfig(level=logging.INFO, format="%(message)s") log = logging.getLogger("quickstart") TENANT = os.getenv("IBL_TENANT", "") USERNAME = os.getenv("IBL_USERNAME", "") PLATFORM_API_KEY = os.getenv("IBL_PLATFORM_API_KEY", "") MENTOR_ID = os.getenv("IBL_MENTOR_ID", "") SESSION_ID = os.getenv("IBL_SESSION_ID", "") def run(prompt: str, mentor_unique_id: str = "", session_id: str = "") -> None: """Chat with a mentor using an existing mentor/session or create a new one. - Provide a mentor_unique_id to create a new session with an existing mentor. - Provide a mentor_unique_id and session_id to continue an existing session. """ if not mentor_unique_id: mentor_name = f"Mentor-{random.randint(0, 100)}" log.info("Creating a new mentor: %s", mentor_name) mentor_settings = { "new_mentor_name": mentor_name, "template_name": "ai-mentor", "display_name": mentor_name, "description": "A mentor used to explain difficult concepts in simple terms", "system_prompt": "Explain it like the user is a 5-year-old", } mentor = api.create_mentor( api_token=PLATFORM_API_KEY, tenant=TENANT, username=USERNAME, settings=mentor_settings, ) mentor_unique_id = mentor["unique_id"] log.info("Created Mentor: %s", mentor_unique_id) else: log.info("Using existing Mentor: %s", mentor_unique_id) if not session_id: log.info("Creating a new session for mentor: %s", mentor_unique_id) session_id = api.create_chat_session( username=USERNAME, tenant=TENANT, mentor_unique_id=mentor_unique_id, ) log.info("Created Session ID: %s", session_id) else: log.info("Using existing Session ID: %s", session_id) asyncio.run( api.chat_with_websocket( prompt=prompt, session_id=session_id, mentor=mentor_unique_id, tenant=TENANT, username=USERNAME, api_key=PLATFORM_API_KEY, ) ) if __name__ == "__main__": api.validate_env("IBL_TENANT", "IBL_USERNAME", "IBL_PLATFORM_API_KEY") log.info("Using tenant=%s, username=%s", TENANT, USERNAME) log.info("Using Manager URL: %s", api.MANAGER_URL) log.info("Using Asgi URL: %s", api.ASGI_URL) if len(sys.argv) < 2: log.error('Usage: python quickstart.py ""') sys.exit(1) prompt = sys.argv[1] if SESSION_ID and not MENTOR_ID: log.error("Error: If IBL_SESSION_ID is set, IBL_MENTOR_ID must also be set.") sys.exit(1) # - Provide a mentor_unique_id to create a new session with an existing mentor. # - Provide a mentor_unique_id and session_id to continue an existing session. run(prompt, mentor_unique_id=MENTOR_ID, session_id=SESSION_ID) ``` ### api.py The API client module. Handles mentor creation, session management, and WebSocket communication. ```python from __future__ import annotations import asyncio import json import logging import os import sys import typing as t import requests from websockets.client import connect log = logging.getLogger("api") DEFAULT_MANAGER_URL = "https://base.manager.iblai.app" DEFAULT_ASGI_URL = "wss://asgi.data.iblai.app" MANAGER_URL = os.getenv("IBL_MANAGER_URL", DEFAULT_MANAGER_URL) or DEFAULT_MANAGER_URL ASGI_URL = os.getenv("IBL_ASGI_URL", DEFAULT_ASGI_URL) or DEFAULT_ASGI_URL def create_mentor( api_token: str, tenant: str, username: str, settings: dict[str, str] ) -> dict[str, t.Any]: """Create a new mentor with the provided settings.""" headers = {"Authorization": f"Api-Token {api_token}"} resp = requests.post( f"{MANAGER_URL}/api/ai-mentor/orgs/{tenant}/users/{username}/mentor-with-settings/", headers=headers, json=settings, ) if not resp.ok: log.error( "Failed to create mentor (status=%s): %s", resp.status_code, resp.text, ) sys.exit(1) return resp.json() def create_chat_session(username: str, tenant: str, mentor_unique_id: str) -> str: """Create a new chat session for the given user and mentor.""" resp = requests.post( f"{MANAGER_URL}/api/ai-mentor/orgs/{tenant}/users/{username}/sessions/", json={"mentor": mentor_unique_id}, ) if not resp.ok: log.error( "Failed to create chat session (status=%s): %s", resp.status_code, resp.json(), ) sys.exit(1) session_id = resp.json().get("session_id") return session_id async def chat_with_websocket( prompt: str, session_id: str, mentor: str, tenant: str, username: str, api_key: str ): data = { "flow": { "name": mentor, "tenant": tenant, "username": username, "pathway": mentor, }, "session_id": session_id, "token": api_key, "prompt": prompt, } ws = await connect(f"{ASGI_URL}/ws/langflow/") log.info("Connected to Mentor") print("Response: ", end="") await ws.send(json.dumps(data)) eos = False while not eos: try: data = await asyncio.wait_for(ws.recv(), timeout=10) msg: dict[str, str] = json.loads(data) if "error" in msg: log.error("Error from server: %s", msg["error"]) await ws.close() sys.exit(1) if "data" in msg: print(msg["data"], end="", flush=True) if msg.get("eos"): eos = True print("") except asyncio.TimeoutError: log.warning("Timeout while waiting for response. Exiting...") await ws.close() sys.exit(1) await ws.close() def validate_env(*args: str) -> None: """Validate that all required environment variables are set.""" for key in args: if not os.getenv(key, None): log.error(f"Environment variable {key} is not set.") sys.exit(1) ``` --- ## Repository - **GitHub**: [iblai/quickstarts](https://github.com/iblai/quickstarts) --- ## .iblai Agent Standard URL: https://ibl.ai/docs/developer/agents/standard # .iblai Agent Standard The [.iblai Agent File Format](https://github.com/iblai/standard) is a Markdown-based portable agent definition format. --- ## Overview **standard** defines the `.iblai` file format, a Markdown-based specification for portable agent definitions. This format allows agents to be described, shared, and deployed across different platforms using a human-readable document structure. --- ## Repository - **GitHub**: [iblai/standard](https://github.com/iblai/standard) - **License**: Open Source --- ## Getting Started ```bash git clone https://github.com/iblai/standard.git cd standard ``` Read the specification to understand the `.iblai` file format and start defining your own portable agent configurations. --- ## App CLI URL: https://ibl.ai/docs/developer/applications/app-cli # App CLI Interactive CLI for scaffolding [ibl.ai frontend applications](https://github.com/iblai/iblai-app-cli) with Next.js and React. --- ## Overview **iblai-app-cli** is a command-line scaffolding tool that generates ibl.ai frontend applications built with Next.js and React. It provides project templates, component libraries, and configuration presets to accelerate frontend development on the ibl.ai platform. --- ## Repository - **GitHub**: [iblai/iblai-app-cli](https://github.com/iblai/iblai-app-cli) - **License**: Open Source --- ## Getting Started ```bash git clone https://github.com/iblai/iblai-app-cli.git cd iblai-app-cli ``` Run the CLI to scaffold a new ibl.ai frontend application with your preferred configuration. --- ## MCP Servers URL: https://ibl.ai/docs/developer/applications/mcp # MCP Servers A collection of [Model Context Protocol](https://github.com/iblai/iblai-mcp) servers for the ibl.ai platform. --- ## Overview **iblai-mcp** provides a collection of Model Context Protocol (MCP) servers designed for the ibl.ai platform. These servers expose platform capabilities as tools that AI agents can use, enabling deep integration between language models and ibl.ai services. --- ## Repository - **GitHub**: [iblai/iblai-mcp](https://github.com/iblai/iblai-mcp) - **License**: Open Source --- ## Getting Started ```bash git clone https://github.com/iblai/iblai-mcp.git cd iblai-mcp ``` Follow the setup instructions to configure and run MCP servers for your ibl.ai deployment. --- ## IBL Notification System — Developer Documentation URL: https://ibl.ai/docs/developer/applications/notifications # IBL Notification System — Developer Documentation > **Base URL:** `/api/notification/v1/` > **Authentication:** `Authorization: Token YOUR_ACCESS_TOKEN` > **API Version:** v1 --- ## Table of Contents 1. [Introduction](#1-introduction) 2. [System Overview](#2-system-overview) 3. [Authentication](#3-authentication) 4. [Quickstart](#4-quickstart) 5. [Core Concepts](#5-core-concepts) 6. [Notification Types Reference](#6-notification-types-reference) 7. [API Reference](#7-api-reference) - [7.1 Notifications](#71-notifications-api-reference) - [7.2 Notification Templates](#72-notification-templates-api-reference) - [7.3 Push Notifications](#73-push-notifications) - [7.4 SMTP Configuration](#74-smtp-configuration) - [7.5 Sending Direct Notifications](#75-sending-direct-notifications) 8. [Template System](#8-template-system) - [8.1 How Templates Work](#81-how-templates-work) - [8.2 Template Variables](#82-template-variables) - [8.3 Customizing Templates](#83-customizing-templates) - [8.4 Testing Templates](#84-testing-templates) - [8.5 Resetting to Default](#85-resetting-to-default) 9. [Special Notification Types](#9-special-notification-types) - [9.1 Human Support Notifications](#91-human-support-notifications) - [9.2 Policy Assignment Notifications](#92-policy-assignment-notifications) - [9.3 Proactive Learner Notifications (AI-Powered)](#93-proactive-learner-notifications-ai-powered) 10. [Notification Preferences](#10-notification-preferences) 11. [Platform SMTP Configuration](#11-platform-smtp-configuration) 12. [Notification Lifecycle](#12-notification-lifecycle) 13. [Error Reference](#13-error-reference) 14. [Rate Limits and Best Practices](#14-rate-limits-and-best-practices) --- ## 1. Introduction The IBL Notification System provides multi-channel notification delivery for the IBL learning platform. It handles routing, templating, and delivery of notifications to learners, administrators, and other platform participants across email, push, and in-app channels. ### Supported Channels - **Email** — Delivered via configured SMTP - **Push Notifications** — Delivered to registered mobile or browser clients via FCM - **In-App** — Surfaced within the IBL platform UI and accessible via API ### Key Capabilities - Multi-channel delivery with per-channel enable/disable controls - 22+ built-in notification types triggered automatically by platform events (enrollments, completions, credential issuance, and more) - Customizable notification templates for your platform with inheritance from defaults — your edits apply to a copy, not the original - Enable or disable specific notification types for your platform - AI-powered proactive learner notifications for engagement and progress recommendations (optional, requires configuration) - Human support ticket routing with configurable recipient lists ### Delivery Overview ```mermaid flowchart LR P([Your Platform]) --> NS[Notification System] NS --> E[Email] NS --> Push[Push Notifications] NS --> IA[In-App] ``` --- ## 2. System Overview The notification system operates on an event-driven model. Actions within your platform — such as a learner completing a course, a manager sending an invitation, or a policy being assigned — trigger notifications. The system evaluates your configuration, selects the appropriate template, and delivers the notification through the enabled channels. Your templates, preferences, and recipient rules are scoped to your platform only. ### How Notifications Are Generated Notifications reach your users through two paths: **Automatic** — Events on your platform trigger notifications without any action from you. When a learner enrolls in a course, completes a credential, or receives a license assignment, the system generates and delivers the corresponding notification. For each event, the system checks: 1. Whether the notification type is enabled for your platform 2. Which channels are configured for delivery 3. Which template applies (your customized version or the inherited default) 4. Who the recipients are **Manual (Direct Send)** — You compose and send notifications on demand using the Direct Send API. You select a template or write custom content, choose delivery channels, and define your audience from multiple sources (email addresses, usernames, user groups, departments, programs, or CSV uploads). Recipients are merged and deduplicated across sources before sending. Both paths deliver through the same channels and respect your platform's template and preference configuration. ### Admin and User Interaction Points As a platform admin, you use the Admin API to: - Enable or disable notification types for your platform - Customize templates (your edits apply to a copy of the default) - Configure human support routing recipients - Control AI-powered proactive notification settings - Send direct notifications to targeted recipients Your users use the User API to: - Retrieve their notification list - Mark notifications as read - Dismiss notifications ### System Flow ```mermaid flowchart TD PE([Platform Events]) -->|Automatic| NS[Notification System] DS([Direct Send API]) -->|Manual| NS AdminAPI[Admin API] -->|Configure templates\nand preferences| NS UserAPI[User API] -->|Retrieve and manage\nnotifications| NS NS --> CH{Channel\nRouter} CH --> Email[Email] CH --> Push[Push Notifications] CH --> InApp[In-App] Email --> R([Your Recipients]) Push --> R InApp --> R ``` ### Notification Lifecycle Each notification begins as `UNREAD` when delivered. Your users can mark them as `READ` or `CANCELLED`. A `READ` notification can be reverted to `UNREAD`, but `CANCELLED` is terminal. ```mermaid stateDiagram-v2 [*] --> UNREAD : Notification delivered UNREAD --> READ : User marks as read READ --> UNREAD : User marks as unread UNREAD --> CANCELLED : User cancels READ --> CANCELLED : User cancels CANCELLED --> [*] ``` --- ## 3. Authentication All Notification API endpoints require token-based authentication. Requests must include a valid access token in the `Authorization` header: ``` Authorization: Token YOUR_ACCESS_TOKEN ``` Obtain a token from your platform's authentication endpoint. Tokens are scoped to a user identity and validated on every request. ### Permission Levels | Action | Required Role | |---|---| | Read own notifications | Authenticated user | | Manage own notifications (read/unread/delete) | Authenticated user | | List/customize templates | Platform Admin | | Toggle notification types | Platform Admin | | Test SMTP credentials | Platform Admin | | Send direct notifications | Platform Admin or Department Admin | ### Example Request ```bash curl -X GET "https://platform.iblai.app/api/notification/v1/orgs/acme-learning/users/jane.doe/notifications/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" ``` ### RBAC If RBAC is enabled on your platform, permissions are validated against granular resource actions in addition to role checks. For example, listing notifications requires `Ibl.Notifications/Notification/list` and writing requires `Ibl.Notifications/Notification/write`. A role check alone is not sufficient when RBAC enforcement is active — the token's associated user must also hold the relevant resource action grant. ### Security Store tokens securely on the server side. Never expose tokens in client-side JavaScript, browser storage accessible to third-party scripts, or version control. Treat a token with the same care as a password — it grants full API access as the authenticated user. --- ## 4. Quickstart ### Prerequisites - **API token** — Pass it in every request as `Authorization: Token YOUR_ACCESS_TOKEN`. - **Platform key** — The slug that identifies your organisation (e.g., `acme-learning`). This appears as the `org` or `platform_key` path segment in every URL. --- ### Step 1: List Notifications for a User Retrieve a paginated list of notifications. Unread notifications are returned first, followed by read ones in reverse-chronological order. ``` GET /api/notification/v1/orgs/{org}/users/{user_id}/notifications/ ``` **Query parameters (all optional)** | Parameter | Description | |---|---| | `status` | Filter by status: `UNREAD`, `READ` | | `channel` | Filter by delivery channel (e.g. `email`, `push_notification`) | | `exclude_channel` | Exclude a specific channel from results | | `start_date` | Return notifications created on or after this date | | `end_date` | Return notifications created on or before this date | ```bash curl -X GET \ "https://platform.iblai.app/api/notification/v1/orgs/acme-learning/users/jane.doe/notifications/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" ``` **Response** ```json { "count": 2, "next": null, "previous": null, "results": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "username": "jane.doe", "title": "New course available: Introduction to Data Science", "body": "

Hi Jane, you have been enrolled in Introduction to Data Science.

", "status": "UNREAD", "channel": "email", "context": { "course_name": "Introduction to Data Science", "username": "jane.doe" }, "short_message": "You have been enrolled in Introduction to Data Science.", "created_at": "2026-04-11T08:30:00Z", "updated_at": "2026-04-11T08:30:00Z" }, { "id": "7cb92a41-1e3d-4f8b-a2c6-9d0e5f7b3a12", "username": "jane.doe", "title": "Your certificate is ready", "body": "

Your certificate for Python Fundamentals is ready to download.

", "status": "READ", "channel": "email", "context": { "course_name": "Python Fundamentals", "username": "jane.doe" }, "short_message": "Your Python Fundamentals certificate is ready.", "created_at": "2026-04-10T14:15:00Z", "updated_at": "2026-04-10T14:22:00Z" } ] } ``` **Response fields** | Field | Type | Description | |---|---|---| | `id` | UUID | Unique notification identifier | | `username` | string | Recipient's username | | `title` | string | Rendered notification title | | `body` | string | Rendered full body (may contain HTML) | | `status` | string | `UNREAD`, `READ`, or `CANCELLED` | | `channel` | string or null | Delivery channel name | | `context` | object | Template variables used to render this notification | | `short_message` | string | Short summary for push notification previews | | `created_at` | ISO 8601 | Creation timestamp | | `updated_at` | ISO 8601 | Last status change timestamp | --- ### Step 2: Check Unread Count ``` GET /api/notification/v1/orgs/{org}/users/{user_id}/notifications-count/?status=UNREAD ``` ```bash curl -X GET \ "https://platform.iblai.app/api/notification/v1/orgs/acme-learning/users/jane.doe/notifications-count/?status=UNREAD" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" ``` ```json { "count": 5 } ``` --- ### Step 3: Mark a Notification as Read ``` PUT /api/notification/v1/orgs/{org}/users/{user_id}/notifications/ ``` ```bash curl -X PUT \ "https://platform.iblai.app/api/notification/v1/orgs/acme-learning/users/jane.doe/notifications/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "notification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "status": "READ" }' ``` To mark multiple notifications, pass a comma-separated list: ```json { "notification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6,7cb92a41-1e3d-4f8b-a2c6-9d0e5f7b3a12", "status": "READ" } ``` ```json { "message": "Notification status updated successfully", "success": true } ``` --- ### Step 4: Mark All Notifications as Read ``` POST /api/notification/v1/orgs/{platform_key}/mark-all-as-read ``` ```bash curl -X POST \ "https://platform.iblai.app/api/notification/v1/orgs/acme-learning/mark-all-as-read" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{}' ``` To mark a specific subset, provide `notification_ids`: ```json { "notification_ids": [ "3fa85f64-5717-4562-b3fc-2c963f66afa6", "7cb92a41-1e3d-4f8b-a2c6-9d0e5f7b3a12" ] } ``` **Response** ```json { "message": "Successfully marked 5 notifications as read", "count": 5 } ``` This endpoint operates on the authenticated user derived from the token. The `platform_key` scopes the operation to your platform only. --- ### What's Next - **[Template Customization (Section 8)](#8-template-system):** Edit notification templates, override subject lines and HTML bodies, use template variables. - **[Notification Preferences (Section 10)](#10-notification-preferences):** Enable or disable notification types for your platform. - **[SMTP Setup (Section 11)](#11-platform-smtp-configuration):** Connect your SMTP server and verify credentials. --- ## 5. Core Concepts | Concept | Description | |---------|-------------| | **Platform** | Your isolated notification environment. Templates, preferences, and delivery settings are scoped to your platform. | | **Notification Template** | Defines content, channels, and behavior for a notification type. You inherit defaults until you customize — your changes apply to your own copy only. | | **Notification** | A sent notification record tied to a user, with delivery and read status tracking. | | **Channel** | A delivery method: `email`, `push_notification`, or `sms`. | | **Notification Type** | A category of notification (e.g., `USER_NOTIF_COURSE_ENROLLMENT`). Each type maps to one template for your platform. | | **Notification Preference** | A platform-level toggle that enables or disables a notification type for all users on your platform. | ### How Template Inheritance Works ```mermaid flowchart TD A[Default Template\nshared until you customize] --> B{You customize?} B -- No --> C[Uses default as-is\nno copy created] B -- Yes --> D[Your copy created\nedits apply to your copy only] D --> E{Reset requested?} E -- Yes --> C E -- No --> D ``` --- ## 6. Notification Types Reference | Type | Category | Trigger | Description | |------|----------|---------|-------------| | `USER_NOTIF_USER_REGISTRATION` | User | User account created | Welcomes new users to the platform | | `APP_REGISTRATION` | User | User registers via a linked application | Welcome message with app name and benefits | | `USER_NOTIF_COURSE_ENROLLMENT` | Learning | Enrolled in a course | Confirms course enrollment to the learner | | `USER_NOTIF_COURSE_COMPLETION` | Learning | Completed a course | Congratulates on course completion with certificate link | | `USER_NOTIF_CREDENTIALS` | Learning | Credential issued | Notifies learner of new credential with credential URL | | `USER_NOTIF_LEARNER_PROGRESS` | Learning | Periodic progress summary | Delivers progress digest with courses, time spent, credentials | | `USER_NOTIF_USER_INACTIVITY` | Engagement | User inactive for configured period | Re-engagement notification with inactivity details | | `PLATFORM_INVITATION` | Invitation | Admin sends platform invite | Invites user to join the platform with redirect link | | `COURSE_INVITATION` | Invitation | Admin sends course invite | Invites user to a course with enrollment link | | `PROGRAM_INVITATION` | Invitation | Admin sends program invite | Invites user to a program with enrollment link | | `COURSE_LICENSE_ASSIGNMENT` | License | Course license assigned to user | Notifies user of course access via license | | `COURSE_LICENSE_GROUP_ASSIGNMENT` | License | Course license assigned to user group | Notifies group members of course access | | `PROGRAM_LICENSE_ASSIGNMENT` | License | Program license assigned to user | Notifies user of program access via license | | `PROGRAM_LICENSE_GROUP_ASSIGNMENT` | License | Program license assigned to user group | Notifies group members of program access | | `USER_LICENSE_ASSIGNMENT` | License | Platform user license assigned | Notifies user of platform license with benefits | | `USER_LICENSE_GROUP_ASSIGNMENT` | License | Platform user license assigned to group | Notifies group members of platform license | | `ROLE_CHANGE` | Admin | User's platform role changed | Notifies user of role promotion or demotion | | `ADMIN_NOTIF_COURSE_ENROLLMENT` | Admin | User enrolls in a course | Alerts admins with student name, email, and course | | `POLICY_ASSIGNMENT` | RBAC | RBAC policy assigned or removed | Notifies user of role assignment/removal with resources | | `HUMAN_SUPPORT_NOTIFICATION` | Support | Human support ticket created | Alerts support staff with ticket details and chat link | | `PROACTIVE_LEARNER_NOTIFICATION` | AI | Scheduled | AI-generated personalized learning recommendations | | `REPORT_COMPLETED` | Admin | Async report finishes processing | Notifies user with report status and download URL | | `CUSTOM_NOTIFICATION` | Custom | Code-driven | Platform-defined custom notification with custom targeting | --- ## 7. API Reference **Base path:** `/api/notification/v1/` **Authentication:** `Token` header required on all endpoints unless noted otherwise. --- ### 7.1 Notifications API Reference #### Summary | Method | Endpoint | Auth | RBAC Permission | Description | |--------|----------|------|-----------------|-------------| | GET | `/orgs/{org}/notifications/` | Token | `Ibl.Notifications/Notification/list` | List paginated notifications scoped to the org | | PUT | `/orgs/{org}/notifications/` | Token | `Ibl.Notifications/Notification/write` | Update notification status scoped to the org | | PATCH | `/orgs/{org}/notifications/bulk-update/` | Token | `Ibl.Notifications/Notification/write` | Bulk status update scoped to the org | | GET | `/orgs/{org}/users/{user_id}/notifications/` | Token | `Ibl.Notifications/Notification/list` | List paginated notifications for a specific user | | PUT | `/orgs/{org}/users/{user_id}/notifications/` | Token | `Ibl.Notifications/Notification/write` | Update status of one or more notifications by ID | | PATCH | `/orgs/{org}/users/{user_id}/notifications/bulk-update/` | Token | `Ibl.Notifications/Notification/write` | Set a single status across all of a user's notifications | | DELETE | `/orgs/{org}/users/{user_id}/notifications/{id}/` | Token | `Ibl.Notifications/Notification/delete` | Delete a single notification | | GET | `/orgs/{org}/users/{user_id}/notifications-count/` | Token | `Ibl.Notifications/Notification/list` | Return count of notifications matching filters | | POST | `/orgs/{platform_key}/mark-all-as-read` | Token | `Ibl.Notifications/Notification/write` | Mark all unread notifications as read | --- #### GET `/orgs/{org}/users/{user_id}/notifications/` List notifications for a specific user on your platform. Results are ordered with unread notifications first, then by creation date descending. Paginated. **Path parameters** | Name | Type | Description | |------|------|-------------| | `org` | string (slug) | Platform key | | `user_id` | string | Username of the target user | **Query parameters** | Name | Type | Required | Description | |------|------|----------|-------------| | `status` | string | No | Filter by status: `READ`, `UNREAD`, `CANCELLED` | | `channel` | string | No | Filter by channel name (e.g. `email`, `push_notification`) | | `start_date` | string | No | Notifications created on or after this date | | `end_date` | string | No | Notifications created on or before this date | | `exclude_channel` | string | No | Exclude a specific channel | | `page` | integer | No | Page number for pagination | **Response** `200 OK` ```json { "count": 2, "next": 2, "previous": null, "results": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "username": "jane.doe", "title": "Your course certificate is ready", "body": "

Your certificate for Intro to Data Science is now available.

", "status": "UNREAD", "channel": "email", "context": { "course_name": "Intro to Data Science", "username": "jane.doe" }, "short_message": "Your certificate for Intro to Data Science is ready.", "created_at": "2026-04-10T14:23:01.123456Z", "updated_at": "2026-04-10T14:23:01.123456Z" } ] } ``` **Response fields** | Field | Type | Description | |-------|------|-------------| | `count` | integer | Total notifications matching the query | | `next` | integer or null | Next page number | | `previous` | integer or null | Previous page number | | `results[].id` | UUID | Unique notification identifier | | `results[].username` | string or null | Recipient's username | | `results[].title` | string | Rendered notification title | | `results[].body` | string | Rendered body (may contain HTML) | | `results[].status` | string | `READ`, `UNREAD`, or `CANCELLED` | | `results[].channel` | string or null | Delivery channel name | | `results[].context` | object or null | Template context variables used for rendering | | `results[].short_message` | string | Short message for SMS or preview | | `results[].created_at` | ISO 8601 | Creation timestamp | | `results[].updated_at` | ISO 8601 | Last update timestamp | ```bash curl -X GET "https://platform.iblai.app/api/notification/v1/orgs/acme-learning/users/jane.doe/notifications/?status=UNREAD" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" ``` --- #### PUT `/orgs/{org}/users/{user_id}/notifications/` Update the status of one or more notifications by UUID. **Request body** | Field | Type | Required | Description | |-------|------|----------|-------------| | `notification_id` | string | Yes | Comma-separated notification UUIDs | | `status` | string | Yes | `READ`, `UNREAD`, or `CANCELLED` | ```json { "notification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6,7cb93e21-1234-4abc-9876-def012345678", "status": "READ" } ``` **Response** `200 OK` ```json { "message": "Notification status updated successfully", "success": true } ``` --- #### PATCH `/orgs/{org}/users/{user_id}/notifications/bulk-update/` Set a single status on **all** notifications for the specified user on your platform. **Request body** | Field | Type | Required | Description | |-------|------|----------|-------------| | `status` | string | Yes | `READ`, `UNREAD`, or `CANCELLED` | **Response** `200 OK` ```json { "message": "Notification status updated successfully" } ``` **Error** `400 Bad Request` — returned when the user has no notifications. ```json { "error": "Notification does not exist" } ``` --- #### DELETE `/orgs/{org}/users/{user_id}/notifications/{id}/` Permanently delete a single notification. **Path parameters** | Name | Type | Description | |------|------|-------------| | `org` | string | Platform key | | `user_id` | string | Username | | `id` | string | Notification ID | **Response** `200 OK` ```json { "message": "Notification deleted successfully" } ``` **Error** `404 Not Found` ```json { "message": "Notification does not exist" } ``` --- #### GET `/orgs/{org}/users/{user_id}/notifications-count/` Return the count of notifications for a user. **Query parameters** | Name | Type | Required | Description | |------|------|----------|-------------| | `status` | string | No | `READ`, `UNREAD`, or `CANCELLED` | | `channel` | string | No | Filter by channel name | **Response** `200 OK` ```json { "count": 5 } ``` --- #### POST `/orgs/{platform_key}/mark-all-as-read` Mark all unread notifications as read for the authenticated user on the platform. Optionally restrict to specific IDs. **Request body** | Field | Type | Required | Description | |-------|------|----------|-------------| | `notification_ids` | array of UUIDs | No | If provided, only these are marked. If omitted, all unread are marked. | **Response** `200 OK` ```json { "message": "Successfully marked 3 notifications as read", "count": 3 } ``` --- ### 7.2 Notification Templates API Reference All template endpoints require **Platform Admin** role. #### Summary | Method | Endpoint | Auth | Role | Description | |--------|----------|------|------|-------------| | GET | `/platforms/{platform_key}/templates/` | Token | Platform Admin | List all templates | | GET | `/platforms/{platform_key}/templates/{type}/` | Token | Platform Admin | Get template detail | | PATCH | `/platforms/{platform_key}/templates/{type}/` | Token | Platform Admin | Customize a template (creates your copy on first edit) | | PATCH | `/platforms/{platform_key}/templates/{type}/toggle/` | Token | Platform Admin | Enable or disable a notification type | | POST | `/platforms/{platform_key}/templates/{type}/reset/` | Token | Platform Admin | Reset to default | | POST | `/platforms/{platform_key}/templates/{type}/test/` | Token | Platform Admin | Send a test notification | --- #### GET `/platforms/{platform_key}/templates/` Returns every notification template visible to your platform, including inherited defaults and your customized overrides. Custom notification types are appended after system types. **Response** `200 OK` ```json [ { "id": 14, "type": "USER_NOTIF_COURSE_ENROLLMENT", "name": "Course Enrollment", "description": "Sent when a learner enrolls in a course", "is_inherited": false, "source_platform": "acme-learning", "is_enabled": true, "can_customize": true, "is_custom": false, "message_title": "You have been enrolled in {{ course_name }}", "email_subject": "Welcome to {{ course_name }}", "spas": ["skills", "admin"], "allowed_channels": ["email", "push_notification"], "available_context": { "username": "Learner's username", "course_name": "Name of the course", "site_name": "Platform display name" } } ] ``` **Response fields** | Field | Type | Description | |-------|------|-------------| | `id` | UUID or string | Template ID. Custom notification types use a `custom_` string format. | | `type` | string | Notification type key | | `name` | string | Human-readable name | | `description` | string | When this notification is sent | | `is_inherited` | boolean | `true` if using the unmodified default template | | `source_platform` | string | Platform key that owns the active template | | `is_enabled` | boolean | Whether this type is enabled for the platform | | `can_customize` | boolean | Whether template content is editable | | `is_custom` | boolean | `true` for custom notification types | | `message_title` | string | Push/in-app title | | `email_subject` | string | Email subject line | | `spas` | array of strings | SPAs that surface this notification | | `allowed_channels` | array of strings | Delivery channels | | `available_context` | object | Variable keys and descriptions | --- #### GET `/platforms/{platform_key}/templates/{type}/` Returns full content and configuration of a single template. If you have a customized template, that is returned; otherwise the default template is returned with `is_inherited: true`. **Response** `200 OK` ```json { "id": 14, "type": "USER_NOTIF_COURSE_ENROLLMENT", "name": "Course Enrollment", "description": "Sent when a learner enrolls in a course", "message_title": "You have been enrolled in {{ course_name }}", "message_body": "Hi {{ username }}, you have been enrolled in {{ course_name }}.", "short_message_body": "Enrolled in {{ course_name }}", "email_subject": "Welcome to {{ course_name }}", "email_from_address": "no-reply@acme-learning.com", "email_html_template": "

Hi {{ username }},

You have been enrolled in {{ course_name }}.

", "spas_detail": [ {"id": 2, "name": "skills", "description": "Skills SPA"} ], "allowed_channels_detail": [ {"id": 1, "name": "email"}, {"id": 3, "name": "push_notification"} ], "is_inherited": false, "is_enabled": true, "source_platform": "acme-learning", "can_customize": true, "metadata": {}, "available_context": { "username": "Learner's username", "course_name": "Name of the course" }, "periodic_config": null, "policy_config": null, "human_support_config": null, "created_at": "2025-03-01T09:00:00Z", "updated_at": "2025-11-15T14:32:00Z" } ``` **Additional response fields (detail only)** | Field | Type | Description | |-------|------|-------------| | `message_body` | string | Full body text | | `short_message_body` | string | Short version for SMS/preview | | `email_from_address` | string | Sender email | | `email_html_template` | string | HTML email body | | `spas_detail` | array | SPA objects with `id`, `name`, `description` | | `allowed_channels_detail` | array | Channel objects with `id`, `name` | | `metadata` | object | Raw configuration blob | | `periodic_config` | object or null | Schedule config (for `PROACTIVE_LEARNER_NOTIFICATION` only) | | `policy_config` | object or null | Role config (for `POLICY_ASSIGNMENT` only) | | `human_support_config` | object or null | Recipient config (for `HUMAN_SUPPORT_NOTIFICATION` only) | **`periodic_config` fields** (when present) | Field | Type | Description | |-------|------|-------------| | `learner_scope` | string | `ACTIVE_LEARNERS` or `ALL_LEARNERS` | | `report_period_days` | integer | Days of activity to include | | `frequency` | string | `DAILY`, `WEEKLY`, `MONTHLY`, or `CUSTOM` | | `custom_interval_days` | integer or null | Interval when frequency is `CUSTOM` | | `execution_time` | string | `HH:MM` (24-hour) | | `timezone` | string | Timezone name | | `mentors` | array | Mentor configs: `[{"unique_id", "prompt", "name"}]` | | `last_execution_date` | string or null | ISO 8601 date of last run | | `next_execution_date` | string or null | ISO 8601 date of next run | **`policy_config` fields** (when present) | Field | Type | Description | |-------|------|-------------| | `enabled_policies` | array | Per-role configs: `[{"role_name", "enabled", "subject"}]` | | `notify_on_assignment` | boolean | Notify on policy assignment | | `notify_on_removal` | boolean | Notify on policy removal | **`human_support_config` fields** (when present) | Field | Type | Description | |-------|------|-------------| | `recipient_mode` | string | `platform_admins_and_mentor_owner`, `platform_admins_only`, `mentor_owner_only`, or `custom` | | `custom_recipients` | array | Recipient targets when mode is `custom` | --- #### PATCH `/platforms/{platform_key}/templates/{type}/` Customize a notification template. On the first PATCH for a given type, the system creates your own copy of the default template and applies the changes. Subsequent PATCHes update your existing copy. Only fields present in the request body are changed. **Writable fields** | Field | Type | Description | |-------|------|-------------| | `name` | string | Template display name | | `description` | string | Description | | `message_title` | string | Push/in-app title | | `message_body` | string | Body text | | `short_message_body` | string | Short version | | `email_subject` | string | Email subject | | `email_from_address` | string | Sender email | | `email_html_template` | string | HTML email body | | `spa_ids` | array of integers | SPA IDs | | `channel_ids` | array of integers | Channel IDs | **Periodic config fields** (for `PROACTIVE_LEARNER_NOTIFICATION`) | Field | Type | Description | |-------|------|-------------| | `periodic_learner_scope` | string | `ACTIVE_LEARNERS` or `ALL_LEARNERS` | | `periodic_report_period_days` | integer (1–365) | Report window | | `periodic_frequency` | string | `DAILY`, `WEEKLY`, `MONTHLY`, `CUSTOM` | | `periodic_custom_interval_days` | integer (1–365) | Interval for `CUSTOM` | | `periodic_execution_time` | string | `HH:MM` format | | `periodic_timezone` | string | Timezone name | | `periodic_mentors` | array | Mentor configs | **Policy config fields** (for `POLICY_ASSIGNMENT`) | Field | Type | Description | |-------|------|-------------| | `policy_enabled_policies` | array | Per-role config | | `policy_notify_on_assignment` | boolean | Notify on assignment | | `policy_notify_on_removal` | boolean | Notify on removal | **Human support config fields** (for `HUMAN_SUPPORT_NOTIFICATION`) | Field | Type | Description | |-------|------|-------------| | `human_support_recipient_mode` | string | Recipient mode | | `human_support_custom_recipients` | array | Custom recipients | **Example request** ```bash curl -X PATCH "https://platform.iblai.app/api/notification/v1/platforms/acme-learning/templates/USER_NOTIF_COURSE_ENROLLMENT/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email_subject": "Welcome to {{ course_name }} on Acme Learning", "message_title": "Enrollment confirmed: {{ course_name }}" }' ``` Returns the full updated template in the same shape as the detail response. --- #### PATCH `/platforms/{platform_key}/templates/{type}/toggle/` Enable or disable a notification type. This does not modify template content. **Request body** | Field | Type | Required | Description | |-------|------|----------|-------------| | `allow_notification` | boolean | Yes | `true` to enable; `false` to disable | ```bash curl -X PATCH "https://platform.iblai.app/api/notification/v1/platforms/acme-learning/templates/USER_NOTIF_COURSE_ENROLLMENT/toggle/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"allow_notification": false}' ``` **Response** `200 OK` ```json { "type": "USER_NOTIF_COURSE_ENROLLMENT", "is_enabled": false, "platform": "acme-learning", "message": "Notification disabled successfully" } ``` --- #### POST `/platforms/{platform_key}/templates/{type}/reset/` Deletes your customized template. Your platform falls back to the default template. The toggle state (enabled/disabled) is not affected. No request body required. ```bash curl -X POST "https://platform.iblai.app/api/notification/v1/platforms/acme-learning/templates/USER_NOTIF_COURSE_ENROLLMENT/reset/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" ``` **Response** `200 OK` ```json { "message": "Template reset to default. Platform will now use main template.", "deleted": true } ``` If no platform override existed: ```json { "message": "Template was already using default from main platform.", "deleted": false } ``` --- #### POST `/platforms/{platform_key}/templates/{type}/test/` Send a test email using the template. The email is sent to the requesting admin's email address. **Request body** | Field | Type | Required | Description | |-------|------|----------|-------------| | `context` | object | No | Key-value pairs injected into the template. Overrides defaults. | Default context (auto-injected): `username`, `site_name`, `course_name`, `platform_key`. ```bash curl -X POST "https://platform.iblai.app/api/notification/v1/platforms/acme-learning/templates/USER_NOTIF_COURSE_ENROLLMENT/test/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "context": { "course_name": "Introduction to Machine Learning" } }' ``` **Response** `200 OK` ```json { "success": true, "message": "Test notification sent successfully to admin@acme-learning.com", "recipient": "admin@acme-learning.com" } ``` **Response** `500` — delivery failure ```json { "success": false, "message": "Failed to send test notification. Check email configuration." } ``` --- ### 7.3 Push Notifications Push notifications allow your platform to send real-time alerts via Firebase Cloud Messaging (FCM). > These endpoints are only available when push notifications are enabled on your platform. #### Register FCM Device Token ``` POST /api/notification/v1/orgs/{org}/users/{user_id}/register-fcm-token/ ``` **Request body** | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `name` | string | Yes | — | Device or app name | | `registration_id` | string | Yes | — | FCM device registration token | | `active` | boolean | No | `true` | Whether this registration is active | | `cloud_message_type` | string | No | `"FCM"` | Cloud messaging type | | `application_id` | string | No | `"ibl_fcm_app"` | Application identifier | ```bash curl -X POST \ "https://platform.iblai.app/api/notification/v1/orgs/acme-learning/users/john.doe/register-fcm-token/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "John iPhone 15", "registration_id": "eX3ampleFCMToken:APA91bHPRgkF..." }' ``` **Response** `200 OK` ```json { "success": true, "message": "Token created successfully" } ``` #### Remove FCM Device Token ``` DELETE /api/notification/v1/orgs/{org}/users/{user_id}/register-fcm-token/ ``` **Request body** | Field | Type | Required | Description | |-------|------|----------|-------------| | `registration_id` | string | Yes | Device registration token to remove | ```bash curl -X DELETE \ "https://platform.iblai.app/api/notification/v1/orgs/acme-learning/users/john.doe/register-fcm-token/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"registration_id": "eX3ampleFCMToken:APA91bHPRgkF..."}' ``` **Response** `200 OK` ```json { "success": true, "message": "Registration ID delete successfully" } ``` --- ### 7.4 SMTP Configuration #### Test SMTP Credentials Opens a live SMTP connection and sends a test email to verify credentials. ``` POST /api/notification/v1/platforms/{platform_key}/config/test-smtp/ ``` **Auth:** Token, Platform Admin (`Ibl.Notifications/SMTP/action`) **Request body** | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `smtp_host` | string | Yes | — | SMTP server hostname | | `smtp_port` | integer | Yes | — | SMTP server port (1–65535) | | `smtp_username` | string | Yes | — | SMTP username | | `smtp_password` | string | Yes | — | SMTP password (write-only) | | `use_tls` | boolean | No | `true` | Enable STARTTLS | | `use_ssl` | boolean | No | `false` | Enable SSL/TLS on connect | | `test_email` | email | Yes | — | Recipient for the test email | | `from_email` | email | No | — | Sender address (defaults to `smtp_username`) | > `use_tls` and `use_ssl` are mutually exclusive. Use `use_tls: true` with port 587 or `use_ssl: true` with port 465. ```bash curl -X POST \ "https://platform.iblai.app/api/notification/v1/platforms/acme-learning/config/test-smtp/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "smtp_host": "smtp.gmail.com", "smtp_port": 587, "smtp_username": "notifications@example.com", "smtp_password": "app-specific-password", "use_tls": true, "use_ssl": false, "test_email": "verify@example.com", "from_email": "notifications@example.com" }' ``` **Response** `200 OK` ```json { "success": true, "status": "success", "message": "Test email sent successfully to verify@example.com. Please check your inbox to confirm delivery." } ``` **Response** `500` — authentication failure ```json { "success": false, "status": "error", "message": "SMTP test failed: Authentication failed. Please check your username and password." } ``` **Response** `500` — connection failure ```json { "success": false, "status": "error", "message": "SMTP test failed: Connection failed. Please check your host and port settings." } ``` --- ### 7.5 Sending Direct Notifications Send notifications on demand to any combination of recipients on your platform. You select a template or write custom content, choose delivery channels, define your audience from multiple sources, preview the recipient list, and send. **Auth:** Token, Platform Admin or Department Admin #### Builder Flow ```mermaid flowchart TD A([1. Get context]) --> B([2. Validate sources]) B --> C([3. Preview]) C --> D{Review recipients} D -- Looks good --> E([4. Send]) D -- Adjust --> B C --> F([Browse recipients\npaginated]) style A fill:#f5f5f5,stroke:#333 style E fill:#f5f5f5,stroke:#333 ``` #### Summary | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/orgs/{platform_key}/notification-builder/context/` | Get available templates, channels, and recipient source types | | POST | `/orgs/{platform_key}/notification-builder/validate_source/` | Validate a recipient source before previewing | | POST | `/orgs/{platform_key}/notification-builder/preview/` | Combine sources, deduplicate recipients, create a build | | GET | `/orgs/{platform_key}/notification-builder/{build_id}/recipients/` | Browse paginated recipients for a build | | POST | `/orgs/{platform_key}/notification-builder/send/` | Send the notification to all recipients in a build | --- #### GET `/orgs/{platform_key}/notification-builder/context/` Returns the templates, channels, and recipient source types available on your platform. Call this first to populate your UI or to discover what IDs to use in subsequent requests. **Response** `200 OK` ```json { "status": "success", "data": { "templates": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "Course Enrollment", "type": "USER_NOTIF_COURSE_ENROLLMENT" } ], "channels": [ {"id": 1, "name": "email"}, {"id": 2, "name": "push_notification"}, {"id": 3, "name": "in_app"} ], "sources": [ "email", "username", "platform", "csv", "department", "pathway", "program", "usergroup" ] } } ``` ```bash curl -X GET \ "https://your-domain.com/api/notification/v1/orgs/acme-learning/notification-builder/context/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" ``` --- #### Recipient Source Types You can target recipients from any combination of these sources. When you provide multiple sources in a preview request, recipients are merged and deduplicated. | Source | `data` value | Description | |--------|-------------|-------------| | `email` | Comma-separated email addresses | Direct email list. Validated against Django email rules. | | `username` | Comma-separated usernames | Platform usernames. Validated against your user records. | | `platform` | Platform key (string) | All users on your platform. | | `csv` | Uploaded file (multipart) | CSV file with an `email` column header. | | `department` | Department ID (integer) | All active members of a department. | | `pathway` | Pathway ID (string) | All suggested members of a pathway. | | `program` | Program ID (string) | All suggested members of a program. | | `usergroup` | User Group ID (integer) | All active members of a user group. | --- #### POST `/orgs/{platform_key}/notification-builder/validate_source/` Validates a single recipient source and returns the valid count, any invalid entries, and a sample of resolved recipients. Call this per source before previewing to catch errors early. **Request body** | Field | Type | Required | Description | |-------|------|----------|-------------| | `type` | string | Yes | One of the source types above | | `data` | string | Yes (except CSV) | The source value (emails, usernames, IDs, etc.) | For CSV sources, send as multipart form data with the file in a `file_0` field. ```bash curl -X POST \ "https://your-domain.com/api/notification/v1/orgs/acme-learning/notification-builder/validate_source/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "email", "data": "jane@example.com,john@example.com,invalid-address" }' ``` **Response** `200 OK` ```json { "status": "success", "valid_count": 2, "invalid_entries": ["invalid-address"], "sample_recipients": [ {"username": "jane.doe", "email": "jane@example.com"}, {"username": "john.smith", "email": "john@example.com"} ] } ``` --- #### POST `/orgs/{platform_key}/notification-builder/preview/` Combines all your sources, deduplicates recipients, and creates a build record. Returns a `build_id` you use to browse recipients and to send. **Request body** | Field | Type | Required | Description | |-------|------|----------|-------------| | `template_id` | UUID | No | ID of an existing template. Mutually exclusive with `template_data`. | | `template_data` | object | No | Custom content: `{"message_title": "...", "message_body": "..."}`. Mutually exclusive with `template_id`. | | `channels` | array of integers | Yes | Channel IDs from the context endpoint | | `sources` | array of objects | Yes | Each object: `{"type": "...", "data": "..."}` | | `context` | object | No | Custom template variables (e.g. `{"course_name": "Python 101"}`) | | `process_on` | ISO 8601 datetime | No | Schedule send for a future time. If omitted, sends immediately on `/send`. | You must provide either `template_id` or `template_data`, not both. `template_data.message_body` supports template syntax (`{{ variable }}`). ```bash curl -X POST \ "https://your-domain.com/api/notification/v1/orgs/acme-learning/notification-builder/preview/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "template_data": { "message_title": "Platform Maintenance Notice", "message_body": "Hi {{ username }}, scheduled maintenance is planned for April 20." }, "channels": [1, 3], "sources": [ {"type": "usergroup", "data": "12"}, {"type": "email", "data": "external-user@example.com"} ], "context": {} }' ``` **Response** `200 OK` ```json { "status": "success", "build_id": "9c1a2b3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", "count": 48, "warning": null, "recipients": [ {"username": "jane.doe", "email": "jane@example.com", "status": "pending"}, {"username": "john.smith", "email": "john@example.com", "status": "pending"} ] } ``` **Response fields** | Field | Type | Description | |-------|------|-------------| | `build_id` | UUID | Use this in `/recipients` and `/send` | | `count` | integer | Total deduplicated recipients | | `warning` | string or null | If a similar notification was sent in the last 24 hours, this describes it | | `recipients` | array | First 10 recipients as a preview | --- #### GET `/orgs/{platform_key}/notification-builder/{build_id}/recipients/` Paginated list of all recipients in a build. Use this to review the full audience before sending. **Query parameters** | Name | Type | Required | Description | |------|------|----------|-------------| | `search` | string | No | Filter by username or email (case-insensitive) | | `page` | integer | No | Page number (default: 1) | | `page_size` | integer | No | Results per page (default: 10) | ```bash curl -X GET \ "https://your-domain.com/api/notification/v1/orgs/acme-learning/notification-builder/9c1a2b3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d/recipients/?page=1&page_size=20" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" ``` **Response** `200 OK` ```json { "count": 48, "next": "https://your-domain.com/...?page=2&page_size=20", "previous": null, "results": [ {"username": "jane.doe", "email": "jane@example.com", "status": "pending"}, {"username": "john.smith", "email": "john@example.com", "status": "pending"} ] } ``` --- #### POST `/orgs/{platform_key}/notification-builder/send/` Sends the notification to all recipients in the build. If `process_on` was set during preview and is in the future, the notification is queued for scheduled delivery. **Request body** | Field | Type | Required | Description | |-------|------|----------|-------------| | `build_id` | string (UUID) | Yes | The build ID from the preview response | ```bash curl -X POST \ "https://your-domain.com/api/notification/v1/orgs/acme-learning/notification-builder/send/" \ -H "Authorization: Token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"build_id": "9c1a2b3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d"}' ``` **Response** `200 OK` ```json { "status": "success", "notifications_sent": 48, "build_id": "9c1a2b3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", "message": "Notifications sent" } ``` **Possible `message` values:** | Message | Meaning | |---------|---------| | `"Notifications sent"` | Delivered immediately | | `"Notifications queued"` | Scheduled for future delivery (`process_on` is in the future) | | `"Similar notifications found"` | A matching notification was already sent in the last 24 hours | #### Duplicate Detection The system generates a SHA-256 hash from the combination of recipients, template, and channels. If an identical notification was sent in the last 24 hours, the preview response includes a `warning` and the send response returns `"Similar notifications found"` instead of sending again. #### Build Status Lifecycle ```mermaid stateDiagram-v2 [*] --> draft : preview created draft --> previewed : recipients resolved previewed --> sending : send requested previewed --> queued : scheduled for later queued --> sending : process_on reached sending --> completed : all delivered sending --> failed : error during delivery ``` --- ## 8. Template System ### 8.1 How Templates Work Every notification type ships with a default template. You do not have your own template record for a type until you customize it. Until that point, your platform inherits the default template — reads return it with `"is_inherited": true` in the response. The system creates your own copy the first time you edit a template via `PATCH`. The default template's content and channel settings are copied into a new record for your platform, then your edits are applied. Subsequent `PATCH` requests update your copy directly. **Toggle state** (enabled/disabled) is a separate record from the template content. Enabling or disabling a notification type does not affect the template, and resetting a template does not affect the toggle. **Reset** deletes your customized template. Your platform immediately reverts to inheriting the default template (`"is_inherited": true`). ```mermaid sequenceDiagram participant Admin participant API participant DB Admin->>API: PATCH /platforms/{platform_key}/templates/{type}/ API->>DB: Lookup template for (type, platform) alt No platform copy exists DB-->>API: DoesNotExist API->>DB: Fetch default template DB-->>API: default template API->>DB: Create platform copy API->>DB: Copy channels + SPAs else Platform copy exists DB-->>API: existing platform template end API->>DB: Apply PATCH fields DB-->>API: updated template API-->>Admin: 200 OK ``` --- ### 8.2 Template Variables Templates use Django template syntax. The rendering pipeline auto-loads the `notification_template_tags` library, so its tags are available without explicit `{% load %}`. #### Global Variables (available in every template) | Variable | Description | |---|---| | `site_name` | Platform name | | `site_url` | Platform URL | | `site_logo_url` | Platform logo URL | | `platform_name` | Platform name (capitalized) | | `support_email` | Platform support email | | `privacy_url` | Privacy policy URL | | `terms_url` | Terms of use URL | | `logo_url` | Platform logo URL | | `current_year` | Current year | | `base_domain` | Base domain of the platform | | `skills_url` | Skills platform URL | | `unsubscribe_url` | Campaign unsubscribe URL | #### Common User Variables | Variable | Description | |---|---| | `username` | Recipient's username | | `login_url` | Login or redirect URL | | `login_path` | Path portion of the login URL | #### Variables by Notification Type **Registration** | Type | Variable | Description | |---|---|---| | `APP_REGISTRATION` | `app_name` | Application name | | `APP_REGISTRATION` | `welcome_message` | Welcome message text | | `APP_REGISTRATION` | `benefits` | List of benefit strings | | `APP_REGISTRATION` | `closing_message` | Closing message text | | `USER_NOTIF_USER_REGISTRATION` | `welcome_message` | Welcome message | | `USER_NOTIF_USER_REGISTRATION` | `next_steps` | Next-step instructions | **Course Notifications** | Type | Variable | Description | |---|---|---| | `USER_NOTIF_COURSE_ENROLLMENT` | `course_name` | Course name | | `ADMIN_NOTIF_COURSE_ENROLLMENT` | `course_name` | Course name | | `ADMIN_NOTIF_COURSE_ENROLLMENT` | `student_name` | Enrolled student's name | | `ADMIN_NOTIF_COURSE_ENROLLMENT` | `student_email` | Enrolled student's email | | `USER_NOTIF_COURSE_COMPLETION` | `course_name` | Course name | | `USER_NOTIF_COURSE_COMPLETION` | `completion_date` | Date of completion | | `USER_NOTIF_COURSE_COMPLETION` | `certificate_url` | Certificate URL | | `USER_NOTIF_USER_INACTIVITY` | `days_inactive` | Days inactive | | `USER_NOTIF_USER_INACTIVITY` | `last_activity_date` | Last activity date | **Learner Progress** | Type | Variable | Description | |---|---|---| | `USER_NOTIF_LEARNER_PROGRESS` | `courses_taken` | List of courses | | `USER_NOTIF_LEARNER_PROGRESS` | `videos_watched_count` | Videos watched | | `USER_NOTIF_LEARNER_PROGRESS` | `total_time_spent` | Total time spent (hours) | | `USER_NOTIF_LEARNER_PROGRESS` | `credentials` | Credentials earned | **Credentials** | Type | Variable | Description | |---|---|---| | `USER_NOTIF_CREDENTIALS` | `item_name` | Course or program name | | `USER_NOTIF_CREDENTIALS` | `credential_url` | Full credential URL | | `USER_NOTIF_CREDENTIALS` | `credential_path` | Credential path | **Invitations** | Type | Variable | Description | |---|---|---| | `PLATFORM_INVITATION` | `redirect_to` | Redirect destination after signup | | `COURSE_INVITATION` | `course_name` | Course name | | `PROGRAM_INVITATION` | `program_name` | Program name | **Licenses** | Type | Variable | Description | |---|---|---| | `COURSE_LICENSE_ASSIGNMENT` | `course_name` | Course name | | `PROGRAM_LICENSE_ASSIGNMENT` | `program_name` | Program name | | `USER_LICENSE_ASSIGNMENT` | `welcome_message` | Welcome text | | `USER_LICENSE_ASSIGNMENT` | `benefits` | Benefit list | | `USER_LICENSE_ASSIGNMENT` | `closing_message` | Closing text | **Role and Policy** | Type | Variable | Description | |---|---|---| | `ROLE_CHANGE` | `role` | New role name | | `ROLE_CHANGE` | `demoted` | `True` if user was demoted | | `POLICY_ASSIGNMENT` | `role_name` | Role being assigned/removed | | `POLICY_ASSIGNMENT` | `assigned` | `True` if assigned, `False` if removed | | `POLICY_ASSIGNMENT` | `resources` | List of affected resources | **Human Support** | Type | Variable | Description | |---|---|---| | `HUMAN_SUPPORT_NOTIFICATION` | `ticket_subject` | Ticket subject | | `HUMAN_SUPPORT_NOTIFICATION` | `ticket_description` | Ticket description | | `HUMAN_SUPPORT_NOTIFICATION` | `ticket_status` | Ticket status | | `HUMAN_SUPPORT_NOTIFICATION` | `user_name` | User who created the ticket | | `HUMAN_SUPPORT_NOTIFICATION` | `user_email` | User's email | | `HUMAN_SUPPORT_NOTIFICATION` | `mentor_name` | Mentor name | | `HUMAN_SUPPORT_NOTIFICATION` | `platform_key` | Platform identifier | | `HUMAN_SUPPORT_NOTIFICATION` | `session_id` | Chat session UUID | | `HUMAN_SUPPORT_NOTIFICATION` | `chat_link` | URL to chat transcript | | `HUMAN_SUPPORT_NOTIFICATION` | `mentor_unique_id` | Unique identifier of the mentor | | `HUMAN_SUPPORT_NOTIFICATION` | `template_content` | Optional custom content (rendered if provided) | **AI / Proactive Learner** | Type | Variable | Description | |---|---|---| | `PROACTIVE_LEARNER_NOTIFICATION` | `student_name` | Learner's display name | | `PROACTIVE_LEARNER_NOTIFICATION` | `student_email` | Learner's email | | `PROACTIVE_LEARNER_NOTIFICATION` | `mentor_name` | Mentor name | | `PROACTIVE_LEARNER_NOTIFICATION` | `ai_recommendation` | AI-generated recommendation text | | `PROACTIVE_LEARNER_NOTIFICATION` | `username` | Learner's username | | `PROACTIVE_LEARNER_NOTIFICATION` | `platform_key` | Platform identifier | | `PROACTIVE_LEARNER_NOTIFICATION` | `mentor_unique_id` | Unique identifier of the mentor | **Report** | Type | Variable | Description | |---|---|---| | `REPORT_COMPLETED` | `report_name` | Report display name | | `REPORT_COMPLETED` | `report_status` | `completed`, `error`, or `cancelled` | | `REPORT_COMPLETED` | `download_url` | Download URL (completed reports only) | #### Template Syntax **Variable interpolation:** ```django Hello {{ username }}, you have been enrolled in {{ course_name }}. ``` **Conditionals:** ```django {% if demoted %} Your role has been removed. {% else %} You have been granted the {{ role }} role. {% endif %} ``` **Iterating lists:** ```django {% for benefit in benefits %} - {{ benefit }} {% endfor %} ``` **Rendering example:** Template source: ```django Dear {{ username }}, You have earned a credential for completing {{ item_name }}. View your credential here: {{ credential_url }} © {{ current_year }} {{ platform_name }} ``` Rendered output (with `username="jsmith"`, `item_name="Python Fundamentals"`, `credential_url="https://skills.example.com/credentials/abc123"`, `current_year=2026`, `platform_name="Acme Learning"`): ``` Dear jsmith, You have earned a credential for completing Python Fundamentals. View your credential here: https://skills.example.com/credentials/abc123 © 2026 Acme Learning ``` --- ### 8.3 Customizing Templates You can customize any editable template via `PATCH`. The first `PATCH` creates your own copy of the default; subsequent requests update that copy. **Editable fields:** `name`, `description`, `message_title`, `message_body`, `short_message_body`, `email_subject`, `email_from_address`, `email_html_template`, `spa_ids`, `channel_ids` For system-managed notification types (`PROACTIVE_LEARNER_NOTIFICATION`, `POLICY_ASSIGNMENT`, `HUMAN_SUPPORT_NOTIFICATION`), only configuration fields are editable — `message_body`, `short_message_body`, and `email_html_template` are read-only. #### HTML Sanitization The `email_html_template` field is sanitized using `bleach` before rendering. **Allowed tags:** `a`, `abbr`, `b`, `blockquote`, `br`, `code`, `div`, `em`, `h1`–`h6`, `hr`, `i`, `img`, `li`, `ol`, `p`, `pre`, `span`, `strong`, `sub`, `sup`, `table`, `tbody`, `td`, `th`, `thead`, `tr`, `u`, `ul`, `main`, `footer` **Allowed attributes:** - All elements: `style`, `class`, `id` - ``: `href`, `title`, `target` - ``: `src`, `alt`, `width`, `height` - ``, ``: `colspan`, `rowspan`, `align`, `valign` **Allowed URL protocols:** `http`, `https`, `mailto` Elements like `