# Nabrah documentation > Guides for the Nabrah platform (voice agents, Studio, channels) followed by the complete Nabrah API reference. # Introduction Source: https://www.nabrah.ai/en/docs/introduction ## How Nabrah works Nabrah AI provides AI voice infrastructure built for Arabic: text-to-speech, speech-to-text, voice cloning, and autonomous voice agents purpose-built for the Saudi dialect. You can use it in two ways, suited to different needs. **Nabrah Agents** is the platform for businesses that need to automate phone calls and customer conversations, with a guided setup for non-technical users and full API control for developers. **Nabrah Studio** is the toolkit for content creators and developers who need Arabic voice generation, transcription, and cloning accessible from the browser or via REST API. ## Concepts **Voices** are the speech personas used in audio generation. Each voice has a unique ID that you select in the dashboard or pass in API requests. Nabrah provides three ready-to-use public voices Daniah, Norah, and Abdullah. You can also clone a voice from an audio recording as short as 5 seconds. **Agents** are AI-powered assistants that handle phone calls, WhatsApp conversations, and web calls on your behalf. Each agent is configured with an identity, a language, a knowledge base, and a set of tools it can invoke during live calls. A single workspace can run multiple agents in parallel. **Credits** are the unit of consumption shared across all products. Credits reset monthly according to your plan. See [Plan and Pricing](https://www.nabrah.ai/en/pricing) for a full breakdown. ## Choose your path [Nabrah AgentsBuild, deploy, and monitor AI voice agents for inbound calls, outbound campaigns, and WhatsApp.](https://www.nabrah.ai/en/docs/nabrah-agents-overview) [Nabrah StudioGenerate natural Saudi Arabic audio, transcribe speech to text, and clone voices in the browser or via API.](https://www.nabrah.ai/en/docs/nabrah-studio-overview) ## Browse by capability [Voice AgentDeploy AI agents that handle calls and conversations 24/7](https://www.nabrah.ai/en/docs/agents-incl-built-in-tools-transfer-capture-tools) [Text to SpeechTranscribe spoken audio to text in real time or from uploaded files](https://www.nabrah.ai/en/docs/text-to-speech) [Speech to TextTranscribe spoken audio to text in real time or from uploaded files](https://www.nabrah.ai/en/docs/speech-to-text) [Voice CloningCreate a synthetic voice replica from a short recording](https://www.nabrah.ai/en/docs/voice-cloning) [WhatsAppRun AI-driven conversations over WhatsApp messaging](https://www.nabrah.ai/en/docs/whatsapp) [Call AnalyticsMonitor call performance, sentiment, and outcomes](https://www.nabrah.ai/en/docs/calls-analytics) [TicketingAuto-create support tickets from call interactions](https://www.nabrah.ai/en/docs/ticketing) [Inbound ServicesReceive and route incoming calls via SIP trunk](https://www.nabrah.ai/en/docs/inbound-services) [Outbound ServicesInitiate calls programmatically or on-demand](https://www.nabrah.ai/en/docs/outbound-services) [Web ChatAdd a chat window to your website, answered by your agent and team](https://www.nabrah.ai/en/docs/chat-widget) [Web CallsLet website visitors talk to your agent by voice, in their browser](https://www.nabrah.ai/en/docs/web-call-widget) --- # Core concepts Source: https://www.nabrah.ai/en/docs/core-concepts-workspaces-agents-calls-sip ## Workspace A **workspace** is your organization's environment within Nabrah. It contains your agents, voice library, call logs, analytics, team members, and billing. You can create multiple workspaces to separate different projects, clients, or environments. Each workspace operates independently credits are not shared across workspaces. ## Agent An **agent** is an AI voice assistant configured to handle a specific task. It answers calls, follows your defined workflow, accesses your knowledge base, and invokes tools when needed. Agents operate autonomously without requiring human monitoring during conversations. A single workspace can run multiple agents in parallel. ## Call A **call** is a single voice interaction between your agent and a caller. Every call is recorded, transcribed, and analyzed automatically. The results are stored in Call Logs and include the full transcript, audio recording, sentiment score, and agent accuracy score. ## SIP **SIP** (Session Initiation Protocol) is the standard used to connect phone calls over the internet. Nabrah uses SIP trunking to link your phone number to your agent. You configure SIP connections under **Channels → Inbound services** and **Channels → Outbound services**. ## API key An **API key** is the credential used to authenticate requests to the Nabrah API. Pass it in the `X-API-Key` request header on every request to `https://api.nabrah.ai/api`. Generate and manage your keys under **Developers → API keys**. > **Warning:** Never expose your API key in public or client-side code --- # Quick Start Source: https://www.nabrah.ai/en/docs/quick-start This guide demonstrates how to quickly build a custom voice agent tailored to your business needs. [Watch: Creating your first agent](https://www.youtube.com/watch?v=5anCfxeLmc0&list=PLdjDlf1_3v04&index=5) ## Creating your first Agent In this guide, we’ll create a support assistant capable of answering questions about your product, documentation, or service. ### 1. Creating a Workspace After signing up or logging in, you will be directed to the **[Workspaces](https://www.nabrah.ai/en/docs/workspace-overview)** dashboard. Because all voice agents must reside within a workspace, you must create one first to begin. ### 2. Creating an Agent Click Switch to **[Agents](https://www.nabrah.ai/en/docs/agents-incl-built-in-tools-transfer-capture-tools)**[ ](https://www.nabrah.ai/en/docs/agents-incl-built-in-tools-transfer-capture-tools)in the bottom-left corner of the dashboard Navigate to the Agents tab under the **Build** section Click the **New agent** button Select Start from Scratch. ### 3. Configuring the Agent ### 3.1 Identity & persona Define who the agent is, how it sounds, and the model behavior. #### 3.1.1 basics Display Name : Customer Support Agent Type : Basic Voice : Dania languages : Arabic Agent speaks first : Checked Allow interruptions : Checked #### 3.1.2 Persona & script first sentence : ```text مرحبا فيك معك <اسم الوكيل> ممثلة خدمة عملاء في <اسم الجهة> كيف اقدر أساعدك ``` Who you are : ```text أنتِ ممثلة خدمة عملاء احترافية تعملين لدى [اسم الجهة]. اسمك [اسم الوكيل]. مهمتك مساعدة العملاء بأسلوب ودود ومحترم، والإجابة على استفساراتهم بدقة وبسرعة. تتحدثين باللهجة الخليجية بشكل طبيعي، وتحرصين على أن يشعر كل عميل بأنه يُسمع ويُفهم. لا تتجاوزين صلاحياتك، ولا تقدمين معلومات لست متأكدة منها. إذا لم تستطيعي المساعدة في طلب معين، تُوجهين العميل بلطف إلى القناة المناسبة. ``` Goal ```text هدفك هو تقديم تجربة خدمة عملاء سلسة واحترافية. تسعين لفهم طلب العميل بدقة من أول تفاعل، وتقديم الحل أو المعلومة الصحيحة بأقل عدد ممكن من الأسئلة. تحرصين على: حل مشكلة العميل أو الإجابة على سؤاله بشكل كامل قبل إنهاء المحادثة التأكد من رضا العميل قبل إنهاء المكالمة تصعيد الحالات التي تتجاوز صلاحياتك للجهة المختصة بدون تأخير. ``` Steps / flow ```text الترحيب وتحديد الهوية ابدئي بالجملة الافتتاحية المحددة. إذا احتجتِ للتحقق من هوية العميل، اطلبي المعلومة المطلوبة بأسلوب طبيعي وغير رسمي. فهم الطلب استمعي جيداً لطلب العميل. إذا كان الطلب غير واضح، اسألي سؤالاً واحداً محدداً لتوضيحه — لا تسألي عدة أسئلة دفعة واحدة. المعالجة والمساعدة قدمي المساعدة المطلوبة بناءً على المعلومات المتاحة لديكِ. استخدمي الأدوات المتاحة عند الحاجة للاستعلام أو تنفيذ أي إجراء. التأكيد بعد تقديم الحل أو المعلومة، تأكدي أن العميل فهم وأن طلبه اكتمل. إذا بقي لديه أي استفسار آخر، أعطيه الفرصة قبل الإنهاء. الإنهاء أنهي المحادثة بأسلوب ودود ومهني، وتأكدي أن العميل لديه كل ما يحتاجه. التصعيد (عند الحاجة) إذا كان الطلب خارج نطاق صلاحياتك أو يتطلب تدخلاً بشرياً، أخبري العميل بوضوح وأحيليه للجهة المناسبة دون تأخير. ``` #### 3.1.3 Model & speech Model stack : latest speech speed : 1.0x ### 3.2 Audio and language Fine-tune audio cues and language behavior for phone-like conversations. #### 3.2.1 **Audio environment** Background ambience : 0.2x Thinking sound : 0.2x #### 3.2.2 **Language behavior** Language mode : single language #### 3.2.3 **Ticketing** **Enable ticket creation** : unchecked ### 3.3 **Tags** Optional structured labels for analytics. You can enable tagging later without blocking go-live. #### 3.3.1 **Conversation tags** Enable Tag extraction : checked Tagging instructions : ```text شكوى: إذا كان العميل يعبّر عن عدم رضا، أو يبلّغ عن مشكلة أو خطأ وقع معه استفسار: إذا كان العميل يطلب معلومة أو يسأل عن خدمة أو منتج في حال احتوت المحادثة على الاثنين، طبّقي الوسم الذي يمثل الغرض الرئيسي من المكالمة. ``` Add tags : شكوى - استفسار ### 3.4 **Tools & analysis** Link tools this agent may invoke during a call and Attach analysis pipelines for post-call insights. You can connect custom APIs to this agent, but we’ll skip that for this guide. For in-depth instructions, check the **[Custom Tools](https://www.nabrah.ai/en/docs/custom-tools)** section of the documentation. ### 3.5 **Knowledge** Paste reference material the agent should treat as ground truth during calls. #### 3.5.1 **Reference knowledge** Support / knowledge text** :** ```text تقدم [وصف للخدمات أو المنتجات]. ساعات العمل [الأوقات]. [أي معلومات أخرى يحتاجها الوكيل كسياسات، أسعار، فروع] ``` ### 3.6 **Webhooks** Edit template variables and each webhook URL plus optional header/body parameters. You can connect webhooks to fetch caller data before a call or sync results after it ends, We’ll skip this step for this guide, but check the **[Webhooks](https://www.nabrah.ai/en/docs/agents-incl-built-in-tools-transfer-capture-tools)**[ ](https://www.nabrah.ai/en/docs/agents-incl-built-in-tools-transfer-capture-tools)section of the documentation for more details. ### 3.7 **Review** A read-only summary of this agent. ### 4. Next steps Now that your agent is created, you can test it via dashboard web calls or link it directly to a phone line. By creating a channel, you can connect the agent to a phone number to handle inbound calls or initiate outbound calls. Check **[Inbound services](https://www.nabrah.ai/en/docs/inbound-services)** and **[Outbound services](https://www.nabrah.ai/en/docs/outbound-services)** in the documentation for full setup details. --- # Nabrah Agents overview Source: https://www.nabrah.ai/en/docs/nabrah-agents-overview ## Nabrah Agents Nabrah Agents turns a single AI voice agent into a full customer-conversation team. Build it once, then deploy it across phone calls, WhatsApp, and the web with every interaction recorded, analyzed, and ready to act on. ## Platform capabilities From setup to deployment to monitoring, Nabrah Agents provides everything you need to run AI voice agents at scale. ### Configure | **Goal** | **Where** | **Description** | | ----------------------------- | ------------------------------ | ------------------------------------------------------------------------ | | Create and configure an agent | **Build → Agents** | Define identity, persona, voice, language, and conversation workflow | | Add knowledge to your agent | **Build → Agents → Knowledge** | Paste reference material the agent treats as ground truth during calls | | Connect tools to your agent | **Build → Custom Tools** | Enable the agent to call external APIs and business systems during calls | | Extract data from calls | **Build → Analysis Groups** | Define structured data points to capture from every interaction | | Automate post-call tasks | **Build → Follow-ups** | Create messages or actions triggered automatically after a call ends | ### Deploy | **Goal** | **Where** | **Description** | | ---------------------- | -------------------------------------- | -------------------------------------------------------------------- | | Receive incoming calls | **Channels → Inbound Services** | Connect a phone number to your agent via SIP trunk | | Make outgoing calls | **Channels → Outbound Services** | Configure your agent to initiate calls programmatically or on-demand | | Deploy on WhatsApp | **Engagement → WhatsApp** | Run the same agent across WhatsApp messaging | | Embed on your website | **Studio → Plugins → Web-Call Widget** | Add a voice interface directly to any web page | | Integrate via API | **Developers → API Reference** | Trigger calls and manage agents programmatically | ### Monitor | **Goal** | **Where** | **Description** | | -------------------------- | ---------------------------------- | -------------------------------------------------------------- | | Track call performance | **Workspace → Calls Analytics** | Monitor volume, duration, success rate, and missed calls | | Track WhatsApp performance | **Workspace → WhatsApp Analytics** | Monitor WhatsApp conversation metrics | | Review individual calls | **Engagement → Call Logs** | Access transcripts, recordings, sentiment, and accuracy scores | | Manage customer requests | **Engagement → Ticketing** | Create and track support tickets generated from call outcomes | | View and search contacts | **Engagement → Contacts** | Browse the people your agents interact with | ## Architecture Nabrah Agents coordinates four core components on every call: 1. **Audio Pre-Processing** noise detection, noise reduction, and language identification before the audio reaches the AI layer. 2. **Speech to Text (STT)** converts caller speech into structured text, with dialect detection for Najdi, Hijazi, and Eastern Arabic. 3. **LLM & Agent Intelligence** processes the transcript through safety guardrails, applies your agent's identity and knowledge, and generates a response. 4. **Text to Speech (TTS)** converts the response into natural Arabic audio with dialect-aware synthesis and emotion control, then streams it to the caller. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz1booa000096d9ojd90e6b.png) ## Supported languages Nabrah Agents supports nine languages. Arabic is enabled by default on every agent. Arabic, English, Urdu, Malay, French, German, Spanish, Turkish, Indonesian. --- # Workspace overview Source: https://www.nabrah.ai/en/docs/workspace-overview The Workspace Overview is the first page you see when you open Nabrah Agents. It gives you an at-a-glance summary of your entire workspace how many agents you have running, your active voice channels, your team, and your call performance over the last 30 days. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz4ru4z0000boef8tfb6j9v.png) ## Workspace summary Four cards appear at the top of the page. Each one shows the current count for a key resource in your workspace. | **Card** | **What It Shows** | | ------------------ | --------------------------------------------------- | | **Voice agents** | Total number of agents configured in this workspace | | **Voice channels** | Total number of active voice channels connected | | **Team members** | Total number of users with access to this workspace | | **Integrations** | Total number of active third-party integrations | ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz4uq0v0001boefcl00pib9.png) ## Call performance The Call Performance section shows aggregated statistics from all agents in your workspace over the last 30 days. It updates automatically as new calls come in. | **Metric** | **What It Measures** | | ---------------- | ------------------------------------------------------------- | | **Total calls** | The total number of calls handled across all agents | | **Missed calls** | Calls that were not answered or failed to connect | | **Success rate** | The percentage of calls that reached a successful outcome | | **Avg duration** | The average length of a completed call in minutes and seconds | ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz4vue90002boef01w23dj2.png) ## Hourly distribution The Hourly Distribution chart shows your call volume pattern across a 24-hour period in Riyadh time. Use this chart to understand when your agents are busiest and plan your capacity accordingly. **Note:** The chart is scaled from your resource totals and shows the shape of your call pattern. It is not a live, minute-by-minute feed. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz53mm70005boefljegg240.png) ## Quota headroom The Quota Headroom section shows how much of your monthly credit balance has been consumed versus how much remains. Monitor this regularly to avoid service interruption. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz50k4a0004boef1zf4beyn.png) ## Agent usage The Agent Usage chart lists every agent in your workspace by its call volume over the last 30 days, from highest to lowest. A horizontal bar and exact call count appear next to each agent name. Use this chart to identify your most active agents and spot any agents that are receiving little to no traffic. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz5a6fo0006boefyxmyta06.png) ## Distribution The Distribution chart shows the share of configured resources across your workspace as a donut chart. The total number of items appears in the center. | Resource | What It Counts | | ------------ | ---------------------------------------- | | **Agents** | Total configured agents in the workspace | | **Inbound** | Total inbound services connected | | **Outbound** | Total outbound services configured | | **Team** | Total team members with workspace access | | **Tools** | Total custom tools created | | **Analysis** | Total analysis groups defined | Use this chart to get a quick overview of how your workspace resources are structured. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz5c3o10007boefze69ejok.png) --- # Calls analytics Source: https://www.nabrah.ai/en/docs/calls-analytics Calls Analytics gives you a detailed view of your agents' call performance over time. Use it to track volume trends, measure outcomes, and review AI-generated insights across all agents or a specific one. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz62ivz0000asdwmdi1sagz.png) ## Filters Two filters at the top of the page control what data is displayed. **Agent filter :** select a specific agent or view data across all agents in your workspace. **Time period filter :** select the date range for the data, such as the last 7 days or last 30 days. All metrics and charts update immediately when you change a filter. ## Metrics Four metrics appear at the top of the page. They reflect the totals for the selected agent and time period. | **Metric** | **What It Measures** | | ------------------- | --------------------------------------------------------------- | | **Total Calls** | The total number of calls handled in the selected period | | **Avg Duration** | The average call length across all calls in the selected period | | **Completed** | The number of calls that reached a successful end | | **Failed / Missed** | The number of calls that were not answered or ended in an error | ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz64t300001asdweygrix5e.png) ## Charts ### Call performance trend A line chart showing how your total call volume changes day by day over the selected time period. Use this to spot peaks, drops, and activity patterns across your agents. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz681ou0002asdwg4xnu95x.png) ### Call status distribution A donut chart showing the breakdown of all call outcomes for the selected period. | **Status** | **What It Means** | | ------------- | ---------------------------------------------------------- | | **Completed** | Calls that reached a successful end | | **No Answer** | Calls where the agent received no response from the caller | | **Failed** | Calls that ended due to a technical or connection error | ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz69wlv0003asdwvtm1xzgq.png) ### Hourly call distribution A 24-hour area chart showing when your calls are concentrated throughout the day. Four call types are plotted separately: * **Total calls :** the combined volume across all types * **Inbound :** calls received through your inbound services * **Outbound :** calls initiated through your outbound services * **Web calls :** calls initiated through the web-call widget The chart also highlights three time periods automatically: * **Peak hours** the busiest hour of the day with the highest call volume * **Evening peak** the second concentration of activity * **Off-peak hours** the quietest period with minimal call activity Use this chart to understand when your agents are busiest and plan capacity accordingly. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqz6c2zo0004asdwxr76fc6k.png) ## Agent analysis insights The Agent Analysis Insights section appears below the charts when a specific agent is selected. It provides structured AI-generated signals from every call that matches the selected agent and date range. Two tabs are available: ### System The System tab shows Nabrah's default analysis metrics, applied automatically to every call. **Call Agent response quality** : an average score from 0 to 10 measuring how well the agent adhered to its defined prompt and operational guidelines across all calls in the selected period. **Call follow-up required** : the percentage of calls where the AI determined that further manual intervention was needed. Displays both the true rate (follow-up required) and false rate (resolved during the call). **Call sentiment** : a donut chart breaking down the emotional tone detected across all calls. | **Sentiment** | **What It Indicates** | | ------------------- | -------------------------------------------------- | | **Highly Positive** | Caller expressed strong enthusiasm or appreciation | | **Positive** | Caller expressed satisfaction or agreement | | **Neutral** | Caller showed no strong emotional signal | | **Negative** | Caller expressed dissatisfaction or frustration | | **Highly Negative** | Caller expressed strong frustration or hostility | ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqzpdnb90000c7ddhxyv12vd.png) ### User The User tab shows your custom analysis group data. If you have linked an Analysis Group to the selected agent, the structured data points extracted from calls appear here as charts and distributions. The fields and categories shown depend entirely on how you configured your Analysis Group. For example, if your group tracks customer interest level, you see a breakdown of **Interested**, **Not Interested**, and **Very Interested** responses across all calls. > **Tip:** To start seeing data in the User tab, link an Analysis Group to this agent under **Build → Agents → Tools & Analysis**. See [Analysis Groups](https://www.nabrah.ai/docs/10) for setup instructions. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqzpf73n0001c7dddrhrxs3b.png) --- # WhatsApp analytics Source: https://www.nabrah.ai/en/docs/whatsapp-analytics WhatsApp Analytics gives you a dedicated view of your agents' performance across WhatsApp conversations. It tracks message volume, conversation status, response times, and employee activity separately from your voice call data. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqzpp65u0002c7dd4zigvrxs.png) ## Filters Three time period options appear at the top right of the page: * **Today** : data for the current day only * **Last 7 days :** data for the past seven days * **Last 30 days :** data for the past thirty days All metrics and charts update immediately when you change the selected period. ## Metrics Five metrics appear at the top of the page. | **Metric** | **What It Measures** | | ------------------------ | ------------------------------------------------------------------------------- | | **Total Conversations** | The total number of WhatsApp conversations in the selected period | | **Incoming Messages** | The total number of messages received from customers | | **Outgoing Messages** | The total number of messages sent by the agent | | **Avg. First Response** | The average time between a customer's first message and the agent's first reply | | **Avg. Resolution Time** | The average time from conversation start to resolution | ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqzprm160004c7ddnxgy743u.png) ## Charts ### Conversation Status A donut chart showing the status breakdown of all conversations in the selected period. Four statuses are tracked: **Open**, **Pending**, **Resolved**, and **Snoozed**. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqzpyzzb0005c7ddpip3fxs1.png) ### Messages Over Time A line chart plotting incoming and outgoing message volume day by day over the selected period. Two lines are shown: * **Incoming** messages received from customers * **Outgoing** messages sent by the agent Use this chart to understand messaging patterns and identify days with unusually high or low activity. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqzq361s0006c7dd5ohht4op.png) ## Per-employee analytics The Per-Employee Analytics section shows activity broken down by the team members assigned to WhatsApp conversations in the selected period. Use this to track individual response performance and workload distribution across your team. ![image.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/decc96f689fe4ae0746b7e542e63509f.webp) | Column | What It Shows | | ------------------------ | ------------------------------------------------------------------------------------- | | **Employee** | The team member's name and email | | **Conversations** | Total conversations assigned to this employee in the selected period | | **Resolved** | Number and percentage of assigned conversations marked resolved | | **Avg. First Response** | Average time between a customer's first message and this employee's first reply | | **Avg. Resolution Time** | Average time from conversation start to resolution, for this employee's conversations | | **Avg Reply** | Average time between messages during an ongoing conversation | --- # Agents Source: https://www.nabrah.ai/en/docs/agents-incl-built-in-tools-transfer-capture-tools An agent is an AI voice assistant configured to handle a specific business task. Creating an agent follows a structured seven-section setup that you can complete in order or revisit at any point before going live. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqzrfkom0000d5g66pblt3k3.png) ## Section 1: Identity & persona Defines who the agent is, how it sounds, and how it opens every conversation. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqzrhepd0001d5g6y3rgb9uo.png) ### Basics **Display name** the internal name used to identify this agent across the dashboard, analytics, and call logs. **Agent type** defines the operational mode of the agent. The default is **Basic**. **Voice** the voice profile the agent uses when speaking. Browse the voice library to select the profile that best matches your brand identity. See [Voices](https://markdownlivepreview.com/#). **Languages** select every language this agent should support. Arabic is enabled by default. How the agent determines which language to use at the start of each call is configured in Section 2. **Agent speaks first** when enabled, the agent delivers its opening greeting immediately upon connection. When disabled, the agent waits for the caller to speak first. > **Tip:** Disable **Agent speaks first** for warm transfer scenarios where a human agent introduces the AI. **Allow caller interruptions** when enabled, the agent stops speaking the moment it detects the caller's voice. When disabled, the agent completes its current statement regardless of caller input. > **Tip:** Disable **Allow caller interruptions** when the agent must deliver critical information that must be heard in full. ### Persona & script **First sentence** the exact opening statement delivered upon call connection. This segment is uninterruptible. > **Tip:** Keep the first sentence concise to maintain a natural conversational tone. If **Agent speaks first** is disabled, enter only one or two neutral words. **Who you are** defines the agent's name, professional tone, and the organization it represents. Include the agent's role and authority level to ensure consistent and appropriate responses throughout every interaction. **Goal** the agent's primary objectives and functional boundaries. Define the core tasks the agent must complete and explicitly list prohibited topics such as internal codes, pricing details, or proprietary data. **Steps / flow** the full sequence of the conversation. A complete flow defines three elements: * **Initiation** how the agent begins the interaction and determines the appropriate conversational path. * **Logic paths** the routes a conversation can take based on caller responses, including the data points to collect and the order of questions. * **Termination** the conditions and closing phrases that end the call gracefully once objectives are achieved. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmqzrp5680005d5g65cf0giuy.png) ### Model & speech **Model stack** the AI model powering the agent's responses. The default is **Latest**. **Speech speed** controls vocal cadence using a slider from **0.7x** to **1.3x**. The default is **1.0x**, which delivers the most natural listening experience. ## Section 2: Audio & language Fine-tunes the agent's audio environment and language handling. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0cac0700017vf6gcinqxr1.png) **Background ambience** enables a subtle background soundscape to make the agent sound more natural. Use the volume slider to balance the level. **Thinking sound** enables subtle audio cues during processing pauses, signaling to callers that the agent is still active. Use the volume slider to balance the level. > **Tip:** Keep the thinking sound slightly louder than the background ambience so callers clearly perceive the agent's activity. **Language mode** determines how the agent selects a language at the start of each call: * **Single language** the agent operates in one fixed language. * **Auto-detect** the agent detects the caller's language from their opening phrase and switches automatically. * **DTMF selection** the caller presses a keypad number to choose their language. This is the most reliable option for multilingual environments. **Enable ticket creation** when enabled, the agent automatically creates a support ticket after the call based on defined instructions. Requires the workspace ticketing system to be connected. See [Ticketing](https://www.nabrah.ai/docs/17). ## Section 3: Tags Optional structured labels assigned by the agent at the end of each call for analytics and filtering. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0ccjpr00027vf61oyuv8qd.png) **Enable tag extraction** when enabled, the agent reviews the call transcript and assigns the appropriate tag based on the instructions below. **Tagging instructions** describes the criteria for each tag. A few lines per tag are sufficient. **Add tags** type a tag name and press Enter. Tag names must not contain spaces use underscores, for example billing_issue. > \ > **Note:** Tags can be added or modified after the agent goes live without interrupting active operations. ## Section 4: Tools & analysis Connects the agent to external systems and post-call analysis pipelines. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0cfy4z00037vf6arcyxq7a.png) **Custom tools** link tools created in Build → Custom tools to enable the agent to call external APIs during conversations. See [Custom tools](https://www.nabrah.ai/docs/9). **Analysis groups** link analysis groups to activate structured post-call data extraction. See [Analysis groups](https://www.nabrah.ai/docs/10). ### Built-in Tools | **Tool** | **What It Does** | | --------------------- | ------------------------------------------------------------ | | **Call transfer** | Transfers the active call to a specified phone number | | **Date to weekday** | Converts a date string to the corresponding weekday name | | **Get phone number** | Extracts a phone number from the caller's spoken input | | **Get email address** | Extracts an email address from the caller's spoken input | | **Get national ID** | Extracts a national ID number from the caller's spoken input | ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0coagt00047vf68ok81wve.png) #### Transfer settings **When to use** describes the conditions under which the agent should initiate a transfer. **Transfer type** how the transfer is executed: * **Warm** Agent stays on the line during the handoff. (default) * **Cold** Agent drops off; caller is connected directly to the destination. * **Forward** the call is forwarded to the destination number **Transfer destination** the phone number in E.164 format (e.g. +9665XXXXXXXX). **SIP outbound number** the outbound SIP number used for the transfer leg when applicable. ## Section 5: Knowledge Provides the agent with reference material used to answer caller questions accurately. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0ctm5800057vf6c0m6ohtz.png) **Reference knowledge** a text field where all supporting information is entered. This content is injected into the agent's system prompt and treated as ground truth during every call. Include product details, policies, FAQs, and any information required for accurate responses. > **Tip:** Use headers and categorized lists to organize the knowledge. A well-structured knowledge base allows the agent to navigate complex information faster and reduces inaccurate responses. ## Section 6: Webhooks Sends automated HTTP requests to external systems at defined points before, during, and after a call. Use webhooks to personalize calls with live data, sync results to your CRM, or trigger downstream workflows. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0cxwkt00077vf6ebqwkr27.png) ### Template variables Named placeholders inserted into agent fields using the syntax {{variable_name}}, replaced with actual values at runtime. Each variable has: * *Name* the placeholder key, referenced as {{name}} in agent fields such as the first sentence, persona, or steps (e.g. customer_name). * **Type** the value's data type. * **Default value** the fallback used when no value is supplied for that call. **Example**: Define a variable customer_name. In the first sentence, write Hello, {{customer_name}}!. The agent greets each caller by name when the value is passed at call initiation. If no value is provided, the Default value is used instead. > **Tip***:* Values are supplied per call passed when the call is started via the API or an outbound campaign, or fetched by the **Pre-call webhook**. ### Webhook types | **Webhook** | **When it fires** | **Typical use** | | --------------------------- | ---------------------------------- | ------------------------------------------------------------------------------- | | ***Pre-call webhook*** | Before the call begins | Fetch caller data or load personalization variables for {{template variables}}. | | ***Post-call webhook*** | After the call ends | Update CRM records or trigger downstream workflows. | | ***Post-analysis webhook*** | After post-call analysis completes | Reporting, QA, or scoring workflows. | ### Configuring a webhook Each webhook is configured with: * **Callback URL** an HTTPS endpoint that receives the request. Leave blank to disable that webhook. * **Request parameters** optional fields added to the request for authentication or fixed payload values. Each parameter has: * **Location** whether it is sent as a *Header* (e.g. an Authorization token or API key) or in the **Body** (a fixed field). * **Name** the header or body key. * **Value** the static value sent with every request. ### Request payload The **Post-call** and **Post-analysis** webhooks send a POST request whose JSON body contains the full call record under call_details (caller/callee numbers, timestamps, transcript, tags, and analysis results), along with any **Body** parameters you configured. Configured *Header* parameters are attached as request headers. Requests time out after 30 seconds. > **Note***:* Use **Header** parameters for secrets such as API keys or bearer tokens, and **Body** parameters for fixed fields your endpoint expects on every call. ## Section 7: Review A read-only summary of the full agent configuration. All sections are listed and can be expanded to verify every setting before publishing. Once verified, click **Continue** to publish the agent. --- # Custom tools Source: https://www.nabrah.ai/en/docs/custom-tools Custom tools let an agent call external APIs during a live conversation to create a ticket, look up an order, update a record, or trigger any action exposed by an HTTPS endpoint. Once created, a tool can be linked to any agent under Agents → Tools & analysis. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0ernnn0001fkeywkq0geeh.png) ## Creating a tool Navigate to **Build → Tools** and click **Create tool**. ### Connection **Display name** : the name used to identify this tool when linking it to an agent, for example *Create ticket*. Choose your **HTTP** method that will be the request type sent to the endpoint: | **Method** | **Use** | | ---------- | -------------------------- | | **GET** | Retrieve data | | **POST** | Create a new record | | **PUT** | Replace an existing record | | **PATCH** | Partially update a record | | **DELETE** | Remove a record | **Endpoint URL** the full HTTPS address the tool calls, for example https://api.example.com/v1/action. **Description** explains what this tool does for the agent. The agent uses this description to decide when to call the tool during a conversation. Write it clearly a vague description leads to missed or incorrect tool calls. **Timeout (seconds)** the maximum time the tool waits for a response before failing. Accepts a value between **1 and 300**. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0f7kqh0007fkeyrxz1mtng.png) ## Parameters Click **Add parameter** to define each piece of data the tool sends with its request. Each parameter is one field in that request where it goes, where its value comes from, and its type. **Parameter name** the field's key in the request (e.g. phoneNumber). **Send as** where the parameter is placed in the request: * **URL parameter** appended to the query string * **Header** sent as an HTTP header * **Request body** included in the request payload * **URL path** inserted directly into the endpoint path **Value source** where the parameter's value comes from: * **Fixed value** sends the exact same value every time * **Platform variable** sends a value pulled from a template variable or call context * **AI decides** the agent determines the value during the conversation based on caller input **Type** the data type of the parameter: | **Type** | **Format** | | --------------------- | ---------- | | Text | string | | Number | number | | True/False | boolean | | List | array | | Group (nested fields) | object | **Default value (optional)** the fallback value used when no value is provided. **Notes (optional)** additional context about the parameter for future reference. **Required** when checked, the agent must provide a value for this parameter before the tool can be called. ### Groups (nested fields) Set **Type** to **Group (object)** to nest fields inside a parameter. Nested fields are flattened with , so a phone field inside a customer group is sent as: ```text { "customer": { "phone": "..." } } ``` ### \ Platform variables: Nabrah vs. Custom When **Value source** is set to **Platform variable**, the variable name typed in resolves to one of two kinds: * **Nabrah variables (built-in)** reserved names Nabrah fills in automatically, with no setup required. For example, nabrah_user_number resolves to the caller's phone number. Other nabrah_*** *variables are provided by the call runtime.* * ***Custom variables**** names defined by the agent builder. Values come per call from the agent's Template/Default variables, a campaign, or the* *variables* *object passed via the API. For example, passing* *{ "order*id": "A-1024" } makes a parameter set to order_id send A-1024. At call time, Nabrah looks up the name (built-in or custom) and sends its value; if empty, it sends the Default. Names are exact-match and case-sensitive. Watch out: * **Default is a literal, not another variable.** If Value = nabrah_user_number and Default is also set to the literal text nabrah_user_number, a call with no caller number sends the raw string "nabrah_user_number" instead of a real fallback. Leave Default blank, or set an actual fallback value. * **Variable names must match exactly.** Nabrah_User_Number won't resolve it falls through to the Default, since names are case-sensitive. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0f70pi0006fkey6oxkcfc2.png) ## Live request preview The Live Request Preview panel shows exactly what request the tool will send, based on the current configuration. **Resolved URL** the final endpoint URL with path and query parameters applied. **Query params** the parameters that will be sent in the URL query string. **Headers** the headers that will be sent with the request. **Body** the payload that will be sent in the request body. Click **Apply to parameters** to sync changes made in the preview back into the parameter fields, or **Reset from form** to rebuild the preview from the current form values. Click **Test request** to send a live call to the endpoint and verify the tool works as expected before linking it to an agent. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0f6dsb0005fkeyeoxtr92n.png) ## Saving the tool Click **Save changes** to create the tool. Once saved, the tool becomes available to link under **Agents → Tools & analysis → Custom tools**. --- # Analysis groups Source: https://www.nabrah.ai/en/docs/analysis-groups An analysis group defines the structured data points that Nabrah extracts automatically from every call. Once linked to an agent, the platform captures these data points after each interaction and surfaces them in [Calls Analytics](https://www.nabrah.ai/docs/6) and [Call Logs](https://www.nabrah.ai/docs/16). Use analysis groups when you need to collect consistent, structured information across calls for example, tracking whether a caller was interested in a product, what issue they reported, or how they rated the interaction. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0jz1jc0000bedwdw9yzohi.png) ## Creating an analysis group Navigate to **Build → Analysis groups** and click **Create group**. ### Group name The name must start with a letter and use only English letters, numbers, underscores (`_`), and hyphens (`-`). No spaces are allowed. ### Data collections Each analysis group contains one or more data collections one per data point to extract. **Type** determines what kind of value is captured: | **Type** | **Description** | | ----------- | --------------------------------------------- | | **string** | A text value, for example a reason or a quote | | **boolean** | A true or false value | | **integer** | A whole number | | **number** | A decimal number | | **enum** | One value from a defined list of options | **Description** guides the AI on exactly what to extract from the conversation. Be specific vague descriptions lead to inconsistent extraction across calls. **Minimum / Maximum** available for **integer** and **number** types only. Sets the acceptable value range. Not available for **boolean**. For **enum**, define the list of allowed values instead. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0k8ox30001bedwkgf90ji7.png) ### Linking to an agent An analysis group must be linked to an agent to activate extraction. Open the agent and go to **Tools & analysis** in agent configuration. A single agent can use multiple analysis groups, and the same group can be reused across agents. ### Viewing and exporting data Captured data appears in two places: * **Call logs → AI insights → User tab** per-call breakdown of extracted values * **Calls analytics → Agent analysis insights → User tab** charts and distributions across all calls in the selected period To export the full dataset, click **Export CSV** in Call Logs. The export includes Nabrah's built-in call metrics alongside your custom data points. --- # Follow-ups Source: https://www.nabrah.ai/en/docs/follow-ups Follow-ups send an automated Email or WhatsApp message after a call ends. They are configured per agent and triggered by the outcome of the call's analysis letting you reach customers automatically based on what happened during the conversation. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0lls7m0000dbfahci57sv8.png) ## How it works 1. A call ends, and the transcript is analyzed extracting the boolean fields, scores, and outcomes defined in the agent's analysis group. 2. This triggers a post-analysis callback to the follow-up endpoint. Saving a follow-up template auto-wires the agent's callback URL no separate webhook setup is needed. 3. The endpoint checks each template's trigger conditions against the analysis results. 4. If the conditions match, the message is sent with its placeholders filled in call details, agent variables, and analysis results. ## Email follow-up **Agent** : selecting an agent loads its available variables and analysis fields. **Recipient / CC** : the email address or addresses to send the follow-up to. **Subject** : the email subject line. Supports placeholders for call details. **Body** : the email content. Supports inserted placeholders for call details (Duration, Call From, Call To, Call Type, Status, Agent Name) and any variables defined for the selected agent. ## WhatsApp follow-up **Template name / language** must reference an approved WhatsApp template. **From phone ID / To number (or contact)** your WhatsApp Business API phone ID and the recipient's number or contact. **Agent variables** each ticked variable is sent with the template. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0lx1p90002dbfadoupslhk.png) ## Trigger conditions With no conditions set, the follow-up sends after every call. To restrict it to specific outcomes: * Tick a **boolean analysis field** - the follow-up fires only when that field is true. * Write an expression, for example analysis_[results.my](http://results.my)_group.score > 10. **Note:** If no analysis group is linked to the selected agent, no fields are available to use as triggers. Link one under **Build → Agents → Tools & analysis** first. ## Typical scenarios | **Scenario** | **Follow-up** | | ------------------------------- | --------------------------------------------------------- | | Lead qualified | Email or WhatsApp with a brochure or booking link | | Meeting booked | Confirmation message with the booking details | | Complaint or unresolved issue | Follow-up to the customer, alongside a human-handoff flag | | Order status or quote requested | Recap message sent after the call | --- # Inbound services Source: https://www.nabrah.ai/en/docs/inbound-services An inbound service routes incoming calls into your workspace and connects them to a linked agent. Creating one registers a SIP trunk that Nabrah accepts calls from, and then links one or more agents to handle those calls. ![image.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/b7879167b466bf6a2833b6833d14dd31.webp) ## Nabrah connection values To send calls to Nabrah, your provider or PBX must send SIP signaling to: ```text pbx.nabrah.ai:5060 ``` The default SIP port is **5060**. Use a different port only if explicitly agreed with Nabrah. The inbound SIP URI format is: ```text sip:@pbx.nabrah.ai:5060 ``` | Parameter | Value | | ---------------------- | --------------------------------------------- | | Nabrah SBC/PBX domain | `pbx.nabrah.ai` | | Default SIP port | 5060 | | Inbound SIP URI format | sip:@pbx.nabrah.ai:5060 | | Authentication options | IP only / username-password only / both | | Transport | UDP or TCP confirm with Nabrah before go-live | ## Information to gather before provisioning Before creating the service, collect this information from the customer's technical contact or SIP provider. Passwords should be exchanged over a secure channel never plain email. **General request information** | Item | Notes | | -------------------------------------- | ---------------------------------------------------------- | | Customer / company name | | | Technical contact name and email/phone | | | Environment | Production, Staging, or Test | | Preferred service display name | Example: `CustomerName-Inbound-Prod` | | Phone / SIP number(s) / DID list | Use country code format where possible, e.g. `96651234567` | | Required codecs | Example: PCMU, PCMA | | Planned go-live date and time zone | | Inbound-specific information | Item | Required for which auth type | Notes | | --------------------------------------------- | ---------------------------- | ------------------------------------------------------- | | Remote PBX/provider public source IP(s) | IP only / Both | Add every signaling IP used | | Remote SIP source port | IP only / Both | Default 5060 note if custom | | Remote signaling domain (if used) | Optional | Must resolve to the real source IP | | Authentication type requested | All | IP only, username/password only, or both | | Auth username the remote side will use | Username/password / Both | Case-sensitive, no spaces | | Auth password delivery method | Username/password / Both | Use a secure portal or secret manager never plain email | | Caller ID format sent by provider | All | E.164 without `+`, or national format | | Called number / DID format sent to Nabrah | All | Example: `sip:96651234567@pbx.nabrah.ai:5060` | | Routing target after Nabrah receives the call | All | Agent, AI agent, queue, extension, or other destination | | Failover behavior expected | Optional | Example: second agent group, voicemail, or reject | Do not create the trunk in production until this information is complete and the authentication type is clear. Missing source IPs, wrong ports, or wrong number formats are the most common causes of SIP 403, 404, and timeout errors. ## Creating an inbound service Open **Channels → Inbound services** and click **New inbound**. The **Create inbound service** dialog has two tabs: **Trunk** and **Agents**. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0m6nox0003dbfawfgc8ced.png) ### Trunk tab **Display name** a name that identifies the customer, direction, and environment, for example `CustomerName-Inbound-Prod`. **Phone / SIP number** the number or DID received in the SIP Request-URI or To header. Prefer normalized digits country code plus number, no spaces or symbols, for example `96651234567`. **Allowed IP addresses** required unless an auth username and password are set instead. Add the provider's public source IP. If the provider uses a non-default port, enter it as `IP:port`; otherwise port 5060 is assumed. **Auth username / Auth password** required unless IP authentication is used instead. These are the credentials the remote side authenticates with when sending calls to Nabrah. To use both authentication methods, fill in both the allowed IPs and the credentials. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0mh5r20004dbfad3bbm6yk.png) Click **Create & continue** to move to the **Agents** tab. ### Agents tab Link one or more agents to answer calls on this trunk now, or click **Skip for now** and attach them later from the trunk's side panel. ![image.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/86f33a43d7c2d9fea3e17c7c4bcea9e0.webp) ### Editing after creation Once created, a trunk's SIP credentials, IP rules, and routing can be reviewed or changed from its **side panel** opened either from a linked agent, or by clicking the service's title in the Inbound services list. ## Authentication type | Type | How it works | When to use | | ---------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------- | | **IP only** | Nabrah accepts calls only from the configured source IPs | Carrier interconnects with fixed public IPs and no Digest requirement | | **Username / password only** | The remote side authenticates using SIP Digest credentials | The source IP can change, or the provider requires authentication | | **Both** | Source IP must match and Digest credentials must pass | Recommended for production whenever the remote system supports it | Using both methods reduces fraud risk an attacker would need both the correct source IP and the correct credentials. ## Field format rules | Input example | Meaning | Stored host | Stored port | | ----------------------- | -------------------------------------------- | ------------------ | ----------- | | `203.0.113.10` | Host with no port — default SIP port is used | `203.0.113.10` | `5060` | | `203.0.113.10:5060` | Host with explicit default port | `203.0.113.10` | `5060` | | `sip.provider.com:5061` | Domain with custom port | `sip.provider.com` | `5061` | * No spaces before or after an IP, domain, or port. * Valid ports range from 1 to 65535. Nabrah's default is 5060. * Use a public IP for IP authentication unless a VPN or private link is in place. * If a domain is used instead of an IP, confirm how DNS updates are handled with the provider. * Prefer normalized digits for phone/SIP numbers country code plus number, no `+`, spaces, or dashes. * Outbound caller ID (when this trunk is used for return traffic) must be pre-approved by the provider. ## Troubleshooting These SIP message elements are the most useful when diagnosing a call that isn't routing or connecting correctly. | SIP item | Why it matters | Typical issue | | ---------------------- | ------------------------------------------------- | ---------------------------------------------------------- | | Request-URI | Identifies the called service/number | `404 Not Found` when the number format is wrong | | Via / received / rport | Shows the real source IP and NAT behavior | `403 Forbidden` when the source IP differs from Allowed IP | | Authorization | Appears after a 401/407 challenge for Digest auth | 401/407 loop when username, password, or realm is wrong | | Contact | Used for future requests; can expose private IPs | ACK/BYE routing failure if Contact is unreachable | | SDP `c=` and `m=` | Shows media IP and RTP ports | One-way audio or no audio | **Common issues** | Symptom / SIP code | Most likely cause | First check | | ------------------ | ------------------------------------------------- | ------------------------------------------------------------ | | 403 Forbidden | Wrong source IP, trunk disabled, or auth blocked | Compare the real source IP in Via/received with Allowed IP | | 401/407 loop | Digest username, password, or realm mismatch | Verify username, auth username, password, and provider realm | | 404 Not Found | Phone/SIP number doesn't match a route | Check the Request-URI and To header | | 408 Timeout | Wrong address/port, firewall, or remote side down | Check address, port, and firewall rules | | One-way audio | RTP/NAT path issue | Check SDP `c=` IP, RTP ports, and media path | ## Test plan 1. Verify the collected information: direction, IP/domain, port, number format, authentication type, and max channels. 2. Confirm firewall rules for SIP signaling and RTP media. 3. Run one inbound test call. 4. Confirm the source IP, Request-URI, From/To, response code, and Authorization header (if applicable). 5. Confirm two-way audio, and record the test call time, caller, called number, and trunk name. ## Security checklist * Prefer **Both** authentication for production trunks when supported. * Use unique SIP credentials per customer/trunk. * Never send SIP passwords in plain email use a secure channel. * Whitelist only the IPs actually required, and remove old IPs after migration. * Disable inactive trunks and review trunk usage regularly. --- # Outbound services Source: https://www.nabrah.ai/en/docs/outbound-services An outbound service registers the SIP trunk Nabrah uses to send calls to a client PBX or carrier. Once created, you can trigger outbound calls through the dashboard or the API, and the calls route through this trunk. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0ni7po0000d9cjag3inpxb.png) ## Creating an outbound service Navigate to **Channels → Outbound services** and click **Create**. **Display name** a label to identify this trunk in the dashboard. **Phone / SIP number** the number or SIP identifier associated with this trunk. **Address** the destination IP or domain supplied by the provider. Use `host:port` when the provider uses a non-default port; otherwise port 5060 is assumed. **Max channels** the concurrent call limit. Set this according to your provider contract or commercial limit to avoid exceeding capacity. **Auth username / Auth password** required when the provider uses username/password authentication. For IP authentication instead, confirm that the provider has whitelisted `pbx.nabrah.ai:5060` or Nabrah's current public IP. Click **Create outbound service**, then run a test call before going live ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0njqd30001d9cj7rk0q5fv.png) ## Information to gather before provisioning Before creating the service, collect this information from the client/provider. Passwords should be exchanged over a secure channel never plain email. **General request information** * Customer / company name * Technical contact name and email/phone * Environment Production, Staging, or Test * Preferred service display name example: CustomerName-Outbound-Prod * Phone / SIP number(s) / DID list country code format where possible, e.g. 96651234567 * Required codecs example: PCMU, PCMA * Planned go-live date and time zone **Outbound-specific information** * **Destination PBX/provider IP or domain** required for all auth types. Example: 12.55.230.2 or [sip.provider.com](https://sip.provider.com). * **Destination SIP port** required for all auth types. Default is 5060 unless the provider uses another port. * **Transport required by destination** UDP or TCP. * **Authentication type expected by provider** IP only, username/password only, or both. * **Auth username Nabrah must use** required for username/password or both. Provider-supplied SIP username. * **Auth password delivery method** required for username/password or both. Use a secure portal or secret manager never share in plain documents unless approved. * **Provider realm/domain (if required)** needed by some SIP Digest providers. * **Maximum concurrent channels** required for all auth types. Must match the commercial/provider limit. * **Allowed caller ID / CLI format** required for all auth types. Confirm which caller IDs the provider accepts. * **Destination number format** required for all auth types. Example: 00 + country code, E.164, or national format. * **Failover destination IP/domain** optional, only if the provider supports failover. * **Client/provider whitelist requirement** required for IP-only or both. Whitelist Nabrah's SBC/PBX: pbx.nabrah.ai:5060. Nabrah can confirm its current public IP if the provider requires an IP instead of DNS. Do not create the trunk in production until this information is complete and the authentication type is clear. Missing destination details, wrong ports, or wrong number formats are the most common causes of SIP 403, 404, and timeout errors. ## Authentication type * **IP only** the provider accepts calls only from Nabrah's whitelisted source IP/domain. Best for carrier interconnects with fixed public IPs and no Digest requirement. * **Username / password only** Nabrah authenticates to the provider using SIP Digest credentials. Best when the provider requires auth or Nabrah's source IP can change. * **Both** the provider whitelists Nabrah and still requires Digest credentials. Recommended for production whenever the provider supports it. Using both methods reduces fraud risk an attacker would need both the correct source path and the correct credentials. ## Field format rules * 12.55.230.2 host with no port; the default SIP port (5060) is used. * 12.55.230.2:5060 host with an explicit default port. * sip.provider.com:5061 domain with a custom port. * No spaces before or after an IP, domain, or port. * Valid ports range from 1 to 65535. Nabrah's default is 5060. * Use a public IP for IP authentication unless a VPN or private link is in place. * If a domain is used instead of an IP, confirm how DNS updates are handled with the provider. * Prefer normalized digits for phone/SIP numbers country code plus number, no +, spaces, or dashes. * Outbound caller ID must be pre-approved by the provider unapproved caller IDs may be rejected or blocked. ## Troubleshooting These SIP message elements are the most useful when diagnosing a call that isn't connecting correctly. * **Request-URI** identifies the called service/number. A 404 Not Found usually means the number format is wrong. * **Via / received / rport** shows the real source IP and NAT behavior. A 403 Forbidden usually means the source IP differs from what's whitelisted. * **Authorization** appears after a 401/407 challenge for Digest auth. A 401/407 loop usually means the username, password, or realm is wrong. * **Contact** used for future requests; can expose private IPs. An unreachable Contact can cause ACK/BYE routing failures. * **SDP** **c=**** and** **m=** shows media IP and RTP ports. Wrong values here cause one-way or no audio. **Common issues** * **403 Forbidden** wrong source IP, trunk disabled, or auth blocked. Compare the real source IP in Via/received with what's whitelisted. * **401/407 loop** Digest username, password, or realm mismatch. Verify username, auth username, password, and provider realm. * **404 Not Found** phone/SIP number doesn't match a route. Check the Request-URI and To header. * **408 Timeout** wrong address/port, firewall, or the provider is down. Check the destination address, port, and firewall rules. * **488 / 415** codec or SDP mismatch. Check the offered codecs and media policy. * **One-way audio** RTP/NAT path issue. Check SDP c= IP, RTP ports, and the media path. * **Max channels reached** the concurrent call limit is full. Check Max channels against active calls. ## Test plan 1. Verify the collected information: destination address, port, transport, number format, authentication type, and max channels. 2. Confirm firewall rules for SIP signaling and RTP media. 3. Run one outbound test call. 4. Confirm the response code and, if applicable, the Authorization header. 5. Confirm two-way audio, and record the test call time, caller, called number, and trunk name. ## Security checklist * Prefer **Both** authentication for production trunks when supported. * Use unique SIP credentials per customer/trunk. * Never send SIP passwords in plain email use a secure channel. * Whitelist only the IPs actually required, and remove old IPs after migration. * Keep Max channels aligned with the commercial and fraud-risk limit. * Disable inactive trunks and review trunk usage regularly. --- # Contacts Source: https://www.nabrah.ai/en/docs/contacts Contacts store the identity and channel details of the people your agents interact with. They are shared across calls, WhatsApp, and follow-ups so any interaction with a phone number is automatically linked to the matching contact. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0o2fbz0000b3d52vozfwc4.png) ## Creating a contact Click **Create Contact**, then choose **Manual** or **Bulk**. ### Manual **Name** *(optional)* a display name for the contact. **Email** *(optional)* the contact's email address. **Phone number** *(required)* a contact created without a name or email is saved under its number. ### Bulk Upload a CSV file with the headers `name`, `email`, `phone_number`. A template is available to download from the upload dialog. As with manual creation, `phone_number` is required for each row. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0o3ss10001b3d5w9zkdkzg.png) ## Exporting contacts Click **Export**, configure the scope and sort order, then download the file as CSV. **Export scope** all contacts, or a filtered subset based on the current view. **Only include contacts with email -** narrows the export to contacts that have an email address on file. **Only include contacts with phone** - narrows the export to contacts that have a phone number on file. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr0o4rfj0002b3d5vrgbi085.png) --- # WhatsApp Source: https://www.nabrah.ai/en/docs/whatsapp # WhatsApp The WhatsApp section centralizes every WhatsApp conversation your workspace handles — resolved by your AI agent or by your team. Conversations land in a shared inbox where members review, assign, and reply. This guide covers the full connection: preparing your Meta app, generating a permanent token, creating the inbox in Nabrah, connecting the webhook back in Meta, and sending your first test message. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr1tkc6r00009rdlw6l739y7.png) ## Before you start You need all of these. Missing any one will stop you partway through. | Requirement | How to check | | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | A Meta business portfolio | It appears at [business.facebook.com/settings](https://business.facebook.com/settings). If you have none, Meta offers to create one during app setup | | Admin access to that portfolio | You can open **Users → System users** | | A phone number **not** currently registered on the WhatsApp or WhatsApp Business app | Sending yourself a message on that number fails | | Owner or Admin role in your Nabrah workspace | **WhatsApp Settings** is visible in the sidebar | | A payment method on the Meta account | Needed only for business-initiated messages. Replies within a customer's 24-hour window are free, up to 1,000 service messages per number per month | | The Meta app published | An unpublished app receives only test webhooks. No real customer messages are delivered until you publish it | > **If your number is live on the WhatsApp or WhatsApp Business app:** registering it here disconnects it from that account and its chat history is lost. Use a different number — one that is not in use on WhatsApp. ## Part 1 Prepare your Meta app **1.1 Create or select an app.** Go to [Meta for Developers](https://developers.facebook.com/apps/). Select an existing app, or click **Create app**, enter an app name and contact email, then click **Next**. > **Do not put "WhatsApp" in the app name.** Meta rejects it. Use your company name instead. ![Screenshot 2026-09-30 124036.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/619584b9d471d3f35034f098905f1459.webp) ![Screenshot 2026-09-30 131701.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/a6165605465912896a470b292f03adab.webp) ![Screenshot 2026-04-20 144632.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/49b51681014a5c5f74c8921d7835c928.webp) **1.2 Choose the WhatsApp use case.** Select **Connect with customers through WhatsApp**, then **Next**. Add only this use case. Meta does not allow every combination on one app, and a second use case can force you to start over with a new app. ![Screenshot 2026-09-30 131806.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/83495fea5a1d2eb6a9357e9340c3df89.webp) **1.3 Choose a business portfolio.** Select the portfolio that owns your WhatsApp Business Account. An unverified portfolio is accepted now, but complete verification before you go live. ![Screenshot 2026-09-30 132253.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/4120ac8c8406ed1fa16c7f72e76508f9.webp) Meta then shows a **Requirements** step usually empty for a WhatsApp-only app and an **Overview** step summarising your choices. Review it and click **Create app**. You may be asked to re-enter your password. ![Screenshot 2026-09-30 132545.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/8078ebb9b3061a54c9a2a87bb42e89b8.webp) **1.4 Open the WhatsApp setup.** On the app **Dashboard**, under **App customization and requirements**, click **Customize the Connect with customers through WhatsApp use case**, then open **Step 2. Production setup**. ![Screenshot 2026-09-30 133040.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/3c00c5519c03f8a4f40a7c4e796ba7d9.webp) ![Screenshot 2026-09-30 133336.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/1105ca319c0ef64de7e8a51512ec1567.webp) ![Screenshot 2026-09-30 133627.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/b22d818b8e96dd5b8a852070cb6fcec5.webp) > Skip **Step 1. Try it out** it only sends a test message from Meta's own number. And ignore **Become a Tech Provider**; it is not needed here. > **If your screen looks different:** Meta changes this interface often. Older apps show **WhatsApp → API setup** in the left sidebar instead. Look for the WhatsApp setup page and the selected sender number. ![Screenshot 2026-09-30 134038.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/511127a402beb62adeab09bc80a0b54f.webp) **1.5 Add and verify your number.** In **Register your WhatsApp phone number**, click **Add phone number**. Meta walks you through three screens: 1. **WA Business Profile** display name, time zone, and business category. The display name must match your business name or Meta rejects it. 2. **Add number** the number and whether to verify by text message or phone call. For a landline, choose phone call. 3. **Verify number** enter the code Meta sends. ![Screenshot 2026-09-30 134734.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/c5174d5176b522c437d94713b57eb168.webp) ![Screenshot 2026-09-30 134809.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/38eaab32618b2820c5e0143fd9a96d5a.webp) > **If Meta rejects the number at Verify number**, it is already registered on WhatsApp. Use a different number see Troubleshooting. ![Screenshot 2026-09-30 135047.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/173eac95ce976e004031d83899f7e29e.webp) **1.6 Copy the two IDs.** Still in **Step 2**, open **Send message** and pick your number from the **From** selector. The **Phone number ID** and **WhatsApp Business Account ID** appear beside it. Paste both somewhere temporary you need them in Part 3. > The IDs only exist once a number is registered. If the **From** selector is empty, go back to 1.5. ## Part 2 Generate a permanent access token Nabrah needs a **System User token** that never expires. You create it in Meta Business Settings. There are three kinds of token and only one works. The token displayed on Meta's own setup page is temporary and dies after 24 hours. Your app's **Generate token** button may produce one with a default expiry. Follow the steps below for the third kind. **2.1 Open System users.** Go to [Meta Business Settings](https://business.facebook.com/settings), select your portfolio, then **Users → System users**. **2.2 Create the system user.** Click **Add**, name it `Nabrah WhatsApp`, set the role to **Admin**, then **Create system user**. **2.3 Assign assets.** Select the system user, click **Assign assets**, choose your Meta app and your WhatsApp Business Account, grant **Full control**, and save. > Assign both assets, not just the app. Assigning only the Meta app is the most common reason a token stops working weeks later. **2.4 Generate the token.** Click **Generate new token**, select the same Meta app, then set the expiry. > **Set the expiry to Never.** This one dropdown is the most common cause of a WhatsApp connection that works for weeks and then stops without warning. Any other value and the token dies on that date. Then tick exactly these two permissions: - `whatsapp_business_messaging` - `whatsapp_business_management` **2.5 Copy the token.** Click **Copy** and store it securely. Meta shows it once. > **Treat the token like a password.** Anyone holding it can send messages from your number. Do not paste it into chat, tickets, or screenshots. ## Part 3 Create the inbox in Nabrah Go to **WhatsApp**, open the number menu at the top, click **Add number**, then choose **Enter credentials manually**. ![Screenshot 2026-09-30 140338.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/178362ce8f253f67e0f0dd768ab94d9e.webp) ![Screenshot 2026-10-20 081544.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/198476b25a2c41813ecad1a3c4a22145.webp) **Step 1 — Inbox and number.** Enter an inbox name and the sending number in E.164 format, for example `+9665XXXXXXXX`. No spaces, no dashes, no leading zero. ![Screenshot 2026-09-30 140439.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/47f0c98bc9b69dea7b3309e04d4c65d9.webp) **Step 2 — Meta IDs.** Paste the **Phone number ID** and **Business account ID** from step 1.6. ![Screenshot 2026-09-30 140754.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/18a41f5388e25a5e5c1c4ae51482cb8c.webp) **Step 3 — Token and routing.** Paste the system user token from step 2.5 into **Meta / Facebook API key**. No spaces before or after. Turn on **Auto assignment** if you want incoming conversations distributed to team members automatically. Leave it off to triage from the **Unassigned** tab. ![Screenshot 2026-09-30 140846.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/1b113ab966749437e9e3d6b78f3cc268.webp) **Step 4 — Webhook.** Once Meta accepts your token, Nabrah advances to a fourth step that gives you the two values Meta needs. | Field | What to do | | ------------------------ | -------------------------------------------------------------------------------------------------------------------- | | **Callback URL** | Appears once WhatsApp provisions the inbox. If it is still blank, close and reopen **WhatsApp settings** to refresh. | | **Webhook verify token** | Generated for you. Click **Copy**. Change it only if you also rotate it in Meta. | | **App secret** | Optional. Leave it empty unless your security policy requires payload signing. | ![image (2).png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/c4a781ee4b5e0ac6ed65c93d1e26b0d3.webp) Now go back to Meta: **Step 2. Production setup → Configure Webhooks**, paste the Callback URL and the Verify token, click **Verify and save**, then subscribe to the `messages` field. ![Screenshot 2026-09-30 133919.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/78415899ee3506a20b8f0f54c5238132.webp) > **Step 4 only appears after a valid token.** If the wizard stops at step 3 with *Failed to create whatsapp inbox*, the token is expired, mistyped, or missing the `whatsapp_business_messaging` and `whatsapp_business_management` permissions. Fix the token there is nothing wrong with the webhook. ![Screenshot 2026-09-30 141714.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/0d3ca84cbafa3ff8ca36ac1bcd172947.webp) > **Your app must be published.** An unpublished Meta app receives only test webhooks from the app dashboard. No real customer messages are delivered until you publish it. ## Part 4 Connect an agent Open the **Chatbot** tab on the inbox you just created. | Field | What it does | | ------------------------------ | ------------------------------------------------------------- | | **Agent** | The Nabrah agent that answers incoming messages | | **Display name** *(optional)* | A label shown in the inbox so the team can tell the bot apart | | **Allow referring to a human** | Lets the agent escalate to a team member | | **Human contact emails** | Who receives the escalation notification | ![Screenshot 2026-09-30 140937.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/2ea1a2374e6b68bbc53f79d5b2a8edfd.webp) ## Test the connection 1. From a personal phone, send any message to your business number. 2. Within a few seconds the conversation appears in **WhatsApp → All**. 3. Reply from the inbox and confirm the message arrives on the phone. 4. If an agent is connected, send a question it should answer and confirm it replies on its own. Nothing arrives? Go to Troubleshooting below. ## Using the inbox **Conversation list.** The left panel shows contact name, status, priority, last-message preview, and time elapsed. Four tabs filter it: | Tab | Shows | | -------------- | ----------------------------------- | | **All** | Every conversation in the workspace | | **Mine** | Conversations assigned to you | | **Unassigned** | Conversations with no assignee | | **Unread** | Conversations with unread messages | Three further filters narrow the list: **Status** (Open, Pending, Resolved, Snoozed), **Priority** (Urgent, High, Medium, Low, No priority), and **Assignee**. ![Screenshot 2026-10-01 135304.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/206a3520b1a18519c5024517b72da755.webp) **Conversation actions.** From the actions panel, assign the conversation, change its status, and set its priority. Priority is always set by hand. Conversations the AI agent closes are marked resolved by **Nabrah System** automatically. ![Screenshot 2026-10-01 135558.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/6a6ce1ad99d39526cd03154eb254cbac.webp) ## Troubleshooting | Symptom | Cause | Fix | | --------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | Inbox saves, but no incoming messages | The Callback URL and Verify token from step 4 were never pasted into Meta | Complete Part 3, Step 4 | | Still nothing after the webhook is configured | The Meta app is unpublished, so only test webhooks are delivered | Publish the app | | The wizard stops at step 3, *Failed to create whatsapp inbox* | Token expired, mistyped, or missing permissions | Generate a new system user token with both WhatsApp permissions | | Worked for a day, then stopped | The temporary 24-hour token from Meta's setup page was used | Generate a system user token with expiry **Never** | | Worked for weeks, then stopped with no warning | The token was generated with a fixed expiry instead of **Never** | Generate a new token and set expiry to **Never** | | Worked for months, then "Invalid access token" | The system user was removed, or its asset assignments were revoked | Re-assign the app and the WhatsApp account, generate a new token | | "This phone number is already registered to a WhatsApp account" | The number is in use on WhatsApp or WhatsApp Business | Use a different number, or disconnect it from the existing account after backing up its chats | | Meta rejects the app name | The name contains "WhatsApp" | Use your company name instead | | Meta rejects the display name | It does not match your business name | Follow Meta's display name guidelines | | "Phone number not found" | Phone number ID and Business account ID were swapped | Re-copy both from Step 2 | | Number rejected when saving the inbox | Not in E.164 format | Use `+`, then country code, no spaces and no leading zero | | Can only reply with templates | The 24-hour customer service window has closed | Send an approved template to reopen it | | Business-initiated messages fail | No payment method on the Meta account | Add one in Billing Hub | ## Frequently asked questions **Can I connect more than one number?** Yes. Create one inbox per number. A Meta app accepts a single webhook endpoint, so all numbers under that app route through it. **Which media types are supported?** Images, documents, audio, and video, within Meta's size limits per type. **Why can I only send templates sometimes?** WhatsApp allows free-form replies for 24 hours after the customer's last message. Outside that window, only approved templates. **Does the token ever need refreshing?** No. A system user token with expiry set to **Never** keeps working until the system user or its asset assignments are removed. --- # Call logs Source: https://www.nabrah.ai/en/docs/call-logs Call Logs is a historical record of every call handled by your workspace agents. Each record includes the transcript, recording, AI analysis, and all session metadata giving you full visibility into every interaction. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr20njei00009oevzrc3khnj.png) ## Call list Each entry in the list shows the caller, destination, date and time, status, tags, recording state, and duration. Two view modes are available: **List** and **Grid**. Filter by **Agent**, **Status**, and **Call type**: | **Filter** | **Options** | | ------------- | ----------------------------------------------------------------- | | **Status** | Queued, In progress, Completed, No answer, Busy, Failed, Canceled | | **Call type** | Inbound, Outbound, Web | Search by number, call ID, agent name, or tag. ## Advanced filters Click **Advanced filters** to refine results further by: Call ID, Call from, Call to, Tags, Started from, Created at, and any analysis fields captured by linked analysis groups. ## Per-call record Selecting a call opens its full record. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr20s8ev00029oeviufc7ozw.png) ### Contact overview Shows the caller's number or name, total call count, and total WhatsApp chats associated with that contact. ### Call summary An AI-generated summary of the conversation what the caller wanted, how the agent responded, and the outcome. ### Call recording A playable audio recording of the full call with duration, volume control, playback speed, and a download option. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr21ca8800079oevusw2ggdd.png) ### AI call insights Four metrics displayed in the right panel: | **Metric** | **What It Shows** | | ------------------ | ------------------------------------------------------------------------------------ | | **Sentiment** | The caller's detected emotional state across the call | | **Call purpose** | The primary reason for the call as identified by the AI | | **Agent accuracy** | A percentage score of how closely the agent followed its defined prompt and workflow | | **Follow-up** | Whether the interaction requires further action (true or false) | ### Call information Session metadata: Call ID, Call Date, Start Time, Call Duration, Call Status, Agent Name, Call From, and Call To. ### Tags Tags assigned by the agent to this call. You can also add tags manually enter one or more tags as comma-separated values and choose: * **Append tags** : adds the new tags alongside existing ones * **Replace tags** : overwrites all existing tags with the new values Tags are configured under [Agents → Tags](https://markdownlivepreview.com/#). ## Per-call tabs ### Conversation transcript The full text transcript of the call, showing each message from the agent and the customer with timestamps. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr21bbfv00069oevcxc1q4eg.png) ### AI insights The structured data points extracted by the linked analysis group for this call. Two views are available: * **System** Nabrah's default analysis fields: call summary, call sentiment, agent response quality, improvement suggestions, follow-up required, and call objective. * **User** custom data points defined in the linked analysis group. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr21for400089oev7ru5voo4.png) ### Tool logs A record of every tool the agent invoked during the call, showing the tool name, execution time, and success or failure status. ### Variables The platform variables available during the call such as agent name and caller number with their values. ## Export Click **Export CSV** to download all call records. The export includes Nabrah's built-in call metrics and any custom data points captured by linked analysis groups. --- # Ticketing Source: https://www.nabrah.ai/en/docs/ticketing The Ticket Inbox bridges Nabrah's AI agents and your human support team. It centralizes customer requests captured during live interactions, creating a structured layer that ensures no task gets lost in handoff transforming conversational data into actionable, trackable tickets within the platform. ## Creating a ticket inbox Navigate to **Engagement → Ticketing** and click **Create inbox**. Each inbox acts as a dedicated queue for a specific team or use case. Every inbox requires a unique name used throughout the platform to distinguish between different support queues. **Ticket types** select one or more from Bug, Feature Request, General Inquiry, Billing, and Technical Support. You can also add custom types tailored to specific business processes. **Always create ticket** - when enabled, every eligible interaction creates a ticket automatically. This includes calls the agent successfully resolves, allowing teams to monitor agent performance or collect comprehensive data on request types even when manual intervention is not required. **Auto assign** automatically routes new tickets to users listed in the assign list, ensuring every ticket reaches the right person immediately. **One open ticket** prevents duplicate requests from cluttering the inbox. When enabled, the agent will not create a new ticket for a contact who already has an unresolved one. A new ticket is only generated once the previous one is marked as **Resolved**. **Assign list** the team members who receive auto-assigned tickets. Once created, link the inbox to an agent under **Build → Agents → Section 2: Audio & language → Enable ticket creation**. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr23teig0001d0dtlt6v1eu3.png) ## Ticket dashboard The ticket dashboard is the primary command center for viewing and addressing customer requests. Two view modes are available **Kanban** and **Cards**. Filter tickets by: | **Filter** | **Options** | | -------------- | ----------------------------------------------------------------------------------- | | **Status** | All, Open, Pending, Resolved, Snoozed | | **Assignment** | All, My tickets, Assigned, Unassigned | | **Priority** | Urgent, High, Medium, Low, None | | **Type** | Bug, Feature Request, General Inquiry, Billing, Technical Support, and custom types | | **Assignee** | Any team member | | **Contact** | Any contact in the workspace | Search by subject, contact name, or ticket number. You can also create tickets manually. A **Subject** and **Description** are required.  **Type** and **Customer phone** are optional. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr23vjqq0002d0dtel8r01k2.png) ## Per-ticket record Each ticket displays its **Status**, **Priority**, **Type**, and **Assignee**, populated automatically by the AI agent based on the context of the call and editable by the team at any time. > **Note:** Priority is the exception it is set and updated exclusively by team members to ensure human oversight over urgency. Three tabs are available inside each ticket: **Conversation** the AI-generated summary of the interaction that triggered the ticket, with a direct link to the original call record. **Activity** a full log of every change made to the ticket since it was opened. **Calls** the call history associated with the ticket's contact. The right panel shows the customer's phone, email, total tickets, and linked call history, with a shortcut to view the full contact profile. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr23zmur0004d0dtiefikxae.png) ## Internal discussion Each ticket includes an internal discussion area where team members can leave comments, share context, or coordinate next steps. These notes remain invisible to the customer, providing a secure space for teams to collaborate without affecting the customer-facing conversation. --- # Plugins overview Source: https://www.nabrah.ai/en/docs/plugins-overview **Plugins** connect an agent you built in Nabrah Agents to a new place customers can reach it — a website, a shareable link, and more over time. Each plugin uses the agent's existing setup: its prompt, voice, variables, and analysis. The agent is built once; the plugin decides where it can be reached. Plugins live under **Agents → Plugins**. Each plugin appears as a card you can open, configure and manage on its own. The list of available plugins grows over time. ![Screenshot 2026-10-20 083620.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/5a19bab68702a62d76c73194718210fa.webp) ## Available plugins | Plugin | What it does | | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | **[Web-call widget](https://www.nabrah.ai/en/docs/web-call-widget)** | Adds a call button to your website so visitors can talk to your agent by voice, in their browser | | **[Chat widget](https://www.nabrah.ai/en/docs/chat-widget)** | Adds a chat window to your website. Your agent answers first, and your team can take over from the inbox | ## Choosing between the two Both widgets can run on the same site, and each has a different starting point. | | Web-call widget | Chat widget | | ------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------- | | Visitor starts with | A voice call | A typed message | | Best for | Questions faster to say than to type bookings, support calls, sales | Visitors who prefer to read and type, or are somewhere they can't speak | | Handled by | The agent, for the whole call | The agent first, then your team from the inbox | | Can switch to voice | — | Yes, if you turn on voice in the widget | | Conversation record | Call logs | Inbox → Web chat | Pick **Web-call widget** when a spoken conversation is the point. Pick **Chat widget** when visitors should be able to type it can still hand off to a voice call mid-chat. ## Before you go live Whichever widget you use, set its allowed websites before you publish the embed code. If that list is empty, anyone who copies your snippet can run your widget and spend your usage on their own site. --- # Web-call widget Source: https://www.nabrah.ai/en/docs/web-call-widget The Web-call widget adds a click-to-call button to any website so visitors can talk to your agent live, right in their browser. No phone number, no downloads: visitors click the button, allow microphone access, and start a voice conversation instantly. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr3c0ok3000dfze1po0mgczv.png) ## Creating a widget Open **Plugins → Web-call widget** and create a new widget. Setup happens in two steps: **Widget** and **Call panel**. A preview beside each step shows how the button and panel will look. ### Step 1: Basics ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr3c1t4r000efze1zv9zoy7z.png) | Field | Description | | ---------- | -------------------------------------------------------------- | | **Name** | A label to recognize this widget later. Visitors don't see it. | | **Agent** | The agent that answers calls started from this widget | #### Appearance Sets how the call button itself looks on the page. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr3c3qv7000ffze1o4on0ckw.png) | Setting | Options | | ------------ | --------------------------------------------------------- | | **Icon** | Phone, chat, headset, mic, sparkle, or a custom image URL | | **Color** | Preset palette or a custom hex color | | **Position** | Any corner of the page | | **Size** | Small, medium, large | | **Theme** | Light, dark, or **Auto** | **Tip:** **Auto** matches the visitor's site or system theme automatically, the safer default for sites with both light and dark modes. #### Allowed origins Allowed origins restricts which websites can load the widget. Enter a comma-separated list of URLs. If left empty, the widget runs on any site it's embedded in. > **Set allowed origins before going live.** The embed snippet is public on your page. Anyone can copy it and run your widget on their own site, at your expense. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr3c5pai000hfze15m6tzko8.png) ## Customizing the call panel Step 2 designs the window that opens when a visitor starts a call. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr3cd8zn000jfze1lga0gndk.png) ### Templates Six ready-made styles cover most cases without further tuning: **Standard**, **Glossy**, **Wave**, **Bold**, **Soft**, and **Spotlight**. Selecting one updates the live preview immediately. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr3ceykn000kfze11ll2gf7e.png) ### Advanced For finer control, the **Advanced** tab exposes every element individually: | Group | Controls | | -------------------- | --------------------------------------------------------------------------- | | **Layout** | Classic, Centered, or Compact | | **Brand color** | Preset palette or custom hex | | **Avatar** | Icon or image, with circle or rounded shape | | **Text** | Title, subtitle (optional), and font style: System, Rounded, Serif, or Mono | | **Status indicator** | Dot, Orb, or Bars, or hidden entirely | | **Buttons** | Mute and end-call labels, Solid or Soft style, end-button color | | **Panel** | Button radius, panel width, corner radius, shadow, background | The preview reflects every change instantly. What you see is exactly what visitors see. ## Adding the widget to your site Once the widget is created, Nabrah generates an embed snippet. Copy it and paste it just before the closing `` tag on your page. ```text ``` The call button appears on your site automatically, with no further setup. > **Appearance changes need a new snippet.** Every setting from Appearance and the call panel designer is encoded into the snippet's data attributes. If you change how the widget looks, copy the updated snippet and re-embed it. (The [Chat widget](https://www.nabrah.ai/en/docs/chat-widget) works differently: its design is served from Nabrah, so its snippet never changes.) ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr3cipxd000lfze1yu8qmvsg.png) ## Direct link Direct link turns the widget into a public page anyone can open to talk to your agent, with no website or embed needed. Useful for sharing a call option before a site exists, or as a fallback link in emails, social bios, or WhatsApp messages. It's a feature of the Web-call widget only. The Chat widget doesn't have one. ### Enabling it Turn on **Enable direct link** in the Basics step, below **Allowed origins**. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr7n4iq10001acdvwa1kogtg.png) > The link isn't generated the moment you flip the toggle. Save the widget first. Until then a placeholder appears where the link will be. ### Where the link appears Once the widget is saved, the link sits on the widget's own page under **Direct link**, next to the embed snippet, with buttons to copy it or open it in a new tab. It looks like this: ```text https://app.nabrah.ai/call/wgt_xxxxxxxxxxxxxxxxxxxxxx ``` ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr7nh6ma0003acdvl0u90mlu.png) ### What visitors see The direct link opens the same call experience as the embedded widget: same layout, same branding, same behavior. There is no separate design step for it. Whatever the call panel designer is set to is what visitors get. ### Managing the link | Action | Supported | | ----------------------------------- | ----------------------------------------------------------------------------------------------------------- | | Copy the link | Yes, with the **Copy** button next to the URL | | Open the link | Yes, it opens the hosted call page in a new tab | | Customize the URL | No, the link uses a fixed, generated id | | Regenerate the link | No, the URL doesn't change once created | | Disable without deleting the widget | Yes, turning off **Enable direct link** removes the public page while the widget and its embed keep running | > Disabling the direct link only removes the shareable page. The embedded call button on your website is not affected. The two run independently once both exist. ## During a call When a visitor starts a call, they see live status, a mute button, and an end-call button. The conversation runs on the selected agent, so everything it knows (answers, variables, and follow-ups) works the same as on any other channel. Calls started from the widget appear in **Call logs**, and they use your workspace's call minutes. ## Troubleshooting | Symptom | Cause | Fix | | ---------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------- | | The button doesn't appear | The snippet is missing, or the site isn't in **Allowed origins** | Check the snippet sits before ``, and add the exact origin | | The button appears but the call never connects | The browser blocked microphone access | The site must be served over HTTPS, and the visitor must allow the mic prompt | | The widget looks different from the preview | An older snippet is still on the page | Copy the current snippet and re-embed it | | The agent doesn't answer | No agent is selected on the widget | Set one in **Basics** | | The direct link shows a placeholder instead of a URL | The widget hasn't been saved since the toggle was turned on | Save the widget, then reopen its page | --- # Chat widget Source: https://www.nabrah.ai/en/docs/chat-widget The Chat widget adds a chat window to your website. Your agent answers first, your team steps in when needed, and every conversation lands in your inbox. ![Screenshot 2026-10-20 084412.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/2eb9aaaffe88ad3afb80be53f2630165.webp) ## Creating a widget Open **Plugins → Chat widget** and click **New chat widget**. Setup runs through five steps, with a live preview beside them the whole way. ### Step 1: Setup | Field | Description | | ---------------------------------- | ------------------------------------------------------------------------------------------------------ | | **Name** | A label to recognize this widget later not shown to visitors | | **Agent** | The agent that answers chats on this widget | | **Display name** | An optional label shown in your inbox, so your team can tell this widget apart. Visitors don't see it | | **Auto-close conversations after** | How long a conversation stays open once the visitor goes quiet. Default: 2 hours | | **Bot replies per chat** | After this many agent replies, the chat goes to your team or closes if no one can take it. Default: 40 | ![Screenshot 2026-10-20 085318.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/e4167cd76ee7c8279e95c5ed06410062.webp) #### Tools on this widget The agent's tools are listed here, each with its own switch. Pick an agent first — the list is empty until you do. > **Website visitors aren't verified.** Tools that rely on a verified phone number get none on web chat, so turn on only what's safe for the public. #### Allowed websites Only the sites you list can load the widget. Separate them with commas, for example `https://example.com, https://www.example.com`. > **Set this before you go live.** The embed code is public on your page. If the list is empty, anyone can copy your snippet and run your widget and spend your usage on their own site. ### Handing off to your team Turn on **Allow referring to a human** and choose who takes over. | Mode | What happens | | -------------------- | --------------------------------------------------------- | | **Anyone available** | Any teammate connected to this inbox | | **Specific people** | Always hands over to the people you list | | **By department** | The agent reads the conversation and picks the right team | ![Screenshot 2026-10-20 085035.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/91a340c8e31273ffd6a5d6fda84ace56.webp) For **Specific people** and **By department**, each person has their own availability hours. A live line at the bottom of the panel shows the current time and whether anyone is reachable right now, so you can see at a glance whether a handoff would reach someone. ![Screenshot 2026-10-20 085047.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/3771b9731c44ca907e9d590e19940a6d.webp) > **If you use departments, keep the descriptions broad.** The agent must pick a department every time, and it sees only the text in **When should the AI choose this?** nothing else. If no description matches what the visitor is asking, nobody is contacted. ![Screenshot 2026-10-20 085108.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/cb920463fb4c10e917a4a9db11e4b58a.webp) ### Step 2: Design Start from a template, then adjust whatever you want. Your texts are kept when you switch templates. **Templates** — eight ready-made styles: Help Center, Messenger, Islands, Clay, Assistant, Pastel, Wave, and Noir. ![Screenshot 2026-10-20 085624.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/74f8515a1cb271b3100c30aa16edeec5.webp) **Position & size** — which corner the launcher sits in, and how big it is. **Brand & theme** — brand color, theme (Auto, Light, Dark), font, corner radius, shadow, and a frosted glass effect. **Language** can be fixed to English or Arabic, or left on **Automatic** to follow each visitor's browser. ![Screenshot 2026-10-20 085630.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/43567510189194ac6feecc953fb294ba.webp) **Launcher button** — shape, icon or image URL, spacing, and whether it shows on mobile. A **Teaser message** puts a short bubble beside the button after a delay you set, and the widget can open on its own after a number of seconds (`0` means never). ![Screenshot 2026-10-20 085638.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/488ff73367d19f4a163bf1ca86f69dab.webp) **Chat window** — width, height, and full screen on mobile. **Header** — style, title, subtitle, status text and icon, avatar, and whether to show the team member's name after a handoff. **Messages** — chat background, bubble shape, separate colors for visitor and reply bubbles and text, avatars, timestamps, and how the welcome message and notices are displayed. **Message box** — bar or card style, send button, and whether visitors can send voice messages. > Hiding **Powered by Nabrah.Ai** is available on Enterprise plans. Undo, redo and reset buttons sit at the top of the step. ### Step 3: Texts Six pieces of text visitors read, each with an English and an Arabic field side by side: - Welcome message - When no one is available - When a chat ends - Pre-chat intro - Message box placeholder - New chat button ![Screenshot 2026-10-20 090220.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/314e371e85b22a6ff39ff09867ca88ff.webp) Leave a field empty to use the default shown. Which language a visitor sees follows the **Language** setting in Design, or their browser when that is set to Automatic. > These texts are the widget's own wording. What your agent says comes from the agent itself, so if the widget is in Arabic but the agent replies in English, visitors see both. Set the agent's language to match. ### Step 4: Pre-chat Ask visitors for their details before their first message. Turn on **Show the pre-chat form**, then set **Name**, **Email** and **Phone** to Hidden, Optional or Required. **Consent text** adds a checkbox visitors must tick before they can start. ![Screenshot 2026-10-20 090454.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/e3e7207999c7072c06820d1e373a93d4.webp) > **Visitor details aren't verified.** A visitor can type any name, any email, any number. Treat these as a way to reach someone back, never as proof of who they are. > **Asking for an email or phone number means collecting personal data.** Fill in the consent text and point it at your privacy policy before you turn those fields on. ### Step 5: Voice **Let visitors call the agent** adds an AI orb to the chat. Visitors tap it to start a voice call without leaving the widget. - The same agent answers chats and calls - Calls work exactly like the [Web-call widget](https://www.nabrah.ai/en/docs/web-call-widget) - Calls use your call minutes **Show the call screen** adds a **Call** state to the preview so you can see what visitors get. ![Screenshot 2026-10-20 090756.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/67e72ff50588e2e33d07002dd479bf7b.webp) Click **Create widget** when you're done. ## The live preview The panel beside every step shows the widget exactly as visitors will see it, updating as you type. Switch between: - **Conversation**, **Pre-chat**, **No one available**, **Chat ended** — and **Call** when voice is on - English and Arabic - Light and dark - Desktop and mobile The offline and chat-ended states are the hardest to test on a live site, so check them here before you publish. ## Adding the widget to your site Once the widget is created, an **Install** tab appears. ```text ``` 1. Copy the code. 2. Paste it just before the closing `` tag on every page where the chat should appear. 3. Add your site to **Allowed websites**, then open your site to check it. ![Screenshot 2026-10-20 091344.png](https://nuonmwcmc4avrmft.public.blob.vercel-storage.com/cms/678163d23e0a425a12b3d5440f5813a9.webp) > **Design changes don't need a new snippet.** The appearance is served from Nabrah, so edits apply to your live site as soon as you save. (The [Web-call widget](https://www.nabrah.ai/en/docs/web-call-widget) works differently its settings are encoded in the snippet itself.) **Test on this page** loads the live widget inside Nabrah so you can send a real message. Add `https://app.nabrah.ai` to your allowed websites first. **Sign out all visitors** ends every visitor's session on this widget. Their next message starts a new conversation useful after a change you want everyone to see fresh. ## Handling conversations Chats appear in your inbox under **Web chat**, where your team reads and replies. A chat reaches them when the agent hands off, when it hits the **Bot replies per chat** limit, or when a visitor asks for a person. ## Troubleshooting | Symptom | Cause | Fix | | --------------------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------- | | The widget doesn't appear | The snippet is missing, or the site isn't in **Allowed websites** | Check the snippet sits before ``, and add the exact site | | **Test on this page** does nothing | `https://app.nabrah.ai` isn't in **Allowed websites** | Add it, then test again | | The agent doesn't reply | No agent is selected in **Setup** | Pick one | | Chats never reach the team | **Allow referring to a human** is off, or nobody is available | Turn it on, and check the availability line in the handoff panel | | A department handoff contacts nobody | No department description matched the conversation | Broaden the descriptions in **When should the AI choose this?** | | The widget is in Arabic but replies come in English | The agent's language doesn't match the widget's | Set the agent's language, or fix **Language** in Design | | A tool returns nothing on web chat | It relies on a verified phone number | Web chat has no caller number to check turn the tool off here | --- # Nabrah Studio overview Source: https://www.nabrah.ai/en/docs/nabrah-studio-overview Nabrah Studio gives creators and developers everything needed to work with voice generate speech, transcribe audio, and clone voices all built for Arabic and tuned for the Saudi dialect from the ground up. Turn a script into a professional voiceover in seconds. Capture spoken audio as accurate text. Clone any voice from a few seconds of audio and reuse it everywhere in Studio and in Nabrah Agents. Everything in Studio is organized around three areas. ### Voice Tools [Text to Speechturn written text into natural-sounding audio, ready for voiceovers, confirmations, or marketing content](https://www.nabrah.ai/en/docs/text-to-speech) [Speech to Texttranscribe spoken audio into text, from an uploaded file or live microphone input](https://www.nabrah.ai/en/docs/speech-to-text) [Voice Cloningcapture any voice in seconds and reuse it anywhere in Nabrah](https://www.nabrah.ai/en/docs/voice-cloning) ### Library [Voicesthe workspace voice library, with every public voice Nabrah offers and every private voice cloned within the workspace, ready to preview and use instantly](https://www.nabrah.ai/en/docs/voices) --- # Text to speech Source: https://www.nabrah.ai/en/docs/text-to-speech Nabrah's Text to Speech converts written Arabic text into natural-sounding audio using voice models purpose-built for the Saudi dialect. Use it to produce voiceovers, podcast intros, booking confirmations, marketing content, or any audio that needs a consistent, professional Arabic voice. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr26hie20004eeescgx1mncm.png) ## Guide Follow these four steps to generate audio. ### 1. Text input Type or paste your text into the input field. The limit is **4,000 characters** per generation. Sample prompts appear below the input field if you need a starting point. > **Tip:** Use proper Arabic punctuation and spacing. Well-punctuated text produces more natural pauses and phrasing in the output audio. ### 2. Voice selection Open the **Voices** tab on the right panel to browse available voices. Each voice can be previewed before selection. * **Public voices** provided by Nabrah, available to all workspaces. * **Private voices** cloned voices created within your workspace, visible only to you. ### 3. Adjust settings Open the **Settings** tab to fine-tune the output before generating. | **Setting** | **What it controls** | | ------------------ | ------------------------------------------------------------------------- | | **Model** | The voice model used for synthesis affects naturalness and expressiveness | | **Speed** | The speaking pace from slow to fast | | **Expressiveness** | The emotional range and energy level of the delivery | ### 4. Generate Click **Generate speech** to render the audio. Once generated, the audio player appears at the bottom of the page. Play the audio to review it, or click **Download** to save the file. --- # Speech to text Source: https://www.nabrah.ai/en/docs/speech-to-text Nabrah's Speech-to-Text transcribes spoken audio into accurate text. It officially supports both **Arabic (AR)** and **English (EN)** languages, with advanced optimization specifically tailored to recognize **diverse Saudi dialects** in all their variations. Two input modes are available: upload an audio file or transcribe live microphone input in real time. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr270lr20005eeesd4x8eotf.png) ## Guide ### 1. Input **Upload:** Submit a `WAV` or `MP3` file for transcription (supports files up to 60 seconds). **Live:** Activate the microphone to transcribe speech in real time. Partial results will appear dynamically as the audio is being processed. ### 2. View results The transcript appears in the main panel as the audio is processed. Use **Copy** to copy the full transcript to the clipboard, or **Download** to save it directly to your device. --- # Voice cloning Source: https://www.nabrah.ai/en/docs/voice-cloning Voice cloning creates a synthetic replica of any voice from a short audio sample and saves it as a private workspace voice. Once created, the cloned voice is available for use in Text to Speech and Nabrah Agents. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr278uu80006eees16s0d2jz.png) ## Guide ### 1. Provide audio Two input methods are available: **Record** record directly in the browser. Recording stops automatically at 20 seconds. A sample Arabic sentence is provided on screen to read aloud if needed. **Upload** drop a WAV file or click to browse. Maximum file size is 4 MB and maximum duration is 20 seconds. ### 2. Preview and trim Once audio is added, the waveform appears in the preview panel on the right. Trim a **4–7 second segment** from the recording to use as the clone source. Aim for a segment with clear, natural speech and no silence or background noise at the edges. ### 3. Name and clone Enter a voice name, then click **Clone voice**. The cloned voice is saved to the workspace and available immediately under [Voices](https://www.nabrah.ai/en/docs/Voices) and in the Text to Speech voice selector. ## Best results * Record or upload up to 20 seconds of clear speech. * Avoid long silences and background noise. * Match the pacing and volume intended for the final output the clone reflects the characteristics of the source recording. --- # Voices Source: https://www.nabrah.ai/en/docs/voices The Voices page is the workspace voice library. It contains all public voices provided by Nabrah and any private voices created through Voice Cloning. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr27o6tz0007eeesfm17bk0o.png) ## Voice types **Public** voices provided by Nabrah, available to all workspaces. The public voice library is updated periodically with new voices. **Private** voices cloned within your workspace. Only visible and accessible to members of that workspace. Use the tabs at the top right to filter the library by **All**, **Public**, or **Private**. Search by voice name to find a specific voice quickly. ## Actions Each voice card supports four actions: | **Action** | **What it does** | | -------------- | --------------------------------------------------------------------------------- | | **Try** | Preview the voice with a sample playback before using it | | **Use in TTS** | Open Text to Speech with this voice pre-selected | | **Copy ID** | Copy the voice ID to use in API requests | | **Delete** | Permanently remove the voice from the workspace (public voices cannot be deleted) | ## Cloning a new voice Click **Clone new voice** to create a new private voice from a short audio recording. See [Voice Cloning](https://www.nabrah.ai/en/docs/voice-cloning) for the full process. --- # Account Source: https://www.nabrah.ai/en/docs/account ## Creating an account Create a free account at [nabrah.ai](https://nabrah.ai). Two sign-up options are available: **Email** : register with an email address and password. A verification email will be sent click the link to activate the account before logging in. **Google** : sign up using a Google account. No email verification is required. The account remains linked to the Google email and cannot be changed to a different address later. After sign-up, the workspace dashboard opens automatically. ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr28mrdv000191dxku5dlvir.png) ## Managing your account Account management is accessible from the account settings page in the dashboard. **Plan** : view your current active plan, upgrade to a higher tier, or downgrade to a lower one. Changes take effect at the start of the next billing cycle. **Cancellation** : cancel the active subscription from the account settings. After cancellation, the account reverts to the free tier at the end of the current billing period. For plan details, credit rates, and usage history, see [Billing & Credits](https://markdownlivepreview.com/#). ![image.png](https://jg7fuogekuhtmhth.public.blob.vercel-storage.com/media/docs/import/cmr28o218000291dxatd4n2fq.png) --- # Workspace & teams Source: https://www.nabrah.ai/en/docs/workspace-teams-members-roles-ownership-transfer A workspace is the organizational environment within Nabrah. It contains agents, voices, call logs, analytics, team members, and billing. Every action and credit consumption is tracked per workspace. **My Workspaces** is the central page where all workspaces owned or shared with the account are listed. From here, switch between workspaces, manage settings, and control team access. ## Multiple workspaces One account can own or be a member of multiple workspaces. Each workspace operates independently with its own agents, voices, data, and credit balance. Credits are not shared across workspaces. ## Team members The workspace owner can invite team members and assign each one a role that controls what they can access and modify within the workspace. ### Roles | **Role** | **Access level** | | ---------- | ------------------------------------------------------------------------------------------ | | **Owner** | Full control over the workspace including billing, team management, and ownership transfer | | **Admin** | Near-full control can manage agents, members, and settings but cannot transfer ownership | | **Editor** | Can create and modify content within the workspace | | **Viewer** | Read-only access | | **Custom** | The owner defines exactly what the member can view and edit | Assign roles carefully. Owner and Admin grant broad control over the entire workspace. ## Ownership transfer The workspace owner can transfer ownership to any existing team member from the workspace settings in **My Workspaces**. After transfer, the previous owner's role is reassigned by the new owner. --- # TTS-V1 Source: https://www.nabrah.ai/en/docs/tts-v1 # Nabrah TTS V1 #### `Nabrah_tts_v1` ### **Stable** Nabrah TTS V1 is a text-to-speech model designed for real-time voice applications. It converts text into natural speech and supports both streaming and non-streaming generation. The model can be used with Nabrah's available voices as well as custom cloned voices. ## Model overview | Specification | Value | | ------------------------ | ------------------------------------------------------------------------------------ | | **Model ID** | `Nabrah_tts_v1` | | **Version** | V1 | | **Status** | Stable | | **Release date** | `2026-06` | | **Supported languages** | Arabic,English,French, German, Hindi, Indonesian, Italian, Turkish, Spanish, Russian | | **Supported dialects** | Saudi Arabic | | **Streaming** | Supported | | **Non-streaming** | Supported | | **Voice cloning** | Supported | | **Server-side TTFB** | 120 – 130 ms | | **Maximum input length** | 16,384 characters | | **Audio formats** | MP3, WAV, PCM | ## Recommended use cases Nabrah TTS V1 is suitable for applications that require generated speech, including: * Voice agents * Customer service applications * IVR systems * Conversational AI * Interactive voice experiences * Content narration * Real-time speech generation ## Streaming and non-streaming Nabrah TTS V1 supports both streaming and non-streaming speech generation. ### Streaming In streaming mode, audio is delivered progressively as it is generated. This allows applications to begin receiving audio before the full speech output has been synthesized. Streaming is recommended for latency-sensitive applications such as voice agents and real-time conversational systems. **Server-side TTFB:** 120 –130 ms > TTFB represents the server-side time until the first response bytes are returned. It does not represent full end-to-end application latency. ### Non-streaming In non-streaming mode, the complete audio file is returned after speech generation has finished. This mode is suitable when progressive playback is not required, such as generating audio files for later playback or storage. ## Audio output Nabrah TTS V1 supports three output formats. | Format | Specification | Recommended for | | ---------- | ----------------------------------------- | --------------------------------- | | **MP3** | 192 kbps compressed audio | Web playback and smaller payloads | | **WAV** | 48 kHz, mono, 16-bit PCM | High-quality lossless audio | | **PCM** | 48 kHz, mono, signed 16-bit little-endian | Real-time audio pipelines | ### MP3 MP3 provides compressed audio at **192 kbps**, making it suitable for applications where smaller payload size and broad playback compatibility are important. ### WAV WAV output uses uncompressed **16-bit PCM audio at 48 kHz mono**. It is suitable for applications that require lossless audio or a self-contained audio file with format information included in the file header. ### PCM PCM output contains raw signed **16-bit little-endian audio samples at 48 kHz mono**. It does not contain a file header and is intended for applications that consume raw audio frames directly. ## Voice cloning Nabrah TTS V1 supports custom cloned voices. A cloned voice can be used for speech generation after the cloning process has completed and the voice is marked as ready. The same voice IDs can be used when configuring Nabrah voice agents. [Learn about Voice Cloning](https://www.nabrah.ai/en/docs/voice-cloning) [View available Voices ](https://www.nabrah.ai/en/docs/voices) ## Input limits The model accepts up to: **16,384 characters per request** The limit is calculated after text normalization. Requests that exceed the supported input length must be divided into smaller segments before synthesis. ## Speech controls Some speech control parameters are currently exposed through the API but are not yet applied to generated audio. | Capability | Current support | | -------------------------- | --------------------- | | **Voice cloning** | Supported | | **Speed control** | Not currently applied | | **Expressiveness control** | Not currently applied | | **Pronunciation control** | Not currently applied | | **SSML** | Supported | > Support for additional speech controls may change as the model evolves. ## Latency | Metric | Result | | -------------------- | ---------- | | **Server-side TTFB** | 120–130 ms | Actual end-to-end latency may vary depending on network conditions, infrastructure, request configuration, and client-side audio handling. ## Using Nabrah TTS V1 Use the following model ID when generating speech: `Nabrah_tts_v1` Speech generation is available through: `POST /api/ext/tts/generations` Example: ````text ```json \ { \ "model": "Nabrah_tts_v1", \ "input": "Hello from Nabrah!", \ "voice": "YOUR_VOICE_ID", \ "response_format": "mp3", \ "stream": false \ } ``` ```` [Generate Speech ](https://www.nabrah.ai/en/docs/text-to-speech) [API Reference ](https://www.nabrah.ai/.well-known/agent-skills/SKILL.md) --- # STT-V1 Source: https://www.nabrah.ai/en/docs/stt-v1 ## Nabrah STT V1 **Stable** Nabrah STT V1 is a real-time speech-to-text model designed for streaming speech recognition. It supports Arabic, English, and code-switching between Arabic and English within the same conversation. Nabrah STT V1 is available in two variants: **Default**: optimized for higher transcription accuracy. **EOT**: optimized for conversational applications that require end-of-turn detection. ## Model overview | Specification | Value | | ------------------------- | --------------------------------- | | **Model family** | Nabrah STT V1 | | **Version** | V1 | | **Status** | Stable | | **Release date** | `2026-03` | | **Primary use case** | Real-time streaming transcription | | **Languages** | Arabic, English | | **Code-switching** | Arabic-English supported | | **Streaming** | Supported | | **Streaming latency** | As low as 160 ms | | **Streaming sample rate** | 16 kHz | | **Streaming encoding** | LINEAR_PCM | | **Channels** | Mono | | **Priority words** | Supported | ## Available variants ### Default variant The default Nabrah STT V1 model provides the highest transcription accuracy within the current Nabrah STT model family. It is recommended when accurate transcription is more important than built-in end-of-turn detection. The default model is selected automatically when no alternative `recognition_model` is specified. ### EOT variant `eot_nabrah` The EOT variant is designed for real-time conversational AI and voice agent applications. It emits a special `` token when it detects that the speaker has reached the end of their conversational turn. Compared with the default model, the EOT variant provides lower transcription accuracy but adds native end-of-turn signaling and applies `Inverse Text Normalization (ITN)`. ## Compare variants | Capability | Default | EOT | | --------------------------------- | ------------------------------- | ---------------------------------- | | **Transcription accuracy** | Higher | Lower | | **Real-time streaming** | Yes | Yes | | **Arabic** | Yes | Yes | | **English** | Yes | Yes | | **Arabic-English code-switching** | Yes | Yes | | **End-of-turn detection** | No | Yes | | **Text normalization** | Yes | Yes | | **Automatic punctuation** | No | Yes | | **Recommended for** | General real-time transcription | Voice agents and conversational AI | ## Recommended use cases ### Default model use cases Use the default model for applications such as: * Real-time transcription * Customer support calls * Live speech recognition * Voice applications where transcription accuracy is the priority * Arabic-English conversations * Applications that use external turn-detection logic ### EOT model use cases Use the EOT variant for applications such as: * Voice agents * Conversational AI * AI assistants * Real-time interactive voice systems * Applications that require native end-of-turn detection ## Languages and code-switching Nabrah STT V1 supports: * Arabic * English * Arabic-English code-switching Code-switching allows the model to recognize Arabic and English within the same sentence or conversation. Example: > على الإيميل Report أرسل لي ال ## Real-time streaming Nabrah STT V1 is primarily designed for real-time streaming speech recognition. Streaming is available over WebSocket: `WS /api/ext/stt/ws` Audio is streamed as raw PCM binary frames. ### Streaming audio requirements | Specification | Value | | --------------------- | ---------------- | | **Encoding** | LINEAR_PCM | | **Sample rate** | 16 kHz | | **Channels** | Mono | | **Transport** | WebSocket | | **Streaming latency** | As low as 160 ms | ## Performance ### Transcription accuracy The default Nabrah STT V1 model achieved: **WER: 31.84%** **Benchmark:** SADA \ **Environment:** Cloud-based streaming use case For more info about the SADA click [here](https://www.kaggle.com/datasets/sdaiancai/sada2022) **35.19%** for `eot_nabrah` model ### Streaming latency Streaming latency can be as low as: **160 ms** > Actual latency may vary depending on infrastructure, network conditions, audio input, application configuration, and the first token it self. ## End-of-turn detection The EOT variant can detect when the speaker has completed their conversational turn. Select it using: `eot_nabrah` When an end-of-turn is detected, the model emits: `` Example transcription: ````text ```text \ \ ابي احجز موعد بكرة ``` ```` Applications can use the `` token as a signal to trigger the next stage of the conversational pipeline, such as sending the transcript to an LLM. The token is returned inline with the transcript and should be removed by the client if it should not be displayed to the end user. ## End-of-utterance control For streaming transcription, the `end_of_utterance_silence_ms` setting controls how much trailing silence is required before the current utterance is treated as complete. Example: ````text ```json \ { \ "end_of_utterance_silence_ms": 500 \ } ``` ```` Lower values can make conversational responses faster, while higher values allow speakers to pause for longer without ending the utterance. This setting affects both preliminary and final transcription behavior. ## Priority words Nabrah STT V1 supports recognition biasing using priority words and phrases. This is useful for improving recognition of: - Brand names - Product names - Industry terminology - Names - Domain-specific vocabulary Example: ````text ```json \ { \ "priority_words": [ \ "نبرة", \ "ذكاء اصطناعي", \ "خدمة العملاء" \ ], \ "priority_words_strength": 0.5 \ } ``` ```` The `priority_words_strength` parameter controls how strongly the recognizer favors the supplied vocabulary. ## Text normalization The default model applies stronger Arabic orthographic normalization to some characters in the returned transcript. Examples include normalization of some Alef forms and converting `ة` to `ه`. The EOT variant applies lighter normalization and preserves the original text form more closely. For conversational applications where the transcript is passed directly to an LLM, this normalization generally preserves the intended meaning. ## Punctuation The default Nabrah STT V1 model does not currently generate punctuation automatically. | Variant | Automatic punctuation | | ----------- | --------------------- | | **Default** | Not supported | | **EOT** | Supported | ## Async file transcription Nabrah also supports asynchronous transcription for longer audio files. **Maximum audio duration:** 2 hours File transcription is available through: `POST /api/ext/stt/async-transcribe` The transcription runs in the background and moves through the following states: `queued → in_progress → completed` Completed results include: - Transcribed text - Audio duration - Timestamps For file-based transcription, **WAV is recommended**. ## Capabilities | Capability | Support | | --------------------------------- | ------------- | | **Real-time streaming** | Yes | | **Arabic** | Yes | | **English** | Yes | | **Arabic-English code-switching** | Yes | | **Priority words** | Yes | | **End-of-turn detection** | EOT variant | | **Automatic punctuation** | No on default | | **Async file transcription** | Yes | | **Timestamps** | Yes | | **Speaker diarization** | No | | **Word-level timestamps** | Yes | | **Language detection** | No | | **Noise robustness** | Yes | ## Using Nabrah STT V1 ### Default model The default model is selected when `recognition_model` is omitted or left empty. ````text ```json \ { \ "api_key": "YOUR_API_KEY", \ "recognition_model": "", \ "end_of_utterance_silence_ms": 500 \ } ``` ```` ### EOT variant To enable native end-of-turn detection: ````text ```json \ { \ "api_key": "YOUR_API_KEY", \ "recognition_model": "eot_nabrah", \ "end_of_utterance_silence_ms": 500 \ } ``` ```` [Generate Text ](https://www.nabrah.ai/en/docs/speech-to-text)\ [API Reference ](https://www.nabrah.ai/.well-known/agent-skills/SKILL.md) --- # Nabrah API reference The Nabrah API lets you run Arabic-first voice agents from your own code: create agents and the tools they call, place and analyze calls, generate and transcribe speech, and work your WhatsApp, Instagram and TikTok inbox. ## Base URL Every endpoint in this reference is served from one base URL over HTTPS. Requests and responses are JSON unless an endpoint says otherwise, such as file uploads and generated audio. ```text https://api.nabrah.ai/api ``` ## Authentication Send your workspace API key in the `X-API-Key` header on every request. A key belongs to one workspace, so it only reaches that workspace’s agents, calls and conversations. Create and rotate keys in the dashboard under API keys, keep them out of public code and repositories, and rotate a key straight away if it leaks. ```text X-API-Key: ``` ## Make your first request 1. **Create an API key.** In the Nabrah dashboard, open API keys and create a key for your workspace. It is shown once, so store it somewhere safe. 2. **Verify it.** Call the verify endpoint with the key. A `200` response confirms the key and shows which workspace it belongs to. 3. **Build on it.** Create an agent, connect a number, and place a call, or start with speech: generate audio or transcribe a file. ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/test' \ --header 'X-API-Key: ' ``` ```json { "status": "success", "message": "API key is valid", "key_details": { "id": "7c0d3f0e-2a52-4b8e-9d3c-5f0e7f4a1b2c", "user ID": "0b6f1c0a-3d9e-4c11-8f5e-2e7a9d1c4b6f", "project_id": "5e2a9c7d-1f4b-4e8a-a3c6-9d0b2f7e8c1a", "created_at": "2026-01-15T12:00:00.000Z" } } ``` ## Errors Errors use standard HTTP status codes with a JSON body. The `detail` field says what went wrong: a message for most errors, and a list of problems for validation errors. - `200–204`: The request worked. `204` responses have no body. - `401`: The `X-API-Key` header is missing, or the key is invalid or revoked. - `404`: The resource does not exist in the workspace your key belongs to. - `422`: The request failed validation. Each item in `detail` names the field (`loc`) and the problem (`msg`). - `5xx`: Something failed on our side. Retry with backoff; contact support if it continues. ```json { "detail": [ { "loc": [ "body", "agent_id" ], "msg": "Field required", "type": "missing" } ] } ``` ## Endpoints Endpoints are grouped by what they work on. Each page documents its parameters and responses, with samples in cURL, Python, JavaScript and Go, and a playground to send real requests. - [Authentication](https://www.nabrah.ai/en/docs/api-reference/authentication.md): Check that an API key works and see which workspace it belongs to. - [Agents](https://www.nabrah.ai/en/docs/api-reference/agents.md): Create and configure voice agents, and choose which analysis groups run after their calls. - [Tools](https://www.nabrah.ai/en/docs/api-reference/tools.md): Register the HTTP tools an agent can call during a conversation, and link them to agents. - [Analysis groups](https://www.nabrah.ai/en/docs/api-reference/analysis-groups.md): Define the data extracted from every call, and link analysis groups to agents. - [Knowledge bases](https://www.nabrah.ai/en/docs/api-reference/knowledge-bases.md): Create the knowledge bases an agent retrieves answers from during a call. - [Documents](https://www.nabrah.ai/en/docs/api-reference/documents.md): Add documents to a knowledge base and track their ingestion. - [Calls](https://www.nabrah.ai/en/docs/api-reference/calls.md): Place outbound calls, then search, export and analyze call records and recordings. - [Web calls](https://www.nabrah.ai/en/docs/api-reference/web-calls.md): Start a browser voice session with an agent and get the token your frontend joins with. - [SIP inbound](https://www.nabrah.ai/en/docs/api-reference/sip-inbound.md): Manage the inbound SIP trunks that bring calls to your numbers into Nabrah. - [SIP outbound](https://www.nabrah.ai/en/docs/api-reference/sip-outbound.md): Manage the outbound SIP trunks your agents place calls through. - [Agent inbound links](https://www.nabrah.ai/en/docs/api-reference/agent-inbound-links.md): Choose which agent answers the calls that arrive on each inbound trunk. - [Text to speech](https://www.nabrah.ai/en/docs/api-reference/text-to-speech.md): Generate natural Arabic and English speech, and manage the voices you generate with. - [Speech to text](https://www.nabrah.ai/en/docs/api-reference/speech-to-text.md): Transcribe recorded audio in the background, or stream audio for live transcripts. - [Contacts](https://www.nabrah.ai/en/docs/api-reference/contacts.md): Create, update and search the contacts your workspace talks to on every channel. - [Conversations](https://www.nabrah.ai/en/docs/api-reference/conversations.md): Read and reply to inbox conversations, assign them, and change their status and priority. - [Tickets](https://www.nabrah.ai/en/docs/api-reference/tickets.md): Create and work support tickets, and configure the inboxes they arrive in. - [WhatsApp](https://www.nabrah.ai/en/docs/api-reference/whatsapp.md): Connect WhatsApp numbers, work their conversations and put an agent on each number. - [Instagram](https://www.nabrah.ai/en/docs/api-reference/instagram.md): Work the conversations of connected Instagram accounts and put an agent on each account. - [TikTok](https://www.nabrah.ai/en/docs/api-reference/tiktok.md): Work the conversations of connected TikTok accounts and put an agent on each account. --- # Authentication API Check that an API key works and see which workspace it belongs to. - [Verify API key](https://www.nabrah.ai/en/docs/api-reference/authentication/verify-api-key.md) `GET /api/ext/test`: Validate an API key and confirm which project it belongs to. --- # Verify API key > GET https://api.nabrah.ai/api/ext/test Group: Authentication. Page: https://www.nabrah.ai/en/docs/api-reference/authentication/verify-api-key Validate an API key and confirm which project it belongs to. Send your key in the X-API-Key header; on success the endpoint echoes back the key id, the owning user id, the project (workspace) id the key is scoped to, and when it was created. Use it as a connectivity/auth smoke test before calling any other endpoint. ## Authentication — start here Every /api/ext endpoint authenticates the same way. This endpoint is the quickest way to confirm your setup works. - **Get a key**: In the dashboard open the workspace, go to the Developers tab, and generate a Web API key. The full key (prefixed nb_) is shown once at creation — copy and store it securely. Generate an Embed key instead only for browser/front-end use. Fields: `X-API-Key`. - **Send the key**: Pass it on every request as the header X-API-Key: nb_xxxxxxxxxxxxxxxxxxxx. There is no project_id parameter anywhere in this API — the key is bound to exactly one project/workspace, and that scope is derived from the key automatically. Fields: `X-API-Key`. - **Interpret the result**: 200 with status "success" means the key is valid; the returned project_id tells you which workspace you are acting on. 401 means the key is missing or invalid. Base URL: https://api.nabrah.ai/api — so the full URL for this call is https://api.nabrah.ai/api/ext/test ## Where to go next Common first calls once your key is verified: - **List voices**: GET /api/ext/tts/voices — the voice ids used for text-to-speech and for an agent’s voice. - **List agents**: GET /api/ext/agent — the agents available in this project and their ids. - **List tools**: GET /api/ext/tool — function-calling tools you can attach to agents. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/test' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/test" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/test", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/test", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Agents API Create and configure voice agents, and choose which analysis groups run after their calls. - [Create agent](https://www.nabrah.ai/en/docs/api-reference/agents/create-agent.md) `POST /api/ext/agent`: Creates a new agent in the project tied to your API key and returns the full agent configuration, including the generated agent_id. - [List agents](https://www.nabrah.ai/en/docs/api-reference/agents/list-agents.md) `GET /api/ext/agent`: Returns a brief listing of every agent in the workspace tied to your API key. - [Get agent](https://www.nabrah.ai/en/docs/api-reference/agents/get-agent.md) `GET /api/ext/agent/{agent_id}`: Returns the complete configuration for a single agent identified by agent_id, including persona, voice/language settings, behavioral toggles, dynamic variables, webhook callbacks, and the fully expanded lists of linked tools, system_tools, and analysis_groups. - [Update agent](https://www.nabrah.ai/en/docs/api-reference/agents/update-agent.md) `PUT /api/ext/agent/{agent_id}`: Updates the agent identified by agent_id and returns the full updated configuration. - [Delete agent](https://www.nabrah.ai/en/docs/api-reference/agents/delete-agent.md) `DELETE /api/ext/agent/{agent_id}`: Permanently deletes the agent identified by agent_id. - [Link analysis groups to an agent](https://www.nabrah.ai/en/docs/api-reference/agents/link-analysis-groups-to-an-agent.md) `POST /api/ext/agent/{agent_id}/analysis_groups`: Links one or more existing analysis groups to the agent identified by agent_id in a single call. - [List analysis groups for an agent](https://www.nabrah.ai/en/docs/api-reference/agents/list-analysis-groups-for-an-agent.md) `GET /api/ext/agent/{agent_id}/analysis_groups`: Lists all analysis groups currently linked to the agent identified by agent_id. --- # Create agent > POST https://api.nabrah.ai/api/ext/agent Group: Agents. Page: https://www.nabrah.ai/en/docs/api-reference/agents/create-agent Creates a new agent in the project tied to your API key and returns the full agent configuration, including the generated `agent_id`. An agent bundles the conversational persona (`who_are_you`, `goal`, `steps`), the speaking voice and languages, behavioral toggles (interruptions, ambient/thinking sounds), optional dynamic `variables`, webhook callbacks, and optional links to existing tools and analysis groups. Only `name` is conceptually essential; the remaining fields tune behavior and have sensible defaults. This is typically the first step in the workflow: create the agent, then create an analysis group, then link them. ## Key body fields Field meanings as they appear in the request body. - **Persona & conversation**: `name` is a human-readable label. `who_are_you` describes the agent's identity/persona, `goal` states what the call should accomplish, and `steps` outlines the procedure the agent should follow. `first_sentence` is the opening line and `start_first` controls whether the agent speaks first. Fields: `name`, `who_are_you`, `goal`, `steps`, `first_sentence`, `start_first`, `support_data`, `agent_type`. - **Voice & language**: `voice` is a TTS voice ID — obtain valid values from GET /api/ext/tts/voices (e.g. "dania"). `languages` is the list of supported language codes (e.g. "ar", "en"). `language_mode` is "single" or "multi"; when multilingual, `dtmf_language_message` can prompt the caller to choose. `speech_speed` is a multiplier (e.g. 0.7–1.5) and `allow_interruptions` lets the caller barge in. Fields: `voice`, `languages`, `language_mode`, `dtmf_language_message`, `speech_speed`, `allow_interruptions`. - **Audio ambience**: `background_ambient` plays subtle background audio and `background_ambient_volume` sets its level; `thinking_sound` plays a filler sound while the agent processes and `thinking_sound_volume` sets its level. Fields: `background_ambient`, `background_ambient_volume`, `thinking_sound`, `thinking_sound_volume`. - **Tools, analysis groups & stack**: `tool_ids` links existing tools (IDs from GET /api/ext/tool) and `analysis_group_ids` links existing analysis groups (IDs from GET /api/ext/analysis) at creation time. `stack` selects the model/runtime stack (e.g. "latest"). Fields: `tool_ids`, `analysis_group_ids`, `stack`. - **Dynamic variables**: `variables` are named placeholders the agent can reference at runtime; each has a `name`, a `type`, and a `default_value`. Fields: `variables`. - **Webhook callbacks**: `pre_call_callback`, `post_call_callback`, and `post_analysis_callback` each define an outbound HTTP webhook with a `url` and a list of `parameters`. Each parameter has a `name`, a `location` (e.g. "header", "query", "body"), a `data_type`, and a `value`. Fields: `pre_call_callback`, `post_call_callback`, `post_analysis_callback`. ## Response Returns the full AgentResponse with the new agent_id and resolved relations. - **Capture the agent_id**: Store the returned `agent_id` — you need it to fetch, update, delete, or link analysis groups to this agent. The response also expands linked `tools`, `system_tools`, and `analysis_groups` so you can confirm the wiring. A 422 response means the body failed validation; inspect `detail` for the offending field path. ## Callback delivery Callbacks are sent once and never retried. - **pre_call_callback**: Called before each call, and before each chatbot reply on WhatsApp. Only an HTTP 200 JSON object is used; on any other response, an error or a timeout (10 seconds for calls, 30 seconds for WhatsApp) the conversation continues without those values. Fields: `pre_call_callback`. - **post_call_callback** ({ "call_details": { "id", … } }): Sent once after a completed call, with a 30-second timeout. Calls that did not complete (for example no answer or busy) do not trigger it. Use `call_details.id` to deduplicate. Fields: `post_call_callback`. - **post_analysis_callback**: Same payload, sent once after the call's analysis finishes. Fields: `post_analysis_callback`. - **Authentication**: Callbacks are not signed. Add a secret header in `parameters` (location `header`) and check it on your side; `body` parameters are added to the JSON body. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Minimal Smallest practical payload: an Arabic-speaking agent with a persona and goal. ```json { "name": "Support Assistant", "start_first": true, "first_sentence": "مرحبًا، كيف يمكنني مساعدتك اليوم؟", "agent_type": "basic", "who_are_you": "You are Layla, a friendly customer support agent for Nabrah.", "goal": "Resolve the caller's billing question or hand off to a human if needed.", "steps": "1) Greet the caller. 2) Identify the issue. 3) Look up account details. 4) Offer a resolution.", "allow_interruptions": true, "voice": "dania", "languages": [ "ar" ], "stack": "latest" } ``` ### Example: Full All supported fields populated, including variables, callbacks, ambience, and links. ```json { "name": "Premium Support Assistant", "start_first": true, "first_sentence": "Hello, you've reached Nabrah support. How can I help?", "agent_type": "basic", "who_are_you": "You are Layla, a calm and knowledgeable support agent for Nabrah.", "goal": "Diagnose the caller's issue, resolve it, and confirm satisfaction before ending the call.", "steps": "1) Greet. 2) Verify identity using {{customer_name}}. 3) Diagnose. 4) Resolve or escalate. 5) Confirm and close.", "allow_interruptions": true, "voice": "dania", "support_data": "Refund window is 14 days. Business hours are 9am-6pm GST.", "languages": [ "ar", "en" ], "stack": "latest", "tool_ids": [ "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f" ], "analysis_group_ids": [ "018f3a2b-1111-7e1a-bf52-0a1b2c3d4e5f" ], "speech_speed": 1, "variables": [ { "name": "customer_name", "type": "string", "default_value": "valued customer" } ], "pre_call_callback": { "url": "https://example.com/hooks/pre-call", "parameters": [ { "name": "Authorization", "location": "header", "data_type": "string", "value": "Bearer token123" } ] }, "post_call_callback": { "url": "https://example.com/hooks/post-call", "parameters": [ { "name": "call_id", "location": "body", "data_type": "string", "value": "{{call_id}}" } ] }, "post_analysis_callback": { "url": "https://example.com/hooks/post-analysis", "parameters": [ { "name": "X-Source", "location": "header", "data_type": "string", "value": "nabrah" } ] }, "background_ambient": true, "background_ambient_volume": 0.2, "thinking_sound": true, "thinking_sound_volume": 0.3, "language_mode": "multi", "dtmf_language_message": "Press 1 for Arabic, press 2 for English." } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "name": "string", "start_first": false, "first_sentence": "string", "agent_type": "basic", "who_are_you": "string", "goal": "string", "steps": "string", "allow_interruptions": false, "voice": "dania", "support_data": "string", "languages": [ "ar" ], "stack": "latest", "speech_speed": 0.7, "variables": [ { "name": "string", "type": "string", "default_value": "string" } ], "pre_call_callback": { "url": "string", "parameters": [ { "name": "string", "location": "header", "data_type": "string", "value": "string" } ] }, "post_call_callback": { "url": "string", "parameters": [ { "name": "string", "location": "header", "data_type": "string", "value": "string" } ] }, "post_analysis_callback": { "url": "string", "parameters": [ { "name": "string", "location": "header", "data_type": "string", "value": "string" } ] }, "tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "system_tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "when_to_use": "string" } ], "analysis_groups": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "background_ambient": false, "background_ambient_volume": 0, "thinking_sound": false, "thinking_sound_volume": 0, "language_mode": "single" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/agent' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Support Assistant", "start_first": true, "first_sentence": "مرحبًا، كيف يمكنني مساعدتك اليوم؟", "agent_type": "basic", "who_are_you": "You are Layla, a friendly customer support agent for Nabrah.", "goal": "Resolve the caller'\''s billing question or hand off to a human if needed.", "steps": "1) Greet the caller. 2) Identify the issue. 3) Look up account details. 4) Offer a resolution.", "allow_interruptions": true, "voice": "dania", "languages": [ "ar" ], "stack": "latest" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent" headers = { "X-API-Key": "", } payload = { "name": "Support Assistant", "start_first": True, "first_sentence": "مرحبًا، كيف يمكنني مساعدتك اليوم؟", "agent_type": "basic", "who_are_you": "You are Layla, a friendly customer support agent for Nabrah.", "goal": "Resolve the caller's billing question or hand off to a human if needed.", "steps": "1) Greet the caller. 2) Identify the issue. 3) Look up account details. 4) Offer a resolution.", "allow_interruptions": True, "voice": "dania", "languages": [ "ar" ], "stack": "latest" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Support Assistant", "start_first": true, "first_sentence": "مرحبًا، كيف يمكنني مساعدتك اليوم؟", "agent_type": "basic", "who_are_you": "You are Layla, a friendly customer support agent for Nabrah.", "goal": "Resolve the caller's billing question or hand off to a human if needed.", "steps": "1) Greet the caller. 2) Identify the issue. 3) Look up account details. 4) Offer a resolution.", "allow_interruptions": true, "voice": "dania", "languages": [ "ar" ], "stack": "latest" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Support Assistant", "start_first": true, "first_sentence": "مرحبًا، كيف يمكنني مساعدتك اليوم؟", "agent_type": "basic", "who_are_you": "You are Layla, a friendly customer support agent for Nabrah.", "goal": "Resolve the caller's billing question or hand off to a human if needed.", "steps": "1) Greet the caller. 2) Identify the issue. 3) Look up account details. 4) Offer a resolution.", "allow_interruptions": true, "voice": "dania", "languages": [ "ar" ], "stack": "latest" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/agent", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List agents > GET https://api.nabrah.ai/api/ext/agent Group: Agents. Page: https://www.nabrah.ai/en/docs/api-reference/agents/list-agents Returns a brief listing of every agent in the workspace tied to your API key. Agents are the voice/chat AI configurations that define how a call behaves (persona, goal, voice, language, linked tools and analysis groups). Use this endpoint as the entry point for discovering agents and obtaining the `agent_id` values that the per-agent endpoints (get, update, delete, link analysis groups) require. The response is a flat array of compact summaries; call GET /api/ext/agent/{agent_id} to retrieve the full configuration of any single agent. ## Authentication & scope These endpoints are scoped to a single project by your API key. - **X-API-Key header is required**: Send your Web API key (prefixed `nb_`) in the `X-API-Key` header on every request. Keys are generated in the dashboard under the Developers tab and are bound to exactly one project/workspace, so no `project_id` is passed — the project is derived from the key. Validate a key with GET /api/ext/test. ## Response shape The body is a JSON array of AgentBrief objects (not a wrapped/paginated object). - **Fields per item**: Each item contains the agent identifier and lightweight metadata. There is no server-side pagination on this endpoint — all agents in the project are returned in one array. Fields: `agent_id`, `name`, `created_at`, `last_update`, `created_by_email`, `enable_ticket_creation`. Use the `agent_id` from this list anywhere an agent ID is required. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json [ { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "created_by_email": "string", "enable_ticket_creation": false } ] ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/agent' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/agent", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get agent > GET https://api.nabrah.ai/api/ext/agent/{agent_id} Group: Agents. Page: https://www.nabrah.ai/en/docs/api-reference/agents/get-agent Returns the complete configuration for a single agent identified by `agent_id`, including persona, voice/language settings, behavioral toggles, dynamic variables, webhook callbacks, and the fully expanded lists of linked `tools`, `system_tools`, and `analysis_groups`. Use this to inspect an agent before updating it or to confirm how it is wired to tools and analysis groups. ## Where agent_id comes from - **Discovering the ID**: List agents with GET /api/ext/agent and copy the `agent_id`, or use the ID returned by POST /api/ext/agent. ## Response shape Returns a full AgentResponse object. - **Expanded relations**: `tools` and `analysis_groups` are returned as `{ id, name }` summaries; `system_tools` include additional config and `when_to_use` metadata. `project_id` reflects the workspace the API key is scoped to. Fields: `agent_id`, `project_id`, `name`, `voice`, `languages`, `tools`, `system_tools`, `analysis_groups`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `agent_id` (string, required): UUID of the agent to retrieve. Get this from GET /api/ext/agent or from the create-agent response (POST /api/ext/agent). ## Response ### 200 Successful Response Content type: `application/json` ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "name": "string", "start_first": false, "first_sentence": "string", "agent_type": "basic", "who_are_you": "string", "goal": "string", "steps": "string", "allow_interruptions": false, "voice": "dania", "support_data": "string", "languages": [ "ar" ], "stack": "latest", "speech_speed": 0.7, "variables": [ { "name": "string", "type": "string", "default_value": "string" } ], "pre_call_callback": { "url": "string", "parameters": [ { "name": "string", "location": "header", "data_type": "string", "value": "string" } ] }, "post_call_callback": { "url": "string", "parameters": [ { "name": "string", "location": "header", "data_type": "string", "value": "string" } ] }, "post_analysis_callback": { "url": "string", "parameters": [ { "name": "string", "location": "header", "data_type": "string", "value": "string" } ] }, "tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "system_tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "when_to_use": "string" } ], "analysis_groups": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "background_ambient": false, "background_ambient_volume": 0, "thinking_sound": false, "thinking_sound_volume": 0, "language_mode": "single" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/agent/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/agent/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Update agent > PUT https://api.nabrah.ai/api/ext/agent/{agent_id} Group: Agents. Page: https://www.nabrah.ai/en/docs/api-reference/agents/update-agent Updates the agent identified by `agent_id` and returns the full updated configuration. The body uses the same fields as agent creation; provide the values you want the agent to have. Note that `tool_ids` and `analysis_group_ids` set the agent's links — use them to attach or replace linked tools and analysis groups. For adding analysis-group links specifically, you can also use the dedicated linking endpoints. Fetch the agent first (GET /api/ext/agent/{agent_id}) if you need its current values before modifying. ## Where agent_id comes from - **Discovering the ID**: Use GET /api/ext/agent to list agents, or the `agent_id` returned by POST /api/ext/agent. ## Field guidance Body fields mirror Create Agent. - **Voice & links**: `voice` must be a valid TTS voice ID from GET /api/ext/tts/voices. `tool_ids` (from GET /api/ext/tool) and `analysis_group_ids` (from GET /api/ext/analysis) define the agent's current links. The update body does not include `tool_ids`/`analysis_group_ids` in the response; the response instead expands the resolved `tools`, `system_tools`, and `analysis_groups`. Fields: `voice`, `tool_ids`, `analysis_group_ids`. ## Callback delivery Callbacks are sent once and never retried. - **pre_call_callback**: Called before each call, and before each chatbot reply on WhatsApp. Only an HTTP 200 JSON object is used; on any other response, an error or a timeout (10 seconds for calls, 30 seconds for WhatsApp) the conversation continues without those values. Fields: `pre_call_callback`. - **post_call_callback** ({ "call_details": { "id", … } }): Sent once after a completed call, with a 30-second timeout. Calls that did not complete (for example no answer or busy) do not trigger it. Use `call_details.id` to deduplicate. Fields: `post_call_callback`. - **post_analysis_callback**: Same payload, sent once after the call's analysis finishes. Fields: `post_analysis_callback`. - **Authentication**: Callbacks are not signed. Add a secret header in `parameters` (location `header`) and check it on your side; `body` parameters are added to the JSON body. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `agent_id` (string, required): UUID of the agent to update. Get this from GET /api/ext/agent or from the create-agent response (POST /api/ext/agent). ## Body (application/json) ### Example: Rename & tune Update the display name and core persona while keeping behavior simple. ```json { "name": "Support Assistant (v2)", "start_first": true, "first_sentence": "Hi, thanks for calling Nabrah support. How can I help today?", "agent_type": "basic", "who_are_you": "You are Layla, a senior support agent for Nabrah.", "goal": "Resolve the caller's issue on the first call whenever possible.", "steps": "1) Greet. 2) Identify issue. 3) Resolve or escalate. 4) Confirm.", "allow_interruptions": true, "voice": "dania", "languages": [ "ar", "en" ], "stack": "latest", "speech_speed": 1, "language_mode": "multi" } ``` ### Example: Full All supported update fields populated. ```json { "name": "Premium Support Assistant", "start_first": true, "first_sentence": "Hello, you've reached Nabrah support.", "agent_type": "basic", "who_are_you": "You are Layla, a calm and knowledgeable support agent for Nabrah.", "goal": "Diagnose, resolve, and confirm satisfaction before ending the call.", "steps": "1) Greet. 2) Verify with {{customer_name}}. 3) Diagnose. 4) Resolve or escalate. 5) Close.", "allow_interruptions": true, "voice": "dania", "support_data": "Refund window is 14 days. Business hours 9am-6pm GST.", "languages": [ "ar", "en" ], "stack": "latest", "tool_ids": [ "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f" ], "analysis_group_ids": [ "018f3a2b-1111-7e1a-bf52-0a1b2c3d4e5f" ], "speech_speed": 1, "variables": [ { "name": "customer_name", "type": "string", "default_value": "valued customer" } ], "pre_call_callback": { "url": "https://example.com/hooks/pre-call", "parameters": [ { "name": "Authorization", "location": "header", "data_type": "string", "value": "Bearer token123" } ] }, "post_call_callback": { "url": "https://example.com/hooks/post-call", "parameters": [ { "name": "call_id", "location": "body", "data_type": "string", "value": "{{call_id}}" } ] }, "post_analysis_callback": { "url": "https://example.com/hooks/post-analysis", "parameters": [ { "name": "X-Source", "location": "header", "data_type": "string", "value": "nabrah" } ] }, "background_ambient": true, "background_ambient_volume": 0.2, "thinking_sound": true, "thinking_sound_volume": 0.3, "language_mode": "multi" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "name": "string", "start_first": false, "first_sentence": "string", "agent_type": "basic", "who_are_you": "string", "goal": "string", "steps": "string", "allow_interruptions": false, "voice": "dania", "support_data": "string", "languages": [ "ar" ], "stack": "latest", "speech_speed": 0.7, "variables": [ { "name": "string", "type": "string", "default_value": "string" } ], "pre_call_callback": { "url": "string", "parameters": [ { "name": "string", "location": "header", "data_type": "string", "value": "string" } ] }, "post_call_callback": { "url": "string", "parameters": [ { "name": "string", "location": "header", "data_type": "string", "value": "string" } ] }, "post_analysis_callback": { "url": "string", "parameters": [ { "name": "string", "location": "header", "data_type": "string", "value": "string" } ] }, "tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "system_tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "when_to_use": "string" } ], "analysis_groups": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "background_ambient": false, "background_ambient_volume": 0, "thinking_sound": false, "thinking_sound_volume": 0, "language_mode": "single" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request PUT \ --url 'https://api.nabrah.ai/api/ext/agent/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Support Assistant (v2)", "start_first": true, "first_sentence": "Hi, thanks for calling Nabrah support. How can I help today?", "agent_type": "basic", "who_are_you": "You are Layla, a senior support agent for Nabrah.", "goal": "Resolve the caller'\''s issue on the first call whenever possible.", "steps": "1) Greet. 2) Identify issue. 3) Resolve or escalate. 4) Confirm.", "allow_interruptions": true, "voice": "dania", "languages": [ "ar", "en" ], "stack": "latest", "speech_speed": 1, "language_mode": "multi" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent/" headers = { "X-API-Key": "", } payload = { "name": "Support Assistant (v2)", "start_first": True, "first_sentence": "Hi, thanks for calling Nabrah support. How can I help today?", "agent_type": "basic", "who_are_you": "You are Layla, a senior support agent for Nabrah.", "goal": "Resolve the caller's issue on the first call whenever possible.", "steps": "1) Greet. 2) Identify issue. 3) Resolve or escalate. 4) Confirm.", "allow_interruptions": True, "voice": "dania", "languages": [ "ar", "en" ], "stack": "latest", "speech_speed": 1, "language_mode": "multi" } response = requests.put(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent/", { method: "PUT", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Support Assistant (v2)", "start_first": true, "first_sentence": "Hi, thanks for calling Nabrah support. How can I help today?", "agent_type": "basic", "who_are_you": "You are Layla, a senior support agent for Nabrah.", "goal": "Resolve the caller's issue on the first call whenever possible.", "steps": "1) Greet. 2) Identify issue. 3) Resolve or escalate. 4) Confirm.", "allow_interruptions": true, "voice": "dania", "languages": [ "ar", "en" ], "stack": "latest", "speech_speed": 1, "language_mode": "multi" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Support Assistant (v2)", "start_first": true, "first_sentence": "Hi, thanks for calling Nabrah support. How can I help today?", "agent_type": "basic", "who_are_you": "You are Layla, a senior support agent for Nabrah.", "goal": "Resolve the caller's issue on the first call whenever possible.", "steps": "1) Greet. 2) Identify issue. 3) Resolve or escalate. 4) Confirm.", "allow_interruptions": true, "voice": "dania", "languages": [ "ar", "en" ], "stack": "latest", "speech_speed": 1, "language_mode": "multi" }`) req, _ := http.NewRequest("PUT", "https://api.nabrah.ai/api/ext/agent/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete agent > DELETE https://api.nabrah.ai/api/ext/agent/{agent_id} Group: Agents. Page: https://www.nabrah.ai/en/docs/api-reference/agents/delete-agent Permanently deletes the agent identified by `agent_id`. On success the endpoint returns HTTP 204 with no body. This removes the agent configuration; any analysis-group links involving this agent are also removed. The operation is irreversible — fetch the agent first if you need to preserve its configuration. ## Before you call - **Where agent_id comes from**: Obtain `agent_id` from GET /api/ext/agent (list) or from the `agent_id` returned when you created the agent via POST /api/ext/agent. - **No response body**: A successful delete returns 204 No Content. A 422 indicates a malformed or invalid `agent_id`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `agent_id` (string, required): UUID of the agent to delete. Get this from GET /api/ext/agent or from the create-agent response (POST /api/ext/agent). ## Response ### 204 Successful Response ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/agent/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/agent/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Link analysis groups to an agent > POST https://api.nabrah.ai/api/ext/agent/{agent_id}/analysis_groups Group: Agents. Page: https://www.nabrah.ai/en/docs/api-reference/agents/link-analysis-groups-to-an-agent Links one or more existing analysis groups to the agent identified by `agent_id` in a single call. This establishes the many-to-many association so that, after each call, the agent runs the data collections defined by those analysis groups. The response returns one `link_id` per created link; record these to unlink later. The analysis groups must already exist — create them with POST /api/ext/analysis first. ## Order of operations Agents and analysis groups are created independently, then linked. - **Recommended flow**: 1) Create the agent (POST /api/ext/agent). 2) Create the analysis group (POST /api/ext/analysis). 3) Link them with this endpoint (or with POST /api/ext/analysis/{analysis_group_id}/agent(s) from the analysis side). - **Where the IDs come from**: `agent_id` (path) comes from GET /api/ext/agent. Each value in `analysis_group_ids` comes from GET /api/ext/analysis. ## Response shape - **link_ids array**: Returns `{ "link_ids": [...] }` — one ID per analysis group linked. Use any of these with DELETE /api/ext/analysis/link/{link_id} to remove that specific link. Fields: `link_ids`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `agent_id` (string, required): UUID of the agent to attach analysis groups to. Get this from GET /api/ext/agent or from the create-agent response (POST /api/ext/agent). ## Body (application/json) ### Example: Single group Link one analysis group to the agent. ```json { "analysis_group_ids": [ "018f3a2b-1111-7e1a-bf52-0a1b2c3d4e5f" ] } ``` ### Example: Multiple groups Link several analysis groups in one request. ```json { "analysis_group_ids": [ "018f3a2b-1111-7e1a-bf52-0a1b2c3d4e5f", "018f3a2b-2222-7e1a-bf52-0a1b2c3d4e5f" ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "link_ids": [ "00000000-0000-4000-8000-000000000000" ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/agent//analysis_groups' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "analysis_group_ids": [ "018f3a2b-1111-7e1a-bf52-0a1b2c3d4e5f" ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent//analysis_groups" headers = { "X-API-Key": "", } payload = { "analysis_group_ids": [ "018f3a2b-1111-7e1a-bf52-0a1b2c3d4e5f" ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent//analysis_groups", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "analysis_group_ids": [ "018f3a2b-1111-7e1a-bf52-0a1b2c3d4e5f" ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "analysis_group_ids": [ "018f3a2b-1111-7e1a-bf52-0a1b2c3d4e5f" ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/agent//analysis_groups", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List analysis groups for an agent > GET https://api.nabrah.ai/api/ext/agent/{agent_id}/analysis_groups Group: Agents. Page: https://www.nabrah.ai/en/docs/api-reference/agents/list-analysis-groups-for-an-agent Lists all analysis groups currently linked to the agent identified by `agent_id`. Agents and analysis groups have a many-to-many relationship; this endpoint shows that relationship from the agent's side. Crucially, each returned item includes the `link_id` for the agent↔analysis-group association — you need that `link_id` to unlink them later via DELETE /api/ext/analysis/link/{link_id}. ## Where agent_id comes from - **Discovering the ID**: Use GET /api/ext/agent to list agents, or the `agent_id` returned by POST /api/ext/agent. ## Response shape Returns an object with an `analysis_groups` array. - **The link_id matters**: Each analysis group in the array carries a `link_id` representing this specific agent↔group link. Save it to unlink later. The `id` field is the analysis group's own ID (usable with the /api/ext/analysis endpoints). Fields: `name`, `id`, `project_id`, `created_at`, `updated_at`, `link_id`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `agent_id` (string, required): UUID of the agent whose linked analysis groups you want to list. Get this from GET /api/ext/agent or from the create-agent response (POST /api/ext/agent). ## Response ### 200 Successful Response Content type: `application/json` ```json { "analysis_groups": [ { "name": "string", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "link_id": "00000000-0000-4000-8000-000000000000" } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/agent//analysis_groups' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent//analysis_groups" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent//analysis_groups", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/agent//analysis_groups", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Tools API Register the HTTP tools an agent can call during a conversation, and link them to agents. - [Create tool](https://www.nabrah.ai/en/docs/api-reference/tools/create-tool.md) `POST /api/ext/tool`: Create a new function-calling tool in the current project. - [List tools](https://www.nabrah.ai/en/docs/api-reference/tools/list-tools.md) `GET /api/ext/tool`: List every tool defined in the current project. - [Get tool](https://www.nabrah.ai/en/docs/api-reference/tools/get-tool.md) `GET /api/ext/tool/{tool_id}`: Retrieve the full record for a single tool by its id, including its url, method, timeout, and the complete list of parameters with their tool_id back-reference. - [Update tool](https://www.nabrah.ai/en/docs/api-reference/tools/update-tool.md) `PUT /api/ext/tool/{tool_id}`: Update an existing tool in the current project. - [Delete tool](https://www.nabrah.ai/en/docs/api-reference/tools/delete-tool.md) `DELETE /api/ext/tool/{tool_id}`: Permanently delete a tool from the current project. - [Link a tool to an agent](https://www.nabrah.ai/en/docs/api-reference/tools/link-a-tool-to-an-agent.md) `POST /api/ext/tool/link`: Link a tool to an agent so the agent can call that tool during conversations. - [Unlink a tool from an agent](https://www.nabrah.ai/en/docs/api-reference/tools/unlink-a-tool-from-an-agent.md) `DELETE /api/ext/tool/unlink/{link_id}`: Remove the link between a tool and an agent, so the agent can no longer call that tool. - [List tools for an agent](https://www.nabrah.ai/en/docs/api-reference/tools/list-tools-for-an-agent.md) `GET /api/ext/tool/agent/{agent_id}`: List every tool attached to the given agent. - [List agents for a tool](https://www.nabrah.ai/en/docs/api-reference/tools/list-agents-for-a-tool.md) `GET /api/ext/tool/{tool_id}/agents`: List every agent that the given tool is linked to. --- # Create tool > POST https://api.nabrah.ai/api/ext/tool Group: Tools. Page: https://www.nabrah.ai/en/docs/api-reference/tools/create-tool Create a new function-calling tool in the current project. A tool is an HTTP webhook the agent can call during a conversation; the agent decides when to invoke it based on the name and description, then sends the configured parameters to your url. The response returns the full tool record including its generated id (the tool_id). After creating a tool you typically link it to one or more agents with POST /api/ext/tool/link before it can be used in a call. ## Request fields Describe the webhook the agent should call and the parameters it may pass. - **Tool definition**: name and description tell the model what the tool does and when to use it — write them clearly because the agent selects tools from these. url is the endpoint Nabrah calls; method is the HTTP verb (GET, POST, etc.). timeout_seconds bounds how long Nabrah waits for your endpoint. Fields: `name`, `description`, `url`, `method`, `timeout_seconds`. - **parameters[]**: Defines each value sent with the call. name is the parameter key. location is where it is placed (query, header, path, or body). parameter_type is either constant (a fixed value you supply in value) or inferred from the conversation by the model. data_type is the JSON type (string, number, boolean, etc.). description guides the model when it must fill the value. required marks whether the agent must provide it. value holds the fixed value for constant parameters; default_value is used when no value is supplied. Fields: `name`, `location`, `parameter_type`, `data_type`, `description`, `required`, `value`, `default_value`. ## Next step: linking Creating a tool does not attach it to any agent. Take the id from the response and call POST /api/ext/tool/link with that tool_id plus an agent_id to make the tool available during the agent's conversations. ## Delivery and retries How Nabrah calls your endpoint during a call or chat. - **One attempt per use**: Each time the agent uses the tool, Nabrah calls your endpoint once and waits up to `timeout_seconds`. There is no automatic retry or backoff, and no request ID is sent. Fields: `timeout_seconds`. - **Errors**: Your status code and response body are passed back to the agent, which decides what to tell the caller. On a timeout or connection error the agent is told the tool failed. The agent may call the tool again in the same conversation, so make booking-style endpoints idempotent, for example by keying them on your own appointment ID. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Get weather (query params) A GET webhook with two query parameters: a constant API token and a model-filled city name. ```json { "name": "get_weather", "description": "Look up the current weather for a city. Call this when the caller asks about weather conditions.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 5, "parameters": [ { "name": "api_token", "location": "header", "parameter_type": "constant", "data_type": "string", "description": "Service API token", "required": true, "value": "secret-token-123", "default_value": "" }, { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": true, "value": "", "default_value": "Riyadh" } ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "description": "string", "url": "string", "method": "GET", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "timeout_seconds": 5, "parameters": [ { "name": "string", "location": "query", "parameter_type": "constant", "data_type": "string", "description": "string", "tool_id": "00000000-0000-4000-8000-000000000000", "required": false, "value": "string", "default_value": "string" } ], "link_id": "00000000-0000-4000-8000-000000000000" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/tool' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "get_weather", "description": "Look up the current weather for a city. Call this when the caller asks about weather conditions.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 5, "parameters": [ { "name": "api_token", "location": "header", "parameter_type": "constant", "data_type": "string", "description": "Service API token", "required": true, "value": "secret-token-123", "default_value": "" }, { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": true, "value": "", "default_value": "Riyadh" } ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tool" headers = { "X-API-Key": "", } payload = { "name": "get_weather", "description": "Look up the current weather for a city. Call this when the caller asks about weather conditions.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 5, "parameters": [ { "name": "api_token", "location": "header", "parameter_type": "constant", "data_type": "string", "description": "Service API token", "required": True, "value": "secret-token-123", "default_value": "" }, { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": True, "value": "", "default_value": "Riyadh" } ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tool", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "get_weather", "description": "Look up the current weather for a city. Call this when the caller asks about weather conditions.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 5, "parameters": [ { "name": "api_token", "location": "header", "parameter_type": "constant", "data_type": "string", "description": "Service API token", "required": true, "value": "secret-token-123", "default_value": "" }, { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": true, "value": "", "default_value": "Riyadh" } ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "get_weather", "description": "Look up the current weather for a city. Call this when the caller asks about weather conditions.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 5, "parameters": [ { "name": "api_token", "location": "header", "parameter_type": "constant", "data_type": "string", "description": "Service API token", "required": true, "value": "secret-token-123", "default_value": "" }, { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": true, "value": "", "default_value": "Riyadh" } ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/tool", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List tools > GET https://api.nabrah.ai/api/ext/tool Group: Tools. Page: https://www.nabrah.ai/en/docs/api-reference/tools/list-tools List every tool defined in the current project. Tools are custom function-calling capabilities (HTTP webhooks) that an agent can invoke during a conversation to fetch data or trigger actions in your own systems. Authentication is by API key, so the project is determined by the key and there is no project_id parameter. Use this endpoint to discover existing tool_id values for linking, updating, or deleting tools. ## Authentication Every request to the Tools API uses your Web API key. - **X-API-Key header** (prefix nb_): Send your key in the X-API-Key header. Find or create it in the dashboard under the Developers tab. Each key is scoped to a single project, which is why no project_id is ever passed. Validate a key first with GET /api/ext/test. ## Response fields Each array item is a compact tool summary. Use the id as the tool_id in other Tool endpoints. - **Tool summary**: id is the tool_id used throughout this API. method is the HTTP verb the tool calls on its target url. timeout_seconds is how long Nabrah waits for the webhook before giving up. Fields: `id`, `name`, `description`, `url`, `method`, `timeout_seconds`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "description": "string", "url": "string", "method": "GET", "timeout_seconds": 0 } ] ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/tool' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tool" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tool", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/tool", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get tool > GET https://api.nabrah.ai/api/ext/tool/{tool_id} Group: Tools. Page: https://www.nabrah.ai/en/docs/api-reference/tools/get-tool Retrieve the full record for a single tool by its id, including its url, method, timeout, and the complete list of parameters with their tool_id back-reference. Use this to inspect a tool's exact configuration before updating it or linking it to an agent. Authenticated with your API key. ## Response fields - **Full tool record**: Includes id (the tool_id), project_id, timestamps, the webhook url and method, timeout_seconds, and the full parameters array. Each parameter carries a tool_id back-reference to this tool. link_id is populated when the tool is being viewed in the context of a specific agent link. Fields: `id`, `name`, `description`, `url`, `method`, `project_id`, `timeout_seconds`, `parameters`, `link_id`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `tool_id` (string, required): The ID of the tool to fetch. Obtain it from GET /api/ext/tool (the id field) or from the response of POST /api/ext/tool. ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "description": "string", "url": "string", "method": "GET", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "timeout_seconds": 5, "parameters": [ { "name": "string", "location": "query", "parameter_type": "constant", "data_type": "string", "description": "string", "tool_id": "00000000-0000-4000-8000-000000000000", "required": false, "value": "string", "default_value": "string" } ], "link_id": "00000000-0000-4000-8000-000000000000" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/tool/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tool/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tool/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/tool/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Update tool > PUT https://api.nabrah.ai/api/ext/tool/{tool_id} Group: Tools. Page: https://www.nabrah.ai/en/docs/api-reference/tools/update-tool Update an existing tool in the current project. Send the fields you want to change in the request body; the tool is identified by the tool_id path parameter. When you include parameters, provide the full intended set, since it replaces the tool's parameter list. The response returns the updated tool record. Authenticated with your API key. ## Request fields All fields are optional; only the ones you send are changed. - **Editable fields**: name, description, url, method, and timeout_seconds behave exactly as on create. parameters replaces the tool's full parameter list when supplied — to keep existing parameters, include them in the array. Fields: `name`, `description`, `url`, `method`, `timeout_seconds`, `parameters`. - **parameters[] entry**: Same shape as on create: name, location (query/header/path/body), parameter_type (constant or inferred), data_type, description, required, value (for constant parameters), and default_value. Fields: `name`, `location`, `parameter_type`, `data_type`, `description`, `required`, `value`, `default_value`. ## Delivery and retries How Nabrah calls your endpoint during a call or chat. - **One attempt per use**: Each time the agent uses the tool, Nabrah calls your endpoint once and waits up to `timeout_seconds`. There is no automatic retry or backoff, and no request ID is sent. Fields: `timeout_seconds`. - **Errors**: Your status code and response body are passed back to the agent, which decides what to tell the caller. On a timeout or connection error the agent is told the tool failed. The agent may call the tool again in the same conversation, so make booking-style endpoints idempotent, for example by keying them on your own appointment ID. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `tool_id` (string, required): The ID of the tool to update. Obtain it from GET /api/ext/tool (the id field) or from the response of POST /api/ext/tool. ## Body (application/json) ### Example: Update description & timeout Rename and re-document the tool and raise its timeout, keeping a single parameter. ```json { "name": "get_weather", "description": "Look up current weather conditions for a given city.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 10, "parameters": [ { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": true, "value": "", "default_value": "Riyadh" } ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "description": "string", "url": "string", "method": "GET", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "timeout_seconds": 5, "parameters": [ { "name": "string", "location": "query", "parameter_type": "constant", "data_type": "string", "description": "string", "tool_id": "00000000-0000-4000-8000-000000000000", "required": false, "value": "string", "default_value": "string" } ], "link_id": "00000000-0000-4000-8000-000000000000" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request PUT \ --url 'https://api.nabrah.ai/api/ext/tool/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "get_weather", "description": "Look up current weather conditions for a given city.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 10, "parameters": [ { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": true, "value": "", "default_value": "Riyadh" } ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tool/" headers = { "X-API-Key": "", } payload = { "name": "get_weather", "description": "Look up current weather conditions for a given city.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 10, "parameters": [ { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": True, "value": "", "default_value": "Riyadh" } ] } response = requests.put(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tool/", { method: "PUT", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "get_weather", "description": "Look up current weather conditions for a given city.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 10, "parameters": [ { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": true, "value": "", "default_value": "Riyadh" } ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "get_weather", "description": "Look up current weather conditions for a given city.", "url": "https://api.example.com/weather", "method": "GET", "timeout_seconds": 10, "parameters": [ { "name": "city", "location": "query", "parameter_type": "inferred", "data_type": "string", "description": "The city the caller is asking about", "required": true, "value": "", "default_value": "Riyadh" } ] }`) req, _ := http.NewRequest("PUT", "https://api.nabrah.ai/api/ext/tool/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete tool > DELETE https://api.nabrah.ai/api/ext/tool/{tool_id} Group: Tools. Page: https://www.nabrah.ai/en/docs/api-reference/tools/delete-tool Permanently delete a tool from the current project. This removes the tool definition itself; any agent links that reference it are removed along with it, so agents will no longer be able to call it. The operation is identified by the tool_id path parameter and authenticated with your API key. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `tool_id` (string, required): The ID of the tool to delete. Obtain it from GET /api/ext/tool (the id field) or from the response of POST /api/ext/tool when the tool was created. ## Response ### 200 Successful Response Content type: `application/json` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/tool/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tool/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tool/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/tool/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Link a tool to an agent > POST https://api.nabrah.ai/api/ext/tool/link Group: Tools. Page: https://www.nabrah.ai/en/docs/api-reference/tools/link-a-tool-to-an-agent Link a tool to an agent so the agent can call that tool during conversations. The relationship is many-to-many: a tool can be linked to many agents and an agent can have many tools. The response returns a link record whose id is the link_id — keep it, since it is how you later remove the link via DELETE /api/ext/tool/unlink/{link_id}. Authenticated with your API key. ## Request fields - **Link target**: tool_id is the tool to attach (from GET /api/ext/tool or the create-tool response). agent_id is the agent to attach it to (from GET /api/ext/agent). Both must belong to the project the API key is scoped to. Fields: `tool_id`, `agent_id`. ## End-to-end flow Typical lifecycle for making a tool callable by an agent. 1) Create a tool with POST /api/ext/tool (or pick one from GET /api/ext/tool). 2) Create or choose an agent with GET /api/ext/agent. 3) Link them here — the response id is the link_id. 4) Later, detach with DELETE /api/ext/tool/unlink/{link_id}. ## Response fields - **Link record**: id is the link_id used to unlink. tool_id and agent_id echo the linked pair, project_id is the owning project, and created_at / updated_at are the link timestamps. Fields: `id`, `tool_id`, `agent_id`, `project_id`, `created_at`, `updated_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Link tool to agent ```json { "tool_id": "00000000-0000-4000-8000-000000000000", "agent_id": "00000000-0000-4000-8000-000000000000" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "tool_id": "00000000-0000-4000-8000-000000000000", "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/tool/link' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "tool_id": "00000000-0000-4000-8000-000000000000", "agent_id": "00000000-0000-4000-8000-000000000000" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tool/link" headers = { "X-API-Key": "", } payload = { "tool_id": "00000000-0000-4000-8000-000000000000", "agent_id": "00000000-0000-4000-8000-000000000000" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tool/link", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "tool_id": "00000000-0000-4000-8000-000000000000", "agent_id": "00000000-0000-4000-8000-000000000000" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "tool_id": "00000000-0000-4000-8000-000000000000", "agent_id": "00000000-0000-4000-8000-000000000000" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/tool/link", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Unlink a tool from an agent > DELETE https://api.nabrah.ai/api/ext/tool/unlink/{link_id} Group: Tools. Page: https://www.nabrah.ai/en/docs/api-reference/tools/unlink-a-tool-from-an-agent Remove the link between a tool and an agent, so the agent can no longer call that tool. This deletes only the link, not the tool or the agent themselves — both remain in the project and any other links are unaffected. Identified by the link_id path parameter and authenticated with your API key. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `link_id` (string, required): The ID of the tool-agent link to remove. This is the link_id returned by POST /api/ext/tool/link, and also appears as link_id in GET /api/ext/tool/{tool_id}/agents and GET /api/ext/tool/agent/{agent_id}. ## Response ### 200 Successful Response Content type: `application/json` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/tool/unlink/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tool/unlink/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tool/unlink/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/tool/unlink/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List tools for an agent > GET https://api.nabrah.ai/api/ext/tool/agent/{agent_id} Group: Tools. Page: https://www.nabrah.ai/en/docs/api-reference/tools/list-tools-for-an-agent List every tool attached to the given agent. This is the inverse of listing agents for a tool: it returns the full tool records (url, method, parameters, etc.) that the agent can invoke during a conversation. Each tool includes a link_id identifying its link to this agent, which you pass to DELETE /api/ext/tool/unlink/{link_id} to detach it. Identified by the agent_id path parameter and authenticated with your API key. ## Response fields - **Linked tool record**: Each item is a full tool record. id is the tool_id; parameters is the full parameter list; timeout_seconds and method describe the webhook call. link_id identifies the link between this tool and the agent — pass it to the unlink endpoint to detach the tool from this agent. Fields: `id`, `name`, `description`, `url`, `method`, `project_id`, `timeout_seconds`, `parameters`, `link_id`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `agent_id` (string, required): The ID of the agent whose tools you want to list. Obtain it from GET /api/ext/agent (the id field). ## Response ### 200 Successful Response Content type: `application/json` ```json [ { "name": "string", "description": "string", "url": "string", "method": "GET", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "timeout_seconds": 5, "parameters": [ { "name": "string", "location": "query", "parameter_type": "constant", "data_type": "string", "description": "string", "tool_id": "00000000-0000-4000-8000-000000000000", "required": false, "value": "string", "default_value": "string" } ], "link_id": "00000000-0000-4000-8000-000000000000" } ] ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/tool/agent/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tool/agent/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tool/agent/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/tool/agent/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List agents for a tool > GET https://api.nabrah.ai/api/ext/tool/{tool_id}/agents Group: Tools. Page: https://www.nabrah.ai/en/docs/api-reference/tools/list-agents-for-a-tool List every agent that the given tool is linked to. Tools and agents form a many-to-many relationship: one tool can be attached to multiple agents. Each result includes the agent plus the link_id that joins it to this tool, which you pass to DELETE /api/ext/tool/unlink/{link_id} to detach them. Identified by the tool_id path parameter and authenticated with your API key. ## Response fields - **Linked agent**: id is the agent_id. link_id is the identifier of the tool-agent link; use it to unlink via DELETE /api/ext/tool/unlink/{link_id}. created_at and updated_at reflect when the link was made or last changed. Fields: `id`, `name`, `link_id`, `created_at`, `updated_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `tool_id` (string, required): The ID of the tool whose linked agents you want to list. Obtain it from GET /api/ext/tool (the id field) or from the response of POST /api/ext/tool. ## Response ### 200 Successful Response Content type: `application/json` ```json [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "link_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ] ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/tool//agents' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tool//agents" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tool//agents", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/tool//agents", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Analysis groups API Define the data extracted from every call, and link analysis groups to agents. - [Create analysis group](https://www.nabrah.ai/en/docs/api-reference/analysis-groups/create-analysis-group.md) `POST /api/ext/analysis`: Creates a new analysis group together with its data collections and returns the full group, including the generated id. - [List analysis groups](https://www.nabrah.ai/en/docs/api-reference/analysis-groups/list-analysis-groups.md) `GET /api/ext/analysis`: Returns a brief listing of every analysis group in the workspace tied to your API key. - [Get analysis group](https://www.nabrah.ai/en/docs/api-reference/analysis-groups/get-analysis-group.md) `GET /api/ext/analysis/{analysis_group_id}`: Returns the full definition of a single analysis group identified by analysis_group_id, including all of its data_collections (each with name, type, description, and constraints). - [Update analysis group](https://www.nabrah.ai/en/docs/api-reference/analysis-groups/update-analysis-group.md) `PUT /api/ext/analysis/{analysis_group_id}`: Updates the analysis group identified by analysis_group_id, replacing its name and data collections with the values provided, and returns the full updated group. - [Delete analysis group](https://www.nabrah.ai/en/docs/api-reference/analysis-groups/delete-analysis-group.md) `DELETE /api/ext/analysis/{analysis_group_id}`: Permanently deletes the analysis group identified by analysis_group_id along with all its data collections and any links to agents. - [Link an agent to an analysis group](https://www.nabrah.ai/en/docs/api-reference/analysis-groups/link-an-agent-to-an-analysis-group.md) `POST /api/ext/analysis/{analysis_group_id}/agent`: Links a single agent to the analysis group identified by analysis_group_id and returns the new link_id. - [Link agents to an analysis group](https://www.nabrah.ai/en/docs/api-reference/analysis-groups/link-agents-to-an-analysis-group.md) `POST /api/ext/analysis/{analysis_group_id}/agents`: Links multiple agents to the analysis group identified by analysis_group_id in a single call and returns one link_id per created link. - [List agents for an analysis group](https://www.nabrah.ai/en/docs/api-reference/analysis-groups/list-agents-for-an-analysis-group.md) `GET /api/ext/analysis/{analysis_group_id}/agents`: Lists all agents currently linked to the analysis group identified by analysis_group_id. - [Unlink an analysis group from an agent](https://www.nabrah.ai/en/docs/api-reference/analysis-groups/unlink-an-analysis-group-from-an-agent.md) `DELETE /api/ext/analysis/link/{link_id}`: Removes a single agent↔analysis-group association identified by link_id. --- # Create analysis group > POST https://api.nabrah.ai/api/ext/analysis Group: Analysis groups. Page: https://www.nabrah.ai/en/docs/api-reference/analysis-groups/create-analysis-group Creates a new analysis group together with its data collections and returns the full group, including the generated `id`. Each data collection describes one piece of information to extract or evaluate from a call — a `name`, a `type`, a human-readable `description`, and optional `constraints`. After creating a group, link it to one or more agents (via POST /api/ext/agent/{agent_id}/analysis_groups or POST /api/ext/analysis/{analysis_group_id}/agent(s)) so it runs after those agents' calls. ## Body fields - **Group and data collections**: `name` labels the group. `data_collections` is the list of items to capture; each has a `name` (the field key), a `type` (the expected data type, e.g. "string", "number", "boolean"), a `description` guiding what to extract/evaluate, and an optional `constraints` object for additional rules. Fields: `name`, `data_collections`. ## Order of operations - **Then link to agents**: Creating a group does not attach it to any agent. After capturing the returned `id`, link it using the agent or analysis linking endpoints. A 422 response means the body failed validation; inspect `detail` for the offending field path. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Minimal A group with a single extraction field. ```json { "name": "Call Outcome", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller's issue was resolved during the call.", "constraints": {} } ] } ``` ### Example: Full A group with multiple data collections of different types. ```json { "name": "Support Call Analysis", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller's issue was resolved during the call.", "constraints": {} }, { "name": "sentiment", "type": "string", "description": "Overall caller sentiment at the end of the call.", "constraints": { "enum": [ "positive", "neutral", "negative" ] } }, { "name": "satisfaction_score", "type": "number", "description": "Estimated caller satisfaction on a 1-5 scale.", "constraints": { "min": 1, "max": 5 } } ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "data_collections": [ { "name": "string", "type": "string", "id": "00000000-0000-4000-8000-000000000000", "analysis_group_id": "00000000-0000-4000-8000-000000000000", "description": "string", "constraints": {} } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/analysis' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Call Outcome", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller'\''s issue was resolved during the call.", "constraints": {} } ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/analysis" headers = { "X-API-Key": "", } payload = { "name": "Call Outcome", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller's issue was resolved during the call.", "constraints": {} } ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/analysis", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Call Outcome", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller's issue was resolved during the call.", "constraints": {} } ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Call Outcome", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller's issue was resolved during the call.", "constraints": {} } ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/analysis", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List analysis groups > GET https://api.nabrah.ai/api/ext/analysis Group: Analysis groups. Page: https://www.nabrah.ai/en/docs/api-reference/analysis-groups/list-analysis-groups Returns a brief listing of every analysis group in the workspace tied to your API key. Analysis groups define the post-call analysis (data extraction and evaluation collections) that can be linked to one or more agents. Use this endpoint to discover analysis groups and obtain the `id` values needed by the per-group endpoints (get, update, delete, link agents) and by the agent linking endpoints. ## What an analysis group is - **Concept**: An analysis group is a named set of data collections (extraction/evaluation definitions) applied after a call. The same group can be linked to many agents, and an agent can have many groups — a many-to-many relationship. ## Response shape Returns an object with an `analysis_groups` array (brief form, no data collections). - **Fields per item**: Each item lists the group's `id`, `name`, owning `project_id`, and timestamps. Call GET /api/ext/analysis/{analysis_group_id} to see the full `data_collections`. This endpoint returns all groups in the project without pagination. Fields: `name`, `id`, `project_id`, `created_at`, `updated_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "analysis_groups": [ { "name": "string", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/analysis' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/analysis" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/analysis", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/analysis", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get analysis group > GET https://api.nabrah.ai/api/ext/analysis/{analysis_group_id} Group: Analysis groups. Page: https://www.nabrah.ai/en/docs/api-reference/analysis-groups/get-analysis-group Returns the full definition of a single analysis group identified by `analysis_group_id`, including all of its `data_collections` (each with name, type, description, and constraints). Use this to inspect a group before updating it or to verify exactly what data is extracted/evaluated after a call. ## Where analysis_group_id comes from - **Discovering the ID**: List groups with GET /api/ext/analysis, or use the `id` returned by POST /api/ext/analysis. ## Response shape Returns a full AnalysisGroupResponse. - **Data collections**: Each entry in `data_collections` includes its own `id` and the parent `analysis_group_id`, plus the `name`, `type`, `description`, and `constraints` that drive extraction/evaluation. Fields: `name`, `id`, `project_id`, `created_at`, `updated_at`, `data_collections`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `analysis_group_id` (string, required): UUID of the analysis group to retrieve. Get this from GET /api/ext/analysis or from the create response (POST /api/ext/analysis). ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "data_collections": [ { "name": "string", "type": "string", "id": "00000000-0000-4000-8000-000000000000", "analysis_group_id": "00000000-0000-4000-8000-000000000000", "description": "string", "constraints": {} } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/analysis/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/analysis/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/analysis/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/analysis/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Update analysis group > PUT https://api.nabrah.ai/api/ext/analysis/{analysis_group_id} Group: Analysis groups. Page: https://www.nabrah.ai/en/docs/api-reference/analysis-groups/update-analysis-group Updates the analysis group identified by `analysis_group_id`, replacing its name and data collections with the values provided, and returns the full updated group. Supply the complete intended set of `data_collections` — this defines the group's collections going forward. Existing agent links to the group are preserved. ## Where analysis_group_id comes from - **Discovering the ID**: List groups with GET /api/ext/analysis, or use the `id` returned by POST /api/ext/analysis. ## Body fields - **Same shape as create**: The body mirrors Create Analysis Group: a `name` and a `data_collections` array of `{ name, type, description, constraints }`. Fields: `name`, `data_collections`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `analysis_group_id` (string, required): UUID of the analysis group to update. Get this from GET /api/ext/analysis or from the create response (POST /api/ext/analysis). ## Body (application/json) ### Example: Update collections Rename the group and redefine its data collections. ```json { "name": "Support Call Analysis (v2)", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller's issue was resolved during the call.", "constraints": {} }, { "name": "follow_up_needed", "type": "boolean", "description": "Whether a follow-up call or ticket is required.", "constraints": {} } ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "data_collections": [ { "name": "string", "type": "string", "id": "00000000-0000-4000-8000-000000000000", "analysis_group_id": "00000000-0000-4000-8000-000000000000", "description": "string", "constraints": {} } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request PUT \ --url 'https://api.nabrah.ai/api/ext/analysis/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Support Call Analysis (v2)", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller'\''s issue was resolved during the call.", "constraints": {} }, { "name": "follow_up_needed", "type": "boolean", "description": "Whether a follow-up call or ticket is required.", "constraints": {} } ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/analysis/" headers = { "X-API-Key": "", } payload = { "name": "Support Call Analysis (v2)", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller's issue was resolved during the call.", "constraints": {} }, { "name": "follow_up_needed", "type": "boolean", "description": "Whether a follow-up call or ticket is required.", "constraints": {} } ] } response = requests.put(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/analysis/", { method: "PUT", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Support Call Analysis (v2)", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller's issue was resolved during the call.", "constraints": {} }, { "name": "follow_up_needed", "type": "boolean", "description": "Whether a follow-up call or ticket is required.", "constraints": {} } ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Support Call Analysis (v2)", "data_collections": [ { "name": "resolved", "type": "boolean", "description": "Whether the caller's issue was resolved during the call.", "constraints": {} }, { "name": "follow_up_needed", "type": "boolean", "description": "Whether a follow-up call or ticket is required.", "constraints": {} } ] }`) req, _ := http.NewRequest("PUT", "https://api.nabrah.ai/api/ext/analysis/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete analysis group > DELETE https://api.nabrah.ai/api/ext/analysis/{analysis_group_id} Group: Analysis groups. Page: https://www.nabrah.ai/en/docs/api-reference/analysis-groups/delete-analysis-group Permanently deletes the analysis group identified by `analysis_group_id` along with all its data collections and any links to agents. This operation is irreversible. Fetch the group first (GET /api/ext/analysis/{analysis_group_id}) if you need to preserve its definition. ## Where analysis_group_id comes from - **Discovering the ID**: List groups with GET /api/ext/analysis and copy the `id`, or use the `id` returned by POST /api/ext/analysis. - **Cascading effect**: Deleting a group also removes its agent links, so agents previously linked to it will no longer run its collections. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `analysis_group_id` (string, required): UUID of the analysis group to delete. Get this from GET /api/ext/analysis or from the create response (POST /api/ext/analysis). ## Response ### 200 Successful Response Content type: `application/json` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/analysis/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/analysis/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/analysis/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/analysis/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Link an agent to an analysis group > POST https://api.nabrah.ai/api/ext/analysis/{analysis_group_id}/agent Group: Analysis groups. Page: https://www.nabrah.ai/en/docs/api-reference/analysis-groups/link-an-agent-to-an-analysis-group Links a single agent to the analysis group identified by `analysis_group_id` and returns the new `link_id`. This is the analysis-side counterpart to the agent linking endpoints and is convenient when you want to attach one specific agent. The agent must already exist (create it with POST /api/ext/agent). Save the returned `link_id` to unlink later via DELETE /api/ext/analysis/link/{link_id}. ## Where the IDs come from - **Path and body IDs**: `analysis_group_id` (path) comes from GET /api/ext/analysis. `agent_id` (body) comes from GET /api/ext/agent. ## Response shape - **link_id**: Returns `{ "link_id": "..." }`. Use this to remove the link with DELETE /api/ext/analysis/link/{link_id}. To link many agents at once, use POST /api/ext/analysis/{analysis_group_id}/agents instead. Fields: `link_id`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `analysis_group_id` (string, required): UUID of the analysis group to link an agent to. Get this from GET /api/ext/analysis or from the create response (POST /api/ext/analysis). ## Body (application/json) ### Example: Link agent Link one agent to this analysis group. ```json { "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "link_id": "00000000-0000-4000-8000-000000000000" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/analysis//agent' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/analysis//agent" headers = { "X-API-Key": "", } payload = { "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/analysis//agent", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/analysis//agent", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Link agents to an analysis group > POST https://api.nabrah.ai/api/ext/analysis/{analysis_group_id}/agents Group: Analysis groups. Page: https://www.nabrah.ai/en/docs/api-reference/analysis-groups/link-agents-to-an-analysis-group Links multiple agents to the analysis group identified by `analysis_group_id` in a single call and returns one `link_id` per created link. Use this to attach a batch of existing agents to the same analysis group at once. The agents must already exist (create them with POST /api/ext/agent). ## Where the IDs come from - **Path and body IDs**: `analysis_group_id` (path) comes from GET /api/ext/analysis. Each value in `agent_ids` comes from GET /api/ext/agent. ## Response shape - **link_ids array**: Returns `{ "link_ids": [...] }` — one ID per agent linked. Use any with DELETE /api/ext/analysis/link/{link_id} to remove a specific link. For a single agent, POST /api/ext/analysis/{analysis_group_id}/agent is simpler. Fields: `link_ids`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `analysis_group_id` (string, required): UUID of the analysis group to link agents to. Get this from GET /api/ext/analysis or from the create response (POST /api/ext/analysis). ## Body (application/json) ### Example: Link agents Link several agents to this analysis group in one request. ```json { "agent_ids": [ "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "018f3a2b-8b3c-7e1a-bf52-0a1b2c3d4e5f" ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "link_ids": [ "00000000-0000-4000-8000-000000000000" ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/analysis//agents' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "agent_ids": [ "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "018f3a2b-8b3c-7e1a-bf52-0a1b2c3d4e5f" ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/analysis//agents" headers = { "X-API-Key": "", } payload = { "agent_ids": [ "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "018f3a2b-8b3c-7e1a-bf52-0a1b2c3d4e5f" ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/analysis//agents", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "agent_ids": [ "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "018f3a2b-8b3c-7e1a-bf52-0a1b2c3d4e5f" ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "agent_ids": [ "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "018f3a2b-8b3c-7e1a-bf52-0a1b2c3d4e5f" ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/analysis//agents", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List agents for an analysis group > GET https://api.nabrah.ai/api/ext/analysis/{analysis_group_id}/agents Group: Analysis groups. Page: https://www.nabrah.ai/en/docs/api-reference/analysis-groups/list-agents-for-an-analysis-group Lists all agents currently linked to the analysis group identified by `analysis_group_id`. This shows the many-to-many relationship from the analysis group's side. Each returned agent includes the `link_id` for its association with this group — needed to unlink via DELETE /api/ext/analysis/link/{link_id}. ## Where analysis_group_id comes from - **Discovering the ID**: List groups with GET /api/ext/analysis, or use the `id` returned by POST /api/ext/analysis. ## Response shape Returns a JSON array of brief agent objects. - **Each item carries a link_id**: Alongside agent metadata, each item includes the `link_id` for this agent↔group link. Save it to unlink later. Fields: `agent_id`, `name`, `created_at`, `last_update`, `created_by_email`, `link_id`, `enable_ticket_creation`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `analysis_group_id` (string, required): UUID of the analysis group whose linked agents you want to list. Get this from GET /api/ext/analysis or from the create response (POST /api/ext/analysis). ## Response ### 200 Successful Response Content type: `application/json` ```json [ { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "created_by_email": "string", "link_id": "00000000-0000-4000-8000-000000000000", "enable_ticket_creation": false } ] ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/analysis//agents' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/analysis//agents" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/analysis//agents", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/analysis//agents", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Unlink an analysis group from an agent > DELETE https://api.nabrah.ai/api/ext/analysis/link/{link_id} Group: Analysis groups. Page: https://www.nabrah.ai/en/docs/api-reference/analysis-groups/unlink-an-analysis-group-from-an-agent Removes a single agent↔analysis-group association identified by `link_id`. This unlinks one agent from one analysis group without deleting either the agent or the group. On success the endpoint returns HTTP 204 with no body. ## Where link_id comes from A link_id is produced whenever you create an agent↔group link. - **Sources of link_id**: Captured from the response of POST /api/ext/agent/{agent_id}/analysis_groups (`link_ids`), POST /api/ext/analysis/{analysis_group_id}/agent (`link_id`), or POST /api/ext/analysis/{analysis_group_id}/agents (`link_ids`). You can also read it from GET /api/ext/agent/{agent_id}/analysis_groups or GET /api/ext/analysis/{analysis_group_id}/agents, where each item carries its `link_id`. - **No response body**: A successful unlink returns 204 No Content. A 422 indicates a malformed or unknown `link_id`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `link_id` (string, required): UUID of the agent↔analysis-group link to remove. Get this from the link-creation responses or from GET /api/ext/agent/{agent_id}/analysis_groups / GET /api/ext/analysis/{analysis_group_id}/agents. ## Response ### 204 Successful Response ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/analysis/link/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/analysis/link/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/analysis/link/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/analysis/link/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Knowledge bases API Create the knowledge bases an agent retrieves answers from during a call. - [Create knowledge base](https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/create-knowledge-base.md) `POST /api/ext/kb`: Creates a knowledge base (KB) in your project. - [List knowledge bases](https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/list-knowledge-bases.md) `GET /api/ext/kb`: Lists all knowledge bases in your project, newest first. - [Get knowledge base](https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/get-knowledge-base.md) `GET /api/ext/kb/{knowledge_base_id}`: Returns a single knowledge base by id, including a documents list — each with id, type, status, filename, and created_at. - [Update knowledge base](https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/update-knowledge-base.md) `PATCH /api/ext/kb/{knowledge_base_id}`: Updates a knowledge base's name, key, and/or desc. - [Delete knowledge base](https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/delete-knowledge-base.md) `DELETE /api/ext/kb/{knowledge_base_id}`: Permanently deletes a knowledge base and all of its documents, and removes their indexed vectors so they are no longer retrievable. --- # Create knowledge base > POST https://api.nabrah.ai/api/ext/kb Group: Knowledge bases. Page: https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/create-knowledge-base Creates a knowledge base (KB) in your project. A KB is a named container of documents that an agent can retrieve from during a call. Provide a human-readable `name`, a stable machine `key` used to reference the KB from agents/tools, and a `desc` that tells the agent **when** to use this KB. The `key` must be unique within the project — a duplicate returns **409**. Returns the created KB, including its `id` and `document_count` (0 to start). Next steps: upload documents with `POST /api/ext/document/file`, then link the KB to an agent so it becomes retrievable at call time. ## Request - **name**: Human-readable display name for the knowledge base. 1–255 characters. Fields: `name`. - **key**: Stable machine identifier used to reference this KB from agents. Must match `^[A-Za-z_][A-Za-z0-9_]*$` (letters, digits, underscore; not starting with a digit), 1–100 chars, and be unique within the project. Fields: `key`. - **desc**: Short description of what the KB contains and when the agent should consult it. This guides retrieval, so be specific. Fields: `desc`. Duplicate `key` in the same project returns 409. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Create KB A simple product knowledge base. ```json { "name": "Product FAQ", "key": "product_faq", "desc": "Answers about product features, pricing, plans, and returns. Use for general product questions." } ``` ## Response ### 201 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "key": "string", "desc": "string", "document_count": 0, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/kb' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Product FAQ", "key": "product_faq", "desc": "Answers about product features, pricing, plans, and returns. Use for general product questions." }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/kb" headers = { "X-API-Key": "", } payload = { "name": "Product FAQ", "key": "product_faq", "desc": "Answers about product features, pricing, plans, and returns. Use for general product questions." } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/kb", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Product FAQ", "key": "product_faq", "desc": "Answers about product features, pricing, plans, and returns. Use for general product questions." }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Product FAQ", "key": "product_faq", "desc": "Answers about product features, pricing, plans, and returns. Use for general product questions." }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/kb", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List knowledge bases > GET https://api.nabrah.ai/api/ext/kb Group: Knowledge bases. Page: https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/list-knowledge-bases Lists all knowledge bases in your project, newest first. Each item includes `id`, `name`, `key`, `desc`, `document_count`, and timestamps. Use the `id` to fetch a KB's documents, or the `key` when referencing the KB from an agent. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json [ { "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "key": "string", "desc": "string", "document_count": 0, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ] ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/kb' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/kb" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/kb", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/kb", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get knowledge base > GET https://api.nabrah.ai/api/ext/kb/{knowledge_base_id} Group: Knowledge bases. Page: https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/get-knowledge-base Returns a single knowledge base by `id`, including a `documents` list — each with `id`, `type`, `status`, `filename`, and `created_at`. Use this to inspect a KB's contents and track each document's ingestion `status`. Returns **404** if the KB does not exist in your project. ## Document status Each document moves through the ingestion pipeline: `QUEUED → EXTRACTING → CHUNKING → EMBEDDING → INDEXING → DONE` (or `FAILED`). Only `DONE` documents are retrievable. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `knowledge_base_id` (string, required): Identifier of the knowledge base to fetch. From POST /api/ext/kb or GET /api/ext/kb. ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "key": "string", "desc": "string", "document_count": 0, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "documents": [ { "id": "00000000-0000-4000-8000-000000000000", "type": "file", "status": "unknown", "filename": "string", "created_at": "2026-01-15T12:00:00.000Z" } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/kb/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/kb/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/kb/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/kb/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Update knowledge base > PATCH https://api.nabrah.ai/api/ext/kb/{knowledge_base_id} Group: Knowledge bases. Page: https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/update-knowledge-base Updates a knowledge base's `name`, `key`, and/or `desc`. All fields are optional — send only what you want to change; an empty body is rejected. A changed `key` must remain unique in the project (**409** on conflict). Returns **404** if the KB does not exist. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `knowledge_base_id` (string, required): Identifier of the knowledge base to update. From POST /api/ext/kb or GET /api/ext/kb. ## Body (application/json) ### Example: Update description Refine when the agent should use this KB. ```json { "desc": "Answers about billing, invoices, and refunds only." } ``` ### Example: Rename Change the display name and key. ```json { "name": "Billing FAQ", "key": "billing_faq" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "key": "string", "desc": "string", "document_count": 0, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request PATCH \ --url 'https://api.nabrah.ai/api/ext/kb/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "desc": "Answers about billing, invoices, and refunds only." }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/kb/" headers = { "X-API-Key": "", } payload = { "desc": "Answers about billing, invoices, and refunds only." } response = requests.patch(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/kb/", { method: "PATCH", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "desc": "Answers about billing, invoices, and refunds only." }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "desc": "Answers about billing, invoices, and refunds only." }`) req, _ := http.NewRequest("PATCH", "https://api.nabrah.ai/api/ext/kb/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete knowledge base > DELETE https://api.nabrah.ai/api/ext/kb/{knowledge_base_id} Group: Knowledge bases. Page: https://www.nabrah.ai/en/docs/api-reference/knowledge-bases/delete-knowledge-base Permanently deletes a knowledge base **and all of its documents**, and removes their indexed vectors so they are no longer retrievable. Any agents linked to this KB are automatically unlinked. Returns **204 No Content**. This action is irreversible. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `knowledge_base_id` (string, required): Identifier of the knowledge base to delete. From POST /api/ext/kb or GET /api/ext/kb. ## Response ### 204 Successful Response ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/kb/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/kb/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/kb/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/kb/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Documents API Add documents to a knowledge base and track their ingestion. - [Add PDF document](https://www.nabrah.ai/en/docs/api-reference/documents/add-pdf-document.md) `POST /api/ext/document/file`: Uploads a PDF into a knowledge base and queues it for ingestion. - [Add text document](https://www.nabrah.ai/en/docs/api-reference/documents/add-text-document.md) `POST /api/ext/document/text`: Not yet available. - [Add URL document](https://www.nabrah.ai/en/docs/api-reference/documents/add-url-document.md) `POST /api/ext/document/url`: Not yet available. - [List documents](https://www.nabrah.ai/en/docs/api-reference/documents/list-documents.md) `GET /api/ext/document`: Lists documents across all knowledge bases in your project (deleted documents are excluded). - [Get document](https://www.nabrah.ai/en/docs/api-reference/documents/get-document.md) `GET /api/ext/document/{document_id}`: Returns a single document by id. - [Delete document](https://www.nabrah.ai/en/docs/api-reference/documents/delete-document.md) `DELETE /api/ext/document/{document_id}`: Removes a document from its knowledge base and deletes its indexed vectors so it is no longer retrievable. --- # Add PDF document > POST https://api.nabrah.ai/api/ext/document/file Group: Documents. Page: https://www.nabrah.ai/en/docs/api-reference/documents/add-pdf-document Uploads a **PDF** into a knowledge base and queues it for ingestion. Send `multipart/form-data` with a `knowledge_base_id` and a `file`. The file must be a valid PDF (content-checked), max **20 MB**. The document is created with status `QUEUED` and processed asynchronously — text extraction → chunking → embedding → vector indexing; poll `GET /api/ext/document/{document_id}` for `status`. Returns the created document. Only PDF is supported today (`/url` and `/text` are not yet available). ## Form fields (multipart/form-data) - **knowledge_base_id**: The target knowledge base `id` to attach the document to. Fields: `knowledge_base_id`. - **file**: The PDF file to ingest. Must be a valid PDF (verified by content), maximum 20 MB. Fields: `file`. The document returns immediately as `QUEUED` and is embedded in the background — it becomes retrievable once its status reaches `DONE`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (multipart/form-data) ## Response ### 201 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "type": "file", "status": "unknown", "title": "string", "summary": "string", "filename": "string", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/document/file' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/document/file" headers = {"X-API-Key": ""} response = requests.post(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const form = new FormData(); const response = await fetch("https://api.nabrah.ai/api/ext/document/file", { method: "POST", headers: { "X-API-Key": "" }, body: form, }); const data = await response.json(); console.log(data); ``` ### Go ```go // Send multipart/form-data with mime/multipart: // POST https://api.nabrah.ai/api/ext/document/file ``` --- # Add text document > POST https://api.nabrah.ai/api/ext/document/text Group: Documents. Page: https://www.nabrah.ai/en/docs/api-reference/documents/add-text-document **Not yet available.** Plain-text ingestion is not supported yet — this endpoint currently returns **501 Not Implemented**. To add content today, upload a PDF with `POST /api/ext/document/file`. (Planned: submit a `knowledge_base_id` + `text` to ingest raw text.) ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ```json { "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "text": "string" } ``` ## Response ### 201 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "type": "file", "status": "unknown", "title": "string", "summary": "string", "filename": "string", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/document/text' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "text": "string" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/document/text" headers = { "X-API-Key": "", } payload = { "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "text": "string" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/document/text", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "text": "string" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "text": "string" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/document/text", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Add URL document > POST https://api.nabrah.ai/api/ext/document/url Group: Documents. Page: https://www.nabrah.ai/en/docs/api-reference/documents/add-url-document **Not yet available.** URL ingestion is not supported yet — this endpoint currently returns **501 Not Implemented**. To add content today, upload a PDF with `POST /api/ext/document/file`. (Planned: submit a `knowledge_base_id` + `url` to fetch and ingest a web page.) ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ```json { "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "url": "string" } ``` ## Response ### 201 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "type": "file", "status": "unknown", "title": "string", "summary": "string", "filename": "string", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/document/url' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "url": "string" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/document/url" headers = { "X-API-Key": "", } payload = { "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "url": "string" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/document/url", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "url": "string" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "url": "string" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/document/url", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List documents > GET https://api.nabrah.ai/api/ext/document Group: Documents. Page: https://www.nabrah.ai/en/docs/api-reference/documents/list-documents Lists documents across all knowledge bases in your project (deleted documents are excluded). Each item includes `id`, `knowledge_base_id`, `type`, `status`, `title`, `filename`, and timestamps. Poll a document's `status` to track ingestion progress. ## Document status `QUEUED → EXTRACTING → CHUNKING → EMBEDDING → INDEXING → DONE` (or `FAILED`). Only `DONE` documents are used for retrieval. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json [ { "id": "00000000-0000-4000-8000-000000000000", "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "type": "file", "status": "unknown", "title": "string", "summary": "string", "filename": "string", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ] ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/document' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/document" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/document", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/document", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get document > GET https://api.nabrah.ai/api/ext/document/{document_id} Group: Documents. Page: https://www.nabrah.ai/en/docs/api-reference/documents/get-document Returns a single document by `id`. For file-backed documents the response includes a short-lived, pre-signed `file_url` (valid ~1 hour) you can use to download or preview the original file. Returns **404** if the document does not exist in your project. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `document_id` (string, required): Identifier of the document to fetch. From GET /api/ext/document or the add-document response. ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "knowledge_base_id": "00000000-0000-4000-8000-000000000000", "type": "file", "status": "unknown", "title": "string", "summary": "string", "filename": "string", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "file_url": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/document/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/document/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/document/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/document/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete document > DELETE https://api.nabrah.ai/api/ext/document/{document_id} Group: Documents. Page: https://www.nabrah.ai/en/docs/api-reference/documents/delete-document Removes a document from its knowledge base and deletes its indexed vectors so it is no longer retrievable. The document is soft-deleted; its stored original file is retained. Returns **204 No Content**. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `document_id` (string, required): Identifier of the document to delete. From GET /api/ext/document or the add-document response. ## Response ### 204 Successful Response ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/document/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/document/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/document/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/document/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Calls API Place outbound calls, then search, export and analyze call records and recordings. - [Create outbound call](https://www.nabrah.ai/en/docs/api-reference/calls/create-outbound-call.md) `POST /api/ext/call/make-call`: Create an outbound phone call. - [Get call](https://www.nabrah.ai/en/docs/api-reference/calls/get-call.md) `GET /api/ext/call/{call_id}`: Retrieve the complete record for a single call by its UUID. - [Search calls](https://www.nabrah.ai/en/docs/api-reference/calls/search-calls.md) `POST /api/ext/call/search`: Search historical call records with filtering and pagination. - [Get call analytics](https://www.nabrah.ai/en/docs/api-reference/calls/get-call-analytics.md) `POST /api/ext/call/analytics`: Compute aggregate analytics over historical calls that match a filter. - [Export calls](https://www.nabrah.ai/en/docs/api-reference/calls/export-calls.md) `POST /api/ext/call/export`: Export the historical calls matching a filter as a downloadable CSV file. - [Get recording download URL](https://www.nabrah.ai/en/docs/api-reference/calls/get-recording-download-url.md) `GET /api/ext/call/get-download-link/{call_id}`: Generate a temporary signed URL to download a call's audio recording. - [Create outbound call (legacy path)](https://www.nabrah.ai/en/docs/api-reference/calls/create-outbound-call-legacy-path.md) `POST /api/ext/make-call`: Backward-compatible alias of POST /api/ext/call/make-call — creates an outbound phone call with identical request and response shapes. --- # Create outbound call > POST https://api.nabrah.ai/api/ext/call/make-call Group: Calls. Page: https://www.nabrah.ai/en/docs/api-reference/calls/create-outbound-call Create an outbound phone call. You specify which agent handles the call (agent_id), the originating number (call_from), and the destination (call_to). call_to may be a single phone number or a list of destinations for batch dialing (up to 100). The response returns call_ids — always a list, with one element for a single call — which you can then poll with GET /api/ext/call/{call_id} or look up via search. This is the canonical outbound-call endpoint; /api/ext/make-call is an equivalent alias kept for backward compatibility. ## Request fields - **agent_id**: UUID of the agent that will conduct the call. Fetch available agents with GET /api/ext/agent. Fields: `agent_id`. - **call_from**: The phone number or identifier the call originates from. Must be a number provisioned to your project. Fields: `call_from`. - **call_to**: The destination. Use a single phone number for one call. (For batch dialing the API also accepts a list of destinations, up to 100, in which case call_ids returns one id per destination.) Fields: `call_to`. - **injected_data**: Optional free-form data injected into the call context. Only applied for single-destination calls. Fields: `injected_data`. - **variables**: Optional key/value map of template variables made available to the agent's prompt during the call. Fields: `variables`. - **tags**: Optional labels attached to the call(s); useful later as filters in search, analytics, and export. Fields: `tags`. ## Response - **call_ids**: List of created call UUIDs. For a single call it contains one id. Use these with GET /api/ext/call/{call_id} to track status and retrieve the transcript. Fields: `call_ids`. ## Related Aliases and follow-ups: - **/api/ext/make-call**: Identical backward-compatible alias of this endpoint. Prefer this canonical path (/api/ext/call/make-call). - **GET /api/ext/call/{call_id}**: Fetch the full record (status, transcript, analysis) for a returned call_id. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Single call Place one outbound call to a single number. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "call_from": "+966510000000", "call_to": "+966500000000" } ``` ### Example: With context Outbound call with injected data, prompt variables, and tags. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "call_from": "+966510000000", "call_to": "+966500000000", "injected_data": "Customer: Ahmed, order #4821 ready for pickup", "variables": { "customer_name": "Ahmed", "order_id": "4821" }, "tags": [ "pickup-reminder" ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "call_ids": [ "00000000-0000-4000-8000-000000000000" ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/call/make-call' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "agent_id": "00000000-0000-4000-8000-000000000000", "call_from": "+966510000000", "call_to": "+966500000000" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/call/make-call" headers = { "X-API-Key": "", } payload = { "agent_id": "00000000-0000-4000-8000-000000000000", "call_from": "+966510000000", "call_to": "+966500000000" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/call/make-call", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "agent_id": "00000000-0000-4000-8000-000000000000", "call_from": "+966510000000", "call_to": "+966500000000" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "agent_id": "00000000-0000-4000-8000-000000000000", "call_from": "+966510000000", "call_to": "+966500000000" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/call/make-call", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get call > GET https://api.nabrah.ai/api/ext/call/{call_id} Group: Calls. Page: https://www.nabrah.ai/en/docs/api-reference/calls/get-call Retrieve the complete record for a single call by its UUID. Returns the full call object including transcript, tool execution logs, analysis results, tags, and timing metadata. The call must belong to the project tied to your API key, otherwise a 404 is returned. Use this after you have a call_id from POST /api/ext/call/search, the analytics endpoint, or the response of a make-call request. ## Where the call_id comes from This endpoint needs a concrete call UUID. You can obtain one in several ways: - **From a search**: Call POST /api/ext/call/search and read calls[].id from the response. Fields: `id`. - **From creating a call**: POST /api/ext/call/make-call returns call_ids[]; use any of those values. Fields: `call_ids`. ## Response shape (FullCall) The 200 body is the complete call record. Key fields: - **status**: Current lifecycle state of the call. One of queued, ringing, in-progress, completed, failed, etc. A freshly created call typically starts as queued. - **call_type**: Direction of the call: inbound or outbound (web calls are also represented here). - **transcript**: Ordered list of conversation turns. Empty until the call has audio to transcribe. Fields: `transcript`. - **tool_logs**: Each entry records a tool invocation during the call: name, executed_at, success, the arguments passed, and the output returned. is_tool_deleted flags tools removed from the agent after the call. Fields: `executed_at`, `name`, `success`, `is_tool_deleted`, `tool_id`, `arguments`, `output`. - **analysis_results**: Post-call analysis grouped by source (system or a custom analysis group). Each group lists its results and the originating analysis_group_id. Fields: `source`, `name`, `results`, `is_analysis_group_deleted`, `analysis_group_id`. - **recording_file**: Internal reference to the recording. To download the audio, call GET /api/ext/call/get-download-link/{call_id} for a temporary signed URL rather than using this field directly. Fields: `recording_file`. duration is in seconds; call_started_at / call_ended_at are ISO-8601 timestamps and may be null for calls that never connected. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `call_id` (string, required): UUID of the call to fetch. Obtain from POST /api/ext/call/search (calls[].id) or from the call_ids[] returned by POST /api/ext/call/make-call. Must belong to the project scoped by your API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "call_from": "string", "call_to": "string", "call_type": "inbound", "status": "queued", "call_started_at": "2026-01-15T12:00:00.000Z", "call_ended_at": "2026-01-15T12:00:00.000Z", "duration": 0, "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "recording_file": "string", "transcript": [ {} ], "agent_name": "string", "variables": {}, "tool_logs": [ { "executed_at": "2026-01-15T12:00:00.000Z", "name": "string", "success": false, "is_tool_deleted": false, "tool_id": "00000000-0000-4000-8000-000000000000", "arguments": {}, "output": "string" } ], "analysis_results": [ { "source": "system", "name": "string", "results": [ {} ], "is_analysis_group_deleted": false, "analysis_group_id": "00000000-0000-4000-8000-000000000000" } ], "tags": [ "string" ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/call/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/call/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/call/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/call/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Search calls > POST https://api.nabrah.ai/api/ext/call/search Group: Calls. Page: https://www.nabrah.ai/en/docs/api-reference/calls/search-calls Search historical call records with filtering and pagination. Returns a page of matching calls plus the total count. This is the primary way to discover call_ids for use with GET /api/ext/call/{call_id} and the download-link endpoint. Send only the filter fields you need; all supplied filters are combined with AND. Results describe past calls, not live ones. ## Filtering Omit any filter you don't want to apply. - **Time windows**: call_started_at_start / call_started_at_end filter on call start time; created_at_start / created_at_end filter on record creation time. ISO-8601 timestamps. Fields: `call_started_at_start`, `call_started_at_end`, `created_at_start`, `created_at_end`. - **Scope**: agent_id restricts to one agent (from GET /api/ext/agent); call_from / call_to filter by number; status filters by lifecycle state (queued, ringing, in-progress, completed, failed); call_type filters inbound vs outbound; call_id fetches a specific record. Fields: `agent_id`, `call_from`, `call_to`, `status`, `call_type`, `call_id`. - **Tags and free text**: tags matches any of the listed tags; tag is a single-tag shortcut; q is a free-text search over call content. Fields: `tags`, `tag`, `q`. - **Analysis filters**: analysis_filters narrows by post-call analysis values; each entry is { name, op, value } with op such as ==, !=, >, <. Fields: `name`, `op`, `value`. ## Pagination - **limit / offset**: limit caps the number of returned rows; offset skips that many. Page through by increasing offset until offset reaches total_count. Fields: `limit`, `offset`. ## Response shape (SearchCallsResponse) The 200 body contains a page of summary rows plus paging metadata. - **calls[]**: Summary rows (id, call_from, call_to, call_type, status, timing, agent_id, agent_name, tags). For the full transcript/analysis of a row, call GET /api/ext/call/{call_id} with its id. Fields: `id`, `call_from`, `call_to`, `call_type`, `status`, `duration`, `call_started_at`, `call_ended_at`, `agent_id`, `agent_name`, `tags`. - **total_count / limit / offset**: total_count is the count of all matching calls; limit and offset echo the applied paging. Fields: `total_count`, `limit`, `offset`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Recent calls First page of the most recent calls. ```json { "limit": 25, "offset": 0 } ``` ### Example: Full filter All filters for one agent's completed outbound calls in a date range. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "call_to": "+966500000000", "call_from": "+966510000000", "call_started_at_start": "2026-06-01T00:00:00.000Z", "call_started_at_end": "2026-06-23T23:59:59.000Z", "status": "completed", "call_type": "outbound", "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z", "tags": [ "sales" ], "analysis_filters": [ { "name": "sentiment", "op": "==", "value": "positive" } ], "tag": "sales", "q": "appointment", "limit": 50, "offset": 0 } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "calls": [ { "id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z", "call_from": "string", "call_to": "string", "call_type": "inbound", "status": "queued", "updated_at": "2026-01-15T12:00:00.000Z", "duration": 0, "call_started_at": "2026-01-15T12:00:00.000Z", "call_ended_at": "2026-01-15T12:00:00.000Z", "agent_id": "00000000-0000-4000-8000-000000000000", "agent_name": "string", "tags": [ "string" ] } ], "total_count": 0, "limit": 0, "offset": 0 } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/call/search' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "limit": 25, "offset": 0 }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/call/search" headers = { "X-API-Key": "", } payload = { "limit": 25, "offset": 0 } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/call/search", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "limit": 25, "offset": 0 }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "limit": 25, "offset": 0 }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/call/search", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get call analytics > POST https://api.nabrah.ai/api/ext/call/analytics Group: Calls. Page: https://www.nabrah.ai/en/docs/api-reference/calls/get-call-analytics Compute aggregate analytics over historical calls that match a filter. Accepts the same SearchCallsRequest body as the search endpoint, but instead of returning individual call rows it returns summarized analytics for the matching set (counts, durations, status breakdowns, and similar metrics). Use it to power dashboards and reporting over a date range, agent, status, or tag rather than to page through raw calls. ## Filtering Send only the fields you want to constrain on; omit the rest. All filters are combined (AND). Same body as POST /api/ext/call/search. - **Time windows**: call_started_at_start / call_started_at_end filter by when the call actually started; created_at_start / created_at_end filter by when the record was created. Use ISO-8601 timestamps. Fields: `call_started_at_start`, `call_started_at_end`, `created_at_start`, `created_at_end`. - **Scope by agent / number / status**: agent_id restricts to one agent; call_from and call_to filter by phone number; status filters by lifecycle state (queued, ringing, in-progress, completed, failed); call_type filters inbound vs outbound. Fields: `agent_id`, `call_from`, `call_to`, `status`, `call_type`. - **Tags and free text**: tags matches calls carrying any of the listed tags; tag is a single-tag convenience filter; q is a free-text search across call content. Fields: `tags`, `tag`, `q`. - **Analysis filters**: analysis_filters constrains by post-call analysis values. Each entry is { name, op, value } where op is a comparison operator such as ==, !=, >, <. Fields: `name`, `op`, `value`. limit and offset are accepted for parity with search but have no effect on aggregate totals; analytics are computed over the entire matching set. ## Getting an agent_id To filter by agent, fetch the project's agents first. - **GET /api/ext/agent**: Lists the agents in your project; use an agent's id as agent_id here. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Minimal Analytics over a date range with no other constraints. ```json { "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" } ``` ### Example: Full All available filters for a single agent's completed outbound calls. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "call_to": "+966500000000", "call_from": "+966510000000", "call_started_at_start": "2026-06-01T00:00:00.000Z", "call_started_at_end": "2026-06-23T23:59:59.000Z", "status": "completed", "call_type": "outbound", "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z", "tags": [ "sales", "follow-up" ], "analysis_filters": [ { "name": "sentiment", "op": "==", "value": "positive" } ], "tag": "sales", "q": "appointment" } ``` ## Response ### 200 Successful Response Content type: `application/json` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/call/analytics' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/call/analytics" headers = { "X-API-Key": "", } payload = { "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/call/analytics", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/call/analytics", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Export calls > POST https://api.nabrah.ai/api/ext/call/export Group: Calls. Page: https://www.nabrah.ai/en/docs/api-reference/calls/export-calls Export the historical calls matching a filter as a downloadable CSV file. The response is a complete CSV encoded with a UTF-8 BOM so Arabic and other non-ASCII text render correctly in Excel. Accepts the same SearchCallsRequest filter body as the search and analytics endpoints. Use it to extract call data for offline reporting or import into spreadsheets and BI tools. ## Filtering The body is a SearchCallsRequest; send only the fields you want to constrain. Everything matching the filter is exported. - **Time windows**: call_started_at_start / call_started_at_end filter on call start time; created_at_start / created_at_end filter on record creation time. Fields: `call_started_at_start`, `call_started_at_end`, `created_at_start`, `created_at_end`. - **Scope**: agent_id, call_from, call_to, status, call_type, tags/tag, and q work exactly as in POST /api/ext/call/search. Fields: `agent_id`, `call_from`, `call_to`, `status`, `call_type`, `tags`, `tag`, `q`. - **Analysis filters**: analysis_filters restricts by analysis values; each entry is { name, op, value }. Fields: `name`, `op`, `value`. The endpoint returns the full filtered set as one CSV; limit/offset are not used for row windowing here. The file is UTF-8 with a BOM — open it directly in Excel for correct Arabic display. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Minimal Export every call created in a date range. ```json { "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" } ``` ### Example: Full Export completed outbound calls for one agent, filtered by tag and analysis. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "call_to": "+966500000000", "call_from": "+966510000000", "call_started_at_start": "2026-06-01T00:00:00.000Z", "call_started_at_end": "2026-06-23T23:59:59.000Z", "status": "completed", "call_type": "outbound", "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z", "tags": [ "sales" ], "analysis_filters": [ { "name": "resolved", "op": "==", "value": "true" } ], "tag": "sales", "q": "refund" } ``` ## Response ### 200 Successful Response Content type: `application/json` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/call/export' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/call/export" headers = { "X-API-Key": "", } payload = { "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/call/export", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "created_at_start": "2026-06-01T00:00:00.000Z", "created_at_end": "2026-06-23T23:59:59.000Z" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/call/export", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get recording download URL > GET https://api.nabrah.ai/api/ext/call/get-download-link/{call_id} Group: Calls. Page: https://www.nabrah.ai/en/docs/api-reference/calls/get-recording-download-url Generate a temporary signed URL to download a call's audio recording. The returned URL is valid for 15 minutes and points directly to the recording file. Returns 404 if the call does not exist in your project or has no recording (for example, a call that never connected). Use this instead of reading the recording_file field on the call record, which is an internal reference and not directly downloadable. ## Response - **url**: A signed, time-limited download link for the recording. Fetch or stream it within 15 minutes; after expiry, call this endpoint again to mint a fresh URL. Fields: `url`. ## Where the call_id comes from - **Search or make-call**: Use calls[].id from POST /api/ext/call/search, or a value from call_ids[] returned by POST /api/ext/call/make-call. Recordings only exist for calls that produced audio; expect a 404 for queued, ringing, or failed-to-connect calls. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `call_id` (string, required): UUID of the call whose recording you want to download. Obtain from POST /api/ext/call/search (calls[].id) or the call_ids[] of a make-call response. Must belong to the API key's project and have a recording. ## Response ### 200 Successful Response Content type: `application/json` ```json { "url": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/call/get-download-link/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/call/get-download-link/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/call/get-download-link/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/call/get-download-link/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Web calls API Start a browser voice session with an agent and get the token your frontend joins with. - [Create web call](https://www.nabrah.ai/en/docs/api-reference/web-calls/create-web-call.md) `POST /api/ext/web/make-web-call`: Create a web call: start a browser/WebRTC voice session with an agent and receive a connection token. - [Create web call (legacy path)](https://www.nabrah.ai/en/docs/api-reference/web-calls/create-web-call-legacy-path.md) `POST /api/ext/make-web-call`: Backward-compatible alias of POST /api/ext/web/make-web-call — starts a browser/WebRTC voice session with an agent and returns a connection token. --- # Create web call > POST https://api.nabrah.ai/api/ext/web/make-web-call Group: Web calls. Page: https://www.nabrah.ai/en/docs/api-reference/web-calls/create-web-call Create a web call: start a browser/WebRTC voice session with an agent and receive a connection token. Designed to be called from your frontend so a user can talk to the agent directly in the browser. Provide the agent_id and optionally an identity for the participant, prompt variables, and free-form extra_data; the returned token is used by your WebRTC client to join the live audio session. This is the canonical web-call endpoint; /api/ext/make-web-call is an equivalent alias kept for backward compatibility. ## Request fields - **agent_id**: UUID of the agent that will answer the web call. Fetch agents with GET /api/ext/agent. Fields: `agent_id`. - **identity**: Optional participant identity for the realtime session — useful to associate the connection with a known user. Fields: `identity`. - **variables**: Optional key/value map of template variables exposed to the agent's prompt during the session. Fields: `variables`. - **extra_data**: Optional free-form data injected into the call context. Fields: `extra_data`. ## Response - **token**: Connection token returned for joining the live voice session from a browser/WebRTC client. Short-lived; mint a new one per session. Fields: `token`. ## Related - **/api/ext/make-web-call**: Identical backward-compatible alias. Prefer this canonical path (/api/ext/web/make-web-call). - **GET /api/ext/call/{call_id}**: After the session, the web call appears as a call record retrievable by its id. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Minimal Start a web call with just an agent. ```json { "agent_id": "00000000-0000-4000-8000-000000000000" } ``` ### Example: Full Web call with participant identity, variables, and extra context. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "identity": "user-12345", "variables": { "customer_name": "Ahmed" }, "extra_data": "Logged-in user, premium tier" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "token": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/web/make-web-call' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "agent_id": "00000000-0000-4000-8000-000000000000" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/web/make-web-call" headers = { "X-API-Key": "", } payload = { "agent_id": "00000000-0000-4000-8000-000000000000" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/web/make-web-call", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "agent_id": "00000000-0000-4000-8000-000000000000" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "agent_id": "00000000-0000-4000-8000-000000000000" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/web/make-web-call", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # SIP inbound API Manage the inbound SIP trunks that bring calls to your numbers into Nabrah. - [Create SIP inbound trunk](https://www.nabrah.ai/en/docs/api-reference/sip-inbound/create-sip-inbound-trunk.md) `POST /api/ext/sip-inbound`: Creates a new inbound SIP trunk for the project tied to your API key, so external SIP/phone calls can be terminated into Nabrah and routed to an agent. - [List SIP inbound trunks](https://www.nabrah.ai/en/docs/api-reference/sip-inbound/list-sip-inbound-trunks.md) `GET /api/ext/sip-inbound`: Lists every inbound SIP trunk configured for the project that owns your API key. - [Get SIP inbound trunk](https://www.nabrah.ai/en/docs/api-reference/sip-inbound/get-sip-inbound-trunk.md) `GET /api/ext/sip-inbound/{sip_id}`: Retrieves a single inbound SIP trunk by its id from the project tied to your API key. - [Delete SIP inbound trunk](https://www.nabrah.ai/en/docs/api-reference/sip-inbound/delete-sip-inbound-trunk.md) `DELETE /api/ext/sip-inbound/{sip_id}`: Permanently deletes a single inbound SIP trunk from the project tied to your API key. --- # Create SIP inbound trunk > POST https://api.nabrah.ai/api/ext/sip-inbound Group: SIP inbound. Page: https://www.nabrah.ai/en/docs/api-reference/sip-inbound/create-sip-inbound-trunk Creates a new inbound SIP trunk for the project tied to your API key, so external SIP/phone calls can be terminated into Nabrah and routed to an agent. You supply a display name, the phone number(s) the trunk should accept, an optional allow-list of source IP addresses, and the SIP digest credentials your provider will use to authenticate. The credentials are stored per-project. On success the API returns the created trunk including its generated `id`, which you use to fetch or delete the trunk later. Authenticate with the `X-API-Key` header. ## Connection fields Provide these exactly as defined in the request body. - **name**: Human-readable label for the trunk, shown in the dashboard and in list responses. - **numbers**: The phone number(s) this inbound trunk accepts calls on, in E.164 format (e.g. +14155552671). Calls arriving on these numbers are answered by Nabrah. - **allowed_addresses**: Optional allow-list of source SIP/IP addresses permitted to deliver calls to this trunk. Restricts which hosts your provider may signal from; leave empty to accept any authenticated source. - **auth_username / auth_password**: SIP digest credentials your provider presents when delivering inbound calls. Stored and scoped to the project that owns the API key. - **pbx_type**: Identifies the SIP/PBX backend the trunk is provisioned against. ## After creation Capture the returned `id`; it is the `sip_id` path parameter for GET /api/ext/sip-inbound/{sip_id} and DELETE /api/ext/sip-inbound/{sip_id}. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Inbound trunk Create an inbound SIP trunk with digest credentials and a source allow-list. ```json { "name": "Support line (US)", "numbers": "+14155552671", "allowed_addresses": [ "203.0.113.10" ], "auth_username": "nabrah_inbound_us", "auth_password": "s3cure-Inbound-Pass!", "pbx_type": "lk" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "numbers": "string", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "allowed_addresses": [ "string" ], "auth_username": "string", "auth_password": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/sip-inbound' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Support line (US)", "numbers": "+14155552671", "allowed_addresses": [ "203.0.113.10" ], "auth_username": "nabrah_inbound_us", "auth_password": "s3cure-Inbound-Pass!", "pbx_type": "lk" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/sip-inbound" headers = { "X-API-Key": "", } payload = { "name": "Support line (US)", "numbers": "+14155552671", "allowed_addresses": [ "203.0.113.10" ], "auth_username": "nabrah_inbound_us", "auth_password": "s3cure-Inbound-Pass!", "pbx_type": "lk" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/sip-inbound", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Support line (US)", "numbers": "+14155552671", "allowed_addresses": [ "203.0.113.10" ], "auth_username": "nabrah_inbound_us", "auth_password": "s3cure-Inbound-Pass!", "pbx_type": "lk" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Support line (US)", "numbers": "+14155552671", "allowed_addresses": [ "203.0.113.10" ], "auth_username": "nabrah_inbound_us", "auth_password": "s3cure-Inbound-Pass!", "pbx_type": "lk" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/sip-inbound", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List SIP inbound trunks > GET https://api.nabrah.ai/api/ext/sip-inbound Group: SIP inbound. Page: https://www.nabrah.ai/en/docs/api-reference/sip-inbound/list-sip-inbound-trunks Lists every inbound SIP trunk configured for the project that owns your API key. Inbound trunks let incoming SIP/phone calls reach a Nabrah agent: when a call arrives on one of the trunk's numbers, it is answered and routed to the bound agent. The response includes each trunk's id (use it as the path parameter for the GET/DELETE single-trunk endpoints), its display name, associated phone numbers, allowed source addresses, and the SIP digest credentials stored for the project. Authenticate with the `X-API-Key` header; the project is inferred from the key, so no project_id is required. ## What inbound trunks do Inbound trunks terminate calls into your project. - **Direction**: Inbound means Nabrah receives calls. A telephony/SIP provider delivers a call to a Nabrah number and the configured agent answers it. This is distinct from outbound trunks, which let an agent place calls out through your own provider. - **Credentials are per-project**: The `auth_username` and `auth_password` returned here are the digest credentials your SIP provider uses to authenticate when delivering calls to Nabrah. They are stored and scoped to the project tied to your API key. ## Listing returns full records Each item in `items` is a complete SipInboundResponse. To retrieve one trunk on its own, call GET /api/ext/sip-inbound/{sip_id} with an `id` value taken from this list. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "items": [ { "name": "string", "numbers": "string", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "allowed_addresses": [ "string" ], "auth_username": "string", "auth_password": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/sip-inbound' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/sip-inbound" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/sip-inbound", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/sip-inbound", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get SIP inbound trunk > GET https://api.nabrah.ai/api/ext/sip-inbound/{sip_id} Group: SIP inbound. Page: https://www.nabrah.ai/en/docs/api-reference/sip-inbound/get-sip-inbound-trunk Retrieves a single inbound SIP trunk by its id from the project tied to your API key. Returns the full configuration: display name, accepted phone number(s), allowed source addresses, and the stored SIP digest credentials. Use this to inspect one trunk's settings rather than listing all of them. Authenticate with the `X-API-Key` header. ## Scope Only trunks belonging to the project that owns your API key are accessible. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `sip_id` (string, required): The unique identifier of the inbound SIP trunk to retrieve. Obtain it from the `id` field of GET /api/ext/sip-inbound (list). ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "numbers": "string", "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "allowed_addresses": [ "string" ], "auth_username": "string", "auth_password": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/sip-inbound/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/sip-inbound/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/sip-inbound/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/sip-inbound/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete SIP inbound trunk > DELETE https://api.nabrah.ai/api/ext/sip-inbound/{sip_id} Group: SIP inbound. Page: https://www.nabrah.ai/en/docs/api-reference/sip-inbound/delete-sip-inbound-trunk Permanently deletes a single inbound SIP trunk from the project tied to your API key. After deletion, calls to the trunk's number(s) can no longer be terminated into Nabrah or routed to the bound agent. A successful request returns 204 No Content with no body. Authenticate with the `X-API-Key` header. ## Irreversible Deletion cannot be undone. To restore inbound calling you must create a new trunk via POST /api/ext/sip-inbound. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `sip_id` (string, required): The unique identifier of the inbound SIP trunk to delete. Obtain it from the `id` field of GET /api/ext/sip-inbound (list) or GET /api/ext/sip-inbound/{sip_id} (single). ## Response ### 204 Successful Response ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/sip-inbound/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/sip-inbound/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/sip-inbound/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/sip-inbound/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # SIP outbound API Manage the outbound SIP trunks your agents place calls through. - [Create SIP outbound trunk](https://www.nabrah.ai/en/docs/api-reference/sip-outbound/create-sip-outbound-trunk.md) `POST /api/ext/sip-outbound`: Creates a new outbound SIP trunk for the project tied to your API key, enabling agents to place calls out through your own SIP provider. - [List SIP outbound trunks](https://www.nabrah.ai/en/docs/api-reference/sip-outbound/list-sip-outbound-trunks.md) `GET /api/ext/sip-outbound`: Lists every outbound SIP trunk configured for the project that owns your API key. - [Get SIP outbound trunk](https://www.nabrah.ai/en/docs/api-reference/sip-outbound/get-sip-outbound-trunk.md) `GET /api/ext/sip-outbound/{sip_id}`: Retrieves a single outbound SIP trunk by its id from the project tied to your API key. - [Delete SIP outbound trunk](https://www.nabrah.ai/en/docs/api-reference/sip-outbound/delete-sip-outbound-trunk.md) `DELETE /api/ext/sip-outbound/{sip_id}`: Permanently deletes a single outbound SIP trunk from the project tied to your API key. --- # Create SIP outbound trunk > POST https://api.nabrah.ai/api/ext/sip-outbound Group: SIP outbound. Page: https://www.nabrah.ai/en/docs/api-reference/sip-outbound/create-sip-outbound-trunk Creates a new outbound SIP trunk for the project tied to your API key, enabling agents to place calls out through your own SIP provider. You supply a display name, the caller phone number(s), the provider's SIP address (host/URI to send calls to), a maximum concurrent channel limit, and the SIP digest credentials Nabrah will use to authenticate against your provider. These credentials are stored per-project. On success the API returns the created trunk including its generated `id`. Authenticate with the `X-API-Key` header. ## Connection fields Provide these exactly as defined in the request body. - **name**: Human-readable label for the trunk, shown in the dashboard and list responses. - **numbers**: The phone number(s) used as the outbound caller identity (caller ID), in E.164 format (e.g. +14155552671). - **address**: The SIP host/URI of your provider that Nabrah signals to when placing outbound calls (e.g. sip.yourprovider.com or sip.yourprovider.com:5060). This is the destination trunk endpoint. - **max_channels**: Maximum number of simultaneous outbound calls (concurrent channels) allowed on this trunk. Caps concurrency so you do not exceed your provider's capacity. - **auth_username / auth_password**: SIP digest credentials Nabrah presents to your provider when authenticating outbound calls. Stored and scoped to the project that owns the API key. - **pbx_type**: Identifies the SIP/PBX backend the trunk is provisioned against. ## After creation Capture the returned `id`; it is the `sip_id` path parameter for GET /api/ext/sip-outbound/{sip_id} and DELETE /api/ext/sip-outbound/{sip_id}. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Outbound trunk Create an outbound SIP trunk pointing at your provider with digest credentials and a concurrency cap. ```json { "name": "Sales dialer (US)", "numbers": "+14155552671", "address": "sip.yourprovider.com:5060", "max_channels": 10, "auth_username": "nabrah_outbound_us", "auth_password": "s3cure-Outbound-Pass!", "pbx_type": "lk" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "numbers": "string", "address": "string", "max_channels": 0, "id": "00000000-0000-4000-8000-000000000000", "created_by": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "auth_username": "string", "auth_password": "string", "created_by_email": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/sip-outbound' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Sales dialer (US)", "numbers": "+14155552671", "address": "sip.yourprovider.com:5060", "max_channels": 10, "auth_username": "nabrah_outbound_us", "auth_password": "s3cure-Outbound-Pass!", "pbx_type": "lk" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/sip-outbound" headers = { "X-API-Key": "", } payload = { "name": "Sales dialer (US)", "numbers": "+14155552671", "address": "sip.yourprovider.com:5060", "max_channels": 10, "auth_username": "nabrah_outbound_us", "auth_password": "s3cure-Outbound-Pass!", "pbx_type": "lk" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/sip-outbound", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Sales dialer (US)", "numbers": "+14155552671", "address": "sip.yourprovider.com:5060", "max_channels": 10, "auth_username": "nabrah_outbound_us", "auth_password": "s3cure-Outbound-Pass!", "pbx_type": "lk" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Sales dialer (US)", "numbers": "+14155552671", "address": "sip.yourprovider.com:5060", "max_channels": 10, "auth_username": "nabrah_outbound_us", "auth_password": "s3cure-Outbound-Pass!", "pbx_type": "lk" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/sip-outbound", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List SIP outbound trunks > GET https://api.nabrah.ai/api/ext/sip-outbound Group: SIP outbound. Page: https://www.nabrah.ai/en/docs/api-reference/sip-outbound/list-sip-outbound-trunks Lists every outbound SIP trunk configured for the project that owns your API key. Outbound trunks let Nabrah agents place calls out through your own SIP provider using stored trunk credentials. Each item includes the trunk's id (use it as the path parameter for the single-trunk GET/DELETE endpoints), its display name, and the associated phone number(s) used as caller identity. Authenticate with the `X-API-Key` header; the project is inferred from the key, so no project_id is required. ## What outbound trunks do Outbound trunks let agents dial out. - **Direction**: Outbound means Nabrah originates calls. An agent places a call that egresses through your SIP provider over this trunk. This is the inverse of inbound trunks, which receive calls into an agent. - **Summary listing**: The list response is intentionally compact, returning only `id`, `name`, and `numbers` per trunk. Fetch a single trunk for the full record including the provider address, channel limit, and credentials. To retrieve one trunk on its own, call GET /api/ext/sip-outbound/{sip_id} with an `id` value taken from this list. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "items": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "numbers": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/sip-outbound' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/sip-outbound" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/sip-outbound", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/sip-outbound", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get SIP outbound trunk > GET https://api.nabrah.ai/api/ext/sip-outbound/{sip_id} Group: SIP outbound. Page: https://www.nabrah.ai/en/docs/api-reference/sip-outbound/get-sip-outbound-trunk Retrieves a single outbound SIP trunk by its id from the project tied to your API key. Returns the full configuration: display name, caller number(s), the provider SIP address, the maximum concurrent channel limit, and the stored SIP digest credentials. Use this to inspect one outbound trunk's settings rather than listing all of them. Authenticate with the `X-API-Key` header. ## Scope Only trunks belonging to the project that owns your API key are accessible. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `sip_id` (string, required): The unique identifier of the outbound SIP trunk to retrieve. Obtain it from the `id` field of GET /api/ext/sip-outbound (list). ## Response ### 200 Successful Response Content type: `application/json` ```json { "name": "string", "numbers": "string", "address": "string", "max_channels": 0, "id": "00000000-0000-4000-8000-000000000000", "created_by": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "auth_username": "string", "auth_password": "string", "created_by_email": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/sip-outbound/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/sip-outbound/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/sip-outbound/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/sip-outbound/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete SIP outbound trunk > DELETE https://api.nabrah.ai/api/ext/sip-outbound/{sip_id} Group: SIP outbound. Page: https://www.nabrah.ai/en/docs/api-reference/sip-outbound/delete-sip-outbound-trunk Permanently deletes a single outbound SIP trunk from the project tied to your API key. After deletion, agents can no longer place outbound calls through this trunk. A successful request returns 204 No Content with no body. Authenticate with the `X-API-Key` header. ## Irreversible Deletion cannot be undone. To restore outbound calling you must create a new trunk via POST /api/ext/sip-outbound. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `sip_id` (string, required): The unique identifier of the outbound SIP trunk to delete. Obtain it from the `id` field of GET /api/ext/sip-outbound (list) or GET /api/ext/sip-outbound/{sip_id} (single). ## Response ### 204 Successful Response ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/sip-outbound/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/sip-outbound/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/sip-outbound/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/sip-outbound/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Agent inbound links API Choose which agent answers the calls that arrive on each inbound trunk. - [Link an agent to an inbound trunk](https://www.nabrah.ai/en/docs/api-reference/agent-inbound-links/link-an-agent-to-an-inbound-trunk.md) `POST /api/ext/agent-inbound-link`: Links an agent to a SIP inbound trunk so the agent answers calls arriving on that trunk. - [List agent inbound links](https://www.nabrah.ai/en/docs/api-reference/agent-inbound-links/list-agent-inbound-links.md) `GET /api/ext/agent-inbound-link`: Returns every agent↔inbound-trunk link in the workspace tied to your API key. - [Delete agent inbound link](https://www.nabrah.ai/en/docs/api-reference/agent-inbound-links/delete-agent-inbound-link.md) `DELETE /api/ext/agent-inbound-link/{link_id}`: Removes a single agent↔trunk association identified by link_id. --- # Link an agent to an inbound trunk > POST https://api.nabrah.ai/api/ext/agent-inbound-link Group: Agent inbound links. Page: https://www.nabrah.ai/en/docs/api-reference/agent-inbound-links/link-an-agent-to-an-inbound-trunk Links an agent to a SIP inbound trunk so the agent answers calls arriving on that trunk. Both the agent and the trunk must already exist in the workspace behind your API key — create them with POST /api/ext/agent and POST /api/ext/sip-inbound. Save the returned `link_id` to de-link later via DELETE /api/ext/agent-inbound-link/{link_id}. ## Where the IDs come from - **agent_id and inbound_id**: `agent_id` comes from GET /api/ext/agent or the create response of POST /api/ext/agent. `inbound_id` comes from GET /api/ext/sip-inbound or the create response of POST /api/ext/sip-inbound. Fields: `agent_id`, `inbound_id`. ## When this returns 400 All three of these produce the same 400 response, so check them in order. - **Agent or trunk not in this workspace**: Both IDs are validated against the workspace behind your API key. An ID belonging to another workspace is rejected exactly like a non-existent one. - **The link already exists**: The same agent cannot be linked to the same trunk twice. Call GET /api/ext/agent-inbound-link first if you need to know whether a pair is already linked. ## Many-to-many - **Several agents on one trunk**: A trunk may carry several agents and an agent may serve several trunks — call this endpoint once per pair. Which agent answers a given call is decided at call time by the voice runtime, not by this API. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Link agent to trunk Attach one agent to one SIP inbound trunk. ```json { "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "inbound_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d5a60" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "link_id": "00000000-0000-4000-8000-000000000000", "agent": { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "string" }, "sip_inbound": { "id": "00000000-0000-4000-8000-000000000000", "numbers": "string" }, "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/agent-inbound-link' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "inbound_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d5a60" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent-inbound-link" headers = { "X-API-Key": "", } payload = { "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "inbound_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d5a60" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent-inbound-link", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "inbound_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d5a60" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "agent_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d4e5f", "inbound_id": "018f3a2b-9c4d-7e1a-bf52-0a1b2c3d5a60" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/agent-inbound-link", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List agent inbound links > GET https://api.nabrah.ai/api/ext/agent-inbound-link Group: Agent inbound links. Page: https://www.nabrah.ai/en/docs/api-reference/agent-inbound-links/list-agent-inbound-links Returns every agent↔inbound-trunk link in the workspace tied to your API key. A link is what makes an agent answer calls arriving on a SIP inbound trunk. The relationship is many-to-many: one trunk can carry several agents, and one agent can serve several trunks. Each item carries the `link_id` you need to remove it later via DELETE /api/ext/agent-inbound-link/{link_id}. ## What each item contains - **link_id**: The identifier of the association itself — not the agent and not the trunk. Pass it to DELETE /api/ext/agent-inbound-link/{link_id} to de-link. Fields: `link_id`. - **agent and sip_inbound**: `agent` echoes the linked agent's `agent_id` and `name`; `sip_inbound` echoes the trunk's `id` and its `numbers` string. Use these to tell links apart without extra lookups. Fields: `agent`, `sip_inbound`. ## Scope - **Workspace only**: Only links belonging to the workspace behind your API key are returned. There is no pagination — the full list comes back in one response. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "links": [ { "link_id": "00000000-0000-4000-8000-000000000000", "agent": { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "string" }, "sip_inbound": { "id": "00000000-0000-4000-8000-000000000000", "numbers": "string" }, "project_id": "00000000-0000-4000-8000-000000000000", "created_at": "2026-01-15T12:00:00.000Z" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/agent-inbound-link' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent-inbound-link" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent-inbound-link", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/agent-inbound-link", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete agent inbound link > DELETE https://api.nabrah.ai/api/ext/agent-inbound-link/{link_id} Group: Agent inbound links. Page: https://www.nabrah.ai/en/docs/api-reference/agent-inbound-links/delete-agent-inbound-link Removes a single agent↔trunk association identified by `link_id`. This de-links one agent from one trunk without deleting either the agent or the trunk. On success the endpoint returns `{ "success": true }`. ## Where link_id comes from A link_id identifies the association, not the agent or the trunk. - **Sources of link_id**: Captured from the response of POST /api/ext/agent-inbound-link, or read from GET /api/ext/agent-inbound-link where each item carries its `link_id`. - **Unknown or foreign link_id**: A link that does not exist, or belongs to another workspace, returns 404. A malformed UUID returns 422. ## De-link before deleting a trunk - **Trunk deletion is blocked while links exist**: DELETE /api/ext/sip-inbound/{sip_id} refuses with 400 ("Cannot delete SIP Inbound that is linked to agents") for as long as any agent is linked. Remove every link with this endpoint first, then delete the trunk. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `link_id` (string, required): UUID of the agent↔trunk link to remove. Get this from POST /api/ext/agent-inbound-link or from GET /api/ext/agent-inbound-link. ## Response ### 200 Successful Response Content type: `application/json` ```json { "success": false } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/agent-inbound-link/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/agent-inbound-link/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/agent-inbound-link/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/agent-inbound-link/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Text to speech API Generate natural Arabic and English speech, and manage the voices you generate with. - [Generate speech](https://www.nabrah.ai/en/docs/api-reference/text-to-speech/generate-speech.md) `POST /api/ext/tts/generations`: Generate speech from text using the OpenAI-compatible TTS endpoint at POST /api/ext/tts/generations. - [List voices](https://www.nabrah.ai/en/docs/api-reference/text-to-speech/list-voices.md) `GET /api/ext/tts/voices`: Lists every voice available to the project tied to your API key. - [Get voice](https://www.nabrah.ai/en/docs/api-reference/text-to-speech/get-voice.md) `GET /api/ext/tts/voices/{voice_id}`: Fetch the full details of a single voice by its ID. - [Clone a voice](https://www.nabrah.ai/en/docs/api-reference/text-to-speech/clone-a-voice.md) `POST /api/ext/tts/clone_voice`: Clone a voice from an uploaded audio sample. - [Delete cloned voice](https://www.nabrah.ai/en/docs/api-reference/text-to-speech/delete-cloned-voice.md) `DELETE /api/ext/tts/voices/{voice_id}`: Delete a voice clone by its ID. --- # Generate speech > POST https://api.nabrah.ai/api/ext/tts/generations Group: Text to speech. Page: https://www.nabrah.ai/en/docs/api-reference/text-to-speech/generate-speech Generate speech from text using the OpenAI-compatible TTS endpoint at POST /api/ext/tts/generations. Put the text in `input` and the target voice id in `voice` (see “Choosing the voice”). `response_format` picks the encoding — `mp3`, `wav` or `pcm` — and `stream` picks the delivery: false returns one complete file, true streams the audio as it is produced. Every format works in both modes. Switch the scenario tab in “Try it” to load a matching body. ## Formats All three encodings are available whether or not you stream. `model` is always `nabrah_tts_v1`. - **mp3 — `audio/mpeg`**: Compressed audio at 192 kbps. The smallest payload and the easiest to play in a browser. This is the default when `stream` is false or omitted. Fields: `response_format`. - **wav — `audio/wav`**: Uncompressed 16-bit PCM in a RIFF container, 48 kHz mono. Lossless and self-describing — the header carries the sample rate and channel count. This is the default when `stream` is true. Fields: `response_format`. - **pcm — `audio/L16; rate=48000; channels=1`**: Raw signed 16-bit little-endian mono samples at 48 kHz, with no container and no header at all. Use it when you are feeding an audio pipeline that wants frames directly; you must already know the sample rate, since nothing in the payload states it. Fields: `response_format`. Omit `response_format` and you get `mp3` for a buffered request or `wav` for a streamed one. ## Delivery modes `stream` controls when the bytes reach you, not what they contain. - **stream false (default) — complete file**: The response arrives once synthesis has finished. A `wav` response carries correct length fields, so it can be written straight to disk. The same rules apply to POST /api/ext/tts/quick_generate. Fields: `stream`, `response_format`, `use_cache`. - **stream true — progressive**: Bytes are forwarded as the engine produces them, so playback can start before synthesis finishes. `speed` is forced to 1.0 while streaming, and `apply_enhancements` must be `none`. Fields: `stream`, `response_format`. Generated audio is cached for a short period. A cache hit is marked with the `X-Cache: HIT` response header; send `use_cache: false` to force a fresh generation. ## Reading a streamed response Set `stream: true` and send a matching `Accept` header — `audio/wav`, `audio/mpeg` or `audio/L16`. Do not await the whole payload; read the response body as a stream while the connection stays open. - **Read chunks as they arrive**: In a browser read them with `response.body.getReader()`; on a server pipe the response stream straight to a file or audio sink. The first bytes typically arrive within a few hundred milliseconds. Fields: `model`, `input`, `voice`, `stream`, `response_format`. - **The streamed WAV header**: A streamed clip cannot know its own length up front, so the first 44 bytes are a WAV header whose two size fields are set to the conventional `0xFFFFFFFF` unknown-length marker. Players read until the connection closes. If you write the stream to a file and need exact sizes, rewrite bytes 4–7 and 40–43 once you know the total, or request `stream: false` instead. `mp3` and `pcm` have no such header and stream through untouched. - **Completion & download**: When the stream ends you hold the full clip; keep the accumulated bytes to offer a download. For a file with correct length fields, request the same `response_format` with `stream: false`. WAV is not appendable to a MediaSource SourceBuffer, so browsers generally buffer the streamed chunks before playback; `mp3` is the friendlier choice for progressive playback in a browser. ## Choosing the voice (the `voice` field) The `voice` field in the request body is a voice ID, not a name. Get a valid ID before calling this endpoint. - **List available voices**: Call GET /api/ext/tts/voices. It returns a `voices` array containing the public platform voices plus your project’s cloned voices. Copy the `id` of the voice you want and pass it as `voice` here. Fields: `voice`. - **Voice must be ready**: The chosen voice must have `is_ready: true`. A freshly cloned voice stays `is_ready: false` until its background job finishes; passing a not-ready voice will fail. - **Use your own cloned voice**: To use a custom voice, create one with POST /api/ext/tts/clone_voice, then poll GET /api/ext/tts/voices (or GET /api/ext/tts/voices/{voice_id}) until it is ready, and use the returned id here. These are the same voice IDs used to configure an agent’s voice. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) - `input` (string, required): The text to synthesize. 1–4096 characters after normalization. Text that normalizes to nothing is rejected with 422. - `voice` (string (uuid), required): The voice ID to speak with — an `id` from GET /api/ext/tts/voices. Must belong to your project (or be a public voice) and have `is_ready: true`. - `model` ("nabrah_tts_v1", optional): The synthesis model. `nabrah_tts_v1` is the only accepted value. Defaults to `nabrah_tts_v1`; any other value is rejected with 422. - `response_format` ("mp3" | "wav" | "pcm", optional): Audio encoding. All three work with or without `stream`. Defaults to `mp3` when `stream` is false and `wav` when it is true. `mp3` returns `audio/mpeg`; `wav` returns `audio/wav` (16-bit PCM in a RIFF container, 48 kHz mono); `pcm` returns `audio/L16; rate=48000; channels=1` — raw signed 16-bit little-endian mono frames with no container. - `stream` (boolean, optional): Deliver the audio progressively instead of as one finished file. Defaults to `false`. When `true`, bytes are forwarded as the engine produces them, `speed` is forced to 1.0, `apply_enhancements` must be `none`, and a streamed `wav` carries an unknown-length header (both size fields set to `0xFFFFFFFF`). - `use_cache` (boolean, optional): Reuse a recently generated clip for the same text, voice and format. Defaults to `true`. A cache hit is marked with the `X-Cache: HIT` response header; set to `false` to force a fresh generation. - `apply_enhancements` ("none" | "simple" | "ultra", optional): Post-processing enhancement level. Defaults to `none`. Must be `none` when `stream` is true, otherwise the request is rejected with 422. Accepted for compatibility on buffered requests but not currently applied to the generated audio. - `speed` (number, optional): Playback speed multiplier. Accepted between 0.01 and 4.0 and forced to 1.0 while streaming. Not currently applied to the generated audio. - `expressiveness` (number, optional): How much inflection the voice uses. Accepted between 0.1 and 1.0. Not currently applied to the generated audio. - `speech_smoothness` (number, optional): Blend duration between synthesized segments. Accepted between 0.1 and 1.0. Not currently applied to the generated audio. - `remove_silence` (boolean, optional): Trim long pauses from the start and end of the clip. Defaults to `false`. Not currently applied to the generated audio. ### Example: File — MP3 (default) One finished MP3. This is what you get when `stream` and `response_format` are both omitted. ```json { "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "mp3", "stream": false, "use_cache": true } ``` ### Example: File — WAV One finished WAV, 48 kHz mono 16-bit, with correct length fields. Lossless and larger than MP3. ```json { "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "wav" } ``` ### Example: File — raw PCM The complete clip as raw signed 16-bit little-endian mono samples at 48 kHz, with no container. Returned as `audio/L16; rate=48000; channels=1`. ```json { "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "pcm" } ``` ### Example: Streaming — WAV (default) Progressive `audio/wav`. This is what streaming gives you when `response_format` is omitted. The header carries the unknown-length marker; `speed` is ignored. ```json { "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "wav", "stream": true } ``` ### Example: Streaming — MP3 Progressive `audio/mpeg`. The friendliest option for browser playback, since MP3 frames can be appended to a MediaSource buffer as they arrive. ```json { "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "mp3", "stream": true } ``` ### Example: Streaming — raw PCM Progressive raw 16-bit little-endian mono frames at 48 kHz, no container and no header. Best for feeding a live audio pipeline. ```json { "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "pcm", "stream": true } ``` ## Response ### 200 Successful Response ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/tts/generations' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "mp3", "stream": false, "use_cache": true }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tts/generations" headers = { "X-API-Key": "", } payload = { "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "mp3", "stream": False, "use_cache": True } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tts/generations", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "mp3", "stream": false, "use_cache": true }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "model": "nabrah_tts_v1", "input": "Hello from Nabrah!", "voice": "00000000-0000-4000-8000-000000000000", "response_format": "mp3", "stream": false, "use_cache": true }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/tts/generations", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List voices > GET https://api.nabrah.ai/api/ext/tts/voices Group: Text to speech. Page: https://www.nabrah.ai/en/docs/api-reference/text-to-speech/list-voices Lists every voice available to the project tied to your API key. The response combines public platform voices (shared across all projects) and the project's own private cloned voices created via the Clone Voice endpoint. Each voice's `id` is the value you pass as the `voice` field when generating speech and as the voice for agents. Call this endpoint first to discover which voice IDs you can use. ## Response shape Returns a `voices` array plus a `total_count`. Each entry in `voices` describes one voice your project can use. - **voices[]**: Array of voice descriptors. Includes both public platform voices and the project's private cloned voices. Fields: `id`, `name`, `is_public`, `created_at`, `is_ready`. - **id**: The voice's unique identifier (UUID). This is exactly the value you put in the `voice` field of POST /api/ext/tts/generations and the voice configured on an agent. - **is_public**: `true` for shared platform voices that any project can use; `false` for private voices that belong only to this project. Only private voices can be deleted. - **is_ready**: Indicates whether the voice is usable. A freshly cloned voice starts as `false` while its background cloning job runs, and flips to `true` when ready. Do not pass a voice to TTS until `is_ready` is `true`. - **total_count**: The number of voices returned in the `voices` array. After cloning a voice, poll this endpoint (or GET /api/ext/tts/voices/{voice_id}) until `is_ready` becomes `true`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "voices": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "is_public": false, "created_at": "2026-01-15T12:00:00.000Z", "is_ready": false } ], "total_count": 0 } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/tts/voices' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tts/voices" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tts/voices", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/tts/voices", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get voice > GET https://api.nabrah.ai/api/ext/tts/voices/{voice_id} Group: Text to speech. Page: https://www.nabrah.ai/en/docs/api-reference/text-to-speech/get-voice Fetch the full details of a single voice by its ID. Use this to check a specific voice — most importantly to poll the `is_ready` flag after starting a clone job. The voice must be accessible to the project tied to your API key (a public platform voice or one of the project's private voices). ## Response shape Returns the descriptor for one voice. - **Voice fields**: The same fields returned per-voice by GET /api/ext/tts/voices. `is_ready` tells you whether the voice can be used for TTS yet. Fields: `id`, `name`, `is_public`, `created_at`, `is_ready`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `voice_id` (string, required): The ID of the voice to retrieve. Use a voice `id` returned by GET /api/ext/tts/voices, or a `voice_id` returned by the Clone Voice endpoint. ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "is_public": false, "created_at": "2026-01-15T12:00:00.000Z", "is_ready": false } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/tts/voices/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tts/voices/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tts/voices/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/tts/voices/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Clone a voice > POST https://api.nabrah.ai/api/ext/tts/clone_voice Group: Text to speech. Page: https://www.nabrah.ai/en/docs/api-reference/text-to-speech/clone-a-voice Clone a voice from an uploaded audio sample. This is a multipart/form-data upload that starts a background cloning job and returns immediately with a `voice_id`. The voice is not usable right away — it becomes available once its `is_ready` flag turns `true`. Poll GET /api/ext/tts/voices or GET /api/ext/tts/voices/{voice_id} to check readiness before using the new voice for TTS or agents. ## Multipart form fields Send the request as multipart/form-data (not JSON). - **file**: The audio sample to clone, uploaded as a WAV file. - **name**: A human-readable display name for the new voice. ## Response & next steps On success the endpoint returns the cloning job result. - **voice_id**: The id of the newly created private voice. Use this to poll for readiness and, once ready, as the `voice` field in TTS generation. - **success / message**: Status flag and human-readable message describing the outcome of starting the cloning job. Fields: `success`, `message`. Cloning runs in the background. The new voice will report `is_ready: false` until processing completes — poll GET /api/ext/tts/voices/{voice_id} until it is `true`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (multipart/form-data) ## Response ### 200 Successful Response Content type: `application/json` ```json { "success": false, "message": "string", "voice_id": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/tts/clone_voice' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tts/clone_voice" headers = {"X-API-Key": ""} response = requests.post(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const form = new FormData(); const response = await fetch("https://api.nabrah.ai/api/ext/tts/clone_voice", { method: "POST", headers: { "X-API-Key": "" }, body: form, }); const data = await response.json(); console.log(data); ``` ### Go ```go // Send multipart/form-data with mime/multipart: // POST https://api.nabrah.ai/api/ext/tts/clone_voice ``` --- # Delete cloned voice > DELETE https://api.nabrah.ai/api/ext/tts/voices/{voice_id} Group: Text to speech. Page: https://www.nabrah.ai/en/docs/api-reference/text-to-speech/delete-cloned-voice Delete a voice clone by its ID. The voice must belong to the project associated with your API key. Only PRIVATE cloned voices can be deleted — public platform voices (`is_public: true`) cannot be removed through this endpoint and the request will be rejected. The response confirms the outcome with a `success` flag and a `message`. ## Restrictions - **Private voices only**: You can only delete voices your project owns (`is_public: false`). Attempting to delete a public platform voice is not permitted. - **Response**: Returns a `success` flag and a `message` describing the result. Fields: `success`, `message`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `voice_id` (string, required): The ID of the private voice clone to delete. Must be a private voice owned by your project. Find candidate IDs via GET /api/ext/tts/voices where `is_public` is `false`. ## Response ### 200 Successful Response Content type: `application/json` ```json { "success": false, "message": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/tts/voices/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/tts/voices/" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/tts/voices/", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/tts/voices/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Speech to text API Transcribe recorded audio in the background, or stream audio for live transcripts. - [Transcribe an audio file](https://www.nabrah.ai/en/docs/api-reference/speech-to-text/transcribe-an-audio-file.md) `POST /api/ext/stt/async-transcribe`: Queue a long audio file (up to 2 hours) for background transcription. - [Get transcription job](https://www.nabrah.ai/en/docs/api-reference/speech-to-text/get-transcription-job.md) `GET /api/ext/stt/jobs/{job_id}`: Get one async transcription job by ID. - [List transcription jobs](https://www.nabrah.ai/en/docs/api-reference/speech-to-text/list-transcription-jobs.md) `GET /api/ext/stt/jobs`: List the async transcription jobs that belong to the project tied to your API key. - [Stream speech to text](https://www.nabrah.ai/en/docs/api-reference/speech-to-text/stream-speech-to-text.md) `WS /api/ext/stt/ws`: Stream audio over a WebSocket and receive live transcription. --- # Transcribe an audio file > POST https://api.nabrah.ai/api/ext/stt/async-transcribe Group: Speech to text. Page: https://www.nabrah.ai/en/docs/api-reference/speech-to-text/transcribe-an-audio-file Queue a long audio file (up to 2 hours) for background transcription. Send the audio as a multipart/form-data upload, optionally with recognition settings that tune how it is transcribed (priority words to boost, the model variant, and end-of-utterance silence). The request returns immediately with a job in `queued` status — nothing streams back and you do not keep the connection open. Poll the job endpoints to collect the transcription once it completes. Authenticate with the `X-API-Key` header. ## Multipart form fields Send the request as multipart/form-data (not JSON). Only `audio_file` is required; the recognition settings use the same names and defaults as the streaming WebSocket config. - **audio_file** (file · required): The audio file to transcribe. Maximum duration is 2 hours — longer files are rejected with a 422 error. Fields: `audio_file`. - **priority_words** (string · repeatable · optional): A word or phrase to bias recognition toward, such as product names, brands or branch names. Repeat the field once per word (for example `-F priority_words=Nabrah -F priority_words=Olaya`). Up to 100 words; more returns a 400 error. Fields: `priority_words`. - **priority_words_strength** (number · default: 0.5): How strongly to bias toward priority_words. 0.5 works well for a relatively small vocabulary; the recommended range for broader biasing is 20-100. A value that is not a number returns a 400 error. Fields: `priority_words_strength`. - **recognition_model** (string · default: server-configured model): Model variant for the job. Leave it out to use the default model, which is the most accurate. Fields: `recognition_model`. - **end_of_utterance_silence_ms** (integer · default: -1): Trailing silence in milliseconds that ends an utterance. -1 keeps the recognizer's own defaults; only a positive value overrides. A value that is not an integer returns a 400 error. Fields: `end_of_utterance_silence_ms`. The settings a job was queued with are returned on the job as `recognition_config`, so you can confirm what was applied. ## Async flow Transcription runs in the background; this request only creates the job. - **1 · Upload**: This endpoint stores the audio, queues a transcription job, and returns the job immediately with `status: "queued"` and `result: null`. - **2 · Track**: Poll `GET /api/ext/stt/jobs/{job_id}` (or list jobs with `GET /api/ext/stt/jobs`). The status moves `queued` → `in_progress` → `completed`, or `failed` if transcription could not finish. - **3 · Result**: Once `status` is `completed`, the job's `result` object holds the transcription: `text_result`, `audio_duration`, and `timestamps`. Fields: `text_result`, `audio_duration`, `timestamps`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (multipart/form-data) ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "status": "queued", "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "result": null, "created_at": "2026-07-23T12:00:00Z", "updated_at": "2026-07-23T12:00:00Z", "filename": "recording.mp3" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/stt/async-transcribe' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/stt/async-transcribe" headers = {"X-API-Key": ""} response = requests.post(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const form = new FormData(); const response = await fetch("https://api.nabrah.ai/api/ext/stt/async-transcribe", { method: "POST", headers: { "X-API-Key": "" }, body: form, }); const data = await response.json(); console.log(data); ``` ### Go ```go // Send multipart/form-data with mime/multipart: // POST https://api.nabrah.ai/api/ext/stt/async-transcribe ``` --- # Get transcription job > GET https://api.nabrah.ai/api/ext/stt/jobs/{job_id} Group: Speech to text. Page: https://www.nabrah.ai/en/docs/api-reference/speech-to-text/get-transcription-job Get one async transcription job by ID. Poll this endpoint after calling `async-transcribe` until `status` is `completed`, then read the transcription from `result`. Authenticate with the `X-API-Key` header. ## Response shape One job record with its current status and, once finished, the transcription. - **status**: `queued`, `in_progress`, `completed`, `failed`, or `canceled`. - **result**: `null` until the job completes. On completion it contains `text_result` (the transcription), `audio_duration`, and `timestamps` (same timing structure as the synchronous transcribe endpoint). Fields: `text_result`, `audio_duration`, `timestamps`. - **filename**: The original uploaded audio filename. - **recognition_config**: The recognition settings the job was queued with: `recognition_model`, `end_of_utterance_silence_ms`, `priority_words` and `priority_words_strength`. Fields: `recognition_config`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `job_id` (string, required): The job ID returned by the async-transcribe endpoint. ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "status": "completed", "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "result": { "text_result": "string", "audio_duration": 0, "timestamps": { "timestep": [ 0 ], "char": [], "word": [], "segment": [] } }, "created_at": "2026-07-23T12:00:00Z", "updated_at": "2026-07-23T12:00:00Z", "filename": "recording.mp3" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/stt/jobs/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/stt/jobs/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/stt/jobs/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/stt/jobs/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List transcription jobs > GET https://api.nabrah.ai/api/ext/stt/jobs Group: Speech to text. Page: https://www.nabrah.ai/en/docs/api-reference/speech-to-text/list-transcription-jobs List the async transcription jobs that belong to the project tied to your API key. Optionally filter by job status. Authenticate with the `X-API-Key` header. ## Response shape A `jobs` array, newest first. Each job has the same fields as the single-job endpoint. - **status**: `queued`, `in_progress`, `completed`, `failed`, or `canceled`. - **recognition_config**: The recognition settings the job was queued with: `recognition_model`, `end_of_utterance_silence_ms`, `priority_words` and `priority_words_strength`. Fields: `recognition_config`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `status` (string, optional): Filter jobs by status. One of `queued`, `in_progress`, `completed`, `failed`, `canceled`. Omit to return jobs in every status. ## Response ### 200 Successful Response Content type: `application/json` ```json { "jobs": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "status": "completed", "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "result": { "text_result": "string", "audio_duration": 0, "timestamps": { "timestep": [ 0 ], "char": [], "word": [], "segment": [] } }, "created_at": "2026-07-23T12:00:00Z", "updated_at": "2026-07-23T12:00:00Z", "filename": "recording.mp3" } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/stt/jobs' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/stt/jobs" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/stt/jobs", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/stt/jobs", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Stream speech to text > WS wss://api.nabrah.ai/api/ext/stt/ws Group: Speech to text. Page: https://www.nabrah.ai/en/docs/api-reference/speech-to-text/stream-speech-to-text Stream audio over a WebSocket and receive live transcription. Open the socket, then send a single JSON configuration message as the very first frame — before any audio. The server replies with a ready signal, after which you stream raw 16 kHz mono LINEAR_PCM audio frames and receive transcript messages back. Authenticate with `api_key` inside the config message (not the `X-API-Key` header). ## Configuration message Sent once, as the first frame after the socket opens. Any field that is missing or the wrong type silently falls back to its default. - **api_key** (string · required): API key for authentication. The connection is closed if it is missing or invalid. Fields: `api_key`. - **recognition_model** (string · default: server-configured model): Model variant for the session. The default model is more accurate. Use "eot_nabrah" to emit inline tokens for turn detection — the raw token appears verbatim in text and the client is responsible for stripping it. Fields: `recognition_model`. - **end_of_utterance_silence_ms** (integer · default: -1): Trailing silence in milliseconds that marks an utterance as ended. -1 leaves the recognizer's own defaults in place; only a positive value overrides. Drives both the final result and the earlier preliminary transcript, so lowering it is the main latency knob. Fields: `end_of_utterance_silence_ms`. - **priority_words** (list of strings · default: []): Words or phrases to bias recognition toward. Fields: `priority_words`. - **priority_words_strength** (number · default: 0.5): How strongly to bias toward priority_words. 0.5 works well for a relatively small vocabulary; the recommended range for broader biasing is 20-100. Fields: `priority_words_strength`. Fixed and never caller-configurable: input language, sample rate (16 kHz), encoding (LINEAR_PCM), channel count (mono), and punctuation. ## Body (application/json) ```json { "api_key": "YOUR_API_KEY", "recognition_model": "", "end_of_utterance_silence_ms": 500, "priority_words": [ "نبرة", "ذكاء اصطناعي", "خدمة العملاء" ], "priority_words_strength": 0.5 } ``` ## Response ### 101 Socket upgraded. The server sends a ready signal, then transcript messages as audio streams in. Content type: `application/json` ```json { "text": "مرحبا بك في نبرة", "is_final": true } ``` ## Request examples ### Python ```python import asyncio import json import websockets async def main(): async with websockets.connect("wss://api.nabrah.ai/api/ext/stt/ws") as ws: await ws.send(json.dumps({ "api_key": "YOUR_API_KEY", "recognition_model": "", "end_of_utterance_silence_ms": 500, "priority_words": [ "نبرة", "ذكاء اصطناعي", "خدمة العملاء" ], "priority_words_strength": 0.5 })) print(await ws.recv()) # ready # Stream raw 16 kHz mono PCM: await ws.send(chunk) async for message in ws: print(json.loads(message)) asyncio.run(main()) ``` ### JavaScript ```javascript const ws = new WebSocket("wss://api.nabrah.ai/api/ext/stt/ws"); ws.addEventListener("open", () => { ws.send(JSON.stringify({ "api_key": "YOUR_API_KEY", "recognition_model": "", "end_of_utterance_silence_ms": 500, "priority_words": [ "نبرة", "ذكاء اصطناعي", "خدمة العملاء" ], "priority_words_strength": 0.5 })); }); ws.addEventListener("message", (event) => { console.log(JSON.parse(event.data)); }); // After the ready message, stream raw 16 kHz mono PCM frames: // ws.send(pcmChunk); ``` --- # Contacts API Create, update and search the contacts your workspace talks to on every channel. - [Create contact](https://www.nabrah.ai/en/docs/api-reference/contacts/create-contact.md) `POST /api/ext/omni_v2/contact`: Creates a new contact in the project the API key is scoped to. - [List contacts](https://www.nabrah.ai/en/docs/api-reference/contacts/list-contacts.md) `GET /api/ext/omni_v2/contact`: Returns a paginated list of all contacts in the project the API key is scoped to. - [Search contacts](https://www.nabrah.ai/en/docs/api-reference/contacts/search-contacts.md) `GET /api/ext/omni_v2/contact/search`: Searches contacts in the project the API key is scoped to and returns a paginated list of matches. - [Get contact](https://www.nabrah.ai/en/docs/api-reference/contacts/get-contact.md) `GET /api/ext/omni_v2/contact/{contact_id}`: Retrieves a single contact by its numeric contact_id. - [Update contact](https://www.nabrah.ai/en/docs/api-reference/contacts/update-contact.md) `PATCH /api/ext/omni_v2/contact/{contact_id}`: Performs a partial update of an existing contact identified by contact_id. - [List conversations for a contact](https://www.nabrah.ai/en/docs/api-reference/contacts/list-conversations-for-a-contact.md) `GET /api/ext/omni_v2/contact/{contact_id}/conversations`: Lists all conversations that belong to the contact identified by contact_id. --- # Create contact > POST https://api.nabrah.ai/api/ext/omni_v2/contact Group: Contacts. Page: https://www.nabrah.ai/en/docs/api-reference/contacts/create-contact Creates a new contact in the project the API key is scoped to. Supply at least one identifying field (such as `phone_number` or `email`) along with an optional `name`, structured `additional_attributes`, and free-form `custom_attributes`. On success the API returns the fully created contact wrapped in `payload.contact`, including the server-assigned numeric `id` you should store as the `contact_id` for subsequent calls. ## Authentication - **X-API-Key header**: Send your Web API key in the `X-API-Key` header (prefix `nb_`, from the dashboard Developers tab). The key is scoped to one project, so the contact is created in that project automatically — no `project_id` is required. ## Request body fields Base your request on the default JSON body below. - **Core fields**: `phone_number`, `name`, and `email` describe the person. Provide a phone number and/or email so the contact can be matched and reached across channels. Fields: `phone_number`, `name`, `email`. - **additional_attributes**: Optional structured profile object with `city`, `country`, `description`, `company_name`, `country_code`, and `social_profiles`. Fields: `city`, `country`, `description`, `company_name`, `country_code`, `social_profiles`. - **custom_attributes**: Optional free-form key/value object for workspace-defined custom fields. Read the created contact's `id` from `payload.contact.id` and reuse it as `contact_id` elsewhere. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Full contact Create a contact with core fields plus structured and custom attributes. ```json { "phone_number": "+15551234567", "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "additional_attributes": { "city": "Riyadh", "country": "Saudi Arabia", "description": "Returning enterprise customer", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "twitter": "sarahjohnson", "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "gold", "lifetime_value": 4200 } } ``` ### Example: Minimal Create a contact with only the essential identifying fields. ```json { "phone_number": "+15557654321", "name": "Omar Haddad", "email": "omar.haddad@example.com", "additional_attributes": {}, "custom_attributes": {} } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "payload": { "contact": { "id": 0, "additional_attributes": { "city": null, "country": null, "description": null, "company_name": null, "country_code": null, "social_profiles": null }, "availability_status": "string", "email": "string", "name": "string", "phone_number": "string", "blocked": false, "identifier": "string", "thumbnail": "string", "custom_attributes": {}, "last_activity_at": 0, "created_at": 0 } } } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/contact' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "phone_number": "+15551234567", "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "additional_attributes": { "city": "Riyadh", "country": "Saudi Arabia", "description": "Returning enterprise customer", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "twitter": "sarahjohnson", "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "gold", "lifetime_value": 4200 } }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/contact" headers = { "X-API-Key": "", } payload = { "phone_number": "+15551234567", "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "additional_attributes": { "city": "Riyadh", "country": "Saudi Arabia", "description": "Returning enterprise customer", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "twitter": "sarahjohnson", "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "gold", "lifetime_value": 4200 } } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/contact", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "phone_number": "+15551234567", "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "additional_attributes": { "city": "Riyadh", "country": "Saudi Arabia", "description": "Returning enterprise customer", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "twitter": "sarahjohnson", "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "gold", "lifetime_value": 4200 } }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "phone_number": "+15551234567", "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "additional_attributes": { "city": "Riyadh", "country": "Saudi Arabia", "description": "Returning enterprise customer", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "twitter": "sarahjohnson", "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "gold", "lifetime_value": 4200 } }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/contact", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List contacts > GET https://api.nabrah.ai/api/ext/omni_v2/contact Group: Contacts. Page: https://www.nabrah.ai/en/docs/api-reference/contacts/list-contacts Returns a paginated list of all contacts in the project the API key is scoped to. Contacts are the people your workspace communicates with across every Omni v2 channel (WhatsApp, web chat, email, and so on). Each contact in the list includes its numeric `id` (the `contact_id` you pass to every other Contacts endpoint), core fields such as `name`, `email`, and `phone_number`, plus structured `additional_attributes` and free-form `custom_attributes`. Use the `sort` query parameter to control ordering and `page` to walk through large result sets. ## Authentication All Omni v2 endpoints authenticate with a Web API key. - **X-API-Key header**: Send your key in the `X-API-Key` header (e.g. `X-API-Key: nb_...`). Keys are created in the dashboard under the Developers tab and are prefixed with `nb_`. Each key is scoped to a single project, so there is no `project_id` parameter on this endpoint. You can validate a key with `GET /api/ext/test`. ## Pagination Results are returned one page at a time. - **Reading meta**: The response `meta` object reports the total `count` of contacts and the `current_page` you are viewing. Use these to decide whether more pages remain. Fields: `meta.count`, `meta.current_page`. - **Paging through results**: Pass `?page=1` for the first page and increment `page` until the returned `payload` array is empty (or you have retrieved `meta.count` contacts). When `page` is omitted the first page is returned. Each contact's `id` field is the `contact_id` used by the get, update, conversations, ticket, and WhatsApp endpoints. ## Contact fields - **additional_attributes**: A structured object of standard profile fields: `city`, `country`, `description`, `company_name`, `country_code`, and `social_profiles`. Fields: `city`, `country`, `description`, `company_name`, `country_code`, `social_profiles`. - **custom_attributes**: A free-form key/value object holding workspace-defined custom fields for the contact. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `page` (integer, optional): 1-based page number to retrieve. Omit to fetch the first page. Increment until the returned payload array is empty to iterate over all contacts. - `sort` (string, optional): Attribute used to order the returned contacts. Accepted values: last_activity_at, name, email, phone_number. Prefix any value with a hyphen (e.g. -last_activity_at, -name, -email, -phone_number) to sort in descending order. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "count": 0, "current_page": "string" }, "payload": [ { "id": 0, "additional_attributes": { "city": "string", "country": "string", "description": "string", "company_name": "string", "country_code": "string", "social_profiles": {} }, "availability_status": "string", "email": "string", "name": "string", "phone_number": "string", "blocked": false, "identifier": "string", "thumbnail": "string", "custom_attributes": {}, "last_activity_at": 0, "created_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/contact' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/contact" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/contact", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/contact", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Search contacts > GET https://api.nabrah.ai/api/ext/omni_v2/contact/search Group: Contacts. Page: https://www.nabrah.ai/en/docs/api-reference/contacts/search-contacts Searches contacts in the project the API key is scoped to and returns a paginated list of matches. Pass the search text in the `q` query parameter to match against contact fields such as name, email, and phone number. Results follow the same shape as the list endpoint — a `meta` block with `count` and `current_page` plus a `payload` array of contacts — and can be ordered with `sort` and paged with `page`. ## Authentication - **X-API-Key header**: Send your Web API key (prefix `nb_`) in the `X-API-Key` header. The key is scoped to one project, so only that project's contacts are searched. ## Searching and paging Combine q, sort, and page to find and walk through matches. - **Query (q)**: Provide the search text in `q`. The search matches against contact fields such as name, email, and phone number. Omitting `q` returns contacts unfiltered (subject to sort and paging). Only existing contacts are found: a number that has never written to you and was never added as a contact returns no results. - **Pagination**: The response `meta.count` reports the total number of matches and `meta.current_page` the page returned. Pass `?page=1`, `?page=2`, and so on, incrementing until the `payload` array comes back empty. Fields: `meta.count`, `meta.current_page`. Each match's `id` is the `contact_id` used by the get, update, and conversations endpoints. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `page` (integer, optional): 1-based page number of search results to retrieve. Omit to fetch the first page. Increment until the returned payload array is empty to iterate over all matches. - `q` (string, optional): Search query text used to match contacts. Matches against contact fields such as name, email, and phone number. - `sort` (string, optional): Attribute used to order the matched contacts. Accepted values: last_activity_at, name, email, phone_number. Prefix with a hyphen (e.g. -last_activity_at, -name, -email, -phone_number) for descending order. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "count": 0, "current_page": "string" }, "payload": [ { "id": 0, "additional_attributes": { "city": "string", "country": "string", "description": "string", "company_name": "string", "country_code": "string", "social_profiles": {} }, "availability_status": "string", "email": "string", "name": "string", "phone_number": "string", "blocked": false, "identifier": "string", "thumbnail": "string", "custom_attributes": {}, "last_activity_at": 0, "created_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/contact/search' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/contact/search" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/contact/search", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/contact/search", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get contact > GET https://api.nabrah.ai/api/ext/omni_v2/contact/{contact_id} Group: Contacts. Page: https://www.nabrah.ai/en/docs/api-reference/contacts/get-contact Retrieves a single contact by its numeric `contact_id`. The full contact record is returned in `payload`, including `name`, `email`, `phone_number`, `identifier`, `additional_attributes`, `custom_attributes`, and activity timestamps such as `last_activity_at` and `created_at`. Obtain a `contact_id` from `GET /api/ext/omni_v2/contact` or `GET /api/ext/omni_v2/contact/search`. ## Authentication - **X-API-Key header**: Send your Web API key (prefix `nb_`) in the `X-API-Key` header. The key is scoped to one project; the contact must belong to that project. ## Where the contact_id comes from - **Listing or searching**: Call `GET /api/ext/omni_v2/contact` to list contacts or `GET /api/ext/omni_v2/contact/search` to look one up by name, email, or phone. Each returned contact's `id` is the value to pass as `contact_id` here. A contact you just created via `POST /api/ext/omni_v2/contact` exposes its id at `payload.contact.id`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `contact_id` (integer, required): Numeric identifier of the contact to retrieve. Obtained from the list (`GET /api/ext/omni_v2/contact`) or search (`GET /api/ext/omni_v2/contact/search`) endpoints — use the `id` field of a contact. ## Response ### 200 Successful Response Content type: `application/json` ```json { "payload": { "id": 0, "additional_attributes": { "city": "string", "country": "string", "description": "string", "company_name": "string", "country_code": "string", "social_profiles": {} }, "availability_status": "string", "email": "string", "name": "string", "phone_number": "string", "blocked": false, "identifier": "string", "thumbnail": "string", "custom_attributes": {}, "last_activity_at": 0, "created_at": 0 } } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/contact/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/contact/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/contact/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/contact/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Update contact > PATCH https://api.nabrah.ai/api/ext/omni_v2/contact/{contact_id} Group: Contacts. Page: https://www.nabrah.ai/en/docs/api-reference/contacts/update-contact Performs a partial update of an existing contact identified by `contact_id`. Only the fields you include in the request body are changed; omitted fields are left untouched. You may update `name`, `email`, `phone_number`, `identifier`, the structured `additional_attributes`, and the free-form `custom_attributes`. The full updated contact is returned in `payload`. ## Authentication - **X-API-Key header**: Send your Web API key (prefix `nb_`) in the `X-API-Key` header. The key is scoped to one project; the contact must belong to that project. ## Partial update semantics PATCH only changes the fields you send. - **Updatable fields**: Include any subset of `name`, `email`, `phone_number`, `identifier`, `additional_attributes`, and `custom_attributes`. Fields you leave out keep their current values. Fields: `name`, `email`, `phone_number`, `identifier`, `additional_attributes`, `custom_attributes`. - **additional_attributes**: Structured profile object with `city`, `country`, `description`, `company_name`, `country_code`, and `social_profiles`. Fields: `city`, `country`, `description`, `company_name`, `country_code`, `social_profiles`. Get a contact_id from the list or search endpoints, or from payload.contact.id when creating a contact. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `contact_id` (integer, required): Numeric identifier of the contact to update. Obtained from `GET /api/ext/omni_v2/contact` or `GET /api/ext/omni_v2/contact/search` (the contact `id` field). ## Body (application/json) ### Example: Update attributes Update profile and custom attributes along with core fields. ```json { "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "phone_number": "+15551234567", "identifier": "crm-7831", "additional_attributes": { "city": "Jeddah", "country": "Saudi Arabia", "description": "Upgraded to enterprise plan", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "platinum" } } ``` ### Example: Email only Partial update changing just the email address. ```json { "email": "sarah.j@newcompany.com" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "payload": { "id": 0, "additional_attributes": { "city": "string", "country": "string", "description": "string", "company_name": "string", "country_code": "string", "social_profiles": {} }, "availability_status": "string", "email": "string", "name": "string", "phone_number": "string", "blocked": false, "identifier": "string", "thumbnail": "string", "custom_attributes": {}, "last_activity_at": 0, "created_at": 0 } } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request PATCH \ --url 'https://api.nabrah.ai/api/ext/omni_v2/contact/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "phone_number": "+15551234567", "identifier": "crm-7831", "additional_attributes": { "city": "Jeddah", "country": "Saudi Arabia", "description": "Upgraded to enterprise plan", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "platinum" } }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/contact/" headers = { "X-API-Key": "", } payload = { "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "phone_number": "+15551234567", "identifier": "crm-7831", "additional_attributes": { "city": "Jeddah", "country": "Saudi Arabia", "description": "Upgraded to enterprise plan", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "platinum" } } response = requests.patch(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/contact/", { method: "PATCH", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "phone_number": "+15551234567", "identifier": "crm-7831", "additional_attributes": { "city": "Jeddah", "country": "Saudi Arabia", "description": "Upgraded to enterprise plan", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "platinum" } }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Sarah Johnson", "email": "sarah.johnson@example.com", "phone_number": "+15551234567", "identifier": "crm-7831", "additional_attributes": { "city": "Jeddah", "country": "Saudi Arabia", "description": "Upgraded to enterprise plan", "company_name": "Acme Corp", "country_code": "SA", "social_profiles": { "linkedin": "in/sarahjohnson" } }, "custom_attributes": { "account_tier": "platinum" } }`) req, _ := http.NewRequest("PATCH", "https://api.nabrah.ai/api/ext/omni_v2/contact/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List conversations for a contact > GET https://api.nabrah.ai/api/ext/omni_v2/contact/{contact_id}/conversations Group: Contacts. Page: https://www.nabrah.ai/en/docs/api-reference/contacts/list-conversations-for-a-contact Lists all conversations that belong to the contact identified by `contact_id`. The response `payload` is an array of conversation objects, each including its `id`, `uuid`, `status`, `inbox_id`, channel metadata under `meta`, the contact (`sender`), and the conversation `messages`. Use this to pull a contact's full cross-channel conversation history. Obtain `contact_id` from the list or search contact endpoints. ## Authentication - **X-API-Key header**: Send your Web API key (prefix `nb_`) in the `X-API-Key` header. The key is scoped to one project; the contact must belong to that project. ## Response shape - **Conversation objects**: Each item in `payload` represents one conversation. Useful top-level fields include `id`, `uuid`, `status`, `inbox_id`, `unread_count`, `last_activity_at`, `priority`, and `labels`. The `meta` object identifies the `channel` and the `sender` (the contact), and `messages` holds the conversation's messages with content, sender, and timestamps. Fields: `id`, `uuid`, `status`, `inbox_id`, `meta.channel`, `messages`, `last_activity_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `contact_id` (integer, required): Numeric identifier of the contact whose conversations are listed. Obtained from `GET /api/ext/omni_v2/contact` or `GET /api/ext/omni_v2/contact/search` (the contact `id` field). ## Response ### 200 Successful Response Content type: `application/json` ```json { "payload": [ { "meta": { "sender": { "id": 0, "additional_attributes": null, "availability_status": "string", "email": "string", "name": "string", "phone_number": "string", "blocked": false, "identifier": "string", "thumbnail": "string", "custom_attributes": {}, "last_activity_at": 0, "created_at": 0 }, "channel": "string", "hmac_verified": false }, "id": 0, "messages": [ { "id": 0, "content": "string", "account_id": 0, "inbox_id": 0, "conversation_id": 0, "message_type": 0, "created_at": 0, "updated_at": "string", "private": false, "status": "string", "content_type": "string", "sender_type": "string", "sender_id": 0, "processed_message_content": "string", "conversation": { "unread_count": 0, "last_activity_at": 0, "contact_inbox": null, "assignee_id": null }, "sender": { "additional_attributes": null, "id": null, "name": null, "phone_number": null, "blocked": null, "type": null, "custom_attributes": null, "email": null, "identifier": null, "thumbnail": null }, "source_id": "string", "content_attributes": {}, "external_source_ids": {}, "additional_attributes": {}, "sentiment": {} } ], "account_id": 0, "uuid": "string", "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "inbox_id": 0, "muted": false, "status": "string", "created_at": 0, "updated_at": 0, "timestamp": 0, "first_reply_created_at": 0, "unread_count": 0, "last_activity_at": 0, "waiting_since": 0, "additional_attributes": {}, "custom_attributes": {}, "labels": [ "string" ], "snoozed_until": "string", "last_non_activity_message": { "id": 0, "content": "string", "account_id": 0, "inbox_id": 0, "conversation_id": 0, "message_type": 0, "created_at": 0, "updated_at": "string", "private": false, "status": "string", "content_type": "string", "sender_type": "string", "sender_id": 0, "processed_message_content": "string", "conversation": { "unread_count": 0, "last_activity_at": 0, "contact_inbox": null, "assignee_id": null }, "sender": { "additional_attributes": null, "id": null, "name": null, "phone_number": null, "blocked": null, "type": null, "custom_attributes": null, "email": null, "identifier": null, "thumbnail": null }, "source_id": "string", "content_attributes": {}, "external_source_ids": {}, "additional_attributes": {}, "sentiment": {} }, "priority": "string", "sla_policy_id": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/contact//conversations' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/contact//conversations" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/contact//conversations", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/contact//conversations", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Conversations API Read and reply to inbox conversations, assign them, and change their status and priority. - [List conversations](https://www.nabrah.ai/en/docs/api-reference/conversations/list-conversations.md) `GET /api/ext/omni_v2/conversation/conversation`: Returns a paginated list of conversations (omnichannel inbox threads) for the project that owns the API key. - [Filter conversations](https://www.nabrah.ai/en/docs/api-reference/conversations/filter-conversations.md) `POST /api/ext/omni_v2/conversation/conversation/filter`: Lists conversations matching a structured filter expression supplied in the JSON body, returning the same response shape as the generic list endpoint (meta folder counts plus a payload array of conversation summaries). - [List conversations by contact](https://www.nabrah.ai/en/docs/api-reference/conversations/list-conversations-by-contact.md) `POST /api/ext/omni_v2/conversation/conversation/contact/{contact_id}`: Lists conversations belonging to the contact identified by contact_id, with optional server-side filtering supplied in the request body and pagination via the page query parameter. - [Get conversation](https://www.nabrah.ai/en/docs/api-reference/conversations/get-conversation.md) `GET /api/ext/omni_v2/conversation/conversation/{id}`: Returns the full detail of a single conversation thread, including its metadata, the contact/sender and assignee in meta, the last_non_activity_message, and an embedded messages array. - [List conversation messages](https://www.nabrah.ai/en/docs/api-reference/conversations/list-conversation-messages.md) `GET /api/ext/omni_v2/conversation/conversation/{id}/messages`: Returns a page of messages for the conversation, newest-relevant first, using cursor-based pagination via the before query parameter. - [Send a message](https://www.nabrah.ai/en/docs/api-reference/conversations/send-a-message.md) `POST /api/ext/omni_v2/conversation/conversation/{id}/messages`: Sends a new message into the conversation. - [Send a message with an attachment](https://www.nabrah.ai/en/docs/api-reference/conversations/send-a-message-with-an-attachment.md) `POST /api/ext/omni_v2/conversation/conversation/{id}/messages_with_attachment`: Sends a message into the conversation with a file attachment. - [List conversation attachments](https://www.nabrah.ai/en/docs/api-reference/conversations/list-conversation-attachments.md) `GET /api/ext/omni_v2/conversation/conversation/{id}/attachments`: Returns every attachment shared in the conversation across all of its messages, in a single flat list. - [Assign conversation](https://www.nabrah.ai/en/docs/api-reference/conversations/assign-conversation.md) `POST /api/ext/omni_v2/conversation/conversation/{id}/assign`: Assigns the conversation to a workspace member (agent). - [Change conversation status](https://www.nabrah.ai/en/docs/api-reference/conversations/change-conversation-status.md) `POST /api/ext/omni_v2/conversation/conversation/{id}/toggle_status`: Changes the lifecycle status of the conversation. - [Change conversation priority](https://www.nabrah.ai/en/docs/api-reference/conversations/change-conversation-priority.md) `POST /api/ext/omni_v2/conversation/conversation/{id}/toggle_priority`: Sets the priority of the conversation. - [Mark conversation as seen](https://www.nabrah.ai/en/docs/api-reference/conversations/mark-conversation-as-seen.md) `POST /api/ext/omni_v2/conversation/conversation/{id}/update_last_seen`: Marks the conversation as seen by the agent/assignee, updating the server-side last-seen timestamp and clearing the unread state for the agent side. --- # List conversations > GET https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/list-conversations Returns a paginated list of conversations (omnichannel inbox threads) for the project that owns the API key. Use the `assignee_type` and `status_param` query filters to scope the inbox view (for example, only open conversations assigned to the current agent), and `q` for free-text search across conversation content and contacts. The `meta` object returns aggregate counts (`all_count`, `mine_count`, `unassigned_count`, `assigned_count`) that power inbox folder badges, while `payload` is the array of conversation summaries. Each conversation `id` returned here is what you pass to the per-conversation endpoints (get, messages, assign, toggle_*, update_last_seen). ## Authentication & scope Every Omni v2 request is authenticated with your Web API key and scoped to a single project. - **X-API-Key header**: Send your key in the `X-API-Key` header (it is created in the dashboard under the Developers tab and is prefixed with `nb_`). The key is bound to one project, so there is no `project_id` parameter on any Omni v2 endpoint — the project is inferred from the key. Fields: `X-API-Key`. - **Validate the key first**: If you are unsure whether a key is valid, call `GET /api/ext/test` once before issuing inbox requests. Base URL: https://api.nabrah.ai/api ## Pagination List results are page-based. - **page**: Pass `page` (1-based) to walk through additional pages of conversations. Combine it with the filters so the page set stays consistent. Page size is fixed by the server. ## Folder counts (meta) ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `assignee_type` (enum(all | me | unassigned | assigned), optional): Filters conversations by who they are assigned to. One of `all`, `me` (assigned to the agent whose key/identity is in context), `unassigned`, or `assigned` (assigned to anyone). Omit to default to the server's standard view. - `status_param` (enum(open | pending | resolved | snoozed | all), optional): Filters conversations by lifecycle status. One of `open`, `pending`, `resolved`, `snoozed`, or `all`. `snoozed` returns conversations temporarily hidden until their `snoozed_until` timestamp; `all` ignores status filtering. - `q` (string, optional): Free-text search query. Matches against conversation content and contact details. Leave empty to return the unfiltered (status/assignee-filtered) list. - `page` (integer, optional): 1-based page number for pagination. Defaults to the first page when omitted. Increment to fetch subsequent pages; page size is server-defined. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Filter conversations > POST https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/filter Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/filter-conversations Lists conversations matching a structured filter expression supplied in the JSON body, returning the same response shape as the generic list endpoint (`meta` folder counts plus a `payload` array of conversation summaries). Use this when the simple `assignee_type`/`status_param`/`q` query filters are not expressive enough — the body's `filters` array lets you combine multiple attribute conditions. Results are paginated with the `page` query parameter. ## Filter body The request body wraps an array of filter conditions. - **filters**: An array of filter condition objects. Each entry expresses a condition on a conversation attribute (such as status, assignee, priority, labels, or inbox); combine multiple entries to narrow results. An empty object matches broadly — provide concrete conditions to filter. Fields: `filters`. The response payload is identical in shape to GET /api/ext/omni_v2/conversation/conversation. ## Pagination ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `page` (integer, optional): 1-based page number for pagination. Defaults to the first page when omitted. Page size is server-defined. ## Body (application/json) ### Example: Filter conversations Provide one or more filter conditions in the `filters` array to scope the conversation list. ```json { "filters": [ { "attribute_key": "status", "filter_operator": "equal_to", "values": [ "open" ] }, { "attribute_key": "priority", "filter_operator": "equal_to", "values": [ "urgent" ] } ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/filter' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [ { "attribute_key": "status", "filter_operator": "equal_to", "values": [ "open" ] }, { "attribute_key": "priority", "filter_operator": "equal_to", "values": [ "urgent" ] } ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/filter" headers = { "X-API-Key": "", } payload = { "filters": [ { "attribute_key": "status", "filter_operator": "equal_to", "values": [ "open" ] }, { "attribute_key": "priority", "filter_operator": "equal_to", "values": [ "urgent" ] } ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/filter", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [ { "attribute_key": "status", "filter_operator": "equal_to", "values": [ "open" ] }, { "attribute_key": "priority", "filter_operator": "equal_to", "values": [ "urgent" ] } ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [ { "attribute_key": "status", "filter_operator": "equal_to", "values": [ "open" ] }, { "attribute_key": "priority", "filter_operator": "equal_to", "values": [ "urgent" ] } ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/filter", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List conversations by contact > POST https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/contact/{contact_id} Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/list-conversations-by-contact Lists conversations belonging to the contact identified by `contact_id`, with optional server-side filtering supplied in the request body and pagination via the `page` query parameter. The response returns a `meta` block with conversation counts (`all_count`, `mine_count`, `unassigned_count`, `assigned_count`) and a `payload` array of conversation objects. Pass an array of filter objects in `filters` to narrow the results, or send an empty filter to return all of the contact's conversations. ## Authentication - **X-API-Key header**: Send your Web API key (prefix `nb_`) in the `X-API-Key` header. The key is scoped to one project; the contact must belong to that project. ## Filtering and pagination - **filters**: The request body takes a `filters` array. Send `[{}]` (an empty filter) to return all of the contact's conversations, or add filter objects to narrow the results. - **page**: Use the `page` query parameter to page through results. The response `meta` reports `all_count`, `mine_count`, `unassigned_count`, and `assigned_count`. Fields: `meta.all_count`, `meta.mine_count`, `meta.unassigned_count`, `meta.assigned_count`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `contact_id` (integer, required): Numeric identifier of the contact whose conversations are listed. Obtained from `GET /api/ext/omni_v2/contact` or `GET /api/ext/omni_v2/contact/search` (the contact `id` field). ## Query parameters - `page` (integer, optional): 1-based page number of conversations to retrieve. Omit to fetch the first page. Increment until the returned payload array is empty to iterate over all conversations. ## Body (application/json) ### Example: All conversations Return every conversation for the contact (empty filter). ```json { "filters": [ {} ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/contact/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [ {} ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/contact/" headers = { "X-API-Key": "", } payload = { "filters": [ {} ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/contact/", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [ {} ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [ {} ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/contact/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get conversation > GET https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/{id} Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/get-conversation Returns the full detail of a single conversation thread, including its metadata, the contact/sender and assignee in `meta`, the `last_non_activity_message`, and an embedded `messages` array. Use this after locating a conversation in one of the list endpoints to render the complete thread header and recent activity. For long threads, paginate the message history separately via `GET.../{id}/messages` with the `before` cursor rather than relying solely on the messages embedded here. ## Where the id comes from ## Response highlights Key fields on the conversation object: - **can_reply**: Indicates whether the channel currently allows sending a reply (for example, outside a messaging window `can_reply` may be false). - **meta.sender / meta.assignee**: `meta.sender` is the contact who started the thread; `meta.assignee` is the workspace member it is assigned to (or absent when unassigned). - ***_last_seen_at**: `agent_last_seen_at`, `assignee_last_seen_at`, and `contact_last_seen_at` are epoch timestamps used to compute unread state. Update the agent/assignee side with the `update_last_seen` endpoint. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Integer id of the conversation to retrieve. Obtain it from a list endpoint such as `GET /api/ext/omni_v2/conversation/conversation`. ## Response ### 200 Successful Response Content type: `application/json` ```json { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": { "source_id": null }, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [ { "id": 0, "account_id": 0, "message_id": 0, "thumb_url": "string", "data_url": "string", "file_size": 0, "file_type": "string", "extension": "string", "width": 0, "height": 0, "created_at": 0, "sender": null, "coordinates_lat": 0, "coordinates_lng": 0, "fallback_title": "string", "meta": {} } ] }, "messages": [ { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": null, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [ { "id": null, "account_id": null, "message_id": null, "thumb_url": null, "data_url": null, "file_size": null, "file_type": null, "extension": null, "width": null, "height": null, "created_at": null, "sender": null, "coordinates_lat": null, "coordinates_lng": null, "fallback_title": null, "meta": null } ] } ], "meta": { "channel": "string", "hmac_verified": false, "sender": { "additional_attributes": {}, "availability_status": "string", "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "id": 0, "identifier": "string", "last_activity_at": 0, "name": "string", "phone_number": "string", "thumbnail": "string" }, "assignee": { "email": "string", "available_name": "string", "name": "string" } }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List conversation messages > GET https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/{id}/messages Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/list-conversation-messages Returns a page of messages for the conversation, newest-relevant first, using cursor-based pagination via the `before` query parameter. `meta` carries thread-level context (contact, labels, last-seen timestamps) and `payload` is the array of message objects (content, sender, attachments, message_type, status). This is the canonical way to load and lazily back-fill a long thread's history rather than relying on the messages embedded in the conversation detail response. ## Cursor-based pagination Use `before` to walk backwards through the thread history. - **First page**: Call without `before` to get the most recent page of messages. - **Older pages**: To fetch older messages, take the smallest (oldest) message `id` from the page you just received and pass it as `before` on the next request. Repeat until a page returns no further messages, which means you have reached the start of the thread. `before` is exclusive — it returns messages older than the supplied message id. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Integer id of the conversation whose messages to list. From a list endpoint or `GET.../conversation/{id}`. ## Query parameters - `before` (integer, optional): Cursor for fetching older messages. Pass the oldest message `id` from the previous page to retrieve messages that precede it. Omit on the first request to start from the most recent messages. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "labels": [ "string" ], "additional_attributes": {}, "contact": { "additional_attributes": {}, "custom_attributes": {}, "email": "string", "id": 0, "identifier": "string", "name": "string", "phone_number": "string", "thumbnail": "string", "blocked": false, "type": "string" }, "agent_last_seen_at": "string", "assignee_last_seen_at": "string" }, "payload": [ { "content": "string", "content_attributes": {}, "content_type": "string", "conversation_id": 0, "created_at": 0, "echo_id": "string", "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "sender": { "id": 0, "name": "string", "available_name": "string", "avatar_url": "string", "type": "string" }, "source_id": "string", "status": "string", "attachments": [ { "id": null, "account_id": null, "message_id": null, "thumb_url": null, "data_url": null, "file_size": null, "file_type": null, "extension": null, "width": null, "height": null, "created_at": null, "sender": null, "coordinates_lat": null, "coordinates_lng": null, "fallback_title": null, "meta": null } ] } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Send a message > POST https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/{id}/messages Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/send-a-message Sends a new message into the conversation. The path `id` selects the conversation; the JSON body carries the message `content` and options. Set `private: true` to post an internal note that the contact cannot see, or `false` for an outgoing reply. Returns the created message object (with its assigned `id`, `created_at`, `status`, and any `attachments`). For replies that include a file, use the `messages_with_attachment` endpoint instead. ## Request body fields Base your request on these fields (only `content` is typically required). - **content**: The message text/body to send. Fields: `content`. - **message_type**: Use `outgoing` (the default) for replies and for WhatsApp templates. `template` is not accepted and returns 422. Fields: `message_type`. - **private**: When true the message is an internal note visible only to agents; when false it is delivered to the contact. Fields: `private`. - **content_type / content_attributes**: Leave `content_type` as `text` and `content_attributes` empty. To show buttons, send an approved template that includes them. Fields: `content_type`, `content_attributes`. - **template_params**: Sends an approved WhatsApp template instead of a free-form message. See WhatsApp templates below. `campaign_id` is not needed. Fields: `template_params`. ## WhatsApp templates Templates are how you reach a customer outside the 24-hour window, for example to confirm or remind an appointment. - **New numbers**: To message a number that has never written to you, first open a conversation with POST /api/ext/omni_v2/whatsapp and `contact_phone`, then post the template into it here. Sent this way, the template shows in the inbox and the customer's reply continues in the same conversation. - **Shape** ("template_params": { "name", "language", "category", "processed_params" }): `name` and `language` must match a template that is approved for this WhatsApp number, for example `appointment_confirmation` and `en`. `category` is optional. Send these fields directly in `template_params`; WhatsApp's own `template` object and `components` list are not accepted. Fields: `template_params`. - **Variables** ("processed_params": { "1": "Sara", "2": "12 Oct, 10:00" }): Values for `{{1}}`, `{{2}}`, … in the template body, as strings, keyed by position. - **content**: Required. It is the text shown in the conversation history; the customer receives the template. Use the filled-in template text. Fields: `content`. - **24-hour window**: Outside the 24-hour customer-service window only templates are delivered. A plain message sent outside it is accepted but is then marked `failed`. - **Delivery status**: The response is the created message. Delivery to WhatsApp happens right after; read `status` (`sent`, `delivered`, `read` or `failed`) on the message from GET /api/ext/omni_v2/conversation/conversation/{id}/messages. - **Errors**: A message that cannot be created returns 422 with the reason in `detail`, for example `Message rejected: Validation failed: Name is required` when `template_params` has no `name`. Nothing is sent in that case. ## Instagram and TikTok reply windows Outgoing messages on Instagram and TikTok conversations are only delivered inside the platform's reply window. - **409 reply_window_closed**: Returned when the window has closed: 24 hours after the customer's last message on Instagram, 48 hours on TikTok. Private notes are not affected. On Instagram, team members replying from the platform can still reply for up to 7 days; API sends cannot. - **Not delivered**: If the platform refuses a message after it was accepted, a private note starting with "Not delivered" is added to the conversation. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Integer id of the conversation to post the message into. From a list endpoint or `GET.../conversation/{id}`. ## Body (application/json) ### Example: Outgoing reply Send a plain-text reply that is delivered to the contact. ```json { "content": "Thanks for reaching out — we have shipped your replacement and it will arrive within 3 business days.", "message_type": "outgoing", "private": false, "content_type": "text", "content_attributes": {} } ``` ### Example: Private note Post an internal note visible only to agents, not the contact. ```json { "content": "Customer is a VIP — escalate any further issues to the success team.", "message_type": "outgoing", "private": true, "content_type": "text", "content_attributes": {} } ``` ### Example: WhatsApp template Send an approved appointment-confirmation template with two variables. ```json { "content": "Hi Sara, your appointment is confirmed for 12 Oct, 10:00.", "message_type": "outgoing", "template_params": { "name": "appointment_confirmation", "category": "UTILITY", "language": "en", "processed_params": { "1": "Sara", "2": "12 Oct, 10:00" } } } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "content": "string", "content_attributes": {}, "content_type": "string", "conversation_id": 0, "created_at": 0, "echo_id": "string", "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "sender": { "id": 0, "name": "string", "available_name": "string", "avatar_url": "string", "type": "string" }, "source_id": "string", "status": "string", "attachments": [ { "id": 0, "account_id": 0, "message_id": 0, "thumb_url": "string", "data_url": "string", "file_size": 0, "file_type": "string", "extension": "string", "width": 0, "height": 0, "created_at": 0, "sender": { "id": 0, "name": "string", "available_name": "string", "avatar_url": "string", "type": "string", "availability_status": "string", "thumbnail": "string", "additional_attributes": {}, "custom_attributes": {}, "email": "string", "identifier": "string", "phone_number": "string", "blocked": false }, "coordinates_lat": 0, "coordinates_lng": 0, "fallback_title": "string", "meta": {} } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "content": "Thanks for reaching out — we have shipped your replacement and it will arrive within 3 business days.", "message_type": "outgoing", "private": false, "content_type": "text", "content_attributes": {} }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages" headers = { "X-API-Key": "", } payload = { "content": "Thanks for reaching out — we have shipped your replacement and it will arrive within 3 business days.", "message_type": "outgoing", "private": False, "content_type": "text", "content_attributes": {} } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "content": "Thanks for reaching out — we have shipped your replacement and it will arrive within 3 business days.", "message_type": "outgoing", "private": false, "content_type": "text", "content_attributes": {} }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "content": "Thanks for reaching out — we have shipped your replacement and it will arrive within 3 business days.", "message_type": "outgoing", "private": false, "content_type": "text", "content_attributes": {} }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Send a message with an attachment > POST https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/{id}/messages_with_attachment Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/send-a-message-with-an-attachment Sends a message into the conversation with a file attachment. Unlike the plain `messages` endpoint, this variant uses `application/x-www-form-urlencoded` (form fields) rather than a JSON body, so the message text and message options are submitted as form parameters alongside the attachment reference. Returns the same created-message object shape as the JSON send endpoint, including the resulting `attachments` array. Use this when a reply needs to include an image, document, or other file. ## Form-encoded request This endpoint does not accept JSON. - **Content-Type**: Submit the body as `application/x-www-form-urlencoded`. The message fields (such as content and message type) are sent as form fields rather than a JSON object, together with the attachment payload. - **When to use it**: Use this endpoint whenever the outgoing message carries a file. For text-only messages, internal notes, or templated content, use the JSON `POST.../{id}/messages` endpoint instead. Because the content type is form-urlencoded, the Try-it panel does not show a JSON body variant for this operation. ## Instagram and TikTok reply windows Outgoing messages on Instagram and TikTok conversations are only delivered inside the platform's reply window. - **409 reply_window_closed**: Returned when the window has closed: 24 hours after the customer's last message on Instagram, 48 hours on TikTok. Private notes are not affected. On Instagram, team members replying from the platform can still reply for up to 7 days; API sends cannot. - **Not delivered**: If the platform refuses a message after it was accepted, a private note starting with "Not delivered" is added to the conversation. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Integer id of the conversation to post the message into. From a list endpoint or `GET.../conversation/{id}`. ## Body (application/x-www-form-urlencoded) ## Response ### 200 Successful Response Content type: `application/json` ```json { "content": "string", "content_attributes": {}, "content_type": "string", "conversation_id": 0, "created_at": 0, "echo_id": "string", "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "sender": { "id": 0, "name": "string", "available_name": "string", "avatar_url": "string", "type": "string" }, "source_id": "string", "status": "string", "attachments": [ { "id": 0, "account_id": 0, "message_id": 0, "thumb_url": "string", "data_url": "string", "file_size": 0, "file_type": "string", "extension": "string", "width": 0, "height": 0, "created_at": 0, "sender": { "id": 0, "name": "string", "available_name": "string", "avatar_url": "string", "type": "string", "availability_status": "string", "thumbnail": "string", "additional_attributes": {}, "custom_attributes": {}, "email": "string", "identifier": "string", "phone_number": "string", "blocked": false }, "coordinates_lat": 0, "coordinates_lng": 0, "fallback_title": "string", "meta": {} } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages_with_attachment' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages_with_attachment" headers = {"X-API-Key": ""} response = requests.post(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const form = new FormData(); const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages_with_attachment", { method: "POST", headers: { "X-API-Key": "" }, body: form, }); const data = await response.json(); console.log(data); ``` ### Go ```go // Send multipart/form-data with mime/multipart: // POST https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//messages_with_attachment ``` --- # List conversation attachments > GET https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/{id}/attachments Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/list-conversation-attachments Returns every attachment shared in the conversation across all of its messages, in a single flat list. `meta.total_count` is the number of attachments and `payload` is the array of attachment objects (each with `data_url`, `thumb_url`, `file_type`, `extension`, `file_size`, dimensions for images, and the `sender`). Use this to render a media/files gallery for the thread without paging through every message. ## Attachment fields Each attachment in `payload` describes one shared file. - **data_url / thumb_url**: `data_url` is the full-resolution file URL; `thumb_url` is a thumbnail (for images/video). Both are served by the storage backend. - **file_type / extension / file_size**: MIME-style type, file extension, and size in bytes. For images, `width` and `height` are populated; for location attachments `coordinates_lat`/`coordinates_lng` are set. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Integer id of the conversation whose attachments to list. From a list endpoint or `GET.../conversation/{id}`. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "total_count": 0 }, "payload": [ { "id": 0, "account_id": 0, "message_id": 0, "thumb_url": "string", "data_url": "string", "file_size": 0, "file_type": "string", "extension": "string", "width": 0, "height": 0, "created_at": 0, "sender": { "id": 0, "name": "string", "available_name": "string", "avatar_url": "string", "type": "string", "availability_status": "string", "thumbnail": "string", "additional_attributes": {}, "custom_attributes": {}, "email": "string", "identifier": "string", "phone_number": "string", "blocked": false }, "coordinates_lat": 0, "coordinates_lng": 0, "fallback_title": "string", "meta": {} } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//attachments' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//attachments" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//attachments", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//attachments", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Assign conversation > POST https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/{id}/assign Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/assign-conversation Assigns the conversation to a workspace member (agent). The conversation is identified by the path `id` and the assignee by `assignee_id` in the JSON body. Returns a boolean indicating success. To unassign, refer to the inbox UI behavior — pass the appropriate assignee reference for your workspace; assignee identifiers correspond to workspace members. ## Resolving assignee_id ## Effect ## WhatsApp numbers ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Integer id of the conversation to assign. From a list endpoint or `GET.../conversation/{id}`. ## Body (application/json) ### Example: Assign to member Assign the conversation to a specific workspace member by their assignee id. ```json { "assignee_id": "42" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//assign' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "assignee_id": "42" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//assign" headers = { "X-API-Key": "", } payload = { "assignee_id": "42" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//assign", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "assignee_id": "42" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "assignee_id": "42" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//assign", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Change conversation status > POST https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/{id}/toggle_status Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/change-conversation-status Changes the lifecycle status of the conversation. The path `id` selects the conversation; the JSON body carries the new `status` (for example `open`, `resolved`, `pending`, or `snoozed`). When snoozing, include `snoozed_until` as an epoch timestamp indicating when the conversation should automatically reopen. Returns a boolean indicating success; the new status is reflected in the conversation's `status` (and `snoozed_until`) fields. ## Body fields Set the target status and, for snoozing, the wake time. - **status**: Target lifecycle status: `open`, `resolved`, `pending`, or `snoozed`. Fields: `status`. - **snoozed_until**: Epoch timestamp at which a snoozed conversation should automatically return to open. Required when `status` is `snoozed`; set to 0 (or omit) for non-snooze transitions. Fields: `snoozed_until`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Integer id of the conversation whose status to change. From a list endpoint or `GET.../conversation/{id}`. ## Body (application/json) ### Example: Resolve Mark the conversation as resolved. ```json { "status": "resolved", "snoozed_until": 0 } ``` ### Example: Reopen Reopen a previously resolved or snoozed conversation. ```json { "status": "open", "snoozed_until": 0 } ``` ### Example: Snooze Snooze the conversation until a specific epoch time, when it will reopen automatically. ```json { "status": "snoozed", "snoozed_until": 1750800000 } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//toggle_status' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "status": "resolved", "snoozed_until": 0 }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//toggle_status" headers = { "X-API-Key": "", } payload = { "status": "resolved", "snoozed_until": 0 } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//toggle_status", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "status": "resolved", "snoozed_until": 0 }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "status": "resolved", "snoozed_until": 0 }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//toggle_status", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Change conversation priority > POST https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/{id}/toggle_priority Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/change-conversation-priority Sets the priority of the conversation. The path `id` selects the conversation and the JSON body's `priority` field carries the new priority level (for example `urgent`, `high`, `medium`, `low`, or none to clear it). Returns a boolean indicating success. The resulting value is reflected in the `priority` field of the conversation in list and detail responses. ## Priority value ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Integer id of the conversation whose priority to set. From a list endpoint or `GET.../conversation/{id}`. ## Body (application/json) ### Example: Set urgent Mark the conversation as urgent. ```json { "priority": "urgent" } ``` ### Example: Set high Set a high priority level. ```json { "priority": "high" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//toggle_priority' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "priority": "urgent" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//toggle_priority" headers = { "X-API-Key": "", } payload = { "priority": "urgent" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//toggle_priority", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "priority": "urgent" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "priority": "urgent" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//toggle_priority", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Mark conversation as seen > POST https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation/{id}/update_last_seen Group: Conversations. Page: https://www.nabrah.ai/en/docs/api-reference/conversations/mark-conversation-as-seen Marks the conversation as seen by the agent/assignee, updating the server-side last-seen timestamp and clearing the unread state for the agent side. The path `id` selects the conversation; no request body is required. Returns the full updated conversation object (same shape as the get-conversation response), where `agent_last_seen_at`/`assignee_last_seen_at` and `unread_count` reflect the change. ## Effect ## No request body ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Integer id of the conversation to mark as seen. From a list endpoint or `GET.../conversation/{id}`. ## Response ### 200 Successful Response Content type: `application/json` ```json { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": { "source_id": null }, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [ { "id": 0, "account_id": 0, "message_id": 0, "thumb_url": "string", "data_url": "string", "file_size": 0, "file_type": "string", "extension": "string", "width": 0, "height": 0, "created_at": 0, "sender": null, "coordinates_lat": 0, "coordinates_lng": 0, "fallback_title": "string", "meta": {} } ] }, "messages": [ { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": null, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [ { "id": null, "account_id": null, "message_id": null, "thumb_url": null, "data_url": null, "file_size": null, "file_type": null, "extension": null, "width": null, "height": null, "created_at": null, "sender": null, "coordinates_lat": null, "coordinates_lng": null, "fallback_title": null, "meta": null } ] } ], "meta": { "channel": "string", "hmac_verified": false, "sender": { "additional_attributes": {}, "availability_status": "string", "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "id": 0, "identifier": "string", "last_activity_at": 0, "name": "string", "phone_number": "string", "thumbnail": "string" }, "assignee": { "email": "string", "available_name": "string", "name": "string" } }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//update_last_seen' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//update_last_seen" headers = {"X-API-Key": ""} response = requests.post(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//update_last_seen", { method: "POST", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/conversation/conversation//update_last_seen", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Tickets API Create and work support tickets, and configure the inboxes they arrive in. - [Create ticket](https://www.nabrah.ai/en/docs/api-reference/tickets/create-ticket.md) `POST /api/ext/omni_v2/ticket`: Creates a new support ticket (conversation) and returns the full ticket object including its server-assigned id, inbox, status, priority, and any seeded message. - [List tickets](https://www.nabrah.ai/en/docs/api-reference/tickets/list-tickets.md) `GET /api/ext/omni_v2/ticket`: Returns a paginated list of support tickets (conversations) for the project tied to your API key, with a meta object holding live counts by assignee bucket and a payload array of ticket objects. - [Filter tickets](https://www.nabrah.ai/en/docs/api-reference/tickets/filter-tickets.md) `POST /api/ext/omni_v2/ticket/filter`: Lists tickets using a structured server-side filter supplied in the request body, returning the same meta/payload shape as the generic list endpoint. - [List tickets by contact](https://www.nabrah.ai/en/docs/api-reference/tickets/list-tickets-by-contact.md) `POST /api/ext/omni_v2/ticket/contact/{contact_id}`: Lists tickets associated with the contact identified by contact_id, with optional server-side filtering in the request body and pagination via the page query parameter. - [Get ticket](https://www.nabrah.ai/en/docs/api-reference/tickets/get-ticket.md) `GET /api/ext/omni_v2/ticket/{id}`: Fetches a single ticket by its numeric id and returns the full ticket object: lifecycle status, priority, assignee and sender metadata, labels, the latest non-activity message, and an embedded messages array. - [List ticket messages](https://www.nabrah.ai/en/docs/api-reference/tickets/list-ticket-messages.md) `GET /api/ext/omni_v2/ticket/{id}/messages`: Returns the message history for a ticket, ordered for display, along with a meta block describing the contact, labels, and seen timestamps. - [Add a ticket message](https://www.nabrah.ai/en/docs/api-reference/tickets/add-a-ticket-message.md) `POST /api/ext/omni_v2/ticket/{id}/messages`: Adds a new message to the ticket identified by path id and returns the created message object (id, content, sender, type, timestamps). - [Add a ticket message with attachments](https://www.nabrah.ai/en/docs/api-reference/tickets/add-a-ticket-message-with-attachments.md) `POST /api/ext/omni_v2/ticket/{id}/messages_with_attachment`: Adds a new message to the ticket identified by path id with one or more file attachments (images, PDFs, etc.), and returns the created message object including its attachments. - [Assign ticket](https://www.nabrah.ai/en/docs/api-reference/tickets/assign-ticket.md) `POST /api/ext/omni_v2/ticket/{id}/assign`: Assigns the ticket identified by path id to a specific agent/user. - [Change ticket status](https://www.nabrah.ai/en/docs/api-reference/tickets/change-ticket-status.md) `POST /api/ext/omni_v2/ticket/{id}/toggle_status`: Changes the lifecycle status of the ticket identified by path id. - [Change ticket priority](https://www.nabrah.ai/en/docs/api-reference/tickets/change-ticket-priority.md) `POST /api/ext/omni_v2/ticket/{id}/toggle_priority`: Sets (toggles) the priority of the ticket identified by path id. - [Change ticket type](https://www.nabrah.ai/en/docs/api-reference/tickets/change-ticket-type.md) `POST /api/ext/omni_v2/ticket/{id}/update_type`: Updates the category (type) of the ticket identified by path id. - [Create ticket inbox](https://www.nabrah.ai/en/docs/api-reference/tickets/create-ticket-inbox.md) `POST /api/ext/omni_v2/ticket/inboxes`: Creates a ticket inbox for the project tied to your API key and returns the created inbox object including its server-assigned id. - [List ticket inboxes](https://www.nabrah.ai/en/docs/api-reference/tickets/list-ticket-inboxes.md) `GET /api/ext/omni_v2/ticket/inboxes`: Lists the ticket inboxes configured for the project tied to your API key. - [Update ticket inbox](https://www.nabrah.ai/en/docs/api-reference/tickets/update-ticket-inbox.md) `PATCH /api/ext/omni_v2/ticket/inboxes`: Updates the ticket inbox configuration for the project tied to your API key and returns the resulting inbox object. - [Delete ticket inbox](https://www.nabrah.ai/en/docs/api-reference/tickets/delete-ticket-inbox.md) `DELETE /api/ext/omni_v2/ticket/inboxes`: Deletes the ticket inbox configuration for the project tied to your API key and returns a boolean indicating success. --- # Create ticket > POST https://api.nabrah.ai/api/ext/omni_v2/ticket Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/create-ticket Creates a new support ticket (conversation) and returns the full ticket object including its server-assigned `id`, inbox, status, priority, and any seeded message. Provide a `subject` and `description`, then identify the customer either by an existing `contact_id` (from GET /api/ext/omni_v2/contact) or by `contact_phone` to look up/create the contact. Optional `agent_id`, `call_id`, and `ticket_type` let you attribute the ticket to a specific agent, link it to a call, and categorize it. ## Identifying the contact Supply exactly one customer identifier. - **contact_id (preferred)**: Numeric id of an existing contact. Obtain it from GET /api/ext/omni_v2/contact. Use this when the customer already exists in the project. - **contact_phone**: E.164 phone number used to resolve (or create) the contact when you do not have a `contact_id`. Useful for inbound channels keyed by phone such as WhatsApp. ## Request fields - **body fields**: `subject` and `description` form the ticket. `agent_id` attributes it to an AI/human agent, `call_id` links it to a related call record, and `ticket_type` categorizes it (must match a configured ticket type on the inbox). Fields: `subject`, `description`, `agent_id`, `call_id`, `ticket_type`, `contact_id`, `contact_phone`. ## Response Returns a GetTicketResponse — the same full ticket shape as GET /ticket/{id}. Read `id` from the response to reference the ticket later. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: By contact id Create a ticket for an existing contact identified by numeric contact_id. ```json { "subject": "Refund request for order #10482", "description": "Customer says the item arrived damaged and would like a full refund.", "agent_id": "agent_support_01", "call_id": "call_8f21 ", "ticket_type": "billing", "contact_id": 4821 } ``` ### Example: By contact phone Create a ticket and resolve/create the contact from a phone number when you have no contact_id. ```json { "subject": "WhatsApp inquiry about delivery", "description": "Customer asked when their order will be delivered.", "ticket_type": "support", "contact_phone": "+966512345678" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": { "source_id": null }, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string" }, "messages": [ { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": null, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string" } ], "meta": { "channel": "string", "hmac_verified": false, "sender": { "additional_attributes": {}, "availability_status": "string", "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "id": 0, "identifier": "string", "last_activity_at": 0, "name": "string", "phone_number": "string", "thumbnail": "string" }, "assignee": { "email": "string", "available_name": "string", "name": "string" } }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "subject": "Refund request for order #10482", "description": "Customer says the item arrived damaged and would like a full refund.", "agent_id": "agent_support_01", "call_id": "call_8f21 ", "ticket_type": "billing", "contact_id": 4821 }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket" headers = { "X-API-Key": "", } payload = { "subject": "Refund request for order #10482", "description": "Customer says the item arrived damaged and would like a full refund.", "agent_id": "agent_support_01", "call_id": "call_8f21 ", "ticket_type": "billing", "contact_id": 4821 } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "subject": "Refund request for order #10482", "description": "Customer says the item arrived damaged and would like a full refund.", "agent_id": "agent_support_01", "call_id": "call_8f21 ", "ticket_type": "billing", "contact_id": 4821 }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "subject": "Refund request for order #10482", "description": "Customer says the item arrived damaged and would like a full refund.", "agent_id": "agent_support_01", "call_id": "call_8f21 ", "ticket_type": "billing", "contact_id": 4821 }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/ticket", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List tickets > GET https://api.nabrah.ai/api/ext/omni_v2/ticket Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/list-tickets Returns a paginated list of support tickets (conversations) for the project tied to your API key, with a `meta` object holding live counts by assignee bucket and a `payload` array of ticket objects. Use the query parameters to scope the list by assignee relationship, lifecycle status, and a free-text search term, and to walk through pages. This is the quickest way to discover ticket `id` values needed by every other ticket endpoint. For more advanced server-side filtering, use the POST `/ticket/filter` endpoint instead. ## Authentication & scope All Omni v2 ticket endpoints are scoped to a single project by your Web API key. - **X-API-Key header**: Send your Web key (prefix `nb_`) in the `X-API-Key` header. The key is created in the dashboard under the Developers tab and is bound to exactly one project, so there is no `project_id` parameter on any ticket route. Validate the key with GET /api/ext/test before calling ticket endpoints. ## Response shape - **meta — bucket counts**: Aggregate counts used to render filter tabs in a support UI. These reflect the whole project, not just the current page. Fields: `all_count`, `mine_count`, `unassigned_count`, `assigned_count`. - **payload[] — ticket summaries**: Each element is a ticket (conversation) with lifecycle and assignment data, the latest non-activity message (`last_non_activity_message`), label list, priority/status strings, and unread/seen timestamps. Capture `payload[].id` to call GET /ticket/{id}, its messages, assign, toggle, and update_type endpoints. Fields: `id`, `inbox_id`, `status`, `priority`, `assignee_last_seen_at`, `last_non_activity_message`, `labels`, `unread_count`, `created_at`, `updated_at`. Timestamps such as created_at, updated_at, and last_activity_at are Unix epoch seconds (integers). ## Pagination - **page-based**: Pass `page` (1-based) to page through results. Increment until the returned `payload` array is empty. The `meta` counts stay constant across pages and can be used to estimate how many pages exist. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `assignee_type` (enum(all | me | unassigned | assigned), optional): Scopes the list by the ticket's assignment relationship to the current API context. One of `all`, `me`, `unassigned`, or `assigned`. `me` returns tickets assigned to the authenticated identity; `unassigned` returns tickets with no assignee; `assigned` returns tickets that have any assignee. Defaults to all tickets when omitted. - `status` (enum(open | pending | resolved | snoozed | all), optional): Filters tickets by lifecycle status. One of `open`, `pending`, `resolved`, `snoozed`, or `all`. Use `all` to ignore status filtering. Combine with `assignee_type` to narrow, e.g. open tickets assigned to no one. - `q` (string, optional): Free-text search term matched against ticket content and contact details. Optional. Leave empty to disable text search. Server-side matching covers message content and contact identifiers. - `page` (integer, optional): 1-based page number for pagination. Optional; defaults to the first page. Increase until `payload` comes back empty. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string" }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/ticket", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Filter tickets > POST https://api.nabrah.ai/api/ext/omni_v2/ticket/filter Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/filter-tickets Lists tickets using a structured server-side filter supplied in the request body, returning the same `meta`/`payload` shape as the generic list endpoint. Pass an array of filter objects under `filters`; results are paged with the `page` query parameter. Use this instead of GET /ticket when you need precise, composable filtering beyond the simple `assignee_type`/`status`/`q` query parameters. ## Filter body - **filters[]**: An array of filter condition objects combined to narrow the result set. Each object expresses a condition on a ticket attribute (such as status, priority, assignee, labels, or inbox). An empty object (`{}`) applies no constraint and returns all tickets in the project. Fields: `filters`. Build filter values from ids you discover elsewhere: inbox_id from GET /ticket/inboxes, contact ids from GET /api/ext/omni_v2/contact. ## Pagination - **page**: 1-based page number passed as a query parameter (the filter itself goes in the body). Increment until `payload` is empty. ## Response Same ListTicketsResponse shape as GET /api/ext/omni_v2/ticket: `meta` bucket counts plus a `payload` array of ticket summaries. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `page` (integer, optional): 1-based page number for pagination. Optional; defaults to the first page. The filter criteria are sent in the request body, not the query string. ## Body (application/json) ### Example: All tickets Empty filter — returns all tickets in the project. ```json { "filters": [ {} ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string" }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket/filter' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [ {} ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket/filter" headers = { "X-API-Key": "", } payload = { "filters": [ {} ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket/filter", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [ {} ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [ {} ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/ticket/filter", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List tickets by contact > POST https://api.nabrah.ai/api/ext/omni_v2/ticket/contact/{contact_id} Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/list-tickets-by-contact Lists tickets associated with the contact identified by `contact_id`, with optional server-side filtering in the request body and pagination via the `page` query parameter. The response returns a `meta` block with ticket counts (`all_count`, `mine_count`, `unassigned_count`, `assigned_count`) and a `payload` array of ticket objects (including status, priority, labels, and messages). Send an empty filter to return all of the contact's tickets. ## Authentication - **X-API-Key header**: Send your Web API key (prefix `nb_`) in the `X-API-Key` header. The key is scoped to one project; the contact must belong to that project. ## Filtering and pagination - **filters**: The request body takes a `filters` array. Send `[{}]` (an empty filter) to return all of the contact's tickets, or add filter objects to narrow the results. - **page**: Use the `page` query parameter to page through results. The response `meta` reports `all_count`, `mine_count`, `unassigned_count`, and `assigned_count`. Fields: `meta.all_count`, `meta.mine_count`, `meta.unassigned_count`, `meta.assigned_count`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `contact_id` (integer, required): Numeric identifier of the contact whose tickets are listed. Obtained from `GET /api/ext/omni_v2/contact` or `GET /api/ext/omni_v2/contact/search` (the contact `id` field). ## Query parameters - `page` (integer, optional): 1-based page number of tickets to retrieve. Omit to fetch the first page. Increment until the returned payload array is empty to iterate over all tickets. ## Body (application/json) ### Example: All tickets Return every ticket for the contact (empty filter). ```json { "filters": [ {} ] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string" }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket/contact/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [ {} ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket/contact/" headers = { "X-API-Key": "", } payload = { "filters": [ {} ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket/contact/", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [ {} ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [ {} ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/ticket/contact/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get ticket > GET https://api.nabrah.ai/api/ext/omni_v2/ticket/{id} Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/get-ticket Fetches a single ticket by its numeric `id` and returns the full ticket object: lifecycle status, priority, assignee and sender metadata, labels, the latest non-activity message, and an embedded `messages` array. Use this to render a ticket detail view or to read current state before mutating it (assign, toggle priority/status, update type). Obtain the `id` from GET /api/ext/omni_v2/ticket or the create-ticket response. ## Response shape - **Top-level ticket**: Core ticket fields plus `meta.sender` (the contact) and `meta.assignee` (the assigned user). `can_reply` indicates whether a reply is currently allowed on the channel. Fields: `id`, `inbox_id`, `status`, `priority`, `can_reply`, `labels`, `meta`, `snoozed_until`, `unread_count`. - **messages[]**: Embedded recent messages. For the full, paginated message history use GET /ticket/{id}/messages with the `before` cursor. Timestamp fields are Unix epoch seconds. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Numeric identifier of the ticket to retrieve. Comes from the `id` field of GET /api/ext/omni_v2/ticket list items, POST /ticket/filter results, or the create-ticket response. ## Response ### 200 Successful Response Content type: `application/json` ```json { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": { "source_id": null }, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string" }, "messages": [ { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": null, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string" } ], "meta": { "channel": "string", "hmac_verified": false, "sender": { "additional_attributes": {}, "availability_status": "string", "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "id": 0, "identifier": "string", "last_activity_at": 0, "name": "string", "phone_number": "string", "thumbnail": "string" }, "assignee": { "email": "string", "available_name": "string", "name": "string" } }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/ticket/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List ticket messages > GET https://api.nabrah.ai/api/ext/omni_v2/ticket/{id}/messages Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/list-ticket-messages Returns the message history for a ticket, ordered for display, along with a `meta` block describing the contact, labels, and seen timestamps. Results are paginated with the `before` cursor for loading older messages (infinite-scroll style). Use the embedded ticket `messages` from GET /ticket/{id} for a quick preview, but use this endpoint to page through the complete history. ## Cursor pagination This endpoint pages backwards in time using a message id cursor, not page numbers. - **before**: Pass the numeric `id` of the oldest message you have already loaded to fetch the page of messages immediately before it. Omit `before` on the first request to get the most recent page, then repeat with the smallest `id` from `payload` until no older messages are returned. ## Response shape - **meta**: Conversation-level context: the `contact` object, applied `labels`, and agent/assignee last-seen markers. Fields: `contact`, `labels`, `agent_last_seen_at`, `assignee_last_seen_at`. - **payload[] — messages**: Each message includes its content, type, sender, privacy flag, and timestamps. `message_type` distinguishes incoming vs outgoing/activity messages; `private` marks internal notes. Fields: `id`, `content`, `content_type`, `message_type`, `private`, `sender`, `created_at`, `conversation_id`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Numeric identifier of the ticket whose messages to list. From GET /api/ext/omni_v2/ticket or the create-ticket response. ## Query parameters - `before` (integer, optional): Message id cursor for fetching older messages. Optional integer. Set it to the smallest message `id` from the previous page to load the next-older page. Omit for the most recent page. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "labels": [ "string" ], "additional_attributes": {}, "contact": { "additional_attributes": {}, "custom_attributes": {}, "email": "string", "id": 0, "identifier": "string", "name": "string", "phone_number": "string", "thumbnail": "string", "blocked": false, "type": "string" }, "agent_last_seen_at": "string", "assignee_last_seen_at": "string" }, "payload": [ { "content": "string", "content_attributes": {}, "content_type": "string", "conversation_id": 0, "created_at": 0, "echo_id": "string", "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "sender": { "id": 0, "name": "string", "type": "string", "email": "string", "phone_number": "string" }, "source_id": "string", "status": "string" } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket//messages' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket//messages" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket//messages", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/ticket//messages", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Add a ticket message > POST https://api.nabrah.ai/api/ext/omni_v2/ticket/{id}/messages Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/add-a-ticket-message Adds a new message to the ticket identified by path `id` and returns the created message object (id, content, sender, type, timestamps). Provide the text in the `message` field. The message is posted into the ticket conversation as a reply on the underlying channel where applicable. ## Request - **message**: The text content of the message to add to the ticket. Fields: `message`. ## Response Returns the created message with its server-assigned `id`, `message_type`, `sender`, and `created_at` (Unix epoch seconds). ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Numeric identifier of the ticket to add a message to. From GET /api/ext/omni_v2/ticket or the create-ticket response. ## Body (application/json) ### Example: Add message Post a reply/message into the ticket conversation. ```json { "message": "Thanks for reaching out! We've issued your refund and you should see it within 3-5 business days." } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "content": "string", "content_attributes": {}, "content_type": "string", "conversation_id": 0, "created_at": 0, "echo_id": "string", "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "sender": { "id": 0, "name": "string", "type": "string", "email": "string", "phone_number": "string" }, "source_id": "string", "status": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket//messages' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "message": "Thanks for reaching out! We'\''ve issued your refund and you should see it within 3-5 business days." }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket//messages" headers = { "X-API-Key": "", } payload = { "message": "Thanks for reaching out! We've issued your refund and you should see it within 3-5 business days." } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket//messages", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "message": "Thanks for reaching out! We've issued your refund and you should see it within 3-5 business days." }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "message": "Thanks for reaching out! We've issued your refund and you should see it within 3-5 business days." }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/ticket//messages", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Add a ticket message with attachments > POST https://api.nabrah.ai/api/ext/omni_v2/ticket/{id}/messages_with_attachment Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/add-a-ticket-message-with-attachments Adds a new message to the ticket identified by path `id` with one or more file attachments (images, PDFs, etc.), and returns the created message object including its `attachments`. Send the request as `multipart/form-data`: put the note text in the `content` field and each file in an `attachments` field (repeat the field to send several files in one message). The message is posted into the ticket conversation as an outgoing reply on the underlying channel where applicable. For text-only messages without files, use POST /api/ext/omni_v2/ticket/{id}/messages instead. ## Multipart form fields Send the request as multipart/form-data (not JSON). - **content**: The text content of the message. May be empty when only attachments are sent. - **attachments**: One or more files to attach. Repeat the `attachments` field once per file to upload several files in a single message. ## Allowed files & limits Uploads are restricted for security. - **Allowed types**: Images (png, jpg, jpeg, gif, webp), pdf, xlsx, csv. SVG and executable/script files are rejected. - **Size & count**: Up to 10 files per message, 10 MB per file. Disallowed type → 415; oversized file → 413; too many files → 400. ## Response Returns the created message with its server-assigned `id`, `message_type`, `sender`, `created_at` (Unix epoch seconds), and an `attachments` array — each entry with `data_url`, `thumb_url`, `file_type`, `file_size`, and `extension`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Numeric identifier of the ticket to add a message to. From GET /api/ext/omni_v2/ticket or the create-ticket response. ## Body (multipart/form-data) ## Response ### 200 Successful Response Content type: `application/json` ```json { "content": "string", "content_attributes": {}, "content_type": "string", "conversation_id": 0, "created_at": 0, "echo_id": "string", "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "sender": { "id": 0, "name": "string", "type": "string", "email": "string", "phone_number": "string" }, "source_id": "string", "status": "string", "attachments": [ { "id": 0, "account_id": 0, "message_id": 0, "thumb_url": "string", "data_url": "string", "file_size": 0, "file_type": "string", "extension": "string" } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket//messages_with_attachment' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket//messages_with_attachment" headers = {"X-API-Key": ""} response = requests.post(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const form = new FormData(); const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket//messages_with_attachment", { method: "POST", headers: { "X-API-Key": "" }, body: form, }); const data = await response.json(); console.log(data); ``` ### Go ```go // Send multipart/form-data with mime/multipart: // POST https://api.nabrah.ai/api/ext/omni_v2/ticket//messages_with_attachment ``` --- # Assign ticket > POST https://api.nabrah.ai/api/ext/omni_v2/ticket/{id}/assign Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/assign-ticket Assigns the ticket identified by path `id` to a specific agent/user. Pass the target user in `assignee_id` in the request body. Returns a boolean indicating whether the assignment succeeded. The user must belong to the same project as the ticket. ## Request - **assignee_id**: Identifier of the user/agent to assign the ticket to. After a successful assignment the ticket moves into the `assigned` bucket (reflected in list `meta` counts). Fields: `assignee_id`. Response is a bare boolean: `true` on success, `false` otherwise. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Numeric identifier of the ticket to assign. From GET /api/ext/omni_v2/ticket or the create-ticket response. ## Body (application/json) ### Example: Assign to user Assign the ticket to a specific user/agent. ```json { "assignee_id": "user_7c19a2" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket//assign' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "assignee_id": "user_7c19a2" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket//assign" headers = { "X-API-Key": "", } payload = { "assignee_id": "user_7c19a2" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket//assign", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "assignee_id": "user_7c19a2" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "assignee_id": "user_7c19a2" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/ticket//assign", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Change ticket status > POST https://api.nabrah.ai/api/ext/omni_v2/ticket/{id}/toggle_status Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/change-ticket-status Changes the lifecycle status of the ticket identified by path `id`. Send the target `status` in the request body; when snoozing, also send `snoozed_until` as the Unix epoch (seconds) at which the ticket should reopen. Returns a boolean indicating success. ## Request - **status**: Target lifecycle status, e.g. `open`, `pending`, `resolved`, or `snoozed`. These mirror the `status` filter on the list endpoints. Fields: `status`. - **snoozed_until**: Required only when `status` is `snoozed`: the Unix epoch timestamp (seconds) at which the ticket should automatically return to `open`. For non-snooze statuses set it to 0 or omit. Fields: `snoozed_until`. Response is a bare boolean: `true` on success. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Numeric identifier of the ticket whose status to change. From GET /api/ext/omni_v2/ticket or the create-ticket response. ## Body (application/json) ### Example: Resolve Mark the ticket as resolved. ```json { "status": "resolved", "snoozed_until": 0 } ``` ### Example: Reopen Set the ticket back to open. ```json { "status": "open", "snoozed_until": 0 } ``` ### Example: Snooze Snooze the ticket until a future Unix timestamp (seconds). ```json { "status": "snoozed", "snoozed_until": 1782518400 } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket//toggle_status' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "status": "resolved", "snoozed_until": 0 }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket//toggle_status" headers = { "X-API-Key": "", } payload = { "status": "resolved", "snoozed_until": 0 } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket//toggle_status", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "status": "resolved", "snoozed_until": 0 }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "status": "resolved", "snoozed_until": 0 }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/ticket//toggle_status", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Change ticket priority > POST https://api.nabrah.ai/api/ext/omni_v2/ticket/{id}/toggle_priority Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/change-ticket-priority Sets (toggles) the priority of the ticket identified by path `id`. Send the desired level in the `priority` field of the request body. Returns a boolean indicating success. High-priority tickets are surfaced more prominently in support queues. ## Request - **priority**: The priority level to apply, e.g. `urgent`, `high`, `medium`, `low`. Setting the same value the ticket already has may clear it back to no priority (toggle behavior). Fields: `priority`. Response is a bare boolean: `true` on success. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Numeric identifier of the ticket whose priority to set. From GET /api/ext/omni_v2/ticket or the create-ticket response. ## Body (application/json) ### Example: Set urgent Mark the ticket as urgent. ```json { "priority": "urgent" } ``` ### Example: Set low Mark the ticket as low priority. ```json { "priority": "low" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket//toggle_priority' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "priority": "urgent" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket//toggle_priority" headers = { "X-API-Key": "", } payload = { "priority": "urgent" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket//toggle_priority", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "priority": "urgent" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "priority": "urgent" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/ticket//toggle_priority", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Change ticket type > POST https://api.nabrah.ai/api/ext/omni_v2/ticket/{id}/update_type Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/change-ticket-type Updates the category (type) of the ticket identified by path `id`. Send the new value in the `ticket_type` field. Returns a boolean indicating success. The value should match one of the ticket types configured on the ticket's inbox (`config.ticket_types`). ## Request - **ticket_type**: The new ticket category. Should be one of the types allowed by the inbox configuration (see the inbox `config.ticket_types` list). Fields: `ticket_type`. Response is a bare boolean: `true` on success. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `id` (integer, required): Numeric identifier of the ticket whose type to update. From GET /api/ext/omni_v2/ticket or the create-ticket response. ## Body (application/json) ### Example: Update type Recategorize the ticket. ```json { "ticket_type": "billing" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket//update_type' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "ticket_type": "billing" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket//update_type" headers = { "X-API-Key": "", } payload = { "ticket_type": "billing" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket//update_type", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "ticket_type": "billing" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "ticket_type": "billing" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/ticket//update_type", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Create ticket inbox > POST https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/create-ticket-inbox Creates a ticket inbox for the project tied to your API key and returns the created inbox object including its server-assigned `id`. Provide a display `name` and a `config` object that defines ticketing behavior for the channel/queue. The `config.ticket_types` you set here become the allowed categories used by ticket create and update_type operations. ## Request fields - **name**: Human-readable display name for the inbox. Fields: `name`. - **config**: `auto_assign` toggles automatic assignment of new tickets. `ticket_types` is the list of allowed ticket categories. `alway_create_ticket` (note the spelling) forces a ticket for every inbound conversation. `assign_list` is the pool of user ids eligible for auto-assignment. `lock_to_single_open_ticket` limits a contact to one open ticket at a time. Fields: `auto_assign`, `ticket_types`, `alway_create_ticket`, `assign_list`, `lock_to_single_open_ticket`. Returns a GetInboxResponse — the created inbox object (id, inbox_type, inbox_id, project_id, name, config, created_at). ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Create inbox Create a ticket inbox with ticketing config. ```json { "name": "WhatsApp Support", "config": { "auto_assign": true, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": false, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": true } } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": 0, "inbox_type": "whatsapp", "inbox_id": 0, "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "WhatsApp Support", "config": { "auto_assign": true, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": false, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": true } }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes" headers = { "X-API-Key": "", } payload = { "name": "WhatsApp Support", "config": { "auto_assign": True, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": False, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": True } } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "WhatsApp Support", "config": { "auto_assign": true, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": false, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": true } }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "WhatsApp Support", "config": { "auto_assign": true, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": false, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": true } }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List ticket inboxes > GET https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/list-ticket-inboxes Lists the ticket inboxes configured for the project tied to your API key. Each inbox represents a channel/queue (e.g. WhatsApp) with a `config` object controlling ticketing behavior. Use the returned `id`/`inbox_id` values when creating tickets or building filters, and to manage inbox config via PATCH/POST/DELETE on the same path. ## Response shape - **inboxes[]**: Each inbox includes its database `id`, the channel `inbox_id`, `inbox_type` (e.g. `whatsapp`), `project_id`, display `name`, the `config` object, and `created_at` (ISO 8601 timestamp). Fields: `id`, `inbox_id`, `inbox_type`, `project_id`, `name`, `config`, `created_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "inboxes": [ { "id": 0, "inbox_type": "whatsapp", "inbox_id": 0, "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Update ticket inbox > PATCH https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/update-ticket-inbox Updates the ticket inbox configuration for the project tied to your API key and returns the resulting inbox object. Send the new settings inside the `config` object; fields control auto-assignment, the catalog of allowed ticket types, whether every inbound conversation always opens a ticket, the pool of agents eligible for assignment, and whether a contact is locked to a single open ticket. ## Config fields - **config**: `auto_assign` toggles automatic assignment of new tickets. `ticket_types` is the list of allowed categories (these are the valid values for a ticket's `ticket_type`). `alway_create_ticket` (note the spelling) forces a ticket for every inbound conversation. `assign_list` is the pool of user ids eligible for auto-assignment. `lock_to_single_open_ticket` prevents a contact from having more than one open ticket at a time. Fields: `auto_assign`, `ticket_types`, `alway_create_ticket`, `assign_list`, `lock_to_single_open_ticket`. Returns a GetInboxResponse — the updated inbox object (id, inbox_type, inbox_id, project_id, name, config, created_at). ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Update config Update the inbox ticketing configuration. ```json { "config": { "auto_assign": true, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": false, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": true } } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": 0, "inbox_type": "whatsapp", "inbox_id": 0, "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request PATCH \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "config": { "auto_assign": true, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": false, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": true } }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes" headers = { "X-API-Key": "", } payload = { "config": { "auto_assign": True, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": False, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": True } } response = requests.patch(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes", { method: "PATCH", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "config": { "auto_assign": true, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": false, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": true } }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "config": { "auto_assign": true, "ticket_types": [ "support", "billing", "sales" ], "alway_create_ticket": false, "assign_list": [ "user_7c19a2", "user_3ab8f1" ], "lock_to_single_open_ticket": true } }`) req, _ := http.NewRequest("PATCH", "https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete ticket inbox > DELETE https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes Group: Tickets. Page: https://www.nabrah.ai/en/docs/api-reference/tickets/delete-ticket-inbox Deletes the ticket inbox configuration for the project tied to your API key and returns a boolean indicating success. An inbox is the channel/queue configuration that governs how incoming conversations become tickets (auto-assign rules, allowed ticket types, etc.). Deleting it removes that ticketing configuration; list current inboxes with GET /ticket/inboxes first. ## Effect - **What is removed**: Removes the ticket inbox config record (its `config` settings such as `auto_assign`, `ticket_types`, `assign_list`, and `lock_to_single_open_ticket`) for the project. Existing tickets are not part of the request body; this operation targets the inbox configuration itself. Response is a bare boolean: `true` on success. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/omni_v2/ticket/inboxes", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # WhatsApp API Connect WhatsApp numbers, work their conversations and put an agent on each number. - [Connect a WhatsApp number](https://www.nabrah.ai/en/docs/api-reference/whatsapp/connect-a-whatsapp-number.md) `POST /api/ext/omni_v2/whatsapp/inboxes`: Connect a new WhatsApp number to the project by creating a WhatsApp inbox. - [List WhatsApp inboxes](https://www.nabrah.ai/en/docs/api-reference/whatsapp/list-whatsapp-inboxes.md) `GET /api/ext/omni_v2/whatsapp/inboxes`: List the WhatsApp inboxes connected to the project, oldest first. - [Get WhatsApp inbox](https://www.nabrah.ai/en/docs/api-reference/whatsapp/get-whatsapp-inbox.md) `GET /api/ext/omni_v2/whatsapp/inboxes/{inbox_id}`: Retrieve the full details of a single WhatsApp inbox by its id. - [Update WhatsApp inbox](https://www.nabrah.ai/en/docs/api-reference/whatsapp/update-whatsapp-inbox.md) `PATCH /api/ext/omni_v2/whatsapp/inboxes`: Update the configuration of one of the project's WhatsApp inboxes, such as its WhatsApp Cloud API credentials, phone number details, webhook verify token, auto-assignment behavior, and display name. - [Delete WhatsApp inbox](https://www.nabrah.ai/en/docs/api-reference/whatsapp/delete-whatsapp-inbox.md) `DELETE /api/ext/omni_v2/whatsapp/inboxes`: Delete one of the project's WhatsApp inboxes, disconnecting that WhatsApp number from the project. - [Create WhatsApp conversation](https://www.nabrah.ai/en/docs/api-reference/whatsapp/create-whatsapp-conversation.md) `POST /api/ext/omni_v2/whatsapp`: Create a new WhatsApp conversation (ticket) for a contact and return the full conversation object. - [List WhatsApp conversations](https://www.nabrah.ai/en/docs/api-reference/whatsapp/list-whatsapp-conversations.md) `GET /api/ext/omni_v2/whatsapp`: List WhatsApp conversations (tickets) on one of the project's WhatsApp numbers, with optional filtering by assignment and status, plus search by the customer's phone number or message text. - [Filter WhatsApp conversations](https://www.nabrah.ai/en/docs/api-reference/whatsapp/filter-whatsapp-conversations.md) `POST /api/ext/omni_v2/whatsapp/filter`: List WhatsApp conversations on one of the project's WhatsApp numbers using a structured filter payload instead of simple query parameters. - [List WhatsApp conversations by contact](https://www.nabrah.ai/en/docs/api-reference/whatsapp/list-whatsapp-conversations-by-contact.md) `POST /api/ext/omni_v2/whatsapp/contact/{contact_id}`: Lists the WhatsApp conversations of the contact identified by contact_id on one of the project's WhatsApp numbers, with optional server-side filtering in the request body and pagination via the page query parameter. - [Assign an agent to a WhatsApp number](https://www.nabrah.ai/en/docs/api-reference/whatsapp/assign-an-agent-to-a-whatsapp-number.md) `POST /api/ext/omni_v2/whatsapp/assign_chatbot`: Attach an AI agent to one of the project's WhatsApp numbers so it automatically replies to incoming WhatsApp messages on that number. - [Get the agent on a WhatsApp number](https://www.nabrah.ai/en/docs/api-reference/whatsapp/get-the-agent-on-a-whatsapp-number.md) `GET /api/ext/omni_v2/whatsapp/get_chatbot`: Retrieve the agent currently answering on one of the project's WhatsApp numbers. - [Remove the agent from a WhatsApp number](https://www.nabrah.ai/en/docs/api-reference/whatsapp/remove-the-agent-from-a-whatsapp-number.md) `DELETE /api/ext/omni_v2/whatsapp/unassign_chatbot`: Remove the agent from one of the project's WhatsApp numbers, stopping automatic AI replies on that number. - [Get WhatsApp analytics](https://www.nabrah.ai/en/docs/api-reference/whatsapp/get-whatsapp-analytics.md) `POST /api/ext/omni_v2/whatsapp/analytics`: Get aggregated WhatsApp conversation analytics for a date window. --- # Connect a WhatsApp number > POST https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/connect-a-whatsapp-number Connect a new WhatsApp number to the project by creating a WhatsApp inbox. Provide the WhatsApp Cloud API credentials and phone details so Nabrah can send and receive messages on that number. The response returns the created inbox summary including its generated `id`; `chatbot_agent_id` and `chatbot_agent_name` are null, since a new number has no agent yet. ## Required connection details These come from your Meta WhatsApp Cloud API setup. - **name**: Display name for the inbox. Fields: `name`. - **WhatsApp Cloud API credentials**: `phone_number` (human-readable), `phone_number_id` (Cloud API id), `business_account_id` (WABA id), and `api_key` (Meta access token). Fields: `phone_number`, `phone_number_id`, `business_account_id`, `api_key`. - **enable_auto_assignment**: Set true to automatically assign new conversations on this inbox to agents. Fields: `enable_auto_assignment`. The created inbox's `id` becomes the `inbox_id` used by other endpoints. Re-list with GET /api/ext/omni_v2/whatsapp/inboxes. Numbers can also be connected through Meta sign-in in the app, without entering these details; that flow is not available through the API. ## Limits - **Number limit**: A project can connect up to `max_inboxes` WhatsApp numbers (GET /api/ext/omni_v2/whatsapp/inboxes). Connecting one more returns 409; contact support to raise the limit. - **Already connected**: Connecting a number whose `phone_number_id` or `phone_number` is already connected to the project returns 409. Fields: `phone_number_id`, `phone_number`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Create inbox ```json { "name": "Support WhatsApp", "phone_number": "+966512345678", "phone_number_id": "109876543210987", "business_account_id": "104958611234567", "api_key": "EAAGm0PX4ZCpsBA...", "enable_auto_assignment": true } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "inbox_type": "whatsapp", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "inbox_id": 0, "chatbot_agent_id": "00000000-0000-4000-8000-000000000000", "chatbot_agent_name": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Support WhatsApp", "phone_number": "+966512345678", "phone_number_id": "109876543210987", "business_account_id": "104958611234567", "api_key": "EAAGm0PX4ZCpsBA...", "enable_auto_assignment": true }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes" headers = { "X-API-Key": "", } payload = { "name": "Support WhatsApp", "phone_number": "+966512345678", "phone_number_id": "109876543210987", "business_account_id": "104958611234567", "api_key": "EAAGm0PX4ZCpsBA...", "enable_auto_assignment": True } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Support WhatsApp", "phone_number": "+966512345678", "phone_number_id": "109876543210987", "business_account_id": "104958611234567", "api_key": "EAAGm0PX4ZCpsBA...", "enable_auto_assignment": true }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Support WhatsApp", "phone_number": "+966512345678", "phone_number_id": "109876543210987", "business_account_id": "104958611234567", "api_key": "EAAGm0PX4ZCpsBA...", "enable_auto_assignment": true }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List WhatsApp inboxes > GET https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/list-whatsapp-inboxes List the WhatsApp inboxes connected to the project, oldest first. Each inbox represents a connected WhatsApp number and includes its `id`, `inbox_type`, `project_id`, display `name`, `config`, `created_at`, and the agent answering on it. Use the returned `id` as the `inbox_id` for the other WhatsApp endpoints. ## Where inbox IDs come from This is the canonical source for `inbox_id` values used by other WhatsApp endpoints. - **inboxes[].id**: The UUID of each WhatsApp inbox. Pass it as `inbox_id` to the other WhatsApp endpoints, or as the `{inbox_id}` path parameter to GET full inbox details. Fields: `id`, `inbox_type`, `project_id`, `name`, `config`, `created_at`. - **inboxes[].inbox_id**: A numeric id for the number. Conversations carry the same value in their `inbox_id` field, so you can tell which number a conversation came in on. Do not pass it as `inbox_id` to other endpoints. Fields: `inbox_id`. - **Answering agent**: The agent auto-replying on the number, or null when none is assigned. Fields: `chatbot_agent_id`, `chatbot_agent_name`. - **max_inboxes**: How many WhatsApp numbers the project may connect. Contact support to raise it. Fields: `max_inboxes`. - **How the number was connected**: `config.connection_method` is `embedded_signup` for numbers connected through Meta sign-in in the app, and is absent for numbers connected with their credentials. Numbers connected through Meta sign-in also include `config.verified_name` and `config.name_status` (the display name and its review status). `config.meta_disconnected` is true when access to the number was removed in Meta; reconnect it from the app to keep its conversations and settings. Fields: `config.connection_method`, `config.verified_name`, `config.name_status`, `config.meta_disconnected`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "inboxes": [ { "id": "00000000-0000-4000-8000-000000000000", "inbox_type": "whatsapp", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "inbox_id": 0, "chatbot_agent_id": "00000000-0000-4000-8000-000000000000", "chatbot_agent_name": "string" } ], "max_inboxes": 0 } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get WhatsApp inbox > GET https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes/{inbox_id} Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/get-whatsapp-inbox Retrieve the full details of a single WhatsApp inbox by its id. The response returns the inbox's `id`, `inbox_type`, `project_id`, `name`, `config`, `created_at`, and the agent answering on it (`chatbot_agent_id` and `chatbot_agent_name`, null when none). Use this to inspect a specific connected WhatsApp number's configuration. ## Where the inbox_id comes from Obtain the `inbox_id` from the inbox list endpoint. Call GET /api/ext/omni_v2/whatsapp/inboxes and use any returned `inboxes[].id` value. ## How the number was connected `config.connection_method` is `embedded_signup` for numbers connected through Meta sign-in in the app, and is absent for numbers connected with their credentials. `config.meta_disconnected` is true when access to the number was removed in Meta; reconnect it from the app. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `inbox_id` (string, required): The UUID of the WhatsApp inbox to fetch. Get this value from GET /api/ext/omni_v2/whatsapp/inboxes (the `inboxes[].id` field). ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "inbox_type": "whatsapp", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "inbox_id": 0, "chatbot_agent_id": "00000000-0000-4000-8000-000000000000", "chatbot_agent_name": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Update WhatsApp inbox > PATCH https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/update-whatsapp-inbox Update the configuration of one of the project's WhatsApp inboxes, such as its WhatsApp Cloud API credentials, phone number details, webhook verify token, auto-assignment behavior, and display name. `api_key`, `business_account_id`, `phone_number_id` and `phone_number` are required on every update; leave out `name` or `enable_auto_assignment` to keep their current values. Leave out `webhook_verify_token` to keep the current token, or send a new value to change it. The response returns the updated inbox summary (`id`, `inbox_type`, `project_id`, `name`, `config`, `created_at`) and the agent answering on it (`chatbot_agent_id`, `chatbot_agent_name`). ## Configuration fields These map to the WhatsApp Cloud API connection for the inbox. - **WhatsApp Cloud API credentials**: `api_key` is the Meta access token, `business_account_id` is the WhatsApp Business Account (WABA) id, `phone_number_id` is the Cloud API phone number id, and `phone_number` is the human-readable number. Fields: `api_key`, `business_account_id`, `phone_number_id`, `phone_number`. - **webhook_verify_token**: The verify token used when Meta validates the webhook subscription. Leave it out to keep the current token, or send a new value to change it. Fields: `webhook_verify_token`. - **Behavior**: `enable_auto_assignment` toggles automatic assignment of new conversations to agents; `name` sets the inbox display name. Leave `enable_auto_assignment` out to keep the current setting. Fields: `enable_auto_assignment`, `name`. - **Duplicate numbers**: Changing `phone_number_id` or `phone_number` to a number already connected to another WhatsApp inbox of the project returns 409. Fields: `phone_number_id`, `phone_number`. ## Choosing the WhatsApp number A project can connect more than one WhatsApp number. `inbox_id` selects the number this call works on. - **One number**: `inbox_id` is optional; the project's only WhatsApp number is used. - **More than one number**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's WhatsApp numbers returns 404. Fields: `inbox_id`. List the numbers and their ids with GET /api/ext/omni_v2/whatsapp/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the WhatsApp number to use, from GET /api/ext/omni_v2/whatsapp/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one WhatsApp number; required once it has more than one. ## Body (application/json) ### Example: Update inbox config ```json { "api_key": "EAAGm0PX4ZCpsBA...", "business_account_id": "104958611234567", "phone_number_id": "109876543210987", "phone_number": "+966512345678", "webhook_verify_token": "my-verify-token", "enable_auto_assignment": true, "name": "Support WhatsApp" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "inbox_type": "whatsapp", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "inbox_id": 0, "chatbot_agent_id": "00000000-0000-4000-8000-000000000000", "chatbot_agent_name": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request PATCH \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "api_key": "EAAGm0PX4ZCpsBA...", "business_account_id": "104958611234567", "phone_number_id": "109876543210987", "phone_number": "+966512345678", "webhook_verify_token": "my-verify-token", "enable_auto_assignment": true, "name": "Support WhatsApp" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes" headers = { "X-API-Key": "", } payload = { "api_key": "EAAGm0PX4ZCpsBA...", "business_account_id": "104958611234567", "phone_number_id": "109876543210987", "phone_number": "+966512345678", "webhook_verify_token": "my-verify-token", "enable_auto_assignment": True, "name": "Support WhatsApp" } response = requests.patch(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes", { method: "PATCH", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "api_key": "EAAGm0PX4ZCpsBA...", "business_account_id": "104958611234567", "phone_number_id": "109876543210987", "phone_number": "+966512345678", "webhook_verify_token": "my-verify-token", "enable_auto_assignment": true, "name": "Support WhatsApp" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "api_key": "EAAGm0PX4ZCpsBA...", "business_account_id": "104958611234567", "phone_number_id": "109876543210987", "phone_number": "+966512345678", "webhook_verify_token": "my-verify-token", "enable_auto_assignment": true, "name": "Support WhatsApp" }`) req, _ := http.NewRequest("PATCH", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete WhatsApp inbox > DELETE https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/delete-whatsapp-inbox Delete one of the project's WhatsApp inboxes, disconnecting that WhatsApp number from the project. Choose the number with `inbox_id`. The response is a boolean indicating whether the deletion succeeded. ## Irreversible Deleting an inbox removes the WhatsApp channel connection. - **Agents**: An agent that answered on the deleted number stays configured and keeps answering on its other numbers. Use GET /api/ext/omni_v2/whatsapp/inboxes to confirm which inbox(es) exist before deleting. ## Choosing the WhatsApp number A project can connect more than one WhatsApp number. `inbox_id` selects the number this call works on. - **One number**: `inbox_id` is optional; the project's only WhatsApp number is used. - **More than one number**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's WhatsApp numbers returns 404. Fields: `inbox_id`. List the numbers and their ids with GET /api/ext/omni_v2/whatsapp/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the WhatsApp number to use, from GET /api/ext/omni_v2/whatsapp/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one WhatsApp number; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/inboxes", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Create WhatsApp conversation > POST https://api.nabrah.ai/api/ext/omni_v2/whatsapp Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/create-whatsapp-conversation Create a new WhatsApp conversation (ticket) for a contact and return the full conversation object. Identify the contact either by its internal `contact_id` or by `contact_phone` (E.164 phone number). The response is the complete conversation, including its `id`, `inbox_id`, sender metadata, and the initial message list. ## Identifying the contact Provide either `contact_id` or `contact_phone` — you do not need both. - **contact_id**: The internal numeric id of an existing contact. Look it up via GET /api/ext/omni_v2/contact. Fields: `contact_id`. - **contact_phone**: The contact's phone number in E.164 format (for example `+966512345678`). Useful when you only know the phone number. If no contact has this number yet, one is created, so you can open a conversation with a number that has never written to you. Fields: `contact_phone`. The conversation is opened on the WhatsApp number selected by `inbox_id`. ## Choosing the WhatsApp number A project can connect more than one WhatsApp number. `inbox_id` selects the number this call works on. - **One number**: `inbox_id` is optional; the project's only WhatsApp number is used. - **More than one number**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's WhatsApp numbers returns 404. Fields: `inbox_id`. List the numbers and their ids with GET /api/ext/omni_v2/whatsapp/inboxes. ## Next step - **Send the first message**: Use the returned `id` with POST /api/ext/omni_v2/conversation/conversation/{id}/messages, for example to send an approved template. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the WhatsApp number to use, from GET /api/ext/omni_v2/whatsapp/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one WhatsApp number; required once it has more than one. ## Body (application/json) ### Example: By contact id Open a conversation for a known internal contact id. ```json { "contact_id": 12345, "contact_phone": "" } ``` ### Example: By phone number Open a conversation by the contact's WhatsApp phone number in E.164 format. ```json { "contact_id": 0, "contact_phone": "+966512345678" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": { "source_id": null }, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string" }, "messages": [ { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": { "assignee_id": 0, "contact_inbox": null, "last_activity_at": 0, "unread_count": 0 }, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": { "availability_status": "string", "available_name": "string", "avatar_url": "string", "id": 0, "name": "string", "thumbnail": "string", "type": "string", "additional_attributes": {}, "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "identifier": "string", "last_activity_at": 0, "phone_number": "string" }, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string" } ], "meta": { "channel": "string", "hmac_verified": false, "sender": { "additional_attributes": {}, "availability_status": "string", "blocked": false, "created_at": 0, "custom_attributes": {}, "email": "string", "id": 0, "identifier": "string", "last_activity_at": 0, "name": "string", "phone_number": "string", "thumbnail": "string" } }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "contact_id": 12345, "contact_phone": "" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp" headers = { "X-API-Key": "", } payload = { "contact_id": 12345, "contact_phone": "" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "contact_id": 12345, "contact_phone": "" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "contact_id": 12345, "contact_phone": "" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List WhatsApp conversations > GET https://api.nabrah.ai/api/ext/omni_v2/whatsapp Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/list-whatsapp-conversations List WhatsApp conversations (tickets) on one of the project's WhatsApp numbers, with optional filtering by assignment and status, plus search by the customer's phone number or message text. Results are paginated and the response includes a `meta` block with counts for each assignment bucket (all, mine, unassigned, assigned) so you can render conversation tabs. This is the generic conversation listing endpoint — use the POST `/filter` variant for structured filters or the by-contact endpoint to scope to a single contact. ## Pagination & counts The endpoint returns one page of conversations at a time inside `payload`, plus aggregate `meta` counts. - **page**: Pass `?page=1` for the first page and increment to walk subsequent pages. When a page returns fewer items than the page size (or an empty `payload`), you have reached the end. - **meta counts**: `meta.all_count`, `meta.mine_count`, `meta.unassigned_count`, and `meta.assigned_count` reflect the totals for each assignment bucket and are independent of the current page. Fields: `all_count`, `mine_count`, `unassigned_count`, `assigned_count`. ## Reading conversations Each item in `payload` is a conversation. Useful fields: `id` (the conversation/ticket id), `inbox_id` (which WhatsApp number it belongs to), `status`, `unread_count`, and `last_non_activity_message` (the most recent real message). Authenticate every request with the `X-API-Key` header. The key is scoped to a single project, so no project_id is required. ## Choosing the WhatsApp number A project can connect more than one WhatsApp number. `inbox_id` selects the number this call works on. - **One number**: `inbox_id` is optional; the project's only WhatsApp number is used. - **More than one number**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's WhatsApp numbers returns 404. Fields: `inbox_id`. List the numbers and their ids with GET /api/ext/omni_v2/whatsapp/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `assignee_type` (enum(all | me | unassigned | assigned), optional): Filter conversations by assignment relative to the API key's identity. One of `all`, `me`, `unassigned`, or `assigned`. Defaults to `all` when omitted. - `status_param` (enum(open | pending | resolved | snoozed | all), optional): Filter conversations by their workflow status. One of `open`, `pending`, `resolved`, `snoozed`, or `all`. Defaults to `open` when omitted. - `q` (string, optional): Search by the customer's phone number or by message text. A phone number returns every conversation of the contacts whose number matches. It can be international (`+966500000001`), local (`0500000001`), partial (at least 4 digits), and may contain spaces, dashes, dots or brackets. Arabic-Indic digits are accepted. Any other text is matched against message content. Optional; leave empty to disable search. A number with no conversation yet, for example one that has never written to you, returns nothing; open one with POST /api/ext/omni_v2/whatsapp. - `page` (integer, optional): 1-based page number for paginating results. Optional; defaults to the first page when omitted. - `inbox_id` (string, optional): The `id` of the WhatsApp number to use, from GET /api/ext/omni_v2/whatsapp/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one WhatsApp number; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Filter WhatsApp conversations > POST https://api.nabrah.ai/api/ext/omni_v2/whatsapp/filter Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/filter-whatsapp-conversations List WhatsApp conversations on one of the project's WhatsApp numbers using a structured filter payload instead of simple query parameters. The request body carries a `filters` array of filter objects, and results are paginated with the `page` query parameter. The response shape matches the generic list endpoint, including `meta` counts and a `payload` array of conversations. ## Filters & pagination Combine the structured `filters` body with the `page` query parameter to page through matching conversations. - **filters**: An array of filter objects describing the conditions to match. Pass an empty array for no constraints, or add filter conditions; each condition needs `attribute_key`, `filter_operator` and `values` to narrow the result set. Fields: `filters`. The response `meta` counts (all/mine/unassigned/assigned) are independent of the current page. ## Choosing the WhatsApp number A project can connect more than one WhatsApp number. `inbox_id` selects the number this call works on. - **One number**: `inbox_id` is optional; the project's only WhatsApp number is used. - **More than one number**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's WhatsApp numbers returns 404. Fields: `inbox_id`. List the numbers and their ids with GET /api/ext/omni_v2/whatsapp/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `page` (integer, optional): 1-based page number for paginating filtered results. Optional; defaults to the first page when omitted. - `inbox_id` (string, optional): The `id` of the WhatsApp number to use, from GET /api/ext/omni_v2/whatsapp/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one WhatsApp number; required once it has more than one. ## Body (application/json) ### Example: Filter conversations ```json { "filters": [] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/filter' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/filter" headers = { "X-API-Key": "", } payload = { "filters": [] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/filter", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/filter", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List WhatsApp conversations by contact > POST https://api.nabrah.ai/api/ext/omni_v2/whatsapp/contact/{contact_id} Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/list-whatsapp-conversations-by-contact Lists the WhatsApp conversations of the contact identified by `contact_id` on one of the project's WhatsApp numbers, with optional server-side filtering in the request body and pagination via the `page` query parameter. The response returns a `meta` block with conversation counts (`all_count`, `mine_count`, `unassigned_count`, `assigned_count`) and a `payload` array of conversation objects. Send an empty `filters` array to return all of the contact's WhatsApp conversations. ## Authentication - **X-API-Key header**: Send your Web API key (prefix `nb_`) in the `X-API-Key` header. The key is scoped to one project; the contact must belong to that project. ## Filtering and pagination - **filters**: The request body takes a `filters` array. Send an empty array `[]` to return all of the contact's WhatsApp conversations, or add filter objects to narrow the results. - **page**: Use the `page` query parameter to page through results. The response `meta` reports `all_count`, `mine_count`, `unassigned_count`, and `assigned_count`. Fields: `meta.all_count`, `meta.mine_count`, `meta.unassigned_count`, `meta.assigned_count`. ## Choosing the WhatsApp number A project can connect more than one WhatsApp number. `inbox_id` selects the number this call works on. - **One number**: `inbox_id` is optional; the project's only WhatsApp number is used. - **More than one number**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's WhatsApp numbers returns 404. Fields: `inbox_id`. List the numbers and their ids with GET /api/ext/omni_v2/whatsapp/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `contact_id` (integer, required): Numeric identifier of the contact whose WhatsApp conversations are listed. Obtained from `GET /api/ext/omni_v2/contact` or `GET /api/ext/omni_v2/contact/search` (the contact `id` field). ## Query parameters - `page` (integer, optional): 1-based page number of conversations to retrieve. Omit to fetch the first page. Increment until the returned payload array is empty to iterate over all conversations. - `inbox_id` (string, optional): The `id` of the WhatsApp number to use, from GET /api/ext/omni_v2/whatsapp/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one WhatsApp number; required once it has more than one. ## Body (application/json) ### Example: All WhatsApp conversations Return every WhatsApp conversation for the contact (empty filters). ```json { "filters": [] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/contact/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/contact/" headers = { "X-API-Key": "", } payload = { "filters": [] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/contact/", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/contact/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Assign an agent to a WhatsApp number > POST https://api.nabrah.ai/api/ext/omni_v2/whatsapp/assign_chatbot Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/assign-an-agent-to-a-whatsapp-number Attach an AI agent to one of the project's WhatsApp numbers so it automatically replies to incoming WhatsApp messages on that number. You bind an existing agent (by `agent_id`) and configure, for this number, whether the bot may escalate to a human, who it hands over to, when idle conversations close, and where conversation events are sent. The response returns the bound agent's full configuration plus this number's `text_chatbot_settings`. ## How auto-reply works Once assigned, incoming WhatsApp messages on that number are handled by the chosen agent, which generates and sends replies automatically. - **agent_id**: The UUID of an existing text/basic agent in this project that should answer messages. Fields: `agent_id`. - **name**: The bot's display name. It is shared by every WhatsApp number this agent answers on. Fields: `name`. - **Human handoff**: Set `can_refer_to_human` to true to let the bot escalate a conversation, then describe who it reaches in `human_emails` (see below). Fields: `can_refer_to_human`, `human_emails`. Use GET /api/ext/omni_v2/whatsapp/get_chatbot to inspect the current assignment and DELETE /api/ext/omni_v2/whatsapp/unassign_chatbot to remove it. ## One agent, several numbers Assigning adds this number to the agent. The agent keeps answering on its other numbers, and their settings stay as they are. - **Per-number settings**: These apply to the selected number only. Fields: `can_refer_to_human`, `human_emails`, `auto_resolve_hours`, `webhook_url`, `webhook_hmac_secret`. - **Settings left out**: Leaving out `can_refer_to_human` turns handoff off, and leaving out `human_emails` sends an empty list, which hands over to a random project member who can see this number's conversations. Leaving out `auto_resolve_hours`, `webhook_url` or `webhook_hmac_secret` keeps this number's current value, also when the request replaces the number's agent. On a number with no agent, every setting left out is copied from the agent's first number when the agent already answers on another number, and starts empty otherwise. Fields: `can_refer_to_human`, `human_emails`, `auto_resolve_hours`, `webhook_url`, `webhook_hmac_secret`. - **Replacing an agent**: A number has one agent at a time. Assigning a different agent to a number replaces the previous one there; ongoing conversations on that number continue with the new agent, which does not see the previous agent's replies. The number keeps its own settings, except those sent in the request and the handoff rule above. - **inbox_ids**: `text_chatbot_settings.inbox_ids` in the response lists every WhatsApp number the agent now answers on. Fields: `inbox_ids`. ## Choosing who the bot hands over to `human_emails` accepts three shapes. Working hours are optional on any destination; when they are set, only destinations inside their window are eligible and one of them is picked at random. - **Anyone available** (human_emails: []): An empty list refers the conversation to a random project member who can see this number's conversations. Working hours are ignored on this path. Fields: `human_emails`. - **Specific people** (human_emails: ["a@example.com", { "email": "b@example.com", "hours": { ... } }]): A list of addresses. Each item is either a bare string (always available) or an object with an optional `hours` window. Fields: `human_emails`. - **By department** (human_emails: { "IT": { "desc": "...", "emails": [ ... ] } }): A category map. `desc` is required and is the only text the assistant sees when deciding which department fits, so make it specific. The assistant must pass a category on every call; if none of the descriptions match, nobody is contacted. Fields: `human_emails`. - **hours** ({ "days": [1,2,3,4,5], "start": "09:00", "end": "17:00", "timezone": "Asia/Riyadh" }): `days` are ISO weekdays, 1 = Monday through 7 = Sunday; an empty list means every day. `start` and `end` are zero-padded 24-hour times, and a window whose start is later than its end wraps past midnight. `timezone` is an IANA zone and defaults to Asia/Riyadh. Fields: `human_emails`. Addresses that do not belong to a project member who can see this number's conversations still open the conversation, but it is left unassigned. ## Choosing the WhatsApp number A project can connect more than one WhatsApp number. `inbox_id` selects the number this call works on. - **One number**: `inbox_id` is optional; the project's only WhatsApp number is used. - **More than one number**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's WhatsApp numbers returns 404. Fields: `inbox_id`. List the numbers and their ids with GET /api/ext/omni_v2/whatsapp/inboxes. ## Errors - **409 Conflict**: Another change to the same number's agent, or to the same agent on another number, is still being saved. Wait a moment, then try again; GET /api/ext/omni_v2/whatsapp/get_chatbot shows the result once it is saved. ## Event webhooks Set `webhook_url` to receive a POST for each chatbot event on this number. Leave the field out to keep the current value, or send null to turn webhooks off. - **Events**: `message.received`: every incoming customer message on the number, including chats a person has taken over. `message.replied`: a reply sent by the bot. `message.sent`: a reply sent by a person or through the API. `conversation.status_changed`: a handoff to a person, the bot ending the chat, or a manual status change. `conversation.auto_resolved`: the chat was closed after the auto-resolve window. Fields: `webhook_url`. - **Payload** ({ "event", "occurred_at", "project_id", "agent_id", "conversation_id", "data" }): For message events, `data` has `conversation_id`, `content`, `content_type`, `created_at`, `message_type` (0 = incoming, 1 = outgoing) and `attachments` (each with `file_type` and `data_url`). For status events, `data` has `conversation_id`, `status`, `previous_status` (can be null) and `reason` (`refer_to_human`, `end_chat`, `auto_resolve` or `manual`). - **Signature** (X-Nabrah-Signature: sha256=): When `webhook_hmac_secret` is set, each request has `X-Nabrah-Timestamp` (Unix seconds) and `X-Nabrah-Signature`: `sha256=` followed by the hex HMAC-SHA256 of `.`, keyed with your secret. Verify it against the raw body, compare in constant time, and reject old timestamps. Every request also has `X-Nabrah-Event` with the event name. Fields: `webhook_hmac_secret`. - **Delivery**: Each event is sent once with a 5-second timeout. Failed or non-2xx deliveries are not retried, and events have no event ID. One status change can arrive as two events, for example `conversation.auto_resolved` and then `conversation.status_changed`. Keep your handler idempotent, and use GET /api/ext/omni_v2/conversation/conversation/{id}/messages to fill any gaps. Events are only sent while a chatbot is assigned to the number. GET /api/ext/omni_v2/whatsapp/get_chatbot returns `webhook_url` and `has_webhook_hmac_secret`; the secret itself is never returned. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the WhatsApp number to use, from GET /api/ext/omni_v2/whatsapp/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one WhatsApp number; required once it has more than one. ## Body (application/json) ### Example: Specific people A flat list of addresses, always available. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "WhatsApp Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] } ``` ### Example: With working hours Only reachable Monday to Friday, 09:00-17:00 Riyadh time. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "WhatsApp Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com", { "email": "oncall@example.com", "hours": { "days": [ 1, 2, 3, 4, 5 ], "start": "09:00", "end": "17:00", "timezone": "Asia/Riyadh" } } ] } ``` ### Example: By department The assistant picks a department from the descriptions. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "WhatsApp Support Bot", "can_refer_to_human": true, "human_emails": { "IT": { "desc": "Bugs, outages, login and password problems", "emails": [ "it@example.com" ] }, "Sales": { "desc": "Pricing, quotes and product demos", "emails": [ "sales@example.com", { "email": "nightshift@example.com", "hours": { "days": [], "start": "22:00", "end": "06:00", "timezone": "UTC" } } ] } } } ``` ### Example: Anyone available An empty list refers to a random project member who can see this number's conversations. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "WhatsApp Support Bot", "can_refer_to_human": true, "human_emails": [] } ``` ### Example: With event webhook Send chatbot events to your endpoint, signed with a shared secret. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "WhatsApp Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ], "webhook_url": "https://example.com/nabrah/events", "webhook_hmac_secret": "replace-with-a-long-random-secret" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "agent": { "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "name": "string", "start_first": false, "first_sentence": "string", "agent_type": "basic", "who_are_you": "string", "goal": "string", "steps": "string", "allow_interruptions": false, "voice": "dania", "support_data": "string", "languages": [ "ar" ], "stack": "latest", "speech_speed": 0.7, "variables": [ { "name": "string", "type": "string", "default_value": "string" } ], "pre_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_analysis_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "system_tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "when_to_use": "string" } ], "analysis_groups": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "background_ambient": false, "background_ambient_volume": 0, "thinking_sound": false, "thinking_sound_volume": 0, "language_mode": "single" }, "text_chatbot_settings": { "can_refer_to_human": false, "human_emails": [ "string" ], "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "auto_resolve_hours": 0, "webhook_url": "string", "has_webhook_hmac_secret": false, "inbox_ids": [ "00000000-0000-4000-8000-000000000000" ] } } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/assign_chatbot' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "agent_id": "00000000-0000-4000-8000-000000000000", "name": "WhatsApp Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/assign_chatbot" headers = { "X-API-Key": "", } payload = { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "WhatsApp Support Bot", "can_refer_to_human": True, "human_emails": [ "support@example.com" ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/assign_chatbot", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "agent_id": "00000000-0000-4000-8000-000000000000", "name": "WhatsApp Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "agent_id": "00000000-0000-4000-8000-000000000000", "name": "WhatsApp Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/assign_chatbot", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get the agent on a WhatsApp number > GET https://api.nabrah.ai/api/ext/omni_v2/whatsapp/get_chatbot Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/get-the-agent-on-a-whatsapp-number Retrieve the agent currently answering on one of the project's WhatsApp numbers. The response returns the agent's full configuration under `agent` and that number's chatbot settings under `text_chatbot_settings`. Use this to confirm which agent is auto-replying on a number before updating or removing the assignment. ## Reading the response - **agent**: The complete configuration of the agent handling WhatsApp messages, including `agent_id`, `name`, languages, voice, tools, and callbacks. - **text_chatbot_settings**: The settings of this number. `name` is shared by every number the agent answers on; `inbox_ids` lists those numbers. `webhook_url` is the event webhook set on assign; the signing secret is never returned, only `has_webhook_hmac_secret`. Fields: `can_refer_to_human`, `human_emails`, `auto_resolve_hours`, `webhook_url`, `has_webhook_hmac_secret`, `name`, `inbox_ids`, `created_at`. Assign with POST /api/ext/omni_v2/whatsapp/assign_chatbot and remove with DELETE /api/ext/omni_v2/whatsapp/unassign_chatbot. ## Reading human_emails `text_chatbot_settings.human_emails` comes back in whichever shape it was stored: a list of addresses, a list of objects with `hours`, or a category map. - **Null fields in the response**: Responses are not stripped of nulls, so an `hours` object can come back carrying `start: null` or `end: null`, and a destination that has no window at all can appear as `hours: null`. Treat all three as `always available`. Fields: `human_emails`. ## Choosing the WhatsApp number A project can connect more than one WhatsApp number. `inbox_id` selects the number this call works on. - **One number**: `inbox_id` is optional; the project's only WhatsApp number is used. - **More than one number**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's WhatsApp numbers returns 404. Fields: `inbox_id`. List the numbers and their ids with GET /api/ext/omni_v2/whatsapp/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the WhatsApp number to use, from GET /api/ext/omni_v2/whatsapp/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one WhatsApp number; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json { "agent": { "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "name": "string", "start_first": false, "first_sentence": "string", "agent_type": "basic", "who_are_you": "string", "goal": "string", "steps": "string", "allow_interruptions": false, "voice": "dania", "support_data": "string", "languages": [ "ar" ], "stack": "latest", "speech_speed": 0.7, "variables": [ { "name": "string", "type": "string", "default_value": "string" } ], "pre_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_analysis_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "system_tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "when_to_use": "string" } ], "analysis_groups": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "background_ambient": false, "background_ambient_volume": 0, "thinking_sound": false, "thinking_sound_volume": 0, "language_mode": "single" }, "text_chatbot_settings": { "can_refer_to_human": false, "human_emails": [ "string" ], "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "auto_resolve_hours": 0, "webhook_url": "string", "has_webhook_hmac_secret": false, "inbox_ids": [ "00000000-0000-4000-8000-000000000000" ] } } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/get_chatbot' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/get_chatbot" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/get_chatbot", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/get_chatbot", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Remove the agent from a WhatsApp number > DELETE https://api.nabrah.ai/api/ext/omni_v2/whatsapp/unassign_chatbot Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/remove-the-agent-from-a-whatsapp-number Remove the agent from one of the project's WhatsApp numbers, stopping automatic AI replies on that number. The agent keeps answering on its other numbers with their settings unchanged. The response is an object confirming the operation. ## Effect After unassigning, incoming WhatsApp messages on that number are no longer answered automatically by an AI agent. Check the current assignment first with GET /api/ext/omni_v2/whatsapp/get_chatbot, and re-attach later with POST /api/ext/omni_v2/whatsapp/assign_chatbot. ## Choosing the WhatsApp number A project can connect more than one WhatsApp number. `inbox_id` selects the number this call works on. - **One number**: `inbox_id` is optional; the project's only WhatsApp number is used. - **More than one number**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's WhatsApp numbers returns 404. Fields: `inbox_id`. List the numbers and their ids with GET /api/ext/omni_v2/whatsapp/inboxes. ## Errors - **409 Conflict**: Another change to the same number's agent is still being saved. Wait a moment, then try again; GET /api/ext/omni_v2/whatsapp/get_chatbot shows the result once it is saved. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the WhatsApp number to use, from GET /api/ext/omni_v2/whatsapp/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one WhatsApp number; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/unassign_chatbot' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/unassign_chatbot" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/unassign_chatbot", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/unassign_chatbot", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get WhatsApp analytics > POST https://api.nabrah.ai/api/ext/omni_v2/whatsapp/analytics Group: WhatsApp. Page: https://www.nabrah.ai/en/docs/api-reference/whatsapp/get-whatsapp-analytics Get aggregated WhatsApp conversation analytics for a date window. The request body specifies the time range (`since`/`until`) and optionally an `inbox_id`; if `inbox_id` is omitted, the analytics combine every WhatsApp number of the project. The response includes a `summary` of totals (conversations by status, incoming/outgoing message counts, average first-response and resolution times), time-series `trend` arrays, per-assignee metrics, and an `inboxes` breakdown with one row per WhatsApp number. ## Request body All timestamps are ISO 8601 date-times. - **since / until**: The inclusive start and end of the analytics window. Fields: `since`, `until`. - **inbox_id**: Optional WhatsApp inbox UUID to scope the analytics to one number. If omitted, every WhatsApp number of the project is combined: counts are summed and average times are weighted by volume. Get the value from GET /api/ext/omni_v2/whatsapp/inboxes. An `inbox_id` that is not one of the project's WhatsApp numbers returns 404, as does a project with no WhatsApp number connected. Fields: `inbox_id`. ## Response shape - **summary**: Totals for the window: conversation counts by status, incoming/outgoing message counts, and average first-response and resolution times (seconds). Fields: `total_conversations`, `open_conversations`, `pending_conversations`, `resolved_conversations`, `snoozed_conversations`, `incoming_messages`, `outgoing_messages`, `avg_first_response_time`, `avg_resolution_time`. - **trends**: Time-series arrays of `{ timestamp, value }` points for conversation volume and inbound/outbound message volume. Fields: `conversations_trend`, `incoming_trend`, `outgoing_trend`. - **assignee_metrics**: Per-agent breakdown of conversation handling and timing. It always covers the whole project, even when `inbox_id` is sent. Fields: `agent_id`, `agent_name`, `agent_email`, `conversations_count`, `resolved_conversations_count`, `avg_first_response_time`, `avg_resolution_time`, `avg_reply_time`. - **inboxes**: One row per WhatsApp number in the result, with its totals for the window. Fields: `inbox_id`, `name`, `phone_number`, `conversations`, `resolutions`, `incoming_messages`, `outgoing_messages`, `avg_first_response_time`, `avg_resolution_time`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Body (application/json) ### Example: Analytics by date range Scope analytics to a specific WhatsApp inbox and date window. ```json { "since": "2026-06-01T00:00:00.000Z", "until": "2026-06-23T23:59:59.000Z", "inbox_id": "00000000-0000-4000-8000-000000000000" } ``` ### Example: All numbers (no inbox_id) Omit inbox_id to combine every WhatsApp number of the project. ```json { "since": "2026-06-01T00:00:00.000Z", "until": "2026-06-23T23:59:59.000Z" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "summary": { "total_conversations": 0, "open_conversations": 0, "pending_conversations": 0, "resolved_conversations": 0, "snoozed_conversations": 0, "incoming_messages": 0, "outgoing_messages": 0, "avg_first_response_time": 0, "avg_resolution_time": 0 }, "conversations_trend": [ { "timestamp": 0, "value": 0 } ], "incoming_trend": [ { "timestamp": 0, "value": 0 } ], "outgoing_trend": [ { "timestamp": 0, "value": 0 } ], "assignee_metrics": [ { "agent_id": 0, "agent_name": "string", "agent_email": "string", "conversations_count": 0, "resolved_conversations_count": 0, "avg_first_response_time": 0, "avg_resolution_time": 0, "avg_reply_time": 0 } ], "inboxes": [ { "inbox_id": "00000000-0000-4000-8000-000000000000", "name": "string", "phone_number": "string", "conversations": 0, "resolutions": 0, "incoming_messages": 0, "outgoing_messages": 0, "avg_first_response_time": 0, "avg_resolution_time": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/whatsapp/analytics' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "since": "2026-06-01T00:00:00.000Z", "until": "2026-06-23T23:59:59.000Z", "inbox_id": "00000000-0000-4000-8000-000000000000" }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/analytics" headers = { "X-API-Key": "", } payload = { "since": "2026-06-01T00:00:00.000Z", "until": "2026-06-23T23:59:59.000Z", "inbox_id": "00000000-0000-4000-8000-000000000000" } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/whatsapp/analytics", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "since": "2026-06-01T00:00:00.000Z", "until": "2026-06-23T23:59:59.000Z", "inbox_id": "00000000-0000-4000-8000-000000000000" }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "since": "2026-06-01T00:00:00.000Z", "until": "2026-06-23T23:59:59.000Z", "inbox_id": "00000000-0000-4000-8000-000000000000" }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/whatsapp/analytics", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Instagram API Work the conversations of connected Instagram accounts and put an agent on each account. - [List Instagram accounts](https://www.nabrah.ai/en/docs/api-reference/instagram/list-instagram-accounts.md) `GET /api/ext/omni_v2/instagram/inboxes`: List the Instagram accounts connected to the project, oldest first. - [Get Instagram account](https://www.nabrah.ai/en/docs/api-reference/instagram/get-instagram-account.md) `GET /api/ext/omni_v2/instagram/inboxes/{inbox_id}`: Retrieve one of the project's Instagram accounts by its id: id, name, username, avatar_url, disconnected, created_at, and the agent answering on it (agent_id and agent_name, null when none). - [Delete Instagram account](https://www.nabrah.ai/en/docs/api-reference/instagram/delete-instagram-account.md) `DELETE /api/ext/omni_v2/instagram/inboxes`: Remove one of the project's Instagram accounts. - [List Instagram conversations](https://www.nabrah.ai/en/docs/api-reference/instagram/list-instagram-conversations.md) `GET /api/ext/omni_v2/instagram`: List Instagram conversations (tickets) on one of the project's Instagram accounts, with optional filtering by assignment and status. - [Filter Instagram conversations](https://www.nabrah.ai/en/docs/api-reference/instagram/filter-instagram-conversations.md) `POST /api/ext/omni_v2/instagram/filter`: List Instagram conversations on one of the project's Instagram accounts using a structured filter payload instead of simple query parameters. - [List Instagram conversations by contact](https://www.nabrah.ai/en/docs/api-reference/instagram/list-instagram-conversations-by-contact.md) `POST /api/ext/omni_v2/instagram/contact/{contact_id}`: Lists the Instagram conversations of the contact identified by contact_id on one of the project's Instagram accounts, with optional server-side filtering in the request body and pagination via the page query parameter. - [Assign an agent to an Instagram account](https://www.nabrah.ai/en/docs/api-reference/instagram/assign-an-agent-to-an-instagram-account.md) `POST /api/ext/omni_v2/instagram/assign_chatbot`: Attach an AI agent to one of the project's Instagram accounts so it automatically replies to incoming Instagram direct messages on that account. - [Get the agent on an Instagram account](https://www.nabrah.ai/en/docs/api-reference/instagram/get-the-agent-on-an-instagram-account.md) `GET /api/ext/omni_v2/instagram/get_chatbot`: Retrieve the agent currently answering on one of the project's Instagram accounts. - [Remove the agent from an Instagram account](https://www.nabrah.ai/en/docs/api-reference/instagram/remove-the-agent-from-an-instagram-account.md) `DELETE /api/ext/omni_v2/instagram/unassign_chatbot`: Remove the agent from one of the project's Instagram accounts, stopping automatic AI replies on that account. --- # List Instagram accounts > GET https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes Group: Instagram. Page: https://www.nabrah.ai/en/docs/api-reference/instagram/list-instagram-accounts List the Instagram accounts connected to the project, oldest first. Each entry includes its `id`, display `name`, `username`, `avatar_url`, whether it needs reconnecting (`disconnected`), `created_at`, and the agent answering on it. Use the returned `id` as the `inbox_id` for the other Instagram endpoints. ## Where inbox IDs come from This is the canonical source for `inbox_id` values used by the other Instagram endpoints. - **inboxes[].id**: The UUID of each Instagram account. Pass it as `inbox_id` to the other Instagram endpoints, or as the `{inbox_id}` path parameter to get one account. Fields: `id`, `name`, `username`, `avatar_url`, `created_at`. - **Answering agent**: The agent auto-replying on the account, or null when none is assigned. Fields: `agent_id`, `agent_name`. - **disconnected**: True when the account's access was revoked or expired. Replies are not delivered until someone reconnects it from the platform; its conversations and settings are kept. Fields: `disconnected`. - **max_inboxes**: How many Instagram accounts the project may connect. Contact support to raise it. Fields: `max_inboxes`. ## Connecting accounts Accounts are connected and reconnected in the platform, because the account owner has to sign in with Instagram in their browser. There is no API for connecting an account. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "inboxes": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "username": "string", "avatar_url": "string", "disconnected": false, "agent_id": "00000000-0000-4000-8000-000000000000", "agent_name": "string" } ], "max_inboxes": 0 } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get Instagram account > GET https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes/{inbox_id} Group: Instagram. Page: https://www.nabrah.ai/en/docs/api-reference/instagram/get-instagram-account Retrieve one of the project's Instagram accounts by its id: `id`, `name`, `username`, `avatar_url`, `disconnected`, `created_at`, and the agent answering on it (`agent_id` and `agent_name`, null when none). ## Where the inbox_id comes from Obtain the `inbox_id` from the account list endpoint. Call GET /api/ext/omni_v2/instagram/inboxes and use any returned `inboxes[].id` value. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `inbox_id` (string, required): The UUID of the Instagram account to fetch. Get this value from GET /api/ext/omni_v2/instagram/inboxes (the `inboxes[].id` field). ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "username": "string", "avatar_url": "string", "disconnected": false, "agent_id": "00000000-0000-4000-8000-000000000000", "agent_name": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete Instagram account > DELETE https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes Group: Instagram. Page: https://www.nabrah.ai/en/docs/api-reference/instagram/delete-instagram-account Remove one of the project's Instagram accounts. Its conversations and its customers' contacts are deleted, the agent stops replying on it, and member access to it is removed. Choose the account with `inbox_id`. The response is a boolean indicating whether the removal succeeded. ## Irreversible Removing an account deletes its conversations and its customers' contacts. Connect it again from the platform to start over. - **Agents**: An agent that answered on the deleted account stays configured and keeps answering on its other accounts. Use GET /api/ext/omni_v2/instagram/inboxes to confirm which inbox(es) exist before deleting. ## Choosing the Instagram account A project can connect more than one Instagram account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only Instagram account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's Instagram accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/instagram/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the Instagram account to use, from GET /api/ext/omni_v2/instagram/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one Instagram account; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/omni_v2/instagram/inboxes", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List Instagram conversations > GET https://api.nabrah.ai/api/ext/omni_v2/instagram Group: Instagram. Page: https://www.nabrah.ai/en/docs/api-reference/instagram/list-instagram-conversations List Instagram conversations (tickets) on one of the project's Instagram accounts, with optional filtering by assignment and status. Results are paginated and the response includes a `meta` block with counts for each assignment bucket (all, mine, unassigned, assigned) so you can render conversation tabs. This is the generic conversation listing endpoint — use the POST `/filter` variant for structured filters or the by-contact endpoint to scope to a single contact. ## Pagination & counts The endpoint returns one page of conversations at a time inside `payload`, plus aggregate `meta` counts. - **page**: Pass `?page=1` for the first page and increment to walk subsequent pages. When a page returns fewer items than the page size (or an empty `payload`), you have reached the end. - **meta counts**: `meta.all_count`, `meta.mine_count`, `meta.unassigned_count`, and `meta.assigned_count` reflect the totals for each assignment bucket and are independent of the current page. Fields: `all_count`, `mine_count`, `unassigned_count`, `assigned_count`. ## Reading conversations Each item in `payload` is a conversation. Useful fields: `id` (the conversation/ticket id), `inbox_id` (which Instagram account it belongs to), `status`, `unread_count`, and `last_non_activity_message` (the most recent real message). Authenticate every request with the `X-API-Key` header. The key is scoped to a single project, so no project_id is required. ## Choosing the Instagram account A project can connect more than one Instagram account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only Instagram account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's Instagram accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/instagram/inboxes. ## Reply window Instagram allows replies within 24 hours of the customer's last message; after that, API sends return 409 until the customer writes again. Team members replying from the platform can still reply for up to 7 days. - **reply_window_ends_at**: Each Instagram conversation carries `reply_window_ends_at`: a Unix timestamp for when the reply window closes, counted from the customer's last message. Sending after it returns 409 `reply_window_closed`. Fields: `reply_window_ends_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `assignee_type` (enum(all | me | unassigned | assigned), optional): Filter conversations by assignment relative to the API key's identity. One of `all`, `me`, `unassigned`, or `assigned`. Defaults to `all` when omitted. - `status_param` (enum(open | pending | resolved | snoozed | all), optional): Filter conversations by their workflow status. One of `open`, `pending`, `resolved`, `snoozed`, or `all`. Defaults to `open` when omitted. - `page` (integer, optional): 1-based page account for paginating results. Optional; defaults to the first page when omitted. - `inbox_id` (string, optional): The `id` of the Instagram account to use, from GET /api/ext/omni_v2/instagram/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one Instagram account; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/instagram' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/instagram" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/instagram", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/instagram", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Filter Instagram conversations > POST https://api.nabrah.ai/api/ext/omni_v2/instagram/filter Group: Instagram. Page: https://www.nabrah.ai/en/docs/api-reference/instagram/filter-instagram-conversations List Instagram conversations on one of the project's Instagram accounts using a structured filter payload instead of simple query parameters. The request body carries a `filters` array of filter objects, and results are paginated with the `page` query parameter. The response shape matches the generic list endpoint, including `meta` counts and a `payload` array of conversations. ## Filters & pagination Combine the structured `filters` body with the `page` query parameter to page through matching conversations. - **filters**: An array of filter objects describing the conditions to match. Pass an empty array for no constraints, or add filter conditions; each condition needs `attribute_key`, `filter_operator` and `values` to narrow the result set. Fields: `filters`. The response `meta` counts (all/mine/unassigned/assigned) are independent of the current page. ## Choosing the Instagram account A project can connect more than one Instagram account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only Instagram account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's Instagram accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/instagram/inboxes. ## Reply window Instagram allows replies within 24 hours of the customer's last message; after that, API sends return 409 until the customer writes again. Team members replying from the platform can still reply for up to 7 days. - **reply_window_ends_at**: Each Instagram conversation carries `reply_window_ends_at`: a Unix timestamp for when the reply window closes, counted from the customer's last message. Sending after it returns 409 `reply_window_closed`. Fields: `reply_window_ends_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `page` (integer, optional): 1-based page account for paginating filtered results. Optional; defaults to the first page when omitted. - `page_size` (integer, optional) - `status` (enum(open | pending | resolved | snoozed | all), optional) - `assignee_type` (enum(all | me | unassigned | assigned), optional) - `inbox_id` (string, optional): The `id` of the Instagram account to use, from GET /api/ext/omni_v2/instagram/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one Instagram account; required once it has more than one. ## Body (application/json) ### Example: Filter conversations ```json { "filters": [] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/instagram/filter' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/instagram/filter" headers = { "X-API-Key": "", } payload = { "filters": [] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/instagram/filter", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/instagram/filter", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List Instagram conversations by contact > POST https://api.nabrah.ai/api/ext/omni_v2/instagram/contact/{contact_id} Group: Instagram. Page: https://www.nabrah.ai/en/docs/api-reference/instagram/list-instagram-conversations-by-contact Lists the Instagram conversations of the contact identified by `contact_id` on one of the project's Instagram accounts, with optional server-side filtering in the request body and pagination via the `page` query parameter. The response returns a `meta` block with conversation counts (`all_count`, `mine_count`, `unassigned_count`, `assigned_count`) and a `payload` array of conversation objects. Send an empty `filters` array to return all of the contact's Instagram conversations. ## Authentication - **X-API-Key header**: Send your Web API key (prefix `nb_`) in the `X-API-Key` header. The key is scoped to one project; the contact must belong to that project. ## Filtering and pagination - **filters**: The request body takes a `filters` array. Send an empty array `[]` to return all of the contact's Instagram conversations, or add filter objects to narrow the results. - **page**: Use the `page` query parameter to page through results. The response `meta` reports `all_count`, `mine_count`, `unassigned_count`, and `assigned_count`. Fields: `meta.all_count`, `meta.mine_count`, `meta.unassigned_count`, `meta.assigned_count`. ## Choosing the Instagram account A project can connect more than one Instagram account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only Instagram account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's Instagram accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/instagram/inboxes. ## Reply window Instagram allows replies within 24 hours of the customer's last message; after that, API sends return 409 until the customer writes again. Team members replying from the platform can still reply for up to 7 days. - **reply_window_ends_at**: Each Instagram conversation carries `reply_window_ends_at`: a Unix timestamp for when the reply window closes, counted from the customer's last message. Sending after it returns 409 `reply_window_closed`. Fields: `reply_window_ends_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `contact_id` (integer, required): Numeric identifier of the contact whose Instagram conversations are listed. Obtained from `GET /api/ext/omni_v2/contact` or `GET /api/ext/omni_v2/contact/search` (the contact `id` field). ## Query parameters - `page` (integer, optional): 1-based page account of conversations to retrieve. Omit to fetch the first page. Increment until the returned payload array is empty to iterate over all conversations. - `inbox_id` (string, optional): The `id` of the Instagram account to use, from GET /api/ext/omni_v2/instagram/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one Instagram account; required once it has more than one. ## Body (application/json) ### Example: All Instagram conversations Return every Instagram conversation for the contact (empty filters). ```json { "filters": [] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/instagram/contact/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/instagram/contact/" headers = { "X-API-Key": "", } payload = { "filters": [] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/instagram/contact/", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/instagram/contact/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Assign an agent to an Instagram account > POST https://api.nabrah.ai/api/ext/omni_v2/instagram/assign_chatbot Group: Instagram. Page: https://www.nabrah.ai/en/docs/api-reference/instagram/assign-an-agent-to-an-instagram-account Attach an AI agent to one of the project's Instagram accounts so it automatically replies to incoming Instagram direct messages on that account. You bind an existing agent (by `agent_id`) and configure, for this account, whether the bot may escalate to a human, who it hands over to, when idle conversations close, and where conversation events are sent. The response returns the bound agent's full configuration plus this account's `text_chatbot_settings`. ## How auto-reply works Once assigned, incoming Instagram direct messages on that account are handled by the chosen agent, which generates and sends replies automatically. - **agent_id**: The UUID of an existing text/basic agent in this project that should answer messages. Fields: `agent_id`. - **name**: The bot's display name. It is shared by every Instagram account this agent answers on. Fields: `name`. - **Human handoff**: Set `can_refer_to_human` to true to let the bot escalate a conversation, then describe who it reaches in `human_emails` (see below). Fields: `can_refer_to_human`, `human_emails`. Use GET /api/ext/omni_v2/instagram/get_chatbot to inspect the current assignment and DELETE /api/ext/omni_v2/instagram/unassign_chatbot to remove it. ## One agent, several accounts Assigning adds this account to the agent. The agent keeps answering on its other accounts, and their settings stay as they are. - **Per-account settings**: These apply to the selected account only. Fields: `can_refer_to_human`, `human_emails`, `auto_resolve_hours`, `webhook_url`, `webhook_hmac_secret`. - **Settings left out**: Leaving out `can_refer_to_human` turns handoff off, and leaving out `human_emails` sends an empty list, which hands over to a random project member who can see this account's conversations. Leaving out `auto_resolve_hours`, `webhook_url` or `webhook_hmac_secret` keeps this account's current value, also when the request replaces the account's agent. On a account with no agent, every setting left out is copied from the agent's first account when the agent already answers on another account, and starts empty otherwise. Fields: `can_refer_to_human`, `human_emails`, `auto_resolve_hours`, `webhook_url`, `webhook_hmac_secret`. - **Replacing an agent**: A account has one agent at a time. Assigning a different agent to a account replaces the previous one there; ongoing conversations on that account continue with the new agent, which does not see the previous agent's replies. The account keeps its own settings, except those sent in the request and the handoff rule above. - **inbox_ids**: `text_chatbot_settings.inbox_ids` in the response lists every Instagram account the agent now answers on. Fields: `inbox_ids`. ## Choosing who the bot hands over to `human_emails` accepts three shapes. Working hours are optional on any destination; when they are set, only destinations inside their window are eligible and one of them is picked at random. - **Anyone available** (human_emails: []): An empty list refers the conversation to a random project member who can see this account's conversations. Working hours are ignored on this path. Fields: `human_emails`. - **Specific people** (human_emails: ["a@example.com", { "email": "b@example.com", "hours": { ... } }]): A list of addresses. Each item is either a bare string (always available) or an object with an optional `hours` window. Fields: `human_emails`. - **By department** (human_emails: { "IT": { "desc": "...", "emails": [ ... ] } }): A category map. `desc` is required and is the only text the assistant sees when deciding which department fits, so make it specific. The assistant must pass a category on every call; if none of the descriptions match, nobody is contacted. Fields: `human_emails`. - **hours** ({ "days": [1,2,3,4,5], "start": "09:00", "end": "17:00", "timezone": "Asia/Riyadh" }): `days` are ISO weekdays, 1 = Monday through 7 = Sunday; an empty list means every day. `start` and `end` are zero-padded 24-hour times, and a window whose start is later than its end wraps past midnight. `timezone` is an IANA zone and defaults to Asia/Riyadh. Fields: `human_emails`. Addresses that do not belong to a project member who can see this account's conversations still open the conversation, but it is left unassigned. ## Choosing the Instagram account A project can connect more than one Instagram account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only Instagram account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's Instagram accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/instagram/inboxes. ## Errors - **409 Conflict**: Another change to the same account's agent, or to the same agent on another account, is still being saved. Wait a moment, then try again; GET /api/ext/omni_v2/instagram/get_chatbot shows the result once it is saved. ## Event webhooks Set `webhook_url` to receive a POST for each chatbot event on this account. Leave the field out to keep the current value, or send null to turn webhooks off. - **Events**: `message.received`: every incoming customer message on the account, including chats a person has taken over. `message.replied`: a reply sent by the bot. `message.sent`: a reply sent by a person or through the API. `conversation.status_changed`: a handoff to a person, the bot ending the chat, or a manual status change. `conversation.auto_resolved`: the chat was closed after the auto-resolve window. Fields: `webhook_url`. - **Payload** ({ "event", "occurred_at", "project_id", "agent_id", "conversation_id", "data" }): For message events, `data` has `conversation_id`, `content`, `content_type`, `created_at`, `message_type` (0 = incoming, 1 = outgoing) and `attachments` (each with `file_type` and `data_url`). For status events, `data` has `conversation_id`, `status`, `previous_status` (can be null) and `reason` (`refer_to_human`, `end_chat`, `auto_resolve` or `manual`). - **Signature** (X-Nabrah-Signature: sha256=): When `webhook_hmac_secret` is set, each request has `X-Nabrah-Timestamp` (Unix seconds) and `X-Nabrah-Signature`: `sha256=` followed by the hex HMAC-SHA256 of `.`, keyed with your secret. Verify it against the raw body, compare in constant time, and reject old timestamps. Every request also has `X-Nabrah-Event` with the event name. Fields: `webhook_hmac_secret`. - **Delivery**: Each event is sent once with a 5-second timeout. Failed or non-2xx deliveries are not retried, and events have no event ID. One status change can arrive as two events, for example `conversation.auto_resolved` and then `conversation.status_changed`. Keep your handler idempotent, and use GET /api/ext/omni_v2/conversation/conversation/{id}/messages to fill any gaps. Events are only sent while a chatbot is assigned to the account. GET /api/ext/omni_v2/instagram/get_chatbot returns `webhook_url` and `has_webhook_hmac_secret`; the secret itself is never returned. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the Instagram account to use, from GET /api/ext/omni_v2/instagram/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one Instagram account; required once it has more than one. ## Body (application/json) ### Example: Specific people A flat list of addresses, always available. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "Instagram Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] } ``` ### Example: With working hours Only reachable Monday to Friday, 09:00-17:00 Riyadh time. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "Instagram Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com", { "email": "oncall@example.com", "hours": { "days": [ 1, 2, 3, 4, 5 ], "start": "09:00", "end": "17:00", "timezone": "Asia/Riyadh" } } ] } ``` ### Example: By department The assistant picks a department from the descriptions. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "Instagram Support Bot", "can_refer_to_human": true, "human_emails": { "IT": { "desc": "Bugs, outages, login and password problems", "emails": [ "it@example.com" ] }, "Sales": { "desc": "Pricing, quotes and product demos", "emails": [ "sales@example.com", { "email": "nightshift@example.com", "hours": { "days": [], "start": "22:00", "end": "06:00", "timezone": "UTC" } } ] } } } ``` ### Example: Anyone available An empty list refers to a random project member who can see this account's conversations. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "Instagram Support Bot", "can_refer_to_human": true, "human_emails": [] } ``` ### Example: With event webhook Send chatbot events to your endpoint, signed with a shared secret. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "Instagram Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ], "webhook_url": "https://example.com/nabrah/events", "webhook_hmac_secret": "replace-with-a-long-random-secret" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "agent": { "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "name": "string", "start_first": false, "first_sentence": "string", "agent_type": "basic", "who_are_you": "string", "goal": "string", "steps": "string", "allow_interruptions": false, "voice": "dania", "support_data": "string", "languages": [ "ar" ], "stack": "latest", "speech_speed": 0.7, "variables": [ { "name": "string", "type": "string", "default_value": "string" } ], "pre_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_analysis_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "knowledge_bases": [ { "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "key": "string", "desc": "string", "kind": "unstructured", "document_count": 0, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ], "system_tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "when_to_use": "string" } ], "analysis_groups": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "background_ambient": false, "background_ambient_volume": 0, "thinking_sound": false, "thinking_sound_volume": 0 }, "text_chatbot_settings": { "can_refer_to_human": false, "human_emails": [ "string" ], "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "auto_resolve_hours": 0, "webhook_url": "string", "has_webhook_hmac_secret": false, "inbox_ids": [ "00000000-0000-4000-8000-000000000000" ] } } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/instagram/assign_chatbot' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "agent_id": "00000000-0000-4000-8000-000000000000", "name": "Instagram Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/instagram/assign_chatbot" headers = { "X-API-Key": "", } payload = { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "Instagram Support Bot", "can_refer_to_human": True, "human_emails": [ "support@example.com" ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/instagram/assign_chatbot", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "agent_id": "00000000-0000-4000-8000-000000000000", "name": "Instagram Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "agent_id": "00000000-0000-4000-8000-000000000000", "name": "Instagram Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/instagram/assign_chatbot", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get the agent on an Instagram account > GET https://api.nabrah.ai/api/ext/omni_v2/instagram/get_chatbot Group: Instagram. Page: https://www.nabrah.ai/en/docs/api-reference/instagram/get-the-agent-on-an-instagram-account Retrieve the agent currently answering on one of the project's Instagram accounts. The response returns the agent's full configuration under `agent` and that account's chatbot settings under `text_chatbot_settings`. Use this to confirm which agent is auto-replying on a account before updating or removing the assignment. ## Reading the response - **agent**: The complete configuration of the agent handling Instagram direct messages, including `agent_id`, `name`, languages, voice, tools, and callbacks. - **text_chatbot_settings**: The settings of this account. `name` is shared by every account the agent answers on; `inbox_ids` lists those accounts. `webhook_url` is the event webhook set on assign; the signing secret is never returned, only `has_webhook_hmac_secret`. Fields: `can_refer_to_human`, `human_emails`, `auto_resolve_hours`, `webhook_url`, `has_webhook_hmac_secret`, `name`, `inbox_ids`, `created_at`. Assign with POST /api/ext/omni_v2/instagram/assign_chatbot and remove with DELETE /api/ext/omni_v2/instagram/unassign_chatbot. ## Reading human_emails `text_chatbot_settings.human_emails` comes back in whichever shape it was stored: a list of addresses, a list of objects with `hours`, or a category map. - **Null fields in the response**: Responses are not stripped of nulls, so an `hours` object can come back carrying `start: null` or `end: null`, and a destination that has no window at all can appear as `hours: null`. Treat all three as `always available`. Fields: `human_emails`. ## Choosing the Instagram account A project can connect more than one Instagram account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only Instagram account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's Instagram accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/instagram/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the Instagram account to use, from GET /api/ext/omni_v2/instagram/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one Instagram account; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json { "agent": { "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "name": "string", "start_first": false, "first_sentence": "string", "agent_type": "basic", "who_are_you": "string", "goal": "string", "steps": "string", "allow_interruptions": false, "voice": "dania", "support_data": "string", "languages": [ "ar" ], "stack": "latest", "speech_speed": 0.7, "variables": [ { "name": "string", "type": "string", "default_value": "string" } ], "pre_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_analysis_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "knowledge_bases": [ { "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "key": "string", "desc": "string", "kind": "unstructured", "document_count": 0, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ], "system_tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "when_to_use": "string" } ], "analysis_groups": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "background_ambient": false, "background_ambient_volume": 0, "thinking_sound": false, "thinking_sound_volume": 0 }, "text_chatbot_settings": { "can_refer_to_human": false, "human_emails": [ "string" ], "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "auto_resolve_hours": 0, "webhook_url": "string", "has_webhook_hmac_secret": false, "inbox_ids": [ "00000000-0000-4000-8000-000000000000" ] } } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/instagram/get_chatbot' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/instagram/get_chatbot" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/instagram/get_chatbot", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/instagram/get_chatbot", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Remove the agent from an Instagram account > DELETE https://api.nabrah.ai/api/ext/omni_v2/instagram/unassign_chatbot Group: Instagram. Page: https://www.nabrah.ai/en/docs/api-reference/instagram/remove-the-agent-from-an-instagram-account Remove the agent from one of the project's Instagram accounts, stopping automatic AI replies on that account. The agent keeps answering on its other accounts with their settings unchanged. The response is an object confirming the operation. ## Effect After unassigning, incoming Instagram direct messages on that account are no longer answered automatically by an AI agent. Check the current assignment first with GET /api/ext/omni_v2/instagram/get_chatbot, and re-attach later with POST /api/ext/omni_v2/instagram/assign_chatbot. ## Choosing the Instagram account A project can connect more than one Instagram account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only Instagram account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's Instagram accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/instagram/inboxes. ## Errors - **409 Conflict**: Another change to the same account's agent is still being saved. Wait a moment, then try again; GET /api/ext/omni_v2/instagram/get_chatbot shows the result once it is saved. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the Instagram account to use, from GET /api/ext/omni_v2/instagram/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one Instagram account; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json 0 ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/omni_v2/instagram/unassign_chatbot' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/instagram/unassign_chatbot" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/instagram/unassign_chatbot", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/omni_v2/instagram/unassign_chatbot", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # TikTok API Work the conversations of connected TikTok accounts and put an agent on each account. - [List TikTok accounts](https://www.nabrah.ai/en/docs/api-reference/tiktok/list-tiktok-accounts.md) `GET /api/ext/omni_v2/tiktok/inboxes`: List the TikTok accounts connected to the project, oldest first. - [Get TikTok account](https://www.nabrah.ai/en/docs/api-reference/tiktok/get-tiktok-account.md) `GET /api/ext/omni_v2/tiktok/inboxes/{inbox_id}`: Retrieve one of the project's TikTok accounts by its id: id, name, username, avatar_url, disconnected, created_at, and the agent answering on it (agent_id and agent_name, null when none). - [Delete TikTok account](https://www.nabrah.ai/en/docs/api-reference/tiktok/delete-tiktok-account.md) `DELETE /api/ext/omni_v2/tiktok/inboxes`: Remove one of the project's TikTok accounts. - [List TikTok conversations](https://www.nabrah.ai/en/docs/api-reference/tiktok/list-tiktok-conversations.md) `GET /api/ext/omni_v2/tiktok`: List TikTok conversations (tickets) on one of the project's TikTok accounts, with optional filtering by assignment and status. - [Filter TikTok conversations](https://www.nabrah.ai/en/docs/api-reference/tiktok/filter-tiktok-conversations.md) `POST /api/ext/omni_v2/tiktok/filter`: List TikTok conversations on one of the project's TikTok accounts using a structured filter payload instead of simple query parameters. - [List TikTok conversations by contact](https://www.nabrah.ai/en/docs/api-reference/tiktok/list-tiktok-conversations-by-contact.md) `POST /api/ext/omni_v2/tiktok/contact/{contact_id}`: Lists the TikTok conversations of the contact identified by contact_id on one of the project's TikTok accounts, with optional server-side filtering in the request body and pagination via the page query parameter. - [Assign an agent to a TikTok account](https://www.nabrah.ai/en/docs/api-reference/tiktok/assign-an-agent-to-a-tiktok-account.md) `POST /api/ext/omni_v2/tiktok/assign_chatbot`: Attach an AI agent to one of the project's TikTok accounts so it automatically replies to incoming TikTok direct messages on that account. - [Get the agent on a TikTok account](https://www.nabrah.ai/en/docs/api-reference/tiktok/get-the-agent-on-a-tiktok-account.md) `GET /api/ext/omni_v2/tiktok/get_chatbot`: Retrieve the agent currently answering on one of the project's TikTok accounts. - [Remove the agent from a TikTok account](https://www.nabrah.ai/en/docs/api-reference/tiktok/remove-the-agent-from-a-tiktok-account.md) `DELETE /api/ext/omni_v2/tiktok/unassign_chatbot`: Remove the agent from one of the project's TikTok accounts, stopping automatic AI replies on that account. --- # List TikTok accounts > GET https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes Group: TikTok. Page: https://www.nabrah.ai/en/docs/api-reference/tiktok/list-tiktok-accounts List the TikTok accounts connected to the project, oldest first. Each entry includes its `id`, display `name`, `username`, `avatar_url`, whether it needs reconnecting (`disconnected`), `created_at`, and the agent answering on it. Use the returned `id` as the `inbox_id` for the other TikTok endpoints. ## Where inbox IDs come from This is the canonical source for `inbox_id` values used by the other TikTok endpoints. - **inboxes[].id**: The UUID of each TikTok account. Pass it as `inbox_id` to the other TikTok endpoints, or as the `{inbox_id}` path parameter to get one account. Fields: `id`, `name`, `username`, `avatar_url`, `created_at`. - **Answering agent**: The agent auto-replying on the account, or null when none is assigned. Fields: `agent_id`, `agent_name`. - **disconnected**: True when the account's access was revoked or expired. Replies are not delivered until someone reconnects it from the platform; its conversations and settings are kept. Fields: `disconnected`. - **max_inboxes**: How many TikTok accounts the project may connect. Contact support to raise it. Fields: `max_inboxes`. ## Connecting accounts Accounts are connected and reconnected in the platform, because the account owner has to sign in with TikTok in their browser. There is no API for connecting an account. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Response ### 200 Successful Response Content type: `application/json` ```json { "inboxes": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "username": "string", "avatar_url": "string", "disconnected": false, "agent_id": "00000000-0000-4000-8000-000000000000", "agent_name": "string" } ], "max_inboxes": 0 } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get TikTok account > GET https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes/{inbox_id} Group: TikTok. Page: https://www.nabrah.ai/en/docs/api-reference/tiktok/get-tiktok-account Retrieve one of the project's TikTok accounts by its id: `id`, `name`, `username`, `avatar_url`, `disconnected`, `created_at`, and the agent answering on it (`agent_id` and `agent_name`, null when none). ## Where the inbox_id comes from Obtain the `inbox_id` from the account list endpoint. Call GET /api/ext/omni_v2/tiktok/inboxes and use any returned `inboxes[].id` value. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `inbox_id` (string, required): The UUID of the TikTok account to fetch. Get this value from GET /api/ext/omni_v2/tiktok/inboxes (the `inboxes[].id` field). ## Response ### 200 Successful Response Content type: `application/json` ```json { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "username": "string", "avatar_url": "string", "disconnected": false, "agent_id": "00000000-0000-4000-8000-000000000000", "agent_name": "string" } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes/' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes/" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes/", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes/", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Delete TikTok account > DELETE https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes Group: TikTok. Page: https://www.nabrah.ai/en/docs/api-reference/tiktok/delete-tiktok-account Remove one of the project's TikTok accounts. Its conversations and its customers' contacts are deleted, the agent stops replying on it, and member access to it is removed. Choose the account with `inbox_id`. The response is a boolean indicating whether the removal succeeded. ## Irreversible Removing an account deletes its conversations and its customers' contacts. Connect it again from the platform to start over. - **Agents**: An agent that answered on the deleted account stays configured and keeps answering on its other accounts. Use GET /api/ext/omni_v2/tiktok/inboxes to confirm which inbox(es) exist before deleting. ## Choosing the TikTok account A project can connect more than one TikTok account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only TikTok account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's TikTok accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/tiktok/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the TikTok account to use, from GET /api/ext/omni_v2/tiktok/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one TikTok account; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json false ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/omni_v2/tiktok/inboxes", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List TikTok conversations > GET https://api.nabrah.ai/api/ext/omni_v2/tiktok Group: TikTok. Page: https://www.nabrah.ai/en/docs/api-reference/tiktok/list-tiktok-conversations List TikTok conversations (tickets) on one of the project's TikTok accounts, with optional filtering by assignment and status. Results are paginated and the response includes a `meta` block with counts for each assignment bucket (all, mine, unassigned, assigned) so you can render conversation tabs. This is the generic conversation listing endpoint — use the POST `/filter` variant for structured filters or the by-contact endpoint to scope to a single contact. ## Pagination & counts The endpoint returns one page of conversations at a time inside `payload`, plus aggregate `meta` counts. - **page**: Pass `?page=1` for the first page and increment to walk subsequent pages. When a page returns fewer items than the page size (or an empty `payload`), you have reached the end. - **meta counts**: `meta.all_count`, `meta.mine_count`, `meta.unassigned_count`, and `meta.assigned_count` reflect the totals for each assignment bucket and are independent of the current page. Fields: `all_count`, `mine_count`, `unassigned_count`, `assigned_count`. ## Reading conversations Each item in `payload` is a conversation. Useful fields: `id` (the conversation/ticket id), `inbox_id` (which TikTok account it belongs to), `status`, `unread_count`, and `last_non_activity_message` (the most recent real message). Authenticate every request with the `X-API-Key` header. The key is scoped to a single project, so no project_id is required. ## Choosing the TikTok account A project can connect more than one TikTok account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only TikTok account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's TikTok accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/tiktok/inboxes. ## Reply window TikTok allows replies within 48 hours of the customer's last message; after that, nothing can be sent until the customer writes again. TikTok also limits how many messages you can send before the customer replies. - **reply_window_ends_at**: Each TikTok conversation carries `reply_window_ends_at`: a Unix timestamp for when the reply window closes, counted from the customer's last message. Sending after it returns 409 `reply_window_closed`. Fields: `reply_window_ends_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `assignee_type` (enum(all | me | unassigned | assigned), optional): Filter conversations by assignment relative to the API key's identity. One of `all`, `me`, `unassigned`, or `assigned`. Defaults to `all` when omitted. - `status_param` (enum(open | pending | resolved | snoozed | all), optional): Filter conversations by their workflow status. One of `open`, `pending`, `resolved`, `snoozed`, or `all`. Defaults to `open` when omitted. - `page` (integer, optional): 1-based page account for paginating results. Optional; defaults to the first page when omitted. - `inbox_id` (string, optional): The `id` of the TikTok account to use, from GET /api/ext/omni_v2/tiktok/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one TikTok account; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/tiktok' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/tiktok" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/tiktok", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/tiktok", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Filter TikTok conversations > POST https://api.nabrah.ai/api/ext/omni_v2/tiktok/filter Group: TikTok. Page: https://www.nabrah.ai/en/docs/api-reference/tiktok/filter-tiktok-conversations List TikTok conversations on one of the project's TikTok accounts using a structured filter payload instead of simple query parameters. The request body carries a `filters` array of filter objects, and results are paginated with the `page` query parameter. The response shape matches the generic list endpoint, including `meta` counts and a `payload` array of conversations. ## Filters & pagination Combine the structured `filters` body with the `page` query parameter to page through matching conversations. - **filters**: An array of filter objects describing the conditions to match. Pass an empty array for no constraints, or add filter conditions; each condition needs `attribute_key`, `filter_operator` and `values` to narrow the result set. Fields: `filters`. The response `meta` counts (all/mine/unassigned/assigned) are independent of the current page. ## Choosing the TikTok account A project can connect more than one TikTok account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only TikTok account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's TikTok accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/tiktok/inboxes. ## Reply window TikTok allows replies within 48 hours of the customer's last message; after that, nothing can be sent until the customer writes again. TikTok also limits how many messages you can send before the customer replies. - **reply_window_ends_at**: Each TikTok conversation carries `reply_window_ends_at`: a Unix timestamp for when the reply window closes, counted from the customer's last message. Sending after it returns 409 `reply_window_closed`. Fields: `reply_window_ends_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `page` (integer, optional): 1-based page account for paginating filtered results. Optional; defaults to the first page when omitted. - `page_size` (integer, optional) - `status` (enum(open | pending | resolved | snoozed | all), optional) - `assignee_type` (enum(all | me | unassigned | assigned), optional) - `inbox_id` (string, optional): The `id` of the TikTok account to use, from GET /api/ext/omni_v2/tiktok/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one TikTok account; required once it has more than one. ## Body (application/json) ### Example: Filter conversations ```json { "filters": [] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/tiktok/filter' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/tiktok/filter" headers = { "X-API-Key": "", } payload = { "filters": [] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/tiktok/filter", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/tiktok/filter", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # List TikTok conversations by contact > POST https://api.nabrah.ai/api/ext/omni_v2/tiktok/contact/{contact_id} Group: TikTok. Page: https://www.nabrah.ai/en/docs/api-reference/tiktok/list-tiktok-conversations-by-contact Lists the TikTok conversations of the contact identified by `contact_id` on one of the project's TikTok accounts, with optional server-side filtering in the request body and pagination via the `page` query parameter. The response returns a `meta` block with conversation counts (`all_count`, `mine_count`, `unassigned_count`, `assigned_count`) and a `payload` array of conversation objects. Send an empty `filters` array to return all of the contact's TikTok conversations. ## Authentication - **X-API-Key header**: Send your Web API key (prefix `nb_`) in the `X-API-Key` header. The key is scoped to one project; the contact must belong to that project. ## Filtering and pagination - **filters**: The request body takes a `filters` array. Send an empty array `[]` to return all of the contact's TikTok conversations, or add filter objects to narrow the results. - **page**: Use the `page` query parameter to page through results. The response `meta` reports `all_count`, `mine_count`, `unassigned_count`, and `assigned_count`. Fields: `meta.all_count`, `meta.mine_count`, `meta.unassigned_count`, `meta.assigned_count`. ## Choosing the TikTok account A project can connect more than one TikTok account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only TikTok account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's TikTok accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/tiktok/inboxes. ## Reply window TikTok allows replies within 48 hours of the customer's last message; after that, nothing can be sent until the customer writes again. TikTok also limits how many messages you can send before the customer replies. - **reply_window_ends_at**: Each TikTok conversation carries `reply_window_ends_at`: a Unix timestamp for when the reply window closes, counted from the customer's last message. Sending after it returns 409 `reply_window_closed`. Fields: `reply_window_ends_at`. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Path parameters - `contact_id` (integer, required): Numeric identifier of the contact whose TikTok conversations are listed. Obtained from `GET /api/ext/omni_v2/contact` or `GET /api/ext/omni_v2/contact/search` (the contact `id` field). ## Query parameters - `page` (integer, optional): 1-based page account of conversations to retrieve. Omit to fetch the first page. Increment until the returned payload array is empty to iterate over all conversations. - `inbox_id` (string, optional): The `id` of the TikTok account to use, from GET /api/ext/omni_v2/tiktok/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one TikTok account; required once it has more than one. ## Body (application/json) ### Example: All TikTok conversations Return every TikTok conversation for the contact (empty filters). ```json { "filters": [] } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "meta": { "all_count": 0, "mine_count": 0, "unassigned_count": 0, "assigned_count": 0 }, "payload": [ { "account_id": 0, "additional_attributes": {}, "agent_last_seen_at": 0, "assignee_last_seen_at": 0, "can_reply": false, "contact_last_seen_at": 0, "created_at": 0, "custom_attributes": {}, "first_reply_created_at": 0, "id": 0, "inbox_id": 0, "labels": [ "string" ], "last_activity_at": 0, "last_non_activity_message": { "account_id": 0, "additional_attributes": {}, "content": "string", "content_attributes": {}, "content_type": "string", "conversation": null, "conversation_id": 0, "created_at": 0, "external_source_ids": {}, "id": 0, "inbox_id": 0, "message_type": 0, "private": false, "processed_message_content": "string", "sender": null, "sender_id": 0, "sender_type": "string", "sentiment": {}, "source_id": "string", "status": "string", "updated_at": "string", "attachments": [] }, "messages": [ { "account_id": null, "additional_attributes": null, "content": null, "content_attributes": null, "content_type": null, "conversation": null, "conversation_id": null, "created_at": null, "external_source_ids": null, "id": null, "inbox_id": null, "message_type": null, "private": null, "processed_message_content": null, "sender": null, "sender_id": null, "sender_type": null, "sentiment": null, "source_id": null, "status": null, "updated_at": null, "attachments": null } ], "meta": { "channel": "string", "hmac_verified": false, "sender": null, "assignee": null }, "muted": false, "priority": "string", "sla_policy_id": 0, "snoozed_until": 0, "status": "string", "timestamp": 0, "unread_count": 0, "updated_at": 0 } ] } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/tiktok/contact/' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "filters": [] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/tiktok/contact/" headers = { "X-API-Key": "", } payload = { "filters": [] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/tiktok/contact/", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "filters": [] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "filters": [] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/tiktok/contact/", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Assign an agent to a TikTok account > POST https://api.nabrah.ai/api/ext/omni_v2/tiktok/assign_chatbot Group: TikTok. Page: https://www.nabrah.ai/en/docs/api-reference/tiktok/assign-an-agent-to-a-tiktok-account Attach an AI agent to one of the project's TikTok accounts so it automatically replies to incoming TikTok direct messages on that account. You bind an existing agent (by `agent_id`) and configure, for this account, whether the bot may escalate to a human, who it hands over to, when idle conversations close, and where conversation events are sent. The response returns the bound agent's full configuration plus this account's `text_chatbot_settings`. ## How auto-reply works Once assigned, incoming TikTok direct messages on that account are handled by the chosen agent, which generates and sends replies automatically. - **agent_id**: The UUID of an existing text/basic agent in this project that should answer messages. Fields: `agent_id`. - **name**: The bot's display name. It is shared by every TikTok account this agent answers on. Fields: `name`. - **Human handoff**: Set `can_refer_to_human` to true to let the bot escalate a conversation, then describe who it reaches in `human_emails` (see below). Fields: `can_refer_to_human`, `human_emails`. Use GET /api/ext/omni_v2/tiktok/get_chatbot to inspect the current assignment and DELETE /api/ext/omni_v2/tiktok/unassign_chatbot to remove it. ## One agent, several accounts Assigning adds this account to the agent. The agent keeps answering on its other accounts, and their settings stay as they are. - **Per-account settings**: These apply to the selected account only. Fields: `can_refer_to_human`, `human_emails`, `auto_resolve_hours`, `webhook_url`, `webhook_hmac_secret`. - **Settings left out**: Leaving out `can_refer_to_human` turns handoff off, and leaving out `human_emails` sends an empty list, which hands over to a random project member who can see this account's conversations. Leaving out `auto_resolve_hours`, `webhook_url` or `webhook_hmac_secret` keeps this account's current value, also when the request replaces the account's agent. On a account with no agent, every setting left out is copied from the agent's first account when the agent already answers on another account, and starts empty otherwise. Fields: `can_refer_to_human`, `human_emails`, `auto_resolve_hours`, `webhook_url`, `webhook_hmac_secret`. - **Replacing an agent**: A account has one agent at a time. Assigning a different agent to a account replaces the previous one there; ongoing conversations on that account continue with the new agent, which does not see the previous agent's replies. The account keeps its own settings, except those sent in the request and the handoff rule above. - **inbox_ids**: `text_chatbot_settings.inbox_ids` in the response lists every TikTok account the agent now answers on. Fields: `inbox_ids`. ## Choosing who the bot hands over to `human_emails` accepts three shapes. Working hours are optional on any destination; when they are set, only destinations inside their window are eligible and one of them is picked at random. - **Anyone available** (human_emails: []): An empty list refers the conversation to a random project member who can see this account's conversations. Working hours are ignored on this path. Fields: `human_emails`. - **Specific people** (human_emails: ["a@example.com", { "email": "b@example.com", "hours": { ... } }]): A list of addresses. Each item is either a bare string (always available) or an object with an optional `hours` window. Fields: `human_emails`. - **By department** (human_emails: { "IT": { "desc": "...", "emails": [ ... ] } }): A category map. `desc` is required and is the only text the assistant sees when deciding which department fits, so make it specific. The assistant must pass a category on every call; if none of the descriptions match, nobody is contacted. Fields: `human_emails`. - **hours** ({ "days": [1,2,3,4,5], "start": "09:00", "end": "17:00", "timezone": "Asia/Riyadh" }): `days` are ISO weekdays, 1 = Monday through 7 = Sunday; an empty list means every day. `start` and `end` are zero-padded 24-hour times, and a window whose start is later than its end wraps past midnight. `timezone` is an IANA zone and defaults to Asia/Riyadh. Fields: `human_emails`. Addresses that do not belong to a project member who can see this account's conversations still open the conversation, but it is left unassigned. ## Choosing the TikTok account A project can connect more than one TikTok account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only TikTok account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's TikTok accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/tiktok/inboxes. ## Errors - **409 Conflict**: Another change to the same account's agent, or to the same agent on another account, is still being saved. Wait a moment, then try again; GET /api/ext/omni_v2/tiktok/get_chatbot shows the result once it is saved. ## Event webhooks Set `webhook_url` to receive a POST for each chatbot event on this account. Leave the field out to keep the current value, or send null to turn webhooks off. - **Events**: `message.received`: every incoming customer message on the account, including chats a person has taken over. `message.replied`: a reply sent by the bot. `message.sent`: a reply sent by a person or through the API. `conversation.status_changed`: a handoff to a person, the bot ending the chat, or a manual status change. `conversation.auto_resolved`: the chat was closed after the auto-resolve window. Fields: `webhook_url`. - **Payload** ({ "event", "occurred_at", "project_id", "agent_id", "conversation_id", "data" }): For message events, `data` has `conversation_id`, `content`, `content_type`, `created_at`, `message_type` (0 = incoming, 1 = outgoing) and `attachments` (each with `file_type` and `data_url`). For status events, `data` has `conversation_id`, `status`, `previous_status` (can be null) and `reason` (`refer_to_human`, `end_chat`, `auto_resolve` or `manual`). - **Signature** (X-Nabrah-Signature: sha256=): When `webhook_hmac_secret` is set, each request has `X-Nabrah-Timestamp` (Unix seconds) and `X-Nabrah-Signature`: `sha256=` followed by the hex HMAC-SHA256 of `.`, keyed with your secret. Verify it against the raw body, compare in constant time, and reject old timestamps. Every request also has `X-Nabrah-Event` with the event name. Fields: `webhook_hmac_secret`. - **Delivery**: Each event is sent once with a 5-second timeout. Failed or non-2xx deliveries are not retried, and events have no event ID. One status change can arrive as two events, for example `conversation.auto_resolved` and then `conversation.status_changed`. Keep your handler idempotent, and use GET /api/ext/omni_v2/conversation/conversation/{id}/messages to fill any gaps. Events are only sent while a chatbot is assigned to the account. GET /api/ext/omni_v2/tiktok/get_chatbot returns `webhook_url` and `has_webhook_hmac_secret`; the secret itself is never returned. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the TikTok account to use, from GET /api/ext/omni_v2/tiktok/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one TikTok account; required once it has more than one. ## Body (application/json) ### Example: Specific people A flat list of addresses, always available. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "TikTok Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] } ``` ### Example: With working hours Only reachable Monday to Friday, 09:00-17:00 Riyadh time. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "TikTok Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com", { "email": "oncall@example.com", "hours": { "days": [ 1, 2, 3, 4, 5 ], "start": "09:00", "end": "17:00", "timezone": "Asia/Riyadh" } } ] } ``` ### Example: By department The assistant picks a department from the descriptions. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "TikTok Support Bot", "can_refer_to_human": true, "human_emails": { "IT": { "desc": "Bugs, outages, login and password problems", "emails": [ "it@example.com" ] }, "Sales": { "desc": "Pricing, quotes and product demos", "emails": [ "sales@example.com", { "email": "nightshift@example.com", "hours": { "days": [], "start": "22:00", "end": "06:00", "timezone": "UTC" } } ] } } } ``` ### Example: Anyone available An empty list refers to a random project member who can see this account's conversations. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "TikTok Support Bot", "can_refer_to_human": true, "human_emails": [] } ``` ### Example: With event webhook Send chatbot events to your endpoint, signed with a shared secret. ```json { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "TikTok Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ], "webhook_url": "https://example.com/nabrah/events", "webhook_hmac_secret": "replace-with-a-long-random-secret" } ``` ## Response ### 200 Successful Response Content type: `application/json` ```json { "agent": { "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "name": "string", "start_first": false, "first_sentence": "string", "agent_type": "basic", "who_are_you": "string", "goal": "string", "steps": "string", "allow_interruptions": false, "voice": "dania", "support_data": "string", "languages": [ "ar" ], "stack": "latest", "speech_speed": 0.7, "variables": [ { "name": "string", "type": "string", "default_value": "string" } ], "pre_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_analysis_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "knowledge_bases": [ { "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "key": "string", "desc": "string", "kind": "unstructured", "document_count": 0, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ], "system_tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "when_to_use": "string" } ], "analysis_groups": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "background_ambient": false, "background_ambient_volume": 0, "thinking_sound": false, "thinking_sound_volume": 0 }, "text_chatbot_settings": { "can_refer_to_human": false, "human_emails": [ "string" ], "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "auto_resolve_hours": 0, "webhook_url": "string", "has_webhook_hmac_secret": false, "inbox_ids": [ "00000000-0000-4000-8000-000000000000" ] } } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request POST \ --url 'https://api.nabrah.ai/api/ext/omni_v2/tiktok/assign_chatbot' \ --header 'X-API-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "agent_id": "00000000-0000-4000-8000-000000000000", "name": "TikTok Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] }' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/tiktok/assign_chatbot" headers = { "X-API-Key": "", } payload = { "agent_id": "00000000-0000-4000-8000-000000000000", "name": "TikTok Support Bot", "can_refer_to_human": True, "human_emails": [ "support@example.com" ] } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/tiktok/assign_chatbot", { method: "POST", headers: { "X-API-Key": "", "Content-Type": "application/json", }, body: JSON.stringify({ "agent_id": "00000000-0000-4000-8000-000000000000", "name": "TikTok Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] }), }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := strings.NewReader(`{ "agent_id": "00000000-0000-4000-8000-000000000000", "name": "TikTok Support Bot", "can_refer_to_human": true, "human_emails": [ "support@example.com" ] }`) req, _ := http.NewRequest("POST", "https://api.nabrah.ai/api/ext/omni_v2/tiktok/assign_chatbot", payload) req.Header.Set("X-API-Key", "") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Get the agent on a TikTok account > GET https://api.nabrah.ai/api/ext/omni_v2/tiktok/get_chatbot Group: TikTok. Page: https://www.nabrah.ai/en/docs/api-reference/tiktok/get-the-agent-on-a-tiktok-account Retrieve the agent currently answering on one of the project's TikTok accounts. The response returns the agent's full configuration under `agent` and that account's chatbot settings under `text_chatbot_settings`. Use this to confirm which agent is auto-replying on a account before updating or removing the assignment. ## Reading the response - **agent**: The complete configuration of the agent handling TikTok direct messages, including `agent_id`, `name`, languages, voice, tools, and callbacks. - **text_chatbot_settings**: The settings of this account. `name` is shared by every account the agent answers on; `inbox_ids` lists those accounts. `webhook_url` is the event webhook set on assign; the signing secret is never returned, only `has_webhook_hmac_secret`. Fields: `can_refer_to_human`, `human_emails`, `auto_resolve_hours`, `webhook_url`, `has_webhook_hmac_secret`, `name`, `inbox_ids`, `created_at`. Assign with POST /api/ext/omni_v2/tiktok/assign_chatbot and remove with DELETE /api/ext/omni_v2/tiktok/unassign_chatbot. ## Reading human_emails `text_chatbot_settings.human_emails` comes back in whichever shape it was stored: a list of addresses, a list of objects with `hours`, or a category map. - **Null fields in the response**: Responses are not stripped of nulls, so an `hours` object can come back carrying `start: null` or `end: null`, and a destination that has no window at all can appear as `hours: null`. Treat all three as `always available`. Fields: `human_emails`. ## Choosing the TikTok account A project can connect more than one TikTok account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only TikTok account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's TikTok accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/tiktok/inboxes. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the TikTok account to use, from GET /api/ext/omni_v2/tiktok/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one TikTok account; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json { "agent": { "agent_id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "created_by_email": "string", "created_at": "2026-01-15T12:00:00.000Z", "last_update": "2026-01-15T12:00:00.000Z", "name": "string", "start_first": false, "first_sentence": "string", "agent_type": "basic", "who_are_you": "string", "goal": "string", "steps": "string", "allow_interruptions": false, "voice": "dania", "support_data": "string", "languages": [ "ar" ], "stack": "latest", "speech_speed": 0.7, "variables": [ { "name": "string", "type": "string", "default_value": "string" } ], "pre_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_call_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "post_analysis_callback": { "url": "string", "parameters": [ { "name": null, "location": null, "data_type": null, "value": null } ] }, "tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "knowledge_bases": [ { "id": "00000000-0000-4000-8000-000000000000", "project_id": "00000000-0000-4000-8000-000000000000", "name": "string", "key": "string", "desc": "string", "kind": "unstructured", "document_count": 0, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } ], "system_tools": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string", "config": {}, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "when_to_use": "string" } ], "analysis_groups": [ { "id": "00000000-0000-4000-8000-000000000000", "name": "string" } ], "background_ambient": false, "background_ambient_volume": 0, "thinking_sound": false, "thinking_sound_volume": 0 }, "text_chatbot_settings": { "can_refer_to_human": false, "human_emails": [ "string" ], "name": "string", "created_at": "2026-01-15T12:00:00.000Z", "auto_resolve_hours": 0, "webhook_url": "string", "has_webhook_hmac_secret": false, "inbox_ids": [ "00000000-0000-4000-8000-000000000000" ] } } ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request GET \ --url 'https://api.nabrah.ai/api/ext/omni_v2/tiktok/get_chatbot' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/tiktok/get_chatbot" headers = {"X-API-Key": ""} response = requests.get(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/tiktok/get_chatbot", { method: "GET", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.nabrah.ai/api/ext/omni_v2/tiktok/get_chatbot", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ``` --- # Remove the agent from a TikTok account > DELETE https://api.nabrah.ai/api/ext/omni_v2/tiktok/unassign_chatbot Group: TikTok. Page: https://www.nabrah.ai/en/docs/api-reference/tiktok/remove-the-agent-from-a-tiktok-account Remove the agent from one of the project's TikTok accounts, stopping automatic AI replies on that account. The agent keeps answering on its other accounts with their settings unchanged. The response is an object confirming the operation. ## Effect After unassigning, incoming TikTok direct messages on that account are no longer answered automatically by an AI agent. Check the current assignment first with GET /api/ext/omni_v2/tiktok/get_chatbot, and re-attach later with POST /api/ext/omni_v2/tiktok/assign_chatbot. ## Choosing the TikTok account A project can connect more than one TikTok account. `inbox_id` selects the account this call works on. - **One account**: `inbox_id` is optional; the project's only TikTok account is used. - **More than one account**: `inbox_id` is required and the call returns 400 without it. An id that is not one of the project's TikTok accounts returns 404. Fields: `inbox_id`. List the accounts and their ids with GET /api/ext/omni_v2/tiktok/inboxes. ## Errors - **409 Conflict**: Another change to the same account's agent is still being saved. Wait a moment, then try again; GET /api/ext/omni_v2/tiktok/get_chatbot shows the result once it is saved. ## Authorization - `X-API-Key` (string, header, required): your workspace API key. ## Query parameters - `inbox_id` (string, optional): The `id` of the TikTok account to use, from GET /api/ext/omni_v2/tiktok/inboxes. Use `inboxes[].id` (a UUID), not the numeric `inboxes[].inbox_id`. Optional while the project has one TikTok account; required once it has more than one. ## Response ### 200 Successful Response Content type: `application/json` ```json 0 ``` ### 422 Validation Error Content type: `application/json` ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] } ``` ## Request examples ### cURL ```bash curl --request DELETE \ --url 'https://api.nabrah.ai/api/ext/omni_v2/tiktok/unassign_chatbot' \ --header 'X-API-Key: ' ``` ### Python ```python import requests url = "https://api.nabrah.ai/api/ext/omni_v2/tiktok/unassign_chatbot" headers = {"X-API-Key": ""} response = requests.delete(url, headers=headers) response.raise_for_status() print(response.json()) ``` ### JavaScript ```javascript const response = await fetch("https://api.nabrah.ai/api/ext/omni_v2/tiktok/unassign_chatbot", { method: "DELETE", headers: { "X-API-Key": "" }, }); const data = await response.json(); console.log(data); ``` ### Go ```go package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("DELETE", "https://api.nabrah.ai/api/ext/omni_v2/tiktok/unassign_chatbot", nil) req.Header.Set("X-API-Key", "") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out)) } ```