Mitsolab product manual

ML Console documentation

Configure, operate, secure, and troubleshoot every part of a Mitsolab workspace. This manual covers the settings involved, the correct setup order, practical examples, validation, and failure handling.

Complete Console referenceUpdated August 15, 2026

Overview

Understand what the Console controls, how it relates to the Portal, and how to establish a workspace safely.

Console and Portal are different products

ML Console is the administrative control plane. Owners and Console teammates use it to configure workspaces, channels, AI Agents, routing, integrations, permissions, billing, notifications, APIs, and webhooks.

Mitsolab Portal is the daily operations application. Human agents use its Inbox, CRM, tasks, knowledge, and approved Action Tools. A setting made in Console can change what Portal agents can see or do, but the two applications have separate sign-in surfaces and responsibilities.

Choose, create, and switch workspaces

A workspace is the security, billing, data, and configuration boundary for one organization. The workspace chooser marks workspaces as Owner or Shared; the label describes your relationship to the workspace, not a different workspace type.

  1. Open Console and select a workspace from Choose a workspace. The workspace icon is identical for every workspace; use the name and relationship label to distinguish them.
  2. To create another workspace, choose Create workspace, enter a clear organization name, and submit. Console enforces the account's workspace limit.
  3. Inside Console, use the current-workspace control in the sidebar to return to the chooser. Confirm the workspace name before changing channels, keys, routing, or billing.

The workspace list does not expose owner user IDs. Console only needs the workspace ID internally and returns the user-facing relationship text Owner or Shared.

Portal activation and checkout

Portal-dependent configuration requires an active Portal subscription or eligible trial. The activation gate checks workspace requirements, displays the selected seat quantity and billing terms, sends the checkout request, and waits for the billing provider to synchronize the subscription.

  1. Open a Portal-dependent page and review the activation card. Resolve any missing workspace requirement shown there.
  2. Choose the number of Portal seats. Each paid seat represents one Portal user and contributes 1,000 pooled Copilot drafts per monthly allowance.
  3. Continue to checkout. Do not close the provider window until payment succeeds or is cancelled.
  4. Return to Console and allow the synchronization state to finish. If checkout failed, retry from the failure card; do not create another workspace to work around the state.

Navigation, theme, and account settings

The sidebar groups workspace operations, Portal configuration, and settings. Open Portal opens the operational app for the same workspace. Documentation opens this manual. The bottom theme control changes the Console color scheme.

User settings let you update display name, confirm a new email address, and change your password. Passwords must contain at least eight characters. Account or workspace deletion is a destructive workflow; review the confirmation carefully. If a deletion control is marked preview, it does not perform a production deletion.

Access model and responsibilities

Person or systemWhere access is grantedTypical responsibility
Workspace ownerWorkspace ownershipSubscription, destructive changes, security ownership, and final administrative control.
Console teammateTeam pageConfigures agents, channels, routing, tools, notifications, and other Console settings.
Portal Human AgentHuman Agents pageWorks conversations, email, CRM, tasks, and approved Portal Action Tools.
Portal Human Agent with Admin enabledHuman Agent invite or editAlso manages the Portal workspace and Portal membership.
AI Agent or DispatcherAI Agent roster slotHandles or routes customer conversations according to saved configuration.
External integrationAgent key, Action Tool credential, provider credential, or webhook secretPerforms only the API operations allowed by that credential.

One person can be both a Console teammate and a Portal Human Agent, but the memberships remain independent. When someone changes roles, update both records deliberately. Removing Portal access does not remove Console access, and removing Console access does not end an active Portal shift.

Recommended first-hour setup

  1. Create or open the production workspace and confirm its name in the sidebar before entering credentials.
  2. Activate the Portal subscription or trial and choose the initial seat count. Invite one test Human Agent and create the categories that routing will use.
  3. Create an AI Agent around a real customer journey. Connect the knowledge and actions it needs to answer questions, complete multi-step work, and hand off only when human authority is required. Save setup and test the full journey in the private chat preview.
  4. Connect one channel or email provider, route it to the test target, and exchange real inbound and outbound messages.
  5. Configure Copilot access for the test agent, add a short workspace guidance rule, and verify that the Portal control appears only for that person.
  6. Add Console teammates only after the workspace has an owner-controlled credential and billing process.
  7. Configure notifications and webhooks last, after the actions that produce those alerts and events are working.

Example: production and test workspaces

A company named Northwind can keep Northwind Production for real channels and Northwind Test for provider test identities. Each workspace has separate agents, routing, keys, webhook secret, Copilot pool, and billing state. Configuration is not inherited between them.

When copying a configuration manually, replace every workspace-specific identifier and secret. A production Agent API key, webhook signing secret, address route, or channel assignment must never be reused in the test workspace.

Home and workspace analytics

Read workspace activity without confusing activity summaries with billable balances.

Workspace overview

Home summarizes message activity, AI credit consumption, audience geography, countries, and channels. Use the date controls before comparing totals: a date-range change can alter activity cards while billing balances remain tied to the billing cycle.

  • Message activity shows traffic over the selected period.
  • AI credits shows consumed or remaining agent credits.
  • Audience geography maps locations derived from available contact or channel information; unknown locations remain unclassified.
  • Country volume shows messages, conversations, and share. It displays ten rows per page and uses pagination instead of an inner scroll.
  • Channel volume compares connected channels. Channels are a bounded list and do not paginate.

How to investigate a traffic change

  1. Set the intended date range and timezone first. Record them when sharing a screenshot or export.
  2. Check Message activity to determine whether the change affects total traffic or only one subset.
  3. Compare Channel volume. A rise isolated to Email or WhatsApp usually points to a channel campaign, incident, or routing change rather than global growth.
  4. Open Country volume and page through results when location is relevant. Treat unknown locations as missing attribution, not a new country.
  5. Compare AI credit consumption with message volume. Credits can rise faster than messages when more conversations use Advanced reasoning or action-heavy agents.
  6. Open the relevant Agent or Dispatcher Insights page for the same range to identify the responsible agent, action, hour, or destination.

Example: messages rise but conversations do not

If Email messages increase from 800 to 1,600 while conversations remain near 400, the average messages per conversation doubled. Investigate replies, automated acknowledgements, reopened tickets, and repeated delivery failures before assuming customer acquisition doubled. If AI credits also doubled, inspect whether the same conversations were reassigned to an AI Agent or changed to a higher intelligence level.

AI Agents

Create specialist agents, supply reliable knowledge, inspect performance, and deploy them through supported surfaces.

Agent roster and capacity

The roster separates active slots from inactive agents. A slot can hold either an AI Agent or an AI Dispatcher. Empty slots are usable capacity; locked rows require a plan or add-on upgrade.

  1. Choose an empty slot or Add agent and select AI Agent.
  2. Enter a name, choose a starting role template, review the generated role, and write the opening greeting.
  3. Create the agent, then complete its setup and knowledge before exposing it to customers.
  4. To reactivate an inactive agent, drag it into an eligible empty active slot. To add capacity, choose Add agent slot and complete the add-on flow.

Deleting an agent is different from making it inactive. Deletion removes the configured agent after confirmation; use inactivity when you may need the configuration later.

Agent setup

Agent setup controls judgment, voice, opening behavior, and Portal email eligibility. Save changes before leaving the page; the chat preview is for validation and is not a substitute for a real channel test.

SettingWhat it controlsGuidance
NameConsole, routing, and preview identityUse a durable role name; edit with the pencil control.
Fast1× credit reasoning modeBest for short, low-complexity replies.
Smart1× balanced reasoning modeDefault for most support and sales conversations.
Advanced reasoning2× credit deep reasoningUse for complex requests where latency and higher credit use are acceptable.
Portal email assignmentWhether teammates may assign email tickets to this agentEnable only after its email knowledge and handoff behavior are tested.
Role templateStarting instruction structureChoose General, Customer Support, Sales, or Custom, then edit the resulting role.
RoleHow the agent behaves and answersState duties, limits, escalation rules, tone, and prohibited commitments.
GreetingFirst message in a new conversationKeep it short and avoid promising unsupported capabilities.

The right-side chat supports New chat and lets you test the saved agent. Its fixed desktop version keeps the button at the header edge; the retractable version reserves space for its close control.

Chat logs

Chat logs list stored agent conversations. Filter by date or source, open a conversation to inspect messages and metadata, and load older rows when offered. Deleting a conversation requires confirmation and removes that stored log; it does not retract messages already delivered to an external channel.

Agent insights and reports

Select a date range, timezone, or preset and choose Generate report. The report can include message trend, channel distribution, retention, action usage, geography, peak hours, weekdays, and months. A returning user is measured across at least two distinct eight-hour activity windows. Download the HTML report when you need a portable snapshot.

Country tables use ten-row pages so wheel scrolling remains attached to the page. Channel volume does not paginate because the supported channel set is bounded.

Design agents around complete customer journeys

A Mitsolab AI Agent can combine knowledge, customer context, conversation history, and Action Tools to handle multiple related tasks from the first question through resolution. Define the outcomes it should achieve and the decisions that require human authority; do not split a connected customer journey into separate agents simply because it contains several steps.

Agent designCan handleUse another agent when
Customer experience agentProduct questions, plan guidance, qualification, account lookups, order updates, troubleshooting, follow-ups, and routingA separate brand, department, language operation, permission boundary, or workflow needs independent configuration
Commerce agentProduct discovery, availability checks, order status, customer-data collection, post-purchase support, and escalationAnother business unit requires different knowledge, tools, policies, or ownership
Service agentFAQs, guided diagnosis, account context, Action Tool execution, case updates, summaries, and human handoffA regulated or high-authority process must be isolated from the broader service journey

Start with the fewest agents that match your actual operating model. Expand the roster when separation improves ownership, access control, reporting, routing, or customer experience.

Write a production role

The role should establish identity, objectives, source hierarchy, limits, escalation, and response style. Put frequently changing facts in knowledge rather than the role.

Role example
You are Northwind's subscription support agent.

Objectives:
- Explain current plans using Product and Dynamic Source records.
- Ask at most one clarifying question when the customer's requirement is ambiguous.
- Recommend a plan only when its documented limits satisfy the requirement.

Boundaries:
- Never invent discounts, renewal dates, account balances, or roadmap commitments.
- Do not request payment-card details in chat.
- Hand off refund decisions and contract negotiations to Billing Support.

Response style:
- Answer in the dominant meaningful language of the recent conversation.
- Lead with the direct answer, then provide the minimum supporting detail.

Test every stated boundary. A role that says “never invent discounts” is incomplete unless the test set includes a customer asking for an undocumented discount.

Choose and validate intelligence level

LevelUse it whenTest before choosing
FastIntent is obvious and replies mostly retrieve one factShort FAQ, simple status response, one-field collection
SmartReplies combine context, policy, and normal judgmentAmbiguous plan question, several knowledge sources, routine action decision
Advanced reasoningThe request has competing constraints or a complex multi-step decisionLong exception policy, several dependent calculations, complex tool sequence

Run the same representative conversations at candidate levels. Compare factual accuracy, tool choice, latency, and credits—not writing style alone. Advanced reasoning costs 2× credits, so use it only when the measured result justifies it.

Pre-launch test matrix

TestExpected result
Direct supported questionAnswers from the intended source and does not add unsupported claims.
Paraphrased questionFinds the same answer despite different wording.
Missing factStates the limitation or asks for needed context instead of inventing.
Conflicting sourcesUses the maintained authoritative source or escalates; conflict is then removed.
Forbidden requestFollows the role boundary and offers the approved next step.
Action should runSelects the correct action once with valid fields.
Action must not runDoes not call an external system speculatively.
Human handoffRoutes with a concise summary and collected required fields.
Language changeFollows meaningful recent conversation language rather than a stray character or greeting.

Use Chat logs for diagnosis

Filter by date and source to reduce the conversation list, then open Chat for message order and Details for source, country, message count, start, and last activity. Search is useful for a known phrase or customer label. Load More requests the next result page; it does not broaden the current filters.

When reporting a bad answer, preserve the conversation long enough to capture the agent, timestamp, user request, answer, source, and expected behavior. Deletion is permanent for the Console log and removes the evidence needed to reproduce the issue.

Interpret Agent Insights

  • Message trend answers when volume changed.
  • Channels answers where conversations entered.
  • Retention distinguishes one activity window from users returning in another eight-hour window.
  • Action usage shows whether external operations are being selected and completed at the expected rate.
  • Countries reveals attributed geography, with unknown values remaining unknown.
  • Hours, weekdays, and months support staffing and maintenance scheduling in the selected timezone.

Generate reports with identical date and timezone settings before comparing agents. Downloaded HTML captures that generated state; regenerating later can produce different totals as delayed provider data arrives.

Agent knowledge

Give agents maintained, scoped sources instead of placing changing facts in the role prompt.

FAQs

An FAQ contains a title, up to five example questions, and one authoritative answer. Example questions help match user phrasing; they are not separate answers.

  1. Choose Create FAQ.
  2. Write a recognizable title and add realistic variants of the customer question.
  3. Write the complete answer, including conditions and escalation boundaries.
  4. Save, test from the agent chat, and use the item menu to edit or delete it later.

Notes

Notes store internal context, policies, and details the agent should remember while replying. Use one subject per note, give it a descriptive title, and revise it when policy changes. Create, edit, and delete from the item menu. Never store passwords, private API keys, or payment-card data in a note.

Products

Product records give the agent consistent commercial facts. Each record supports a title, description, price, currency, and billing period: single, daily, weekly, monthly, quarterly, or yearly. Keep variations as separate records when their price or entitlement differs.

Documents

Upload a supported document and keep the page open while Console parses and chunks it. Progress indicates ingestion, not answer quality. After processing, ask several questions whose answers occur in different parts of the file. Delete obsolete documents so the agent cannot cite conflicting versions.

Notion

Choose Connect Notion, authorize the intended Notion workspace, select pages that the integration can access, and import them. Imported pages become agent knowledge snapshots; verify them after material Notion edits. Removing an imported page stops using that source. Disconnecting removes the Console connection and requires a new OAuth authorization to import again.

Dynamic Sources

Dynamic Sources are searchable structured tables. Use them for catalogs, plans, locations, stock, eligibility, or other records that are better filtered than read as prose.

  1. Create or name the table and define columns. Supported column types include text, integer, float, boolean, and date.
  2. For each column, decide whether the agent may filter or sort by it. Do not expose internal-only fields to agent search.
  3. Insert rows manually or import CSV. Match CSV headers and data types before importing.
  4. Enable the source only after checking representative searches with column, condition, and value filters.
  5. Use Export CSV for review or backup. Column deletion and row deletion are destructive.

The table toolbar can search rows, choose a filter column, apply conditions such as Contains, and combine values. The enabled switch controls whether the agent can use the table; it does not delete data.

Choose the correct knowledge source

InformationBest sourceReason
One common question with one approved answerFAQExample phrasings improve retrieval around a single response.
Internal policy or operating contextNoteFree-form maintained context without customer-question framing.
Named offer with price and billing intervalProductDedicated commercial fields reduce ambiguity.
Long handbook, policy, or specificationDocumentParsing and chunking make sections independently retrievable.
Team-maintained Notion pageNotionImports the selected authorized page without copying it manually.
Many structured records that need filtersDynamic SourceTyped columns support exact search, filtering, sorting, and CSV maintenance.

FAQ example and quality check

FAQ record
Title: Refund eligibility

Example questions:
- Can I get a refund?
- I was charged by mistake. What should I do?
- Is my annual plan refundable?

Answer:
Purchases are reviewed under the refund policy that applied on the payment date. Do not promise approval. Collect the invoice email and payment date, then route the request to Billing Support for a decision. Never request full card details.

Use distinct, meaningful examples. Five near-identical questions add less retrieval value than three realistic variations. The answer must stand alone because the agent can retrieve it without surrounding notes.

Note and Product examples

Note example
Note title: Delivery promise policy
Content: Agents may repeat a delivery estimate returned by the shipping provider, but must call it an estimate. Never promise an arrival date unless an approved guaranteed service is present in the order data.
Product example
Product: Growth Plan
Description: For teams that need two active AI Agent or Dispatcher slots and the plan's current included message-credit allowance.
Price: 149
Currency: USD
Period: Monthly

Do not put several plan prices in one Product description. Separate records allow the matching plan to be retrieved without bringing unrelated prices into context.

Document ingestion lifecycle

  1. Remove draft comments, duplicated appendices, hidden credentials, and obsolete versions before upload.
  2. Use a descriptive filename that includes the subject and effective version.
  3. Upload and wait through parse and chunk progress. A completed progress indicator means the content was processed, not that every answer is correct.
  4. Test a fact near the beginning, middle, and end; a table value; a negative rule; and a question the document does not answer.
  5. When publishing a replacement, upload it, validate it, and then delete the obsolete document so the agent cannot retrieve both.

Notion authorization and refresh

The Notion authorization determines which pages Mitsolab can see. If a page is missing, first check that the integration was granted access to that page or its parent in Notion, then reconnect or reload the selection. Import only the pages needed by the agent.

After a significant page edit, verify that the imported source reflects it before relying on the new fact. Removing one imported page is narrower than disconnecting Notion; disconnect only when the workspace should no longer authorize the integration.

Dynamic Source example

ColumnTypeAgent permission
nameTextSearch/filter
countryTextFilter
monthly_priceFloatFilter/sort
annual_priceFloatFilter/sort
activeBooleanFilter
effective_dateDateFilter/sort
internal_marginFloatDo not expose
CSV import example
name,country,monthly_price,annual_price,active,effective_date,internal_margin
Hobby,JO,39,390,true,2026-08-01,0.42
Growth,JO,149,1490,true,2026-08-01,0.48
Legacy Starter,JO,29,290,false,2024-01-01,0.31

With filtering enabled, the agent can request active records for Jordan and sort by monthly price. It cannot query internal_margin. Import validation should reject a nonnumeric price or malformed date instead of silently treating it as text.

Resolve conflicts and training state

If two sources disagree, edit or remove the obsolete one rather than relying on prompt wording to override it. Check the Console training or data status after source changes. A “training in progress” state means testing can still hit the previous state; a failed state requires correcting or retrying the source before launch.

AI Agent actions

Let an AI Agent perform narrowly defined external operations while keeping credentials out of prompts.

Custom API

A Custom API action tells one AI Agent when and how to call an endpoint. Configure a title, a precise “when to use” description, endpoint URL, method (GET, POST, PUT, PATCH, or DELETE), secret headers, and JSON fields. Fields can be text, integer, boolean, float, array, required, or nested.

  1. Describe the business condition that permits the call.
  2. Use an HTTPS endpoint and the least-privileged credential possible.
  3. Model only required inputs; give every field a clear semantic name.
  4. Test success and validation failures using non-production data.
  5. Save and run a chat test that should use the action and one that must not use it.

Custom Button

A Custom Button appears when the configured condition is met and sends the user to a URL. Set the appearance condition, redirect URL, button label or message, and colors, then check the live preview. The destination must be safe for end users; do not put secrets or untrusted raw values in a query string.

Zapier, Make.com, and Slack

Zapier and Make.com actions post to a platform webhook. Create the receiving workflow first, paste its webhook URL, define when the agent may call it, and map optional headers and body fields. Test in the automation platform before enabling customer traffic.

Slack uses a Slack Incoming Webhook. Configure when to notify, a concise message template, and optional username. Treat the webhook URL as a secret and rotate it from Slack if exposed.

Custom API example: check an order

FieldConfiguration
TitleCheck order status
When to useUse only after the customer asks about an existing order and provides an order number.
MethodGET
URLhttps://operations.example.com/orders/{{order_id}}
HeaderAuthorization: Bearer <server token>
order_idRequired text field; customer supplied
Expected endpoint response
{
  "order_id": "NW-10482",
  "status": "in_transit",
  "estimated_delivery": "2026-08-16",
  "carrier": "DHL"
}

The “when to use” rule prevents the agent from calling the endpoint for a general shipping-policy question. The endpoint must still authenticate, validate the order ID, and ensure the credential can access only the intended tenant.

Body fields, types, and nesting

Use text for identifiers even when they contain only digits; integer and float for values that must be numeric; boolean for true/false behavior; and arrays for repeated values. Required means the action cannot run without the value. A nested child belongs under its parent object rather than being sent at the top level.

Nested request example
{
  "customer": {
    "name": "Amina Saleh",
    "email": "amina@example.com"
  },
  "items": ["SKU-104", "SKU-220"],
  "expedited": false
}

Test the serialized request at the receiver. A value that visually looks correct in Console can still be wrong when a boolean is quoted as text or an array is sent as one comma-separated string.

Custom Button example

A “Track shipment” button can appear after an order number is known and redirect to https://tracking.example.com/order/NW-10482. Configure the customer-visible label, supporting message, button color, and readable text color. Test keyboard focus, mobile width, missing order IDs, and an expired tracking link.

Do not put an authorization token, internal contact ID, or unrestricted redirect destination in the URL. Prefer a short-lived, server-generated public reference.

Zapier and Make.com example

  1. Create a Catch Hook or Custom Webhook trigger in the automation platform and copy its unique HTTPS URL.
  2. In Console, describe the exact event that permits the agent to send it, such as an explicitly confirmed demo request.
  3. Add the minimum body fields: customer name, business email, requested date, and source conversation reference.
  4. Run a Console test while the automation platform is listening, then map the captured fields into the next step.
  5. Add validation, deduplication, and error notification in the automation before enabling real conversations.
Automation payload
{
  "event": "demo_requested",
  "customer_name": "Amina Saleh",
  "business_email": "amina@northwind.example",
  "requested_date": "2026-08-18"
}

Slack action example

Create a dedicated Slack Incoming Webhook for the destination channel. A useful message template identifies the customer, request, urgency, and conversation link without pasting the entire transcript. Set the condition to a meaningful escalation such as “customer confirms a production outage,” not a broad keyword such as “problem.”

Slack message template
Production escalation
Customer: {{customer_name}}
Summary: {{issue_summary}}
Urgency: {{urgency}}
Conversation: {{conversation_url}}

Diagnose an agent action

  • Confirm the saved agent is the one running the conversation.
  • Check that the “when to use” description matches the user's intent and that required fields were collected.
  • Inspect method, final HTTPS URL, header name, JSON types, and nested structure.
  • Test the credential outside Console from a secure server environment.
  • Return a small valid JSON response and a meaningful non-2xx error; avoid HTML error pages.
  • After changing the action, start a new test conversation so previous tool context does not obscure the result.

Deploy an AI Agent

Choose a supported delivery surface, keep credentials private, and validate the production behavior.

Agent API

Generate an Agent API key from the agent's API page. The complete key is shown once. Copy it into a server-side secret manager, never browser code. Revoke a key immediately if it is exposed; generated clients using it will stop working.

Server-side request
curl -X POST https://app.mitsolab.com/api/v1/chat \
  -H "Authorization: Bearer ml_cva_REPLACE_WITH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "AGENT_UUID",
    "message": "Do you offer annual plans?",
    "anon_id": "stable-anonymous-user-id",
    "chat_id": "optional-existing-chat-id"
  }'

agent_id and message identify the agent and new user input. Preserve anon_id for the same anonymous person. Supply chat_id to continue an existing conversation when available.

Web Embed

Enable the embed, select primary and text colors, set the displayed agent name, and use the live preview. Console provides a loader snippet and an iframe snippet; install one, not both.

Loader example
<script
  src="https://app.mitsolab.com/widget.js"
  data-agent="AGENT_UUID"
  data-theme="dark"
  data-text="#ffffff"
  data-agent-name="Support"
  data-position="bottom-right"
  data-offset-x="24"
  data-offset-y="24"
  data-z-index="9999"
  data-panel-width="390"
  data-panel-height="640"
  data-mobile-breakpoint="720"
  data-mobile-behavior="fullscreen"
  data-header-selector="header">
</script>

data-agent is required. Theme, text color, display name, position, offsets, stacking order, panel dimensions, mobile breakpoint or behavior, and header selector are optional. Test cookie restrictions, CSP, mobile keyboard behavior, and overlap with your site's chat or consent controls.

Hosted chat page

Enable the page, set display name, icon, headline, input placeholder, default theme, and light/dark surface, accent, and text colors. Copy the public URL only after testing it in a private browser window. Optional password protection restricts casual access but should not be treated as user identity.

A custom-domain add-on lets the hosted page use your domain. Follow the DNS values shown in Console, wait for DNS propagation, and verify before distributing the URL. The add-on and renewal state are managed from Billing.

Agent API key lifecycle

  1. Generate a key only when the server integration is ready to store it. The full value is displayed once.
  2. Store it as a deployment secret such as MITSOLAB_AGENT_API_KEY; never commit it or return it to a browser.
  3. Use separate keys for independent services when the page permits, so one service can be revoked without stopping another.
  4. Log request correlation IDs and response status, but redact Authorization and customer message content where it is not needed.
  5. On exposure, revoke first, generate a replacement, update the server, and run a new-conversation and continuation test.

Maintain conversation identity

Use a stable anon_id for the same anonymous visitor so abuse controls and conversation behavior do not treat every request as a new person. Preserve the returned or established chat_id for a continuing conversation. Creating a random identifier for every message discards continuity and can multiply stored chats.

Server integration pattern
const response = await fetch("https://app.mitsolab.com/api/v1/chat", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.MITSOLAB_AGENT_API_KEY,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    agent_id: process.env.MITSOLAB_AGENT_ID,
    message: userMessage,
    anon_id: visitor.stableId,
    chat_id: conversation.mitsolabChatId || undefined
  })
});

if (!response.ok) throw new Error("Mitsolab request failed: " + response.status);
const result = await response.json();

Install and test Web Embed

  1. Configure colors and display name in Console, then copy the generated loader or iframe—not both.
  2. Place the loader once near the end of <body>. In a single-page application, do not inject it again on every route change.
  3. If the launcher overlaps another fixed control, change position or offsets. Use z-index only as high as needed.
  4. Set panel width and height for desktop and select fullscreen mobile behavior when a narrow floating panel would be unusable.
  5. If your site has a fixed header, set the header selector so fullscreen behavior can account for it.
  6. Test anonymous continuity, new chat, scrolling, the mobile keyboard, color contrast, Content Security Policy, and a failed network request.

For CSP, allow the exact script, frame, connection, and asset origins used by the generated snippet. Do not use a wildcard policy merely to make the widget load.

Launch a hosted chat page

Write a headline that explains the agent's purpose, not a generic welcome. The input placeholder should suggest a real request. Configure both light and dark palettes even when one is the default because visitors can arrive with different preferences.

  1. Enable the page and open the copied URL in a signed-out private browser.
  2. Validate icon, name, headline, composer, first greeting, and contrast in both themes.
  3. If password protection is enabled, test incorrect and correct passwords; use it only as shared access protection, not individual identity.
  4. For a custom domain, add exactly the DNS records shown, wait for public propagation, and use Console verification before publishing it.
  5. Keep the original hosted URL available during DNS rollout so support can distinguish DNS failure from agent failure.

AI Dispatcher

Route new conversations to the right human category or AI specialist after collecting only the required context.

Configure routing and intake

  1. Create an AI Dispatcher from an available agent slot and give it a customer-facing name.
  2. Enable the Human Agent Categories that are valid routing destinations. Disabled categories cannot be selected by this dispatcher.
  3. Enable AI handoff if the dispatcher may route to configured AI specialists.
  4. Select required standard intake fields. Add up to five custom text fields only when the answer materially changes routing.
  5. Write custom instructions that define routing precedence, ambiguity handling, unavailable destinations, and when to ask a follow-up question.
  6. Save, then test every target, an ambiguous request, missing information, and a request with no valid target.

The summary panel shows enabled human targets, AI handoff state, required fields, and custom-field usage. On medium screens section descriptions move above their controls; on small screens the summary also joins the single content column. This responsive change does not alter routing behavior.

Activity and insights

Activity uses the same conversation log workflow as an AI Agent, scoped to conversations handled by the selected Dispatcher. Filter by date, source, or search text; open Chat or Details; load older results; and delete a non-email conversation only after confirmation.

Insights is also scoped to the selected Dispatcher. Choose a preset or custom date range and timezone, generate the report, review message trend, channels, retention, action usage, geography, and time distributions, and download the HTML snapshot. Use these views to confirm that routing volume and destinations match the Dispatcher's configuration.

Design routing before configuring it

Write the destination matrix first. A category should represent a team that can actually receive the conversation, and every route needs a fallback for missing or ambiguous information.

Customer intentRequired informationDestinationFallback
New purchaseProduct family and countrySalesGeneral Support
Existing technical issueProduct and short problem descriptionTechnicalSupport
Invoice or paymentInvoice email or referenceBilling SupportSupport
Known specialist questionSpecialist topicMatching AI AgentHuman category

Enable only destinations present in the matrix. A large undifferentiated list makes routing harder to explain and test.

Standard and custom intake fields

Required standard fields use known workspace/contact fields. Custom fields are free-text questions unique to this Dispatcher, with a maximum of five. Ask only for information that changes the destination or lets the receiving team begin work.

Good fieldWhyAvoid
Which product is this about?Changes specialist destinationTell us everything about your issue
What country is the account registered in?Can change sales or compliance routeFull home address before it is needed
What error appears?Gives Technical a usable summaryUpload all system logs immediately

Order questions from easy to sensitive. If the user already supplied an answer, the Dispatcher should use it rather than asking again.

Dispatcher instruction example

Routing instructions
Route by the customer's primary requested outcome.

- New purchases and plan comparisons go to Sales.
- Existing-account product failures go to Technical after collecting product and error summary.
- Billing, invoices, and payment questions go to Billing Support.
- If the request clearly matches an enabled AI specialist, use that specialist.
- If two destinations are equally likely, ask one short clarifying question.
- Never claim that a team is online or promise a response time.
- If no valid target exists, route to Support with a one-sentence summary.

Dispatcher acceptance tests

ScenarioPass condition
Clear Sales requestRoutes immediately without unrelated questions.
Clear Technical request missing productCollects product, then routes Technical.
Ambiguous 'I need help'Asks one useful clarification rather than guessing.
Disabled target mentionedUses configured fallback and does not route to the disabled category.
AI handoff offNever selects an AI specialist even when one exists.
AI handoff onSelects only an appropriate active AI specialist.
All five custom fields configuredAsks only fields relevant to the selected path, not all five mechanically.

Messaging channels

Connect provider-owned identities and choose where each incoming conversation is routed.

Connection and routing rules

Channel setup has two concerns: provider authorization and Mitsolab routing. A successful provider connection does not guarantee the desired owner; confirm the routing target after connecting and after replacing credentials.

Choose an AI Agent or AI Dispatcher for automated handling, or Portal — Manual Dispatching to place new work into the Portal flow without assigning it to an AI. Change routing updates future ownership without recreating the provider connection.

Disconnecting stops Mitsolab from using that connection. It does not normally delete the account, page, bot, or provider data itself. Confirm disconnect prompts and record any provider-side cleanup that remains.

WhatsApp

  1. Choose WhatsApp and start the provider authorization flow.
  2. Authorize the intended business portfolio and return to Console.
  3. Refresh available phone numbers if the expected number is missing.
  4. Connect the correct number and choose its AI Agent, Dispatcher, or supported routing target.
  5. Send and receive a real test message before publishing the number.

Disconnecting a number removes its Mitsolab route; reconnect it explicitly if authorization is restored later.

Instagram and Messenger

Both use the Meta authorization flow. Connect Meta, load Facebook Pages, and inspect linked Instagram professional accounts. Connect Messenger for the intended Page and Instagram for the intended linked professional account independently. A Page connection does not automatically enable both products. Use Disconnect Meta only when you intend to invalidate the shared authorization.

Telegram

  1. Create a bot with Telegram's BotFather and copy its token.
  2. Paste the bot token into Console and choose a route.
  3. If approval mode is enabled, review pending users and choose Allow or Reject.
  4. Remove an approved user to require approval again, or disconnect the bot to stop the integration.

LINE

  1. Create or open a LINE Messaging API channel and copy the Channel ID and Channel secret.
  2. Enter both values in Console and connect.
  3. Copy the Mitsolab webhook URL into the LINE Messaging API webhook setting and enable webhook delivery in LINE.
  4. Choose routing and configure approval mode if needed.
  5. Review pending identities with Allow or Reject; remove approvals or disconnect when access must end.

Use the Console change-routing control when ownership changes; do not create a duplicate LINE provider channel merely to change the target.

Prepare provider ownership and permissions

ChannelPrepare before Console
WhatsAppMeta business access, the intended WhatsApp Business Account, and permission to manage its phone number.
MessengerAdmin or required task access to the intended Facebook Page.
InstagramA professional Instagram account linked to the intended Facebook Page and appropriate Meta access.
TelegramA bot created with BotFather and its current bot token.
LINEA LINE Developers provider, Messaging API channel, Channel ID, and Channel secret.

Use organization-owned provider accounts. A connection authorized through one employee's temporary personal access is harder to recover and audit.

Example: change a channel from AI to Portal

  1. Open Channels and identify the exact page, bot, number, or LINE channel by provider label—not icon alone.
  2. Choose Change routing and select Portal — Manual Dispatching.
  3. Confirm the modal. Do not disconnect the provider; disconnecting is unnecessary for a route change.
  4. Send a new inbound test message. Existing open conversations can retain their current ownership; validate with a new conversation.
  5. Confirm that the message reaches the expected Portal queue and that a Human Agent can reply through the same provider identity.

WhatsApp connection details

The Meta authorization can return multiple business accounts and numbers. Refresh after authorization, then select by verified name and display number. Connecting the wrong number can route production customer traffic even when the label looks similar.

  • Send an inbound customer message from a separate phone.
  • Reply from the assigned AI or Portal route.
  • Test a reply after the customer-service window has ended so the team understands template/window behavior.
  • When replacing authorization, reconnect and verify the number before removing the old path.

Messenger and Instagram details

Console lists Facebook Pages returned by Meta and, for Instagram, the linked professional account. Connect each surface independently. If Instagram is absent, verify in Meta that the Instagram professional account is linked to the Page and that the authorizing user granted the required assets.

A Meta disconnect affects the shared authorization and can stop both Messenger and Instagram connections. Use the individual Page disconnect when retiring only one channel.

Telegram approval mode

Approval mode is useful when a bot should not accept every Telegram identity automatically. A first contact appears as pending. Allow grants access; Reject declines it. Removing an approved identity returns it to a state that requires approval on a future interaction.

Test one approved and one unapproved account. If no messages arrive, verify the token is current, the bot was started by the user, and another service is not consuming the bot's webhook or updates.

LINE webhook and approval details

  1. Enter the Channel ID and secret from the same LINE Messaging API channel.
  2. After Console connects it, copy the displayed Mitsolab webhook URL exactly into LINE Developers.
  3. Enable webhook use in LINE and run LINE's webhook verification where available.
  4. Disable any conflicting provider auto-response that would send a second reply.
  5. Set routing and approval behavior, then message the Official Account from a separate LINE identity.

A successful credential connection without the LINE-side webhook produces no inbound events. A verified webhook with the wrong Channel secret produces rejected requests.

Disconnect and credential rotation

Before disconnecting, record the provider asset and intended replacement route. Disconnect stops Mitsolab handling and removes or disables the local assignment; it does not delete the Facebook Page, WhatsApp number, Telegram bot, LINE channel, or provider business account.

For a leaked Telegram token or LINE secret, rotate at the provider, update Console, and test. For Meta, reauthorize with the correct business user and permissions. Do not assume changing a Console route rotates a provider credential.

Human Agents

Control who can work in Portal and organize people into routing categories.

Categories

Categories represent teams, queues, or responsibilities such as Sales, Support, and Technical. AI Dispatchers, email routing, Copilot permissions, and Portal workflows can refer to them, so use stable operational names.

  1. Choose Create category, enter a unique name, and save.
  2. Use a category's edit control to rename it after checking downstream routing.
  3. Before deletion, move or update affected agents and routes. Deleting a category sets members that used it to no category.

Invite and manage a Portal agent

  1. Choose Invite Human Agent and enter the person's email address.
  2. Select a category or leave it unassigned when your workflow permits.
  3. Enable Admin only if the person should manage the Portal workspace and members.
  4. Send the invitation. The recipient follows the link to portal.mitsolab.com to finish the Portal account and workspace access.
  5. Use search and filters to find an existing agent. Edit category or admin access, or remove workspace access after confirmation.

A Human Agent is a Portal operator. A Console teammate can administer Console. These are separate permissions; inviting someone to one does not silently grant the other.

Design categories that routing can use

A category should answer “which group can own this work?” Good categories are stable and mutually understandable: Sales, Billing, Technical Support. Avoid categories based on temporary campaigns, individual names, or vague seniority unless they are real queues.

ChangeCheck before saving
Rename categoryDispatcher destinations, email receive/send rules, Copilot allowed categories, and Portal operating language.
Delete categoryMove members, replace routes, and confirm fallback behavior; affected people become uncategorized.
Move agentConfirm new queue visibility, email permissions, Copilot rule, and shift expectations.

Invitation lifecycle

  1. Enter the exact work email and choose the person's initial category.
  2. Leave Admin off for ordinary operators. Admin is for people who should manage Portal membership and workspace administration.
  3. Send the invitation and ask the recipient to use the same email when completing Portal access.
  4. After acceptance, verify the person appears active, can start the intended shift or queue workflow, and cannot access restricted categories or tools.
  5. If the address was wrong or the invitation should no longer be used, cancel or replace it rather than inviting several variants.

The information strip in the invite modal explains that the invitation finishes at portal.mitsolab.com. The Portal account and workspace access are completed there; the Console modal does not set a password for the recipient.

Edit, suspend, and remove access

Use Edit for category and Admin changes. After a category change, ask the agent to reload Portal so queue and Copilot capability are refreshed. Remove workspace access when the person no longer works in the workspace; confirm first because active assignments may need reassignment.

Before removing an agent, review open conversations, email tickets, tasks, category-specific Action Tools, and shift state. Console access, if the same person has it, must be removed separately from Team.

Shared email

Connect a delivery provider, verify domains, create addresses, and route received and sent mail independently.

Provider connections and domains

Email supports provider-backed connections including Resend, Mailgun, SendGrid, Amazon SES, and Postmark. The form changes by provider and may request API credentials, region, server token, signing data, or return-path information.

  1. Choose Add account, select the provider, enter a recognizable connection label, and supply the requested provider credentials.
  2. Save and let Console validate the credentials. Correct authentication or region errors before continuing.
  3. Synchronize domains. For a new domain, add it at the provider and create the exact DNS records the provider returns.
  4. Refresh verification after DNS propagation. A verified sending domain can still require a separate receiving or inbound-webhook setup.
  5. Copy or register the Mitsolab inbound webhook where the provider requires it, then enable receiving and run the provider check.

Refreshing a connection re-reads provider state. Disconnecting removes its Mitsolab connection, synchronized domain records, addresses, and routes; it does not delete the provider account or domain itself.

Addresses and routing

The Addresses & routing tab owns exact mailbox addresses and the fallback for all other addresses. When an add or edit form is open, the page hides the header actions and address list so you can complete one route without editing the wrong row. Save or Cancel returns to the list.

  1. Choose Add address, select a verified domain, enter the local part, and set the display name used when sending.
  2. Choose one handler: Portal workflow, AI Agent, or AI Dispatcher.
  3. For Portal workflow, configure who may receive conversations and who may send from the address independently: all human agents, categories, specific agents, or nobody.
  4. For an AI Agent, choose the exclusive owning agent and configure permitted human handoff where offered.
  5. For an AI Dispatcher, choose the dispatcher so its intake and routing rules decide the destination.
  6. Save, then test one inbound and one outbound message. Use All other addresses to configure unmatched local parts explicitly.

Edit changes future routing. Delete removes the Mitsolab address route after confirmation; provider-side aliases or domains can remain.

Delivery events

Provider callbacks update message delivery. A provider reports bounces or spam complaints to its registered Mitsolab webhook; Mitsolab normalizes those callbacks into Email.message.bounced and Email.message.complained workspace events when you subscribe to them.

Choose and prepare an email provider

ProviderCommon Console inputsProvider-side work
ResendAPI key and connection labelDomain creation, DNS verification, inbound webhook, receiving domain state.
MailgunAPI key, region, and domain detailsRegional API selection, DNS, routes/webhooks, signing settings.
SendGridAPI key and connection labelSender authentication, inbound parse/webhook configuration.
Amazon SESRegion and access credentialsVerified identity, DNS, receiving rules where supported, IAM permissions.
PostmarkServer token and connection labelSender signature/domain, inbound stream/webhook configuration.

Create a provider credential limited to the necessary sending, domain, and event operations. The exact required fields shown in Console are authoritative because providers can change their credential model.

DNS verification procedure

  1. Add the sending domain at the provider and synchronize it into Console.
  2. Copy each DNS name, type, and value exactly. At DNS providers that automatically append the zone, do not duplicate the root domain.
  3. Keep existing SPF records in mind: a domain should not publish several independent SPF TXT records. Merge according to the provider's instructions.
  4. Wait for public DNS propagation, then refresh verification. Local browser cache does not control DNS verification.
  5. Confirm sending verification and receiving/webhook state separately.

Address routing scenarios

AddressHandlerReceiveSendWhy
support@mail.example.comPortal workflowSupport categoryAll human agentsSupport owns inbound; any trained operator may reply.
sales@mail.example.comAI DispatcherDispatcher decidesAccording to routed workflowDispatcher collects country and product before routing.
plans@mail.example.comAI AgentProduct AdvisorAssigned AI workflowOne specialist owns plan questions.
All other addressesPortal workflowAdmin categoryNobodyCatch unexpected local parts without allowing replies from them.

Receive and Send are independent. “Nobody” for Send is valid for an inbound-only address. “All other addresses” should be intentionally restricted because catch-all traffic can include typos and abuse.

Create, edit, and delete an address safely

  1. Open Addresses & routing and choose Add address. The list and page actions hide while the form is active.
  2. Select a verified domain, enter the local part and display name, then choose exactly one handler.
  3. Configure receive and send permissions or the specific AI/Dispatcher target.
  4. Save and wait for the list to return. Send inbound mail from an external account and reply through the intended owner.
  5. For an edit, change one dimension at a time and test a new message. Cancel returns without applying the form.
  6. Before deletion, move open tickets and confirm whether provider-side aliases or catch-all delivery will still send mail to the deleted local part.

Understand message delivery state

Email.message.send means Mitsolab submitted or initiated the outbound send. Provider acceptance, delivery, bounce, and complaint are later states. Store and correlate the provider message ID when building an external integration.

  • A bounce can indicate an invalid mailbox, policy rejection, or temporary delivery failure.
  • A complaint means the provider reported spam feedback and should trigger suppression or review.
  • A ticket can be resolved, reopened by later activity, and archived; those states are separate from message delivery.

Email diagnosis by direction

SymptomCheck in order
Cannot sendProvider credential → region/server → verified sending domain → exact address Send permission → provider response.
Cannot receivePublic MX/inbound setup → provider inbound webhook/route → receiving enabled → exact or fallback address → receive permission.
Can receive but reply from wrong identityExact address display name and Send rule → selected mailbox/domain → provider From authorization.
Bounce/complaint event missingProvider event webhook → provider message ID → subscribed workspace webhook event → receiver signature and response.

Portal Action Tools

Create secure, human-triggered API actions that run beside a Portal conversation.

How Action Tools work

Portal Action Tools let an authorized human agent call your HTTPS API without seeing stored credentials or leaving the conversation. The agent supplies visible inputs; Console can map hidden values from the signed-in agent or current contact; the result can be displayed back in Portal.

The examples library includes starting configurations for Slack notifications, Shopify draft orders, GitHub issues, Cal.com appointments, package tracking, and shipping-rate lookup. A template is editable configuration, not an external account connection.

Create or edit a tool

  1. Choose Create new tool or a template. Give it an action-oriented name and explain exactly when a Portal agent should use it.
  2. Choose GET or POST and enter the HTTPS endpoint.
  3. Add secret request headers such as Authorization. Header values are encrypted and never shown to Portal agents.
  4. Define variables as text, integer, float, or boolean. Set a clear label, required state, and whether the field is hidden from the agent.
  5. For hidden or prefilled values, map from agent context (name, email, user ID) or contact context (customer name, email, phone, external ID, country, or contact ID). Leave it agent-entered only when human judgment is required.
  6. For POST, write the JSON body and insert variable tokens at the correct types. Do not put quotes around a token that should resolve to a number or boolean.
  7. Configure response JSON paths and labels for the values Portal should show.
  8. Test with mock values. Verify expected success, authentication failure, validation failure, timeout, and a response missing an optional mapping.
  9. Grant access to all categories or selected categories, save, and confirm the tool from a permitted and non-permitted Portal account.
Request body example
{
  "order_id": "{{order_id}}",
  "customer_email": "{{contact_email}}",
  "expedited": {{expedited}}
}

Security model

  • Use a dedicated, least-privileged credential for each external system.
  • Validate authorization and every variable again at your API; hidden does not mean trusted.
  • Return only fields the Portal agent needs. Avoid full upstream payloads containing secrets or customer data.
  • Rotate exposed credentials and update the Action Tool header.
  • Use category access to reduce availability, not as the only authorization control on your endpoint.

Example: create a refund-review request

FieldValue
NameRequest refund review
DescriptionUse after the customer explicitly asks for a refund and the agent has confirmed the invoice reference and reason.
MethodPOST
Endpointhttps://operations.example.com/refund-reviews
AccessBilling and Support categories
VariableTypeSourceVisible
invoice_referenceText, requiredAgent enteredYes
reasonText, requiredAgent enteredYes
contact_idText, requiredContact IDNo
requester_emailText, requiredAgent emailNo
urgentBooleanAgent enteredYes
Tool request body
{
  "invoice_reference": "{{invoice_reference}}",
  "reason": "{{reason}}",
  "contact_id": "{{contact_id}}",
  "requested_by": "{{requester_email}}",
  "urgent": {{urgent}}
}

Choose context mappings

Agent context identifies the signed-in Portal operator: name, email, or user ID. Contact context identifies the customer in the open conversation: customer name, email, phone, external ID, country, or contact ID. A mapping is convenient but must be validated by your endpoint.

Use contact ID as a stable internal lookup when your service understands it. Use external ID only when it is meaningful to the receiving system. Do not send every available field “just in case.”

Map a useful response

Endpoint response
{
  "request": {
    "id": "RR-2048",
    "status": "queued",
    "review_url": "https://operations.example.com/reviews/RR-2048"
  }
}
Portal labelJSON path
Review IDrequest.id
Statusrequest.status
Open reviewrequest.review_url

Map only values the Human Agent needs for the next step. If an optional path is absent, the tool should still show the successful required result. Never map an access token or full diagnostic object into Portal.

Test success and failure behavior

  1. Provide realistic mock values for every variable and run the built-in test.
  2. Inspect the receiver's request to confirm headers, JSON types, and hidden mappings.
  3. Return a valid success response and confirm every response path renders with the intended label.
  4. Test missing required input, invalid input, 401/403, 404, validation 4xx, 5xx, malformed JSON, and timeout.
  5. Open Portal as a permitted category and run the tool in a non-production conversation.
  6. Open as a non-permitted agent and confirm the tool is absent, not merely disabled after opening.

Use templates correctly

A Slack template still needs your incoming webhook and message fields. Shopify needs shop-specific API access and draft-order permissions. GitHub needs repository and issue permissions. Cal.com needs the correct event type and availability inputs. Tracking and shipping templates need the selected carrier/provider credential and field mappings.

After applying a template, review every URL, header, variable, body token, response path, and category. Template defaults are examples, not proof that the external account is connected.

Copilot drafts

Allocate a pooled drafting allowance, define access, and add workspace-wide writing guidance.

Balance and analytics

Each paid Portal seat contributes 1,000 included drafts to a shared workspace pool. Any permitted human agent can consume the pool; allowances are not reserved per person. Purchased draft packs are consumed after included drafts and do not expire. Included drafts reset with the applicable billing cycle.

The page loads independently: balance and analytics can render before the human-agent permission list completes. Cards show available balance, generated today, activity over the last 30 days, failures or refunded attempts, average response time, and rate limits. The activity chart covers the recent 14 days; channel and per-agent summaries show where drafts were generated.

Enable and grant access

  1. Turn on Copilot enabled. Turning it off hides the draft control in Portal for the workspace.
  2. Check All human agents to allow every active Portal human agent. This disables the more specific controls below because they cannot narrow an all-agent rule.
  3. Leave it unchecked for selected access and choose allowed categories from the vertical checkbox list.
  4. Set optional per-agent radio overrides: Category rule, Always allow, or Block. An explicit block always takes priority.
  5. Write optional Workspace guidance and save. Guidance is limited to 1,200 characters.

Portal checks capability when Inbox opens and keeps it for the browser session. The Copilot control is not rendered for a disabled workspace or an unauthorized agent.

What a Portal agent experiences

  1. Open a conversation and choose the Copilot draft control beside Send.
  2. The composer becomes an instruction field. Describe the reply you want; typing is blocked briefly while generation is in progress and the instruction is visually muted.
  3. Copilot uses bounded recent conversation history and relevant contact context, including age and gender when available. It omits irrelevant identifiers and follows workspace guidance and safety rules.
  4. The generated text fills the composer with a quick character-reveal animation. Edit it, verify facts and tone, then send it manually.

A failed request shows a short message asking the agent to try again later. An exhausted balance shows a credit-specific message. A failed charged attempt is refunded where indicated by analytics.

Pooled balance examples

WorkspaceIncluded monthly poolHow it can be used
1 paid Portal seat1,000 draftsThe one permitted agent can use the entire pool.
10 paid Portal seats10,000 draftsOne permitted agent can use all 10,000; there is no automatic per-agent reserve.
20 seats plus 5,000 purchased drafts20,000 included + 5,000 purchasedIncluded drafts are consumed first; purchased balance remains until needed under current terms.

Reducing future seat quantity changes the future included allowance according to billing state. It does not create a per-person quota. Use permissions and agent-level overrides to control access, not seat assignment.

Permission evaluation order

Workspace stateAll Human AgentsCategoryOverrideResult
DisabledAnyAnyAnyHidden
EnabledCheckedAnyCategory ruleAllowed
EnabledCheckedAnyBlockBlocked
EnabledUncheckedAllowedCategory ruleAllowed
EnabledUncheckedNot allowedCategory ruleBlocked
EnabledUncheckedNot allowedAlways allowAllowed
EnabledUncheckedAllowedBlockBlocked

Explicit Block has the highest priority. When All Human Agents is checked, category controls are intentionally disabled because they no longer narrow the workspace rule.

Write effective Workspace guidance

Guidance applies to every generated draft, so include durable writing conventions rather than one campaign's temporary response.

Guidance example
Keep replies warm and direct. Address customers by first name when known. Use "workspace" instead of "account." Do not promise delivery dates unless one appears in the conversation or verified order data. For billing disputes, acknowledge the concern and direct the customer to the documented review process.

Safety and factuality rules remain higher priority. Guidance cannot authorize invented facts or override access. Keep it under 1,200 characters and test it across several channels and languages.

What generation uses

The generation request uses the Human Agent's instruction, a bounded recent conversation history, and selected contact context. Recent history can include up to eight relevant messages within backend character limits. Useful contact fields such as name, country, age, and gender can be included when available; irrelevant internal identifiers are omitted.

The prompt asks for the language dominating the meaningful recent conversation. Very short noise in another script can still make language detection ambiguous; the Human Agent must review the draft before sending.

Interpret Copilot analytics

  • Available now separates included and purchased balances.
  • Generated today counts successful workspace drafts for the current reporting day.
  • Last 30 days includes successful use and identifies failed/refunded attempts where recorded.
  • Average response measures generation latency for recorded attempts, not the time a Human Agent takes to edit and send.
  • Draft activity displays recent daily volume.
  • Usage by agent reveals concentration in the shared pool.
  • Channels shows where generation was initiated.

A high per-agent share is not inherently abuse because the balance is intentionally pooled. Compare it with staffing, conversation volume, and permission intent.

Human Agent drafting example

For a customer asking “Can I move delivery to Friday?”, the agent can instruct: Confirm that we can request Friday delivery but make clear it is not guaranteed; ask for the order number. Copilot should produce an editable customer reply, not execute the delivery change.

The Human Agent checks the name, date, policy, language, and promised action before sending. If the draft is wrong, edit it or generate from a clearer instruction; do not assume a fluent draft is factually verified.

Notifications

Route operational and balance alerts to the right external destinations without duplicating noise.

Delivery destinations

DestinationSetup
EmailAdd up to four recipient addresses and enable the destination.
SlackCreate a Slack Incoming Webhook, paste its URL, test, and enable.
DiscordCreate a Discord channel webhook, paste its URL, test, and enable.
Microsoft TeamsUse a Teams Workflow or incoming-webhook URL supported by your tenant.
Custom WebhookEnter an HTTPS endpoint and optional signing secret; verify requests at your receiver.

Each destination has its own enabled state. Configure and test the endpoint before enabling broad rules. The Console notification bell shows in-product state and links back to notification settings.

Credit and Copilot rules

AI credits and Copilot drafts each support rules for included allowance low, total available low, purchased balance low, included allowance exhausted, forecasted exhaustion within a chosen number of days based on the prior seven days, and usage spikes at 1.5×, 2×, or 3× the comparison level.

Choose thresholds that give the team time to act. A percentage threshold suits changing plan sizes; an absolute threshold suits a fixed operational buffer. Save changes before leaving when the page indicates unsaved settings.

Configure a destination safely

  1. Create a dedicated destination: distribution email, alerts Slack/Discord channel, Teams workflow, or HTTPS receiver.
  2. Enter the destination in its tab and save or test it before enabling high-volume rules.
  3. Enable one low-risk rule with a testable threshold and confirm the message reaches the intended people.
  4. Add remaining rules, avoiding several thresholds that trigger for the same condition unless escalation is intentional.
  5. Document who responds to each alert and where they check the underlying balance or usage.

Destination-specific behavior

  • Email: add up to four monitored recipients. Prefer a team address over a personal mailbox for operational alerts.
  • Slack: create an Incoming Webhook for a dedicated channel. The URL is a secret; channel renames and archive state can affect delivery.
  • Discord: create a channel webhook with permission to post. Rotate the URL if it is exposed.
  • Microsoft Teams: use the workflow/incoming URL supported by the tenant. Test after workflow ownership or tenant policy changes.
  • Custom Webhook: use HTTPS, validate the optional signing mechanism, acknowledge quickly, and queue slow processing.

Choose useful thresholds

RuleExampleOperational action
Included allowance low20% remainingReview expected use before the reset date.
Total available low1,500 credits/draftsBuy or reduce use if service must continue.
Purchased balance low500 remainingReplenish the non-included reserve.
Included exhausted0 includedConfirm purchased balance or accept interruption.
Forecast exhaustionWithin 5 daysInvestigate recent seven-day burn and planned campaigns.
Usage spikeCheck routing changes, loops, unusual traffic, or a legitimate launch.

Percentage and amount rules can overlap. If both are enabled, choose values that represent different escalation stages rather than generating duplicate alerts minutes apart.

Validate alerts without waiting for an incident

Use the destination test where available. For threshold rules, select a value that the current balance already satisfies, save, and confirm one alert; then restore the production threshold. Record the time so the test is not mistaken for a real incident.

If a notification is missing, check destination enabled state, unsaved changes, rule enabled state, current measured value, provider/workflow validity, and receiver logs in that order.

Console team

Share Console administration without confusing it with Portal agent access.

Invite and remove Console teammates

  1. Open Team and choose Invite.
  2. Enter the teammate's email and send the invitation.
  3. Track pending invitations and cancel one if it was sent to the wrong address or is no longer needed.
  4. Review workspace members periodically. Remove a member only after confirming they no longer require Console access.

Team membership grants Console access according to the product's workspace permissions. It does not automatically create a Human Agent record in Portal. Use Human Agents for Portal operational access.

Invite lifecycle and access review

  1. Verify the person requires Console administration rather than only Portal operational access.
  2. Send the invitation to an organization-controlled email address.
  3. Review the pending row. Cancel it if sent incorrectly, duplicated, or no longer needed.
  4. After acceptance, ask the teammate to confirm the workspace name before changing anything.
  5. Review members on a schedule and remove people who changed roles or left the organization.

Before removal, transfer ownership of provider accounts, billing operations, documentation, and secrets that the person maintained. Removing Console membership does not rotate credentials they previously copied.

Example: administrator who also handles customers

Sam needs Console access to configure email and also works tickets in Portal. Invite Sam from Team for Console and from Human Agents for Portal. Assign the Support category on the Human Agent record. If Sam stops handling customers but still administers integrations, remove only the Human Agent access.

Billing

Manage Portal seats, AI plans, shared balances, add-ons, renewal changes, and billing history.

Billing areas

Portal seats shows seat quantity, active Portal users, yearly or monthly total, price per seat, renewal, and pooled Copilot balance. AI Agents shows the current AI plan, message-credit use, storage, agent slots, and renewal. Recent Activity lists billing changes and transactions with pagination.

Only authorized workspace owners should change subscriptions. Manage Billing opens the billing provider's customer portal for invoices, payment methods, and supported subscription operations.

Seats and AI plans

  1. Review current active users and paid seats before reducing capacity.
  2. Choose Manage seats, set the future quantity, and select users to deactivate at renewal when the new quantity is below active usage.
  3. For AI Agents, compare included credits, storage, slots, billing interval, and current use before choosing a plan.
  4. Confirm the checkout or scheduled change. Wait for synchronization before repeating the action.

Portal trials can offer a skip-trial or immediate activation path. AI plan cancellation and downgrade can schedule changes for renewal rather than remove service immediately; read the confirmation state.

Balances and add-ons

  • Extra Copilot drafts add a purchased, non-expiring workspace balance after included drafts.
  • Extra AI credits add non-expiring credits shown as active items with remaining amounts.
  • Extra Agents add AI Agent or Dispatcher slots on monthly or yearly billing.
  • Custom Domain enables a branded hosted chat domain and its DNS workflow.
  • Remove Powered by MitsoLab removes deployment branding while the add-on is active.
  • Auto-recharge can purchase credits when a configured threshold is reached; review its cap and payment method.

Each add-on modal displays the current live price, interval, renewal or remaining items, and confirmation action. Use those live values as authoritative. Do not rely on a price copied from documentation.

Checkout, failure, and synchronization

A checkout success does not become usable until Console receives and applies the provider result. Keep the page open through synchronization. If the provider reports failure, use the retry action once after correcting payment details. Avoid rapid repeated clicks, which can create overlapping pending operations. Billing skeletons indicate loading only and are not a zero balance.

Portal seat and Copilot calculation

Paid seats represent Portal capacity. If 12 seats are paid and 9 Portal users are active, three seats remain available under that quantity. The same 12 seats contribute 12,000 included Copilot drafts to the monthly workspace pool under the current 1,000-per-seat allowance.

When reducing to 8 seats, select which users should no longer remain active when the change takes effect. Confirm the effective date shown in checkout or the management flow; a scheduled renewal change does not immediately remove the current paid entitlement.

Compare AI plans using actual workload

  1. Record credits used, storage, active Agent/Dispatcher slots, and days remaining in the current cycle.
  2. Estimate the next cycle using recent Insights and known launches. Do not extrapolate from one abnormal day without investigating it.
  3. Count required active slots, including Dispatchers. An inactive saved agent does not need an active slot until reactivated.
  4. Compare monthly and annual cash commitment, included credits, storage, and slot limits shown live.
  5. Choose the plan or add-on combination and review effective date, proration, renewal, and tax before confirming.

Choose a plan change or add-on

NeedUsually evaluate
Temporary message-credit spikeOne-time extra AI credits before a permanent plan upgrade.
One more specialist but adequate creditsExtra Agent slot.
More included credits and slots every monthAI plan upgrade.
Copilot reserve beyond included seat poolPurchased Copilot draft pack.
Branded public chat URLCustom Domain add-on and DNS configuration.
Remove deployment brandingRemove Powered by Mitsolab add-on.

The live Billing modal is authoritative for price, billing interval, renewal, and active items. A yearly add-on can have a scheduled interval switch rather than an immediate replacement.

Configure auto-recharge deliberately

Choose a threshold high enough to avoid service interruption and a purchase amount large enough to avoid repeated charges during normal use. Review any monthly cap or payment safeguards shown. Monitor the first trigger and keep the billing contact informed.

Auto-recharge does not correct an unexpected usage loop. If credits fall unusually fast, disable or limit the responsible route and investigate before relying on repeated purchases.

Use Recent Activity and the billing portal

Recent Activity is the Console audit view for billing-related changes and transactions; use pagination to inspect older rows. The external billing portal is the source for invoices, payment methods, and provider-supported subscription management.

When reconciling an issue, capture workspace, action, amount, currency, event time, current subscription state, and provider transaction reference. Never send full card details to support.

Understand modal states

  • Confirmation summarizes the intended item, quantity, interval, and effective timing.
  • Checkout collects or confirms payment through the billing provider.
  • Synchronizing means payment may have completed but Console has not applied the provider event yet.
  • Failure includes a reason or retry path; correct the cause before another attempt.
  • Active Items lists remaining purchased credit batches or active add-on slots and renewal details.

API

Connect a server-side integration to Portal through one workspace-scoped POST endpoint. This section explains the request model, authentication, reliability rules, limits, and response contract.

How the API works

The Portal API uses one HTTPS endpoint: POST https://api.mitsolab.com/api/v1. The JSON field event selects the operation, and data contains that event's inputs. You do not send a workspace ID. The API key identifies exactly one workspace and every operation is restricted to it.

API calls run as the service account selected when the key was created. Notes, tasks, messages, email activity, and other attributable records can therefore show that service account's name. Service accounts are integration identities rather than interactive Portal users.

Create a service account and API key

  1. In Console, choose the workspace and open API.
  2. Create a service account with a durable integration name such as Production CRM sync. This is the identity recorded for API-authored work.
  3. Select that service account and create an API key. Choose Full workspace access for reads and writes or Read only for read events only.
  4. Copy the full key immediately and store it in a server-side secret manager. Mitsolab displays it once and retains only a cryptographic hash.
  5. Send a request to api.events.list or workspace.get to verify the credential before enabling production writes.

Make the first request

Every call is a JSON POST. Authenticate with Authorization: Bearer YOUR_API_KEY. The X-API-Key header is also accepted, but using the Authorization header keeps one consistent convention.

First API request
curl -X POST https://api.mitsolab.com/api/v1 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event":"workspace.get","data":{}}'
Successful response
{
  "ok": true,
  "event": "workspace.get",
  "data": {
    "workspace": {
      "id": "9d2f6a63-20cd-4d42-a87e-52ebc21a9b41",
      "name": "Northwind Support",
      "active": true,
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Request and response contract

PartRequiredMeaning
HTTP methodYesAlways POST. OPTIONS is available for browser preflight, although secret API keys should normally be used from a server.
AuthorizationYesBearer YOUR_API_KEY, or the key in X-API-Key.
Content-TypeYesapplication/json.
eventYesExact case-sensitive event name.
dataEvent-specificA JSON object containing only the selected event's inputs.
Idempotency-KeyWritesA unique value that makes a retried write return the original result instead of performing the operation twice.

A successful response always contains ok: true, the selected event, event-specific data, and a request_id. Retain the request ID with integration logs because it identifies the call without exposing the API key.

Request bodies are limited to 256 KB. Upload files through the signed attachment-upload events; do not place file bytes or base64 content in the API JSON body.

Pagination

List events use offset pagination. Send limit and offset inside data. A paginated response includes the returned collection, total when available, and a pagination object with limit, offset, and returned.

Request page three
{
  "event": "contacts.list",
  "data": {
    "limit": 50,
    "offset": 100
  }
}

Use the maximum documented for the selected event. Continue while pagination.returned equals the requested limit, increasing offset by the number returned. Stop when fewer records are returned. Content-heavy lists default to 25 and allow up to 50; standard lists allow up to 100; lightweight lists allow up to 200.

Safe retries and idempotency

Send a unique Idempotency-Key header with every write. It is mandatory for messages.send, email.messages.send, and whatsapp.messages.send_template, and strongly recommended for every other create, update, link, unlink, status, assignment, and delete event.

Reuse the same key only when retrying the exact same event and data. Reusing it for different content returns 409 idempotency_conflict. A simultaneous retry can return 409 request_in_progress; wait before retrying with the same key. Generate a new UUID for the next intended operation.

Rate limits

By default, each API key may make 120 requests per minute. The workspace may make 300 requests per minute in total across all of its API keys. A request must be within both limits. Exceeding either limit returns HTTP 429 with rate_limit_exceeded.

For higher limits for enterprise, please contact us.

Design callers with bounded concurrency and exponential backoff. Do not immediately retry a large group of requests at the next minute boundary; spread queued work to avoid another burst.

Errors and status codes

Errors use one stable envelope: ok: false, an error object with code and message, and a request_id. Some errors include a safe details object.

Error response
{
  "ok": false,
  "error": {
    "code": "invalid_parameter",
    "message": "contact_id must be a positive integer"
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}
StatusMeaningCaller action
400Missing event, invalid parameter, unsupported input, required idempotency key, or unavailable template.Correct the request before retrying.
401Missing, malformed, expired, or revoked API key.Replace the credential; do not retry repeatedly.
403Portal subscription inactive or the key does not permit the event.Restore entitlement or use a suitable key.
404The requested workspace record or connected resource was not found.Verify the identifier belongs to this workspace.
409Current state conflicts with the operation, an attachment is unavailable, or an idempotent request is already processing.Inspect the error code, update state, or retry later with the same idempotency key.
413Request body exceeds 256 KB.Reduce JSON size and use signed uploads for files.
429Per-key or workspace rate limit exceeded.Back off and retry later.
500The request could not be completed.Retry a bounded number of times and retain request_id for support.
502A connected provider or delivery dependency was unavailable.Retry with backoff; check the provider if the problem persists.

All 76 API events

Event names are case-sensitive. Every event below has its own input contract, behavior notes, cURL request, and representative response.

Workspace

API events for workspace.

api.events.list

Workspaceread

Returns the live catalogue of API events available to the authenticated key, including whether each event reads or writes data.

Request data

This event takes no fields in data. Send an empty object.

Example request
api.events.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"api.events.list","data":{}}'
Representative response
api.events.list response
{
  "ok": true,
  "event": "api.events.list",
  "data": {
    "events": [
      {
        "event": "contacts.list",
        "scope": "contacts.read",
        "method": "POST",
        "operation": "read"
      },
      {
        "event": "contacts.create",
        "scope": "contacts.write",
        "method": "POST",
        "operation": "write"
      }
    ]
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

workspace.get

Workspaceread

Returns the workspace associated with the API key. No workspace ID is accepted because the key already identifies its workspace.

Request data

This event takes no fields in data. Send an empty object.

Example request
workspace.get request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"workspace.get","data":{}}'
Representative response
workspace.get response
{
  "ok": true,
  "event": "workspace.get",
  "data": {
    "workspace": {
      "id": "9d2f6a63-20cd-4d42-a87e-52ebc21a9b41",
      "name": "Northwind Support",
      "active": true,
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

workspace.members.list

Workspaceread

Returns workspace members that can be referenced by assignment events.

Request data
FieldTypeRequirementMeaning
activebooleanOptionalSet to false to include inactive memberships.
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 200.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
workspace.members.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"workspace.members.list","data":{"active":true,"limit":50,"offset":0}}'
Representative response
workspace.members.list response
{
  "ok": true,
  "event": "workspace.members.list",
  "data": {
    "members": [
      {
        "workspace_id": "9d2f6a63-20cd-4d42-a87e-52ebc21a9b41",
        "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
        "admin": true,
        "user_category": "support",
        "active": true,
        "can_access_human_agents": true,
        "agent_id": null,
        "created_at": "2026-08-15T09:20:00.000Z",
        "users": {
          "id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
          "email": "amina@example.com",
          "name": "Amina Saleh"
        }
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

agents.list

Workspaceread

Returns Portal agent records available within the workspace.

Request data
FieldTypeRequirementMeaning
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 200.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
agents.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"agents.list","data":{"limit":50,"offset":0}}'
Representative response
agents.list response
{
  "ok": true,
  "event": "agents.list",
  "data": {
    "agents": [
      {
        "id": "f6bc46a6-5ec8-4bba-88c7-f1237532e72a",
        "name": "Support Agent",
        "active": true,
        "created_at": "2026-08-15T09:20:00.000Z",
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Inbox

API events for inbox.

conversations.list

Inboxread

Returns Inbox conversations in newest-first order, with optional filters for status, channel, contact, or assigned member.

Request data
FieldTypeRequirementMeaning
statusstringOptionalConversation status to match.
channelstringOptionalChannel identifier such as whatsapp, messenger, instagram, telegram, line, or website.
contact_idintegerOptionalReturn conversations for one contact.
assigned_user_idUUIDOptionalReturn conversations assigned to one workspace member.
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
conversations.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"conversations.list","data":{"status":"active","channel":"whatsapp","limit":50,"offset":0}}'
Representative response
conversations.list response
{
  "ok": true,
  "event": "conversations.list",
  "data": {
    "conversations": [
      {
        "id": 4012,
        "status": "active",
        "chat_source": "whatsapp",
        "contact_id": 1842,
        "assigned_human_agent_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
        "subject": "Delivery address update",
        "created_at": "2026-08-15T09:20:00.000Z",
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

conversations.get

Inboxread

Returns one Inbox conversation by its numeric conversation ID.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation to retrieve.
Example request
conversations.get request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"conversations.get","data":{"conversation_id":4012}}'
Representative response
conversations.get response
{
  "ok": true,
  "event": "conversations.get",
  "data": {
    "conversation": {
      "id": 4012,
      "status": "active",
      "chat_source": "whatsapp",
      "contact_id": 1842,
      "assigned_human_agent_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "subject": "Delivery address update",
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

conversations.assign

Inboxwrite

Assigns a conversation directly to an active workspace member, or removes the direct human assignment when user_id is null.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation to update.
user_idUUID or nullRequiredActive member to assign, or null to clear the direct assignment.
Example request
conversations.assign request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"conversations.assign","data":{"conversation_id":4012,"user_id":"40e621d5-4504-4df7-8d2d-e90887df7c74"}}'
Representative response
conversations.assign response
{
  "ok": true,
  "event": "conversations.assign",
  "data": {
    "conversation": {
      "id": 4012,
      "status": "active",
      "chat_source": "whatsapp",
      "contact_id": 1842,
      "assigned_human_agent_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "subject": "Delivery address update",
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

conversations.owner.set

Inboxwrite

Sets the conversation owner to an unassigned state, category, human member, or AI agent.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation to update.
owner_kindstringRequiredOne of unassigned, category, human, or ai_agent.
target_idUUIDOptionalRequired when owner_kind is human or ai_agent.
category_keystringOptionalRequired with category_name when owner_kind is category.
category_namestringOptionalRequired with category_key when owner_kind is category.
Example request
conversations.owner.set request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"conversations.owner.set","data":{"conversation_id":4012,"owner_kind":"human","target_id":"40e621d5-4504-4df7-8d2d-e90887df7c74"}}'
Representative response
conversations.owner.set response
{
  "ok": true,
  "event": "conversations.owner.set",
  "data": {
    "assignment": {
      "owner_kind": "human",
      "target_id": "40e621d5-4504-4df7-8d2d-e90887df7c74"
    },
    "conversation": {
      "id": 4012,
      "status": "active",
      "chat_source": "whatsapp",
      "contact_id": 1842,
      "assigned_human_agent_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "subject": "Delivery address update",
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

conversations.resolve

Inboxwrite

Marks an Inbox conversation closed and records when it ended.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation to resolve.
Example request
conversations.resolve request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"conversations.resolve","data":{"conversation_id":4012}}'
Representative response
conversations.resolve response
{
  "ok": true,
  "event": "conversations.resolve",
  "data": {
    "conversation": {
      "id": 4012,
      "status": "closed",
      "chat_source": "whatsapp",
      "contact_id": 1842,
      "assigned_human_agent_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "subject": "Delivery address update",
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z",
      "ended_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

conversations.reopen

Inboxwrite

Reopens a closed Inbox conversation and returns it to active status.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation to reopen.
Example request
conversations.reopen request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"conversations.reopen","data":{"conversation_id":4012}}'
Representative response
conversations.reopen response
{
  "ok": true,
  "event": "conversations.reopen",
  "data": {
    "conversation": {
      "id": 4012,
      "status": "active",
      "chat_source": "whatsapp",
      "contact_id": 1842,
      "assigned_human_agent_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "subject": "Delivery address update",
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z",
      "ended_at": null
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

messages.list

Inboxread

Returns messages for one conversation in chronological order.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation whose messages should be returned.
limitintegerOptionalNumber of records to return. Defaults to 25; maximum 50.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
messages.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"messages.list","data":{"conversation_id":4012,"limit":25,"offset":0}}'
Representative response
messages.list response
{
  "ok": true,
  "event": "messages.list",
  "data": {
    "messages": [
      {
        "id": 9124,
        "conversation_id": 4012,
        "sender_type": "human_agent",
        "source": "whatsapp",
        "result": "Your delivery address has been updated.",
        "created_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 25,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

messages.send

Inboxwrite

Sends a reply through the conversation's connected channel and stores the sent message in Inbox.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation to reply to.
text or attachmentsstring or arrayRequiredSupply text, uploaded attachments, or both.
textstringOptionalMessage text, up to 3,500 characters.
attachmentsarrayOptionalUp to 10 descriptors returned by message_attachments.upload.create.
Example request
messages.send request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"messages.send","data":{"conversation_id":4012,"text":"Your delivery address has been updated."}}'
Representative response
messages.send response
{
  "ok": true,
  "event": "messages.send",
  "data": {
    "message": {
      "id": 9124,
      "conversation_id": 4012,
      "sender_type": "human_agent",
      "source": "whatsapp",
      "result": "Your delivery address has been updated.",
      "created_at": "2026-08-15T10:05:00.000Z"
    },
    "delivery_status": "accepted"
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

message_attachments.upload.create

Inboxwrite

Creates a one-hour signed upload destination for an outbound Inbox attachment.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation that will receive the attachment.
filenamestringRequiredOriginal filename.
mime_typestringRequiredFile media type.
byte_sizeintegerRequiredFile size from 1 byte through 10 MB.
Example request
message_attachments.upload.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"message_attachments.upload.create","data":{"conversation_id":4012,"filename":"invoice.pdf","mime_type":"application/pdf","byte_size":248302}}'
Representative response
message_attachments.upload.create response
{
  "ok": true,
  "event": "message_attachments.upload.create",
  "data": {
    "upload": {
      "bucket": "channel-attachments",
      "path": "9d2f6a63-20cd-4d42-a87e-52ebc21a9b41/outbound/whatsapp/4012/invoice.pdf",
      "token": "signed-upload-token",
      "upload_url": "https://storage.example/upload/channel-attachments/invoice.pdf",
      "filename": "invoice.pdf",
      "mime_type": "application/pdf",
      "byte_size": 248302,
      "attachment_kind": "document",
      "expires_in": 3600
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

message_attachments.download.create

Inboxread

Creates a 15-minute signed download URL for an Inbox message attachment.

Request data
FieldTypeRequirementMeaning
attachment_idintegerRequiredAttachment to download.
Example request
message_attachments.download.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"message_attachments.download.create","data":{"attachment_id":781}}'
Representative response
message_attachments.download.create response
{
  "ok": true,
  "event": "message_attachments.download.create",
  "data": {
    "download": {
      "url": "https://storage.example/download/invoice.pdf?token=signed-download-token",
      "expires_in": 900,
      "filename": "invoice.pdf",
      "mime_type": "application/pdf",
      "byte_size": 248302
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

conversation_notes.list

Inboxread

Returns internal notes attached to a conversation, newest first.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation whose notes should be returned.
limitintegerOptionalNumber of records to return. Defaults to 25; maximum 50.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
conversation_notes.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"conversation_notes.list","data":{"conversation_id":4012,"limit":25,"offset":0}}'
Representative response
conversation_notes.list response
{
  "ok": true,
  "event": "conversation_notes.list",
  "data": {
    "notes": [
      {
        "id": 611,
        "handoff_chat_id": 4012,
        "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
        "note": "Customer verified the order number.",
        "created_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 25,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

conversation_notes.create

Inboxwrite

Creates an internal note on an Inbox conversation under the service account identity.

Request data
FieldTypeRequirementMeaning
conversation_idintegerRequiredConversation to annotate.
notestringRequiredInternal note text, up to 10,000 characters.
Example request
conversation_notes.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"conversation_notes.create","data":{"conversation_id":4012,"note":"Customer verified the order number."}}'
Representative response
conversation_notes.create response
{
  "ok": true,
  "event": "conversation_notes.create",
  "data": {
    "note": {
      "id": 611,
      "handoff_chat_id": 4012,
      "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "note": "Customer verified the order number.",
      "created_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

conversation_notes.update

Inboxwrite

Replaces the text of an existing conversation note.

Request data
FieldTypeRequirementMeaning
note_idintegerRequiredConversation note to update.
notestringRequiredReplacement note text, up to 10,000 characters.
Example request
conversation_notes.update request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"conversation_notes.update","data":{"note_id":611,"note":"Customer verified the order number and delivery postcode."}}'
Representative response
conversation_notes.update response
{
  "ok": true,
  "event": "conversation_notes.update",
  "data": {
    "note": {
      "id": 611,
      "handoff_chat_id": 4012,
      "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "note": "Customer verified the order number and delivery postcode.",
      "created_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

conversation_notes.delete

Inboxwrite

Deletes an internal conversation note.

Request data
FieldTypeRequirementMeaning
note_idintegerRequiredConversation note to delete.
Example request
conversation_notes.delete request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"conversation_notes.delete","data":{"note_id":611}}'
Representative response
conversation_notes.delete response
{
  "ok": true,
  "event": "conversation_notes.delete",
  "data": {
    "deleted": {
      "id": 611,
      "handoff_chat_id": 4012,
      "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "note": "Customer verified the order number.",
      "created_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Contacts

API events for contacts.

contacts.list

Contactsread

Returns contacts in most-recently-updated order and supports text or exact-identifier filtering.

Request data
FieldTypeRequirementMeaning
searchstringOptionalMatches customer name, email, or phone number.
emailstringOptionalExact email address match.
external_idstringOptionalExact external identifier match.
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
contacts.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"contacts.list","data":{"search":"Amina","limit":50,"offset":0}}'
Representative response
contacts.list response
{
  "ok": true,
  "event": "contacts.list",
  "data": {
    "contacts": [
      {
        "id": 1842,
        "customer_name": "Amina Saleh",
        "email": "amina@example.com",
        "phone_number": "+962790000000",
        "country": "JO",
        "tags": [
          "priority"
        ],
        "external_id": "customer_1842",
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contacts.get

Contactsread

Returns one contact by numeric contact ID.

Request data
FieldTypeRequirementMeaning
contact_idintegerRequiredContact to retrieve.
Example request
contacts.get request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"contacts.get","data":{"contact_id":1842}}'
Representative response
contacts.get response
{
  "ok": true,
  "event": "contacts.get",
  "data": {
    "contact": {
      "id": 1842,
      "customer_name": "Amina Saleh",
      "email": "amina@example.com",
      "phone_number": "+962790000000",
      "country": "JO",
      "tags": [
        "priority"
      ],
      "external_id": "customer_1842",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contacts.create

Contactswrite

Creates a workspace contact. The API assigns the contact to the authenticated workspace automatically.

Request data
FieldTypeRequirementMeaning
customer_namestringOptionalContact display name.
phone_numberstringOptionalTelephone number, preferably international format.
emailstringOptionalEmail address.
countrystringOptionalCountry name or code used by the workspace.
genderstringOptionalOptional gender value.
agenumberOptionalOptional age value.
profile_handlestringOptionalChannel profile handle.
profile_idstringOptionalChannel profile identifier.
external_idstringOptionalIdentifier from your system.
external_identifierstringOptionalAlternative source identifier.
agent_idUUIDOptionalRelated Portal agent when applicable.
chat_sourcestringOptionalSource label; defaults to api.
sourcestringOptionalAdditional source label.
tagsarray of stringsOptionalContact tags.
metadataobjectOptionalIntegration-owned metadata.
custom_fieldsobjectOptionalWorkspace custom-field values.
Example request
contacts.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"contacts.create","data":{"customer_name":"Amina Saleh","email":"amina@example.com","phone_number":"+962790000000","country":"JO","external_id":"customer_1842","tags":["priority"]}}'
Representative response
contacts.create response
{
  "ok": true,
  "event": "contacts.create",
  "data": {
    "contact": {
      "id": 1842,
      "customer_name": "Amina Saleh",
      "email": "amina@example.com",
      "phone_number": "+962790000000",
      "country": "JO",
      "tags": [
        "priority"
      ],
      "external_id": "customer_1842",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contacts.update

Contactswrite

Updates supported fields on an existing contact.

Request data
FieldTypeRequirementMeaning
contact_idintegerRequiredContact to update.
customer_namestringOptionalReplacement display name.
phone_numberstringOptionalReplacement phone number.
emailstringOptionalReplacement email address.
countrystringOptionalReplacement country value.
genderstringOptionalReplacement gender value.
agenumberOptionalReplacement age value.
profile_handlestringOptionalReplacement channel handle.
profile_idstringOptionalReplacement channel profile ID.
external_idstringOptionalReplacement external identifier.
tagsarray of stringsOptionalComplete replacement tag list.
metadataobjectOptionalComplete replacement metadata object.
custom_fieldsobjectOptionalUpdated custom-field values.
Example request
contacts.update request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"contacts.update","data":{"contact_id":1842,"phone_number":"+962790000111","tags":["priority","verified"]}}'
Representative response
contacts.update response
{
  "ok": true,
  "event": "contacts.update",
  "data": {
    "contact": {
      "id": 1842,
      "customer_name": "Amina Saleh",
      "email": "amina@example.com",
      "phone_number": "+962790000111",
      "country": "JO",
      "tags": [
        "priority",
        "verified"
      ],
      "external_id": "customer_1842",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contacts.delete

Contactswrite

Deletes a contact from the workspace and returns the deleted record.

Request data
FieldTypeRequirementMeaning
contact_idintegerRequiredContact to delete.
Example request
contacts.delete request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"contacts.delete","data":{"contact_id":1842}}'
Representative response
contacts.delete response
{
  "ok": true,
  "event": "contacts.delete",
  "data": {
    "deleted": {
      "id": 1842,
      "customer_name": "Amina Saleh",
      "email": "amina@example.com",
      "phone_number": "+962790000000",
      "country": "JO",
      "tags": [
        "priority"
      ],
      "external_id": "customer_1842",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contacts.activity.list

Contactsread

Returns recorded activity for one contact in newest-first order.

Request data
FieldTypeRequirementMeaning
contact_idintegerRequiredContact whose activity should be returned.
limitintegerOptionalNumber of records to return. Defaults to 25; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
contacts.activity.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"contacts.activity.list","data":{"contact_id":1842,"limit":25,"offset":0}}'
Representative response
contacts.activity.list response
{
  "ok": true,
  "event": "contacts.activity.list",
  "data": {
    "activity": [
      {
        "id": 9821,
        "contact_id": 1842,
        "action": "updated",
        "user": {
          "id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
          "name": "Amina Saleh",
          "email": "amina@example.com"
        },
        "created_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 25,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contacts.conversations.list

Contactsread

Returns Inbox conversations linked to one contact.

Request data
FieldTypeRequirementMeaning
contact_idintegerRequiredContact whose conversations should be returned.
statusstringOptionalConversation status filter.
channelstringOptionalChannel filter.
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
contacts.conversations.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"contacts.conversations.list","data":{"contact_id":1842,"status":"active","limit":50,"offset":0}}'
Representative response
contacts.conversations.list response
{
  "ok": true,
  "event": "contacts.conversations.list",
  "data": {
    "conversations": [
      {
        "id": 4012,
        "status": "active",
        "chat_source": "whatsapp",
        "contact_id": 1842,
        "assigned_human_agent_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
        "subject": "Delivery address update",
        "created_at": "2026-08-15T09:20:00.000Z",
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contact_notes.list

Contactsread

Returns internal notes for one contact, newest first.

Request data
FieldTypeRequirementMeaning
contact_idintegerRequiredContact whose notes should be returned.
limitintegerOptionalNumber of records to return. Defaults to 25; maximum 50.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
contact_notes.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"contact_notes.list","data":{"contact_id":1842,"limit":25,"offset":0}}'
Representative response
contact_notes.list response
{
  "ok": true,
  "event": "contact_notes.list",
  "data": {
    "notes": [
      {
        "id": 733,
        "contact_id": 1842,
        "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
        "note": "Prefers delivery updates by WhatsApp.",
        "created_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 25,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contact_notes.create

Contactswrite

Creates an internal note on a contact under the service account identity.

Request data
FieldTypeRequirementMeaning
contact_idintegerRequiredContact to annotate.
notestringRequiredNote text, up to 10,000 characters.
Example request
contact_notes.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"contact_notes.create","data":{"contact_id":1842,"note":"Prefers delivery updates by WhatsApp."}}'
Representative response
contact_notes.create response
{
  "ok": true,
  "event": "contact_notes.create",
  "data": {
    "note": {
      "id": 733,
      "contact_id": 1842,
      "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "note": "Prefers delivery updates by WhatsApp.",
      "created_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contact_notes.update

Contactswrite

Replaces the text of an existing contact note.

Request data
FieldTypeRequirementMeaning
note_idintegerRequiredContact note to update.
notestringRequiredReplacement note text, up to 10,000 characters.
Example request
contact_notes.update request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"contact_notes.update","data":{"note_id":733,"note":"Prefers WhatsApp updates after 10:00 AM."}}'
Representative response
contact_notes.update response
{
  "ok": true,
  "event": "contact_notes.update",
  "data": {
    "note": {
      "id": 733,
      "contact_id": 1842,
      "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "note": "Prefers WhatsApp updates after 10:00 AM.",
      "created_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

contact_notes.delete

Contactswrite

Deletes an internal contact note.

Request data
FieldTypeRequirementMeaning
note_idintegerRequiredContact note to delete.
Example request
contact_notes.delete request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"contact_notes.delete","data":{"note_id":733}}'
Representative response
contact_notes.delete response
{
  "ok": true,
  "event": "contact_notes.delete",
  "data": {
    "deleted": {
      "id": 733,
      "contact_id": 1842,
      "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "note": "Prefers delivery updates by WhatsApp.",
      "created_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Organizations

API events for organizations.

organizations.list

Organizationsread

Returns organizations in most-recently-updated order.

Request data
FieldTypeRequirementMeaning
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
organizations.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"organizations.list","data":{"limit":50,"offset":0}}'
Representative response
organizations.list response
{
  "ok": true,
  "event": "organizations.list",
  "data": {
    "organizations": [
      {
        "id": 310,
        "name": "Northwind Traders",
        "website": "https://northwind.example",
        "email": "support@northwind.example",
        "country": "JO",
        "industry": "Retail",
        "tags": [
          "customer"
        ],
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

organizations.get

Organizationsread

Returns one organization by numeric organization ID.

Request data
FieldTypeRequirementMeaning
organization_idintegerRequiredOrganization to retrieve.
Example request
organizations.get request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"organizations.get","data":{"organization_id":310}}'
Representative response
organizations.get response
{
  "ok": true,
  "event": "organizations.get",
  "data": {
    "organization": {
      "id": 310,
      "name": "Northwind Traders",
      "website": "https://northwind.example",
      "email": "support@northwind.example",
      "country": "JO",
      "industry": "Retail",
      "tags": [
        "customer"
      ],
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

organizations.create

Organizationswrite

Creates an organization that can be linked to contacts.

Request data
FieldTypeRequirementMeaning
namestringRequiredOrganization name, up to 200 characters.
websitestringOptionalOrganization website.
emailstringOptionalOrganization email.
phone_numberstringOptionalOrganization phone number.
countrystringOptionalCountry name or code.
industrystringOptionalIndustry label.
organization_typestringOptionalWorkspace-defined organization type.
agent_idUUIDOptionalRelated Portal agent when applicable.
tagsarray of stringsOptionalOrganization tags.
metadataobjectOptionalIntegration-owned metadata.
Example request
organizations.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"organizations.create","data":{"name":"Northwind Traders","website":"https://northwind.example","email":"support@northwind.example","country":"JO","industry":"Retail","tags":["customer"]}}'
Representative response
organizations.create response
{
  "ok": true,
  "event": "organizations.create",
  "data": {
    "organization": {
      "id": 310,
      "name": "Northwind Traders",
      "website": "https://northwind.example",
      "email": "support@northwind.example",
      "country": "JO",
      "industry": "Retail",
      "tags": [
        "customer"
      ],
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

organizations.update

Organizationswrite

Updates supported fields on an organization.

Request data
FieldTypeRequirementMeaning
organization_idintegerRequiredOrganization to update.
namestringOptionalReplacement name.
websitestringOptionalReplacement website.
emailstringOptionalReplacement email.
phone_numberstringOptionalReplacement phone number.
countrystringOptionalReplacement country.
industrystringOptionalReplacement industry.
organization_typestringOptionalReplacement type.
tagsarray of stringsOptionalComplete replacement tag list.
metadataobjectOptionalComplete replacement metadata object.
Example request
organizations.update request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"organizations.update","data":{"organization_id":310,"industry":"Wholesale","tags":["customer","enterprise"]}}'
Representative response
organizations.update response
{
  "ok": true,
  "event": "organizations.update",
  "data": {
    "organization": {
      "id": 310,
      "name": "Northwind Traders",
      "website": "https://northwind.example",
      "email": "support@northwind.example",
      "country": "JO",
      "industry": "Wholesale",
      "tags": [
        "customer",
        "enterprise"
      ],
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

organizations.delete

Organizationswrite

Deletes an organization and returns the deleted record.

Request data
FieldTypeRequirementMeaning
organization_idintegerRequiredOrganization to delete.
Example request
organizations.delete request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"organizations.delete","data":{"organization_id":310}}'
Representative response
organizations.delete response
{
  "ok": true,
  "event": "organizations.delete",
  "data": {
    "deleted": {
      "id": 310,
      "name": "Northwind Traders",
      "website": "https://northwind.example",
      "email": "support@northwind.example",
      "country": "JO",
      "industry": "Retail",
      "tags": [
        "customer"
      ],
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

CRM pipelines

API events for crm pipelines.

pipelines.list

CRM pipelinesread

Returns CRM pipelines in configured display order.

Request data
FieldTypeRequirementMeaning
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 200.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
pipelines.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"pipelines.list","data":{"limit":50,"offset":0}}'
Representative response
pipelines.list response
{
  "ok": true,
  "event": "pipelines.list",
  "data": {
    "pipelines": [
      {
        "id": 81,
        "name": "Sales",
        "slug": "sales",
        "color": "#0f3f86",
        "sort_order": 0,
        "active": true
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipelines.create

CRM pipelineswrite

Creates a CRM pipeline.

Request data
FieldTypeRequirementMeaning
namestringRequiredPipeline name, up to 120 characters.
slugstringOptionalStable URL-safe identifier; generated from name when omitted.
colorstringOptionalDisplay color; defaults to #0f3f86.
sort_orderintegerOptionalDisplay order; defaults to 0.
activebooleanOptionalWhether the pipeline is active; defaults to true.
Example request
pipelines.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"pipelines.create","data":{"name":"Sales","slug":"sales","color":"#0f3f86","sort_order":0,"active":true}}'
Representative response
pipelines.create response
{
  "ok": true,
  "event": "pipelines.create",
  "data": {
    "pipeline": {
      "id": 81,
      "name": "Sales",
      "slug": "sales",
      "color": "#0f3f86",
      "sort_order": 0,
      "active": true
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipelines.update

CRM pipelineswrite

Updates a CRM pipeline's display and availability settings.

Request data
FieldTypeRequirementMeaning
pipeline_idintegerRequiredPipeline to update.
namestringOptionalReplacement name.
slugstringOptionalReplacement slug.
colorstringOptionalReplacement display color.
sort_orderintegerOptionalReplacement display order.
activebooleanOptionalActive state.
admin_onlybooleanOptionalWhether the pipeline is limited to administrators.
Example request
pipelines.update request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"pipelines.update","data":{"pipeline_id":81,"name":"Enterprise sales","color":"#214f83"}}'
Representative response
pipelines.update response
{
  "ok": true,
  "event": "pipelines.update",
  "data": {
    "pipeline": {
      "id": 81,
      "name": "Enterprise sales",
      "slug": "sales",
      "color": "#214f83",
      "sort_order": 0,
      "active": true
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipelines.delete

CRM pipelineswrite

Deletes a CRM pipeline and returns the deleted record.

Request data
FieldTypeRequirementMeaning
pipeline_idintegerRequiredPipeline to delete.
Example request
pipelines.delete request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"pipelines.delete","data":{"pipeline_id":81}}'
Representative response
pipelines.delete response
{
  "ok": true,
  "event": "pipelines.delete",
  "data": {
    "deleted": {
      "id": 81,
      "name": "Sales",
      "slug": "sales",
      "color": "#0f3f86",
      "sort_order": 0,
      "active": true
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipeline_stages.list

CRM pipelinesread

Returns CRM stages in display order, optionally restricted to one pipeline.

Request data
FieldTypeRequirementMeaning
pipeline_idintegerOptionalReturn stages belonging to one pipeline.
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 200.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
pipeline_stages.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"pipeline_stages.list","data":{"pipeline_id":81,"limit":50,"offset":0}}'
Representative response
pipeline_stages.list response
{
  "ok": true,
  "event": "pipeline_stages.list",
  "data": {
    "stages": [
      {
        "id": 205,
        "pipeline_id": 81,
        "name": "Qualified",
        "slug": "qualified",
        "color": "#3178c6",
        "sort_order": 20,
        "active": true
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipeline_stages.create

CRM pipelineswrite

Creates a stage inside an existing pipeline.

Request data
FieldTypeRequirementMeaning
pipeline_idintegerRequiredParent pipeline.
namestringRequiredStage name, up to 120 characters.
slugstringOptionalStable URL-safe identifier; generated from name when omitted.
colorstringOptionalDisplay color; defaults to #0f3f86.
sort_orderintegerOptionalDisplay order; defaults to 0.
activebooleanOptionalWhether the stage is active; defaults to true.
Example request
pipeline_stages.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"pipeline_stages.create","data":{"pipeline_id":81,"name":"Qualified","slug":"qualified","color":"#3178c6","sort_order":20,"active":true}}'
Representative response
pipeline_stages.create response
{
  "ok": true,
  "event": "pipeline_stages.create",
  "data": {
    "stage": {
      "id": 205,
      "pipeline_id": 81,
      "name": "Qualified",
      "slug": "qualified",
      "color": "#3178c6",
      "sort_order": 20,
      "active": true
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipeline_stages.update

CRM pipelineswrite

Updates a CRM stage.

Request data
FieldTypeRequirementMeaning
stage_idintegerRequiredStage to update.
namestringOptionalReplacement name.
slugstringOptionalReplacement slug.
colorstringOptionalReplacement color.
sort_orderintegerOptionalReplacement display order.
activebooleanOptionalActive state.
Example request
pipeline_stages.update request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"pipeline_stages.update","data":{"stage_id":205,"name":"Qualified lead","sort_order":30}}'
Representative response
pipeline_stages.update response
{
  "ok": true,
  "event": "pipeline_stages.update",
  "data": {
    "stage": {
      "id": 205,
      "pipeline_id": 81,
      "name": "Qualified lead",
      "slug": "qualified",
      "color": "#3178c6",
      "sort_order": 30,
      "active": true
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipeline_stages.delete

CRM pipelineswrite

Deletes a CRM stage and returns the deleted record.

Request data
FieldTypeRequirementMeaning
stage_idintegerRequiredStage to delete.
Example request
pipeline_stages.delete request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"pipeline_stages.delete","data":{"stage_id":205}}'
Representative response
pipeline_stages.delete response
{
  "ok": true,
  "event": "pipeline_stages.delete",
  "data": {
    "deleted": {
      "id": 205,
      "pipeline_id": 81,
      "name": "Qualified",
      "slug": "qualified",
      "color": "#3178c6",
      "sort_order": 20,
      "active": true
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipeline_cards.list

CRM pipelinesread

Returns contact cards in pipeline order, optionally filtered by pipeline or stage.

Request data
FieldTypeRequirementMeaning
pipeline_idintegerOptionalReturn cards in one pipeline.
stage_idintegerOptionalReturn cards in one stage.
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
pipeline_cards.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"pipeline_cards.list","data":{"pipeline_id":81,"stage_id":205,"limit":50,"offset":0}}'
Representative response
pipeline_cards.list response
{
  "ok": true,
  "event": "pipeline_cards.list",
  "data": {
    "cards": [
      {
        "contact_id": 1842,
        "pipeline_id": 81,
        "stage_id": 205,
        "pipeline_order": 1723716000000,
        "contacts": {
          "id": 1842,
          "customer_name": "Amina Saleh",
          "email": "amina@example.com",
          "phone_number": "+962790000000",
          "country": "JO",
          "tags": [
            "priority"
          ],
          "external_id": "customer_1842",
          "updated_at": "2026-08-15T10:05:00.000Z"
        }
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipeline_cards.upsert

CRM pipelineswrite

Creates a contact card in a pipeline or moves the existing card to the requested stage.

Request data
FieldTypeRequirementMeaning
contact_idintegerRequiredContact represented by the card.
pipeline_idintegerRequiredPipeline containing the card.
stage_idintegerRequiredDestination stage within that pipeline.
pipeline_ordernumberOptionalOrdering value within the stage; current time is used when omitted.
Example request
pipeline_cards.upsert request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"pipeline_cards.upsert","data":{"contact_id":1842,"pipeline_id":81,"stage_id":205,"pipeline_order":1723716000000}}'
Representative response
pipeline_cards.upsert response
{
  "ok": true,
  "event": "pipeline_cards.upsert",
  "data": {
    "card": {
      "contact_id": 1842,
      "pipeline_id": 81,
      "stage_id": 205,
      "pipeline_order": 1723716000000,
      "contacts": {
        "id": 1842,
        "customer_name": "Amina Saleh",
        "email": "amina@example.com",
        "phone_number": "+962790000000",
        "country": "JO",
        "tags": [
          "priority"
        ],
        "external_id": "customer_1842",
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

pipeline_cards.delete

CRM pipelineswrite

Removes a contact card from a pipeline.

Request data
FieldTypeRequirementMeaning
contact_idintegerRequiredContact represented by the card.
pipeline_idintegerRequiredPipeline from which to remove it.
Example request
pipeline_cards.delete request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"pipeline_cards.delete","data":{"contact_id":1842,"pipeline_id":81}}'
Representative response
pipeline_cards.delete response
{
  "ok": true,
  "event": "pipeline_cards.delete",
  "data": {
    "deleted": {
      "contact_id": 1842,
      "pipeline_id": 81,
      "stage_id": 205,
      "pipeline_order": 1723716000000,
      "contacts": {
        "id": 1842,
        "customer_name": "Amina Saleh",
        "email": "amina@example.com",
        "phone_number": "+962790000000",
        "country": "JO",
        "tags": [
          "priority"
        ],
        "external_id": "customer_1842",
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Tasks

API events for tasks.

tasks.list

Tasksread

Returns workspace tasks in status order, optionally filtered by status or assignee.

Request data
FieldTypeRequirementMeaning
statusstringOptionalTask status to match.
assignee_user_idUUIDOptionalReturn tasks assigned to one active member.
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
tasks.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"tasks.list","data":{"status":"new","assignee_user_id":"40e621d5-4504-4df7-8d2d-e90887df7c74","limit":50,"offset":0}}'
Representative response
tasks.list response
{
  "ok": true,
  "event": "tasks.list",
  "data": {
    "tasks": [
      {
        "id": 921,
        "title": "Confirm delivery address",
        "description": "Call the customer before dispatch.",
        "status": "new",
        "priority": true,
        "assignee_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
        "created_at": "2026-08-15T09:20:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

tasks.get

Tasksread

Returns one task together with its linked contact IDs.

Request data
FieldTypeRequirementMeaning
task_idintegerRequiredTask to retrieve.
Example request
tasks.get request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"tasks.get","data":{"task_id":921}}'
Representative response
tasks.get response
{
  "ok": true,
  "event": "tasks.get",
  "data": {
    "task": {
      "id": 921,
      "title": "Confirm delivery address",
      "description": "Call the customer before dispatch.",
      "status": "new",
      "priority": true,
      "assignee_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "created_at": "2026-08-15T09:20:00.000Z",
      "workspace_task_contacts": [
        {
          "contact_id": 1842
        }
      ]
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

tasks.create

Taskswrite

Creates a task assigned to an active workspace member.

Request data
FieldTypeRequirementMeaning
titlestringRequiredTask title, up to 300 characters.
descriptionstringRequiredTask description, up to 10,000 characters.
assignee_user_idUUIDRequiredActive member responsible for the task.
statusstringOptionalInitial status; defaults to new.
prioritybooleanOptionalWhether the task is high priority.
finish_notestringOptionalOptional completion note, up to 400 characters.
status_ordernumberOptionalOrdering value; current time is used when omitted.
Example request
tasks.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"tasks.create","data":{"title":"Confirm delivery address","description":"Call the customer before dispatch.","assignee_user_id":"40e621d5-4504-4df7-8d2d-e90887df7c74","status":"new","priority":true}}'
Representative response
tasks.create response
{
  "ok": true,
  "event": "tasks.create",
  "data": {
    "task": {
      "id": 921,
      "title": "Confirm delivery address",
      "description": "Call the customer before dispatch.",
      "status": "new",
      "priority": true,
      "assignee_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "created_at": "2026-08-15T09:20:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

tasks.update

Taskswrite

Updates a task's content, status, priority, assignee, completion details, or ordering.

Request data
FieldTypeRequirementMeaning
task_idintegerRequiredTask to update.
titlestringOptionalReplacement title.
descriptionstringOptionalReplacement description.
statusstringOptionalReplacement status.
prioritybooleanOptionalPriority state.
assignee_user_idUUIDOptionalReplacement active assignee.
finish_notestringOptionalCompletion note.
finished_atISO 8601 timestampOptionalCompletion time.
status_ordernumberOptionalReplacement ordering value.
Example request
tasks.update request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"tasks.update","data":{"task_id":921,"status":"finished","finish_note":"Address confirmed with customer.","finished_at":"2026-08-15T10:05:00.000Z"}}'
Representative response
tasks.update response
{
  "ok": true,
  "event": "tasks.update",
  "data": {
    "task": {
      "id": 921,
      "title": "Confirm delivery address",
      "description": "Call the customer before dispatch.",
      "status": "finished",
      "priority": true,
      "assignee_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "created_at": "2026-08-15T09:20:00.000Z",
      "finish_note": "Address confirmed with customer.",
      "finished_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

tasks.delete

Taskswrite

Deletes a task and returns the deleted record.

Request data
FieldTypeRequirementMeaning
task_idintegerRequiredTask to delete.
Example request
tasks.delete request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"tasks.delete","data":{"task_id":921}}'
Representative response
tasks.delete response
{
  "ok": true,
  "event": "tasks.delete",
  "data": {
    "deleted": {
      "id": 921,
      "title": "Confirm delivery address",
      "description": "Call the customer before dispatch.",
      "status": "new",
      "priority": true,
      "assignee_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "created_at": "2026-08-15T09:20:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Knowledge

API events for knowledge.

knowledge.list

Knowledgeread

Returns workspace knowledge entries in most-recently-updated order.

Request data
FieldTypeRequirementMeaning
limitintegerOptionalNumber of records to return. Defaults to 25; maximum 50.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
knowledge.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"knowledge.list","data":{"limit":25,"offset":0}}'
Representative response
knowledge.list response
{
  "ok": true,
  "event": "knowledge.list",
  "data": {
    "entries": [
      {
        "id": "34d2156f-4e54-46db-902e-7831dd47ab1d",
        "title": "Returns policy",
        "description": "Returns are accepted within 30 days with proof of purchase.",
        "tags": [
          "returns",
          "policy"
        ],
        "created_at": "2026-08-15T09:20:00.000Z",
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 25,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

knowledge.get

Knowledgeread

Returns one knowledge entry by UUID.

Request data
FieldTypeRequirementMeaning
entry_idUUIDRequiredKnowledge entry to retrieve.
Example request
knowledge.get request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"knowledge.get","data":{"entry_id":"34d2156f-4e54-46db-902e-7831dd47ab1d"}}'
Representative response
knowledge.get response
{
  "ok": true,
  "event": "knowledge.get",
  "data": {
    "entry": {
      "id": "34d2156f-4e54-46db-902e-7831dd47ab1d",
      "title": "Returns policy",
      "description": "Returns are accepted within 30 days with proof of purchase.",
      "tags": [
        "returns",
        "policy"
      ],
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

knowledge.create

Knowledgewrite

Creates a workspace knowledge entry under the service account identity.

Request data
FieldTypeRequirementMeaning
titlestringRequiredEntry title, up to 160 characters.
descriptionstringRequiredKnowledge content, up to 12,000 characters.
tagsarray of stringsOptionalUp to 24 tags.
Example request
knowledge.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"knowledge.create","data":{"title":"Returns policy","description":"Returns are accepted within 30 days with proof of purchase.","tags":["returns","policy"]}}'
Representative response
knowledge.create response
{
  "ok": true,
  "event": "knowledge.create",
  "data": {
    "entry": {
      "id": "34d2156f-4e54-46db-902e-7831dd47ab1d",
      "title": "Returns policy",
      "description": "Returns are accepted within 30 days with proof of purchase.",
      "tags": [
        "returns",
        "policy"
      ],
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

knowledge.update

Knowledgewrite

Updates the title, content, or tags of a knowledge entry.

Request data
FieldTypeRequirementMeaning
entry_idUUIDRequiredKnowledge entry to update.
titlestringOptionalReplacement title.
descriptionstringOptionalReplacement content.
tagsarray of stringsOptionalComplete replacement tag list, up to 24 tags.
Example request
knowledge.update request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"knowledge.update","data":{"entry_id":"34d2156f-4e54-46db-902e-7831dd47ab1d","description":"Returns are accepted within 30 days with the original proof of purchase.","tags":["returns","policy","receipt"]}}'
Representative response
knowledge.update response
{
  "ok": true,
  "event": "knowledge.update",
  "data": {
    "entry": {
      "id": "34d2156f-4e54-46db-902e-7831dd47ab1d",
      "title": "Returns policy",
      "description": "Returns are accepted within 30 days with the original proof of purchase.",
      "tags": [
        "returns",
        "policy",
        "receipt"
      ],
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

knowledge.delete

Knowledgewrite

Deletes a knowledge entry and returns the deleted record.

Request data
FieldTypeRequirementMeaning
entry_idUUIDRequiredKnowledge entry to delete.
Example request
knowledge.delete request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"knowledge.delete","data":{"entry_id":"34d2156f-4e54-46db-902e-7831dd47ab1d"}}'
Representative response
knowledge.delete response
{
  "ok": true,
  "event": "knowledge.delete",
  "data": {
    "deleted": {
      "id": "34d2156f-4e54-46db-902e-7831dd47ab1d",
      "title": "Returns policy",
      "description": "Returns are accepted within 30 days with proof of purchase.",
      "tags": [
        "returns",
        "policy"
      ],
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Action Tools

API events for action tools.

action_tools.list

Action Toolsread

Returns safe Action Tool definitions, visible field configuration, and response mappings without credentials or private request configuration.

Request data
FieldTypeRequirementMeaning
include_disabledbooleanOptionalSet true to include disabled tools; defaults to false.
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
action_tools.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"action_tools.list","data":{"include_disabled":false,"limit":50,"offset":0}}'
Representative response
action_tools.list response
{
  "ok": true,
  "event": "action_tools.list",
  "data": {
    "tools": [
      {
        "id": "6fe09964-c4a6-4215-b894-2a322330b803",
        "name": "Check order status",
        "description": "Looks up the current fulfillment state.",
        "enabled": true,
        "method": "POST",
        "created_at": "2026-08-15T09:20:00.000Z",
        "updated_at": "2026-08-15T10:05:00.000Z",
        "action_tool_fields": [
          {
            "id": 1,
            "label": "Order number",
            "variable_key": "order_number",
            "field_type": "text",
            "required": true,
            "visible_to_agent": true,
            "sort_order": 0
          }
        ],
        "action_tool_response_mappings": [
          {
            "id": 1,
            "label": "Status",
            "json_path": "$.status",
            "display": true,
            "sort_order": 0
          }
        ]
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

action_tool_runs.list

Action Toolsread

Returns sanitized Action Tool run history in newest-first order.

Request data
FieldTypeRequirementMeaning
action_tool_idUUIDOptionalReturn runs for one Action Tool.
limitintegerOptionalNumber of records to return. Defaults to 25; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
action_tool_runs.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"action_tool_runs.list","data":{"action_tool_id":"6fe09964-c4a6-4215-b894-2a322330b803","limit":25,"offset":0}}'
Representative response
action_tool_runs.list response
{
  "ok": true,
  "event": "action_tool_runs.list",
  "data": {
    "runs": [
      {
        "id": "c6d02a58-f265-48c5-b79b-ddcc42ef0ea3",
        "action_tool_id": "6fe09964-c4a6-4215-b894-2a322330b803",
        "action_tool_name": "Check order status",
        "run_mode": "portal",
        "user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
        "contact_id": 1842,
        "status": "success",
        "http_status": 200,
        "submitted_visible_fields": {
          "order_number": "NW-1048"
        },
        "mapped_response": {
          "Status": "Dispatched"
        },
        "error_message": null,
        "created_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 25,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Email

API events for email.

email.mailboxes.list

Emailread

Returns active workspace email mailboxes and the IDs accepted by email.messages.send.

Request data
FieldTypeRequirementMeaning
sending_enabledbooleanOptionalFilter by sending capability; use true to discover mailboxes available for outbound email.
receiving_enabledbooleanOptionalFilter by receiving capability.
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 100.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
email.mailboxes.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"email.mailboxes.list","data":{"sending_enabled":true,"limit":50,"offset":0}}'
Representative response
email.mailboxes.list response
{
  "ok": true,
  "event": "email.mailboxes.list",
  "data": {
    "mailboxes": [
      {
        "id": "7b8c9259-cdc4-42d6-a15a-4a436d3e0e78",
        "address": "support@northwind.example",
        "display_name": "Northwind Support",
        "provider": "resend",
        "sending_enabled": true,
        "receiving_enabled": true,
        "created_at": "2026-08-15T09:20:00.000Z",
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

email.tickets.list

Emailread

Returns shared-email tickets in newest-activity order, optionally filtered by lifecycle status or owner type.

Request data
FieldTypeRequirementMeaning
statusstringOptionalTicket status such as open, resolved, archived, or spam.
owner_kindstringOptionalOwner type such as unassigned, category, human, or ai_agent.
limitintegerOptionalNumber of records to return. Defaults to 25; maximum 50.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
email.tickets.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"email.tickets.list","data":{"status":"open","owner_kind":"human","limit":25,"offset":0}}'
Representative response
email.tickets.list response
{
  "ok": true,
  "event": "email.tickets.list",
  "data": {
    "tickets": [
      {
        "id": "4571db17-bf1a-49e4-82bc-b3200f73b587",
        "mailbox_id": "7b8c9259-cdc4-42d6-a15a-4a436d3e0e78",
        "subject": "Order NW-1048",
        "status": "open",
        "owner_kind": "human",
        "owner_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
        "last_message_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "total": 1,
    "pagination": {
      "limit": 25,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

email.tickets.get

Emailread

Returns one email ticket and a chronological page of its messages.

Request data
FieldTypeRequirementMeaning
ticket_idUUIDRequiredEmail ticket to retrieve.
limitintegerOptionalNumber of records to return. Defaults to 25; maximum 50.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
email.tickets.get request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"email.tickets.get","data":{"ticket_id":"4571db17-bf1a-49e4-82bc-b3200f73b587","limit":25,"offset":0}}'
Representative response
email.tickets.get response
{
  "ok": true,
  "event": "email.tickets.get",
  "data": {
    "ticket": {
      "id": "4571db17-bf1a-49e4-82bc-b3200f73b587",
      "mailbox_id": "7b8c9259-cdc4-42d6-a15a-4a436d3e0e78",
      "subject": "Order NW-1048",
      "status": "open",
      "owner_kind": "human",
      "owner_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "last_message_at": "2026-08-15T10:05:00.000Z"
    },
    "messages": [
      {
        "id": "msg_01J5TQ9B2JH7",
        "conversation_id": "4571db17-bf1a-49e4-82bc-b3200f73b587",
        "direction": "outbound",
        "subject": "Re: Order NW-1048",
        "text_body": "Your order has been dispatched.",
        "delivery_status": "accepted",
        "created_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "pagination": {
      "limit": 25,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

email.tickets.status

Emailwrite

Changes an email ticket's lifecycle status.

Request data
FieldTypeRequirementMeaning
ticket_idUUIDRequiredEmail ticket to update.
statusstringRequiredOne of open, resolved, archived, or spam.
Example request
email.tickets.status request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"email.tickets.status","data":{"ticket_id":"4571db17-bf1a-49e4-82bc-b3200f73b587","status":"resolved"}}'
Representative response
email.tickets.status response
{
  "ok": true,
  "event": "email.tickets.status",
  "data": {
    "ticket": {
      "id": "4571db17-bf1a-49e4-82bc-b3200f73b587",
      "mailbox_id": "7b8c9259-cdc4-42d6-a15a-4a436d3e0e78",
      "subject": "Order NW-1048",
      "status": "resolved",
      "owner_kind": "human",
      "owner_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "last_message_at": "2026-08-15T10:05:00.000Z",
      "resolved_at": "2026-08-15T10:05:00.000Z",
      "resolved_by": "40e621d5-4504-4df7-8d2d-e90887df7c74"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

email.tickets.assign

Emailwrite

Assigns an email ticket to an unassigned state, category, human member, or AI agent.

Request data
FieldTypeRequirementMeaning
ticket_idUUIDRequiredEmail ticket to assign.
owner_kindstringRequiredOne of unassigned, category, human, or ai_agent.
target_idUUIDOptionalRequired when owner_kind is human or ai_agent.
category_keystringOptionalCategory key when assigning to a category.
category_namestringOptionalCategory name when assigning to a category.
ai_modestringOptionalAI assignment behavior; defaults to wait.
Example request
email.tickets.assign request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"email.tickets.assign","data":{"ticket_id":"4571db17-bf1a-49e4-82bc-b3200f73b587","owner_kind":"human","target_id":"40e621d5-4504-4df7-8d2d-e90887df7c74"}}'
Representative response
email.tickets.assign response
{
  "ok": true,
  "event": "email.tickets.assign",
  "data": {
    "assignment": {
      "owner_kind": "human",
      "target_id": "40e621d5-4504-4df7-8d2d-e90887df7c74"
    },
    "ticket": {
      "id": "4571db17-bf1a-49e4-82bc-b3200f73b587",
      "mailbox_id": "7b8c9259-cdc4-42d6-a15a-4a436d3e0e78",
      "subject": "Order NW-1048",
      "status": "open",
      "owner_kind": "human",
      "owner_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "last_message_at": "2026-08-15T10:05:00.000Z"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

email.notes.create

Emailwrite

Adds an internal note to an email ticket under the service account identity.

Request data
FieldTypeRequirementMeaning
ticket_idUUIDRequiredEmail ticket to annotate.
notestringRequiredInternal note text, up to 50,000 characters.
Example request
email.notes.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"email.notes.create","data":{"ticket_id":"4571db17-bf1a-49e4-82bc-b3200f73b587","note":"Customer confirmed the replacement address."}}'
Representative response
email.notes.create response
{
  "ok": true,
  "event": "email.notes.create",
  "data": {
    "note": {
      "id": "msg_01J5TQ9B2JH7",
      "conversation_id": "4571db17-bf1a-49e4-82bc-b3200f73b587",
      "direction": "internal",
      "subject": "Re: Order NW-1048",
      "text_body": "Customer confirmed the replacement address.",
      "delivery_status": "received",
      "created_at": "2026-08-15T10:05:00.000Z",
      "event_kind": "note"
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

email.messages.send

Emailwrite

Sends a new email or replies to an existing email ticket through a connected workspace mailbox.

Request data
FieldTypeRequirementMeaning
mailbox_idUUIDRequiredConnected mailbox used to send.
text_body or html_bodystringRequiredSupply a text body, HTML body, or both.
ticket_idUUIDOptionalExisting ticket to reply to.
tostring or arrayOptionalRecipient address or addresses for a new email.
subjectstringOptionalSubject for a new email, up to 998 characters.
text_bodystringOptionalPlain-text body, up to 200,000 characters.
html_bodystringOptionalHTML body, up to 500,000 characters.
ccarrayOptionalCC recipients.
bccarrayOptionalBCC recipients.
attachmentsarrayOptionalUp to 20 uploaded attachment descriptors.
Example request
email.messages.send request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"email.messages.send","data":{"mailbox_id":"7b8c9259-cdc4-42d6-a15a-4a436d3e0e78","ticket_id":"4571db17-bf1a-49e4-82bc-b3200f73b587","text_body":"Your order has been dispatched."}}'
Representative response
email.messages.send response
{
  "ok": true,
  "event": "email.messages.send",
  "data": {
    "sent": true,
    "message_id": "msg_01J5TQ9B2JH7",
    "ticket_id": "4571db17-bf1a-49e4-82bc-b3200f73b587",
    "delivery_status": "accepted"
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

email_attachments.upload.create

Emailwrite

Creates a one-hour signed upload destination for an outbound email attachment.

Request data
FieldTypeRequirementMeaning
filenamestringRequiredOriginal filename.
mime_typestringRequiredFile media type.
byte_sizeintegerRequiredFile size from 1 byte through 25 MB.
Example request
email_attachments.upload.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"email_attachments.upload.create","data":{"filename":"invoice.pdf","mime_type":"application/pdf","byte_size":248302}}'
Representative response
email_attachments.upload.create response
{
  "ok": true,
  "event": "email_attachments.upload.create",
  "data": {
    "upload": {
      "bucket": "email-attachments",
      "path": "9d2f6a63-20cd-4d42-a87e-52ebc21a9b41/outbound/40e621d5-4504-4df7-8d2d-e90887df7c74/invoice.pdf",
      "token": "signed-upload-token",
      "upload_url": "https://storage.example/upload/email-attachments/invoice.pdf",
      "filename": "invoice.pdf",
      "content_type": "application/pdf",
      "byte_size": 248302,
      "expires_in": 3600
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

email_attachments.download.create

Emailread

Creates a 15-minute signed download URL for a clean email attachment.

Request data
FieldTypeRequirementMeaning
attachment_idUUIDRequiredEmail attachment to download.
Example request
email_attachments.download.create request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"email_attachments.download.create","data":{"attachment_id":"832ee0e9-a6c7-46ec-9687-3e5241702531"}}'
Representative response
email_attachments.download.create response
{
  "ok": true,
  "event": "email_attachments.download.create",
  "data": {
    "download": {
      "url": "https://storage.example/download/invoice.pdf?token=signed-download-token",
      "expires_in": 900,
      "filename": "invoice.pdf",
      "mime_type": "application/pdf",
      "byte_size": 248302
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

WhatsApp

API events for whatsapp.

whatsapp.numbers.list

WhatsAppread

Returns connected WhatsApp sender numbers available to the workspace.

Request data

This event takes no fields in data. Send an empty object.

Example request
whatsapp.numbers.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"whatsapp.numbers.list","data":{}}'
Representative response
whatsapp.numbers.list response
{
  "ok": true,
  "event": "whatsapp.numbers.list",
  "data": {
    "numbers": [
      {
        "id": "number_01J5V2",
        "agent_id": "f6bc46a6-5ec8-4bba-88c7-f1237532e72a",
        "waba_id": "112233445566778",
        "phone_number_id": "998877665544332",
        "display_phone_number": "+962 7 9000 0000",
        "verified_name": "Northwind Support"
      }
    ]
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

whatsapp.templates.list

WhatsAppread

Returns approved WhatsApp templates and the body parameters required by the supported send event.

Request data
FieldTypeRequirementMeaning
languagestringOptionalExact template language code such as en_US.
categorystringOptionalTemplate category filter.
limitintegerOptionalNumber of templates to return. Defaults to 50; maximum 100.
Example request
whatsapp.templates.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"whatsapp.templates.list","data":{"language":"en_US","category":"UTILITY","limit":50}}'
Representative response
whatsapp.templates.list response
{
  "ok": true,
  "event": "whatsapp.templates.list",
  "data": {
    "templates": [
      {
        "id": "template_01J5V4",
        "name": "order_update",
        "language": "en_US",
        "status": "APPROVED",
        "category": "UTILITY",
        "components": [
          {
            "type": "BODY",
            "text": "Order {{1}} is now {{2}}."
          }
        ],
        "parameter_count": 2,
        "parameter_format": "positional",
        "parameter_names": [
          "1",
          "2"
        ],
        "supported": true,
        "unsupported_reason": ""
      }
    ],
    "pagination": {
      "limit": 50,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

whatsapp.messages.send_template

WhatsAppwrite

Starts or continues an outbound WhatsApp conversation by sending an approved, supported template.

Request data
FieldTypeRequirementMeaning
phone_number_idstringRequiredConnected WhatsApp sender number ID.
recipient_phonestringRequiredRecipient in international format, without a leading plus after normalization.
template_namestringRequiredApproved template name.
language_codestringRequiredApproved template language code.
parametersarrayOptionalExact body parameter values required by the template, in order or as name/value objects.
customer_namestringOptionalName used when a new Inbox contact or conversation is created.
Example request
whatsapp.messages.send_template request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: 10f84a56-8952-4e14-96b8-a1286c304dd4" \
+  -d '{"event":"whatsapp.messages.send_template","data":{"phone_number_id":"998877665544332","recipient_phone":"+962790000000","template_name":"order_update","language_code":"en_US","parameters":["NW-1048","dispatched"],"customer_name":"Amina Saleh"}}'
Representative response
whatsapp.messages.send_template response
{
  "ok": true,
  "event": "whatsapp.messages.send_template",
  "data": {
    "conversation": {
      "id": 4012,
      "status": "active",
      "chat_source": "whatsapp",
      "contact_id": 1842,
      "assigned_human_agent_user_id": "40e621d5-4504-4df7-8d2d-e90887df7c74",
      "subject": "Delivery address update",
      "created_at": "2026-08-15T09:20:00.000Z",
      "updated_at": "2026-08-15T10:05:00.000Z"
    },
    "message": {
      "id": 9124,
      "conversation_id": 4012,
      "sender_type": "human_agent",
      "source": "whatsapp",
      "result": "[Template] order_update",
      "created_at": "2026-08-15T10:05:00.000Z"
    },
    "whatsapp_message_id": "wamid.HBgMOTYyNzkwMDAwMDAwFQIAERgS",
    "conversation_created": true,
    "reused_existing_conversation": false,
    "delivery_status": "accepted"
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Analytics

API events for analytics.

analytics.summary

Analyticsread

Returns workspace-wide operational counts suitable for a lightweight external dashboard.

Request data

This event takes no fields in data. Send an empty object.

Example request
analytics.summary request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"analytics.summary","data":{}}'
Representative response
analytics.summary response
{
  "ok": true,
  "event": "analytics.summary",
  "data": {
    "summary": {
      "contacts": 1842,
      "conversations": 731,
      "open_conversations": 28,
      "open_tasks": 14,
      "open_email_tickets": 9
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

AI dashboard

API events for ai dashboard.

dashboard.ai_agents.list

AI dashboardread

Returns AI Agents configured for the workspace with lightweight status and timestamps.

Request data
FieldTypeRequirementMeaning
limitintegerOptionalNumber of records to return. Defaults to 50; maximum 200.
offsetintegerOptionalNumber of records to skip. Defaults to 0.
Example request
dashboard.ai_agents.list request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"dashboard.ai_agents.list","data":{"limit":50,"offset":0}}'
Representative response
dashboard.ai_agents.list response
{
  "ok": true,
  "event": "dashboard.ai_agents.list",
  "data": {
    "agents": [
      {
        "id": "f6bc46a6-5ec8-4bba-88c7-f1237532e72a",
        "name": "Support Agent",
        "active": true,
        "created_at": "2026-08-15T09:20:00.000Z",
        "updated_at": "2026-08-15T10:05:00.000Z"
      }
    ],
    "pagination": {
      "limit": 50,
      "offset": 0,
      "returned": 1
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

dashboard.ai_credits.get

AI dashboardread

Returns current AI credit usage, included allowance, purchased credits, total availability, and remaining balance.

Request data

This event takes no fields in data. Send an empty object.

Example request
dashboard.ai_credits.get request
curl -X POST https://api.mitsolab.com/api/v1 \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"event":"dashboard.ai_credits.get","data":{}}'
Representative response
dashboard.ai_credits.get response
{
  "ok": true,
  "event": "dashboard.ai_credits.get",
  "data": {
    "credits": {
      "used": 1830,
      "included": 5000,
      "extra": 1000,
      "total_available": 6000,
      "remaining": 4170
    }
  },
  "request_id": "dd083521-3922-4e50-bb2c-2b5f1ee8ccdb"
}

Webhooks

Send signed, near-real-time workspace events to one HTTPS endpoint. This section is the canonical setup and event reference.

Create the endpoint

  1. Build a public HTTPS POST endpoint that can read the exact raw request body, return a 2xx response quickly, and process duplicate event IDs safely.
  2. In Console, open Webhooks, turn on Enabled, enter the endpoint URL, and choose Save and generate secret. The signing secret is displayed once; store it server-side.
  3. Select only events your application handles. Use the Save button above or below the subscription list. The selected count beside the top button confirms the intended scope.
  4. Send a real qualifying action in a test workspace and verify signature, event type, event ID, and payload before enabling production automation.
  5. Rotate the secret if exposed. Update the receiver before completing the rotation so valid requests are not rejected.

After an endpoint exists, the Enabled switch saves immediately. Turning it off stops new deliveries and locks subscription controls without requiring an inaccessible second save button.

Delivery behavior and limits

Mitsolab sends each subscribed event as an HTTPS POST after the related workspace action completes. Your endpoint should validate the signature, store the event durably, and return a successful 2xx response promptly. Perform database updates, provider calls, and other longer work after acknowledgement.

  • Destination: use the final public HTTPS URL. Redirect responses are not followed.
  • Success: any 2xx response confirms that your receiver accepted the event.
  • Duplicate safety: treat X-MitsoLab-Event-Id and the envelope id as the delivery identifier. Store it with a unique constraint so processing the same event more than once cannot repeat a business action.
  • Ordering: use each object's timestamps and current state instead of assuming that different event types will always be processed in a particular order.
  • Payload version: read api_version from the envelope and ignore fields your integration does not use, allowing compatible fields to be added over time.

Headers, envelope, and signature

HeaderValue
X-MitsoLab-EventExact case-sensitive event name
X-MitsoLab-Event-IdUnique event envelope ID
X-MitsoLab-TimestampUnix timestamp used by the signature
X-MitsoLab-Signaturev1=<lowercase SHA-256 hex>
Content-Typeapplication/json

Compute HMAC-SHA256 over timestamp + "." + exactRawBody using the generated signing secret. Reject stale timestamps according to your threat model, compare signatures in constant time, and deduplicate by event ID.

Node.js verification
import crypto from "node:crypto";

export function verifyMitsoLabWebhook(rawBody, headers, secret) {
  const timestamp = headers["x-mitsolab-timestamp"];
  const received = headers["x-mitsolab-signature"] || "";
  if (!timestamp || !received.startsWith("v1=")) return false;

  const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!Number.isFinite(ageSeconds) || ageSeconds > 300) return false;

  const expected = "v1=" + crypto
    .createHmac("sha256", secret)
    .update(timestamp + "." + rawBody)
    .digest("hex");

  const a = Buffer.from(received);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python verification
import hashlib
import hmac
import time

def verify_mitsolab_webhook(raw_body: bytes, headers: dict, secret: str) -> bool:
    timestamp = headers.get("X-MitsoLab-Timestamp", "")
    received = headers.get("X-MitsoLab-Signature", "")
    if not timestamp or not received.startswith("v1="):
        return False
    if abs(time.time() - int(timestamp)) > 300:
        return False
    signed = timestamp.encode() + b"." + raw_body
    expected = "v1=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(received, expected)

Production receiver pattern

Express receiver
app.post("/webhooks/mitsolab", express.raw({ type: "application/json" }), async (req, res) => {
  const rawBody = req.body.toString("utf8");
  if (!verifyMitsoLabWebhook(rawBody, req.headers, process.env.MITSOLAB_WEBHOOK_SECRET)) {
    return res.status(401).send("invalid signature");
  }

  const event = JSON.parse(rawBody);
  const accepted = await enqueueIfNew(event.id, event); // unique index on event.id
  res.status(accepted ? 202 : 200).send("accepted");
});

Do not parse and re-serialize JSON before verifying it. Even harmless whitespace changes alter the signature. Acknowledge after your own durable queue accepts the event, then run slow integrations asynchronously.

All 44 events

Names are case-sensitive. Subscribe to the exact value shown. Every example below contains the complete common envelope and a representative data.object.

Conversation.created

Inbox

A new channel conversation is stored.

Conversation.created example
{
  "id": "evt_01K001",
  "type": "Conversation.created",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "conv_01JZ4N7",
      "status": "open",
      "channel": "whatsapp",
      "customer_name": "Amina Saleh",
      "country": "JO",
      "contact_id": "contact_01JZ4",
      "assigned_human_agent_user_id": null,
      "ai_agent_id": null,
      "created_at": "2026-08-13T09:14:22.153Z",
      "updated_at": "2026-08-13T09:14:22.153Z",
      "resolved_at": null
    }
  }
}

Conversation.assigned

Inbox

Conversation ownership changes to a human agent, AI Agent, or supported route.

Conversation.assigned example
{
  "id": "evt_01K002",
  "type": "Conversation.assigned",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "conv_01JZ4N7",
      "status": "open",
      "channel": "whatsapp",
      "assigned_human_agent_user_id": "user_01JQ8",
      "ai_agent_id": null,
      "updated_at": "2026-08-13T09:15:03.911Z"
    }
  }
}

Conversation.ai_handling_started

Inbox

An AI Agent begins handling the conversation.

Conversation.ai_handling_started example
{
  "id": "evt_01K003",
  "type": "Conversation.ai_handling_started",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "conv_01JZ4N7",
      "ai_agent_id": "agent_01JYZ",
      "ai_agent_name": "Support Agent",
      "started_at": "2026-08-13T09:15:06.004Z"
    }
  }
}

Conversation.resolved

Inbox

The conversation is marked resolved.

Conversation.resolved example
{
  "id": "evt_01K004",
  "type": "Conversation.resolved",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "conv_01JZ4N7",
      "status": "resolved",
      "resolved_by_user_id": "user_01JQ8",
      "resolved_at": "2026-08-13T09:38:17.522Z"
    }
  }
}

Conversation.message.received

Inbox

A customer message is received.

Conversation.message.received example
{
  "id": "evt_01K005",
  "type": "Conversation.message.received",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "msg_01JZ51",
      "conversation_id": "conv_01JZ4N7",
      "direction": "inbound",
      "sender_type": "customer",
      "text": "Can I change my delivery address?",
      "channel": "whatsapp",
      "ai_handled": false,
      "ai_agent_name": null,
      "created_at": "2026-08-13T09:14:22.153Z"
    }
  }
}

Conversation.message.sent

Inbox

A human or AI reply is accepted for sending.

Conversation.message.sent example
{
  "id": "evt_01K006",
  "type": "Conversation.message.sent",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "msg_01JZ52",
      "conversation_id": "conv_01JZ4N7",
      "direction": "outbound",
      "sender_type": "human_agent",
      "text": "Yes. Please send the new address.",
      "channel": "whatsapp",
      "ai_handled": false,
      "ai_agent_name": null,
      "created_at": "2026-08-13T09:16:40.102Z"
    }
  }
}

Conversation.message.failed

Inbox

An outbound conversation message cannot be delivered.

Conversation.message.failed example
{
  "id": "evt_01K007",
  "type": "Conversation.message.failed",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "msg_01JZ52",
      "conversation_id": "conv_01JZ4N7",
      "direction": "outbound",
      "channel": "whatsapp",
      "failure_code": "provider_rejected",
      "failure_message": "The provider rejected the message.",
      "created_at": "2026-08-13T09:16:42.551Z"
    }
  }
}

Conversation.note.created

Inbox

An internal note is added to a conversation.

Conversation.note.created example
{
  "id": "evt_01K008",
  "type": "Conversation.note.created",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "note_01JZ54",
      "conversation_id": "conv_01JZ4N7",
      "author_user_id": "user_01JQ8",
      "body": "Customer verified the order number.",
      "created_at": "2026-08-13T09:18:11.771Z"
    }
  }
}

Conversation.window_ended

Inbox

The channel's customer-service reply window ends.

Conversation.window_ended example
{
  "id": "evt_01K009",
  "type": "Conversation.window_ended",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "conv_01JZ4N7",
      "channel": "whatsapp",
      "last_customer_message_id": "msg_01JZ51",
      "window_ended_at": "2026-08-14T09:14:22.153Z"
    }
  }
}

Agent.shift.started

Human Agents

A human agent starts a Portal shift.

Agent.shift.started example
{
  "id": "evt_01K010",
  "type": "Agent.shift.started",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "activity_01JZ60",
      "agent_id": "human_01JQ8",
      "human_agent_user_id": "user_01JQ8",
      "shift_id": "shift_01JZ6",
      "created_at": "2026-08-13T06:00:00.000Z"
    }
  }
}

Agent.shift.ended

Human Agents

A human agent ends a Portal shift.

Agent.shift.ended example
{
  "id": "evt_01K011",
  "type": "Agent.shift.ended",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "activity_01JZ61",
      "agent_id": "human_01JQ8",
      "human_agent_user_id": "user_01JQ8",
      "shift_id": "shift_01JZ6",
      "created_at": "2026-08-13T14:00:00.000Z"
    }
  }
}

Agent.break.started

Human Agents

A human agent starts a break during a shift.

Agent.break.started example
{
  "id": "evt_01K012",
  "type": "Agent.break.started",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "activity_01JZ62",
      "agent_id": "human_01JQ8",
      "human_agent_user_id": "user_01JQ8",
      "shift_id": "shift_01JZ6",
      "created_at": "2026-08-13T10:20:00.000Z"
    }
  }
}

Agent.break.ended

Human Agents

A human agent ends a break.

Agent.break.ended example
{
  "id": "evt_01K013",
  "type": "Agent.break.ended",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "activity_01JZ63",
      "agent_id": "human_01JQ8",
      "human_agent_user_id": "user_01JQ8",
      "shift_id": "shift_01JZ6",
      "created_at": "2026-08-13T10:35:00.000Z"
    }
  }
}

Contact.created

Contacts

A workspace contact is created.

Contact.created example
{
  "id": "evt_01K014",
  "type": "Contact.created",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "contact_01JZ4",
      "customer_name": "Amina Saleh",
      "email": "amina@example.com",
      "phone_number": "+962790000000",
      "country": "JO",
      "gender": "female",
      "age": 31,
      "profile_handle": null,
      "external_id": "customer_1842",
      "tags": [
        "customer"
      ],
      "created_at": "2026-08-13T09:14:22.100Z",
      "updated_at": "2026-08-13T09:14:22.100Z"
    }
  }
}

Contact.updated

Contacts

One or more contact fields change.

Contact.updated example
{
  "id": "evt_01K015",
  "type": "Contact.updated",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "contact_01JZ4",
      "customer_name": "Amina Saleh",
      "email": "amina@example.com",
      "country": "JO",
      "tags": [
        "customer",
        "priority"
      ],
      "updated_at": "2026-08-13T10:02:18.410Z"
    }
  }
}

Contact.deleted

Contacts

A contact is deleted.

Contact.deleted example
{
  "id": "evt_01K016",
  "type": "Contact.deleted",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "contact_01JZ4",
      "deleted_at": "2026-08-13T11:25:02.008Z"
    }
  }
}

Contact.merged

Contacts

Duplicate contacts are merged into a primary contact.

Contact.merged example
{
  "id": "evt_01K017",
  "type": "Contact.merged",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "primary_contact_id": "contact_01JZ4",
      "merged_contact_ids": [
        "contact_01JZ3",
        "contact_01JZ2"
      ],
      "merged_at": "2026-08-13T11:02:09.340Z"
    }
  }
}

Contact.note.created

Contacts

A note is added to a contact.

Contact.note.created example
{
  "id": "evt_01K018",
  "type": "Contact.note.created",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "cnote_01JZ7",
      "contact_id": "contact_01JZ4",
      "body": "Prefers delivery after 4 PM.",
      "author_user_id": "user_01JQ8",
      "created_at": "2026-08-13T10:11:15.090Z",
      "updated_at": "2026-08-13T10:11:15.090Z"
    }
  }
}

Contact.note.updated

Contacts

A contact note is edited.

Contact.note.updated example
{
  "id": "evt_01K019",
  "type": "Contact.note.updated",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "cnote_01JZ7",
      "contact_id": "contact_01JZ4",
      "body": "Prefers weekday delivery after 4 PM.",
      "author_user_id": "user_01JQ8",
      "updated_at": "2026-08-13T10:15:22.610Z"
    }
  }
}

Contact.note.deleted

Contacts

A contact note is deleted.

Contact.note.deleted example
{
  "id": "evt_01K020",
  "type": "Contact.note.deleted",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "cnote_01JZ7",
      "contact_id": "contact_01JZ4",
      "deleted_at": "2026-08-13T10:18:02.430Z"
    }
  }
}

Contact.organization.linked

Contacts

A contact is linked to an organization.

Contact.organization.linked example
{
  "id": "evt_01K021",
  "type": "Contact.organization.linked",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "contact_id": "contact_01JZ4",
      "organization_id": "org_01JZ8",
      "relationship": "employee",
      "linked_at": "2026-08-13T10:30:00.000Z"
    }
  }
}

Contact.organization.updated

Contacts

The contact-to-organization relationship changes.

Contact.organization.updated example
{
  "id": "evt_01K022",
  "type": "Contact.organization.updated",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "contact_id": "contact_01JZ4",
      "organization_id": "org_01JZ8",
      "relationship": "billing_contact",
      "updated_at": "2026-08-13T10:32:00.000Z"
    }
  }
}

Contact.organization.unlinked

Contacts

A contact is unlinked from an organization.

Contact.organization.unlinked example
{
  "id": "evt_01K023",
  "type": "Contact.organization.unlinked",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "contact_id": "contact_01JZ4",
      "organization_id": "org_01JZ8",
      "unlinked_at": "2026-08-13T10:34:00.000Z"
    }
  }
}

Pipeline.created

CRM Pipelines

A CRM pipeline is created.

Pipeline.created example
{
  "id": "evt_01K024",
  "type": "Pipeline.created",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "pipe_01JZA",
      "name": "Sales",
      "created_by": "user_01JQ8",
      "created_at": "2026-08-13T08:00:00.000Z",
      "updated_at": "2026-08-13T08:00:00.000Z"
    }
  }
}

Pipeline.updated

CRM Pipelines

Pipeline metadata or stages change.

Pipeline.updated example
{
  "id": "evt_01K025",
  "type": "Pipeline.updated",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "pipe_01JZA",
      "name": "Enterprise Sales",
      "updated_by": "user_01JQ8",
      "updated_at": "2026-08-13T08:10:00.000Z"
    }
  }
}

Pipeline.deleted

CRM Pipelines

A CRM pipeline is deleted.

Pipeline.deleted example
{
  "id": "evt_01K026",
  "type": "Pipeline.deleted",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "pipe_01JZA",
      "deleted_by": "user_01JQ8",
      "deleted_at": "2026-08-13T08:20:00.000Z"
    }
  }
}

Pipeline.card.created

CRM Pipelines

A contact or note card is added to a pipeline stage.

Pipeline.card.created example
{
  "id": "evt_01K027",
  "type": "Pipeline.card.created",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "card_01JZB",
      "kind": "contact",
      "contact_id": "contact_01JZ4",
      "pipeline_id": "pipe_01JZA",
      "stage_id": "stage_qualified",
      "position": 3,
      "created_at": "2026-08-13T08:30:00.000Z",
      "updated_at": "2026-08-13T08:30:00.000Z"
    }
  }
}

Pipeline.card.updated

CRM Pipelines

A pipeline card's content or metadata changes.

Pipeline.card.updated example
{
  "id": "evt_01K028",
  "type": "Pipeline.card.updated",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "card_01JZB",
      "kind": "note",
      "note": "Decision expected Friday",
      "pipeline_id": "pipe_01JZA",
      "stage_id": "stage_qualified",
      "position": 3,
      "created_by": "user_01JQ8",
      "updated_at": "2026-08-13T08:35:00.000Z"
    }
  }
}

Pipeline.card.deleted

CRM Pipelines

A pipeline card is deleted.

Pipeline.card.deleted example
{
  "id": "evt_01K029",
  "type": "Pipeline.card.deleted",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "card_01JZB",
      "pipeline_id": "pipe_01JZA",
      "stage_id": "stage_qualified",
      "deleted_at": "2026-08-13T08:40:00.000Z"
    }
  }
}

Pipeline.card.moved

CRM Pipelines

A card moves to another stage or position.

Pipeline.card.moved example
{
  "id": "evt_01K030",
  "type": "Pipeline.card.moved",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "card_01JZB",
      "pipeline_id": "pipe_01JZA",
      "from_stage_id": "stage_qualified",
      "to_stage_id": "stage_proposal",
      "from_position": 3,
      "to_position": 1,
      "moved_at": "2026-08-13T08:45:00.000Z"
    }
  }
}

Task.created

Tasks

A Portal task is created.

Task.created example
{
  "id": "evt_01K031",
  "type": "Task.created",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "task_01JZC",
      "title": "Confirm delivery address",
      "description": "Call before dispatch",
      "status": "todo",
      "priority": "high",
      "assignee_user_id": "user_01JQ8",
      "creator_user_id": "user_01JQ9",
      "finish_note": null,
      "finished_at": null,
      "created_at": "2026-08-13T09:20:00.000Z",
      "updated_at": "2026-08-13T09:20:00.000Z"
    }
  }
}

Task.updated

Tasks

Task fields or completion state change.

Task.updated example
{
  "id": "evt_01K032",
  "type": "Task.updated",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "task_01JZC",
      "title": "Confirm delivery address",
      "status": "done",
      "priority": "high",
      "assignee_user_id": "user_01JQ8",
      "finish_note": "Address confirmed",
      "finished_at": "2026-08-13T10:00:00.000Z",
      "updated_at": "2026-08-13T10:00:00.000Z"
    }
  }
}

Task.contact.linked

Tasks

A contact is linked to a task.

Task.contact.linked example
{
  "id": "evt_01K033",
  "type": "Task.contact.linked",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "task_id": "task_01JZC",
      "contact_id": "contact_01JZ4",
      "created_at": "2026-08-13T09:21:00.000Z"
    }
  }
}

Task.contact.unlinked

Tasks

A contact is removed from a task.

Task.contact.unlinked example
{
  "id": "evt_01K034",
  "type": "Task.contact.unlinked",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "task_id": "task_01JZC",
      "contact_id": "contact_01JZ4",
      "unlinked_at": "2026-08-13T10:01:00.000Z"
    }
  }
}

Email.ticket.created

Emails

A shared-email ticket is created.

Email.ticket.created example
{
  "id": "evt_01K035",
  "type": "Email.ticket.created",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "ticket_01JZD",
      "mailbox_id": "mailbox_support",
      "subject": "Invoice correction",
      "customer_name": "Amina Saleh",
      "customer_email": "amina@example.com",
      "contact_id": "contact_01JZ4",
      "status": "open",
      "priority": "normal",
      "owner_kind": "portal",
      "assigned_category_key": "billing",
      "assigned_human_user_id": null,
      "assigned_ai_agent_id": null,
      "created_at": "2026-08-13T12:00:00.000Z",
      "updated_at": "2026-08-13T12:00:00.000Z",
      "resolved_at": null,
      "archived_at": null
    }
  }
}

Email.ticket.assigned

Emails

Email ticket ownership changes.

Email.ticket.assigned example
{
  "id": "evt_01K036",
  "type": "Email.ticket.assigned",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "ticket_01JZD",
      "owner_kind": "human",
      "assigned_category_key": "billing",
      "assigned_human_user_id": "user_01JQ8",
      "assigned_ai_agent_id": null,
      "updated_at": "2026-08-13T12:02:00.000Z"
    }
  }
}

Email.ticket.resolved

Emails

An email ticket is resolved.

Email.ticket.resolved example
{
  "id": "evt_01K037",
  "type": "Email.ticket.resolved",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "ticket_01JZD",
      "status": "resolved",
      "resolved_at": "2026-08-13T12:30:00.000Z",
      "updated_at": "2026-08-13T12:30:00.000Z"
    }
  }
}

Email.ticket.reopened

Emails

A resolved email ticket returns to open state.

Email.ticket.reopened example
{
  "id": "evt_01K038",
  "type": "Email.ticket.reopened",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "ticket_01JZD",
      "status": "open",
      "reopened_at": "2026-08-13T12:35:00.000Z",
      "updated_at": "2026-08-13T12:35:00.000Z"
    }
  }
}

Email.ticket.archived

Emails

An email ticket is archived.

Email.ticket.archived example
{
  "id": "evt_01K039",
  "type": "Email.ticket.archived",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "ticket_01JZD",
      "status": "archived",
      "archived_at": "2026-08-13T13:00:00.000Z",
      "updated_at": "2026-08-13T13:00:00.000Z"
    }
  }
}

Email.message.received

Emails

An inbound email message is stored.

Email.message.received example
{
  "id": "evt_01K040",
  "type": "Email.message.received",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "emailmsg_01JZE",
      "conversation_id": "ticket_01JZD",
      "mailbox_id": "mailbox_support",
      "direction": "inbound",
      "author_kind": "contact",
      "author_user_id": null,
      "author_agent_id": null,
      "author_name": "Amina Saleh",
      "from_address": "amina@example.com",
      "subject": "Invoice correction",
      "text": "Please update the company name.",
      "delivery_status": "received",
      "provider_message_id": "provider_8492",
      "created_at": "2026-08-13T12:00:00.000Z",
      "sent_at": null,
      "delivered_at": null
    }
  }
}

Email.message.send

Emails

An outbound email is submitted for sending. The exact event name uses send, not sent.

Email.message.send example
{
  "id": "evt_01K041",
  "type": "Email.message.send",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "emailmsg_01JZF",
      "conversation_id": "ticket_01JZD",
      "mailbox_id": "mailbox_support",
      "direction": "outbound",
      "author_kind": "human",
      "author_user_id": "user_01JQ8",
      "author_agent_id": null,
      "author_name": "Omar",
      "from_address": "support@mail.example.com",
      "subject": "Re: Invoice correction",
      "text": "We have updated the invoice details.",
      "delivery_status": "sent",
      "provider_message_id": "provider_8493",
      "created_at": "2026-08-13T12:20:00.000Z",
      "sent_at": "2026-08-13T12:20:01.000Z",
      "delivered_at": null
    }
  }
}

Email.message.bounced

Emails

The email provider reports an outbound message bounce.

Email.message.bounced example
{
  "id": "evt_01K042",
  "type": "Email.message.bounced",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "delivery_01JZG",
      "conversation_id": "ticket_01JZD",
      "mailbox_id": "mailbox_support",
      "provider": "resend",
      "provider_event_id": "evt_bounce_449",
      "provider_message_id": "provider_8493",
      "event_type": "bounced",
      "failure_code": "mailbox_not_found",
      "failure_message": "The destination mailbox does not exist.",
      "occurred_at": "2026-08-13T12:20:05.000Z"
    }
  }
}

Email.message.complained

Emails

The provider reports a spam complaint.

Email.message.complained example
{
  "id": "evt_01K043",
  "type": "Email.message.complained",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "delivery_01JZH",
      "conversation_id": "ticket_01JZD",
      "mailbox_id": "mailbox_support",
      "provider": "mailgun",
      "provider_event_id": "evt_complaint_72",
      "provider_message_id": "provider_8493",
      "event_type": "complained",
      "failure_code": "spam_complaint",
      "failure_message": "Recipient marked the message as spam.",
      "occurred_at": "2026-08-13T12:21:00.000Z"
    }
  }
}

Email.note.created

Emails

An internal note is added to an email ticket.

Email.note.created example
{
  "id": "evt_01K044",
  "type": "Email.note.created",
  "api_version": "2026-08-01",
  "created_at": "2026-08-13T12:34:56.789Z",
  "workspace_id": "0ed20582-e518-4d63-a03f-1f3d16027538",
  "data": {
    "object": {
      "id": "enote_01JZI",
      "conversation_id": "ticket_01JZD",
      "author_user_id": "user_01JQ8",
      "body": "Finance approved the correction.",
      "created_at": "2026-08-13T12:10:00.000Z"
    }
  }
}

Choose events by integration goal

GoalSubscribe to
Mirror Inbox activityConversation.created, Conversation.assigned, Conversation.resolved, Conversation.message.received, Conversation.message.sent, Conversation.message.failed
Track staffing presenceAgent.shift.started, Agent.shift.ended, Agent.break.started, Agent.break.ended
Synchronize CRM contactsContact.created, Contact.updated, Contact.deleted, Contact.merged and relevant organization/note events
Mirror pipeline boardPipeline.created, Pipeline.updated, Pipeline.deleted and all Pipeline.card events
Synchronize tasksTask.created, Task.updated, Task.contact.linked, Task.contact.unlinked
Monitor shared emailEmail.ticket events, Email.message.received, Email.message.send, Email.message.bounced, Email.message.complained, Email.note.created

Subscribe only to the events your receiver accepts and processes.

Durable acceptance and deduplication

Receiver storage example
create table received_mitsolab_events (
  event_id text primary key,
  event_type text not null,
  received_at timestamptz not null default now(),
  payload jsonb not null,
  processed_at timestamptz,
  processing_error text
);

Insert using the event ID as a unique key, return 2xx after durable acceptance, and process in a separate worker. Idempotent processing prevents duplicate business actions and keeps the receiver safe as integrations evolve.

Dispatch by exact event type

Event dispatcher
async function processMitsoLabEvent(event) {
  switch (event.type) {
    case "Conversation.message.received":
      return indexInboundMessage(event.data.object);
    case "Contact.updated":
      return upsertContact(event.data.object);
    case "Pipeline.card.moved":
      return moveExternalDeal(event.data.object);
    case "Email.message.bounced":
      return suppressBouncedAddress(event.data.object);
    default:
      throw new Error("Unsupported subscribed event: " + event.type);
  }
}

Keep a default failure for events that were accidentally selected but not implemented so configuration mistakes are visible during testing.

Monitor your webhook receiver

Log the event ID, type, receive time, signature result, acceptance result, processing result, and correlation identifiers. Redact message text or contact data when it is not required for operations.

Alert on signature failures, sustained absence of expected events, receiver 5xx, and queue backlog. To test after deployment, perform a known low-risk action such as creating a test contact and match its event ID through receiver acceptance and processing.

Security and operations

Apply least privilege, secret hygiene, change control, and production validation across Console.

Secrets and credentials

  • Keep Agent API keys, provider tokens, Action Tool headers, notification webhook URLs, and webhook signing secrets in server-side secret storage.
  • Never paste a private key into an agent role, note, FAQ, Dynamic Source, browser script, screenshot, or support conversation.
  • Generate separate credentials per workspace and integration so one exposure has a bounded effect.
  • Rotate first at the provider or receiver, update Console, test, and then revoke the old credential where overlap is supported.
  • Remove access for departed teammates and Portal agents promptly.

Safe change sequence

  1. Confirm the current workspace and record the existing route, credential label, or permission state.
  2. Make one bounded change. For routing, preserve a known fallback while testing.
  3. Test the exact customer path and the failure path using non-production data.
  4. Observe Console, provider, and receiver state. Confirm delivery rather than assuming a successful button click means completion.
  5. Remove superseded routes or credentials only after the replacement is proven.
  6. Document the change for operators who work in Portal.

Data minimization

Give agents and integrations only the information needed for the task. Avoid uploading duplicated policy documents, exposing internal Dynamic Source columns, returning complete third-party objects from Action Tools, or sending unrelated contact fields to external systems. Age and gender can be relevant to Copilot context when available, but external IDs and internal metadata should not be included without a concrete need.

Security launch checklist

AreaRequired check
PeopleOwners, Console teammates, Human Agents, Admin flags, and pending invitations are current.
AgentsRoles prohibit unsupported commitments and knowledge contains no credentials.
ProvidersOrganization-owned credentials use least privilege and known rotation owners.
ActionsEndpoints authenticate, authorize tenant and operation, validate every input, and return minimal data.
WebhooksRaw-body HMAC verification, timestamp tolerance, constant-time compare, durable queue, and event-ID deduplication are active.
BillingAuthorized owners and monitored payment method; auto-recharge has understood limits.
DataDocuments, notes, tables, logs, and exports contain only necessary information.
OperationsIncident owner, revocation procedure, fallback routes, and monitoring are documented.

Credential exposure response

  1. Identify the credential type, workspace, integration, likely exposure time, and systems that used it.
  2. Revoke or rotate at the authority that issued it: Mitsolab for Agent keys, provider for provider tokens, Slack/Discord for webhook URLs, your system for Action Tool credentials.
  3. Update Console or the consuming server with the replacement and run a bounded test.
  4. Inspect provider and application logs for unexpected use during the exposure window.
  5. Remove the leaked value from published pages, screenshots, logs, tickets, and repository history where possible.
  6. Record cause and prevention without copying the old or new secret into the incident note.

Plan routing continuity

Every production channel and address needs a known fallback when an AI Agent, Dispatcher, provider connection, or Human Agent category is unavailable. Test the fallback before maintenance. Route changes affect new work; separately manage open conversations and email tickets.

Troubleshooting

Diagnose configuration in dependency order: access, connection, routing, permissions, provider result, then application behavior.

AI Agent does not answer as expected

  • Save setup changes and start a new preview chat so old context does not mask the result.
  • Check that the correct agent and intelligence level are active.
  • Look for conflicting FAQs, notes, products, documents, or imported Notion pages.
  • Verify the Dynamic Source is enabled and its searchable columns allow the required filter.
  • Confirm the channel or email address routes to this agent.
  • Check remaining AI credits and whether an external action failed.

Channel or email traffic is missing

  • Confirm provider authorization, the exact page/number/bot/domain, and token validity.
  • Refresh provider state and verify required DNS or inbound webhook settings.
  • Inspect the current routing target, including fallback addresses and dispatcher targets.
  • For Meta, verify Messenger and Instagram were connected independently.
  • For LINE or Telegram approval mode, check pending identities.
  • For email, distinguish sending-domain verification from receiving enablement and test both directions.

Webhook is not received or rejected

  • Verify Enabled is on, the event is selected, and changes were saved.
  • Use the final public HTTPS URL; redirects are not followed.
  • Respond within four seconds after durably enqueueing work.
  • Read the raw body and compute HMAC over the timestamp, a period, and the exact bytes.
  • Use the current secret and compare v1= plus lowercase hex in constant time.
  • Check whether the business action actually committed the subscribed event.

Copilot is missing or cannot generate

  • Confirm workspace Copilot is enabled.
  • Check All human agents, allowed category, and the person's explicit override; Block wins.
  • Reload Portal if capability changed after Inbox was opened because permission is cached for the browser session.
  • Check included and purchased balance.
  • If generation fails, retry later; failed/refunded counts appear in analytics where applicable.

Billing change is pending

  • Wait for checkout synchronization and refresh once; do not submit the same purchase repeatedly.
  • Check the billing provider portal for payment method, invoice, and subscription state.
  • Review whether a downgrade or cancellation is scheduled for renewal rather than immediate.
  • Confirm you are an authorized workspace owner.
  • If the state remains inconsistent, contact support with workspace name, approximate time, and provider transaction reference—never a full payment credential.

Use the same diagnostic order

Start with the narrowest confirmed layer and move outward: workspace → user access → saved feature state → provider credential → provider asset → Mitsolab routing → destination permission → external receiver → end-user client. Changing several layers at once destroys the evidence that identifies the failure.

Collect useful support evidence

  • Workspace name and affected feature.
  • Approximate time with timezone.
  • Provider, channel, address, Agent, Dispatcher, or Action Tool label.
  • Expected result and actual result.
  • Relevant event ID, conversation ID, ticket ID, provider message ID, or billing transaction reference.
  • HTTP status and sanitized error text.
  • Whether the problem reproduces in a new conversation or private browser.
  • Recent configuration change before the failure.

Redact Authorization headers, API keys, webhook secrets, provider tokens, passwords, payment credentials, and unnecessary customer content.

Worked diagnostic examples

Copilot button is missing for one agent

Confirm the workspace is enabled, inspect that agent's explicit override, inspect the current category and allowed-category checkbox, then reload Portal because Inbox capability is cached for the browser session. Do not start with balance: an exhausted balance can block generation but does not explain an authorization-specific missing control.

Webhook receives contacts but not email bounces

Confirm Email.message.bounced is selected, then verify the email provider is sending bounce callbacks to Mitsolab and the outbound message has a provider message ID. The workspace webhook cannot emit a normalized bounce that Mitsolab never received from the provider.

Email arrives but nobody can reply

Open the exact address route and inspect Send separately from Receive. A category can receive the ticket while Send is Nobody. Also confirm the chosen domain and provider authorize the From address.

Glossary

Use the product's terms consistently when configuring or integrating a workspace.

Core terms

TermMeaning
WorkspaceOne organization's security, configuration, data, and billing boundary.
ConsoleAdministrative application for workspace configuration; publicly named ML Console.
PortalOperational application used by human agents for Inbox, CRM, tasks, knowledge, and tools.
Console teammateA person invited to administer the workspace in Console.
Human AgentA person with Portal workspace access who handles operational work.
AI AgentA configured autonomous conversational specialist occupying an AI slot.
AI DispatcherAn AI router that collects intake and selects human or AI destinations.
CategoryA Portal human-agent grouping used by routing and permissions.
Portal Action ToolA secure human-triggered external HTTP action available inside Portal.
Agent actionAn external action configured for autonomous use by one AI Agent.
Included balanceAllowance supplied by a plan or seat count and reset on its billing schedule.
Purchased balanceAdditional drafts or credits bought separately; current Console terms determine expiry.
Exact addressA configured email local-part and domain with explicit ownership.
Fallback addressThe All other addresses route for mail that does not match an exact configured address.
Webhook eventA signed JSON notification sent after a subscribed workspace state change.

Status language

StatusMeaning
EnabledFeature is permitted to operate; downstream credentials and routing must still be valid.
ConnectedConsole has stored or validated a provider relationship; it does not prove end-to-end message delivery.
VerifiedThe provider or Console confirmed a required property such as domain ownership at that time.
ConfiguredRequired settings are stored.
ActiveSubscription, item, slot, or record is currently usable under its rules.
InactiveRecord is retained but not occupying or using active capacity.
PendingInvitation, provider state, checkout, or operation awaits another step.
SynchronizingConsole is applying an external provider result.
PreviewUI or contract is shown for evaluation and is not a production availability promise.