# DOS.AI

**Fast, affordable AI inference and agent platform for open-source models.**

DOS AI is an inference platform that lets you run leading open-source language models through a simple, OpenAI-compatible API. Deploy AI agents with DOSClaw, protect your users with DOSafe, and route intelligently with smart model selection -- all from a single platform.

## Why DOS AI?

* **OpenAI-compatible** -- Swap your base URL and you're done. Works with the OpenAI Python SDK, Node.js SDK, LangChain, LlamaIndex, and any HTTP client.
* **Smart routing** -- Use `dos-auto` to let our 15-dimension classifier pick the best model for each request automatically.
* **Low latency** -- Models served on dedicated GPUs with optimized inference (vLLM). No cold starts, no queues.
* **Pay-as-you-go** -- Only pay for the tokens you use. Every new account gets **$5 in free credits** to get started.
* **Open-source models** -- Access the best open-source models without managing your own infrastructure.

## Quick start

Get up and running in under a minute:

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.dos.ai/v1",
    api_key="dos_sk_...",  # Get your key at app.dos.ai
)

response = client.chat.completions.create(
    model="dos-auto",  # Smart routing picks the best model
    messages=[
        {"role": "user", "content": "Explain quantum computing in one paragraph."}
    ],
)

print(response.choices[0].message.content)
```

## Platform

### LLM Inference API

OpenAI-compatible API with smart routing, streaming, function calling, and structured outputs.

| Section                                                       | Description                                                    |
| ------------------------------------------------------------- | -------------------------------------------------------------- |
| [Quickstart](/getting-started/quickstart)                     | Create an account, get an API key, and make your first request |
| [Authentication](/getting-started/authentication)             | API key management, rate limits, and security best practices   |
| [Available Models](/models/available-models)                  | Full model catalog with pricing                                |
| [OpenAI Compatibility](/getting-started/openai-compatibility) | Migration guide and compatibility details                      |

### DOSClaw Agents

Deploy AI agents powered by [OpenClaw](https://github.com/nicejoy/openclaw) with Telegram, Discord, and WhatsApp integration. Each agent runs in its own container with web search, memory, video/music generation, and 5,000+ installable skills.

* Create agents from the [dashboard](https://app.dos.ai/agents)
* Choose from templates: Personal Assistant, Sales, Customer Support, Content Creator
* Credit-based pricing with a free trial

### DOSafe

Safety and threat intelligence engine with AI detection capabilities.

| Feature                                        | Description                                                |
| ---------------------------------------------- | ---------------------------------------------------------- |
| [Entity/URL Check](/dosafe/overview)           | Risk assessment against 3.93M+ threat intelligence entries |
| [AI Text Detection](/dosafe/partner-api)       | Detect AI-generated text                                   |
| [AI Image Detection](/dosafe/partner-api)      | Detect AI-generated or manipulated images                  |
| [AI Video Detection](/dosafe/partner-api)      | 7-layer pipeline for AI video detection                    |
| [AI Audio Detection](/dosafe/partner-api)      | Detect AI-generated speech and voice clones                |
| [Face/Voice Verification](/dosafe/partner-api) | Liveness detection and biometric matching                  |

## Available models

| Model ID           | Base model                  | Context | Pricing           |
| ------------------ | --------------------------- | ------- | ----------------- |
| `dos-auto`         | Smart routing (auto-select) | varies  | varies            |
| `dos-ai`           | Qwen3.5-35B-A3B             | 128K    | $0.15 / 1M tokens |
| `llama-4-maverick` | Llama 4 Maverick 17B-128E   | 1M      | $0.17 / 1M input  |
| `llama-4-scout`    | Llama 4 Scout 17B-16E       | 640K    | $0.11 / 1M input  |
| `deepseek-v3`      | DeepSeek V3                 | 128K    | $0.25 / 1M tokens |
| `llama-3.3-70b`    | Llama 3.3 70B               | 128K    | $0.20 / 1M tokens |
| `llama-3.1-8b`     | Llama 3.1 8B                | 128K    | $0.05 / 1M tokens |

More models are added regularly. Check the [catalog endpoint](https://api.dos.ai/v1/catalog) or the [dashboard](https://app.dos.ai/models) for the latest list.

## Links

* **Dashboard**: [app.dos.ai](https://app.dos.ai)
* **API base URL**: `https://api.dos.ai/v1`
* **DOSafe**: [dosafe.io](https://dosafe.io)
* **Status**: [status.dos.ai](https://status.dos.ai)
* **Community**: [Telegram](https://t.me/dosai_community) | [Discord](https://discord.gg/dosai)


# Changelog

All notable changes to the DOS.AI platform are documented here. This file is the source of truth - synced to `dosai.changelog` on deploy.

Format: `- **type** [product] Title - Description` Types: `feature`, `fix`, `improvement`, `breaking` Products: `dosclaw`, `dashboard`, `gateway`, `dosafe`, `inference`

***

## 2026-09-08

* **feature** \[dashboard] Agent Tool Permission & Policy Governance - Admins can now configure per-tool authorization policies (autonomous, approval\_required, draft\_only) directly in the agent settings; granular permission gates enforce human confirmation before external write mutations
* **improvement** \[gateway] Google Cloud Model Pricing Watch - Gateway price watcher now monitors Google Gemini API model prices in real time, keeping pricing tables synchronized automatically

## 2026-08-31

* **improvement** \[dosclaw] Hermes Runtime v2026.8.31 - Advanced the pinned agent container runtime to v2026.8.31 with optimized tool memory isolation and startup time improvements

## 2026-08-28

* **feature** \[dosclaw] Crove Post Analytics & Post Metrics - Integrated 12 new social marketing MCP tools including get\_social\_post\_analytics and get\_channel\_metrics; marketing agents can query impressions, engagement rates, and follower growth

## 2026-08-26

* **feature** \[dosclaw] Singapore GCP SEA Cluster Rollout - Deployed high-performance agent cluster gcp-sea in asia-southeast1-b with remote Cloudflare Tunnel ingress at claw-gcp.dos.ai and authenticated Status API at claw-status-gcp.dos.ai for low-latency Southeast Asia traffic
* **feature** \[gateway] Connector Cache Shield - High-concurrency read cache with singleflight de-duplication across commerce and CRM tool queries, preventing upstream rate limit exhaustion during flash sales
* **feature** \[dosclaw] Meta Leads Inbound Webhook - Real-time webhook ingestion for Facebook and Instagram Lead Ads forms; triggers instant AI agent consultations within seconds of lead submission

## 2026-08-22

* **feature** \[dosclaw] MISA Enterprise Suite Connectors - Deployed read-only connectors for MISA meInvoice, Inbound E-Invoice, ASP, AMIS Accounting, WeSign, and eSign; financial agents can inspect invoice statuses, tax codes, inventory balances, and signing status securely

## 2026-08-18

* **feature** \[dosclaw] Google Workspace Connector - Safe-scope integration for Google Sheets (read/append rows), Google Calendar (event creation with automatic Google Meet video links), and Google Drive document search
* **feature** \[dashboard] Organization-Scoped Projects & Teams - Added multi-tenant team project boundaries, JIT JWT claim parsing for zero-latency role resolution, and scoped agent listings

## 2026-08-16

* **feature** \[gateway] Smart Router 5-Step Visual Flow - Upgraded the DOS Smart Router engine with real-time prompt compression, automatic tool-call recovery, and live catalog routing
* **fix** \[dashboard] SSO Fallback Infinite Loop - Fixed duplicate auth check on sso-start that trapped users in a redirect loop on beta environments when falling back to legacy login

## 2026-08-10

* **feature** \[dosclaw] Slack & Lark Organization Workspace Connectors - 1-Click Slack App installation with Block Kit interactive UI modals, and Lark Open Platform custom bot event subscriptions for enterprise team chats

## 2026-07-28

* **feature** \[dosclaw] Haravan & Shopee Live Commerce Integration - Official OAuth connectors for Haravan Omnichannel and Shopee Open Platform; agents can look up products, check live inventory, and track orders across multi-channel customer chats

## 2026-07-15

* **feature** \[gateway] Prompt Compression & Tool-Call Recovery - Built-in semantic prompt compression reduces input token latency by up to 40% while automated tool recovery auto-corrects malformed JSON tool calls from edge LLMs

## 2026-06-28

* **feature** \[dosclaw] Zalo OA Follower Sync & Interactive Cards - Connected verified Zalo Official Accounts with interactive action cards, follower profile sync, and seamless human agent handover

## 2026-06-20

* **feature** \[gateway] Hot-Reload Provider Catalog - Upstream LLM provider models, pricing, and availability now hot-reload without gateway restarts

## 2026-06-16

* **feature** \[dosclaw] Multi-Platform Commerce Tools - One agent can now serve more than one storefront at once (e.g. KiotViet POS + WooCommerce); product lookups aggregate across every connected platform with platform-qualified codes (`woocommerce:11`, `kiotviet:DH000002`), and stock/order/create-order tools route to the right platform automatically; single-platform agents are unchanged

## 2026-06-15

* **feature** \[gateway] Response Caching - Exact-match response cache for non-streaming requests; identical requests return instantly with no model call and no token charge. On by default for `temperature: 0`; control per request with the `X-DOS-Cache: on|off` header; cache hits return `X-DOS-Cache: hit` / `X-Provider: cache`
* **feature** \[gateway] Guardrails (PII) - Opt-in deterministic PII detection via the `X-DOS-Guardrails: block|redact|flag` header; detects email, phone, and Luhn-validated card numbers in chat/completions input
* **feature** \[gateway] Batch API - OpenAI-compatible `/v1/files` + `/v1/batches` for async batch processing of `dos-ai` chat completions, processed on idle GPU capacity
* **feature** \[dosclaw] Commerce Connectors - Connect commerce platforms (WooCommerce and KiotViet) to an agent directly from the dashboard; the bot gains in-chat tools to search products, check stock, look up order status, and create orders against the live store
* **feature** \[dosafe] C2PA Provenance + Integrity Clash - Image AI detection now parses real C2PA manifests; flags an "Integrity Clash" when a manifest claims camera origin but the forensic ensemble independently reports the pixels as AI, instead of being laundered past the detector by forged camera credentials
* **improvement** \[dosafe] In-the-Wild Detection Calibration - Recalibrated the standalone image `is_ai_generated` threshold against an in-the-wild benchmark to catch more real-world AI images

## 2026-06-13

* **feature** \[dosclaw] Scope Guard - Business-bounded bots (customer-service, shop, sales, real-estate, finance) now politely decline off-topic general-AI requests (coding, homework, essays, translation, chit-chat) with one short redirect instead of acting as a free ChatGPT; education-tutor and content-creator templates are exempt
* **fix** \[dosclaw] Reasoning Leak on Bank Reset - Serial-less device-reset requests in bank-officer groups no longer leak the model's raw chain-of-thought into the customer reply; the bot now asks for the serial in one short line

## 2026-06-12

* **feature** \[dashboard] Agents Funding Summary - Billing Overview now shows an Agents card: total agents, how many are included in the plan vs billed from credits (\~$1/day each), per-agent status, and a "deleted in Xd" countdown for suspended agents
* **feature** \[dashboard] Subscription Cancel Flow - Billing overview shows the current plan and adds a cancel flow; pay-by-credits wording clarified
* **fix** \[dosclaw] Device Reset Messaging - Tingee loa reset now correctly tells the customer the unlink is immediate (no restart, no waiting) and that the device must be re-linked to use again; actionable failure reasons are relayed instead of a generic system error
* **fix** \[dosafe] QR Decode Accuracy - Swapped the QR decoder (goqr → gozxing/ZXing) after the old library silently mis-decoded a real device QR into the wrong serial

## 2026-06-11

* **feature** \[dosafe] Image Source-URL Analysis - Image AI detection now classifies the source URL (domain reputation + filename/path AI markers like `ai-generated`, `midjourney`, `dall-e`) and feeds it to the meta-judge, catching AI images that the pixel ensemble alone scores as borderline
* **feature** \[dosclaw] No Invented Links - KB grounding now forbids agents from fabricating third-party links in answers
* **feature** \[gateway] Billing Drift Alerts - Real-time DOSiren alert when a paid webhook fails to grant credit, plus a config-drift canary on `/system/health` to catch silent billing-config mismatches

## 2026-06-10

* **feature** \[dosclaw] Cleaner Web-to-KB Ingestion - Ingesting a web URL into an agent's knowledge base now packs chunks, strips markup noise and empty heading-anchor links, and understands GitBook `llms-full.txt` for higher-quality retrieval

## 2026-06-09

* **feature** \[dashboard] Pricing v2 - New plan tiers (Plus $9 / Pro $19) with a 1-day free trial plus a 4-hour countdown, per-billing-cycle plan credits, and free `dos-ai` model usage; existing trials are grandfathered so the shorter trial is not applied retroactively
* **fix** \[gateway] Stripe Subscription Reference - Read the subscription reference from `invoice.parent` to match Stripe's API so subscription credit grants resolve correctly

## 2026-06-08

* **feature** \[dosclaw] Free dos-ai for Agents - DOSClaw agent traffic now runs the `dos-ai` model for free, with per-tier rate limits and a request queue (10s timeout) when the concurrency limit is hit
* **feature** \[dosclaw] Web-Link Knowledge Sources - Agents can ingest web URLs into their knowledge base (with a refresh cron) and manage those web sources from a new dashboard UI
* **feature** \[dosclaw] KB-Only Mode - Per-agent `kb_only` flag denies `web_fetch`/`web_search` so a bot answers strictly from its own knowledge base

## 2026-06-06

* **feature** \[gateway] Provider Marketplace (Phase A/B) - Catalog of upstream providers and a registry-based routing layer (flag-gated, default off) toward a multi-provider LLM marketplace
* **feature** \[dashboard] OIDC Login (app.dos.ai) - Migrated app sign-in to an OIDC authorization-code + PKCE flow; logout now ends the IdP session via SLO so the next sign-in shows the login form instead of silently re-authenticating
* **fix** \[dosclaw] Workspace-Only File Tools - OpenClaw template now enforces `tools.fs.workspaceOnly=true`, restricting agent file operations to the workspace
* **fix** \[gateway] PII Masking in Memory - Vietnamese phone numbers are masked before being written to durable agent memory

## 2026-06-04

* **feature** \[dosclaw] DOS Grounding Plugin - New first-party OpenClaw plugin injects KB context before the prompt is built, giving deterministic knowledge-base grounding across all gateway channels (not just Zalo); blocks fabricated order lookups

## 2026-06-03

* **feature** \[dosclaw] Self-Serve Zalo Personal (zalouser) - Connect a personal Zalo account to an agent via QR code from the dashboard
* **feature** \[dosclaw] Cross-Channel Customer Identity - Per-customer memory is now keyed by phone number across channels, with Telegram `request_contact` to link a customer's identity across Zalo/Telegram
* **feature** \[dosclaw] Device Reset Tool - `reset_device` MCP tool for Tingee loa reset, wired per-agent and gated to designated groups
* **fix** \[dosclaw] Stronger Knowledge Grounding - `search_knowledge` now returns an explicit instruction to answer only from results (curbs hallucination) with a broader-triggering tool description

## 2026-06-02

* **feature** \[dashboard] User Timezone + Zalo OA Schedule - User timezone setting with timezone-aware Zalo OA auto-active scheduling
* **feature** \[dosclaw] Zalo OA Profile Context - Injects the Zalo OA follower's profile into the agent context and strips OA-incompatible markdown; server-side per-customer memory auto-capture and recall on Zalo personal, gated to 1:1 DMs to prevent group leakage
* **fix** \[dosclaw] Zalo OA Reply Hygiene - Hard-strips CJK characters from Zalo OA replies and unwraps code-wrapped replies

## 2026-06-01

* **feature** \[dosclaw] Per-Customer Memory (All Channels) - Agents gain per-customer memory across every channel via MCP tools (`search_memory` + `remember`), so a bot remembers a customer's stated facts between conversations
* **feature** \[dashboard] Self-Service Zalo OA - Connect and manage a Zalo Official Account integration, including an auto-active schedule editor, from the dashboard
* **improvement** \[gateway] Reasoning-Aware Timeouts - Non-streaming timeout now accounts for reasoning tokens (ported from DOSRouter) so long reasoning turns are not cut off

## 2026-05-31

* **feature** \[dosclaw] Zalo OA Scheduled Auto-Active - Zalo OA bots can open on a scheduled window with no `/start` needed
* **fix** \[dosclaw] Memory Capture Accuracy - Memory now captures only customer-stated facts, never the assistant's own claims
* **fix** \[dosclaw] Version Floor - Enforces an OpenClaw image version floor (currently 2026.5.12) so agents can't be pinned to an image version that crash-loops

## 2026-05-30

* **feature** \[dosclaw] Multi-OA Zalo Official Account Channel - Multi-tenant Zalo OA channel with stateless OAuth, webhook verification, activation gating, and a 24h idle sliding-window expiry
* **feature** \[gateway] 3-Tier Detector Fallback - Detection requests fall back across Lambda → local → Cloud Run for resilience
* **improvement** \[gateway] Self-Host-Primary Embeddings - Embeddings serve from self-hosted vLLM first with a cloud fallback

## 2026-05-27

* **feature** \[dosclaw] Google Sheet → Knowledge Base Sync - Sync a Google Sheet into an agent's knowledge base, resolving FAQ columns by header name (diacritic-insensitive) on a recurring cron
* **improvement** \[gateway] On-Demand Detector GPU - Pre-warms the GPU detector only while the primary detector is down, avoiding idle GPU billing

## 2026-05-25

* **feature** \[dosclaw] Agent Location Selection - Choose an agent's hosting location on create, with project-scoped grants/RBAC and a per-backend agent cap
* **feature** \[dosclaw] Human Handoff (Multi-Target) - `handoff_to_human` MCP tool hands a conversation off to a human, fanning out to multiple targets (Zalo + Telegram) and attaching the customer's Zalo group invite link
* **feature** \[dosclaw] Chat-Save to Knowledge - Endpoint to save knowledge from chat with peer-owner auth; SOUL/IDENTITY/AGENTS.md and knowledge files persist across the container lifecycle
* **fix** \[dosclaw] Agents Run 24/7 - Local agents stay running by default; idle-sleep is now opt-in

## 2026-05-24

* **feature** \[gateway] Unified DOSafe API Key - DOSafe routes now accept the unified `dos_sk_` key
* **feature** \[dashboard] DOSafe Usage in Dashboard - View DOSafe API usage from the dashboard

## 2026-05-23

* **feature** \[dosclaw] Custom SOUL/Identity + Knowledge - Per-agent custom SOUL/identity with hybrid-by-size knowledge (small knowledge inlined, larger sets via RAG search)
* **feature** \[dosclaw] Image Anti-Fabrication Rule - All agent SOUL templates get an image anti-fabrication rule so bots don't invent image contents

## 2026-05-22

* **feature** \[dosclaw] RAG Knowledge Base (Phase 1) - Knowledge base with chunking, embedding, and kNN search; uploads replace files in place on re-upload
* **feature** \[dosafe] Partner Server Deployment Package - On-prem/partner server deployment package and backend config for running the stack at a partner site

## 2026-05-20

* **feature** \[dosclaw] Bring-Your-Own-Subscription Plugins - Per-agent plugin slots plus a model-auth status endpoint for agents using their own provider subscription
* **feature** \[gateway] Billing Reconciliation - Billing reconciliation cron with an unbilled-usage status and Telegram alerting on unbilled fallback

## 2026-05-19

* **feature** \[dashboard] Connect ChatGPT (BYOS) - Connect your own ChatGPT/OpenAI account to an agent via device-code OAuth, with a per-agent Model Auth status card
* **fix** \[gateway] Emergency Paid Fallback - Falls back to a paid provider with a TTFB timeout when self-hosted vLLM is down

## 2026-05-16

* **feature** \[dosclaw] FinOne Merchant Pairing - Bind and verify a merchant's FinOne userId during agent provisioning; the pairing survives container rebuild/upgrade
* **feature** \[dosafe] BlazeFace Liveness Pipeline - Face verifier swaps to MediaPipe BlazeFace (Apache 2.0) for detection and the CVPR 2024-winning Swin-V2 liveness model

## 2026-05-14

* **feature** \[dosclaw] VN Merchant Accounting Agent - New `finance-assistant` template and a public FinOne skill giving VN merchants a bot with accounting MCP tools (transactions, budgets, goals, recurring, insights, education)

## 2026-05-13

* **feature** \[dosclaw] Google Workspace OAuth in Agents - Bundles Google Workspace OAuth into agent containers so bots can act on Gmail/Calendar/Drive/Sheets
* **feature** \[dosafe] Plagiarism Detection - `/detect-plagiarism` wires corpus search + upsert for plagiarism checking
* **feature** \[dosafe] Vietnamese AI-Text Detector (Pilot) - Pilots a Vietnamese AI-text detector (mE5-VN, CC BY 4.0)
* **feature** \[dosafe] Partner Webhooks - Risk assessments fire events to B2B partner webhooks

## 2026-05-12

* **improvement** \[dosafe] Speaker Verification Upgrade - Swaps the speaker-verification ensemble to ERes2NetV2 (Apache 2.0)

## 2026-05-10

* **feature** \[dosafe] MamBo-3 Voice Antispoof - New MamBo-3 voice anti-spoof backend (MIT license), replacing the prior in-house model
* **fix** \[gateway] Encrypt Slack Tokens - Slack workspace bot tokens are now encrypted at rest

## 2026-05-08

* **feature** \[gateway] Prometheus Metrics - `/metrics` endpoint with hot-path counters and a structured-logging foundation for the gateway

## 2026-05-04

* **fix** \[gateway] Streaming Billing Hardening - Paid streaming requests hold against the actual provider, use a bounded SSE reader, and split PayOS/VNPay pending-vs-settled idempotency keys to prevent double-charges
* **fix** \[gateway] Webhook PII Redaction - Webhook body logs are redacted to stop leaking PII, and webhook-secret defaults are removed (fail-closed on missing secret)

## 2026-05-03

* **feature** \[gateway] Per-User JWT Rate Limit - Per-user token-bucket rate limiting on the JWT auth path

## 2026-04-30

* **feature** \[gateway] Fleet Upgrade Safety - Post-incident upgrade safety controls and a `/system/agents/fleet-upgrade` admin endpoint with a job-level mutex to prevent simultaneous fleet upgrades

## 2026-04-29

* **feature** \[gateway] Multi-Tenant Gateway (Phase 1.5) - Consolidates per-product routing and quota into the gateway

## 2026-04-27

* **feature** \[gateway] Supabase JWT Auth + Per-Product Quota - Supabase JWT auth (ES256/JWKS verified) with per-product monthly quota, a `/v1/p/<product>/` route prefix, and per-product audit logging

## 2026-04-26

* **feature** \[dosafe] Document Forgery Detection - `/v1/dosafe/detect-doc-forgery` route backed by a multi-layer ensemble (Qwen3.6 + classical forensics + rules)
* **feature** \[dosafe] Multimodal Identity Verify - `/v1/dosafe/identity/verify-multimodal` endpoint with active-liveness challenge nonce, plus a multi-frame anti-spoof voting endpoint
* **feature** \[dosclaw] Skills Installed State - `/v1/agents/:id/skills/installed` endpoint with cross-device hydrate and an Installed tab in the dashboard
* **improvement** \[dosclaw] OpenClaw Changelog Auto-Sync - OpenClaw GitHub releases auto-sync into the changelog every 6h

## 2026-04-24

* **fix** \[dashboard] Cross-Subdomain Logout - Session cookie set on `.dos.ai` for all subdomains, with duplicate `Set-Cookie` clears and a logout layout-race fix

## 2026-04-22

* **feature** \[inference] dos-ai → Qwen3.6-35B-A3B (NVFP4) - Upgraded the self-hosted `dos-ai` model to Qwen3.6-35B-A3B in NVFP4 on vLLM
* **feature** \[dosafe] Detector v2 Consolidation - Merged image-detector, face anti-spoof, and voice anti-spoof into one consolidated detector service

## 2026-04-21

* **feature** \[dashboard] Onboarding Email Drip - Wires a 5-email onboarding drip sequence via Brevo templates

## 2026-04-20

* **feature** \[dosafe] 1:N Speaker Identification - Voice ID gains a 1:N speaker-identification endpoint returning top-K candidates

## 2026-04-18

* **feature** \[dosafe] Face Anti-Spoof v2 - New face anti-spoof v2 service (beta flag)
* **feature** \[gateway] Agent Suspension Lifecycle - Flag-gated agent suspension + purge lifecycle; closes a self-credit exploit with atomic credit RPCs
* **feature** \[dashboard] Playground - Interactive playground chat UI with streaming

## 2026-04-17

* **feature** \[dosafe] Compliance Call Streaming - SSE streaming for progressive call-compliance results, with speaker diarization producing a dialogue-formatted transcript and anti-spoof running independently of enrollment

## 2026-04-15

* **feature** \[dashboard] Transactional Email (Brevo) - Brevo integration for transactional emails
* **fix** \[dashboard] SSO Login Reliability - Reworked the SSO handler (hash-based token flow, direct JWT decode, redirect-loop break, login-spinner timeouts) to fix sign-in

## 2026-04-14

* **feature** \[dashboard] Agent Skills Catalog - New OpenClaw skills catalog tab in the agent dashboard with skill detail views (SKILL.md content, stats, Install button) and a redesigned card-grid agents list

## 2026-04-13

* **feature** \[dosafe] Alibaba Cloud eKYC Fallback - Adds an Alibaba Cloud eKYC fallback for face verification

## 2026-04-12

* **feature** \[gateway] Cache-Aware Sticky Routing - DOSRouter pins model to session when context exceeds 3K tokens (single message) or 5K tokens (cumulative) to maximize provider-side prefix cache hits; sticky TTL is per-provider (5min for API providers, 10min for self-hosted vLLM)
* **feature** \[gateway] Per-Provider Cache TTL - Sticky routing TTL matches each provider's prefix cache lifetime: Anthropic/OpenAI/DeepSeek (5 min), vLLM/self-hosted (10 min); configurable via `providerCacheTTLMs` map
* **fix** \[dashboard] Cross-Account Logout Loop - Logout now passes `prompt=login` to id.dos.me to force login form display instead of auto-SSO, preventing cross-account session loops

## 2026-04-11

* **feature** \[gateway] DOSRouter Upstream Sync to v0.12.146 - 17/19 ClawRouter releases ported; includes usage cost breakdown, eco/premium tier fallback, session pinning, agentic 3-state, model roster updates
* **feature** \[gateway] DOSRouter Full Port Expansion - Wallet module (EVM + Solana), payment module (x402 protocol), image generation endpoint, full CLI (serve, classify, models, stats, logs, cache, report, wallet, chain, doctor)
* **feature** \[gateway] DOSRouter Open-Sourced - Standalone Go LLM router at github.com/DOS/DOSRouter with 15-dimension scoring, tier-based routing, structured fallback chains

## 2026-04-08

* **feature** \[dosclaw] OpenClaw v2026.4.5 - Major engine upgrade with video/music generation, enhanced memory, and improved channel experience
* **feature** \[dosclaw] Video Generation - Agents can create videos using the built-in `video_generate` tool with xAI Grok, Alibaba Wan, and Runway providers
* **feature** \[dosclaw] Music Generation - Built-in `music_generate` tool with Google Lyria and MiniMax providers; async task tracking and follow-up delivery
* **feature** \[dosclaw] ClawHub Skill Store - Search, browse, and install skills directly from the Control UI Skills panel
* **feature** \[dosclaw] Memory Dreaming - Agents automatically distill important conversations into long-term memory via background dreaming phases; Dream Diary surface in the UI
* **feature** \[dosclaw] Thinking Level Control - Per-session thinking depth picker in chat header; choose how much reasoning the agent applies per conversation
* **feature** \[dosclaw] Structured Progress - Long-running agent tasks now show step-by-step progress updates in compatible UIs
* **improvement** \[dosclaw] Multilingual Control UI - Added 12 new locales: Chinese (Simplified/Traditional), Portuguese, German, Spanish, Japanese, Korean, French, Turkish, Indonesian, Polish, and Ukrainian
* **improvement** \[dosclaw] Prompt Caching - Smarter cache reuse across follow-up messages for faster and more cost-efficient conversations
* **improvement** \[dosclaw] Cleaner Replies - Internal tool tags and planning text no longer leak into user-visible messages
* **improvement** \[dosclaw] Discord Media Limit - Inbound/outbound media cap raised to 100MB, matching Telegram
* **fix** \[dosclaw] Telegram - Voice note transcription restored in DMs; model picker fixed; topic replies; reaction persistence across restarts; better image handling
* **fix** \[dosclaw] Discord - Image generation replies now include actual images; reply threading fixed; voice auto-join more reliable
* **fix** \[dosclaw] WhatsApp - Reconnect loop fixed for quiet chats
* **fix** \[dosclaw] Group Chat - Replies now use natural chat-style formatting instead of document-style spacing
* **breaking** \[dosclaw] Config Cleanup - Legacy config aliases removed; existing configs auto-migrate via `openclaw doctor --fix`

## 2026-04-07

* **feature** \[gateway] Streaming Pre-Deduct - Paid provider streaming requests now hold estimated cost before streaming via atomic `hold_credits()` RPC; reconciled on completion or kept on interruption
* **feature** \[dosclaw] Agent Metrics Rewrite - Metrics tab shows billing-based usage (spend, requests, tokens, daily charts) instead of slow openclaw exec; load time reduced from \~10s to \~2s
* **feature** \[dashboard] Usage Cost Tracking - Total Spend, Daily Spend chart, and Avg Cost/Request added to the Usage page
* **feature** \[dosclaw] Agent Usage Self-Lookup - New `GET /v1/agents/:id/usage/summary` endpoint; bots can answer "how much did I cost?" via SOUL.md curl instruction
* **improvement** \[gateway] Atomic Hold RPCs - `hold_credits()` and `release_hold()` PostgreSQL functions for race-free balance holds during streaming
* **improvement** \[dosclaw] Container Env Vars - `AGENT_ID` and `AGENT_SLUG` now injected into all agent containers for self-identification

## 2026-04-04

* **feature** \[dosclaw] Shared Bot Multi-Agent Routing - Messages from shared Telegram/Discord bots now route through the correct agent's OpenClaw container based on chat ID; agent name/ID injected for personality routing
* **improvement** \[gateway] Alert Backend Labels - All container and Status API alerts now include source (LOCAL/AZURE); vLLM health monitoring every 3 minutes via Cloudflare tunnel URLs; recovery alerts bypass dedup cooldown
* **fix** \[dosclaw] Agent Version Display - Agent settings now always show current running version with fallback to "latest" instead of blank

## 2026-04-03

* **feature** \[dosclaw] OpenClaw v2026.4.2 - Agents pinned to v2026.4.2 with SearXNG web search plugin enabled; version selector filters to multi-arch tags only
* **feature** \[dosclaw] Agent Usage Billing - Per-agent usage billing endpoints; agents now track and report token consumption
* **feature** \[dashboard] Crypto Payment - Stripe crypto checkout alongside cards; Google Pay, Apple Pay, and Link payment methods supported
* **fix** \[gateway] Billing & Provider Routing - Retail pricing uses customer-requested model identity; cloud-only models fail honestly instead of falling back to vLLM; promo pricing cost floor during upstream billing
* **fix** \[dashboard] Billing Page - SVG brand icons for payment methods, Link display fix, success toast; fixed fetchBillingData crash from missing brand/last4 fields
* **fix** \[gateway] vLLM Health Check URLs - Cloud Run health checks use configured Cloudflare tunnel URLs instead of localhost (unreachable from Cloud Run)

## 2026-04-02

* **feature** \[dosclaw] Agent Memory Search - Shared Qwen3-Embedding-4B AWQ embedding service; agents now perform semantic memory search via local vLLM instead of keyword-only recall
* **feature** \[dosclaw] SearXNG Web Search - Self-hosted SearXNG enabled for `web_search` tool in agent containers; no external API key required
* **fix** \[dosafe] Audio Speech Detection - Replaced spectral band analysis (too many false positives on music) with energy CoV + pause ratio; music-only clips now excluded from AI probability blend
* **fix** \[dosafe] Video Frame Extraction - LLM visual analysis now sends extracted frames as base64 images instead of raw video URL; text-only fallback on HTTP 500

## 2026-04-01

* **feature** \[gateway] LLM API Marketplace - `GET /v1/catalog` retail endpoint with DB-driven pricing; DeepSeek V3 + Qwen 397B / 122B / 27B now live via DashScope / Alibaba Cloud
* **fix** \[gateway] Billing Race Condition - Replaced race-prone async deduction with atomic `deduct_usage()` PostgreSQL RPC; pre-flight balance gate blocks requests before proxying; streaming billing fixed (was charging 0 tokens on SSE responses)
* **feature** \[gateway] Multi-Backend Agent Routing - `BackendRouter` dispatches agent ops to local or Azure backend per agent; Azure VM CPU/memory/disk monitored via `/metrics`, alerts on 3 consecutive threshold breaches
* **feature** \[gateway] Embeddings Endpoint - `/v1/embeddings` routes to dedicated pooling backend, falls back to DashScope `text-embedding-v4` when local vLLM unavailable
* **feature** \[dashboard] dos-ai Launch Promo - $0.01 / 1M tokens (down from $0.10); promo badge with strikethrough original price on models page and model detail
* **feature** \[dashboard] SEO - JSON-LD structured data, Open Graph, robots.txt, sitemap.xml, `llms.txt`, `ai.txt` for AI crawlers
* **feature** \[dosclaw] Lite Agent Tier - New 2 GB / 0.5 vCPU instance size for free tier users
* **feature** \[dosafe] RDAP + URL Path Detection - RDAP domain registration data and URL path heuristics added to entity risk assessment
* **fix** \[dosclaw] Pairing & Channels - Shared bot auto-approves pairing; Discord Gateway WebSocket reconnect fixed; slug→UUID deep link resolution fixed
* **fix** \[dashboard] Signup Bonus - Fixed $5 credit not granted on Google OAuth login

## 2026-03-31

* **feature** \[gateway] Enriched Entity Check - `firstSeenAt`, `reportCount`, `relatedEntities` added to `/v1/dosafe/check` response
* **feature** \[dosclaw] Custom Bot Token Validation - Token validated against Telegram API before saving; Disconnect button for shared/custom bot links; clear existing webhook on new custom token connect
* **feature** \[dosclaw] Channel Linked State UI - Discord/Telegram channel cards show live connection state (shared bot vs custom token)
* **feature** \[dashboard] i18n Agent Detail - 200+ hardcoded strings translated; agent detail page fully internationalized across all 7 supported languages
* **improvement** \[dashboard] Language Switcher Redesign - Globe icon + locale code dropdown with checkmark, matching DOSafe design
* **fix** \[dosclaw] Agent Stability - Fixed OOM crash loops, memory persistence on restart, deep link slug→UUID resolution

## 2026-03-30

* **feature** \[dashboard] Comprehensive i18n - All dashboard pages internationalized (agents list, agent detail, plans, billing, settings)
* **feature** \[dashboard] Support Banner - Announcement banner in topbar with Telegram and Discord community links
* **feature** \[dashboard] AI Detector Redirect - AI Detector page redirects to dosafe.io (DOSafe owns detection features)
* **fix** \[dosclaw] Agent Deep Links - Slug→UUID resolution fixed in DeepLink handler; pairing code shown explicitly in Telegram connect UI
* **fix** \[gateway] Image Upload Size - nginx `client_max_body_size` increased to 50 MB for image detection uploads
* **perf** \[dashboard] Agent Detail Load - Parallel data fetching reduces agent detail page load time

## 2026-03-29

* **feature** \[dosclaw] Instance ID Badge - Each agent now shows a short instance ID (e.g. #568bc2) in the header for quick identification when reporting issues
* **fix** \[dosclaw] Open Console Instant Load - Console button now appears immediately on page load instead of after a 4-5s delay
* **fix** \[dosclaw] Provisioning Progress Bar - Redeploy now shows the same step-by-step progress bar as initial deploy
* **fix** \[dosclaw] Agent Health Stability - Gateway marked healthy only after 3 consecutive successful health checks, preventing premature "Open Console" during boot
* **feature** \[dashboard] Language Switcher - Added support for Chinese, Japanese, Korean, Thai, and Indonesian in addition to Vietnamese and English

## 2026-03-28

* **feature** \[dosclaw] WhatsApp Dual-Mode - Support both shared bot and native QR code connection for WhatsApp
* **feature** \[dosclaw] Discord Connect UI - New Discord integration panel with bot invite flow and connection status
* **fix** \[dosclaw] Shared Bot Status - Channel cards now correctly show shared bot connection state for all supported platforms
* **fix** \[dosclaw] WhatsApp QR Generation - Fixed CLI flags and stream header parsing for reliable QR code display
* **fix** \[dosclaw] Container Lifecycle - Confirm dialogs for stop/restart; fixed upgrade race condition and lxcfs mount errors on restart
* **fix** \[dosclaw] Agent Entrypoint - Phased background installs to prevent OOM during boot; pinned dependency versions for reproducibility
* **improvement** \[dosclaw] Agent Identity - Agent name and persona now managed via IDENTITY.md for cleaner runtime separation

## 2026-03-26

* **feature** \[dosclaw] Instance Sizing - Choose Standard (1 vCPU, 2 GB, $5) or Plus (2 vCPU, 4 GB, $10) when creating agents
* **fix** \[dosclaw] Version Selector Filter - Hide architecture-specific tags (-arm64, -amd64), show only multi-arch versions
* **fix** \[dosclaw] Token Preservation - Fixed bug where disabling/enabling channels erased encrypted bot tokens

## 2026-03-25

* **feature** \[dashboard] Alert Notifications - Telegram alerts for provisioning failures and container resource limits
* **feature** \[dosclaw] Auto-capture Owner Chat ID - Bot automatically captures owner's Telegram chat ID on first message for alert delivery

## 2026-03-24

* **feature** \[dosclaw] Version Pinning - Pin agents to a specific OpenClaw version or follow latest
* **feature** \[dosclaw] Agent Upgrade - One-click upgrade to latest OpenClaw image with graceful container recreation
* **feature** \[dosclaw] Brave Search Integration - Connect Brave Search API key to enable web\_search tool
* **feature** \[dosclaw] GitHub Integration - Connect GitHub token for code-related agent skills

## 2026-03-23

* **feature** \[dosclaw] Standalone Agent Containers - No HiClaw Manager dependency, agents run independently with trusted-proxy auth
* **feature** \[dosclaw] Bot Personality System - First-run onboarding: name, creature, vibe, emoji
* **feature** \[dosclaw] Agent Templates - 5 templates (Personal Assistant, Sales TikTok, CS Shopee, Content Creator, Custom)
* **feature** \[dosclaw] Credit-based Pricing - Free=1 trial bot 7d, Plus=1 free, Pro=3 free, extra bots charged from credits
* **feature** \[dosclaw] Console Proxy - Trusted-proxy auth for browser-based agent console
* **improvement** \[dashboard] Agent Creation Flow - 2-step UI: template selection then configure screen
* **breaking** \[dosclaw] Removed CoPaw Runtime - All agents now use OpenClaw runtime only

## 2026-03-21

* **feature** \[gateway] Go API Gateway on Cloud Run - Replaced Cloudflare Worker with full Go backend at api.dos.ai
* **feature** \[gateway] LLM Inference Proxy - vLLM primary + fallback providers, SSE streaming
* **feature** \[gateway] API Key Auth - dos\_sk\_\* format, SHA-256 hash lookup, sliding window rate limiting
* **feature** \[gateway] Credit Billing - Balance check, token-based deduction, credit management

## 2026-03-20

* **feature** \[dosclaw] Agent Knowledge Base - File upload with RAG indexing for agent context
* **feature** \[dosclaw] Agent Backup/Restore - Export and import agent configurations
* **feature** \[dosclaw] Default Permissions - Allow-all or ask-permission modes per agent

## 2026-03-15

* **feature** \[dosafe] SPAI Detector - Spectral analysis for modern AI image detection (CVPR 2025)
* **feature** \[dosafe] Context-Aware Ensemble - Dynamic model weights based on image metadata (editor, compression, beauty app)
* **feature** \[dosafe] Exact Match Safety Cap - Reverse search exact matches cap AI score (3+ matches -> 45%)
* **improvement** \[gateway] LLM Prompt V3 - Calibrated for DINOv3 + SPAI + CommFor ensemble

## 2026-03-10

* **feature** \[dosafe] Web Search + LLM Analysis - 4th data source for entity risk assessment (Serper/SerpApi + vLLM)
* **feature** \[dosafe] Partner API - Public API at dosafe.io/api/v1 with API key auth and scoped permissions

## 2026-03-08

* **feature** \[dosclaw] Onboarding Wizard - Full CLI setup: security, model, channels, skills, hooks, hatching
* **feature** \[dosclaw] Plan Enforcement - Soft warning for plan limits instead of hard block
* **feature** \[dashboard] Channel Selector - Telegram, Discord, WhatsApp (coming soon) for agent creation

## 2026-03-01

* **feature** \[gateway] DOSafe Integration - Entity check, bulk check, URL check, text/image AI detection endpoints
* **feature** \[gateway] Agent Management API - Full CRUD + lifecycle (start, stop, upgrade, restart)
* **feature** \[gateway] Anonymous IP Quota - 20 checks/day for DOSafe public endpoints
* **improvement** \[gateway] User Quota Persistence - Moved from in-memory to Supabase

## 2026-02-15

* **feature** \[dashboard] Organization Management - Create and manage organizations
* **feature** \[dashboard] API Key Management - Create, list, revoke keys with usage tracking
* **improvement** \[dashboard] Dark Mode - Fixed flash on select dropdowns
* **fix** \[dashboard] Token Refresh - Fixed billing precision issues

## 2026-02-01

* **feature** \[dashboard] Supabase Auth Migration - OAuth, password login, logout, identity management
* **feature** \[dashboard] Avatar Upload - Profile photo upload with organization support
* **feature** \[dashboard] Billing System - Credit-based billing with Stripe integration
* **improvement** \[dashboard] Server-side OAuth - Redirect to avoid /login flash


# Quickstart

Go from zero to your first AI-generated response in under five minutes.

## 1. Create an account

Sign up at [app.dos.ai](https://app.dos.ai). Every new account receives **$5 in free credits** -- no credit card required.

## 2. Get your API key

1. Log in to the [DOS AI dashboard](https://app.dos.ai).
2. Navigate to **API Keys** in the sidebar.
3. Click **Create new key**.
4. Copy the key (it starts with `dos_sk_`). Store it somewhere safe -- you won't be able to see it again.

## 3. Make your first request

DOS AI is fully compatible with the OpenAI SDK. Install it and point it at our API.

### Python

```bash
pip install openai
```

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.dos.ai/v1",
    api_key="dos_sk_...",  # Replace with your key
)

response = client.chat.completions.create(
    model="dos-ai",
    messages=[
        {"role": "user", "content": "What is the capital of France?"}
    ],
)

print(response.choices[0].message.content)
```

### JavaScript / TypeScript

```bash
npm install openai
```

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.dos.ai/v1",
  apiKey: "dos_sk_...", // Replace with your key
});

const response = await client.chat.completions.create({
  model: "dos-ai",
  messages: [
    { role: "user", content: "What is the capital of France?" }
  ],
});

console.log(response.choices[0].message.content);
```

### cURL

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Authorization: Bearer dos_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "user", "content": "What is the capital of France?"}
    ]
  }'
```

## 4. Try streaming

Streaming lets you receive tokens as they are generated, giving your users a real-time typing experience.

### Python (streaming)

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.dos.ai/v1",
    api_key="dos_sk_...",
)

stream = client.chat.completions.create(
    model="dos-ai",
    messages=[
        {"role": "user", "content": "Write a short poem about open-source AI."}
    ],
    stream=True,
)

for chunk in stream:
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="", flush=True)
print()
```

### JavaScript (streaming)

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.dos.ai/v1",
  apiKey: "dos_sk_...",
});

const stream = await client.chat.completions.create({
  model: "dos-ai",
  messages: [
    { role: "user", content: "Write a short poem about open-source AI." }
  ],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;
  if (content) process.stdout.write(content);
}
console.log();
```

### cURL (streaming)

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Authorization: Bearer dos_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "user", "content": "Write a short poem about open-source AI."}
    ],
    "stream": true
  }'
```

## 5. Explore further

* **Manage your keys**: [Authentication](/getting-started/authentication)
* **Migrate from OpenAI**: [OpenAI Compatibility](/getting-started/openai-compatibility)
* **Check available models**: `GET https://api.dos.ai/v1/models`

> **Tip:** Since DOS AI is OpenAI-compatible, any tutorial, library, or framework that works with the OpenAI API also works with DOS AI. Just change the base URL and API key.


# Authentication

Every request to the DOS AI API must include a valid API key. This page covers how to create, use, and secure your keys.

## API key format

DOS AI keys follow the format:

```
dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

All keys begin with the `dos_sk_` prefix. Keys are hashed (SHA-256) before storage -- we never store your raw key. If you lose a key, you'll need to generate a new one.

## Creating an API key

1. Log in to the [DOS AI dashboard](https://app.dos.ai).
2. Go to **API Keys** in the sidebar.
3. Click **Create new key**.
4. Give your key a descriptive name (e.g., "Production backend", "Local development").
5. Copy the key immediately. It will only be displayed once.

You can create multiple keys to separate concerns -- for example, one key per environment or per team member.

## Using your API key

Include your key in the `Authorization` header of every request:

```
Authorization: Bearer dos_sk_...
```

### With the OpenAI SDK (Python)

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.dos.ai/v1",
    api_key="dos_sk_...",
)
```

### With the OpenAI SDK (JavaScript)

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.dos.ai/v1",
  apiKey: "dos_sk_...",
});
```

### With cURL

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Authorization: Bearer dos_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"model": "dos-ai", "messages": [{"role": "user", "content": "Hello"}]}'
```

### With environment variables (recommended)

Rather than hardcoding your key, set it as an environment variable:

```bash
export DOS_AI_API_KEY="dos_sk_..."
```

Then read it in your code:

**Python:**

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.dos.ai/v1",
    api_key=os.environ["DOS_AI_API_KEY"],
)
```

**JavaScript:**

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.dos.ai/v1",
  apiKey: process.env.DOS_AI_API_KEY,
});
```

## Managing keys

From the dashboard you can:

* **View all keys** -- See key names, creation dates, and last-used timestamps.
* **Delete a key** -- Immediately revokes access. Any request using that key will return `401 Unauthorized`.
* **Create new keys** -- No limit on the number of active keys per account.

## Rate limits

Rate limits are applied per API key using a sliding window (60-second window).

| Plan | Requests per minute | Tokens per minute |
| ---- | ------------------- | ----------------- |
| Free | 60                  | 100,000           |
| Plus | 120                 | 500,000           |
| Pro  | 300                 | 2,000,000         |

When you exceed a rate limit, the API returns HTTP `429 Too Many Requests` with a `Retry-After` header indicating how long to wait.

## Error responses

| Status code             | Meaning                     |
| ----------------------- | --------------------------- |
| `401 Unauthorized`      | Missing or invalid API key  |
| `402 Payment Required`  | Insufficient credit balance |
| `429 Too Many Requests` | Rate limit exceeded         |

Example error response:

```json
{
  "error": {
    "message": "Invalid API key",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}
```

## Security best practices

1. **Never commit keys to version control.** Use environment variables or a secrets manager (e.g., Doppler, AWS Secrets Manager, Vercel Environment Variables).
2. **Use separate keys for each environment.** Create distinct keys for development, staging, and production. If a dev key leaks, revoke it without affecting production.
3. **Rotate keys periodically.** Generate a new key, update your deployment, then delete the old key.
4. **Restrict access.** Only share keys with team members who need them. Use your organization's secrets management tooling.
5. **Monitor usage.** Check the dashboard regularly for unexpected spikes that could indicate a leaked key.

If you believe a key has been compromised, delete it immediately from the dashboard and create a new one.


# OpenAI Compatibility

DOS AI implements the OpenAI API specification. If your application already uses the OpenAI API, you can switch to DOS AI by changing two lines of configuration -- no code rewrite required.

## Drop-in replacement

The only changes needed are `base_url` (or `baseURL`) and `api_key`:

### Python

```python
from openai import OpenAI

# Before (OpenAI)
# client = OpenAI(api_key="sk-...")

# After (DOS AI)
client = OpenAI(
    base_url="https://api.dos.ai/v1",
    api_key="dos_sk_...",
)

# Everything else stays the same
response = client.chat.completions.create(
    model="dos-ai",
    messages=[{"role": "user", "content": "Hello!"}],
    temperature=0.7,
    max_tokens=256,
)
```

### JavaScript / TypeScript

```javascript
import OpenAI from "openai";

// Before (OpenAI)
// const client = new OpenAI({ apiKey: "sk-..." });

// After (DOS AI)
const client = new OpenAI({
  baseURL: "https://api.dos.ai/v1",
  apiKey: "dos_sk_...",
});

// Everything else stays the same
const response = await client.chat.completions.create({
  model: "dos-ai",
  messages: [{ role: "user", content: "Hello!" }],
  temperature: 0.7,
  max_tokens: 256,
});
```

### cURL / HTTP

Replace `https://api.openai.com` with `https://api.dos.ai` and update your API key:

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Authorization: Bearer dos_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dos-ai",
    "messages": [{"role": "user", "content": "Hello!"}],
    "temperature": 0.7,
    "max_tokens": 256
  }'
```

## Supported endpoints

| Endpoint               | Method | Description                             |
| ---------------------- | ------ | --------------------------------------- |
| `/v1/chat/completions` | POST   | Chat completions (single and streaming) |
| `/v1/models`           | GET    | List available models                   |

## Supported parameters

The `/v1/chat/completions` endpoint supports the following request parameters:

| Parameter           | Type            | Description                                                 |
| ------------------- | --------------- | ----------------------------------------------------------- |
| `model`             | string          | **Required.** Model ID (e.g., `dos-ai`)                     |
| `messages`          | array           | **Required.** Conversation messages (`role` + `content`)    |
| `temperature`       | float           | Sampling temperature (0.0 -- 2.0). Default: 1.0             |
| `top_p`             | float           | Nucleus sampling threshold. Default: 1.0                    |
| `max_tokens`        | integer         | Maximum tokens to generate                                  |
| `stream`            | boolean         | Enable server-sent events streaming. Default: false         |
| `stop`              | string or array | Stop sequence(s)                                            |
| `frequency_penalty` | float           | Penalize repeated tokens (-2.0 -- 2.0). Default: 0.0        |
| `presence_penalty`  | float           | Penalize tokens already present (-2.0 -- 2.0). Default: 0.0 |
| `n`                 | integer         | Number of completions to generate. Default: 1               |
| `seed`              | integer         | Seed for deterministic sampling                             |

## Framework compatibility

Since DOS AI follows the OpenAI specification, it works out of the box with popular frameworks and libraries:

### LangChain

```python
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    base_url="https://api.dos.ai/v1",
    api_key="dos_sk_...",
    model="dos-ai",
)

response = llm.invoke("Explain recursion in one sentence.")
print(response.content)
```

### LlamaIndex

```python
from llama_index.llms.openai_like import OpenAILike

llm = OpenAILike(
    api_base="https://api.dos.ai/v1",
    api_key="dos_sk_...",
    model="dos-ai",
    is_chat_model=True,
)
```

### Vercel AI SDK

```typescript
import { createOpenAI } from "@ai-sdk/openai";
import { generateText } from "ai";

const dosai = createOpenAI({
  baseURL: "https://api.dos.ai/v1",
  apiKey: "dos_sk_...",
});

const { text } = await generateText({
  model: dosai("dos-ai"),
  prompt: "Explain recursion in one sentence.",
});
```

## What's different from OpenAI

While we aim for full compatibility, there are a few differences to be aware of:

| Feature                  | Status            | Notes                                                     |
| ------------------------ | ----------------- | --------------------------------------------------------- |
| Chat completions         | Supported         | Full compatibility including streaming                    |
| Model names              | Different         | Use DOS AI model IDs (e.g., `dos-ai` instead of `gpt-4o`) |
| Function calling / tools | Supported         | Works with vLLM tool-calling implementation               |
| JSON mode                | Supported         | Set `response_format: {"type": "json_object"}`            |
| Vision (image inputs)    | Model-dependent   | Supported if the underlying model handles vision          |
| Embeddings               | Not yet available | Coming soon                                               |
| Image generation         | Not available     | Use a dedicated image generation service                  |
| Audio / TTS / STT        | Not available     | Not on the roadmap                                        |
| Assistants API           | Not available     | Use chat completions directly                             |
| Batch API                | Not yet available | Coming soon                                               |

## Migration checklist

Switching from OpenAI to DOS AI:

* [ ] Sign up at [app.dos.ai](https://app.dos.ai) and create an API key
* [ ] Update `base_url` / `baseURL` to `https://api.dos.ai/v1`
* [ ] Update `api_key` / `apiKey` to your `dos_sk_...` key
* [ ] Update the `model` parameter to a DOS AI model ID (e.g., `dos-ai`)
* [ ] Test your application -- request/response format is identical
* [ ] (Optional) Update environment variable names for clarity:

```bash
# .env
# Before
OPENAI_API_KEY=sk-...

# After
DOS_AI_API_KEY=dos_sk_...
DOS_AI_BASE_URL=https://api.dos.ai/v1
```

## Using both OpenAI and DOS AI

You can use both providers in the same application by creating separate client instances:

```python
from openai import OpenAI

openai_client = OpenAI(api_key="sk-...")
dosai_client = OpenAI(base_url="https://api.dos.ai/v1", api_key="dos_sk_...")

# Route requests based on your needs
def get_completion(prompt, provider="dosai"):
    client = dosai_client if provider == "dosai" else openai_client
    model = "dos-ai" if provider == "dosai" else "gpt-4o"

    return client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
    )
```

This makes it easy to evaluate DOS AI side-by-side or use it as a fallback provider.


# Chat Completions

The Chat Completions API is the primary way to interact with DOS AI models. It follows the OpenAI-compatible format, so you can use existing OpenAI SDKs and tools with minimal changes.

## Base URL

```
https://api.dos.ai/v1
```

## Authentication

All requests require an API key passed in the `Authorization` header:

```
Authorization: Bearer dos_sk_your_api_key_here
```

## Basic Request

A chat completion request consists of a list of messages and a model identifier. The model generates a response based on the conversation history.

### Request Format

```json
POST /v1/chat/completions
Content-Type: application/json
Authorization: Bearer dos_sk_your_api_key_here

{
  "model": "dos-ai",
  "messages": [
    { "role": "user", "content": "What is the capital of France?" }
  ]
}
```

### Response Format

```json
{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "created": 1711500000,
  "model": "dos-ai",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The capital of France is Paris."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 14,
    "completion_tokens": 8,
    "total_tokens": 22
  }
}
```

## Message Roles

Each message in the `messages` array has a `role` and `content`. The API supports four roles:

| Role        | Description                                                                                      |
| ----------- | ------------------------------------------------------------------------------------------------ |
| `system`    | Sets the behavior and personality of the assistant. Placed at the beginning of the conversation. |
| `user`      | Messages from the end user.                                                                      |
| `assistant` | Previous responses from the model. Used for multi-turn context.                                  |
| `tool`      | Results from tool/function calls. See [Function Calling](/guides/function-calling).              |

### System Message

Use the system message to instruct the model on how to behave:

```json
{
  "model": "dos-ai",
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful coding assistant. Always include code examples in your answers. Use Python unless the user specifies a different language."
    },
    {
      "role": "user",
      "content": "How do I read a CSV file?"
    }
  ]
}
```

## Parameters

| Parameter           | Type            | Default       | Description                                                                                       |
| ------------------- | --------------- | ------------- | ------------------------------------------------------------------------------------------------- |
| `model`             | string          | *required*    | Model ID to use (e.g., `dos-ai`).                                                                 |
| `messages`          | array           | *required*    | List of messages in the conversation.                                                             |
| `temperature`       | float           | 0.7           | Sampling temperature between 0 and 2. Lower values make output more deterministic.                |
| `max_tokens`        | integer         | model default | Maximum number of tokens to generate in the response.                                             |
| `top_p`             | float           | 1.0           | Nucleus sampling threshold. Only tokens with cumulative probability up to `top_p` are considered. |
| `frequency_penalty` | float           | 0.0           | Penalizes tokens based on how frequently they appear (range: -2.0 to 2.0).                        |
| `presence_penalty`  | float           | 0.0           | Penalizes tokens based on whether they have appeared at all (range: -2.0 to 2.0).                 |
| `stop`              | string or array | null          | Up to 4 sequences where the model will stop generating.                                           |
| `stream`            | boolean         | false         | If true, returns a stream of server-sent events. See [Streaming](/guides/streaming).              |
| `n`                 | integer         | 1             | Number of completions to generate for each prompt.                                                |
| `response_format`   | object          | null          | Force a specific output format. See [Structured Outputs](/guides/structured-outputs).             |
| `tools`             | array           | null          | List of tools the model may call. See [Function Calling](/guides/function-calling).               |

### Temperature vs Top-p

* **Temperature** controls randomness. `0` is nearly deterministic, `2` is highly random.
* **Top-p** controls diversity by limiting the token pool. `0.1` means only the top 10% probability mass is considered.

It is generally recommended to adjust one or the other, not both simultaneously.

```json
{
  "model": "dos-ai",
  "messages": [{ "role": "user", "content": "Write a haiku about AI." }],
  "temperature": 1.2,
  "max_tokens": 50
}
```

## Multi-turn Conversations

To maintain context across multiple exchanges, include previous messages in the request. The model does not retain state between requests -- you must send the full conversation history each time.

```json
{
  "model": "dos-ai",
  "messages": [
    { "role": "system", "content": "You are a math tutor." },
    { "role": "user", "content": "What is the derivative of x^2?" },
    { "role": "assistant", "content": "The derivative of x^2 is 2x." },
    { "role": "user", "content": "What about x^3?" }
  ]
}
```

The model sees the full conversation and can respond contextually: "The derivative of x^3 is 3x^2."

## Code Examples

### cURL

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_your_api_key_here" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Explain quantum computing in simple terms."}
    ],
    "temperature": 0.7,
    "max_tokens": 500
  }'
```

### Python (OpenAI SDK)

The easiest way to use DOS AI in Python is with the official OpenAI SDK, pointed at the DOS AI base URL:

```python
from openai import OpenAI

client = OpenAI(
    api_key="dos_sk_your_api_key_here",
    base_url="https://api.dos.ai/v1",
)

response = client.chat.completions.create(
    model="dos-ai",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Explain quantum computing in simple terms."},
    ],
    temperature=0.7,
    max_tokens=500,
)

print(response.choices[0].message.content)
```

### Python (requests)

If you prefer not to use the OpenAI SDK:

```python
import requests

response = requests.post(
    "https://api.dos.ai/v1/chat/completions",
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer dos_sk_your_api_key_here",
    },
    json={
        "model": "dos-ai",
        "messages": [
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "Explain quantum computing in simple terms."},
        ],
        "temperature": 0.7,
        "max_tokens": 500,
    },
)

data = response.json()
print(data["choices"][0]["message"]["content"])
```

### JavaScript (Node.js)

Using the OpenAI Node.js SDK:

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "dos_sk_your_api_key_here",
  baseURL: "https://api.dos.ai/v1",
});

const response = await client.chat.completions.create({
  model: "dos-ai",
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Explain quantum computing in simple terms." },
  ],
  temperature: 0.7,
  max_tokens: 500,
});

console.log(response.choices[0].message.content);
```

### JavaScript (fetch)

Using the native `fetch` API:

```javascript
const response = await fetch("https://api.dos.ai/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer dos_sk_your_api_key_here",
  },
  body: JSON.stringify({
    model: "dos-ai",
    messages: [
      { role: "system", content: "You are a helpful assistant." },
      { role: "user", content: "Explain quantum computing in simple terms." },
    ],
    temperature: 0.7,
    max_tokens: 500,
  }),
});

const data = await response.json();
console.log(data.choices[0].message.content);
```

## Error Handling

The API returns standard HTTP status codes and a JSON error body:

| Status Code | Meaning                                                                |
| ----------- | ---------------------------------------------------------------------- |
| `400`       | Bad request -- malformed JSON or invalid parameters.                   |
| `401`       | Unauthorized -- missing or invalid API key.                            |
| `402`       | Insufficient credits -- top up your balance.                           |
| `429`       | Rate limit exceeded -- slow down and retry after the indicated period. |
| `500`       | Internal server error -- retry with exponential backoff.               |
| `503`       | Service unavailable -- the model is temporarily overloaded.            |

Error response format:

```json
{
  "error": {
    "message": "Invalid API key provided.",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}
```

### Retry Strategy

For `429` and `5xx` errors, implement exponential backoff:

```python
import time
import requests

def chat_with_retry(payload, max_retries=3):
    for attempt in range(max_retries):
        response = requests.post(
            "https://api.dos.ai/v1/chat/completions",
            headers={
                "Content-Type": "application/json",
                "Authorization": "Bearer dos_sk_your_api_key_here",
            },
            json=payload,
        )

        if response.status_code == 200:
            return response.json()

        if response.status_code in (429, 500, 503):
            wait = 2 ** attempt
            print(f"Retrying in {wait}s (status {response.status_code})...")
            time.sleep(wait)
            continue

        # Non-retryable error
        response.raise_for_status()

    raise Exception("Max retries exceeded")
```

## Available Models

| Model ID | Description                                                     |
| -------- | --------------------------------------------------------------- |
| `dos-ai` | Qwen3.5-35B-A3B -- fast, efficient, recommended for most tasks. |

Check the [Models](/api-reference/models) endpoint for the current list of available models.

## Next Steps

* [Streaming](/guides/streaming) -- receive tokens in real time as they are generated.
* [Function Calling](/guides/function-calling) -- let the model call external tools and APIs.
* [Structured Outputs](/guides/structured-outputs) -- get reliable JSON responses.


# Streaming

Streaming lets you receive the model's response token-by-token as it is generated, rather than waiting for the entire response to complete. This dramatically reduces perceived latency -- the user sees output within milliseconds instead of waiting seconds for a full response.

## Why Use Streaming

* **Faster time-to-first-token.** The user sees output almost immediately.
* **Better UX for long responses.** Progressive rendering feels more responsive than a loading spinner.
* **Real-time applications.** Chat interfaces, live coding assistants, and interactive tools all benefit from streaming.
* **Memory efficiency.** Process tokens incrementally without buffering the entire response.

## How It Works

Set `stream: true` in your request. The API responds with a stream of **Server-Sent Events (SSE)** instead of a single JSON response.

Each event is a line prefixed with `data:` containing a JSON chunk. The stream ends with `data: [DONE]`.

```
data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"The"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" capital"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" of"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" France"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" is"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" Paris."},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]
```

### Key Differences from Non-streaming

| Aspect        | Non-streaming        | Streaming                                           |
| ------------- | -------------------- | --------------------------------------------------- |
| Response type | Single JSON object   | Stream of SSE events                                |
| Object type   | `chat.completion`    | `chat.completion.chunk`                             |
| Message field | `message`            | `delta` (incremental)                               |
| Content       | Complete string      | Token-by-token fragments                            |
| Usage stats   | Included in response | Included in the final chunk (with `stream_options`) |

### The `delta` Object

In streaming, each chunk contains a `delta` instead of a `message`. The delta holds only the new content since the last chunk:

* First chunk: `delta` has `role: "assistant"` (and optionally the first content token).
* Middle chunks: `delta` has `content` with the next token(s).
* Final chunk: `delta` is empty `{}`, and `finish_reason` is set (e.g., `"stop"` or `"tool_calls"`).

## Getting Usage Statistics

By default, streaming responses do not include token usage. To receive usage data, set `stream_options`:

```json
{
  "model": "dos-ai",
  "messages": [{"role": "user", "content": "Hello!"}],
  "stream": true,
  "stream_options": {
    "include_usage": true
  }
}
```

The final chunk before `[DONE]` will include a `usage` field:

```json
{
  "id": "chatcmpl-abc",
  "choices": [],
  "usage": {
    "prompt_tokens": 9,
    "completion_tokens": 12,
    "total_tokens": 21
  }
}
```

## Code Examples

### cURL

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_your_api_key_here" \
  -N \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "user", "content": "Write a short poem about the ocean."}
    ],
    "stream": true
  }'
```

The `-N` flag disables output buffering so you see tokens as they arrive.

### Python (OpenAI SDK -- Synchronous)

```python
from openai import OpenAI

client = OpenAI(
    api_key="dos_sk_your_api_key_here",
    base_url="https://api.dos.ai/v1",
)

stream = client.chat.completions.create(
    model="dos-ai",
    messages=[
        {"role": "user", "content": "Write a short poem about the ocean."}
    ],
    stream=True,
)

for chunk in stream:
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="", flush=True)

print()  # Newline after stream completes
```

### Python (OpenAI SDK -- Async)

```python
import asyncio
from openai import AsyncOpenAI

client = AsyncOpenAI(
    api_key="dos_sk_your_api_key_here",
    base_url="https://api.dos.ai/v1",
)


async def main():
    stream = await client.chat.completions.create(
        model="dos-ai",
        messages=[
            {"role": "user", "content": "Write a short poem about the ocean."}
        ],
        stream=True,
    )

    async for chunk in stream:
        content = chunk.choices[0].delta.content
        if content:
            print(content, end="", flush=True)

    print()


asyncio.run(main())
```

### Python (requests -- Manual SSE Parsing)

For cases where you cannot use the OpenAI SDK:

```python
import json
import requests

response = requests.post(
    "https://api.dos.ai/v1/chat/completions",
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer dos_sk_your_api_key_here",
    },
    json={
        "model": "dos-ai",
        "messages": [
            {"role": "user", "content": "Write a short poem about the ocean."}
        ],
        "stream": True,
    },
    stream=True,
)

for line in response.iter_lines():
    if not line:
        continue

    line = line.decode("utf-8")

    if line.startswith("data: "):
        data = line[6:]  # Remove "data: " prefix

        if data == "[DONE]":
            break

        chunk = json.loads(data)
        content = chunk["choices"][0]["delta"].get("content", "")
        if content:
            print(content, end="", flush=True)

print()
```

### JavaScript (OpenAI SDK)

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "dos_sk_your_api_key_here",
  baseURL: "https://api.dos.ai/v1",
});

const stream = await client.chat.completions.create({
  model: "dos-ai",
  messages: [
    { role: "user", content: "Write a short poem about the ocean." },
  ],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;
  if (content) {
    process.stdout.write(content);
  }
}

console.log();
```

### JavaScript (fetch -- Browser/Edge)

For browser-based applications or edge runtimes where the OpenAI SDK is not available:

```javascript
async function streamChat(messages) {
  const response = await fetch("https://api.dos.ai/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer dos_sk_your_api_key_here",
    },
    body: JSON.stringify({
      model: "dos-ai",
      messages,
      stream: true,
    }),
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });

    // Process complete lines
    const lines = buffer.split("\n");
    buffer = lines.pop(); // Keep incomplete line in buffer

    for (const line of lines) {
      const trimmed = line.trim();
      if (!trimmed || !trimmed.startsWith("data: ")) continue;

      const data = trimmed.slice(6);
      if (data === "[DONE]") return;

      const chunk = JSON.parse(data);
      const content = chunk.choices[0]?.delta?.content;
      if (content) {
        // Append to your UI element
        document.getElementById("output").textContent += content;
      }
    }
  }
}

// Usage
await streamChat([
  { role: "user", content: "Write a short poem about the ocean." },
]);
```

### React (Next.js with Vercel AI SDK)

For Next.js applications, the Vercel AI SDK provides a streamlined experience:

```typescript
// app/api/chat/route.ts
import { createOpenAI } from "@ai-sdk/openai";
import { streamText } from "ai";

const dosai = createOpenAI({
  apiKey: process.env.DOS_AI_API_KEY,
  baseURL: "https://api.dos.ai/v1",
});

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: dosai("dos-ai"),
    messages,
  });

  return result.toUIMessageStreamResponse();
}
```

```tsx
// app/page.tsx
"use client";

import { useChat } from "@ai-sdk/react";

export default function Chat() {
  const { messages, input, setInput, sendMessage } = useChat({
    api: "/api/chat",
  });

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>
          <strong>{m.role}:</strong> {m.content}
        </div>
      ))}
      <form
        onSubmit={(e) => {
          e.preventDefault();
          sendMessage({ text: input });
          setInput("");
        }}
      >
        <input value={input} onChange={(e) => setInput(e.target.value)} />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}
```

## Streaming with Function Calls

When the model makes a tool call during streaming, the chunks contain `delta.tool_calls` instead of `delta.content`. The function name and arguments arrive incrementally:

```python
from openai import OpenAI

client = OpenAI(
    api_key="dos_sk_your_api_key_here",
    base_url="https://api.dos.ai/v1",
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string"},
                },
                "required": ["city"],
            },
        },
    },
]

stream = client.chat.completions.create(
    model="dos-ai",
    messages=[{"role": "user", "content": "What is the weather in Hanoi?"}],
    tools=tools,
    stream=True,
)

# Accumulate tool call data from chunks
tool_calls = {}
current_content = ""

for chunk in stream:
    delta = chunk.choices[0].delta

    if delta.content:
        current_content += delta.content
        print(delta.content, end="", flush=True)

    if delta.tool_calls:
        for tc in delta.tool_calls:
            idx = tc.index
            if idx not in tool_calls:
                tool_calls[idx] = {
                    "id": tc.id,
                    "function": {"name": "", "arguments": ""},
                }
            if tc.function.name:
                tool_calls[idx]["function"]["name"] += tc.function.name
            if tc.function.arguments:
                tool_calls[idx]["function"]["arguments"] += tc.function.arguments

# After the stream, tool_calls contains the complete function call data
for idx, tc in tool_calls.items():
    print(f"\nTool call: {tc['function']['name']}({tc['function']['arguments']})")
```

## Error Handling

### Connection Errors

Streaming connections can be interrupted by network issues. Always handle connection errors and implement reconnection logic:

```python
from openai import OpenAI, APIConnectionError, APITimeoutError

client = OpenAI(
    api_key="dos_sk_your_api_key_here",
    base_url="https://api.dos.ai/v1",
)


def stream_with_retry(messages, max_retries=3):
    for attempt in range(max_retries):
        try:
            stream = client.chat.completions.create(
                model="dos-ai",
                messages=messages,
                stream=True,
            )

            full_response = ""
            for chunk in stream:
                content = chunk.choices[0].delta.content
                if content:
                    full_response += content
                    print(content, end="", flush=True)

            print()
            return full_response

        except (APIConnectionError, APITimeoutError) as e:
            print(f"\nConnection error (attempt {attempt + 1}): {e}")
            if attempt == max_retries - 1:
                raise

        except Exception as e:
            print(f"\nUnexpected error: {e}")
            raise
```

### Incomplete Streams

If the stream ends unexpectedly (no `[DONE]` event), check the last chunk's `finish_reason`:

* `"stop"` -- normal completion.
* `"length"` -- hit the `max_tokens` limit. Increase `max_tokens` or continue the conversation.
* `"tool_calls"` -- the model wants to call a function. Handle the tool call and continue.
* `null` -- stream was interrupted. Retry the request.

### Timeouts

For long-running streams, configure appropriate timeouts:

```python
client = OpenAI(
    api_key="dos_sk_your_api_key_here",
    base_url="https://api.dos.ai/v1",
    timeout=120.0,  # 2 minutes
)
```

```javascript
const client = new OpenAI({
  apiKey: "dos_sk_your_api_key_here",
  baseURL: "https://api.dos.ai/v1",
  timeout: 120000, // 2 minutes in milliseconds
});
```

## Collecting the Full Response

If you need the complete response text (e.g., for logging or saving to a database), accumulate it during streaming:

```python
stream = client.chat.completions.create(
    model="dos-ai",
    messages=[{"role": "user", "content": "Tell me a story."}],
    stream=True,
    stream_options={"include_usage": True},
)

full_text = ""
usage = None

for chunk in stream:
    if chunk.choices:
        content = chunk.choices[0].delta.content
        if content:
            full_text += content
            print(content, end="", flush=True)

    if chunk.usage:
        usage = chunk.usage

print()
print(f"\nTotal tokens: {usage.total_tokens if usage else 'N/A'}")

# Save full_text to your database
```

## Next Steps

* [Chat Completions](/guides/chat-completions) -- learn the basics of the API.
* [Function Calling](/guides/function-calling) -- let the model call external tools.
* [Structured Outputs](/guides/structured-outputs) -- get reliable JSON from the model.


# Function Calling

Function calling (also known as tool use) allows the model to invoke external functions you define. Instead of generating a text-only response, the model can output a structured function call with arguments, which your code executes and returns the result for the model to incorporate into its final answer.

This enables the model to interact with APIs, databases, calculators, and any external system.

## How It Works

1. You define one or more **tools** (functions) in the request, each with a name, description, and JSON Schema for parameters.
2. The model decides whether to call a tool based on the user's message.
3. If the model calls a tool, it returns a response with `finish_reason: "tool_calls"` containing the function name and arguments.
4. Your code executes the function and sends the result back.
5. The model generates a final response using the function result.

```
User message  -->  Model  -->  tool_calls response
                                    |
                          Your code executes function
                                    |
                          Tool result sent back  -->  Model  -->  Final response
```

## Defining Tools

Tools are defined in the `tools` array of the request. Each tool has a `type` of `"function"` and a `function` object containing the name, description, and parameter schema.

```json
{
  "model": "dos-ai",
  "messages": [
    { "role": "user", "content": "What is the weather in Hanoi?" }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather for a given city.",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string",
              "description": "The city name, e.g. 'Hanoi' or 'Ho Chi Minh City'."
            },
            "unit": {
              "type": "string",
              "enum": ["celsius", "fahrenheit"],
              "description": "Temperature unit. Defaults to celsius."
            }
          },
          "required": ["city"]
        }
      }
    }
  ]
}
```

### Best Practices for Tool Definitions

* **Write clear descriptions.** The model uses the description to decide when to call the function. Be specific about what the function does and when it should be used.
* **Use JSON Schema constraints.** Use `enum`, `required`, `minimum`, `maximum`, and `pattern` to constrain parameters. This helps the model generate valid arguments.
* **Keep parameter names intuitive.** Use descriptive names like `city` rather than `c` or `param1`.

## Handling Tool Calls

When the model decides to call a tool, the response looks like this:

```json
{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_abc123",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\": \"Hanoi\", \"unit\": \"celsius\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ]
}
```

Key points:

* `content` may be `null` when the model makes a tool call.
* `arguments` is a JSON string that you need to parse.
* `id` on each tool call is required when sending the result back.
* `finish_reason` is `"tool_calls"` instead of `"stop"`.

## Sending Tool Results

After executing the function, send the result back by appending both the assistant's tool call message and a `tool` role message to the conversation:

```json
{
  "model": "dos-ai",
  "messages": [
    { "role": "user", "content": "What is the weather in Hanoi?" },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_abc123",
          "type": "function",
          "function": {
            "name": "get_weather",
            "arguments": "{\"city\": \"Hanoi\", \"unit\": \"celsius\"}"
          }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "call_abc123",
      "content": "{\"temperature\": 32, \"unit\": \"celsius\", \"condition\": \"Partly cloudy\", \"humidity\": 75}"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather for a given city.",
        "parameters": {
          "type": "object",
          "properties": {
            "city": { "type": "string", "description": "The city name." },
            "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
          },
          "required": ["city"]
        }
      }
    }
  ]
}
```

The model then generates a natural language response incorporating the tool result:

```json
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "The current weather in Hanoi is 32C and partly cloudy with 75% humidity."
      },
      "finish_reason": "stop"
    }
  ]
}
```

## Multiple Tool Calls

The model can call multiple tools in a single response. This is known as **parallel tool calling**.

```json
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_001",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\": \"Hanoi\"}"
            }
          },
          {
            "id": "call_002",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\": \"Ho Chi Minh City\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ]
}
```

When responding, include a separate `tool` message for each call, matched by `tool_call_id`:

```json
{
  "messages": [
    { "role": "user", "content": "Compare the weather in Hanoi and Ho Chi Minh City." },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        { "id": "call_001", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\": \"Hanoi\"}" } },
        { "id": "call_002", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\": \"Ho Chi Minh City\"}" } }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "call_001",
      "content": "{\"temperature\": 32, \"condition\": \"Partly cloudy\"}"
    },
    {
      "role": "tool",
      "tool_call_id": "call_002",
      "content": "{\"temperature\": 35, \"condition\": \"Sunny\"}"
    }
  ]
}
```

## Controlling Tool Use

You can control whether and how the model uses tools with the `tool_choice` parameter:

| Value                                                       | Behavior                                                     |
| ----------------------------------------------------------- | ------------------------------------------------------------ |
| `"auto"`                                                    | The model decides whether to call a tool (default).          |
| `"none"`                                                    | The model will not call any tools, even if they are defined. |
| `"required"`                                                | The model must call at least one tool.                       |
| `{"type": "function", "function": {"name": "get_weather"}}` | Force the model to call a specific function.                 |

```json
{
  "model": "dos-ai",
  "messages": [{ "role": "user", "content": "What is the weather?" }],
  "tools": ["..."],
  "tool_choice": "required"
}
```

## Complete Example

### Python

```python
import json
from openai import OpenAI

client = OpenAI(
    api_key="dos_sk_your_api_key_here",
    base_url="https://api.dos.ai/v1",
)

# Define available tools
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "The city name.",
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "Temperature unit.",
                    },
                },
                "required": ["city"],
            },
        },
    },
]


# Simulate the actual function
def get_weather(city: str, unit: str = "celsius") -> dict:
    # In production, call a real weather API
    return {
        "city": city,
        "temperature": 32,
        "unit": unit,
        "condition": "Partly cloudy",
    }


# Step 1: Send the user message with tools
messages = [{"role": "user", "content": "What is the weather like in Hanoi?"}]

response = client.chat.completions.create(
    model="dos-ai",
    messages=messages,
    tools=tools,
)

assistant_message = response.choices[0].message

# Step 2: Check if the model wants to call a tool
if assistant_message.tool_calls:
    # Append the assistant message (with tool calls) to history
    messages.append(assistant_message)

    # Execute each tool call
    for tool_call in assistant_message.tool_calls:
        function_name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)

        # Dispatch to the actual function
        if function_name == "get_weather":
            result = get_weather(**arguments)
        else:
            result = {"error": f"Unknown function: {function_name}"}

        # Append the tool result
        messages.append(
            {
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result),
            }
        )

    # Step 3: Get the final response with tool results
    final_response = client.chat.completions.create(
        model="dos-ai",
        messages=messages,
        tools=tools,
    )

    print(final_response.choices[0].message.content)
else:
    # No tool call -- direct response
    print(assistant_message.content)
```

### JavaScript

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "dos_sk_your_api_key_here",
  baseURL: "https://api.dos.ai/v1",
});

const tools = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "Get the current weather for a city.",
      parameters: {
        type: "object",
        properties: {
          city: { type: "string", description: "The city name." },
          unit: {
            type: "string",
            enum: ["celsius", "fahrenheit"],
            description: "Temperature unit.",
          },
        },
        required: ["city"],
      },
    },
  },
];

// Simulate the actual function
function getWeather(city, unit = "celsius") {
  return { city, temperature: 32, unit, condition: "Partly cloudy" };
}

// Step 1: Send user message with tools
const messages = [
  { role: "user", content: "What is the weather like in Hanoi?" },
];

let response = await client.chat.completions.create({
  model: "dos-ai",
  messages,
  tools,
});

const assistantMessage = response.choices[0].message;

// Step 2: Check if the model wants to call a tool
if (assistantMessage.tool_calls) {
  messages.push(assistantMessage);

  for (const toolCall of assistantMessage.tool_calls) {
    const args = JSON.parse(toolCall.function.arguments);

    let result;
    if (toolCall.function.name === "get_weather") {
      result = getWeather(args.city, args.unit);
    } else {
      result = { error: `Unknown function: ${toolCall.function.name}` };
    }

    messages.push({
      role: "tool",
      tool_call_id: toolCall.id,
      content: JSON.stringify(result),
    });
  }

  // Step 3: Get the final response
  response = await client.chat.completions.create({
    model: "dos-ai",
    messages,
    tools,
  });

  console.log(response.choices[0].message.content);
} else {
  console.log(assistantMessage.content);
}
```

### cURL

```bash
# Step 1: Send user message with tools
curl https://api.dos.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_your_api_key_here" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "user", "content": "What is the weather in Hanoi?"}
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_weather",
          "description": "Get the current weather for a city.",
          "parameters": {
            "type": "object",
            "properties": {
              "city": {"type": "string", "description": "The city name."},
              "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
          }
        }
      }
    ]
  }'

# Step 2: After parsing the tool call response, send back the result
curl https://api.dos.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_your_api_key_here" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "user", "content": "What is the weather in Hanoi?"},
      {
        "role": "assistant",
        "content": null,
        "tool_calls": [{
          "id": "call_abc123",
          "type": "function",
          "function": {
            "name": "get_weather",
            "arguments": "{\"city\": \"Hanoi\", \"unit\": \"celsius\"}"
          }
        }]
      },
      {
        "role": "tool",
        "tool_call_id": "call_abc123",
        "content": "{\"temperature\": 32, \"unit\": \"celsius\", \"condition\": \"Partly cloudy\"}"
      }
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_weather",
          "description": "Get the current weather for a city.",
          "parameters": {
            "type": "object",
            "properties": {
              "city": {"type": "string", "description": "The city name."},
              "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
          }
        }
      }
    ]
  }'
```

## Error Handling

Common issues with function calling:

| Issue                           | Cause                      | Solution                                                       |
| ------------------------------- | -------------------------- | -------------------------------------------------------------- |
| Model ignores tools             | Description is too vague   | Write a clearer, more specific function description.           |
| Invalid arguments               | Schema is too loose        | Add `required`, `enum`, and constraints to the schema.         |
| Model hallucinates functions    | Too many tools defined     | Reduce the number of tools, or use `tool_choice` to constrain. |
| JSON parse error on `arguments` | Model output was malformed | Wrap `JSON.parse` in a try/catch and retry the request.        |

## Next Steps

* [Chat Completions](/guides/chat-completions) -- learn the basics of the API.
* [Streaming](/guides/streaming) -- stream tool call responses in real time.
* [Structured Outputs](/guides/structured-outputs) -- get reliable JSON from the model.


# Structured Outputs

Structured outputs let you constrain the model to return valid JSON that conforms to a specific schema. This is essential when the model's response needs to be parsed by code rather than read by a human.

## JSON Mode

The simplest way to get JSON output is to set `response_format` to `{ "type": "json_object" }`. This guarantees the model's response is valid JSON.

```json
{
  "model": "dos-ai",
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant that always responds in JSON format."
    },
    {
      "role": "user",
      "content": "List 3 programming languages with their year of creation."
    }
  ],
  "response_format": { "type": "json_object" }
}
```

Response:

```json
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "{\n  \"languages\": [\n    {\"name\": \"Python\", \"year\": 1991},\n    {\"name\": \"JavaScript\", \"year\": 1995},\n    {\"name\": \"Go\", \"year\": 2009}\n  ]\n}"
      },
      "finish_reason": "stop"
    }
  ]
}
```

The `content` field is a JSON string. Parse it in your code:

```python
import json

data = json.loads(response.choices[0].message.content)
print(data["languages"][0]["name"])  # "Python"
```

> **Important:** When using JSON mode, you must include the word "JSON" somewhere in the system or user message. This is a safety measure to ensure the user intends to receive JSON output.

## JSON Schema Enforcement

For stronger guarantees, you can provide a JSON Schema that the model's output must conform to. This uses `response_format` with `type: "json_schema"`:

```json
{
  "model": "dos-ai",
  "messages": [
    {
      "role": "user",
      "content": "Analyze the sentiment of this review: 'The product arrived quickly and works perfectly. Very satisfied with the purchase.'"
    }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "sentiment_analysis",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "sentiment": {
            "type": "string",
            "enum": ["positive", "negative", "neutral", "mixed"]
          },
          "confidence": {
            "type": "number",
            "description": "Confidence score between 0 and 1."
          },
          "summary": {
            "type": "string",
            "description": "Brief explanation of the sentiment."
          },
          "keywords": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Key phrases that influenced the analysis."
          }
        },
        "required": ["sentiment", "confidence", "summary", "keywords"],
        "additionalProperties": false
      }
    }
  }
}
```

Response:

```json
{
  "choices": [
    {
      "message": {
        "content": "{\"sentiment\": \"positive\", \"confidence\": 0.95, \"summary\": \"The reviewer expresses strong satisfaction with both shipping speed and product quality.\", \"keywords\": [\"arrived quickly\", \"works perfectly\", \"very satisfied\"]}"
      }
    }
  ]
}
```

### Schema Rules

When using `strict: true`, the following rules apply:

* All fields must be listed in `required`.
* `additionalProperties` must be set to `false`.
* Supported types: `string`, `number`, `integer`, `boolean`, `array`, `object`, `null`.
* Use `enum` to restrict string values.
* Nested objects must also follow these rules.

## Code Examples

### Python

```python
import json
from openai import OpenAI

client = OpenAI(
    api_key="dos_sk_your_api_key_here",
    base_url="https://api.dos.ai/v1",
)

# Simple JSON mode
response = client.chat.completions.create(
    model="dos-ai",
    messages=[
        {
            "role": "system",
            "content": "Extract the entities from the text. Respond in JSON with keys: persons (array of strings), locations (array of strings), organizations (array of strings).",
        },
        {
            "role": "user",
            "content": "Elon Musk announced that Tesla will open a new factory in Berlin, in partnership with the German government.",
        },
    ],
    response_format={"type": "json_object"},
)

data = json.loads(response.choices[0].message.content)
print(data)
# {
#   "persons": ["Elon Musk"],
#   "locations": ["Berlin"],
#   "organizations": ["Tesla", "German government"]
# }
```

### Python with JSON Schema

```python
import json
from openai import OpenAI

client = OpenAI(
    api_key="dos_sk_your_api_key_here",
    base_url="https://api.dos.ai/v1",
)

response = client.chat.completions.create(
    model="dos-ai",
    messages=[
        {
            "role": "user",
            "content": "Generate a recipe for pho bo (Vietnamese beef noodle soup).",
        }
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "recipe",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "servings": {"type": "integer"},
                    "prep_time_minutes": {"type": "integer"},
                    "cook_time_minutes": {"type": "integer"},
                    "ingredients": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "item": {"type": "string"},
                                "quantity": {"type": "string"},
                            },
                            "required": ["item", "quantity"],
                            "additionalProperties": False,
                        },
                    },
                    "steps": {
                        "type": "array",
                        "items": {"type": "string"},
                    },
                },
                "required": [
                    "name",
                    "servings",
                    "prep_time_minutes",
                    "cook_time_minutes",
                    "ingredients",
                    "steps",
                ],
                "additionalProperties": False,
            },
        },
    },
)

recipe = json.loads(response.choices[0].message.content)
print(f"Recipe: {recipe['name']}")
print(f"Servings: {recipe['servings']}")
print(f"Prep: {recipe['prep_time_minutes']} min, Cook: {recipe['cook_time_minutes']} min")
for ingredient in recipe["ingredients"]:
    print(f"  - {ingredient['quantity']} {ingredient['item']}")
```

### JavaScript

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "dos_sk_your_api_key_here",
  baseURL: "https://api.dos.ai/v1",
});

// Simple JSON mode
const response = await client.chat.completions.create({
  model: "dos-ai",
  messages: [
    {
      role: "system",
      content:
        "Extract entities from the text. Respond in JSON with keys: persons, locations, organizations (all arrays of strings).",
    },
    {
      role: "user",
      content:
        "Elon Musk announced that Tesla will open a new factory in Berlin.",
    },
  ],
  response_format: { type: "json_object" },
});

const data = JSON.parse(response.choices[0].message.content);
console.log(data);
```

### JavaScript with JSON Schema

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "dos_sk_your_api_key_here",
  baseURL: "https://api.dos.ai/v1",
});

const response = await client.chat.completions.create({
  model: "dos-ai",
  messages: [
    {
      role: "user",
      content:
        "Classify this support ticket: 'My order #12345 has not arrived after 2 weeks. I want a refund.'",
    },
  ],
  response_format: {
    type: "json_schema",
    json_schema: {
      name: "ticket_classification",
      strict: true,
      schema: {
        type: "object",
        properties: {
          category: {
            type: "string",
            enum: ["billing", "shipping", "product", "account", "other"],
          },
          priority: {
            type: "string",
            enum: ["low", "medium", "high", "urgent"],
          },
          summary: { type: "string" },
          requires_human: { type: "boolean" },
        },
        required: ["category", "priority", "summary", "requires_human"],
        additionalProperties: false,
      },
    },
  },
});

const ticket = JSON.parse(response.choices[0].message.content);
console.log(ticket);
// {
//   category: "shipping",
//   priority: "high",
//   summary: "Customer reports undelivered order after 2 weeks and requests refund.",
//   requires_human: true
// }
```

### cURL

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_your_api_key_here" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {
        "role": "system",
        "content": "You are a data extraction assistant. Respond in JSON."
      },
      {
        "role": "user",
        "content": "Extract contact info: John Smith, john@example.com, +1-555-0123, works at Acme Corp."
      }
    ],
    "response_format": {"type": "json_object"}
  }'
```

## Tips for Reliable JSON Output

### 1. Always mention JSON in the prompt

When using `response_format: { type: "json_object" }`, include an explicit instruction about JSON in the system message. Without it, the model may produce an error or unpredictable output.

```python
# Good
messages = [
    {"role": "system", "content": "Respond with a JSON object containing..."},
    {"role": "user", "content": "..."}
]

# Bad -- may fail without JSON mentioned in messages
messages = [
    {"role": "user", "content": "List some countries."}
]
```

### 2. Describe the expected shape

Even with JSON mode, the model needs to know what structure you expect. Be explicit about field names and types:

```
Respond with a JSON object with these fields:
- "name" (string): the item name
- "price" (number): price in USD
- "in_stock" (boolean): availability status
- "tags" (array of strings): relevant categories
```

### 3. Use JSON Schema for critical applications

For production systems where invalid JSON would cause failures, prefer `json_schema` with `strict: true` over plain `json_object` mode. The schema enforcement happens at the decoding level, guaranteeing structural correctness.

### 4. Handle parsing errors gracefully

Even with structured outputs, always wrap JSON parsing in error handling:

```python
import json

try:
    data = json.loads(response.choices[0].message.content)
except json.JSONDecodeError as e:
    print(f"Failed to parse JSON: {e}")
    # Retry or fall back to a default
```

### 5. Keep schemas simple

Deeply nested schemas with many optional fields increase the chance of unexpected output. Flatten where possible and use `required` to ensure critical fields are always present.

## Comparison: JSON Mode vs JSON Schema

| Feature       | JSON Mode                      | JSON Schema                                     |
| ------------- | ------------------------------ | ----------------------------------------------- |
| Setting       | `{"type": "json_object"}`      | `{"type": "json_schema", "json_schema": {...}}` |
| Guarantee     | Valid JSON                     | Valid JSON conforming to your schema            |
| Field control | Must be described in prompt    | Enforced by schema                              |
| `strict` mode | Not applicable                 | Optional (recommended)                          |
| Use case      | Simple/flexible JSON responses | Production APIs, data pipelines                 |

## Next Steps

* [Chat Completions](/guides/chat-completions) -- learn the basics of the API.
* [Function Calling](/guides/function-calling) -- let the model call external tools.
* [Streaming](/guides/streaming) -- receive tokens in real time.


# Bring Your Own Key (BYOK)

BYOK lets you use your **own** upstream provider API keys through DOS AI. When you add a provider key, any request that DOS AI routes to that provider is authenticated with your key and billed to **your account with that provider** - DOS AI does not deduct credits for it. You keep DOS AI's unified OpenAI-compatible API, routing, and usage tracking while paying the provider directly.

## How it works

1. Add your provider API key in the [dashboard](https://app.dos.ai).
2. When a request is routed to a model served by that provider, DOS AI uses your key for the upstream call.
3. DOS AI charges **$0 credits** for that request. The response includes the header `X-BYOK: true` so you can confirm your key was used.

You still authenticate to DOS AI with your `dos_sk_*` key as usual. BYOK only changes which key is used for the upstream provider call, and how the request is billed.

## Supported providers

| Provider        | Identifier  |
| --------------- | ----------- |
| OpenAI          | `openai`    |
| Anthropic       | `anthropic` |
| Google (Gemini) | `google`    |
| Mistral         | `mistral`   |
| xAI (Grok)      | `xai`       |
| DeepInfra       | `deepinfra` |

You can store one key per provider (per account, or per project if you use projects).

## Security

* Keys are encrypted at rest with AES-256-GCM.
* The dashboard only ever shows a masked hint (the last 4 characters); the full key is never returned after you save it.
* Removing a key in the dashboard takes effect immediately - requests to that provider revert to DOS AI credit billing.

## Notes

* BYOK applies to chat/completions that are routed to a **supported external provider**.
* It does **not** apply to self-hosted models (such as `dos-ai`) or to [embeddings](/api-reference/embeddings) - those are served on DOS AI's own infrastructure and billed as usual.


# Guardrails

Guardrails let DOS AI inspect a request for sensitive data **before** it reaches the model. Version 1 is **deterministic PII detection** (email, phone, and payment-card numbers) applied to the input of your request. It is **opt-in per request** via a single header, so it never adds latency or blocks anything unless you ask for it.

This is useful when you want to keep personal data out of model prompts for privacy or compliance reasons - for example, masking customer emails and phone numbers before a request is logged or sent upstream.

## How it works

Send the `X-DOS-Guardrails` header on a [Chat Completions](/guides/chat-completions) (or Completions) request and set it to the action you want:

| `X-DOS-Guardrails`    | Behavior when PII is detected in the input                                                                                                                                            |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `block`               | The request is rejected with `400 guardrails_blocked`. No upstream call is made and no credits are spent.                                                                             |
| `redact`              | The detected PII is replaced with a placeholder (such as `[REDACTED_EMAIL]`) before the request is sent to the model. The request proceeds normally.                                  |
| `flag`                | The request passes through **unchanged**. When PII is detected, the `X-DOS-Guardrails: flag` response header is set and the detection is logged, so you can monitor without blocking. |
| *(omitted, or `off`)* | Guardrails are off. This is the default.                                                                                                                                              |

When guardrails detect PII and run an action, the response carries the header `X-DOS-Guardrails: <action>`. If no PII is found, the request proceeds as normal and no header is added - even if you sent one.

## What gets detected

| Category    | What it matches                                                                                                                                                              |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pii.email` | Email addresses.                                                                                                                                                             |
| `pii.phone` | Phone numbers - Vietnamese local format (`0` followed by 9 digits) and international E.164 (`+<country code><number>`).                                                      |
| `pii.card`  | Payment-card numbers (13-19 digits). Each candidate is validated with the **Luhn checksum** before it is flagged, which avoids false positives on ordinary number sequences. |

Detection is applied to:

* **Chat Completions** - every `messages[].content`, including the `text` parts of multimodal (array) content.
* **Completions** - the top-level `prompt` (string or array of strings).

Guardrails run on the **input** you send (output is not filtered in v1), and they run **before caching**, so a redacted request is what gets cached and sent upstream.

## Examples

### Block requests that contain PII

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Authorization: Bearer dos_sk_your_key" \
  -H "Content-Type: application/json" \
  -H "X-DOS-Guardrails: block" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "user", "content": "My email is jane@example.com - summarize my account."}
    ]
  }'
```

Because the input contains an email address, the request is rejected:

```json
{
  "error": {
    "message": "Request blocked by guardrails: sensitive data (PII) detected in the input.",
    "type": "guardrails_blocked"
  }
}
```

### Redact PII and let the request through

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Authorization: Bearer dos_sk_your_key" \
  -H "Content-Type: application/json" \
  -H "X-DOS-Guardrails: redact" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "user", "content": "Email jane@example.com, phone 0901234567 - draft a reply."}
    ]
  }'
```

The model receives `Email [REDACTED_EMAIL], phone [REDACTED_PHONE] - draft a reply.`, and the response includes the header `X-DOS-Guardrails: redact`.

## Notes

* **Opt-in.** Guardrails never run unless you send the `X-DOS-Guardrails` header, so existing integrations are unaffected.
* **Choose the right action.** Use `block` for the strongest guarantee that PII never reaches the model, `redact` to keep the request useful while masking sensitive values, or `flag` to monitor only.
* **v1 scope.** This release is deterministic PII detection on the input. LLM-powered content safety (categories such as hate or violence) and output-side filtering are planned follow-ups.
* See [Error Codes](/support/error-codes) for the `guardrails_blocked` response.


# Response Caching

DOS AI caches non-streaming responses so that an identical request can be served instantly from memory, with **no upstream model call and no token charge**. Caching is **on by default for deterministic requests** (`temperature: 0`) and is fully under your control with a single header.

## How it works

For an eligible request, DOS AI builds an exact-match key from the endpoint and the request body. If an unexpired response with the same key exists, it is returned immediately:

* The response is **free** - no tokens are billed.
* It returns in sub-millisecond time, with no model call.
* The response carries `X-DOS-Cache: hit` and `X-Provider: cache`.

If there is no match, the request runs normally, the response is returned with `X-DOS-Cache: miss`, and it is stored for next time. Cached entries live for up to **24 hours**.

## When a request is cached

| `X-DOS-Cache` request header | Behavior                                                                                                                                |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| *(omitted)*                  | Cached only when the request is **deterministic** (`temperature: 0`). This is the default.                                              |
| `on`                         | Force caching on, even for `temperature > 0`. Use this when you are happy to receive a stored response for repeated identical requests. |
| `off`                        | Disable caching for this request - always call the model.                                                                               |

Only **non-streaming** [Chat Completions](/guides/chat-completions) / Completions are cached. Streaming responses (`stream: true`) are never cached.

## What counts as "the same request"

The cache key is a hash of the endpoint path plus the request body in canonical form. Two requests hit the same entry when their JSON bodies are identical after:

* sorting object keys (field order does not matter), and
* ignoring the non-output fields `stream`, `stream_options`, `user`, and `metadata`.

Everything else - `model`, `messages`, `temperature`, `max_tokens`, `tools`, and so on - is part of the key, so any change produces a different entry.

The exact-match cache is **global but cross-user safe**: a hit requires a byte-identical request that the caller already has, and the response is a pure function of that public input, so nothing private is shared between accounts.

## Response headers

| Header              | Meaning                                                |
| ------------------- | ------------------------------------------------------ |
| `X-DOS-Cache: hit`  | Served from cache - free, no model call.               |
| `X-DOS-Cache: miss` | Not in cache (or first time) - served by the model.    |
| `X-Provider: cache` | Present on a hit, in place of the usual provider name. |

## Examples

A deterministic request is cached automatically. Run this twice - the first call is a `miss`, the second is a `hit` (instant and free):

```bash
curl -i https://api.dos.ai/v1/chat/completions \
  -H "Authorization: Bearer dos_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"model": "dos-ai", "temperature": 0, "messages": [{"role": "user", "content": "Capital of Vietnam?"}]}'
```

To cache a non-deterministic request (`temperature > 0`), opt in with the header:

```bash
curl -i https://api.dos.ai/v1/chat/completions \
  -H "Authorization: Bearer dos_sk_your_key" \
  -H "X-DOS-Cache: on" \
  -H "Content-Type: application/json" \
  -d '{"model": "dos-ai", "temperature": 0.7, "messages": [{"role": "user", "content": "Capital of Vietnam?"}]}'
```

To always bypass the cache, send `X-DOS-Cache: off`.

## Notes

* **Free hits.** A cache hit never consumes tokens or credits.
* **Disable per request** with `X-DOS-Cache: off` when you always need a fresh generation.
* **Today's cache is exact-match.** Semantic (similar-but-not-identical) caching and a shared cross-instance layer are planned follow-ups.


# Available Models

DOS AI serves high-quality open-source LLMs via an OpenAI-compatible API. Self-hosted models run on dedicated RTX Pro 6000 GPUs with 96 GB VRAM in Asia-Southeast 1. Cloud models are served via partner providers for maximum coverage.

## Smart Routing

Use `dos-auto` as the model ID to let DOS AI automatically select the best model for each request. Smart routing uses a 15-dimension classifier to analyze your prompt and route to the optimal model based on task complexity, cost, and latency.

```python
response = client.chat.completions.create(
    model="dos-auto",  # Smart routing picks the best model
    messages=[{"role": "user", "content": "..."}],
)
```

## Model Catalog

### Self-Hosted (Lowest Latency)

| Model               | Provider | Context | Input      | Output     | Model ID |
| ------------------- | -------- | ------- | ---------- | ---------- | -------- |
| **Qwen3.5-35B-A3B** | Alibaba  | 128K    | $0.15 / 1M | $0.15 / 1M | `dos-ai` |

### Cloud Models

| Model                         | Provider         | Context | Input      | Output     | Model ID           |
| ----------------------------- | ---------------- | ------- | ---------- | ---------- | ------------------ |
| **Llama 4 Maverick 17B-128E** | Meta / DeepInfra | 1M      | $0.17 / 1M | $0.66 / 1M | `llama-4-maverick` |
| **Llama 4 Scout 17B-16E**     | Meta / DeepInfra | 640K    | $0.11 / 1M | $0.38 / 1M | `llama-4-scout`    |
| **DeepSeek V3**               | DeepSeek         | 128K    | $0.25 / 1M | $0.25 / 1M | `deepseek-v3`      |
| **Llama 3.3 70B**             | Meta             | 128K    | $0.20 / 1M | $0.20 / 1M | `llama-3.3-70b`    |
| **Llama 3.1 8B**              | Meta             | 128K    | $0.05 / 1M | $0.05 / 1M | `llama-3.1-8b`     |

> All prices are in USD. The catalog is DB-driven -- new models are added regularly. Check `GET /v1/catalog` or the [dashboard](https://app.dos.ai/models) for the latest list. See [Pricing](/models/pricing) for billing details.

### Embedding Models

| Model                  | Provider              | Dimensions | Model ID             |
| ---------------------- | --------------------- | ---------- | -------------------- |
| **Qwen3-Embedding-4B** | Alibaba / Self-hosted | 2560       | `qwen3-embedding-4b` |

See the [Embeddings API reference](/api-reference/embeddings) for the request/response format and code examples.

## Model Details

### Qwen3.5-35B-A3B (default)

Alibaba's Mixture-of-Experts model with 35 billion total parameters and 3 billion active parameters per forward pass. This architecture delivers excellent quality at remarkably low cost and latency, making it our **recommended default model** for most use cases.

* **Best for**: General-purpose chat, code generation, reasoning, multilingual tasks
* **Strengths**: Outstanding cost-efficiency, fast response times, strong multilingual support (especially CJK languages)
* **Model ID**: `dos-ai`

### Llama 4 Maverick 17B-128E

Meta's latest Mixture-of-Experts model with 17 billion active parameters and 128 experts. Strong reasoning and multilingual capabilities with an industry-leading 1 million token context window.

* **Best for**: Complex reasoning, long-context analysis, multilingual tasks
* **Strengths**: Massive context window, strong benchmark scores, efficient MoE architecture
* **Model ID**: `llama-4-maverick`

### Llama 4 Scout 17B-16E

Meta's efficient MoE model with 17 billion active parameters and 16 experts. Fast and cost-effective for everyday tasks with a 640K context window.

* **Best for**: Everyday tasks, fast responses, cost-sensitive workloads
* **Strengths**: Good balance of speed and quality, large context window
* **Model ID**: `llama-4-scout`

### DeepSeek V3

DeepSeek's latest Mixture-of-Experts model, known for strong performance across coding, math, and reasoning benchmarks.

* **Best for**: Code generation, mathematical reasoning, structured output
* **Strengths**: Competitive benchmark scores, good at structured/JSON output, strong code capabilities
* **Model ID**: `deepseek-v3`

### Llama 3.3 70B

Meta's 70-billion-parameter dense model. Offers top-tier reasoning and instruction-following capabilities.

* **Best for**: Complex reasoning, long-form content, detailed analysis
* **Strengths**: Strong English performance, excellent instruction following, robust safety tuning
* **Model ID**: `llama-3.3-70b`

### Llama 3.1 8B

Meta's efficient 8-billion-parameter model. An excellent choice when you need fast, affordable responses and the task does not require the full capability of a larger model.

* **Best for**: Simple tasks, high-throughput workloads, prototyping, cost-sensitive applications
* **Strengths**: Very low latency, lowest cost, suitable for classification and extraction tasks
* **Model ID**: `llama-3.1-8b`

## Choosing the Right Model

| Use Case                             | Recommended Model | Why                                            |
| ------------------------------------ | ----------------- | ---------------------------------------------- |
| Let DOS AI decide                    | `dos-auto`        | Smart routing picks the best model per request |
| General assistant / chatbot          | Qwen3.5-35B-A3B   | Best balance of quality, speed, and cost       |
| Long-context analysis (100K+ tokens) | Llama 4 Maverick  | 1M context window, strong reasoning            |
| Complex reasoning / analysis         | Llama 3.3 70B     | Dense model, top reasoning capability          |
| Code generation / math               | DeepSeek V3       | Top coding and math benchmark scores           |
| High-volume / low-cost tasks         | Llama 3.1 8B      | Fastest and cheapest option                    |
| Multilingual (CJK languages)         | Qwen3.5-35B-A3B   | Superior CJK language performance              |

## Listing Models via API

You can retrieve the current list of available models programmatically:

```bash
curl https://api.dos.ai/v1/models \
  -H "Authorization: Bearer YOUR_API_KEY"
```

For the full retail catalog with pricing and metadata:

```bash
curl https://api.dos.ai/v1/catalog \
  -H "Authorization: Bearer YOUR_API_KEY"
```

See the [Models API reference](/api-reference/models) for the full response schema.


# Pricing

DOS AI uses a simple **pay-as-you-go** pricing model. You only pay for the tokens you use, with no minimum commitments, no monthly fees, and no hidden charges.

## Free Tier

Every new account receives **$5.00 in free credits** to get started. This is enough for substantial experimentation and prototyping before you need to add funds.

| Model           | Approximate Free Usage |
| --------------- | ---------------------- |
| Qwen3.5-35B-A3B | \~33 million tokens    |
| Llama 3.3 70B   | \~25 million tokens    |
| DeepSeek V3     | \~20 million tokens    |
| Llama 3.1 8B    | \~100 million tokens   |

> Free credits do not expire. No credit card is required to start.

## Per-Token Pricing

Pricing is calculated per **1 million tokens** (both input and output).

| Model                         | Input Price (per 1M tokens) | Output Price (per 1M tokens) |
| ----------------------------- | --------------------------- | ---------------------------- |
| **Qwen3.5-35B-A3B** (default) | $0.15                       | $0.15                        |
| **Llama 4 Maverick 17B-128E** | $0.17                       | $0.66                        |
| **Llama 4 Scout 17B-16E**     | $0.11                       | $0.38                        |
| **DeepSeek V3**               | $0.25                       | $0.25                        |
| **Llama 3.3 70B**             | $0.20                       | $0.20                        |
| **Llama 3.1 8B**              | $0.05                       | $0.05                        |

> Prices are DB-driven and may be updated. Check the [dashboard](https://app.dos.ai/models) or `GET /v1/catalog` for the latest pricing.

### What is a Token?

A token is roughly 3-4 characters of English text, or about 0.75 words. For example:

* "Hello, world!" = approximately 4 tokens
* A typical 500-word blog post = approximately 650-700 tokens
* A full 128K context window = approximately 96,000 words

## How Billing Works

1. **Add credits** to your account via the [dashboard](https://app.dos.ai).
2. **Make API calls** as normal. Each request deducts tokens used from your balance.
3. **Monitor usage** in real time through the dashboard billing page.

Token usage is calculated after each request completes. Both input tokens (your prompt) and output tokens (the model's response) are counted and billed at the rates above.

### Usage Tracking

Every API response includes a `usage` object showing exactly how many tokens were consumed:

```json
{
  "usage": {
    "prompt_tokens": 125,
    "completion_tokens": 320,
    "total_tokens": 445
  }
}
```

You can also view historical usage and spending breakdowns on the [dashboard](https://app.dos.ai).

## Enterprise & Volume Discounts

For organizations with high-volume needs, we offer custom pricing:

* **Volume discounts** for sustained usage above $100/month
* **Dedicated capacity** with guaranteed throughput
* **Custom rate limits** tailored to your workload
* **Priority support** with SLA guarantees

Contact us at **<support@dos.ai>** to discuss enterprise pricing.

## Comparison with Other Providers

DOS AI pricing is designed to be significantly more affordable than major cloud LLM providers, while offering comparable model quality. Our infrastructure runs on dedicated GPUs, allowing us to pass the savings directly to you.

## FAQ

### Do free credits expire?

No. Your free credits remain in your account until used.

### Is there a minimum top-up amount?

The minimum credit purchase is $5.00.

### What happens when my balance reaches zero?

API requests will return a `402 Payment Required` error. Add credits to resume usage immediately. No data is lost.

### Can I set spending limits?

Yes. You can configure monthly spending alerts and hard limits in the dashboard settings.

### Are there any hidden fees?

No. You pay only for the tokens you consume. There are no platform fees, no per-request fees, and no bandwidth charges.


# Overview

DOSClaw Connectors allow your AI agents to query live data, perform actions, and integrate business workflows with external platforms—including e-commerce stores, CRMs, social scheduling tools, accounting software, and developer services.

## How Connectors Work

```
User in Chat
     │
     ▼
[AI Agent] ─── Decides to call tool (e.g. search_products)
     │
     ▼
[DOSClaw Model Context Protocol (MCP) Runtime]
     │
     ▼
[Connector Cache Shield] (Singleflight + In-memory TTL Cache)
     │
     ▼
[DOS-Me Vault] (Secure OAuth Tokens & API Credentials)
     │
     ▼
[Third-Party Service API] (Haravan, Shopee, HubSpot, etc.)
```

### Two-Layer Security & Connection Architecture

Following the DOS Platform security model:

1. **Authentication & Token Storage (`DOS-Me`)**: OAuth connections, API tokens, and secret credentials are encrypted and stored in `public.provider_connections` and secured in the DOS-Me Vault. Raw secrets are never exposed to browser clients, agent containers, or LLM reasoning prompts.
2. **Agent Binding & Governance (`DOS.AI`)**: Individual AI agents are bound to provider connections via `dosai.agent_connection_bindings`. Workspace admins specify:
   * **Enabled Tools**: Selectively turn on or off specific capabilities (e.g. allow `search_products` and `check_order_status`, but disable `create_order`).
   * **Governance Policy**:
     * `autonomous`: Agent executes allowed read and write tools automatically.
     * `approval_required`: Agent prepares action parameters and requests human confirmation before mutating data.
     * `draft_only`: Agent can only read and prepare drafts; never commits live records.

***

## Connector Directory

### E-Commerce & Retail

* [Haravan Omnichannel](/plugins-and-connectors/ecommerce/haravan) — Vietnam retail, inventory, orders, customer sync.
* [Shopee Open Platform](/plugins-and-connectors/ecommerce/shopee) — SEA marketplace product lookup, live shipping, stock.
* [TikTok Shop](/plugins-and-connectors/ecommerce/tiktokshop) — Video commerce, live stream product specs, order status.
* [Shopify Storefront & Admin](/plugins-and-connectors/ecommerce/shopify) — Global e-commerce catalog, cart assistance, order lookup.
* [KiotViet Retail POS](/plugins-and-connectors/ecommerce/kiotviet) — Vietnam retail POS, branch stock, offline-to-online sync.
* [WooCommerce](/plugins-and-connectors/ecommerce/woocommerce) — Open-source WordPress store integration.

### CRM & Marketing

* [HubSpot CRM](/plugins-and-connectors/crm/hubspot) — Inbound contacts, companies, deals, pipelines.
* [Salesforce Enterprise CRM](/plugins-and-connectors/crm/salesforce) — Enterprise accounts, leads, opportunities, cases.
* [Crove Post](/plugins-and-connectors/crm/crove-post) — Multi-platform social scheduler and campaign analytics.
* [NueLink](/plugins-and-connectors/crm/nuelink) — Cross-platform social publishing automation.
* [WordPress Direct CMS](/plugins-and-connectors/crm/wordpress) — Blog post drafting, SEO metadata, media uploads.
* [Meta Ads & Lead Ads](/plugins-and-connectors/crm/meta) — Real-time lead form webhook, instant follow-up, CAPI.

### Finance & Invoicing

* [MISA Enterprise Suite](/plugins-and-connectors/finance/misa) — meInvoice, Inbound E-Invoice, ASP, AMIS Accounting, WeSign, eSign.
* [FinOne / Vbill](/plugins-and-connectors/finance/finone-vbill) — Revenue statistics, reconciliation, QR payment generation.
* [Stripe Payments](/plugins-and-connectors/finance/stripe) — Instant checkout links, subscription verification.

### Productivity & Developer

* [Google Workspace](/plugins-and-connectors/productivity/google-workspace) — Google Sheets, Calendar & Meet, Drive documents.
* [GitHub](/plugins-and-connectors/productivity/github) — Issue triage, PR summaries, commit history.
* [Notion](/plugins-and-connectors/productivity/notion) — Company wiki search, database queries, meeting notes.

***

## Connector Cache Shield

High-traffic bots serving peak concurrent customer queries benefit from the built-in **Connector Cache Shield**:

* **Singleflight Requests**: Prevents thundering herds by de-duplicating simultaneous requests for identical resources (e.g. 50 customers asking about the same flash-sale product).
* **Fast Read Cache**: Safe read operations (product searches, category lists) are cached in memory for sub-10ms response times.
* **Fail-Closed Write Safety**: Write and mutation operations (`create_order`, `refund`) always bypass the cache with strict idempotency keys.


# Channels

DOSClaw connects your autonomous AI agents directly to customer inboxes, team communication workspaces, and social messaging channels. Agents receive inbound messages in real time, apply relevant tools and knowledge bases, and send back rich interactive responses.

## Supported Channels

| Channel                                                            | Protocol                       | Message Types                    | Key Capabilities                              |
| ------------------------------------------------------------------ | ------------------------------ | -------------------------------- | --------------------------------------------- |
| [Zalo OA](/plugins-and-connectors/overview-1/zalo)                 | Webhook / Official Account API | Text, Images, Interactive Cards  | Follower sync, human takeover, 2-way chat     |
| [Telegram](/plugins-and-connectors/overview-1/telegram)            | Bot API / Webhook              | MarkdownV2, Media, Commands      | Group chats, inline queries, instant pairing  |
| [Facebook Messenger](/plugins-and-connectors/overview-1/messenger) | Meta Graph API / Webhook       | Text, Quick Replies, Media       | Page inbox, comment-to-inbox auto-reply       |
| [Discord](/plugins-and-connectors/overview-1/discord)              | Discord Gateway / REST         | Markdown, Embeds, Slash Commands | Server moderation, threads, role-based access |
| [Slack](/plugins-and-connectors/overview-1/slack)                  | Slack Events API / Block Kit   | Blocks, Modals, Mentions         | Team assistant, channel monitoring, workflows |
| [WhatsApp](/plugins-and-connectors/overview-1/whatsapp)            | WhatsApp Cloud API             | Text, Media, Templates           | Global reach, end-to-end encrypted, concierge |
| [Lark / Feishu](/plugins-and-connectors/overview-1/lark)           | Open Platform Bot API          | Interactive Cards, Rich Text     | Enterprise chat, group mentions, card actions |

***

## How Channels Work

```
User Message
     │
     ▼
[Messaging Provider] (Zalo, Telegram, Meta, Slack, etc.)
     │  (Inbound Webhook)
     ▼
[DOSClaw Gateway / Event Router]
     │  (Context + Identity + Scope Guard)
     ▼
[Agent Container / Engine]
     │  (Knowledge Base + MCP Tools + Reasoning)
     ▼
[Outbound Dispatcher]
     │  (Formatted response, cards, media)
     ▼
User In-App Reply
```

### Key Architectural Concepts

1. **Independent Channel Bindings**: A single AI agent can listen and respond across multiple channels simultaneously (e.g. Zalo OA for Vietnam customers, Telegram for global community, and Slack for internal staff) while sharing the same underlying memory, personality, and knowledge base.
2. **Cross-Channel Identity**: DOSClaw associates customer identity across different channels when an identifying attribute (phone number, email, or verified account ID) is provided.
3. **Human Takeover & Handoff**: Every channel supports human agent takeover. When an agent detects customer frustration, an explicit request for human support, or when a human agent sends a message from the native channel console, the AI automatically pauses for that conversation session.
4. **Rate Limiting & Safety**: Inbound webhooks are signature-verified and rate-limited. Egress responses adhere to platform quotas and prevent spam loops.

***

## Getting Started

To connect a channel to an agent:

1. Open your agent in the **DOS.AI Dashboard** (`https://app.dos.ai/agents`).
2. Navigate to the **Channels** tab.
3. Select your desired platform and follow the step-by-step connection wizard.
4. Verify the connection by sending a test message to your bot.


# Telegram

Connect your AI agent to Telegram to serve private direct messages, community group chats, support desks, and broadcast notifications with ultra-low latency.

## Capabilities

* **Private 1-on-1 Chat**: Direct, conversational assistant for personal productivity or customer support.
* **Group Assistant**: Mention your bot in group chats (`@YourBot`) or allow it to participate in group discussions.
* **Rich Markdown Formatting**: Bold, italics, inline code, syntax-highlighted code blocks, and structured lists.
* **Inline Commands & Buttons**: Interactive inline keyboards, callback buttons, and quick actions.
* **Voice & Media Handling**: Process voice notes (with automatic speech-to-text), images, documents, and files.

***

## Connection Methods

### Method 1: Instant Pairing via @DOSClawBot (Fastest)

1. Open Telegram and search for `@DOSClawBot`.
2. Send `/start` to receive your pairing code.
3. In the DOS.AI Dashboard under **Channels** $\rightarrow$ **Telegram**, enter the pairing code.
4. Your agent is immediately connected to your Telegram user account for private testing.

### Method 2: Custom Bot with BotFather (For Production & Brand Bots)

To run under your own brand's bot username (e.g. `@MyStoreAssistantBot`):

1. Open Telegram and chat with [@BotFather](https://t.me/botfather).
2. Send `/newbot` and follow the prompts to choose a display name and username (must end in `bot`).
3. BotFather will provide an **HTTP API Bot Token** formatted like `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`.
4. (Optional) Configure group privacy if you want the bot to see all group messages:
   * In BotFather, send `/setprivacy` $\rightarrow$ select your bot $\rightarrow$ choose `Disable`.
5. In **DOS.AI Dashboard**, open your Agent $\rightarrow$ **Channels** $\rightarrow$ **Telegram**.
6. Paste your Bot Token and click **Save & Connect**.
7. DOSClaw automatically registers the high-performance webhook with Telegram API.

***

## Group Chat Usage

When added to a Telegram group:

1. **Mention-Only Mode (Default)**: The bot responds only when explicitly mentioned (`@YourBot <message>`) or when a user directly replies to one of its messages. This prevents noise in active groups.
2. **Always-Listen Mode**: If enabled in Agent Channel Settings and Group Privacy is disabled in BotFather, the bot evaluates every group message and responds when relevant.

***

## Security & Privacy

* **Token Safety**: Your bot token is stored in encrypted vault storage (`public.provider_connections`). It is never exposed to client browsers or LLM prompts.
* **Allowed Users / Chat IDs**: You can restrict bot access to specific Telegram User IDs or Group Chat IDs using the whitelist setting in the dashboard.

***

## Troubleshooting

| Issue                                    | Cause                              | Solution                                                                                         |
| ---------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------ |
| Bot does not reply in groups             | Privacy mode enabled               | Chat with `@BotFather`, send `/setprivacy`, choose `Disable`.                                    |
| Conflict: terminated by other getUpdates | Another service is polling the bot | Ensure no local script or test server is running `getUpdates` with the same token.               |
| Formatting error                         | Invalid Markdown characters        | DOSClaw handles escaping automatically, but verify raw template outputs if using custom prompts. |


# Zalo Official Account (OA)

Deploy your DOSClaw AI agents to Vietnam's leading messaging platform. Connect your verified Zalo OA to automate 24/7 customer service, product inquiries, interactive menus, and order updates.

## Capabilities

* **2-Way Customer Chat**: Natural conversation with customers who message your Official Account.
* **Interactive Action Cards**: Rich product carousels, action buttons, and quick-reply options.
* **Follower Synchronization**: Track active followers and store user profiles.
* **Human Agent Takeover**: Automatic AI pause when a human support staff member joins the conversation.
* **Media Support**: Send and receive photos, PDFs, catalog images, and audio voice messages.

***

## Prerequisites

Before connecting, ensure you have:

1. A verified **Zalo Official Account** (Enterprise or Service type).
2. A **Zalo for Developers** account (`https://developers.zalo.me/`).
3. An active Zalo Developer App linked to your Official Account.

***

## Connection Methods

DOS.AI supports two setup options:

### Option 1: 1-Click OAuth (Recommended)

1. Go to **Dashboard** $\rightarrow$ **Integrations** or open your Agent's **Channels** tab.
2. Select **Zalo Official Account (OA)** and click **Connect via Zalo OAuth**.
3. Log in with your Zalo account that has Admin permissions on the target Official Account.
4. Select the Official Account to grant permissions (`oa.message`, `oa.profile`, `oa.media`).
5. Complete authorization. The access token and refresh token are securely vaulted in DOS-Me.

### Option 2: Manual Credentials / Webhook Pairing

If configuring custom webhooks or enterprise apps:

1. In **Zalo Developer Portal**, retrieve your **App ID** and **App Secret**.
2. Set the Webhook URL to:

   ```
   https://api.dos.ai/v1/channels/zalo/webhook
   ```
3. Set your verification token and subscribe to the `user_send_text`, `user_send_image`, and `follow` events.
4. In the DOS.AI Dashboard, enter your **OA ID**, **App ID**, and **App Secret**.

***

## Agent Configuration & Behavior

### Scope Guard & Business Focus

Zalo OA bots usually serve customer queries. By default, DOSClaw activates **Scope Guard** for business templates (e-commerce, real estate, customer service), ensuring the bot politely redirects off-topic requests (e.g. general coding or school homework) back to your business products and services.

### Human Agent Handoff

When a customer asks for human support ("cho gặp nhân viên", "gặp tư vấn viên"):

1. The agent transitions the conversation state to `human_takeover`.
2. The agent sends a polite confirmation to the customer.
3. Notification alerts can be dispatched to your team's Telegram or Slack group.
4. The AI stays muted until the customer issue is closed or the handover timeout expires.

***

## Troubleshooting

| Issue                             | Cause                         | Solution                                                           |
| --------------------------------- | ----------------------------- | ------------------------------------------------------------------ |
| Bot not replying to user messages | Webhook event not subscribed  | Ensure `user_send_text` event is enabled in Zalo Developer portal. |
| Token expired error               | Refresh token rotation failed | Re-authenticate via the **Reconnect** button on the dashboard.     |
| Image messages not sending        | Unsupported format            | Ensure images are PNG/JPEG and under 5MB.                          |
| Messages duplicated               | Multiple webhook endpoints    | Ensure only DOSClaw webhook URL is configured.                     |


# Facebook Messenger

Connect your Facebook Pages to DOSClaw to automate customer support, answer product questions directly in Messenger, and turn post comments into private conversation threads.

## Capabilities

* **Page Direct Messages**: 24/7 automated responses to inbound customer inquiries in Messenger.
* **Comment-to-Inbox (Auto-DM)**: Automatically send a private message to users who comment on your Facebook posts.
* **Interactive Quick Replies**: Action chips and buttons that let users choose options with one tap.
* **Product & Order Inquiries**: Connect with your store catalog to recommend products and check delivery status.
* **Human Takeover**: Live agent status sync with Meta Business Suite Inbox.

***

## Prerequisites

1. An active **Facebook Page** where you have **Admin** or **Task Access: Manage Messages** permissions.
2. A **Meta Business Account** linked to the Page.

***

## Step-by-Step Setup

### Step 1: Connect via Meta OAuth

1. In the DOS.AI Dashboard, navigate to **Integrations** or your Agent's **Channels** tab.
2. Click **Connect Facebook Messenger**.
3. Log in with your Facebook account and select the **Facebook Pages** you want the AI agent to manage.
4. Grant the required permissions:
   * `pages_messaging`: Send and receive messages as the Page.
   * `pages_manage_metadata`: Subscribe webhooks to page events.
   * `pages_read_engagement`: Read comments and post reactions for auto-reply.
5. Click **Confirm Authorization**.

### Step 2: Select Managed Pages

Once authorized, choose which specific Page(s) to bind to your agent. You can bind multiple Pages to the same agent or assign different agents to different Pages.

### Step 3: Configure Comment-to-Inbox (Optional)

1. Under the Page settings in DOS.AI, toggle **Comment Auto-Reply**.
2. Set trigger keywords or enable for all public comments.
3. Define whether the bot should:
   * Reply to the public comment on the post.
   * Send a private Messenger message with detailed information or a product link.

***

## Human Agent Handoff

DOSClaw integrates with Meta Business Suite Inbox:

* When an agent detects that a customer needs human assistance, it tags the conversation as `human_takeover`.
* If a human operator types a reply directly inside Meta Business Suite or the Facebook Pages Manager app, the AI agent automatically enters standby mode for 24 hours (or until manually released).

***

## Troubleshooting

| Issue                      | Cause                        | Solution                                                                                         |
| -------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------ |
| Bot not receiving messages | Webhook subscription dropped | In DOS.AI dashboard, click **Re-subscribe Webhook** next to your Page.                           |
| Permission denied error    | Admin rights revoked         | Verify your Facebook user account still has full task permissions on the Page.                   |
| Standard Messaging Window  | 24-hour policy restriction   | Meta requires messages outside 24h to use approved Message Tags (e.g. `CONFIRMED_EVENT_UPDATE`). |


# Discord

Integrate DOSClaw AI agents into your Discord servers. Provide automated community management, answer technical questions, run slash commands, and support thread-based discussions.

## Capabilities

* **Server & Channel Chat**: Mention the bot (`@AgentName <question>`) in public channels.
* **Direct Messages (DMs)**: Private support and 1-on-1 assistance for community members.
* **Slash Commands**: Register custom slash commands (e.g. `/ask`, `/help`, `/lookup`).
* **Thread Continuity**: Auto-create or participate in threads to keep main channels organized.
* **Role-Based Access**: Restrict bot commands or sensitive knowledge base content to specific Discord server roles.

***

## Prerequisites

1. A Discord account with administrative permissions on your target server.
2. A registered Discord Application created in the [Discord Developer Portal](https://discord.com/developers/applications).

***

## Setup Guide

### Step 1: Create a Discord Application

1. Open the [Discord Developer Portal](https://discord.com/developers/applications) and click **New Application**.
2. Give your bot a name and accept the terms.
3. Go to the **Bot** tab on the left sidebar:
   * Click **Reset Token** to generate a new **Bot Token**. Copy this value securely.
   * Under **Privileged Gateway Intents**, enable **Message Content Intent** (required for the bot to read user messages).
   * Enable **Server Members Intent** if your agent uses role or member lookups.

### Step 2: Invite the Bot to Your Server

1. In the Developer Portal, go to **OAuth2** $\rightarrow$ **URL Generator**.
2. Under **Scopes**, check:
   * `bot`
   * `applications.commands`
3. Under **Bot Permissions**, select:
   * `Send Messages`, `Read Message History`, `Send Messages in Threads`, `Embed Links`, `Attach Files`.
4. Copy the generated URL, open it in your browser, and select your server to authorize the bot.

### Step 3: Connect in DOS.AI Dashboard

1. In the DOS.AI Dashboard, go to your Agent's **Channels** tab.
2. Click **Add Channel** $\rightarrow$ **Discord**.
3. Paste the **Bot Token** and your **Application ID**.
4. Click **Save & Connect**.

***

## Channel Configuration

* **Allowed Channels**: You can specify a list of channel IDs where the bot is permitted to respond. In all other channels, the bot will remain silent even if mentioned.
* **Thread Participation**: Enable "Respond in Threads" to ensure long multi-turn answers stay contained inside threads.

***

## Troubleshooting

| Issue                          | Cause                           | Solution                                                                                        |
| ------------------------------ | ------------------------------- | ----------------------------------------------------------------------------------------------- |
| Bot appears offline            | Token invalid or regenerated    | Re-copy the bot token from Developer Portal and update in DOS.AI.                               |
| Bot does not read message text | Message Content Intent disabled | In Developer Portal $\rightarrow$ Bot tab, enable **Message Content Intent**.                   |
| Missing permissions            | Discord role below bot role     | Ensure the bot's role in server settings has permission to send messages in the target channel. |


# Slack

Connect DOSClaw AI agents to your enterprise Slack workspaces. Deploy internal knowledge base assistants, automated triage bots, incident response monitors, and productivity tools.

## Capabilities

* **Channel Mentions**: Mention `@AgentName` in any public or private channel to trigger an answer.
* **Direct Messages**: 1-on-1 conversations with employees for private tasks, HR inquiries, or IT helpdesk.
* **Threaded Replies**: Keeps channel conversations neat by replying directly in the message thread.
* **Block Kit UI**: Structured visual messages, action buttons, dropdown menus, and modal dialogs.
* **Multi-Tenant OAuth**: Single-click authorization for your entire workspace or enterprise grid.

***

## Prerequisites

1. Administrative or App Installation permissions in your target Slack workspace.
2. A Slack account associated with your organization.

***

## Connection Guide

### Option 1: 1-Click Slack App Installation (Recommended)

1. In the **DOS.AI Dashboard**, open your Agent $\rightarrow$ **Channels** $\rightarrow$ **Slack**.
2. Click **Add to Slack**.
3. You will be redirected to Slack's official authorization screen.
4. Select your target Slack Workspace from the top-right corner.
5. Review the requested permissions:
   * `app_mentions:read`: Listen to `@agent` mentions in channels.
   * `chat:write`: Send messages and replies.
   * `im:history` & `im:write`: Send and receive direct messages.
   * `channels:history` & `groups:history`: Contextual thread continuity.
6. Click **Allow**. The app will automatically register webhooks and secure credentials in the DOS-Me Vault.

### Option 2: Custom Slack App (Enterprise / Internal App)

For enterprises requiring custom internal Slack App credentials:

1. Create an app on [api.slack.com/apps](https://api.slack.com/apps).
2. Set the Request URL for Event Subscriptions to:

   ```
   https://api.dos.ai/v1/channels/slack/events
   ```
3. Subscribe to bot events: `app_mention`, `message.im`.
4. Install the app to your workspace and copy the **Bot User OAuth Token** (`xoxb-...`) and **Signing Secret**.
5. Paste these credentials into the custom fields in the DOS.AI Dashboard.

***

## Channel Setup in Slack

Once installed:

1. In any Slack channel, type `/invite @YourAgentName` to add the bot to the channel.
2. Tag the bot in a message:

   ```
   @MyAgent How do I request approval for new equipment?
   ```
3. The bot will automatically respond in a threaded reply, maintaining thread context for follow-up questions.

***

## Security & Access Control

* **Internal Data Protection**: Bot interactions adhere to organization-level tenant boundaries.
* **Channel Whitelist**: Restrict bot operation to specific Slack channels (e.g. `#help-it`, `#ask-marketing`).
* **Prompt Injection Defense**: Default Scope Guard filters prevent external manipulation when bots interact with mixed channel audiences.

***

## Troubleshooting

| Issue                         | Cause                      | Solution                                                                        |
| ----------------------------- | -------------------------- | ------------------------------------------------------------------------------- |
| "dispatch\_failed" on mention | Bot not invited to channel | Run `/invite @AgentName` in the channel.                                        |
| URL Verification failed       | Webhook endpoint mismatch  | Ensure you used `https://api.dos.ai/v1/channels/slack/events`.                  |
| Bot doesn't reply in DMs      | `message.im` event missing | Verify `message.im` is checked under Event Subscriptions in Slack App settings. |


# WhatsApp

Deploy your DOSClaw AI agents to WhatsApp to reach global customers with official Meta Cloud API infrastructure. Ideal for cross-border e-commerce, VIP hospitality, appointment scheduling, and customer concierge.

## Capabilities

* **Official Business Verification**: Operates via Meta's WhatsApp Business Cloud API using verified phone numbers.
* **24/7 AI Concierge**: Automated text and audio message processing.
* **Interactive Buttons & Lists**: Send structured list options and call-to-action buttons.
* **Template Messages**: Proactive shipping notifications, appointment reminders, and confirmation messages.
* **End-to-End Encryption**: Compliant with WhatsApp privacy and encryption standards.

***

## Prerequisites

1. A **Meta Business Manager** account.
2. A **Meta Developer Account** with a registered Meta App.
3. A clean phone number that is not currently registered on a consumer WhatsApp account (or migrated via Meta Cloud API).

***

## Setup Guide

### Step 1: Set Up Meta WhatsApp Cloud API

1. In the [Meta for Developers Portal](https://developers.facebook.com/), create an app with the **Business** type.
2. Add the **WhatsApp** product to your application.
3. In WhatsApp $\rightarrow$ **API Setup**, retrieve your:
   * **Phone Number ID**
   * **WhatsApp Business Account ID (WABA ID)**
   * **Permanent System User Access Token** (generated with `whatsapp_business_messaging` permissions in Business Manager).

### Step 2: Configure Inbound Webhooks

1. In your Meta App under **WhatsApp** $\rightarrow$ **Configuration**, click **Edit** on Webhook.
2. Set the Callback URL to:

   ```
   https://api.dos.ai/v1/channels/whatsapp/webhook
   ```
3. Enter your verification token (provided in the DOS.AI Dashboard when you start the setup wizard).
4. Under Webhook fields, click **Manage** and subscribe to **messages**.

### Step 3: Connect in DOS.AI Dashboard

1. In the DOS.AI Dashboard, go to your Agent's **Channels** tab.
2. Select **WhatsApp Business** and enter:
   * **Phone Number ID**
   * **WABA ID**
   * **System User Access Token**
3. Click **Save & Verify**.
4. Send a test message from a personal WhatsApp phone to your business number to verify instant reply.

***

## 24-Hour Customer Service Window

Meta enforces a strict 24-hour customer service window:

* **Customer-Initiated Conversations**: Once a customer messages your bot, the bot can respond freely with session messages for up to 24 hours.
* **Business-Initiated / Re-engagement**: After 24 hours of inactivity, proactive outbound messages must use pre-approved **WhatsApp Message Templates** (Utility, Authentication, or Marketing categories).

***

## Troubleshooting

| Issue                                | Cause                                           | Solution                                                                                                 |
| ------------------------------------ | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Error code 131030                    | Recipient phone number not on sandbox whitelist | In test mode, add recipient numbers to the allowed list in Meta portal, or register a live phone number. |
| Inbound message not triggering reply | Webhook field `messages` not subscribed         | In Meta App $\rightarrow$ WhatsApp $\rightarrow$ Configuration, ensure `messages` is checked.            |
| Expired Access Token                 | Temporary token used                            | Create a permanent System User in Meta Business Manager $\rightarrow$ System Users.                      |


# Lark / Feishu

Connect your DOSClaw agents to Lark (and Feishu) to provide enterprise collaboration, project triage, employee assistance, and automated card interactions in enterprise groups.

## Capabilities

* **Direct Messages & Group Chats**: Respond in private 1-on-1 chats and group mentions (`@AgentName`).
* **Interactive Message Cards**: Send structured interactive cards with buttons, date pickers, and status indicators.
* **Thread Context**: Respond within the thread to maintain topic organization.
* **Enterprise Identity**: Sync employee user IDs and organization departments for permissioned responses.

***

## Prerequisites

1. An enterprise account on **Lark** (`https://www.larksuite.com/`) or **Feishu** (`https://open.feishu.cn/`).
2. Administrator access to create enterprise custom applications in the Lark Developer Console.

***

## Setup Guide

### Step 1: Create an App in Lark Open Platform

1. Go to the [Lark Developer Console](https://open.larksuite.com/app).
2. Click **Create Custom App**, enter an app name, and upload an icon.
3. Under **Credentials & Basic Info**, copy the **App ID** and **App Secret**.

### Step 2: Enable Bot Feature & Permissions

1. In the app navigation, go to **Add Features** $\rightarrow$ select **Bot**.
2. Go to **Permissions & Scopes**:
   * Add `im:message` (Send and receive messages).
   * Add `im:message.group_at_msg:readonly` (Receive messages in groups where bot is mentioned).
   * Add `im:message.p2p_msg:readonly` (Receive private direct messages).
   * Add `im:chat:readonly` (Access group chat information).

### Step 3: Configure Event Subscriptions

1. Go to **Event Subscriptions**:
2. Set the Request URL to:

   ```
   https://api.dos.ai/v1/channels/lark/events
   ```
3. Subscribe to the event: `im.message.receive_v1`.
4. Copy the **Verification Token** and **Encrypt Key** (if enabled).

### Step 4: Connect in DOS.AI Dashboard

1. In the DOS.AI Dashboard, go to your Agent's **Channels** tab.
2. Select **Lark / Feishu**.
3. Enter your **App ID**, **App Secret**, **Verification Token**, and **Encrypt Key** (optional).
4. Publish a version of your custom app in the Lark Developer Console to make it available to your organization.

***

## Usage in Lark

1. Open any chat or group in Lark.
2. Search for the bot by name or add it via group settings.
3. Send `@YourBot <message>` in a group or message the bot directly in a private DM.

***

## Troubleshooting

| Issue                              | Cause                                  | Solution                                                                                       |
| ---------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------- |
| "Verification URL failed"          | Endpoint unreachable or token mismatch | Check that the verification token in DOS.AI matches the token in Lark developer console.       |
| Bot not replying to group mentions | App version not published              | Ensure you submitted a release version in Lark Developer Console and an org admin approved it. |
| Message encryption error           | Encrypt Key mismatch                   | If Encrypt Key is enabled in Lark, provide the exact string in the DOS.AI configuration.       |


# E-Commerce

Connect your online storefronts, marketplaces, and physical retail POS systems to DOSClaw AI agents. Enable agents to search product catalogs, check live multi-branch inventory, track orders, and assist customers with purchases across all chat channels.

## Supported Platforms

| Platform                                                          | Channel / Type               | Key Capabilities                                            |
| ----------------------------------------------------------------- | ---------------------------- | ----------------------------------------------------------- |
| [TikTok Shop](/plugins-and-connectors/ecommerce/tiktokshop)       | Video & Live Commerce        | Live stream inquiries, variant lookup, order status         |
| [Shopee Open Platform](/plugins-and-connectors/ecommerce/shopee)  | Marketplace (SEA)            | Product search, variant stock, live shipping tracking       |
| [Haravan Omnichannel](/plugins-and-connectors/ecommerce/haravan)  | Omnichannel Retail (Vietnam) | Real-time catalog, multi-warehouse stock, order fulfillment |
| [Shopify](/plugins-and-connectors/ecommerce/shopify)              | Global Storefront            | Product search, multi-currency pricing, cart assistance     |
| [KiotViet Retail POS](/plugins-and-connectors/ecommerce/kiotviet) | Retail POS (Vietnam)         | O2O offline-online sync, branch stock, barcode lookup       |
| [WooCommerce](/plugins-and-connectors/ecommerce/woocommerce)      | Self-Hosted WordPress        | Storefront products, order management, coupons              |

***

## Multi-Platform Commerce Suite

Many merchants sell across multiple platforms simultaneously (e.g. Shopee + TikTok Shop + physical store using KiotViet).

DOSClaw provides native **Multi-Platform Commerce Aggregation**:

* **Unified Product Search**: When a customer asks about a product, the agent can query multiple connected stores at once.
* **Platform-Qualified Identifiers**: Items are tagged with platform prefixes (e.g. `shopee:12048512`, `kiotviet:SP00142`) so stock checks and orders route to the correct platform automatically.
* **Connector Cache Shield**: High-concurrency read operations are cached with singleflight de-duplication to prevent rate limit exhaustion during flash sales.


# TikTok Shop

Connect your TikTok Shop Seller Center to DOSClaw AI agents. Empower your AI assistants during live streams, direct messages, and social campaigns with live product specs, inventory counts, and order fulfillment status.

## Capabilities & MCP Tools

| MCP Tool                   | Operation | Description                                                                         |
| -------------------------- | --------- | ----------------------------------------------------------------------------------- |
| `search_products`          | Read      | Search products in your TikTok Shop catalog by title, SKU, or category.             |
| `get_product_detail`       | Read      | Query variant details, live pricing, stock availability, and promotional links.     |
| `check_order_status`       | Read      | Look up customer TikTok Shop orders by Order ID.                                    |
| `get_fulfillment_tracking` | Read      | Retrieve logistics provider details, tracking numbers, and delivery package status. |

***

## Prerequisites

1. An active **TikTok Shop Seller** account (Vietnam, US, UK, SEA).
2. Store owner credentials to authorize third-party partner applications.

***

## Setup Guide

### Step 1: Connect via TikTok Shop OAuth

1. Open the **DOS.AI Dashboard** (`https://app.dos.ai/`).
2. Go to **Integrations** $\rightarrow$ select **TikTok Shop**.
3. Click **Connect TikTok Shop**.
4. Select your target market region (e.g. `Vietnam`, `United States`, `Southeast Asia`).
5. Log in to TikTok Shop Seller Center and review required permissions:
   * Read product catalog and inventory levels.
   * Read orders and shipping fulfillment events.
6. Click **Authorize**. Once approved, your `seller_id` and cipher tokens are saved to the secure vault.

### Step 2: Bind to Your AI Agent

1. In the Agent settings, navigate to **Integrations**.
2. Select **TikTok Shop** from your list of connected stores.
3. Configure the **Reply Context**:
   * Enable "Attach TikTok Product Showcase Links" so the agent formats answers with clickable purchase buttons.
4. Click **Save**.

***

## Example Live Commerce Use Cases

### Real-Time Live Stream FAQ Bot

While streaming on TikTok Live, buyers frequently ask about sizing, material, and bundle discounts:

> **Viewer**: "Mẫu đầm hoa nhí còn size S không shop, cao 1m55 mặc vừa không?"\
> **AI Agent**: Queries `get_product_detail({ "query": "đầm hoa nhí" })` and cross-references your agent's knowledge base sizing chart:\
> **Reply**: "Dạ đầm hoa nhí bên em còn sẵn 8 cái size S ạ! Chiều cao 1m55 nặng 45-50kg mặc size S vừa xinh chuẩn dáng nha. Bạn bấm vào giỏ hàng góc trái màn hình để nhận voucher giảm 20k ạ!"

### Order Tracking in Direct Messages

> **Customer**: "Shop kiểm tra đơn TikTok 57891234 giúp mình"\
> **AI Agent**: Calls `check_order_status({ "order_id": "57891234" })`\
> **Reply**: "Đơn hàng 57891234 của bạn đang được đơn vị vận chuyển J\&T Express giao đến khu vực Quận 1, dự kiến phát trong chiều nay ạ!"

***

## Troubleshooting

| Issue                 | Cause                    | Solution                                                                                |
| --------------------- | ------------------------ | --------------------------------------------------------------------------------------- |
| Authorization failed  | Seller region mismatch   | Ensure you selected the exact market region where your TikTok Shop is registered.       |
| Products show 0 stock | Inactive catalog listing | In TikTok Shop Seller Center, verify the product has passed product review and is live. |
| Token expired         | Token rotation desync    | Click **Reconnect** in DOS.AI dashboard to re-authenticate with TikTok Shop.            |


# Shopee Open Platform

Connect your Shopee merchant store to DOSClaw AI agents. Enable automated product recommendations, instant variant availability checks, live shipping status lookups, and customer chat assistance across Southeast Asia.

## Capabilities & MCP Tools

| MCP Tool             | Operation | Description                                                                                             |
| -------------------- | --------- | ------------------------------------------------------------------------------------------------------- |
| `search_products`    | Read      | Search store catalog across Shopee items by keyword, item ID, or category.                              |
| `get_product_detail` | Read      | Retrieve variant specifications, promotional prices, and real-time stock levels.                        |
| `check_order_status` | Read      | Query order status using Shopee Order SN (serial number) to report package tracking and delivery stage. |
| `get_shipping_info`  | Read      | Retrieve tracking numbers, logistics partner info (Shopee Xpress, SPX, J\&T), and estimated arrival.    |

***

## Prerequisites

1. An active **Shopee Seller Center** account in Vietnam, Singapore, Malaysia, Philippines, Thailand, or Indonesia.
2. Store login credentials with permissions to authorize third-party applications.

***

## Setup Guide

### Step 1: Authorize Shopee Store

1. In the **DOS.AI Dashboard**, navigate to **Integrations** $\rightarrow$ **Shopee Open Platform**.
2. Click **Connect Shop**.
3. Select your store region (e.g. `Vietnam - VN`, `Singapore - SG`).
4. You will be redirected to the official Shopee Open Platform authorization portal (`partner.shopeemobile.com`).
5. Log in with your Shopee Seller account and review the requested permissions:
   * Item Management & Catalog Read
   * Order Management & Logistics Tracking
6. Click **Confirm Authorization**.
7. Once confirmed, you will be redirected back to DOS.AI, where your `shop_id` is registered and verified.

### Step 2: Bind Shopee to Your AI Agent

1. Navigate to **Agents** $\rightarrow$ select your agent $\rightarrow$ **Integrations**.
2. Enable the **Shopee** connector for this agent.
3. If you run multiple stores (e.g. Shopee + TikTok Shop or Haravan), DOSClaw's **Multi-Platform Commerce Router** allows the agent to search across all stores simultaneously, qualifying item codes as `shopee:<item_id>`.
4. Click **Save**.

***

## Example Agent Interactions

### Product & Stock Inquiries

> **Customer**: "Shop ơi son Black Rouge A12 còn hàng không, có ship hỏa tốc không?"\
> **AI Agent**: Calls `get_product_detail({ "query": "Black Rouge A12" })`\
> **Response**: "Dạ chào bạn, Son kem Black Rouge Air Fit Velvet Tint màu A12 bên shop hiện còn sẵn 45 cây tại kho ạ. Shop có hỗ trợ Shopee Hỏa Tốc (giao trong 2h) bạn nhé! Bạn đặt hàng qua link Shopee của shop tại đây nha: \[Link]"

### Order Tracking

> **Customer**: "Kiểm tra đơn Shopee 260815AB1234 giúp mình với"\
> **AI Agent**: Calls `check_order_status({ "order_sn": "260815AB1234" })`\
> **Response**: "Đơn hàng 260815AB1234 của bạn đã được bàn giao cho đơn vị vận chuyển SPX Express vào sáng nay, dự kiến giao vào ngày mai 17/08 ạ!"

***

## Security & Reliability

* **Token Refresh**: Shopee OAuth tokens expire every 4 hours and are automatically refreshed by DOS-Me background workers using the encrypted refresh token.
* **Push Notifications**: Inbound order status changes are synced in real time via Shopee Push API.

***

## Troubleshooting

| Issue                 | Cause                             | Solution                                                                                |
| --------------------- | --------------------------------- | --------------------------------------------------------------------------------------- |
| Authorization timeout | Browser session expired           | Ensure you complete the Shopee Seller login within 3 minutes of opening the OAuth link. |
| Shop ID disconnected  | Store permissions changed         | Click **Reconnect** in DOS.AI dashboard to issue fresh tokens.                          |
| Order SN not found    | Order belongs to a different shop | Verify that the order was placed on the authorized Shopee store.                        |


# Haravan Omnichannel

Connect your Haravan store to DOSClaw AI agents to automate product inquiries, inventory lookups, order status tracking, and fulfillment updates across Zalo OA, Messenger, Telegram, and your website.

## Capabilities & MCP Tools

| MCP Tool             | Operation | Description                                                                                        |
| -------------------- | --------- | -------------------------------------------------------------------------------------------------- |
| `search_products`    | Read      | Search product catalog by keywords, title, SKU, or tags. Returns pricing, description, and images. |
| `get_product_detail` | Read      | Retrieve complete product specifications, variants, and real-time inventory count.                 |
| `check_order_status` | Read      | Look up customer orders by order ID or phone number. Returns fulfillment and shipping status.      |
| `create_order`       | Write     | Generate a draft or confirmed order with selected line items and customer delivery info.           |

***

## Prerequisites

1. An active **Haravan store** (`your-shop.myharavan.com`).
2. An account with **Owner** or **Administrator** privileges in your Haravan admin dashboard.

***

## Setup Guide

### Step 1: Connect via Haravan OAuth

1. Open the **DOS.AI Dashboard** (`https://app.dos.ai/`).
2. Navigate to **Integrations** $\rightarrow$ find **Haravan Omnichannel**.
3. Click **Connect Store**.
4. Enter your Haravan shop domain (e.g. `your-store-name.myharavan.com`).
5. You will be redirected to Haravan's secure authorization screen (`accounts.haravan.com`).
6. Log in with your Haravan credentials and approve access permissions:
   * Read Products & Variants (`com.read_products`)
   * Read Orders & Customers (`com.read_orders`)
   * Offline Access for automatic token refresh (`offline_access`)
7. Upon successful authorization, you are redirected back to DOS.AI.

### Step 2: Bind to Your AI Agent

1. Open your agent in **Agents** $\rightarrow$ **Select Agent** $\rightarrow$ **Integrations / Connectors**.
2. Under **Connected Stores**, check **Haravan Omnichannel**.
3. Select your desired governance policy:
   * **Autonomous**: Agent answers stock inquiries and looks up orders automatically.
   * **Approval Required**: For order creation, the agent asks the customer or operator to confirm details before submission.
4. Click **Save Changes**.

***

## How AI Agents Use Haravan

### 1. Natural Language Product Search

Customers ask:

> "Bên bạn có áo sơ mi trắng size L không, giá bao nhiêu?"

The agent automatically calls:

```json
search_products({ "query": "áo sơ mi trắng", "limit": 5 })
```

The agent inspects the inventory across branches, extracts the price and product URL, and answers:

> "Dạ bên em còn mẫu Áo Sơ Mi Trắng Oxford size L, giá 350.000đ, hiện còn 12 cái trong kho ạ! Bạn có muốn đặt hàng luôn không?"

### 2. Live Order Tracking

Customers provide an order code or phone number:

> "Đơn hàng #HD1042 của mình giao đến đâu rồi?"

The agent calls:

```json
check_order_status({ "order_id": "HD1042" })
```

Returns carrier information (e.g. GHN, Viettel Post) and current delivery stage without requiring human intervention.

***

## Security & Architecture

* **Token Security**: OAuth access tokens and rotating refresh tokens are stored in the DOS-Me Vault (`public.provider_connections`). Secret credentials never enter LLM prompts or client-side storage.
* **Single-Flight Cache Shield**: Frequent catalog queries are cached to protect your Haravan API rate limits during peak sale events.

***

## Troubleshooting

| Issue                             | Cause                          | Solution                                                                                  |
| --------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------- |
| "Token expired" or 401 error      | Haravan refresh token invalid  | Click **Reconnect** in DOS.AI Dashboard to re-authorize the OAuth connection.             |
| Product search returns no results | Inactive status or missing SKU | Ensure products in Haravan admin are marked as **Active** and visible on the Web channel. |
| Order lookup denied               | Phone number mismatch          | For customer privacy, verify that the order's phone matches the verified caller ID.       |


# Shopify Storefront & Admin

Connect your Shopify store to DOSClaw AI agents. Provide high-converting product recommendations, live inventory checks, cart assistance, and order status lookups across global markets.

## Capabilities & MCP Tools

| MCP Tool             | Operation | Description                                                                                                     |
| -------------------- | --------- | --------------------------------------------------------------------------------------------------------------- |
| `search_products`    | Read      | Search Shopify store collections, products, tags, and vendors by natural language.                              |
| `get_product_detail` | Read      | Fetch variant options, inventory levels across multi-location warehouses, and currency pricing.                 |
| `check_order_status` | Read      | Look up customer orders by order number (e.g. `#1001`) or customer email. Returns line items and tracking URLs. |
| `create_draft_order` | Write     | Create draft orders with customized discounts, shipping addresses, and send checkout invoices.                  |

***

## Prerequisites

1. An active **Shopify Store** (Basic, Shopify, Advanced, or Shopify Plus).
2. Store owner or staff account with permissions to install and configure apps.

***

## Setup Guide

### Method 1: Shopify App OAuth (Recommended)

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **Shopify**.
2. Click **Connect Store**.
3. Enter your `.myshopify.com` store domain (e.g. `your-brand.myshopify.com`).
4. You will be redirected to the Shopify App installation screen.
5. Review the requested access scopes:
   * `read_products`, `read_inventory`: Real-time stock and catalog lookup.
   * `read_orders`, `read_fulfillments`: Order tracking and delivery milestones.
   * `write_draft_orders`: (Optional) Allow agent to assemble customer checkout links.
6. Click **Install App**.

### Method 2: Custom App (Private Access Token)

If your enterprise uses a custom private app:

1. In Shopify Admin, go to **Settings** $\rightarrow$ **Apps and sales channels** $\rightarrow$ **Develop apps**.
2. Create a custom app named "DOSClaw Assistant".
3. Configure the Admin API access scopes listed above.
4. Click **Install app** and reveal the **Admin API access token** (`shpat_...`).
5. In DOS.AI Dashboard, select **Custom App Credentials** and paste your Shopify Domain and Access Token.

***

## Example AI Agent Scenarios

### Multilingual Global Sales Assistant

> **Customer (US)**: "Do you have waterproof hiking boots in men's size 10?"\
> **AI Agent**: Queries `search_products({ "query": "waterproof hiking boots", "tag": "men" })`\
> **Reply**: "Yes! We have the **Apex Trail Pro Waterproof Boots** in Men's Size 10 (US), priced at $149.00 USD. We currently have 7 pairs remaining at our California fulfillment center. Would you like me to send you the direct checkout link?"

### Self-Serve Order Tracking

> **Customer**: "Where is my order #1084?"\
> **AI Agent**: Calls `check_order_status({ "order_number": "1084" })`\
> **Reply**: "Your order #1084 was shipped via DHL Express on August 14. Here is your tracking number: `940011189956` ([Track on DHL](https://dhl.com)). Current status: **Out for delivery**."

***

## Troubleshooting

| Issue                      | Cause                                              | Solution                                                                                             |
| -------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| "Invalid myshopify domain" | Custom domain used instead of primary store domain | Enter your original `.myshopify.com` URL (found in Shopify Settings $\rightarrow$ Domains).          |
| Rate limit 429 error       | High API call volume                               | DOSClaw implements leaky-bucket throttling and singleflight cache to stay within Shopify API quotas. |
| Missing line item images   | Product media not published to sales channel       | In Shopify Admin, ensure product media is enabled for the Storefront sales channel.                  |


# KiotViet Retail POS

Connect KiotViet—Vietnam's leading retail point-of-sale (POS) and inventory management software—to DOSClaw AI agents. Enable unified online-to-offline (O2O) stock management, branch inventory inquiries, price list checks, and offline order tracking.

## Capabilities & MCP Tools

| MCP Tool             | Operation | Description                                                                                |
| -------------------- | --------- | ------------------------------------------------------------------------------------------ |
| `search_products`    | Read      | Search product catalog by barcode, SKU, or name across POS inventory.                      |
| `get_branch_stock`   | Read      | Query real-time inventory levels broken down by specific retail branches/stores.           |
| `get_product_prices` | Read      | Look up retail prices, wholesale tiers, and active promotional discounts.                  |
| `check_order_status` | Read      | Look up customer invoices and delivery tickets by KiotViet invoice code (e.g. `HD000123`). |

***

## Prerequisites

1. An active **KiotViet** account (Retail, F\&B, or Pharmacy edition).
2. KiotViet Open API access enabled for your retailer (`https://developer.kiotviet.vn/`).
3. **Retailer Name**, **Client ID**, and **Client Secret** issued by KiotViet.

***

## Setup Guide

### Step 1: Retrieve KiotViet API Credentials

1. Log in to your KiotViet management portal (`https://manage.kiotviet.vn/`).
2. Go to **Thiết lập** (Settings) $\rightarrow$ **Thiết lập cửa hàng** $\rightarrow$ **Thiết lập tính năng**.
3. Under **Ứng dụng & Tích hợp**, click **Tạo ứng dụng kết nối** (Create connection app) or register on KiotViet Developer Portal.
4. Note your:
   * **Retailer (Tên gian hàng)**: The identifier in your store URL (e.g. `cuahangthoitrang`).
   * **Client ID**: Public application client key.
   * **Client Secret**: Private secret key.

### Step 2: Connect in DOS.AI Dashboard

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **KiotViet Retail POS**.
2. Click **Connect KiotViet**.
3. Fill in:
   * **Retailer Name**
   * **Client ID**
   * **Client Secret**
4. Click **Test & Connect**. DOS.AI verifies connectivity with KiotViet's OAuth Token endpoint (`https://id.kiotviet.vn/connect/token`).

### Step 3: Bind to Your AI Agent

1. Open your agent $\rightarrow$ **Integrations** $\rightarrow$ enable **KiotViet**.
2. (Optional) Set the **Default Branch ID** if your bot represents a specific store location.
3. Save changes.

***

## Example O2O Inquiries

### Branch Stock Check

> **Customer**: "Shop xem giúp em chi nhánh Cầu Giấy còn mẫu giày sneaker trắng size 38 không ạ?"\
> **AI Agent**: Calls `get_branch_stock({ "sku": "GI-SNK-TR-38", "branch": "Cầu Giấy" })`\
> **Response**: "Dạ chào bạn, chi nhánh Cầu Giấy (125 Cầu Giấy) hiện còn 3 đôi size 38 ạ! Bạn có muốn shop giữ hàng trước để bạn qua thử không ạ?"

### Price & Barcode Search

> **Customer**: "Quét mã vạch 8935001234567 xem giá bao nhiêu?"\
> **AI Agent**: Calls `search_products({ "barcode": "8935001234567" })`\
> **Response**: "Mã vạch 8935001234567 là sản phẩm **Nước hoa vùng kín Foellie Eau de Bijou 5ml**, giá niêm yết là 210.000đ ạ."

***

## Troubleshooting

| Issue                             | Cause                         | Solution                                                                                                  |
| --------------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------- |
| "Invalid retailer or credentials" | Typos in Client ID/Secret     | Copy the exact credentials from KiotViet settings without leading/trailing spaces.                        |
| Stock numbers out of date         | KiotViet inventory sync delay | KiotViet stock caches for up to 60 seconds; verify direct in KiotViet POS if recent transaction occurred. |
| Inactive branch error             | Branch ID closed or archived  | Verify in KiotViet admin that the branch is marked as active.                                             |


# WooCommerce

Connect self-hosted WordPress WooCommerce stores to DOSClaw AI agents. Provide 24/7 store support, inventory lookups, coupon code verification, and order tracking directly via WooCommerce REST API.

## Capabilities & MCP Tools

| MCP Tool             | Operation | Description                                                                            |
| -------------------- | --------- | -------------------------------------------------------------------------------------- |
| `search_products`    | Read      | Query store products by name, category slug, SKU, or attribute (color, size).          |
| `get_product_detail` | Read      | Retrieve product prices, description, stock status, gallery images, and variation IDs. |
| `check_order_status` | Read      | Look up WooCommerce order status, line items, customer notes, and payment status.      |
| `verify_coupon`      | Read      | Validate promotion coupons, expiration dates, and discount calculations.               |

***

## Prerequisites

1. A self-hosted WordPress site with the **WooCommerce** plugin installed and active.
2. An HTTPS domain with a valid SSL certificate (required by WooCommerce REST API).
3. Administrator access to generate WooCommerce REST API keys.

***

## Setup Guide

### Step 1: Generate WooCommerce REST API Keys

1. Log in to your WordPress Admin Dashboard.
2. Go to **WooCommerce** $\rightarrow$ **Settings** $\rightarrow$ **Advanced** $\rightarrow$ **REST API**.
3. Click **Add key**.
4. Set:
   * **Description**: `DOSClaw AI Agent`
   * **User**: Select an admin user account.
   * **Permissions**: `Read` (or `Read/Write` if using order creation tools).
5. Click **Generate API key**.
6. Copy the **Consumer Key** (`ck_...`) and **Consumer Secret** (`cs_...`). Keep this tab open—WooCommerce only displays the secret once.

### Step 2: Connect in DOS.AI Dashboard

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **WooCommerce**.
2. Click **Connect Store**.
3. Enter:
   * **Store URL**: Your website's root URL (e.g. `https://my-store.com`).
   * **Consumer Key**: `ck_...`
   * **Consumer Secret**: `cs_...`
4. Click **Verify & Connect**. DOSClaw makes a lightweight test call to `GET /wp-json/wc/v3/system_status`.

### Step 3: Bind to Your Agent

1. Open your agent $\rightarrow$ **Integrations** $\rightarrow$ enable **WooCommerce**.
2. Save changes.

***

## Multi-Platform Commerce Aggregation

If your business sells on both WooCommerce (website) and an offline POS (like KiotViet):

* DOSClaw supports **Multi-Platform Commerce**: Your agent can query both stores at once.
* Products are qualified with platform prefixes (e.g. `woocommerce:11`, `kiotviet:DH000002`).
* The agent automatically routes customer questions to the appropriate store.

***

## Troubleshooting

| Issue                         | Cause                                       | Solution                                                                                                                                              |
| ----------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| 401 Unauthorized error        | WordPress permalinks set to Plain           | WooCommerce REST API requires "Pretty Permalinks". Go to WP Admin $\rightarrow$ Settings $\rightarrow$ Permalinks $\rightarrow$ select **Post name**. |
| SSL / HTTPS error             | Self-signed or expired SSL                  | WooCommerce API requires a valid HTTPS certificate.                                                                                                   |
| Cloudflare / WAF blocking API | Cloudflare Bot Fight Mode blocking requests | In Cloudflare WAF, add a custom firewall rule to allow requests with User-Agent `DOSClaw/1.0`.                                                        |


# CRM & Marketing

Connect customer relationship management (CRM) systems, social media schedulers, and ad platforms to DOSClaw AI agents. Enable automated lead capture, pipeline updates, blog creation, and multi-channel social campaign execution with review-gated approval workflows.

## Supported Platforms

| Platform                                                            | Type               | Key Capabilities                                     |
| ------------------------------------------------------------------- | ------------------ | ---------------------------------------------------- |
| [HubSpot CRM](/plugins-and-connectors/crm/hubspot)                  | Inbound CRM        | Contact lookup, company data, deal pipelines         |
| [Salesforce Enterprise CRM](/plugins-and-connectors/crm/salesforce) | Enterprise CRM     | Leads, Accounts, Opportunities, Service Cloud cases  |
| [Crove Post](/plugins-and-connectors/crm/crove-post)                | Social Scheduler   | Multi-channel publishing, analytics, campaign review |
| [NueLink](/plugins-and-connectors/crm/nuelink)                      | Social Automation  | Cross-platform scheduling queues (10+ networks)      |
| [WordPress Direct CMS](/plugins-and-connectors/crm/wordpress)       | Content Management | SEO blog post drafts, category tagging, media upload |
| [Meta Ads & Lead Ads](/plugins-and-connectors/crm/meta)             | Ad Automation      | Real-time lead form webhook, instant follow-up, CAPI |

***

## Marketing Governance & Review Gates

To safeguard brand voice and ad spend, DOSClaw enforces governance modes:

* **Review Gated (`draft_only`)**: The agent drafts articles, social posts, or ad actions and submits them for human review.
* **Approval Required (`approval_required`)**: Mutating actions require explicit confirmation from a manager before dispatch.
* **Autonomous (`autonomous`)**: Routine tasks (lead ingestion, contact lookups) execute automatically.


# HubSpot CRM

Connect your HubSpot CRM account to DOSClaw AI agents. Enable customer service, sales development (SDR), and account management agents to search contacts, look up company profiles, check active deal stages, and inspect pipeline health in real time.

## Capabilities & MCP Tools

| MCP Tool              | Operation | Description                                                                                                 |
| --------------------- | --------- | ----------------------------------------------------------------------------------------------------------- |
| `search_contacts`     | Read      | Search CRM contacts by email, phone, or name. Returns contact properties, lifecycle stage, and lead status. |
| `get_contact_detail`  | Read      | Retrieve complete contact record including owner ID, associated company, and custom properties.             |
| `search_companies`    | Read      | Query companies by domain or name. Returns company revenue, industry, and phone.                            |
| `search_deals`        | Read      | Search active and closed deals by name, pipeline, or associated contact/company.                            |
| `get_pipeline_stages` | Read      | Fetch deal pipelines, stages, probability percentages, and stage IDs.                                       |

***

## Prerequisites

1. An active **HubSpot account** (Free, Starter, Professional, or Enterprise).
2. Super Admin or App Marketplace installation permissions in your HubSpot portal.

***

## Setup Guide

### Step 1: Connect via HubSpot OAuth

1. In the **DOS.AI Dashboard**, navigate to **Integrations** $\rightarrow$ **HubSpot CRM**.
2. Click **Connect with HubSpot**.
3. You will be redirected to HubSpot's official authorization screen (`app.hubspot.com`).
4. Select your target HubSpot Account / Portal.
5. Review the requested CRM scopes:
   * `crm.objects.contacts.read`: Search and read contacts.
   * `crm.objects.companies.read`: Search companies.
   * `crm.objects.deals.read`: Look up deals and pipelines.
   * `crm.schemas.contacts.read`: Retrieve custom property mappings.
6. Click **Connect app**.
7. DOS-Me securely encrypts and vaults the generated OAuth tokens.

### Step 2: Bind to Your Agent

1. Open your agent $\rightarrow$ **Integrations** $\rightarrow$ enable **HubSpot CRM**.
2. Configure permissions:
   * **Allowed Objects**: Toggle Contacts, Companies, and Deals.
3. Click **Save Changes**.

***

## Example Conversational Workflows

### Contact & Lead Verification

When a customer interacts with your agent on WhatsApp, Telegram, or Web Chat:

> **Customer**: "Chào bạn, mình là Hoàng từ công ty ABC Logistics (email: <hoang@abclogistics.vn>). Dự án trước báo giá đến đâu rồi?"\
> **AI Agent**: Calls `search_contacts({ "email": "hoang@abclogistics.vn" })` followed by `search_deals({ "associated_contact_id": "189421" })`\
> **Response**: "Dạ chào anh Hoàng! Em thấy trên hệ thống hợp đồng triển khai giải pháp kho vận của ABC Logistics đang ở giai đoạn **Thương thảo hợp đồng** do chuyên viên Nguyễn Văn Nam phụ trách. Anh Nam đã gửi bản phụ lục hôm qua qua email của anh. Anh có cần em kết nối trực tiếp với anh Nam không ạ?"

***

## Security & Architecture

* **No Third-Party Brokers**: Unlike generic integration wrappers, DOS.AI connects directly to HubSpot's official API (`api.hubapi.com`). No third-party data broker touches your customer records.
* **Zero-Secret Containers**: Agents execute tools through the DOSClaw Gateway runtime. Access tokens are dynamically injected per request and never stored in agent disk memory or LLM contexts.

***

## Troubleshooting

| Issue                             | Cause                   | Solution                                                                           |
| --------------------------------- | ----------------------- | ---------------------------------------------------------------------------------- |
| 403 Forbidden on Deals            | Missing deal scopes     | In HubSpot, verify your user account has access to the Sales Hub and Deals object. |
| Contact search returns no records | Strict query formatting | Search by exact email or international phone format (e.g. `+84901234567`).         |
| Token refresh failure             | Portal uninstalled app  | Re-click **Connect with HubSpot** in the dashboard to re-authorize.                |


# Salesforce Enterprise CRM

Connect Salesforce Enterprise CRM to DOSClaw AI agents. Empower sales, service, and operations teams with automated Lead ingestion, Account and Contact lookups, Opportunity tracking, and Service Cloud case creation.

## Capabilities & MCP Tools

| MCP Tool               | Operation | Description                                                                           |
| ---------------------- | --------- | ------------------------------------------------------------------------------------- |
| `search_leads`         | Read      | Search Salesforce Leads by email, company name, status, or lead source.               |
| `get_account_detail`   | Read      | Retrieve complete Salesforce Account records, billing addresses, and parent accounts. |
| `search_opportunities` | Read      | Query active Opportunities, stage names, probability percentages, and close dates.    |
| `get_case_status`      | Read      | Look up customer support cases in Service Cloud by Case Number or subject.            |
| `create_case`          | Write     | Open a new support ticket in Service Cloud when an issue requires escalation.         |

***

## Prerequisites

1. A **Salesforce Enterprise, Unlimited, or Developer Edition** org.
2. An administrative account with permissions to install Connected Apps (`Manage Connected Apps`).

***

## Setup Guide

### Step 1: Authorize via Salesforce OAuth

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **Salesforce Enterprise CRM**.
2. Click **Connect Salesforce**.
3. Choose your instance type:
   * **Production / Developer Edition**: `login.salesforce.com`
   * **Sandbox / Test Environment**: `test.salesforce.com`
4. Log in with your Salesforce credentials and click **Allow** to grant access scopes:
   * `api` (Access and manage your data)
   * `refresh_token, offline_access` (Perform requests at any time)
5. Upon callback, DOS-Me securely stores your org URL and vaulted OAuth refresh credentials.

### Step 2: Bind to Agent & Select Governance

1. In Agent Settings $\rightarrow$ **Integrations**, toggle **Salesforce**.
2. Set the governance mode:
   * `read_only`: Agent can search records and cases but cannot create or modify data.
   * `approval_required`: Creating cases or updating lead statuses requires human staff approval.
   * `autonomous`: Agent creates triage cases directly in Service Cloud.

***

## Example Enterprise Scenario

### VIP Support Escalation

> **Customer**: "Our production server is throwing timeout errors on our API integration. Case #002910."\
> **AI Agent**: Calls `get_case_status({ "case_number": "002910" })`\
> **Reply**: "I've pulled up Case #002910 (Priority: High). Technical Account Manager David Chen is currently assigned. I have escalated this directly to the Tier 3 on-call queue and attached your latest error logs."

***

## Troubleshooting

| Issue                                 | Cause                                     | Solution                                                                                                                                             |
| ------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| "OAUTH\_APPROVAL\_ERROR\_GENERIC"     | Connected app blocked in Salesforce Org   | In Salesforce Setup $\rightarrow$ Connected Apps OAuth Usage, ensure DOSClaw app is set to **Admin approved users are pre-authorized** or unblocked. |
| Sandbox vs Production domain mismatch | Selected wrong login endpoint             | Re-connect and choose Sandbox if testing on a `.sandbox.my.salesforce.com` domain.                                                                   |
| API Limit Exceeded                    | Daily Salesforce REST API calls exhausted | DOSClaw implements smart caching; review API allocations in Salesforce System Overview.                                                              |


# Crove Post

Connect **Crove Post** (`post.crove.com`)—DOS's first-party social publishing and marketing platform—to your AI agents. Empower marketing agents to discover social channels, upload media assets, draft campaign posts, schedule publications across platforms, and inspect engagement analytics with review-gated approval workflows.

## Capabilities & MCP Tools

DOSClaw exposes normalized, English `snake_case` MCP tools for Crove Post:

| MCP Tool                          | Operation | Description                                                                                         |
| --------------------------------- | --------- | --------------------------------------------------------------------------------------------------- |
| `list_channels`                   | Read      | List active social channels (Facebook, X/Twitter, LinkedIn, Instagram, TikTok, YouTube, Pinterest). |
| `list_channel_groups`             | Read      | Discover organization channel groups and brand profiles.                                            |
| `list_social_posts`               | Read      | Query drafts, scheduled posts, and historical published content with status filters.                |
| `get_social_post_analytics`       | Read      | Retrieve engagement statistics (impressions, clicks, likes, shares, comments) for published posts.  |
| `get_channel_metrics`             | Read      | Aggregated audience growth and channel performance metrics.                                         |
| `create_social_post_draft`        | Write     | Draft social posts with text copy, media attachments, first comments, and thread replies.           |
| `schedule_social_posts`           | Write     | Schedule approved posts to specific date/time slots across multiple channels.                       |
| `upload_marketing_media_from_url` | Write     | Ingest remote image or video URLs into Crove Post's CDN for social attachments.                     |

***

## Prerequisites

1. An account on **Crove Post** (`https://post.crove.com`).
2. Connected social profiles on Crove Post (e.g. your Facebook Page, LinkedIn Company, X handle, or Instagram Business account).

***

## Setup Guide

### Step 1: Connect via 1-Click OAuth

1. Open the **DOS.AI Dashboard** (`https://app.dos.ai/`).
2. Navigate to **Integrations** $\rightarrow$ **Crove Post**.
3. Click **Connect Crove Post**.
4. Authorize access. DOSClaw pairs your DOS organization directly with your Crove Post workspace.
5. All credentials and `pos_` access tokens are securely vaulted in DOS-Me.

### Step 2: Bind to Marketing Agents

1. In Agent Settings, select your Marketing or Content Creator agent.
2. Under **Integrations**, toggle **Crove Post**.
3. Set your **Governance Policy**:
   * **Review Gated (Default & Recommended)**: The agent creates drafts and submits them for human review. Posts are not published live without explicit approval.
   * **Autonomous**: The agent is authorized to schedule and publish approved content directly to connected channels.
4. Click **Save**.

***

## Human Approval & Review Workflow

To protect brand reputation, DOSClaw enforces a strict approval gate for social media:

1. **Content Generation**: The AI agent drafts a series of posts based on a marketing prompt or calendar topic.
2. **Draft Submission**: The agent calls `create_social_post_draft` and provides a review preview card in chat.
3. **Operator Verification**: The business owner or marketing lead reviews the copy, hashtags, and media preview.
4. **Approval & Dispatch**: Once approved (via the dashboard button or chat confirmation), Crove Post queues and schedules the post to the designated social networks.

***

## Analytics & Performance Monitoring

Marketing agents can also inspect post performance:

> **User**: "Tuần trước bài viết về AI Agent trên LinkedIn và Facebook tiếp cận được bao nhiêu người?"\
> **Agent**: Calls `get_channel_metrics` and `get_social_post_analytics`\
> **Response**: "Tuần trước, 3 bài đăng trên LinkedIn và Facebook Page đã đạt tổng cộng 14.850 lượt hiển thị (impressions), 842 lượt tương tác (engagements) và 126 lượt click vào link landing page ạ!"

***

## Troubleshooting

| Issue                           | Cause                                | Solution                                                                                                    |
| ------------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| "No active channels found"      | Channels not connected in Crove Post | Open `post.crove.com`, navigate to Channels, and connect your social accounts first.                        |
| Post rejected by social network | Media format or aspect ratio invalid | Verify video length and image dimensions meet individual platform rules (e.g. Instagram Reels vs X images). |
| Approval link expired           | One-time action ticket timeout       | Generate a new draft by asking the agent to refresh the campaign.                                           |


# NueLink Social Scheduler

Connect NueLink—a multi-platform social media management and automation tool—to DOSClaw AI agents. Schedule, auto-publish, and queue viral marketing content across Facebook, Instagram, Twitter/X, LinkedIn, TikTok, YouTube Shorts, and Pinterest.

## Capabilities & MCP Tools

| MCP Tool           | Operation | Description                                                                                 |
| ------------------ | --------- | ------------------------------------------------------------------------------------------- |
| `list_collections` | Read      | Fetch active NueLink collections (content queues, topical categories, and campaigns).       |
| `list_channels`    | Read      | Retrieve connected social profiles and platform destinations.                               |
| `create_post`      | Write     | Draft or schedule a social media post with captions, hashtags, links, and image/video URLs. |
| `get_post_status`  | Read      | Check publishing status, scheduled time, and distribution progress.                         |

***

## Prerequisites

1. An active account on **NueLink** (`https://nuelink.com/`).
2. A generated **NueLink API Key** from your NueLink account settings.

***

## Setup Guide

### Step 1: Obtain Your NueLink API Key

1. Log in to your NueLink dashboard.
2. Go to **Settings** $\rightarrow$ **API & Integrations**.
3. Click **Generate New API Key**.
4. Copy the API Key securely.

### Step 2: Connect in DOS.AI Dashboard

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **NueLink Social Scheduler**.
2. Click **Connect NueLink**.
3. Paste your **API Key** into the credential field.
4. Click **Verify & Connect**. DOS.AI verifies your key by querying `GET /api/v1/user`.

### Step 3: Bind to Your Marketing Agent

1. In Agent Settings $\rightarrow$ **Integrations**, toggle **NueLink**.
2. Select default destination collections (e.g. "Weekly Tips", "Product Launches").
3. Set your approval governance (`approval_required` or `autonomous`).
4. Click **Save**.

***

## Example Marketing Workflow

Agents create cross-platform queues without manual formatting:

> **User**: "Tạo 3 bài đăng về xu hướng AI trong thương mại điện tử và lên lịch đăng vào 9h sáng thứ Hai, thứ Tư, thứ Sáu tuần tới trên Fanpage và LinkedIn."\
> **Agent**: Prepares 3 tailored variations matching LinkedIn and Facebook formatting, calls `create_post` with the target timestamps, and returns confirmation receipts.

***

## Troubleshooting

| Issue                | Cause                              | Solution                                                                  |
| -------------------- | ---------------------------------- | ------------------------------------------------------------------------- |
| 401 Unauthorized     | API Key revoked or invalid         | Regenerate your API key in NueLink Settings and update in DOS.AI.         |
| Collection not found | Collection ID deleted or archived  | Refresh collections list in the agent channel settings.                   |
| Media upload failed  | Remote URL not publicly accessible | Ensure media URLs are public HTTPS links with valid Content-Type headers. |


# WordPress Direct CMS

Connect your WordPress sites (both self-hosted WordPress 5.6+ and WordPress.com / Jetpack sites) directly to DOSClaw AI agents. Enable automated blogging, SEO content creation, article revision management, category tagging, and featured media uploads.

## Capabilities & MCP Tools

| MCP Tool            | Operation | Description                                                                                     |
| ------------------- | --------- | ----------------------------------------------------------------------------------------------- |
| `list_posts`        | Read      | Query published, draft, and scheduled WordPress articles by status, category, or author.        |
| `get_post_detail`   | Read      | Fetch raw HTML/Gutenberg post content, excerpt, SEO metadata, and slug.                         |
| `create_post_draft` | Write     | Create a new WordPress draft article with title, content, categories, tags, and excerpt.        |
| `update_post`       | Write     | Update existing post content, title, status (`draft`, `pending`, `publish`), or featured image. |
| `upload_media`      | Write     | Upload image attachments directly to the WordPress Media Library (`wp-content/uploads`).        |
| `list_categories`   | Read      | Retrieve taxonomy categories and tags to organize generated articles.                           |

***

## Prerequisites

### For Self-Hosted WordPress

* WordPress 5.6 or higher.
* HTTPS enabled on your website.
* Pretty Permalinks enabled (Settings $\rightarrow$ Permalinks $\rightarrow$ `Post name`).
* Application Passwords enabled (standard feature in WordPress 5.6+).

### For WordPress.com / Jetpack Sites

* A WordPress.com account with admin access to the target site.

***

## Setup Guide

### Method 1: Self-Hosted WordPress (Secure Application Password Flow)

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **WordPress Direct CMS**.
2. Click **Connect WordPress Site**.
3. Select **Self-Hosted WordPress**.
4. Enter your site URL (e.g. `https://myblog.com`).
5. DOSClaw redirects you to your site's native authorization screen:

   ```
   https://myblog.com/wp-admin/authorize-application.php?app_name=DOSClaw+AI
   ```
6. Log in to your WordPress admin account and click **Yes, I approve this connection**.
7. WordPress mints a dedicated Application Password and redirects back to DOS-Me to encrypt and store it in Vault.
8. You never have to manually copy-paste passwords.

### Method 2: WordPress.com / Jetpack (OAuth)

1. Select **WordPress.com / Jetpack** in the connection dialog.
2. Click **Authorize with WordPress.com**.
3. Log in with your WordPress.com account and select your site from the list.
4. Approve the requested content creation scopes.

***

## Human Approval & Editorial Governance

Content creation workflows adhere to strict editorial policies:

* **Draft Only (Recommended)**: The agent creates posts strictly with `status: "draft"` or `status: "pending"`. Human editors review, polish, and publish from WordPress admin.
* **Direct Publish**: If authorized, the agent can publish immediately or schedule posts for specific publication times.

***

## Example Blogging Prompt

> **User**: "Viết một bài viết chuẩn SEO 1200 từ về 'Top 5 công cụ AI tự động hóa bán hàng năm 2026', chèn các thẻ H2/H3 hợp lý, chọn chuyên mục 'Kiến thức AI' và lưu vào bản nháp trên website WordPress."\
> **Agent**:
>
> 1. Calls `list_categories` to find the ID for 'Kiến thức AI'.
> 2. Generates the structured HTML content with proper semantic tags.
> 3. Calls `create_post_draft({ "title": "Top 5 Công Cụ AI...", "content": "...", "categories": [14], "status": "draft" })`.
> 4. Returns the draft URL (`/wp-admin/post.php?post=452&action=edit`) for one-click editorial review.

***

## Troubleshooting

| Issue                             | Cause                                 | Solution                                                                                     |
| --------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- |
| "REST API disabled"               | Security plugin blocking `/wp-json`   | In Wordfence, iThemes, or Cloudflare, allow REST API requests to `/wp-json/wp/v2/*`.         |
| Application Passwords not showing | SSL disabled or plugin conflict       | Ensure your site uses `https://`. Some basic auth plugins may disable Application Passwords. |
| Media upload HTTP 413 error       | Server `upload_max_filesize` exceeded | Increase `client_max_body_size` in Nginx or `upload_max_filesize` in `php.ini`.              |


# Meta Ads & Lead Ads

Connect Meta Marketing API to DOSClaw AI agents. Capture real-time lead submissions from Facebook & Instagram Lead Ads to trigger instant follow-ups within seconds, monitor campaign ROAS, and report server-side conversion events via Meta Conversions API (CAPI).

## Capabilities & MCP Tools

| MCP Tool                 | Operation | Description                                                                                                |
| ------------------------ | --------- | ---------------------------------------------------------------------------------------------------------- |
| `list_meta_accounts`     | Read      | List connected Meta Ad Accounts, Pages, and Pixel Datasets.                                                |
| `get_meta_lead`          | Read      | Fetch detailed lead data (name, email, phone number, form questions) by Lead ID.                           |
| `list_form_leads`        | Read      | Retrieve recent lead submissions from specific Instant Forms.                                              |
| `list_meta_campaigns`    | Read      | Query active and paused ad campaigns, budgets, and objectives.                                             |
| `get_campaign_insights`  | Read      | Retrieve performance metrics (Spend, Impressions, Clicks, CPC, CTR, Leads, ROAS).                          |
| `update_campaign_status` | Write     | Pause or resume ad campaigns based on target CPA or performance thresholds.                                |
| `send_capi_event`        | Write     | Dispatch server-side conversion events (`Lead`, `Purchase`, `Contact`) with automatic PII SHA-256 hashing. |

***

## Prerequisites

1. A **Meta Business Manager** (Meta Business Suite) account.
2. A **Facebook Page** with active or scheduled Lead Generation Ad campaigns.
3. Administrator or Lead Access Manager permissions on the target Page.

***

## Setup Guide

### Step 1: Connect via Meta OAuth

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **Meta Ads & Lead Ads Automation**.
2. Click **Connect with Meta**.
3. Log in with your Facebook credentials.
4. Select the **Ad Accounts** and **Facebook Pages** you want to automate.
5. Review and approve the permissions:
   * `leads_retrieval`: Ingest real-time leads from instant forms.
   * `pages_read_engagement`: Access form definitions and metadata.
   * `ads_read`: Query campaign insights and ROAS metrics.
   * `ads_management`: (Optional) Allow agent to pause underperforming ad sets.
6. Confirm authorization. Your long-lived access token is encrypted and stored in the DOS-Me Vault.

### Step 2: Configure Inbound Lead Webhook

DOSClaw provides an inbound real-time webhook endpoint:

```
https://api.dos.ai/v1/webhooks/meta-leads
```

When a user submits a Lead Form on Facebook or Instagram, Meta sends a webhook event to DOSClaw within \~2 seconds.

### Step 3: Bind to an Agent

1. Open your Sales, Real Estate, or Customer Service agent $\rightarrow$ **Integrations**.
2. Enable **Meta Ads & Leads**.
3. Under **Lead Form Trigger Action**, specify what the agent should do when a new lead arrives:
   * **Send Zalo ZNS / WhatsApp Message**: Trigger an immediate greeting message to the lead's phone number.
   * **Notify Sales Team**: Dispatch a lead card to your team's Telegram or Slack channel with form responses.
   * **Create CRM Contact**: Sync the lead into HubSpot, KiotViet, or Google Sheets.

***

## Conversions API (CAPI)

When an AI agent successfully closes a sale or qualifies a prospect during chat:

1. The agent calls `send_capi_event({ "event_name": "Purchase", "value": 450000, "currency": "VND" })`.
2. DOSClaw automatically normalizes and applies **SHA-256 hashing** to personal identifiers (email, phone).
3. The event is delivered directly to Meta's server-side Conversions API, improving ad attribution and lowering customer acquisition costs (CAC).

***

## Troubleshooting

| Issue                            | Cause                                     | Solution                                                                                                                                                     |
| -------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| "100 Unsupported get request"    | Meta user lacking Lead Access permissions | In Meta Business Suite $\rightarrow$ Settings $\rightarrow$ Integrations $\rightarrow$ **Leads Access**, grant your user account access to the Page's leads. |
| Inbound leads delayed            | Webhook subscription dropped              | Re-save the integration in DOS.AI to refresh the Page webhook subscription.                                                                                  |
| CAPI event deduplication warning | Event ID missing                          | DOSClaw generates unique event IDs; verify your browser pixel isn't sending mismatched IDs.                                                                  |


# Finance & Invoicing

Connect accounting ledgers, electronic invoicing systems, and merchant payment suites to DOSClaw AI agents. Provide financial assistants and operations bots with secure, read-only ledger access and in-chat payment links.

## Supported Platforms

| Platform                                                       | Type                   | Key Capabilities                                                  |
| -------------------------------------------------------------- | ---------------------- | ----------------------------------------------------------------- |
| [MISA Enterprise Suite](/plugins-and-connectors/finance/misa)  | Accounting & Invoicing | meInvoice, Inbound E-Invoice, ASP, AMIS Accounting, WeSign, eSign |
| [FinOne / Vbill](/plugins-and-connectors/finance/finone-vbill) | Merchant Payments      | Revenue statistics, reconciliation reports, dynamic VietQR links  |
| [Stripe Payments](/plugins-and-connectors/finance/stripe)      | Global Payments        | Instant Checkout links, subscription checks, payment verification |

***

## Strict Read-Only Ledgers

To protect accounting integrity:

* Financial ledgers (MISA, accounting systems) are strictly **read-only**.
* Payments are processed through secure hosted links (Stripe Checkout or dynamic VietQR) so sensitive card numbers or bank credentials never pass through the agent conversation.


# MISA Enterprise Suite

Connect MISA—Vietnam's leading accounting, taxation, and enterprise software ecosystem—to DOSClaw AI agents. Provide financial assistants, operations bots, and account managers with secure, read-only access to electronic invoices, inbound invoices, accounting dictionaries, debt balances, electronic signatures, and document templates.

## Supported MISA Products

DOSClaw provides specialized, read-only connectors for six MISA products:

| Product               | Connector Slug          | Auth Method                 | Capabilities                                                 |
| --------------------- | ----------------------- | --------------------------- | ------------------------------------------------------------ |
| **MISA meInvoice**    | `misa-meinvoice`        | App Credentials / Vault     | Template lookup, existing invoice status verification        |
| **Inbound E-Invoice** | `misa-inbound-einvoice` | Service Credentials / Vault | Inbound invoice search, organization discovery, item details |
| **MISA ASP**          | `misa-asp`              | Access Code / Vault         | Read-only accounting dictionaries (accounts, tax rates)      |
| **AMIS Accounting**   | `misa-amis-accounting`  | Tenant Access Code / Vault  | Company information, inventory balances, debt balances       |
| **MISA WeSign**       | `misa-wesign`           | OAuth2 / Vault              | Signing templates, document signing status                   |
| **MISA eSign**        | `misa-esign`            | Credentials & OTP           | Digital certificate metadata, signing transaction status     |

***

## Security Model: Read-Only by Design

> \[!IMPORTANT] To comply with Vietnamese financial regulations and protect enterprise ledgers, all MISA connectors in DOSClaw are **strictly read-only**. AI agents **cannot** modify financial ledgers, issue invoices, sign contracts, or alter tax filings.

* **Encrypted Token Vault**: Credentials, client tokens, and access codes are stored in the DOS-Me Vault (`public.provider_connections`).
* **Fail-Closed Boundary**: If a tenant access code expires or is revoked in MISA, all agent queries fail closed immediately without exposing underlying system details.

***

## Setup Guide

### 1. MISA meInvoice (`misa-meinvoice`)

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **MISA meInvoice**.
2. Enter your MISA App ID, Tax Code (Mã số thuế), and Service Account credentials.
3. Click **Connect & Verify**.
4. Agent gains tool: `get_invoice_status({ "invoice_number": "...", "symbol": "..." })`.

### 2. Inbound E-Invoice (`misa-inbound-einvoice`)

1. Open **Integrations** $\rightarrow$ **Inbound E-Invoice** (`Hóa đơn điện tử đầu vào`).
2. Provide your MISA MEINVOICEBOT service token.
3. Agent gains tools: `search_inbound_invoices`, `get_inbound_invoice_detail`.

### 3. AMIS Accounting (`misa-amis-accounting`)

1. Open **Integrations** $\rightarrow$ **AMIS Accounting** (`AMIS Kế toán DN`).
2. Enter your company's registered AMIS access code.
3. Agent gains tools:
   * `get_company_info`: Verify company registration and active branches.
   * `get_inventory_balance`: Real-time warehouse balance from accounting books.
   * `get_debt_balance`: Outstanding accounts receivable (AR) and payable (AP) balances.

### 4. MISA WeSign & eSign (`misa-wesign`, `misa-esign`)

1. Connect via MISA WeSign OAuth or provide verified eSign certificate identifiers.
2. Agents gain tools to track whether a contract has been signed by all parties (`get_document_status`).

***

## Example Financial Queries

### Checking Supplier Invoices

> **Accountant**: "Kiểm tra xem hóa đơn đầu vào số 000123 của nhà cung cấp FPT Telecom đã về hệ thống chưa?"\
> **Agent**: Calls `search_inbound_invoices({ "supplier_tax_code": "0101248141", "invoice_number": "000123" })`\
> **Reply**: "Hóa đơn số 000123 ngày 10/08/2026 từ Cty Cổ phần FPT Telecom (Tổng tiền: 2.200.000đ, VAT: 200.000đ) đã được đồng bộ vào hệ thống Inbound Invoice, trạng thái hợp lệ ạ."

### Checking Customer Debt Balance

> **Sales Rep**: "Khách hàng Công ty TNHH Hoàng Gia còn dư nợ bao nhiêu?"\
> **Agent**: Calls `get_debt_balance({ "customer_tax_code": "0312345678" })`\
> **Reply**: "Theo dữ liệu MISA AMIS Kế toán, Công ty TNHH Hoàng Gia hiện có số dư nợ phải thu là 45.000.000đ, trong đó có 1 hóa đơn quá hạn 5 ngày ạ."

***

## Troubleshooting

| Issue                   | Cause                    | Solution                                                                                 |
| ----------------------- | ------------------------ | ---------------------------------------------------------------------------------------- |
| 401 Unauthorized        | MISA Access Code revoked | Regenerate the access code in your MISA product management portal and update in DOS.AI.  |
| Inbound invoice missing | Tax code desync          | Verify that the supplier issued the invoice to the exact Tax Code registered in MISA.    |
| Read operation blocked  | Permission restriction   | Verify that your MISA API account has Read permissions on the requested data dictionary. |


# FinOne / Vbill

Connect FinOne and Vbill merchant accounts to DOSClaw AI agents. Enable automated revenue reporting, payment reconciliation, and instant VietQR dynamic payment link generation during customer chat conversations.

## Capabilities & MCP Tools

| MCP Tool                   | Operation | Description                                                                                             |
| -------------------------- | --------- | ------------------------------------------------------------------------------------------------------- |
| `get_revenue_stats`        | Read      | Fetch daily, weekly, or monthly merchant transaction revenue and order count.                           |
| `reconcile_orders`         | Read      | Reconcile bank transfer records and POS transactions against pending merchant orders.                   |
| `generate_payment_qr`      | Write     | Generate dynamic VietQR code images and payment links with exact order amount and transfer description. |
| `check_transaction_status` | Read      | Verify if a specific payment transfer has been credited to the merchant wallet.                         |

***

## Prerequisites

1. An active **FinOne** or **Vbill** merchant account.
2. Merchant ID and API Secret Key provided by your FinOne account manager.

***

## Setup Guide

### Step 1: Connect in DOS.AI Dashboard

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **FinOne / Vbill Merchant Suite**.
2. Click **Connect Merchant**.
3. Enter your:
   * **Merchant ID**
   * **Partner Secret Key**
   * **Bank Account / QR Preset**
4. Click **Verify & Connect**.

### Step 2: Bind to Your AI Agent

1. Open your Sales or E-Commerce agent $\rightarrow$ **Integrations**.
2. Enable **FinOne / Vbill**.
3. Select whether the agent can automatically generate payment QR codes (`autonomous`) when customers confirm an order.

***

## Example In-Chat Payment Flow

1. Customer agrees to purchase products totaling 450,000 VND.
2. Agent calls:

   ```json
   generate_payment_qr({
     "amount": 450000,
     "order_ref": "ORDER-9821",
     "description": "DH 9821"
   })
   ```
3. Agent sends back a formatted VietQR image card with bank transfer details.
4. Customer scans QR code via banking app (Vietcombank, MB, Techcombank, etc.).
5. FinOne webhook alerts DOSClaw, and the agent confirms payment receipt to the customer within seconds.

***

## Troubleshooting

| Issue                        | Cause                       | Solution                                                                                |
| ---------------------------- | --------------------------- | --------------------------------------------------------------------------------------- |
| "Invalid Merchant Signature" | Partner Secret Key mismatch | Ensure the secret key in DOS.AI matches the key in FinOne merchant portal.              |
| Payment confirmation delayed | Bank NAPAS delay            | Check FinOne transaction dashboard or allow up to 30 seconds for bank webhook callback. |


# Stripe Payments

Connect Stripe to DOSClaw AI agents. Generate dynamic checkout links directly in conversation threads, check customer subscription statuses, verify payment receipts, and automate customer billing inquiries.

## Capabilities & MCP Tools

| MCP Tool                    | Operation | Description                                                                                        |
| --------------------------- | --------- | -------------------------------------------------------------------------------------------------- |
| `create_payment_link`       | Write     | Generate an instant Stripe Checkout URL with customized line items, currency, and tax calculation. |
| `check_subscription_status` | Read      | Look up customer subscription state (`active`, `past_due`, `canceled`) by email or customer ID.    |
| `get_invoice_detail`        | Read      | Retrieve payment invoices, line items, and PDF download links.                                     |
| `verify_payment_intent`     | Read      | Verify successful payment webhook execution before granting digital access.                        |

***

## Prerequisites

1. An active **Stripe account** (`https://stripe.com`).
2. Administrator access to retrieve your Stripe Restricted API Key or connect via Stripe Connect.

***

## Setup Guide

### Step 1: Connect via Stripe API Key

1. In the **Stripe Dashboard**, go to **Developers** $\rightarrow$ **API keys**.
2. Click **Create restricted key**:
   * `Checkout Sessions`: Write
   * `Payment Intents`: Read
   * `Customers`: Read
   * `Subscriptions`: Read
   * `Invoices`: Read
3. Copy the restricted key (`rk_live_...` or `rk_test_...` for testing).
4. In **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **Stripe Payments**.
5. Paste your restricted key and click **Save & Connect**.
6. DOS-Me validates the key by retrieving account information and stores the credential in encrypted Vault storage.

### Step 2: Configure Webhook (Optional but Recommended)

To enable automatic fulfillment when a customer pays:

1. In Stripe Dashboard, add a webhook destination:

   ```
   https://api.dos.ai/v1/webhooks/stripe
   ```
2. Select events: `checkout.session.completed`, `customer.subscription.updated`.
3. Copy the **Signing secret** (`whsec_...`) and paste it into DOS.AI.

***

## Example Sales Concierge Flow

> **Customer**: "I'd like to sign up for your quarterly consulting package for $450."\
> **Agent**: Calls `create_payment_link({ "amount": 45000, "currency": "usd", "description": "Quarterly Consulting Package" })`\
> **Reply**: "Great! You can complete your secure checkout here: [Pay $450 with Stripe Checkout](https://buy.stripe.com/demo123). Once finished, I will automatically send you the calendar invite for our kickoff call!"

***

## Troubleshooting

| Issue                                    | Cause                                         | Solution                                                                            |
| ---------------------------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------- |
| "Key does not have required permissions" | Restricted key missing write scope            | In Stripe Dashboard, ensure `Checkout Sessions` has **Write** permission.           |
| Currency not supported                   | Currency code not enabled on merchant account | Check Stripe Dashboard $\rightarrow$ Settings $\rightarrow$ Currencies.             |
| Test mode transactions in production     | Using `rk_test_...` key                       | Replace test key with live restricted key (`rk_live_...`) when going to production. |


# Productivity & Tools

Connect productivity suites, internal knowledge bases, developer repositories, and real-time search engines to DOSClaw AI agents.

## Supported Tools & Connectors

| Tool / Platform                                                           | Type            | Key Capabilities                                                                    |
| ------------------------------------------------------------------------- | --------------- | ----------------------------------------------------------------------------------- |
| [Google Workspace](/plugins-and-connectors/productivity/google-workspace) | Office Suite    | Google Sheets read/append, Calendar scheduling with Google Meet links, Drive search |
| [GitHub](/plugins-and-connectors/productivity/github)                     | Developer Tools | Repository search, issue triage, PR summaries, commit history                       |
| [Notion](/plugins-and-connectors/productivity/notion)                     | Knowledge Base  | Workspace search, internal SOP wiki, database queries                               |
| [Brave Search Engine](/plugins-and-connectors/productivity/brave-search)  | Web Grounding   | Real-time web and news search, factual verification, anti-hallucination             |

***

## Safe Scopes & Access Control

* **Google Workspace**: Exclusively uses Safe Scopes (`spreadsheets`, `calendar.events`, `drive.file`) to eliminate CASA Tier 2/3 third-party audit requirements while ensuring data privacy.
* **Brave Search**: Independent index with zero query tracking and strict **No Invented Links** anti-hallucination rules.


# Google Workspace

Connect your Google Workspace account to DOSClaw AI agents. Automate spreadsheet record keeping with **Google Sheets**, schedule meetings with automatic **Google Meet** links via **Google Calendar**, and search and manage documents in **Google Drive**.

## Capabilities & MCP Tools

DOSClaw provides 12 atomic MCP tools adhering to the English `snake_case` naming standard:

| MCP Tool                | Operation | Description                                                                                 |
| ----------------------- | --------- | ------------------------------------------------------------------------------------------- |
| `read_google_sheets`    | Read      | Read cell values and rows from a spreadsheet range (e.g. `Sheet1!A1:E50`).                  |
| `append_google_sheets`  | Write     | Append new rows to the end of a sheet (ideal for capturing leads and customer feedback).    |
| `update_google_sheets`  | Write     | Overwrite or update specific cell ranges.                                                   |
| `get_google_sheet_info` | Read      | Retrieve spreadsheet metadata, sheet titles, and column dimensions.                         |
| `list_calendar_events`  | Read      | Check user/company calendar availability for upcoming meetings.                             |
| `create_calendar_event` | Write     | Schedule a calendar event with attendee emails and an automatic **Google Meet video link**. |
| `update_calendar_event` | Write     | Reschedule or modify an existing meeting.                                                   |
| `delete_calendar_event` | Write     | Cancel a scheduled calendar event.                                                          |
| `list_drive_files`      | Read      | Search files and documents in Drive created by or shared with the application.              |
| `get_drive_file`        | Read      | Fetch document metadata and temporary download links.                                       |

***

## Prerequisites

1. A **Google Account** (Google Workspace enterprise email or personal `@gmail.com`).
2. Permissions to grant OAuth access to third-party applications.

***

## Safe Scopes & Security Boundary

To protect enterprise privacy and ensure data isolation, DOSClaw connects strictly using **Safe Scopes**:

* `https://www.googleapis.com/auth/spreadsheets`: Full read and append capabilities on Google Sheets.
* `https://www.googleapis.com/auth/calendar.events`: Read and create calendar events with Meet video links.
* `https://www.googleapis.com/auth/drive.file`: Per-file access to files created or uploaded by DOSClaw, protecting private Drive folders.

Raw secrets and OAuth refresh tokens are stored exclusively in the DOS-Me Vault (`public.provider_connections`). The AI agent container receives temporary, scoped credentials only at tool execution time.

***

## Setup Guide

### Step 1: Connect via Google OAuth

1. In the **DOS.AI Dashboard**, navigate to **Integrations** $\rightarrow$ **Google Workspace**.
2. Click **Connect Google Account**.
3. Choose your Google Workspace or Gmail account.
4. Review the requested permissions (Google Sheets, Google Calendar Events, and Drive Files).
5. Click **Allow**.
6. The connection will appear as **Connected** in your dashboard.

### Step 2: Bind to Your Agent

1. Open your agent $\rightarrow$ **Integrations** $\rightarrow$ enable **Google Workspace**.
2. Under **Tool Permissions**, select the tools you want to activate (e.g. enable Sheets append and Calendar booking, but keep Drive disabled).
3. If using Google Sheets, provide the default `Spreadsheet ID` (the string between `/d/` and `/edit` in your Google Sheets URL).
4. Save your changes.

***

## Example Agent Automations

### 1. Booking a Discovery Call with Google Meet

> **Customer**: "Tôi muốn đặt lịch tư vấn vào 14h00 chiều thứ Tư tuần này qua Google Meet."\
> **Agent**:
>
> 1. Calls `list_calendar_events` to verify the team is free at 14:00.
> 2. Calls `create_calendar_event({ "summary": "Tư vấn giải pháp AI - Khách hàng", "start_time": "2026-08-26T14:00:00+07:00", "end_time": "2026-08-26T14:45:00+07:00", "create_meeting_link": true, "attendees": ["khachhang@gmail.com"] })`.
> 3. Replies: "Dạ em đã đặt lịch tư vấn cho anh vào lúc **14:00 - 14:45 thứ Tư ngày 26/08/2026**. Link Google Meet tham gia: `https://meet.google.com/abc-defg-hij`. Lịch hẹn cũng đã được gửi về email của anh ạ!"

### 2. Capturing Inbound Leads to Google Sheets

> When a customer provides their contact info in chat, the agent calls:
>
> ```json
> append_google_sheets({
>   "spreadsheet_id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
>   "range": "Leads!A:E",
>   "values": [["2026-08-26 10:15", "Trần Văn Minh", "0912345678", "Gói Doanh nghiệp", "Zalo OA"]]
> })
> ```
>
> The row is instantly saved into your master Google Sheet.

***

## Troubleshooting

| Issue                                    | Cause                                       | Solution                                                                                  |
| ---------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------- |
| "Spreadsheet not found or 403 Forbidden" | Sheet not shared with the connected account | Ensure the Google account you authorized in DOS.AI has Editor access to the spreadsheet.  |
| Timezone discrepancy in Calendar         | Missing UTC offset                          | Always include timezone offset in ISO 8601 timestamps (e.g. `+07:00` for Indochina Time). |
| Meet link not generated                  | `create_meeting_link` parameter omitted     | Ensure `create_meeting_link: true` is passed to the tool call.                            |


# GitHub Developer

Connect GitHub repositories to DOSClaw AI agents. Empower engineering assistants and DevOps bots to triage issues, summarize pull requests, inspect commit history, and answer code repository questions.

## Capabilities & MCP Tools

| MCP Tool           | Operation | Description                                                                        |
| ------------------ | --------- | ---------------------------------------------------------------------------------- |
| `list_issues`      | Read      | Search and list repository issues by state (`open`, `closed`), label, or assignee. |
| `get_issue_detail` | Read      | Fetch issue descriptions, comments, author info, and discussion threads.           |
| `create_issue`     | Write     | Open a new GitHub issue with title, Markdown body, labels, and assignees.          |
| `get_pull_request` | Read      | Retrieve PR diffs, changed files, review comments, and mergeability status.        |
| `list_commits`     | Read      | Inspect recent commit history on a specific branch.                                |
| `get_release_info` | Read      | Fetch latest release tags, changelogs, and release assets.                         |

***

## Prerequisites

1. A GitHub account with access to the target repository (public or private).
2. A **GitHub Personal Access Token (Classic or Fine-Grained)** or GitHub App installation.

***

## Setup Guide

### Step 1: Generate a GitHub Access Token

1. Go to your GitHub profile $\rightarrow$ **Settings** $\rightarrow$ **Developer settings** $\rightarrow$ **Personal access tokens**.
2. Generate a fine-grained token with access to your desired repository:
   * **Repository permissions**:
     * `Issues`: Read & Write
     * `Pull requests`: Read-only
     * `Contents`: Read-only
3. Copy the token (`github_pat_...` or `ghp_...`).

### Step 2: Connect in DOS.AI Dashboard

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **GitHub Developer Connector**.
2. Click **Connect GitHub**.
3. Paste your **Personal Access Token** and default repository (e.g. `owner/repo-name`).
4. Click **Verify & Save**. DOSClaw validates token validity against `https://api.github.com/user`.

### Step 3: Bind to Your Agent

1. Open your developer or tech lead agent $\rightarrow$ **Integrations** $\rightarrow$ enable **GitHub**.
2. Select your governance mode (`approval_required` for creating issues or `autonomous` for triaging).
3. Save changes.

***

## Example DevOps Scenarios

### Automated Bug Triage in Slack/Discord

When a user reports a bug in a developer community chat:

> **Developer**: "@DevBot issue #142 đang bị lỗi gì vậy?"\
> **Agent**: Calls `get_issue_detail({ "repo": "DOS/DOS.AI", "issue_number": 142 })`\
> **Reply**: "Issue #142 báo lỗi `TypeError: Cannot read properties of undefined (reading 'session')` tại file `auth-redirect.ts`. Lỗi này đã được đánh nhãn `bug` và assign cho @lead-dev. Hiện PR #145 đang fix và chờ review ạ."

***

## Troubleshooting

| Issue                   | Cause                                         | Solution                                                                                       |
| ----------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| 404 Not Found           | Token lacks access to private repo            | Ensure your fine-grained token includes repository permissions for that specific private repo. |
| 403 Rate limit exceeded | Unauthenticated rate limit or token exhausted | Verify token is active; authenticated requests have a 5,000 req/hour limit on GitHub API.      |
| Cannot create issue     | Token missing `issues:write` scope            | Regenerate token with Read and Write permissions for Issues.                                   |


# Notion Knowledge Base

Connect your Notion workspace to DOSClaw AI agents. Enable agents to search internal SOPs, product documentation, team wikis, and databases as a dynamic Retrieval-Augmented Generation (RAG) knowledge base, and capture meeting notes into Notion databases.

## Capabilities & MCP Tools

| MCP Tool               | Operation | Description                                                                                |
| ---------------------- | --------- | ------------------------------------------------------------------------------------------ |
| `search_notion_pages`  | Read      | Search Notion workspace pages and documents by semantic query or keywords.                 |
| `get_page_content`     | Read      | Fetch page blocks, Markdown content, nested toggles, and callouts.                         |
| `query_database`       | Read      | Query Notion databases with filters, sorting, and property values (select, tags, dates).   |
| `create_database_page` | Write     | Add a new record/row to a Notion database (e.g. log customer feedback or meeting minutes). |

***

## Prerequisites

1. An active **Notion workspace** (Free, Plus, or Enterprise).
2. Permission to add connections or install Notion Integrations in your workspace.

***

## Setup Guide

### Step 1: Connect via Notion OAuth

1. In the **DOS.AI Dashboard**, open **Integrations** $\rightarrow$ **Notion Knowledge Base**.
2. Click **Connect with Notion**.
3. You will be redirected to Notion's authorization screen (`api.notion.com/v1/oauth/authorize`).
4. Select the specific Notion pages or workspace sections you want to share with the AI agent.
5. Click **Allow Access**.
6. The connection is registered with encrypted authorization tokens stored in the DOS-Me Vault.

### Step 2: Bind to Your Agent

1. Open your agent $\rightarrow$ **Integrations** $\rightarrow$ enable **Notion**.
2. Under **Knowledge Base Source**, you can choose to:
   * Use Notion as an **On-Demand MCP Tool** (agent searches Notion when prompted).
   * Ingest Notion pages into the agent's **Vector Knowledge Base** (pre-indexed for instant RAG responses).
3. Save changes.

***

## Example Knowledge Base Queries

### Querying Company SOPs

> **Employee in Slack**: "Chính sách nghỉ phép năm của công ty áp dụng như thế nào đối với nhân viên thử việc?"\
> **Agent**: Calls `search_notion_pages({ "query": "chính sách nghỉ phép nhân viên thử việc" })`\
> **Reply**: "Theo mục 3.2 trong tài liệu **Sổ tay Nhân sự 2026** trên Notion: Nhân viên trong thời gian thử việc chưa được hưởng ngày nghỉ phép năm có lương. Tuy nhiên, sau khi ký hợp đồng chính thức, thời gian thử việc sẽ được tính cộng dồn vào thâm niên để tính số ngày phép trong năm ạ."

***

## Troubleshooting

| Issue                        | Cause                            | Solution                                                                                                                     |
| ---------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Page not found in search     | Page not shared with integration | In Notion, open the page $\rightarrow$ click `...` top right $\rightarrow$ **Connections** $\rightarrow$ add **DOSClaw AI**. |
| Database query returns empty | Database permissions missing     | Ensure the parent page containing the database is shared with the integration.                                               |
| Token expired error          | Workspace re-authenticated       | Click **Reconnect** in DOS.AI dashboard to update the integration token.                                                     |


# Brave Search Engine

Equip your AI agents with real-time web grounding using the **Brave Search API**. Enables bots to retrieve current news, market data, sports scores, weather updates, and fact-check information that occurred beyond the LLM's training cutoff date.

## Capabilities & MCP Tools

| MCP Tool      | Operation | Description                                                                                            |
| ------------- | --------- | ------------------------------------------------------------------------------------------------------ |
| `web_search`  | Read      | Perform privacy-first web searches. Returns ranked page titles, snippets, URLs, and publication dates. |
| `news_search` | Read      | Search recent news articles with country, freshness, and language filters.                             |
| `fact_check`  | Read      | Cross-reference factual claims against authoritative public web sources.                               |

***

## Why Brave Search?

1. **Independent Index**: Powered by Brave's own web index, not resold from Google or Bing, avoiding vendor lock-in.
2. **Privacy First**: Zero tracking of customer search queries or personal identifiers.
3. **High Speed & Low Latency**: Fast API response times (<250ms) prevent lag during streaming conversational chat.

***

## Setup Guide

### Method 1: Managed by DOS Platform (Default)

For agents on Plus and Pro plans, web grounding is pre-configured and managed by the platform. You do not need to provide an external API key.

1. Open your agent $\rightarrow$ **Tools / Skills**.
2. Toggle **Web Search (Brave Search)** to `ON`.
3. Save changes. Your agent can now search the web autonomously when answering questions about current events.

### Method 2: Custom Brave Search API Key (BYOK)

If you have your own Brave Search developer plan with custom rate limits:

1. Register at [brave.com/search/api/](https://brave.com/search/api/) and copy your **Brave Search API Key**.
2. In the **DOS.AI Dashboard**, go to **Integrations** $\rightarrow$ **Brave Search Engine**.
3. Select **Use Custom API Key** and paste your key.
4. Click **Verify & Save**.

***

## Agent Usage & Grounding Rules

### When Does the Agent Search?

Agents evaluate user prompts using reasoning models:

* **Searches Triggered**: "Thời tiết Hà Nội hôm nay thế nào?", "Tỷ giá USD/VND hiện tại", "Tin tức mới nhất về iPhone 18", "Giá cổ phiếu VinFast hôm nay".
* **Searches Avoided**: General coding, math calculations, roleplay, or questions answered fully by the agent's uploaded knowledge base.

### Anti-Hallucination & Link Verification

DOSClaw enforces strict **No Invented Links** grounding policies:

* The agent is forbidden from fabricating URLs.
* All outbound links included in answers must be derived directly from the `url` returned in `web_search` results.

***

## Troubleshooting

| Issue                                | Cause                            | Solution                                                                                                 |
| ------------------------------------ | -------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Search returns 429 Too Many Requests | Rate limit on custom key reached | Upgrade your Brave Search plan tier or switch to the DOS Platform managed search pool.                   |
| Outdated search results              | Query lacks freshness parameter  | Specify time constraints in prompt (e.g. "tin tức trong 24h qua") so the agent passes `freshness: "pd"`. |


# Chat Completions

Create a chat completion by sending a conversation (a list of messages) to a model. The API is fully compatible with the OpenAI Chat Completions format, so you can use existing OpenAI SDKs and tools by changing the base URL.

## Endpoint

```
POST https://api.dos.ai/v1/chat/completions
```

## Authentication

Include your API key in the `Authorization` header using the Bearer scheme:

```
Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

API keys can be created and managed from the [dashboard](https://app.dos.ai).

## Request Headers

| Header          | Required | Description           |
| --------------- | -------- | --------------------- |
| `Authorization` | Yes      | `Bearer YOUR_API_KEY` |
| `Content-Type`  | Yes      | `application/json`    |

## Request Body

| Parameter           | Type             | Required | Default       | Description                                                                                                        |
| ------------------- | ---------------- | -------- | ------------- | ------------------------------------------------------------------------------------------------------------------ |
| `model`             | string           | Yes      | -             | The model ID to use. See [Available Models](/models/available-models).                                             |
| `messages`          | array            | Yes      | -             | A list of messages comprising the conversation. See [Message Format](#message-format).                             |
| `temperature`       | number           | No       | 1.0           | Sampling temperature between 0 and 2. Lower values produce more focused output; higher values increase randomness. |
| `max_tokens`        | integer          | No       | Model default | Maximum number of tokens to generate in the response.                                                              |
| `top_p`             | number           | No       | 1.0           | Nucleus sampling parameter. Only tokens within the top `top_p` probability mass are considered.                    |
| `stream`            | boolean          | No       | false         | If `true`, the response is streamed back as Server-Sent Events (SSE).                                              |
| `stop`              | string or array  | No       | null          | Up to 4 sequences where the model will stop generating further tokens.                                             |
| `tools`             | array            | No       | null          | A list of tool (function) definitions the model may call. See [Tool Calling](#tool-calling).                       |
| `tool_choice`       | string or object | No       | "auto"        | Controls which tool the model calls: `"auto"`, `"none"`, or a specific function.                                   |
| `response_format`   | object           | No       | null          | Force a specific output format. Use `{"type": "json_object"}` for JSON mode.                                       |
| `frequency_penalty` | number           | No       | 0             | Penalizes new tokens based on their frequency in the text so far (-2.0 to 2.0).                                    |
| `presence_penalty`  | number           | No       | 0             | Penalizes new tokens based on whether they appear in the text so far (-2.0 to 2.0).                                |
| `n`                 | integer          | No       | 1             | Number of completions to generate for each prompt.                                                                 |

### Message Format

Each message in the `messages` array is an object with the following fields:

| Field          | Type   | Required | Description                                                             |
| -------------- | ------ | -------- | ----------------------------------------------------------------------- |
| `role`         | string | Yes      | One of `system`, `user`, `assistant`, or `tool`.                        |
| `content`      | string | Yes      | The text content of the message.                                        |
| `name`         | string | No       | An optional name for the participant.                                   |
| `tool_calls`   | array  | No       | Tool calls generated by the model (for `assistant` messages).           |
| `tool_call_id` | string | No       | The ID of the tool call this message responds to (for `tool` messages). |

## Response Body

```json
{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "created": 1711000000,
  "model": "dos-ai",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I help you today?"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 9,
    "total_tokens": 21
  }
}
```

### Response Fields

| Field                     | Type    | Description                                                                         |
| ------------------------- | ------- | ----------------------------------------------------------------------------------- |
| `id`                      | string  | Unique identifier for the completion.                                               |
| `object`                  | string  | Always `"chat.completion"`.                                                         |
| `created`                 | integer | Unix timestamp of when the completion was created.                                  |
| `model`                   | string  | The model used for the completion.                                                  |
| `choices`                 | array   | A list of completion choices.                                                       |
| `choices[].index`         | integer | The index of this choice in the list.                                               |
| `choices[].message`       | object  | The generated message.                                                              |
| `choices[].finish_reason` | string  | Why the model stopped: `"stop"`, `"length"`, `"tool_calls"`, or `"content_filter"`. |
| `usage`                   | object  | Token usage statistics for the request.                                             |
| `usage.prompt_tokens`     | integer | Number of tokens in the input prompt.                                               |
| `usage.completion_tokens` | integer | Number of tokens in the generated response.                                         |
| `usage.total_tokens`      | integer | Total tokens (prompt + completion).                                                 |

## Streaming

When `stream: true` is set, the response is delivered as **Server-Sent Events (SSE)**. Each event contains a JSON chunk:

```
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":9,"total_tokens":21}}

data: [DONE]
```

* Each `data:` line contains a JSON object with a `delta` field instead of `message`.
* The `delta` contains incremental content as it is generated.
* The final chunk includes `finish_reason` and `usage` statistics.
* The stream ends with `data: [DONE]`.

## Tool Calling

You can provide tool (function) definitions that the model can choose to call. This enables agentic workflows where the model can request external actions.

### Defining Tools

```json
{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather for a location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City name, e.g. San Francisco, CA"
            }
          },
          "required": ["location"]
        }
      }
    }
  ]
}
```

### Tool Call Response

When the model decides to call a tool, the response will have `finish_reason: "tool_calls"` and include tool call details in the message:

```json
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_abc123",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"location\": \"San Francisco, CA\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ]
}
```

## Error Responses

| Status Code | Error                 | Description                                                   |
| ----------- | --------------------- | ------------------------------------------------------------- |
| 400         | Bad Request           | Invalid request parameters.                                   |
| 401         | Unauthorized          | Invalid or missing API key.                                   |
| 402         | Payment Required      | Insufficient credits.                                         |
| 429         | Too Many Requests     | Rate limit exceeded. See [Rate Limits](/support/rate-limits). |
| 500         | Internal Server Error | Unexpected server error.                                      |
| 503         | Service Unavailable   | Model temporarily unavailable.                                |

See [Error Codes](/support/error-codes) for detailed troubleshooting.

## Examples

### Basic Chat Completion (cURL)

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "What is the capital of France?"}
    ],
    "temperature": 0.7,
    "max_tokens": 256
  }'
```

### Streaming (cURL)

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -N \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "user", "content": "Write a haiku about programming."}
    ],
    "stream": true
  }'
```

### Using the OpenAI Python SDK

Since DOS AI is OpenAI-compatible, you can use the official OpenAI SDK by changing the base URL:

```python
from openai import OpenAI

client = OpenAI(
    api_key="dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.dos.ai/v1"
)

response = client.chat.completions.create(
    model="dos-ai",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Explain quantum computing in simple terms."}
    ],
    temperature=0.7,
    max_tokens=512
)

print(response.choices[0].message.content)
```

### Using the OpenAI Node.js SDK

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  baseURL: "https://api.dos.ai/v1",
});

const response = await client.chat.completions.create({
  model: "dos-ai",
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Explain quantum computing in simple terms." },
  ],
  temperature: 0.7,
  max_tokens: 512,
});

console.log(response.choices[0].message.content);
```

### JSON Mode

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "system", "content": "Respond in JSON format."},
      {"role": "user", "content": "List 3 programming languages with their year of creation."}
    ],
    "response_format": {"type": "json_object"}
  }'
```

### Tool Calling Example

```bash
curl https://api.dos.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "model": "dos-ai",
    "messages": [
      {"role": "user", "content": "What is the weather in Tokyo?"}
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_weather",
          "description": "Get the current weather for a location",
          "parameters": {
            "type": "object",
            "properties": {
              "location": {
                "type": "string",
                "description": "City name"
              }
            },
            "required": ["location"]
          }
        }
      }
    ]
  }'
```


# Embeddings

Generate vector embeddings for text. The API is compatible with the OpenAI Embeddings format, so you can use existing OpenAI SDKs by changing the base URL.

Embeddings are dense vector representations of text, useful for semantic search, clustering, retrieval-augmented generation (RAG), classification, and similarity comparison.

## Endpoint

```
POST https://api.dos.ai/v1/embeddings
```

## Authentication

Include your API key in the `Authorization` header using the Bearer scheme:

```
Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

API keys can be created and managed from the [dashboard](https://app.dos.ai).

## Request Headers

| Header          | Required | Description           |
| --------------- | -------- | --------------------- |
| `Authorization` | Yes      | `Bearer YOUR_API_KEY` |
| `Content-Type`  | Yes      | `application/json`    |

## Request Body

| Parameter         | Type            | Required | Default | Description                                                                                    |
| ----------------- | --------------- | -------- | ------- | ---------------------------------------------------------------------------------------------- |
| `model`           | string          | Yes      | -       | The embedding model ID. Use `qwen3-embedding-4b`.                                              |
| `input`           | string or array | Yes      | -       | The text to embed. Pass a single string, or an array of strings to embed many at once (batch). |
| `encoding_format` | string          | No       | `float` | Format of the returned vectors: `float` (array of numbers) or `base64`.                        |

## Response Body

```json
{
  "object": "list",
  "data": [
    {
      "object": "embedding",
      "index": 0,
      "embedding": [0.0123, -0.0456, 0.0789]
    }
  ],
  "model": "qwen3-embedding-4b",
  "usage": {
    "prompt_tokens": 8,
    "total_tokens": 8
  }
}
```

### Response Fields

| Field                 | Type    | Description                                                      |
| --------------------- | ------- | ---------------------------------------------------------------- |
| `object`              | string  | Always `"list"`.                                                 |
| `data`                | array   | One embedding object per input. Order matches the `input` array. |
| `data[].index`        | integer | Position of this embedding in the input list.                    |
| `data[].embedding`    | array   | The embedding vector (2560 dimensions for `qwen3-embedding-4b`). |
| `model`               | string  | The embedding model used.                                        |
| `usage.prompt_tokens` | integer | Number of input tokens embedded.                                 |
| `usage.total_tokens`  | integer | Total tokens billed.                                             |

## Model

| Model                  | Dimensions | Model ID             |
| ---------------------- | ---------- | -------------------- |
| **Qwen3-Embedding-4B** | 2560       | `qwen3-embedding-4b` |

Served in FP16 on dedicated GPUs in Asia-Southeast, with an automatic cloud fallback for high availability. Embeddings are not affected by smart routing (`dos-auto`) or [BYOK](/guides/byok) - they are always served on DOS AI infrastructure.

## Examples

### cURL

```bash
curl https://api.dos.ai/v1/embeddings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "model": "qwen3-embedding-4b",
    "input": "The quick brown fox jumps over the lazy dog."
  }'
```

### Batch (multiple inputs)

```bash
curl https://api.dos.ai/v1/embeddings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "model": "qwen3-embedding-4b",
    "input": ["first document", "second document", "third document"]
  }'
```

### Using the OpenAI Python SDK

```python
from openai import OpenAI

client = OpenAI(
    api_key="dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.dos.ai/v1"
)

resp = client.embeddings.create(
    model="qwen3-embedding-4b",
    input="The quick brown fox jumps over the lazy dog."
)

vector = resp.data[0].embedding   # 2560-dim list of floats
print(len(vector))
```

### Using the OpenAI Node.js SDK

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  baseURL: "https://api.dos.ai/v1",
});

const resp = await client.embeddings.create({
  model: "qwen3-embedding-4b",
  input: "The quick brown fox jumps over the lazy dog.",
});

console.log(resp.data[0].embedding.length); // 2560
```

## Error Responses

| Status Code | Error               | Description                                                   |
| ----------- | ------------------- | ------------------------------------------------------------- |
| 400         | Bad Request         | Invalid request parameters (e.g. missing `input`).            |
| 401         | Unauthorized        | Invalid or missing API key.                                   |
| 402         | Payment Required    | Insufficient credits.                                         |
| 429         | Too Many Requests   | Rate limit exceeded. See [Rate Limits](/support/rate-limits). |
| 503         | Service Unavailable | Embedding backend temporarily unavailable.                    |

See [Error Codes](/support/error-codes) for detailed troubleshooting.


# Media Generation

Generate video and audio from text prompts. These are DOS AI extension endpoints (not part of the OpenAI API).

* **Video** generation is **asynchronous**: create a job, then poll for the result.
* **Audio (music)** generation is **synchronous**: the response includes the audio URL directly.

All media-generation endpoints use the same API key authentication as the rest of the API:

```
Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

***

## Video Generation

### Create a video

```
POST https://api.dos.ai/v1/videos/generations
```

#### Request Body

| Parameter    | Type    | Required | Default          | Description                                                                        |
| ------------ | ------- | -------- | ---------------- | ---------------------------------------------------------------------------------- |
| `prompt`     | string  | Yes      | -                | Text description of the video to generate.                                         |
| `model`      | string  | No       | `wan-t2v`        | Video model ID. See [Video Models](#video-models).                                 |
| `duration`   | integer | No       | 5                | Length of the video in seconds.                                                    |
| `resolution` | string  | No       | provider default | Output resolution, e.g. `720p`, `1080p`.                                           |
| `image_url`  | string  | No       | -                | A source image URL. Providing it switches the model to its image-to-video variant. |

#### Response (202 Accepted)

Video generation runs in the background. The call returns immediately with a `task_id` you use to poll for the result.

```json
{
  "id": "req-1a2b3c",
  "task_id": "task-abc123",
  "status": "processing",
  "model": "wan-t2v",
  "provider": "alibaba"
}
```

#### Video Models

| Model ID                 | Description            |
| ------------------------ | ---------------------- |
| `wan-t2v` (default)      | Wan 2.7 text-to-video  |
| `wan2.7-t2v`             | Wan 2.7 text-to-video  |
| `wan-i2v` / `wan2.7-i2v` | Wan 2.7 image-to-video |
| `wan2.6-t2v`             | Wan 2.6 text-to-video  |
| `wan2.6-i2v`             | Wan 2.6 image-to-video |

> When `image_url` is supplied with a text-to-video model, DOS AI automatically routes to that model's image-to-video variant.

### Get video status

```
GET https://api.dos.ai/v1/videos/generations/{task_id}
```

Poll this endpoint (using the `task_id` from the create call) until `status` is `succeeded` or `failed`.

```json
{
  "task_id": "task-abc123",
  "status": "succeeded",
  "provider": "alibaba",
  "data": [
    { "url": "https://.../video.mp4" }
  ]
}
```

| `status`     | Meaning                                           |
| ------------ | ------------------------------------------------- |
| `processing` | The job is still running. Keep polling.           |
| `succeeded`  | Done. `data[].url` holds the generated video URL. |
| `failed`     | Generation failed. `message` contains the reason. |

***

## Audio (Music) Generation

```
POST https://api.dos.ai/v1/audio/generations
```

Generates music from a text prompt and/or lyrics. This call is **synchronous** and typically takes 30-60 seconds.

#### Request Body

| Parameter      | Type    | Required | Default             | Description                                 |
| -------------- | ------- | -------- | ------------------- | ------------------------------------------- |
| `prompt`       | string  | Yes\*    | -                   | Description / style of the music.           |
| `lyrics`       | string  | Yes\*    | -                   | Lyrics for the song.                        |
| `model`        | string  | No       | `minimax-music-2.5` | Audio model ID.                             |
| `duration`     | integer | No       | 180                 | Target length in seconds.                   |
| `instrumental` | boolean | No       | false               | Generate an instrumental track (no vocals). |

\* Provide at least one of `prompt` or `lyrics`.

#### Response (200 OK)

```json
{
  "id": "trace-xyz789",
  "created": 1718000000,
  "model": "minimax-music-2.5",
  "provider": "minimax",
  "data": [
    { "url": "https://.../music.mp3", "duration": 30.0 }
  ]
}
```

Audio is returned as a direct URL (MP3, 44.1 kHz, 256 kbps). `data[0].duration` is the actual length in seconds.

***

## Billing

Video and audio are billed **per second of generated media** (not per token). The rate depends on the model; see the [dashboard](https://app.dos.ai/models) for current pricing.

## Error Responses

| Status Code | Error                 | Description                                 |
| ----------- | --------------------- | ------------------------------------------- |
| 400         | Bad Request           | Invalid parameters (e.g. missing `prompt`). |
| 401         | Unauthorized          | Invalid or missing API key.                 |
| 402         | Payment Required      | Insufficient credits.                       |
| 429         | Too Many Requests     | Rate limit exceeded.                        |
| 500         | Internal Server Error | Generation backend error.                   |

## Examples

### Generate a video, then poll for it (cURL)

```bash
# 1. Create the job
curl https://api.dos.ai/v1/videos/generations \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "prompt": "A red panda surfing a wave at sunset, cinematic",
    "model": "wan-t2v",
    "duration": 5,
    "resolution": "720p"
  }'
# -> {"id":"req-...","task_id":"task-abc123","status":"processing",...}

# 2. Poll until succeeded
curl https://api.dos.ai/v1/videos/generations/task-abc123 \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# -> {"task_id":"task-abc123","status":"succeeded","data":[{"url":"https://.../video.mp4"}]}
```

### Generate music (cURL)

```bash
curl https://api.dos.ai/v1/audio/generations \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "prompt": "upbeat lo-fi hip hop, mellow piano",
    "instrumental": true,
    "duration": 30
  }'
```


# Models

Retrieve the list of models currently available on the DOS AI platform. This endpoint is useful for dynamically discovering which models you can use without hard-coding model IDs.

## Endpoint

```
GET https://api.dos.ai/v1/models
```

## Authentication

Include your API key in the `Authorization` header:

```
Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

## Request

No request body or query parameters are required.

## Response

### Success (200 OK)

```json
{
  "object": "list",
  "data": [
    {
      "id": "dos-ai",
      "object": "model",
      "created": 1711000000,
      "owned_by": "dos-ai"
    },
    {
      "id": "llama-3.3-70b",
      "object": "model",
      "created": 1711000000,
      "owned_by": "dos-ai"
    },
    {
      "id": "deepseek-v3",
      "object": "model",
      "created": 1711000000,
      "owned_by": "dos-ai"
    },
    {
      "id": "llama-3.1-8b",
      "object": "model",
      "created": 1711000000,
      "owned_by": "dos-ai"
    }
  ]
}
```

### Response Fields

| Field             | Type    | Description                                                              |
| ----------------- | ------- | ------------------------------------------------------------------------ |
| `object`          | string  | Always `"list"`.                                                         |
| `data`            | array   | Array of model objects.                                                  |
| `data[].id`       | string  | The model identifier. Use this as the `model` parameter in API requests. |
| `data[].object`   | string  | Always `"model"`.                                                        |
| `data[].created`  | integer | Unix timestamp of when the model was added.                              |
| `data[].owned_by` | string  | The organization that owns the model.                                    |

## Model ID Mapping

The `id` field is the value you use when specifying a model in API requests:

| Model Name      | Model ID        |
| --------------- | --------------- |
| Qwen3.5-35B-A3B | `dos-ai`        |
| Llama 3.3 70B   | `llama-3.3-70b` |
| DeepSeek V3     | `deepseek-v3`   |
| Llama 3.1 8B    | `llama-3.1-8b`  |

## Error Responses

| Status Code | Description                 |
| ----------- | --------------------------- |
| 401         | Invalid or missing API key. |
| 500         | Internal server error.      |

See [Error Codes](/support/error-codes) for details.

## Examples

### cURL

```bash
curl https://api.dos.ai/v1/models \
  -H "Authorization: Bearer dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

### Python

```python
from openai import OpenAI

client = OpenAI(
    api_key="dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.dos.ai/v1"
)

models = client.models.list()
for model in models.data:
    print(model.id)
```

### Node.js

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "dos_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  baseURL: "https://api.dos.ai/v1",
});

const models = await client.models.list();
for (const model of models.data) {
  console.log(model.id);
}
```

> **Note:** The list of available models may change over time as new models are added. We recommend fetching the model list dynamically rather than hard-coding model IDs in your application.


# Overview

DOSafe is the safety and threat intelligence engine for the DOS ecosystem. It aggregates data from **19+ sources** with over **3.93 million threat intelligence entries** to provide real-time risk assessments for entities such as domains, URLs, wallets, phone numbers, and more.

## Key Capabilities

* **Entity check** -- Assess the risk of a single entity against the threat intelligence database, on-chain attestations, and DOS.Me identity data.
* **Bulk check** -- Evaluate up to 50 entities in a single request.
* **URL check** -- Analyze a URL for phishing, scam, and malware indicators.
* **AI text detection** -- Determine whether a piece of text was generated by AI.
* **AI image detection** -- Determine whether an image was generated or manipulated by AI.
* **AI video detection** -- Analyze video for AI-generated content using a 7-layer pipeline (frame analysis, temporal consistency, audio-visual sync, LLM visual reasoning).
* **AI audio detection** -- Detect AI-generated speech and voice clones using BEATs + mHuBERT ensemble (AUROC 0.88).
* **Face verification** -- Liveness detection and face matching for identity verification.

## Supported Entity Types

| Type           | Example                  |
| -------------- | ------------------------ |
| `domain`       | `evil.com`               |
| `url`          | `https://evil.com/phish` |
| `wallet`       | `0xdeadbeef...`          |
| `phone`        | `+84901234567`           |
| `bank_account` | `VCB:1234567890`         |
| `email`        | `scammer@evil.com`       |
| `facebook`     | `fakeshop.vn`            |

## API Endpoints

All DOSafe endpoints use the base URL `https://api.dos.ai/v1/dosafe`.

| Method | Endpoint                  | Description                        |
| ------ | ------------------------- | ---------------------------------- |
| POST   | `/v1/dosafe/check`        | Single entity safety check         |
| POST   | `/v1/dosafe/check/bulk`   | Bulk entity check (max 50)         |
| POST   | `/v1/dosafe/url-check`    | URL/domain safety check            |
| POST   | `/v1/dosafe/detect`       | AI text detection                  |
| POST   | `/v1/dosafe/detect-image` | AI image detection                 |
| POST   | `/v1/dosafe/detect-video` | AI video detection                 |
| POST   | `/v1/dosafe/detect-audio` | AI audio/voice detection           |
| POST   | `/v1/dosafe/face/enroll`  | Face enrollment for verification   |
| POST   | `/v1/dosafe/face/verify`  | Face liveness + match verification |
| POST   | `/v1/dosafe/voice/enroll` | Voice enrollment for speaker ID    |
| POST   | `/v1/dosafe/voice/verify` | Voice speaker verification         |

## Authentication

DOSafe endpoints accept authentication via the `X-Api-Key` header:

```
X-Api-Key: your_api_key_here
```

Alternatively, if you already have a DOS AI API key (`dos_sk_*`), you can use the standard `Authorization` header:

```
Authorization: Bearer dos_sk_...
```

Anonymous access is available with a limited daily quota for evaluation purposes.

## Risk Scoring

Risk scores range from 0 to 100 and map to the following levels:

| Score   | Level      |
| ------- | ---------- |
| 0--19   | `safe`     |
| 20--49  | `low`      |
| 50--74  | `medium`   |
| 75--89  | `high`     |
| 90--100 | `critical` |

Scores are computed by weighted aggregation of signals from multiple data sources. No single source determines the final verdict.

### Data Sources

| Source           | Description                                                                |
| ---------------- | -------------------------------------------------------------------------- |
| DOSafe Threat DB | 3.93M+ entries from 19 scrapers covering phishing, scam, malware, and spam |
| DOS Chain        | Immutable on-chain risk attestations via EAS                               |
| DOS.Me Identity  | Member trust scores, verified providers, and flagged status                |
| Web Analysis     | Real-time web search and LLM-powered risk analysis                         |

## Quick Example

### Check an entity

```bash
curl -X POST https://api.dos.ai/v1/dosafe/check \
  -H "X-Api-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "entityType": "domain",
    "entityId": "suspicious-site.com"
  }'
```

**Response:**

```json
{
  "entityType": "domain",
  "entityId": "suspicious-site.com",
  "riskScore": 85,
  "riskLevel": "high",
  "flagged": true,
  "signals": ["db_flagged_phishing"],
  "categories": ["phishing"],
  "sources": ["phishing_database"],
  "checkedAt": "2026-03-24T10:00:00Z"
}
```

### Check a URL

```bash
curl -X POST https://api.dos.ai/v1/dosafe/url-check \
  -H "X-Api-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://suspicious-site.com/login"
  }'
```

### Bulk check

```bash
curl -X POST https://api.dos.ai/v1/dosafe/check/bulk \
  -H "X-Api-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "entities": [
      { "entityType": "wallet", "entityId": "0xdeadbeef..." },
      { "entityType": "phone", "entityId": "+84901234567" }
    ]
  }'
```

## Next Steps

* [DOSafe Partner API Reference](/dosafe/partner-api) -- Full endpoint documentation with request/response schemas and signal reference.
* [Error Codes](/support/error-codes) -- Common error responses and how to handle them.


# Partner API

> **Base URL:** `https://app.dosafe.io/api`
>
> **Auth:** All endpoints require `X-Api-Key` header. One key covers all DOSafe services.

***

## Overview

The DOSafe API is the unified safety gateway for the DOS ecosystem. A single API key grants access to all DOSafe services — entity/URL safety checks, AI text/image/video/audio detection, face and voice verification, and community reporting — with scopes controlling which capabilities are available.

### Data Sources (Safety Check)

| Source          | Weight   | Description                                                        |
| --------------- | -------- | ------------------------------------------------------------------ |
| DOSafe DB       | Highest  | 3.93M+ entries from 19 scrapers (phishing, scam, malware, wallets) |
| DOS Chain       | High     | Immutable on-chain attestations via EAS                            |
| DOS.Me Identity | Moderate | Member trust score, verified providers, flagged status             |
| Web Analysis    | Moderate | Real-time web search + LLM-powered risk analysis                   |

**Architecture:** DOSafe is the safety engine and public gateway. DOS.Me is an identity data provider — external services call DOSafe, not DOS.Me.

### Risk Score → Level

| Score  | Level      |
| ------ | ---------- |
| 0–19   | `safe`     |
| 20–49  | `low`      |
| 50–74  | `medium`   |
| 75–89  | `high`     |
| 90–100 | `critical` |

Scores are computed by weighted aggregation of signals — no single source determines the verdict alone.

***

## Authentication

```
X-Api-Key: dsk_xxxx...
```

Keys are stored as SHA-256 hashes in `dosafe.api_keys`. Plaintext is never persisted after provisioning.

### Scopes

| Scope          | Endpoints                                                                        |
| -------------- | -------------------------------------------------------------------------------- |
| `check`        | `POST /check`                                                                    |
| `bulk`         | `POST /check/bulk`                                                               |
| `report`       | `POST /report`                                                                   |
| `detect`       | `POST /detect`, `POST /detect-image`, `POST /detect-video`, `POST /detect-audio` |
| `url-check`    | `POST /url-check`                                                                |
| `entity-check` | `POST /entity-check`                                                             |
| `face`         | `POST /face/enroll`, `POST /face/verify`                                         |
| `voice`        | `POST /voice/enroll`, `POST /voice/verify`                                       |

A key can have multiple scopes. Contact the DOSafe team to provision a key with required scopes.

***

## Entity Types

| Type                                                             | Example                  |
| ---------------------------------------------------------------- | ------------------------ |
| `wallet`                                                         | `0xdeadbeef...`          |
| `domain`                                                         | `evil.com`               |
| `url`                                                            | `https://evil.com/phish` |
| `email`                                                          | `scammer@evil.com`       |
| `phone`                                                          | `+84901234567`           |
| `bank_account`                                                   | `VCB:1234567890`         |
| `telegram` / `telegram_user` / `telegram_group` / `telegram_bot` | `scammerbot`             |
| `facebook` / `facebook_profile` / `facebook_page`                | `fakeshop.vn`            |
| `national_id`                                                    | `079123456789`           |
| `organization`                                                   | Company name             |

***

## Safety Check API

Structured developer API. Cleaner response format optimized for machine consumption.

### `POST /check`

**Scope:** `check`

Single entity safety check. Runs DB lookup + on-chain query + DOS.Me identity in parallel.

**Request:**

```json
{
  "entityType": "wallet",
  "entityId": "0xdeadbeef..."
}
```

**Response:**

```json
{
  "entityType": "wallet",
  "entityId": "0xdeadbeef...",
  "riskScore": 85,
  "riskLevel": "critical",
  "flagged": true,
  "signals": ["db_flagged_phishing", "onchain_high_risk"],
  "categories": ["phishing"],
  "sources": ["phishing_database", "scamsniffer"],
  "clusterLinked": true,
  "member": {
    "found": false
  },
  "onChain": {
    "attestationCount": 2,
    "latestRiskScore": 88
  },
  "checkedAt": "2026-03-09T10:00:00.000Z"
}
```

**Example — Bexly: check wallet before transaction:**

```typescript
const res = await fetch('https://app.dosafe.io/api/check', {
  method: 'POST',
  headers: {
    'X-Api-Key': process.env.DOSAFE_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ entityType: 'wallet', entityId: recipientAddress }),
})

const result = await res.json()

if (result.flagged && result.riskLevel === 'critical') {
  throw new Error(`Recipient flagged as ${result.categories.join(', ')}`)
}
```

***

### `POST /check/bulk`

**Scope:** `bulk`

Batch entity check. Max 50 entities per request. Results are returned in the same order as input. Individual errors do not fail the whole batch.

**Request:**

```json
{
  "entities": [
    { "entityType": "wallet", "entityId": "0xdeadbeef..." },
    { "entityType": "domain", "entityId": "evil.com" },
    { "entityType": "phone", "entityId": "+84901234567" }
  ]
}
```

**Response:**

```json
{
  "results": [
    {
      "entityType": "wallet",
      "entityId": "0xdeadbeef...",
      "riskScore": 85,
      "riskLevel": "critical",
      "flagged": true,
      "categories": ["phishing"],
      "signals": ["db_flagged_phishing"]
    },
    {
      "entityType": "domain",
      "entityId": "evil.com",
      "riskScore": 20,
      "riskLevel": "low",
      "flagged": false,
      "categories": [],
      "signals": []
    }
  ],
  "checkedAt": "2026-03-09T10:00:00.000Z"
}
```

***

### `POST /report`

**Scope:** `report`

Submit a safety report for an entity. Reports are staged in `raw_imports` for review. High-confidence reports (`riskScore >= 70`) are promoted to threat intel within 24h.

**Request:**

```json
{
  "entityType": "wallet",
  "entityId": "0xdeadbeef...",
  "category": "scam",
  "riskScore": 75,
  "description": "User reported as pig butchering scam"
}
```

| Field         | Required | Description                                                              |
| ------------- | -------- | ------------------------------------------------------------------------ |
| `entityType`  | ✓        | See entity types table                                                   |
| `entityId`    | ✓        | Raw value                                                                |
| `category`    | ✓        | `phishing`, `scam`, `malware`, `fraud`, `spam`, `impersonation`, `other` |
| `riskScore`   | ✓        | Integer 0–100                                                            |
| `description` | ✗        | Human-readable context                                                   |

**Response (201):**

```json
{
  "reportId": "uuid-here",
  "status": "pending",
  "message": "Report received. High-confidence reports (risk_score >= 70) are reviewed and promoted within 24h."
}
```

***

## AI Detection API

### `POST /detect`

**Scope:** `detect`

AI text detection. Returns probability that the input was AI-generated.

**Request:**

```json
{
  "text": "The quick brown fox..."
}
```

**Response:**

```json
{
  "aiProbability": 87,
  "verdict": "AI",
  "confidence": "high",
  "signals": {
    "perplexity": 42.1,
    "burstiness": 0.12,
    "binoculars": 0.93,
    "rubricScore": 81
  }
}
```

***

### `POST /detect-image`

**Scope:** `detect`

AI image detection. Combines C2PA, EXIF/DCT metadata, reverse image search, and LLM visual analysis.

**Request:** `multipart/form-data` with `image` field (JPEG/PNG/WEBP/GIF, ≤10MB), or JSON `{ "url": "..." }`.

**Response:**

```json
{
  "aiProbability": 92,
  "verdict": "AI",
  "confidence": "high",
  "signals": {
    "c2pa": "ai_generated",
    "exif": "no_camera_metadata",
    "reverseSearch": "not_found"
  }
}
```

***

### `POST /detect-video`

**Scope:** `detect`

AI video detection. Uses a 7-layer pipeline: frame-level AI detection, temporal consistency analysis, audio-visual synchronization, and LLM visual reasoning.

**Request:** `multipart/form-data` with `video` field (MP4/MOV/WEBM, max 100MB), or JSON `{ "url": "..." }`.

**Response:**

```json
{
  "aiProbability": 78,
  "verdict": "AI",
  "confidence": "medium",
  "signals": {
    "frameAnalysis": 0.82,
    "temporalConsistency": 0.71,
    "audioSync": 0.65,
    "llmVisual": 0.85
  },
  "framesAnalyzed": 24,
  "duration": 15.2
}
```

***

### `POST /detect-audio`

**Scope:** `detect`

AI audio/voice detection. BEATs + mHuBERT ensemble for detecting AI-generated speech and voice clones.

**Request:** `multipart/form-data` with `audio` field (WAV/MP3/OGG/FLAC, max 50MB), or JSON `{ "url": "..." }`.

**Response:**

```json
{
  "aiProbability": 91,
  "verdict": "AI",
  "confidence": "high",
  "signals": {
    "beats": 0.93,
    "mhubert": 0.89,
    "ensemble": 0.91
  },
  "hasSpeech": true,
  "duration": 8.5
}
```

***

### `POST /url-check`

**Scope:** `url-check`

URL/domain safety check. DB lookup + runtime checks (Google Safe Browsing, WHOIS, on-chain).

**Request:**

```json
{ "url": "https://evil.com/phish" }
```

**Response:**

```json
{
  "url": "https://evil.com/phish",
  "domain": "evil.com",
  "riskLevel": "critical",
  "riskScore": 95,
  "signals": ["db_flagged_phishing", "domain_new_7d"],
  "sources": ["phishing_database"]
}
```

***

### `POST /entity-check`

**Scope:** `entity-check`

Full entity risk check with raw DB entries and member data. Used internally by DOSafe clients (Telegram, mobile, extension).

**Request:**

```json
{ "entityType": "phone", "entityId": "+84901234567" }
```

**Response:**

```json
{
  "riskLevel": "high",
  "riskScore": 78,
  "riskSignals": ["db_flagged_scam", "db_source_checkscam_vn"],
  "threatIntel": { "entries": [...] },
  "onChain": { "attestationCount": 1, "latestRiskScore": 80 },
  "trustedMember": { "found": true, "member": { "trustScore": 45, "isFlagged": false, "passingThreshold": false } }
}
```

***

## Signal Reference

Signals are the raw evidence contributing to a risk score. Returned in `signals[]` for transparency.

### Threat DB Signals

| Signal                   | Description                                                | Weight |
| ------------------------ | ---------------------------------------------------------- | ------ |
| `db_flagged_phishing`    | In DOSafe DB as phishing                                   | +90    |
| `db_flagged_malware`     | In DOSafe DB as malware                                    | +85    |
| `db_flagged_scam`        | In DOSafe DB as scam                                       | +75    |
| `db_verified_legitimate` | Verified legitimate in DOSafe DB                           | −45    |
| `cluster_linked`         | Linked to a known scammer cluster                          | +15    |
| `db_source_*`            | Which scraper source flagged it (informational, no weight) | 0      |

### On-Chain Signals

| Signal                        | Description                    | Weight |
| ----------------------------- | ------------------------------ | ------ |
| `onchain_flagged_phishing`    | On-chain attestation: phishing | +85    |
| `onchain_flagged_scam`        | On-chain attestation: scam     | +70    |
| `onchain_high_risk`           | On-chain risk score ≥ 80       | +60    |
| `onchain_medium_risk`         | On-chain risk score 50–79      | +35    |
| `onchain_verified_legitimate` | On-chain: verified legitimate  | −40    |
| `onchain_trusted`             | On-chain risk score < 20       | −30    |

### DOS.Me Identity Signals

| Signal                 | Description                    | Weight |
| ---------------------- | ------------------------------ | ------ |
| `dosme_member_flagged` | Flagged on DOS.Me              | +40    |
| `dosme_trust_passing`  | Meets DOS.Me trust threshold   | −20    |
| `dosme_high_trust`     | Trust score ≥ 70               | −15    |
| `dosme_multi_verified` | 3+ verified identity providers | −10    |
| `dosme_medium_trust`   | Trust score ≥ 40               | −5     |
| `dosme_has_dosid`      | Has custom DOS.me username     | −5     |

***

## Error Responses

| Status                      | Reason                             |
| --------------------------- | ---------------------------------- |
| `401 Unauthorized`          | Missing or invalid `X-Api-Key`     |
| `403 Forbidden`             | Key valid but lacks required scope |
| `400 Bad Request`           | Validation error                   |
| `500 Internal Server Error` | Lookup failed                      |

***

## Migration from DOS.Me Trust API

If you were previously using `api.dos.me/trust/check`, migrate to DOSafe:

| Old                           | New                                             |
| ----------------------------- | ----------------------------------------------- |
| `POST api.dos.me/trust/check` | `POST app.dosafe.io/api/check/bulk`             |
| `GET api.dos.me/trust/member` | Included in `/check` response as `member` field |
| `POST api.dos.me/trust/flags` | `POST app.dosafe.io/api/report`                 |

The DOS.Me Trust API endpoints are deprecated and will be removed on **2026-11-01**.

***

## Getting API Keys

Contact the DOSafe team with:

1. Your product name
2. Required scopes (`check`, `bulk`, `report`, `detect`, `url-check`, `entity-check`)
3. Expected daily volume


# Error Codes

When an API request fails, the response includes an HTTP status code and a JSON body with error details.

## Error Response Format

Most errors follow this structure:

```json
{
  "error": {
    "message": "A human-readable description of the error.",
    "type": "error_type",
    "code": 401
  }
}
```

## 400 Bad Request

The request is malformed or contains invalid parameters.

**Common triggers:**

* Missing required fields (`model` or `messages`)
* Invalid `model` value (model does not exist)
* `messages` array is empty
* `temperature` outside the valid range (0-2)
* `max_tokens` is negative or exceeds the model context limit
* Malformed JSON in the request body

**Example:**

```json
{
  "error": {
    "message": "Invalid value for temperature: must be between 0 and 2.",
    "type": "invalid_request_error",
    "code": 400
  }
}
```

**How to fix:**

* Verify the request body is valid JSON
* Check that all required fields are present
* Ensure parameter values are within documented ranges
* Use the [Models API](/api-reference/models) to confirm valid model IDs

### Guardrails Block (`guardrails_blocked`)

If you opt into [Guardrails](/guides/guardrails) with the `X-DOS-Guardrails: block` header and sensitive data (PII) is detected in your input, the request is rejected with a `400` and this body:

```json
{
  "error": {
    "message": "Request blocked by guardrails: sensitive data (PII) detected in the input.",
    "type": "guardrails_blocked"
  }
}
```

To mask the PII and let the request through instead, use `X-DOS-Guardrails: redact`. See [Guardrails](/guides/guardrails) for details.

***

## 401 Unauthorized

The API key is missing, invalid, or has been revoked.

**Common triggers:**

* No `Authorization` header in the request
* Incorrect header format (must be `Bearer YOUR_API_KEY`)
* API key has been deleted or deactivated

**Example:**

```json
{
  "error": {
    "message": "Invalid API key.",
    "type": "authentication_error",
    "code": 401
  }
}
```

**How to fix:**

* Verify your API key starts with `dos_sk_`
* Check the `Authorization` header format: `Authorization: Bearer dos_sk_...`
* Generate a new key from the [dashboard](https://app.dos.ai) if your current key may be compromised
* Ensure there are no trailing spaces or newlines in the key

***

## 402 Payment Required

Your account does not have sufficient credits to process the request.

**Example:**

```json
{
  "error": {
    "message": "Insufficient credits. Please top up your account.",
    "type": "payment_required",
    "code": 402
  }
}
```

**How to fix:**

* Check your current balance on the [dashboard](https://app.dos.ai)
* Add credits to your account
* If you believe this is an error, contact <support@dos.ai>

***

## 429 Too Many Requests

You have exceeded the rate limit for your account.

**Example:**

```json
{
  "error": {
    "message": "Rate limit exceeded. Please wait before making another request.",
    "type": "rate_limit_error",
    "code": 429
  }
}
```

**How to fix:**

* Implement exponential backoff (wait, then retry with increasing delays)
* Check the `Retry-After` header for how long to wait
* Reduce the frequency of your requests
* See [Rate Limits](/support/rate-limits) for detailed limits and best practices
* Contact us for higher limits if your use case requires them

***

## 500 Internal Server Error

An unexpected error occurred on our servers.

**Example:**

```json
{
  "error": {
    "message": "An internal error occurred. Please try again later.",
    "type": "server_error",
    "code": 500
  }
}
```

**How to fix:**

* Retry the request after a short delay (1-5 seconds)
* If the error persists, try a different model
* Check [status.dos.ai](https://status.dos.ai) for any ongoing incidents
* Contact <support@dos.ai> with the request details if the problem continues

***

## 503 Service Unavailable

The requested model is temporarily unavailable, typically because it is being loaded, updated, or restarted.

**Example:**

```json
{
  "error": {
    "message": "Model is currently unavailable. Please try again shortly.",
    "type": "service_unavailable",
    "code": 503
  }
}
```

**How to fix:**

* Wait 30-60 seconds and retry. Model loading typically completes within a few minutes.
* Try a different model if you need an immediate response
* Check [status.dos.ai](https://status.dos.ai) for maintenance announcements

***

## Best Practices for Error Handling

### Implement Retry Logic

For transient errors (429, 500, 503), implement automatic retries with exponential backoff:

```python
import time
import requests

def call_api_with_retry(url, headers, data, max_retries=3):
    for attempt in range(max_retries):
        response = requests.post(url, headers=headers, json=data)

        if response.status_code == 200:
            return response.json()

        if response.status_code in (429, 500, 503):
            wait_time = 2 ** attempt  # 1s, 2s, 4s
            time.sleep(wait_time)
            continue

        # Non-retryable error
        response.raise_for_status()

    raise Exception("Max retries exceeded")
```

### Log Error Details

Always log the full error response for debugging. The `message` field often contains specific information about what went wrong.

### Validate Before Sending

Validate your request parameters client-side before making API calls to avoid unnecessary 400 errors. Check that:

* The model ID is valid
* Messages array is not empty
* Numeric parameters are within range
* JSON is well-formed


# Rate Limits

DOS AI enforces rate limits to ensure fair usage and platform stability for all users.

## Default Limits

| Metric              | Limit                       |
| ------------------- | --------------------------- |
| Requests per minute | 60                          |
| Window type         | Sliding window (60 seconds) |

The rate limiter uses a **sliding window** algorithm. This means the limit is calculated over a rolling 60-second period, not fixed calendar minutes.

## Rate Limit Headers

Every API response includes headers that report your current rate limit status:

| Header                  | Description                                    |
| ----------------------- | ---------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed in the current window |
| `X-RateLimit-Remaining` | Requests remaining in the current window       |

### Example Response Headers

```
HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
Content-Type: application/json
```

Use these headers to proactively manage your request rate and avoid hitting the limit.

## What Happens When You Hit the Limit

When you exceed the rate limit, the API returns a `429 Too Many Requests` response:

```json
{
  "error": {
    "message": "Rate limit exceeded. Please wait before making another request.",
    "type": "rate_limit_error",
    "code": 429
  }
}
```

The request is **not processed** and **no credits are charged**.

## Handling 429 Errors

### Exponential Backoff

The recommended strategy is **exponential backoff with jitter**. This progressively increases wait time between retries and adds randomness to prevent thundering herd problems.

#### Python Example

```python
import time
import random
import requests

def call_with_backoff(url, headers, data, max_retries=5):
    for attempt in range(max_retries):
        response = requests.post(url, headers=headers, json=data)

        if response.status_code == 200:
            return response.json()

        if response.status_code == 429:
            base_delay = 2 ** attempt  # 1, 2, 4, 8, 16 seconds
            jitter = random.uniform(0, base_delay * 0.5)
            wait_time = base_delay + jitter

            print(f"Rate limited. Retrying in {wait_time:.1f}s...")
            time.sleep(wait_time)
            continue

        response.raise_for_status()

    raise Exception("Max retries exceeded")
```

#### Node.js Example

```javascript
async function callWithBackoff(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.ok) {
      return await response.json();
    }

    if (response.status === 429) {
      const baseDelay = Math.pow(2, attempt) * 1000;
      const jitter = Math.random() * baseDelay * 0.5;
      const waitTime = baseDelay + jitter;

      console.log(`Rate limited. Retrying in ${(waitTime / 1000).toFixed(1)}s...`);
      await new Promise((resolve) => setTimeout(resolve, waitTime));
      continue;
    }

    throw new Error(`API error: ${response.status}`);
  }

  throw new Error("Max retries exceeded");
}
```

### Proactive Rate Management

Use the rate limit headers to throttle requests before hitting the limit:

```python
import time
import requests

def call_with_throttle(url, headers, data):
    response = requests.post(url, headers=headers, json=data)

    remaining = int(response.headers.get("X-RateLimit-Remaining", 60))

    if remaining < 5:
        time.sleep(2)
    elif remaining < 10:
        time.sleep(0.5)

    return response.json()
```

## Tips for Staying Within Limits

1. **Batch your work** -- Pace requests evenly rather than sending them all at once.
2. **Use streaming** -- Streaming responses (`stream: true`) count as a single request regardless of response length.
3. **Cache responses** -- Avoid making the same API call repeatedly.
4. **Monitor usage** -- Check `X-RateLimit-Remaining` headers and slow down as you approach the limit.
5. **Use fewer, larger requests** -- One comprehensive prompt is better than multiple small ones.

## Enterprise Custom Limits

For organizations with high-volume needs, we offer custom limits:

* **Higher request-per-minute caps** based on your workload
* **Burst allowances** for predictable traffic spikes
* **Dedicated capacity** with guaranteed throughput

Contact **<support@dos.ai>** to discuss custom rate limits for your organization.


